1. 背景
年底了,在进行技术梳理,发现对于动态调试so方面还不太会,每年都说要学习,结果两年了还没学会,近期也是进行了补课,做一个笔记记录一下。
2. 工具准备
2.1 IDA客户端
本次使用版本是:IDA_Pro_v7.5_Portable
2.2 一台Root手机
Android版本:4.4.2
手机系统:aosp_mako-userdebug 4.4.2 KK
2.3 ApkToolBox_V1.6.4
主要用于反编译与回编译,写成了bat脚本,目标直接拖拽到上面即可。需要的可以私聊我。
3. 动态调试步骤
3.1 开启android_server监听
1. adb push d:\android_server(IDA的dbgsrv目录下) /data/local/tmp/android_server(这个目录是可以随便放的)
注意: android_server 的版本 和IDA的版本需要保持一致
2. 赋予权限
1. adb shell
2. cd /data/local/tmp
3. chmod 777 android_server
3. 开启监听
./android_server
3.2 端口转发,方便IDA连接
另开一个CMD窗口
adb forward tcp:23946 tcp:23946
3.3 以DEBUG模式打开 目标应用
e.g.
adb shell am start -D -n com.yaotong.crackme/com.yaotong.crackme.MainActivity
e.g.
adb shell am start -D -n com.example.hellojni/com.example.hellojni.HelloJni
注意下,线上APK应用的android:debuggable属性应该都是false,但是我们想要调试的话必须将其改为android:debuggable="true",如下图所示:
一般有两种方法可以解决:
第一种:反编译APK,然后在目标的AndroidManifest.xml中添加上,然后回编译签名即可。(不通用,如果目标有做签名校验是不行的)
第二种:编译特殊的Android系统,把调试开关打开,这样所有在此系统上运行的app都会以debug模式运行,比较通用。
3.4 打开IDA,附加进程
1. Debugger-Attach-Remote ARM Linux/Android debugger
2. Hostname:127.0.0.1 port:23946
3. 设置 Debug options
1. 打钩 Events/Suspend on process entry point
2. 打钩 Events/Suspend on library load/unload
3. 打钩 Logging/Debugging message
4. 打钩 Logging/Thread start/exit
5. 打钩 Logging/Library load/unload
4. 点击OK,打开进程面板,CTRL + F,搜索选择目标应用进程
3.5 打开Eclipse,查看DDMS-当前应该是红色虫子状态
运行: jdb -connect com.sun.jdi.SocketAttach:port=8700,hostname=localhost
运行结束后,红色虫子应该会变绿,代表此时程序可调式。
3.6 另外打开一个IDA窗口,打开目标so,静态分析,找到目标函数的起始地址
观察发现相对地址为:000011A8,同时记录一下jniOnLoad的相对地址:00001B9C,后面可能会用到。
3.7 切换到已经附加进程的IDA界面
3.7.1 查看输出面板,是否已经加载到 目标so
3.7.2 如果没有,使用 F9 使程序继续加载
3.7.3 如已经加载到目标so,使用 CTRL + S,打开 程序已经加载的so窗口
3.7.4 使用 CTRL + F 快速搜索目标so名称,找到第一条就是在内存空间中加载此so的起始地址:75904000,如下图所示:
3.7.5 结合上一步分析的目标函数的相对地址(000011A8),两者相加得出此次目标函数的起始地址为:759051A8,使用 G 打开 jump to address窗口,输入地址,即可到达目标函数,添加断点。
4. 遇到问题
4.1 目标函数断点不到
首先考虑目标是否做了apk反调试功能,然后断点到jniOnLoad函数,使用 F8 单步调试,查看具体哪一步挂掉了。一般都是BLX or BL (函数调用的意思),发现在jniOnLoad中进行到如下步骤时,崩溃了:
双击进去发现是 pthread_create(创建线程),猜测是开启一个线程,在里面做了反调试的功能,如下图:
解决办法:
第一种:修改汇编指令,跳过该函数
第二种:hook pthread_create 方法,跳过该函数,不创建线程
这里使用了方法一,断点调试将要运行到 BLX R7的时候,在要修改的指令处右键,keypatch-Patcher,修改为nop,然后patch即可,如下图所示: