栗子:阿里移动安全挑战赛第二题

本栗反调试方法:
用fopen打开/proc/<pid>/status文件读取其中的TracerPid值来检测自己的进程是否被attach,
TracerPid如果为0说明没有别的进程在调试这个进程,如果不为0说明有程序在调试。

使用ida附加进程,关键函数下断点F9之后,进程退出,ida退出.
调试过后发现JNI_OnLoad中有反调试方法(实际上是前辈的结论 :p).

根据使用ida调试so文件的步骤在library load处断下.

验证

打开Debugger windows -> Module list,找到/data/app-lib/com.yaotong.crackme-1/libcrackme.so,为其中的JNI_OnLoad方法右键Add breakpoint.这里我们也可以得到libcrackme.so的基址为0x520AE000.
(当然如果反调试方法是在.initarray中,其地址需要手动计算.)

一步一步F8发现在执行这一句之后程序退出.

F7进去,发现是pthread_create函数.

所以上上图中的unk_520AF6A4函数就是线程回调函数咯.

先不去分析(其实是水平达不到 :( ),所以我们要用守株待兔的方法.
在module list中找到libc.so,在fopen函数下断点,以此检验程序是否打开了某些文件检测状态值.
同时打开一个hex view(View -> Open subviews -> Hex dump),设置数据与R0同步.

F9运行到fopen函数处(有时并不能一次就断在fopen,注意看注释).

所以程序确实是读取了status文件. :P
这是需要找到是谁调用了fopen函数,然后修改读取status的返回值为0(不然就露馅了).
一直摁F8直到fopen函数返回,于是我们来到了这里.

一边是数据一边是代码,完全找不到函数开始的地方哇.
但是我们知道此处地址为520AF420,减去我们之前记下的libcrackme.so的基址520AE000,得到相对地址为1420
另开一个ida,摁G键跳至此地址,F5发现是函数sub_130c.

sub_130c的文件偏移转换为绝对地址:130c+520AE000=520AF30C.
p键将此处数据视为代码变成这样:

程序随后会用fgets()和strstr()来获取TracerId的值,于是我们在strstr()处下个断点,然后让hex view的数据与R0同步.
一直F9,直到R0的值变为这样:

F2修改为这样:

但是过一会程序又要获取,我们得一直不停的改,所以我们需要将这段代码nop掉,世界才安静.

patch so

如果静态分析时找到JNI_OnLoad中关键的那句代码:

进入线程的回调函数,发现竟然是这样子的!

回调函数sub_16a4中调用了sub_130c!
跟我们在动态调试时看到的完全不一样嘛.
现在将回调函数中的这一句nop掉就好了呗.
右键选择copy to assembly

我们的目标是BL sub_130C. arm中没有nop语句,所以将movs r0,r0作为NOP,对应的机器码为00 00 A0 E1.

右键进入hex view.

摁F2将13 FF FF EB改为00 00 A0 E1,再摁F2应用.
点击Edit -> Patch program -> Apply patches to input file...保存即可.

将原apk中的so文件替换为此so文件,重新打包签名安装,现在直接打开app调试就不会退出了. Success!


番外

ida中按三下D,将数据格式从字符转化为指针形式.

Reference

安卓动态调试七种武器之孔雀翎 – Ida Pro