最近健身、工作、学习啥的有点忙,感觉老久没更新文章了,今天来水一片
其实我并不推荐使用 AndroidNativeEmu, 他并不是一个好的选择
弊端
- AndroidNativeEmu 只能模拟未加壳的 So
- 很多 jni 函数并没有实现,容易被针对
- 很多 java 函数也没有实现
- 貌似作者停止维护项目了
结论如果不是只会用 python 的话,不建议使用
特色
- elf 文件解析及 so 加载
- 栈支持
- 内存管理
- 文件系统
- JNI 支持
- 常见 syscall 模拟支持
项目地址 https://github.com/AeonLucid/AndroidNativeEmu.git
pip install pyelftools
核心目录
底层系统调用模拟实现
JNIenv 模拟实现调用
对抗:
- 因为本身 Emu 有许多系统函数和 env 并没有实现,可以利用这一点对 Emu 模拟调用进行对抗。
- 反观,我们也可以自己进行函数补全来避免被对抗。
JNI 实现源码分析
源码例子中
实例话 Emulator
首先实例化了 Emulator
2 个参数
- 虚拟文件系统目录
- 目录位置
默认设置在这里了。
接下来要模拟CPU就要进行映射了!
写入寄存器,堆栈大小地址参数,这些在 Emu 中是固定值(可以自行配置)正常情况这么大的空间也是够用了的。
这些步骤在自己写 unicorn 的时候也是同样的写法
- 接下来
modules
用来加载 so memory
用来管理内存的
后面就是初始化系统调用的模拟实现
也就是源码中这一部分
后面是 hook 代码部分
根据 hooker
可以发现他对所有区段进行了 hook,这样效率是很低的!
这一点在之前自己写 unicorn hook jni 的 hook 回调时候指定区段进行 hook 效率可以提升数倍!!如图当然是要根据自己写的区域去指定, hook 段。
当然他写的hook jni 是
用 4 行汇编代码,统一生成的,不同的汇编代码(自己在其中生产了个动态的 hexhook_id)。
我之前用的是
b'\x00\xb5\x00\xbd'
也就是
push {lr}
pop {pc}
然后就是 jni 的模拟实现部分
顺带一提,这 5 个函数是 JavaVM 的函数啦, 这个很眼熟吧
之前也用。unicorn 写过
很明现下面的 self.jni_env = JNIEnv(emu, class_loader, hooker)
是我们要找的看的地方, 跟进
这里有 230 多个 jni 函数(并不完整)每个函数都可以跟进进行查看。
提供的回调函数
在实例化 Emu 对象后, 他还提供给我们了一些可用的回调函数。
位置在 samples/debug_utils
文件夹中,使用的时候可以直接对其修改即可, 举个栗子:
我们想要输出更详细的相应汇编代码,那么我们可以利用 capstone
来输出. 在 debug_utils.py
中修改代码
然后在调用例子中,注释他自己的回调 hook_coode
, 引入刚刚我们改版的
这一来就可以输出更详细的汇编指令了
当然,可以更具自己的需求进行调整修改.
demo example 分析
调用的是
中的
也就是取 "Hello from Native Library."
字符串的长度。、
操作流程也是:
模拟器初始化与加载 so 文件
模拟调用函数(无参)
当然这个例子是无参数的, 我们正常情况下是需要带参数的。所以可以跟进函数 call_symbol
看看怎么传递参数
这里只是简单的判断了下,便传入了 call_native
该函数在这里
先判断了是否是 jni 然后根据参数个数进行初始化,并写好 LR, 最后启动执行
拓展
这里我们用添加 so 的放过解决了依赖库。
其实我们也可以用 hook 的方法来解决,就如图自写 unicorn 一样。hook 注后手动实现其功能即可。
而且 Emu 已经写了一些常用的了,位置在 androidemu-native-hooks.py
我们这里也参考他的写法,整一个 strlen
的来通过 hook 重写的方法来实现 demo 功能
- 引入 emu 的工具类
- 引入 装饰器 native_method
- 参考 hooks.py 中的方式重写 strlen (当然如果函数作者已经写好了的话就直接用)
- 把这个 hook 给挂上
运行结果没问题!
当然还可以用这种方法替换掉任意目标函数
调用 Jni
后面有空的好再更新这个帖子吧
因为我觉得 Emu 并不是一个很好的选择,我们只是学习一下他的原理就好。
大家如果有兴趣的话可以看看
ExAndroidNativeEmu 这个项目