本文主要记录如何通过IDA动态调试SO层存在反调试的APK。
前言
最近在实践文章[原创]分析一个安卓简单CrackMe的过程中,当IDADebugger->attach
进程后,APP就崩溃退出了。后来作者回复说是因为JNI_OnLoad
中存在反调试。
自己查阅了相关资料,其中定位反调试位置主要参考[原创]Android APP漏洞之战(10)——调试与反调试详解中的so层过反调。
本机环境:MacOS Catalina、IDA Pro 7.0、pixel xl android 8.1.0
定位反调试
启动
android_server
1
2
3
4
5
6
7adb push android_server /data/local/tmp/android_server
adb shell
su
cd /data/local/tmp
mv android_server as
chmod +x as
./as -p12344端口转发
1
adb forward tcp:12344 tcp:12344
挂起程序,此时处于
Waiting for Debugger
状态1
adb shell am start -D -n com.yaotong.crackme/.MainActivity
通过Android sdk工具
Android Device Monitor
查看端口1
2cd ~/Library/Android/sdk/tools/
./monitor打开IDA,点击菜单栏
Debugger->attach->Remote ARMLinux/Android debugger
。Hostname
填写localhost
代表本机,Port
填写12344。点击Debug options
,勾选Suspend on process entry point
、Suspend on thread start/exit
、Suspend on library load/unload
三项,点击OK。attach
进程com.yaotong.crackme
,点击OK。点击菜单栏Debugger-Debugger options
,再次确认勾选Suspend on process entry point
、Suspend on thread start/exit
、Suspend on library load/unload
。不然后续调试会出现异常退出的情况。jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8602
,其中端口为步骤4的端口。按F9调试,同时查看
Output indow
中目标SOlibcrackme.so
是否加载。当目标SO加载后,在Path
窗口搜索JNI_OnLoad
函数,下断点,进行单步调试。定位具体反调试位置。以下来自文章[原创]Android APP漏洞之战(10)——调试与反调试详解:下面使用F8开始单步调试了,发现每次到达BLX R7这条指令执行完之后,JNI_OnLoad函数就退出了,这个地方存在问题,可能就是反调试的地方了。我们再次进入调试,看见BLX跳转的地方R7寄存器中是pthread_create函数,到了这里我们就找到了出问题的地方,接下来我们只需要修改对应的位置就可以了。可以把BLX R7这条指令给nop掉,也就是把这条指令变成空指令(相当于删除这条指令)这样apk就不会新建线程去执行检测代码了。
Patch program
定位到反调试的位置后,有两种方式可以nop掉指令。
Hex View
视图右键Edit
修改成00 00 00 00
,然后再右键选择Apply changes
。- 菜单栏
Edit->Patch program->Patch Bytes
,然后同目录再选择Apply patchs to input file
保存更改。
这样就过掉一个简单的反调试了。
回编译
主要使用工具apktool
,官网:https://ibotpeaches.github.io/Apktool/
反编译
1
apktool d com.yaotong.crackme.apk -o com.yaotong.crackme
将上一步patch掉反调试的SO放到com.yaotong.crackme/lib
目录下再进行回编译。
回编译
1
apktool b com.yaotong.crackme -o com.yaotong.crackme_unsigned.apk
签名
要想把上一步回编译好的apk安装到手机上,还需要一步:签名。
生成
keystore
证书1
keytool -genkey -v -keystore myApp.keystore -alias myApp.keystore -keyalg RSA -validity 3000
签名
1
jarsigner -verbose -keystore myApp.keystore -signedjar com.yaotong.crackme_signed.apk com.yaotong.crackme_unsigned.apk myApp.keystore
具体参数选项含义可以通过keytool -genkey -h
、jarsigner -h
来查看。
经过patch掉反调试以及重新签名的apk就可以愉快地进行IDA动态调试了。
其它知识
反调试检测
现在很多应用防止别的进程调试或者注入,通常会自我检测。原理就是:循环检测/proc/[mypid]/status文件,查看TracerPid是否为0,如果不为0,表示被其它进程trace了。那么这时候就直接退出程序。因为现在的IDA调试时需要进程的注入。进程注入现在都是使用Linux中的ptrace机制。那么这里的TracerPid就可以记录trace的pid,就可以发现程序被哪个进程注入了。或者是被它调试,进而采取一些措施。
IDA调试的整体原理
首先需要在被调试端放一个程序,用于IDA端和调试设备通信,这个程序就是android_server
。因为要附加进程,所以这个程序必须要用root身份运行。这个程序起来之后,会开一个端口23946(默认端口)。使用adb forward进行端口转发到远程调试端,这时候IDA就可以和调试端的android_server
进行通信了。后面获取设备的进程列表、附加进程、传递调试信息,都可以使用这个通信机制完成。IDA可以获取被调试进程的内存数据,一般是在/proc/[pid]/maps
文件中,所以在使用ctrl+s
可以查看所有SO文件的基地址,可以遍历maps文件即可做到。
参考文章: