一种简单的java函数hook思路

更新时间:2023-07-26 01:01:51 阅读: 评论:0

⼀种简单的java函数hook思路
⼀种简单的java函数hook思路
总结
本⽂取⾃《深⼊理解android java虚拟机art》第10章,在读书过程中总结的⼀些能让⾃⼰豁然开朗的点,通过这些点我发现了hook java 函数的⽅法,有了此⽂。
LinkCode
LinkCode函数当中有个重要的过程,就是GetEntryPointFromQuickCompiledCode函数,他的返回值也就是quick_code为空的话代表该⽅法没有进⾏编译,在oat⽂件当中没有对应的汇编代码,需要进⼊解释执⾏模式⽽不是jit,这⾥有⼀个分⽀,如果ArtMethod是jni⽅法的话会将类似于elf⽂件的ENTRYPOINT 设置为art_quick_generic_jni_trampoline,普通⽅法会设置为
art_quick_to_interpreter_bridge,如下表(取⾃该书565页),这⾥是关键代表着我们可以通过修改这⾥来完成java或jni函数的跳转,也就是每次art虚拟机解释执⾏某个java函数的时候,都会跳转到该java函数的EntryPoint,那么只要把这⾥替换成我们⾃⼰的c函数的地址,就可以实现拦截java函数了,当然也要保留原函数的 执⾏过程。
⽅案思考
了解了java函数⼤致的执⾏流程,下⼀补就是实操了,那么怎么更改⼀个java函数的EntryPoint呢?这是⼀个困难的问题,⾸先提出⼀点1:是否可以通过hook关键art函数的⽅式来获得ArtMethod?
当然可以,但是我们要hook的是⾃⾝的dex,它的加载时机要早于我们的so加载进内存中,所以像LinkCode或者LoadMethod这种在dex 加载流程上的函数是不⾏的,那就需要找⼀个系统函数,既有我们的函数的ArtMethod,⼜是在dex加载完成之后才进⾏的,这个就很难办了,⾸先通过函数名过滤ArtMethod就很难,其次dex加载完成之后也很难找到覆盖所有ArtMethod的函数,所以这种⽅法不可⾏
2:是否可以像frida⼀样Java choo内存搜索ArtMethod?
倒是可以通过flect.Method这个类来进⾏内存漫游,直接把Method类的实例全找出来,⾼版本可以再通过它⽗类的⽅法getArtMethod将它转化为ArtMethod的指针,但是内存漫游太难实现,可能⼀会会参考frida实现⼀下,⽬前不考虑,所以这种也不⾏3:最终⽅案
为什么要节约用电
最终的⽅案其实和frida差不多,回顾⼀下frida如何hook的java函数,先Java.u拿到类,然后⽤implementation直接覆盖原函数,那么我们也可以参考jni反射执⾏java函数的思路,通过FindClass拿到类,再通过GetMethodID的⽅式拿到⼀个jmethodID
var MainActivity = Java.u("xxxxx");
return xxxx;
}
失败的案例
static inline ArtMethod*DecodeArtMethod(jmethodID method_id){
return reinterpret_cast<ArtMethod*>(method_id);
}
现在把他的EntryPoint改成我们想要的函数就好了,那么就⼜产⽣了2个问题
1.EntryPoint在哪我如何能找到它
庆幸的是安卓源码中有提供函数,也就是上⾯提到的GetEntryPointFromQuickCompiledCode那么只要通过ida查看它的实现就好了,遗憾的是这是⼀个inline函数,没有办法使⽤ida查看,只能通过⾃⼰修改源码,来查看偏移了,然后直接脱出libart.so,查看导出函数getmynative就好了
//ArtMethod
extern"C"const void*getmynative(ArtMethod* m)REQUIRES_SHARED(Locks::mutator_lock_){
m->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
奖学金推荐理由
m->UnregisterNative();
const void* entry_point = m->GetEntryPointFromQuickCompiledCode();
if(m->IsFastNative())
vr是什么东西
抚今追昔return GetQuickGenericJniStub();
return entry_point;
}
这样就知道了,偏移是40,那么直接更改就好了
2.直接改会少很多系统函数的调⽤能否成功
这个从理论上来说,是⼀定不成功的,看书上后⾯还有许多的过程,包括参数、栈的保存,都没有做,⼀定不可能成功,但是也不妨试⼀下,没准歪打正着了呢,第⼀版代码如下
void* func(void* a,void* b,int c,int d){
记录近义词__android_log_print(6,"r0ysue","i am success");
return reinterpret_cast<void *>(5);
}
jclass a=env->FindClass(classname);
jmethodID b=env->GetMethodID(a,methodname, shoty);
*((long *)a1 + 5)= reinterpret_cast<long>(func);
居然没拉稀,和之前猜的不太⼀样,虽然成功执⾏了我们⾃⼰的函数,但是要思考⼀个问题就是如何执⾏原函数,这⾥如果通过反射直接执⾏的话,我们拿不到参数所以这种⽅案有点垃圾,我们还需要
继续改进
进阶
既然上⾯的⽅案有瑕疵,就是有很多函数没有执⾏,导致我们拿不到参数,那么可以采取下⼀个,就是模仿Native函数的⽅式,将本函数改成Native函数,然后再使⽤将Native函数注册就好了,那么根据上⾯第⼀节的内容只要将EntryPoint改成
art_quick_generic_jni_trampoline,就好了,再次⽤ida打开libart.so,发现art_quick_generic_jni_trampoline是⼀个函数,但它不是⼀个导出函数,使⽤导出表没有办法找到它,那么就⽤上篇⽂章提到的节头表索引发来搞定,最后再⽤RegisterNatives注册函数地址就好了
int so=findsym("/system/lib64/libart.so","art_quick_generic_jni_trampoline");
*((long*)a1 +5)= reinterpret_cast<long>((char*) startr +so -0x25000);//需要-0x25000是因为libart.so的程序头在偏移为0x25000这⾥没遍历偷懒了
env->RegisterNatives(a,getMethods,1);
这样就完成了Java函数的Native化,那么试验⼀下好不好使,果然不⾏提⽰不是native函数⽆法完成注册,那么就看⼀下如何的判断是否是Native函数,之前编译rom的时候我就加⼊了,Native相关的判断,可以看到,公式(~*(_DWORD *)(a1 + 4) & 0x80100),就是判断是否是native函数的,只要它不等0,那么就可以认为我们的函数是Native函数.
那么我们就可以构造了,直接做异或就好了
*(_QWORD *)(a1 + 4)= *(_QWORD *)(a1 + 4)^0x80100 ;
这样准备就完成了,我们就可以像动态注册的jni函数⼀样,去实现我们的c层的add函数
,执⾏就正常了,这⾥不给⼤家贴图了,因为还有最后⼀个问题没解决,就是如何执⾏原函数。
实现原函数
c调⽤java函数只有⼀种⽅式,那就是反射,所以我们只能⽤反射,那么就要收集参数了,⽐如我下⾯的这个函数,我需要⼀个实例,⼀个env,2个参数
public int add2(int a,int b){
return7;
}
轻装上阵
幸运的是,我们是动态注册的,动态注册的实例函数会⾃带env和this实例,所以我们直接调⽤就好了
void*add(void* a,void* b,int c,int d){
JNIEnv *st=(JNIEnv *)a;
jclass a2=st->FindClass("com/r0ysue/myjavahook/MainActivity");
jmethodID b2=st->GetMethodID(a2,"add2","(II)I");
int yy= st->CallIntMethod(static_cast<jobject>(b), b2, c, d);
__android_log_print(6,"r0ysue","%x",yy);
return reinterpret_cast<void*>(5);
}
直接死循环爆栈了…,太垃圾了,还是思路没找好,想想也是,我没有将函数恢复就直接反射,肯定会再次调⽤的,所以要想⼀个办法将ArtMethod恢复为当初java函数的样⼦,这⾥我⼜⽤了全局变量(没错就是inlinehook 时候坑了我那么久的东西,所以⼀会还要解决多函数的hook问题)
这⾥⽅案是这样的,将hook函数中保存的jump和nativ,直接复原,但是这⾥我没想好怎么搞,所以baocun函数要⼀个参数,就是ArtMethod指针,保存完还要回复到执⾏Native函数的状态
void hook(){
....
nativ=*(_QWORD *)(a1 +4);//isNative()?
jump=*((long*)a1 +5);//EntryPoint
....
}
void huifu(__int64 a,int n){
*((long*)a +5)=old1;
*(_QWORD *)(a +4)=old2;
}
void baocun(__int64 a){
娱乐至死
old1=*((long*)a +5);
old2=*(_QWORD *)(a +4);
*((long*)a +5)= jump;
*(_QWORD *)(a +4)= nativ|0x80100;
作业总结}
void*add(void* a,void* b,int c,int d){
JNIEnv *st=(JNIEnv *)a;
jclass a2=st->FindClass("com/r0ysue/myjavahook/MainActivity");
jmethodID b2=st->GetMethodID(a2,"add2","(II)I");
baocun((__int64)b2);
int yy= st->CallIntMethod(static_cast<jobject>(b), b2, c, d);
__android_log_print(6,"r0ysue","%x",yy);
huifu((__int64)b2)
return reinterpret_cast<void*>(5);
}
这样就很完美了,不管我怎么hook都没有问题了,也不好奔溃
hook 多个函数
但是当hook多个函数的时候就有问题了,我⽤的是全局变量,第⼆次hook的时候会覆盖第⼀次的baocun()和huifu()函数,所以我⼜采⽤了数组的⽅式来保存,这部分代码很简单就直接贴了

本文发布于:2023-07-26 01:01:51,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1096725.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   没有   注册   参数
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图