编写精简的测试代码
因为作者可能想要全面的展示一下SandHook的强大,测试案例写的相对复杂,而我们初学者看到之后会头疼。所以我们精简一下SandHook的测试代码。
1 | package com.swift.sandhook; |
hook代码
1 | package com.swift.sandhook; |
没有Xposed基础的请粗去!
调试(Java流程)
直接打断点到Xposed的hook地方来。
从调用栈一步一步来。
我们看到当程序执行到我们的Hook函数时进行了跳转,调到了
看看作者对这个文件的描述,该文件是由genhookstubs.py生成的,其作用是提供SandHook的内部Hook和备份函数。这些函数实际上的作用确实是用来hook的,因为程序进行hook时会改变原函数的结构,这会破坏原函数的执行逻辑,所以在执行剩下的执行逻辑时,SandHook会对原函数进行备份,并把原函数和备份函数都传递到c/cpp代码当中。
当代码跳转到hookBridg时就已经获取到原方法的Member对象(实际上这里就是Method对象)以及原方法的函数体。
到这里时,snapshot已经是一个包含我们写的Xposed的Hook代码的数组了。
在此处生成XC_MethodHook.MethodHookParam
对象,该对象存放的是beforeHookedMethod
和afterHookedMethod
的参数param,我们可以通过这个param参数修改和获取返回值以及参数、Class等。
然后开始执行beforeHookedMethod
当执行完没有报错的话程序将跳出循环向下继续执行,这里会判断是否存在函数备份,如果没有的话就执行远函数,并把结果设置到param里。
什么时候开始备份函数呢?
按这堆栈来说下一步将会判断是否是Stub函数,也就是用来填充的函数,如果不是的话执行函数备份。
函数内部还会先去判断是否hook的函数是不是静态函数。因为静态函数不需要传递对象来执行函数,所以直接执行原静态函数即可。
随后执行afterHookedMethod
执行完成之后还不会立即返回到原流程。会先判断返回值的类型并对参数进行封装。
主流程的执行流程思维导图如下
调试(c/cpp流程)
稍微调整一下,把要hook的函数从静态改成动态。
实际上我们刚才省略了很多的流程,比如这里
我们可以看到这里,我随意在这个静态块里打下断点,由于静态块会比非静态函数先加载到堆栈,而我们前面打断点都是在非静态函数里,由于静态块先自执行了,所以刚才我们的调用栈根本没有发现这一块被调用。
所以我们先补全一下这一块的逻辑。
先看到调用栈这里的onCreate。虽然调用栈显示是执行了onCreate,但是实际上这里执行的是XposedHelpers.findAndHookMethod(MainActivity.class, "getText", new XC_MethodHook()
这行代码。
然后我们在看这里的这个成员属性
说明此时当流程走到我们hook函数时,此时已经具备了被hook的能力了(了解Xposed源码的可以知道这个对象就是存着被hook的Method对象)。
当函数有参数时,先把参数存放于一个Class[]数组里,并给参数先置为long.class.
hookMethod原来都是有名字的,都叫stub_hoob_xx
再往下走
这里通过反射的原理直接拿到了stub_hook_0
这个Method对象,细心的读者一定记起来这是刚才执行hookBridge的地方,没错,慢慢可以跟刚才的流程串起来了。
下一步就是拿到函数的备份Method了,这里,我们可以做个大胆假设,为什么要有原函数的备份,我猜测,hook必定会破坏原函数的结构导致无法执行接下来的流程。
要进入重要流程了。
有点好奇SandHook是如何判断函数是否能进行运行的。
SandHook的代码逻辑设计的是非常的好了,单单给大家看看这个getCurrentThread()