安卓动态调试七种武器之离别钩 – Hooking(下)

0x00 序

随着移动安全愈来愈火,各类调试工具也都层出不穷,但由于环境和需求的不一样,并无工具是万能的。另外工具是死的,人是活的,若是能搞懂工具的原理再结合上自身的经验,你也能够创造出属于本身的调试武器。所以,笔者将会在这一系列文章中分享一些本身常常用或原创的调试工具以及手段,但愿能对国内移动安全的研究起到一些催化剂的做用。java

目录以下:
安卓动态调试七种武器之长生剑 - Smali Instrumentation
安卓动态调试七种武器之孔雀翎 – Ida Pro
安卓动态调试七种武器之离别钩 – Hooking (上)
安卓动态调试七种武器之离别钩 – Hooking (下)
安卓动态调试七种武器之碧玉刀- Customized DVM
安卓动态调试七种武器之多情环- Customized Kernel
安卓动态调试七种武器之霸王枪 - Anti Anti-debugging
安卓动态调试七种武器之拳头 - Tricks & Summarypython

文章中全部提到的代码和工具均可以在个人github下载到,地址是: https://github.com/zhengmin1989/TheSevenWeaponsandroid

0x01 利用函数挂钩实现native层的hook

咱们在离别钩(上)中已经能够作到动态的加载自定义so文件而且运行so文件中的函数了,但还不能作到hook目标函数,这里咱们须要用到函数挂钩的技术来作到这一点。函数挂钩的基本原理是先用mprotect()将原代码段改为可读可写可执行,而后修改原函数入口处的代码,让pc指针跳转到动态加载的so文件中的hook函数中,执行完hook函数之后再让pc指针跳转回本来的函数中。git

用来注入的程序hook5逻辑与以前的hook4相比并无太大变化,仅仅少了“调用 dlclose 卸载so文件”这一个步骤,由于咱们想要执行的hook后的函数在so中,因此并不须要调用dlclose进行卸载。基本步骤以下:github

保存当前寄存器的状态 -> 获取目标程序的mmap, dlopen, dlsym, dlclose 地址 -> 调用mmap分配一段内存空间用来保存参数信息 –> 调用dlopen加载so文件 -> 调用dlsym找到目标函数地址 -> 使用ptrace_call执行目标函数 -> 恢复寄存器的状态算法

hook5的主要代码逻辑以下:
图片描述
图片描述数组

咱们知道arm处理器支持两种指令集,一种是arm指令集,另外一种是thumb指令集。因此要hook的函数多是被编译成arm指令集的,也有多是被编译成thumb指令集的。Thumb指令集能够看做是arm指令压缩形式的子集,它是为减少代码量而提出,具备16bit的代码密度。Thumb指令体系并不完整,只支持通用功能,必要时仍须要使用ARM指令,如进入异常时。须要注意的一点是thumb指令的长度是不固定的,但arm指令是固定的32位长度。缓存

为了让你们更容易的理解hook的原理,咱们先只考虑arm指令集,由于arm相比thumb要简单一点,不须要考虑指令长度的问题。因此咱们须要将target和hook的so编译成arm指令集的形式。怎么作呢?很简单,只要在Android.mk中的文件名后面加上”.arm”便可 (真正的文件不用加)。
图片描述安全

肯定了指令集之后,咱们来看实现挂钩最重要的逻辑,这个逻辑是在注入后的so里实现的。首先咱们须要一个结构体保存汇编代码和hook地址:
图片描述微信

咱们接着来看注入的逻辑,最重要的函数为hook_direct(),他有三个参数,一个参数是咱们最开始定义的用来保存汇编代码和hook地址的结构体,第二个是咱们要hook的原函数的地址,第三个是咱们用来执行hook的函数。函数的源码以下:
图片描述

虽然android有ASLR,但并无PIE,因此program image是固定在0x8000这个地址的,所以咱们用mprotect()函数将整个target代码段变成RWX,这样咱们就能修改函数入口处的代码了。是否修改为功能够经过cat /proc/[pid]/maps查看:
图片描述

随后咱们须要肯定目标函数的地址,这个有两种方法。若是目标程序自己没有被strip的话,那些symbol都是存在的,所以可使用dlopen()和dlsym()等方法来获取目标函数地址。但不少状况,目标程序都会被strip,特别是能够直接运行的二进制文件默认都会被直接strip。好比target中的sevenWeapons()这个函数名会在编译的时候去掉,因此咱们使用dlsym()的话是没法找到这个函数的。这时候咱们就须要使用ida或者objdump来定位一下目标函数的地址。好比咱们用objdump找一下target程序里面sevenWeapons(int number)这个函数的地址:
图片描述

虽然target这个binary被strip了,但仍是能够找到sevenWeapons()这个函数的起始地址是在0x84f4。由于”mov r2, r0”就是加载number这个参数的指令,随后调用了<printf@plt>用来输出结果。 最后一个参数也就是咱们要执行的hook函数的地址。获得这个地址很是简单,由于是so中的函数,调用hook_direct()的时候直接写上函数名便可。

hook_direct(&eph,hookaddr,my_sevenWeapons);

接下来咱们看如何修改函数入口(modify function entry),首先咱们保存一下原函数的地址和那个函数的前三条指令。随后咱们把目标函数的第一条指令修改成 LDR pc, [pc, #0],这条指令的意思是跳转到PC指针所指的地址,因为pc寄存器读出的值其实是当前指令地址加8,因此咱们把后面两处指令都保存为hook函数的地址,这样的话,咱们就能控制PC跳转到hook函数的地址了。
最后咱们再调用hook_cacheflush()这个函数来刷新一下指令的缓存。由于虽然前面的操做修改了内存中的指令,但有可能被修改的指令已经被缓存起来了,再执行的话,CPU可能会优先执行缓存中的指令,使得修改的指令得不到执行。因此咱们须要使用一个隐藏的系统调用来刷新一下缓存。hook_cacheflush()代码以下:
图片描述

刷新完缓存后,再执行到原函数的时候,pc指针就会跳转到咱们自定义的hook函数中了,hook函数里的代码以下:
图片描述

首先在hook函数中,咱们能够得到原函数的参数,而且咱们能够对原函数的参数进行修改,好比说将数字乘2。随后咱们使用hook_precall(&eph);将本来函数的内容进行还原。hook_precall()内容以下:
图片描述

在hook_precall()中,咱们先对本来的三条指令进行还原,而后使用hook_cacheflush()对内存进行刷新。通过处理以后,咱们就能够执行原来的函数orig_sevenWeapons(number)了。执行完后,若是咱们还想再次hook这个函数,就须要调用hook_postcall(&eph)将本来的三条指令再进行一次修改。
下面咱们来使用hook5和libinject2.so来注入一下target这个程序:
图片描述

能够看到通过注入后,咱们成功的获取了参数number的值,而且将”Hello,LiBieGou!”后面的数字变成了原来的两倍。

0x02 使用adbi实现JNI层的hook

咱们在上一节中介绍了如何hook native层的函数。下面咱们再来说讲如何利用adbi来hook JNI层的函数。Adbi是一个android平台上的一个注入框架,自己是开源的。Hook的原理和咱们以前介绍的技术是同样的,这个框架支持arm和thumb指令集,也支持经过字符串定位symbol函数的地址。
首先咱们须要一个例子用来说解,因此我写了程序叫test2。
图片描述

enter image description here

点击程序中的button后,程序会调用so中的Java_com_mzheng_libiegou_test2_MainActivity_stringFromJNI(JNIEnv* env,jobject thiz,jint a,jint b)函数用来计算a+b的结果。咱们默认传的参数是a=1, b=1。接下来我就来教你如何利用adbi来hook这个JNI函数。

由于adbi是一个注入框架,咱们下载好源码后,只要对应着源码中给的example照猫画虎便可。Hijack那个文件夹是保存的用来注入的程序,和咱们以前讲的hook5.c的原理是同样的,因此不用作任何修改。咱们只须要修改example中的代码,也就是将要注入的so文件的源码。

首先,咱们在/adbi-master/instruments/example这个文件夹下新建两个文件”hookjni.c”和” hookjni_arm.c”。“hookjni_arm.c”其实只是一个壳,用来将hook函数的入口编译成arm指令集的,内容以下:
图片描述

这个文件的目的仅仅是为了用arm指令集进行编译,能够看到Android.mk中在”hookjni_arm.c”后面多了个”.arm”:
图片描述

下面咱们来看”hookjni.c”:
图片描述

这段代码和我上一节讲的代码很是像,my_init()用来进行hook操做,咱们须要提供想要hook的so文件名和函数名,而后再提供thumb指令集和arm指令集的hook函数地址。
my_Java_com_mzheng_libiegou_test2_MainActivity_stringFromJNI()就是咱们提供的hook函数了。咱们在这个hook函数中把a和b都改为了10。除此以外,咱们还使用counter这个全局变量来控制hook的次数,这里咱们把counter设置为3,当hook了3次之后,就再也不进行hook操做了。
编辑好代码后,咱们只须要在adbi的根目录下执行” build.sh”进行编译:
图片描述

编译好后,咱们把hijack和libexample.so拷贝到/data/local/tmp目录下。而后使用hijack进行注入:
图片描述

而后咱们再点击button就能够看到咱们已经成功的修改了a和b的值为10了,最后显示1+1=20。
图片描述

经过cat adbi_example.log咱们能够看到hook过程当中打印的log:
图片描述

能够看到adbi是经过thumb指令集进行hook的,缘由是test2程序是用thumb指令集进行编译的。其实hook thumb指令集和arm指令集差很少,在”/adbi-master/instruments/base”中能够找到hook thumb指令集的逻辑:
图片描述

其实h->jumpt[20]这个字符数组保存的就是thumb指令集下控制pc指针跳转到hook函数地址的代码。Hook完以后的流程就和arm指令集的hook同样了。

0x03 使用Cydia或Xposed实现JAVA层的hook

关于Cydia和Xposed的文章和例子已经不少了,这里就再也不重复的进行介绍了。这里推荐一下瘦蛟舞和我写的文章,基本上就知道怎么使用这两个框架了:

Android.Hook框架xposed篇(Http流量监控) http://drops.wooyun.org/papers/7488
Android.Hook框架Cydia篇(脱壳机制做) http://drops.wooyun.org/tips/8084
手把手教你当微信运动第一名 – 利用Android Hook进行微信运动做弊 http://drops.wooyun.org/tips/8416

我的感受Xposed框架要作的更好一些,主要缘由是Cydia的做者已经好久没有更新过Cydia框架了,不光有不少bug还不支持ART。可是有不少不错的调试软件/插件是基于两个框架制做的,因此有时候仍是须要用到Cyida的。

接下来就推荐几个很实用的基于Cydia和Xposed的插件:

  1. ZjDroid: ZjDroid是基于Xposed Framewrok的动态逆向分析模块,逆向分析者能够经过ZjDroid完成如下工做: 一、DEX文件的内存dump 二、基于Dalvik关键指针的内存BackSmali,有效破解主流加固方案 三、敏感API的动态监控 四、指定内存区域数据dump 五、获取应用加载DEX信息。 六、获取指定DEX文件加载类信息。 七、dump Dalvik java堆信息。 八、在目标进程动态运行lua脚本。 https://github.com/halfkiss/ZjDroid

  2. XPrivacy: XPrivacy是一款基于Xposed框架的模块应用,能够对全部应用可能泄露隐私的权限进行管理,对禁止可能会致使崩溃的应用采起欺骗策略,提供伪造信息,好比说能够伪造手机的IMEI号码等。 https://github.com/M66B/XPrivacy

  3. Introspy: Introspy是一款能够追踪分析移动应用的黑盒测试工具而且能够发现安全问题。这个工具支持不少密码库的hook,还支持自定义hook。 https://github.com/iSECPartners/Introspy-Android

0x04 Introspy实战

咱们使用alictf上的evilapk400做为例子讲解如何利用introspy来调试程序。Evilapk400使用了比较复杂的dex加壳技术,若是不利用基于自定义dalvik的脱壳工具来进行脱壳的话作起来会很是麻烦。但咱们若是换一种思路,直接经过动态调试的方法来获取加密算法的字符串,key和IV等信息就能够直接获取答案了。
首先咱们安装cyida.apk,Introspy-Android Config.apk到手机上,而后用eclipse打开“Introspy-Android Core”的源码增长一个自定义的hook函数。虽然Introspy默认对密码库进行了hook,但却没有对一些strings的函数进行hook。因此咱们手动添加一个对String.equals()的hook:
图片描述
图片描述

而后咱们编译,生成并安装Introspy-Android Core.apk到手机上。而后咱们安装上EvilApk400。而后打开Introspy-Android Config勾选com.ali.encryption。
图片描述

接着打开evilapk400,而后随便输入点内容并点击登录。
图片描述

而后咱们使用adb logcat就能看到Introspy输出的信息了:
图片描述

经过log,很容易就能看出来evilapk400使用了DES加密。经过log咱们获取了密文,Key以及IV,因此咱们能够写一个python程序来计算出最后的答案:
图片描述
图片描述
图片描述

0x05 总结

本篇介绍了native层,JNI层以及JAVA层的hook,基本上能够知足咱们平时对于android上hook的需求了。 另外文章中全部提到的代码和工具均可以在个人github下载到,地址是: https://github.com/zhengmin1989/TheSevenWeapons

0x06 参考资料

Android平台下hook框架adbi的研究(下)http://blog.csdn.net/roland_sun/article/details/36049307
ALICTF Writeups from Dr. Mario

做者:蒸米@阿里聚安全,更多技术文章,请访问阿里聚安全博客

相关文章
相关标签/搜索