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的静态代理):虽然实现了业务逻辑与输出日志 ...
随机推荐
- PAT-1146(Topological Order)拓扑排序+判断一个序列是否满足拓扑序列
Topological Order PAT-1146 #include<iostream> #include<cstring> #include<string> # ...
- CCF(URL映射:80分):字符串处理+模拟
URL映射 CCF201803-3 #include<iostream> #include<cstdio> #include<cstring> #include&l ...
- CSV 注入实战
oxo1 前言 之前看到过 CSV 注入的文章,具体想了解的请搜索学习,这里不多作介绍.今天刚好碰到了导出功能,就随手测试一波,没想到还真的存在 CSV 注入漏洞. oxo2 经过 1.测试漏洞 看到 ...
- 迷宫问题(DFS)
声明:图片及内容基于https://www.bilibili.com/video/BV1oE41177wk?t=3245 问题及分析 8*8的迷宫,最外周是墙,0表示可以走,1表示不可以走 设置迷宫 ...
- vue 弹窗禁止底层滚动
原因:底层视图高度超出百分百,加入弹窗后再苹果浏览器隐藏上下栏的情况下遮罩层没有完全遮住底层. 处理:打开弹窗后禁止底层滚动调用stop事件,关闭则开启底层滚动调用move事件. let mo=fun ...
- [SPOJ2021] Moving Pebbles
[SPOJ2021] Moving Pebbles 题目大意:给你\(N\)堆\(Stone\),两个人玩游戏. 每次任选一堆,首先拿掉至少一个石头,然后移动任意个石子到任意堆中. 谁不能移动了,谁就 ...
- 二分法排序-Python实现
有一个无序序列[37,99,73,48,47,40,40,25,99,51],先进行排序打印输出,分别尝试插入20/40/41 数值到序列中合适的位置,保证其有序. 1.for 循环实现 第一种实现, ...
- c/c++ switch case内括号
如果在case语句中有定义变量,则必须要加{},否则会报错.
- Tomcat详解系列(2) - 理解Tomcat架构设计
Tomcat - 理解Tomcat架构设计 前文我们已经介绍了一个简单的Servlet容器是如何设计出来,我们就可以开始正式学习Tomcat了,在学习开始,我们有必要站在高点去看看Tomcat的架构设 ...
- C# - 实现类型的比较
IComparable<T> .NET 里,IComparable<T>是用来作比较的最常用接口. 如果某个类型的实例需要与该类型的其它实例进行比较或者排序的话,那么该类型就可 ...