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",如下图所示:

android 动态调试 防止 动态调试so_android

     一般有两种方法可以解决:

    第一种:反编译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-当前应该是红色虫子状态

android 动态调试 防止 动态调试so_apache_02

运行: jdb -connect com.sun.jdi.SocketAttach:port=8700,hostname=localhost

运行结束后,红色虫子应该会变绿,代表此时程序可调式。

android 动态调试 防止 动态调试so_apache_03

3.6 另外打开一个IDA窗口,打开目标so,静态分析,找到目标函数的起始地址

android 动态调试 防止 动态调试so_java_04

 观察发现相对地址为:000011A8,同时记录一下jniOnLoad的相对地址:00001B9C,后面可能会用到。

3.7 切换到已经附加进程的IDA界面

3.7.1 查看输出面板,是否已经加载到 目标so

android 动态调试 防止 动态调试so_android_05

3.7.2 如果没有,使用 F9 使程序继续加载

android 动态调试 防止 动态调试so_android 动态调试 防止_06

3.7.3 如已经加载到目标so,使用 CTRL + S,打开 程序已经加载的so窗口

3.7.4 使用 CTRL + F 快速搜索目标so名称,找到第一条就是在内存空间中加载此so的起始地址:75904000,如下图所示:

android 动态调试 防止 动态调试so_加载_07

3.7.5 结合上一步分析的目标函数的相对地址(000011A8),两者相加得出此次目标函数的起始地址为:759051A8,使用 G 打开 jump to address窗口,输入地址,即可到达目标函数,添加断点。

4. 遇到问题

4.1 目标函数断点不到

    首先考虑目标是否做了apk反调试功能,然后断点到jniOnLoad函数,使用 F8 单步调试,查看具体哪一步挂掉了。一般都是BLX or BL (函数调用的意思),发现在jniOnLoad中进行到如下步骤时,崩溃了:

android 动态调试 防止 动态调试so_apache_08

     双击进去发现是 pthread_create(创建线程),猜测是开启一个线程,在里面做了反调试的功能,如下图:

android 动态调试 防止 动态调试so_加载_09

     解决办法:

    第一种:修改汇编指令,跳过该函数

    第二种:hook pthread_create 方法,跳过该函数,不创建线程

    这里使用了方法一,断点调试将要运行到 BLX    R7的时候,在要修改的指令处右键,keypatch-Patcher,修改为nop,然后patch即可,如下图所示:

android 动态调试 防止 动态调试so_apache_10