adbi学习:java hook实现机制
adbi的java hook实现代码ddi不在之前下载的文件中,下载地址:https://github.com/crmulliner/ddi,具体的编译看readme里面很详细的介绍了。注意ddi代码不能单独使用要跟之前的adbi相结合,因为adbi提供了注入so。本文不对代码进行详细的剖析(你可以看参考资料的文章),分析下2个问题:java如何hook;如何执行自己的java函数。
java hook:
其实在ddi的java hook和xposed的hook原理(不清楚的看我之前xposed的分析)是相同的,都是改变被hook函数的accessflag;将原本的java函数变成native函数,从而去执行自己定义的native函数;它实现的代码在dalvik_hook.c的dalvik_hook函数中。但不同于xposed是,它没有直接修改Mehtod结构的insns和nativeFunc而是调用了dvmUseJNIBridge函数来修改insns和nativeFunc。但在我看这个函数时发现其跟android源码中的dvmRegisterJNIMethod很相似:
static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
const char* signature, void* fnPtr)
{
// 解释下参数:
// clazz:类名"shy/luo/jni/ClassWithJni";
// methodName:需要注册的jni方法名 nanosleep;
// signature:方法的签名 实质是方法的参数和返回值,区别不同参数的函数
// fnPtr: jni方法函数地址 即shy_luo_jni_ClassWithJni_nanosleep函数;dalvik执行的就是这个函数,很重要哎
Method* method;
......
method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
......
dvmUseJNIBridge(method, fnPtr);
......
}
dvmRegisterJNIMethod(关于此函数的解析,看dalvik浅析二:jni、so)函数中用dvmFindDirectMethodByDescriptor找到函数在dalvik中的Method结构,然后再dvmUseJNIBridge为Method注册native函数(在正常情况下,该Method的accessflag就是native所以无需修改)。下面来看ddi中的dalvik_hook函数实现
void* dalvik_hook(struct dexstuff_t *dex, struct dalvik_hook_t *h)
{ //dalvik_hook_t结构体中已包含函数所属的class、name、signature
......
h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
if (h->method == 0) {
h->method = dex->dvmFindDirectMethodByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
}
......
//保留method的属性带以后还原
h->iss = h->method->insSize;
h->rss = h->method->registersSize;
h->oss = h->method->outsSize;
// 这里要处理
h->method->insSize = h->n_iss;
h->method->registersSize = h->n_rss;
h->method->outsSize = h->n_oss; h->method->jniArgInfo = 0x80000000; // <--- also important;这里很重要,下面要分析
......
h->access_flags = h->method->a; //保留原先函数的accessflag
h->method->a = h->method->a | h->af; // make method native
......
// bind function
dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
......
}
显而易见,dalvik_hook按dvmRegisterJNIMethod的逻辑来编写的,所以hook其实是自己为java函数注册了一个jni方法!(熟读源码理解实现机制,你会发现很多框架和工具都是基于源码的)ok,上面的知识点大部分都是熟悉,注意蓝色代码很重要的是函数使用的dalvik寄存器个数,xposed也对其进行修正但不同的是jniArgInfo属性,xposed没对它修改,看下对jniArgInfo的描述
jniArgInfo:这个变量记录了一些预先计算好的信息,从而不需要在调用的时候再通过方法的参数和返回值实时计算了,方便了JNI的调用,提高了调用的速度。如果第一位为1(即0x80000000),则Dalvik虚拟机会忽略后面的所有信息,强制在调用时实时计算;
可以这么理解jniArgInfo相当于jni方法的cache标识符,若jniArgInfo不为0x80000000则dalvik会按之前调用过的jni方法去执行。回到xposed和ddi的区别:
xposed:在loadPackage时hook函数,而在此时绝对没有执行过函数的jni方法;
ddi:在app运行时hook函数,我们可不敢肯定此时没有执行过jni方法啊
执行自己的java函数:
在so中执行了上面的java hook后,app在调用被hook函数时就会执行我们的自定义的native函数。那我们又怎样去执行我们自己的java函数呢(难道不可以直接在native函数中实现逻辑?当然可以啊,但java开发android简单啊)?很简单,1.加载自己的dex(包含自定义java函数的文件);2.找到自定义java函数地址3.执java函数。下面对以上三步用到的知识点剖析。
const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { // /dalvik/vm/native/dalvik_system_DexFile.cpp基于源码4.4
520 { "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
521 Dalvik_dalvik_system_DexFile_openDexFileNative },
522 { "openDexFile", "([B)I",
523 Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
524 { "closeDexFile", "(I)V",
525 Dalvik_dalvik_system_DexFile_closeDexFile },
526 { "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
527 Dalvik_dalvik_system_DexFile_defineClassNative },
528 { "getClassNameList", "(I)[Ljava/lang/String;",
529 Dalvik_dalvik_system_DexFile_getClassNameList },
530 { "isDexOptNeeded", "(Ljava/lang/String;)Z",
531 Dalvik_dalvik_system_DexFile_isDexOptNeeded },
532 { NULL, NULL, NULL },
533};
第一、二步的知识点都在上面的代码里。加载dex的知识我们在app加壳的文章中有所提及,ddi利用Dalvik_dalvik_system_DexFile_openDexFileNative直接加载目录里的dex文件。关于第二步找函数是利用Dalvik_dalvik_system_DexFile_defineClassNative来实现,具体可以看dalvik浅析三:类加载。 这里我有一点不明白的是为什么调用了Dalvik_dalvik_system_DexFile_defineClassNative还要再去调用(*env)->FindClass去得到ClassObject,明明2者都是调用dvmFindClassNoInit去实现的啊,有谁知道告诉我!第三步更简单了,得到函数的ClassObject后直接找到methodID,再调用CallVoidMethodA就可以在native执行java函数啦。
最后添幅图来理解下ddi的整个执行过程吧
当然你也可以在my_init中hook到自定义native函数2中,省去native函数1的执行。
ok,adbi的分析就到此为止啦。总结下adbi在hijack中利用ptrace在目标进程注入代码,该代码会加载so;而在so里面可以进行so和java hook:
so hook:找到native函数的指令利用Inline hook去修改它,就可以在执行native函数时去执行自定义的函数;
java hook:改变函数的accessflag去执行自己的native函数,而在native中去执行自定义的java函数
参考资料:
1 Android平台下Dalvik层hook框架ddi的研究
adbi学习:java hook实现机制的更多相关文章
- Java 类反射机制分析
Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...
- Java学习系列(一)Java的运行机制、JDK的安装配置及常用命令详解
俗话说:“十五的月亮十六圆”.那学习是不是也是如此呢?如果把月亮看成是我们的愿望,那十五便是我们所处的“高原期”,坚持迈过这个坎,我相信你的愿望终究会现实的.记得马云曾说:今天很残酷,明天更残酷,后天 ...
- JAVA的反射机制学习笔记(二)
上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...
- java学习笔记09--反射机制
java学习笔记09--反射机制 什么是反射: 反射是java语言的一个特性,它允许程序在运行时来进行自我检查并且对内部的成员进行操作.例如它允许一个java的类获取他所有的成员变量和方法并且显示出来 ...
- 深入浅出学习hibernate框架(三):java的反射机制
上篇博客写到了JDBC的基本操作,今天准备写一篇关于JAVA反射机制的文章,因为java的反射机制和上一篇JDBC都是Hibernate框架的基本要素.在Hibernate的运行机制中,这两块的内容正 ...
- java学习笔记13--反射机制与动态代理
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...
- Java反射学习:深入学习Java反射机制
一.Java反射的理解(反射是研究框架的基础之一) Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的 ...
- java垃圾回收机制学习总结
最近学习了一下java垃圾回收机制,将其主要内容大致总结一下: 1.什么是垃圾回收机制 java GC机制(garbage collection,垃圾收集,垃圾回收),是java特有的机制,作为jav ...
- 学习AOP之JAVA的代理机制
从一个输出日志的实例分析JAVA的代理机制 一.通用的日志输出方法 :需要在每个类里都增加对输出日志信息的代码 二.通过面向接口编程实现日志的输出(JAVA的静态代理):虽然实现了业务逻辑与输出日志 ...
随机推荐
- 微信小程序onReachBottom第二次失效
当整个页面就是一个view包着一个轮播.一个横向scroll-view和一个纵向scroll-view onReachBottom方法只执行一次 解决方法:
- springmvc字符 中文乱码问题
springmvc字符 中文乱码问题 1.字符过滤器 输入中文测试,发现乱码 以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置,修改了xml文 ...
- 如何使用 HttpReports 监控 .NET Core 应用程序
简介 HttpReports 基于.NET Core 开发的APM监控系统,使用MIT开源协议,主要功能包括,统计, 分析, 可视化, 监控,追踪等,适合在中小项目中使用. github:https: ...
- Nginx常见的错误配置
Blog:博客园 个人 翻译自Common Nginx misconfigurations that leave your web server open to attack Nginx是当前主流的W ...
- Serverless Wordpress 系列建站教程(三)
从前面两篇教程文章里,我们可以了解到 Serverless WordPress 的低门槛部署,免运维等功能优势.而建站场景中,开发者关注的另一个重点则是成本问题,Serverless 架构究竟如何计费 ...
- Sequelize 和 MySQL 对照Sequelize 和 MySQL 对照
安装 这篇文章主要使用MySQL.Sequelize.co来进行介绍.安装非常简单: $ npm install --save co $ npm install --save sequelize $ ...
- frp实现内网穿透
frp实现内网穿透 目标 通过外网访问内网设备,本文中实现通过手机的移动流量,可以访问到树莓派设备 设备准备 需要被访问的设备(本文中使用Raspberry Pi`).公网IP设备(本文中使用阿里云 ...
- bootstrap期末考试习题整理
1.Which is true about Bootstrap? A. Bootstrap is the most popular and powerful front-end (HTML, CSS, ...
- Redis持久化——内存快照(RDB)
最新:Redis持久化--如何选择合适的持久化方式 最新:Redis持久化--AOF日志 最新:Redis持久化--内存快照(RDB) 一文回顾Redis五大对象(数据类型) Redis对象--有序集 ...
- 有了CMDB,为什么还需要应用配置管理?
有了CMDB,为什么还需要应用配置管理? 你不妨先停下来,思考一下这个问题. 我抛出的观点是: CMDB是面向资源的管理,应用配置是面向应用的管理. 请注意,这里是面向"资源",不 ...