2014-阿里移动安全挑战赛第二题,考察IDA动态调试、反调试绕过、修改so汇编代码、kill挂载应用等能力;

样本:https://github.com/Go0s/APKSample/blob/master/2014-阿里移动安全挑战赛/AliCrackme_2.apk

apk工具:https://github.com/Go0s/BombAPK

基本逻辑

1、安装应用,观察应用交互逻辑;

224492949.png

有个输入框及登陆按钮,输入错误弹窗提示;

2、使用jadx反编译查看伪java代码,发现只是简单的将输入内容传入crackme so库的securityCheck()方法,若返回正确则跳转正确页面,否则弹窗失败;

3564225164.png

3、使用IDA打开libcrackme.so,找到Java_com_yaotong_crackme_MainActivity_securityCheck函数段,F5查看伪C代码,发现程序将输入内容获取到v5变量中,同时与off_628C位置的v6变量值进行比较,不相同则直接break循环,验证了反编译的java代码;

同时发现比较前调用了_android_log_print()敏感函数,用于打log;

2992403003.png

4、双击off_628C进入此位置,发现字符串“wojiushidaan”,输入后弹窗失败。可见其v6值在此之前进行了改变,怀疑在.init_array或JNI_OnLoad时动了手脚;

5、使用IDA进行动态调试此so文件,直接attach附加到进程后F9开始调试会直接跳出动态调试,可见其有反调试功能,要想进行动态调试需要先绕过此反调试机制;

参考了网上各位师傅的writeup,下面记录三种解题方法:①反调试绕过;②kill大法;③巧用日志打印函数。

反调试绕过

IDA动态调试so,参考前一篇博文:https://www.bodkin.ren/index.php/archives/641

1、打开android_server,端口转发,获取应用包和入口,打开DDMS,以调试状态启动应用,打开so文件,在JNI_OnLoad处下断点,配置IDA,Attach附加进程,F9开始调试,jdb恢复进程;

1232476638.png

2、成功在JNI_OnLoad断了下来,F8快速步过到【BLX R7】时IDA退出动态调试,怀疑R7即为反调试程序位置;

1238140987.png

3、重新进入动态调试,到【BLX R7】前使用F7单步步入进入此函数,发现pthread_create敏感字样;

3890404230.png

4、退出动态调试,在JNI_OnLoad处使用“/”斜线将伪C代码显示到汇编注释中,找到BLX R7处伪C代码:v5 = dword_62B4(&v9, 0, sub_16A4, 0, 0);

3585051706.png

5、双击sub_16A4进入此函数代码段,F5查看伪C代码,发现是个while无限循环,反调试检查功能就在sub_130C()中;

1643067831.png

6、思路很清晰了,要想绕过反调试,只需要NOP掉此函数或者直接NOP掉BLX R7跳转,我选择后者;

直接使用keypatch将BLX R7这句代码NOP掉,保存;

875497372.png

7、重新打包签名安装,同时在so文件的Java_com_yaotong_crackme_MainActivity_securityCheck处下断点,重新动态调试,F9运行后随意输入字符串确定,程序停到断点处,查看伪C代码直接定位到v6参数,发现其值为“aiyou,bucuoo”,输入后发现正确为flag;

2159919408.png

kill 大法

kill -19 PID:进程挂起

kill -18 PID:进程恢复

由于v6参数值在加载完内存时就完成了变换,因此可以先让应用挂起,然后在使用IDA Attach附加此程序的进程,以此来躲避程序的反调试检查同时寻找变换后的v6参数值,最后恢复进程;

1、获取应用进程PID,可以通过DDMS直接获取,也可以在终端使用ps | grep crackme来获取;

[email protected]:/ # ps | grep crackme
u0_a158   2555  759   1585176 73308 ffffffff b6ee897c S com.yaotong.crackme

2、在终端中使用kill -19 2555将进程挂起,应用界面呆住了;

3、使用IDA附加此进程:开启android_server,端口转发,打开so文件,附加进程;

在Modules界面Ctrl+f搜索crackme.so,双击进入此库函数库,找到Java_com_yaotong_crackme_MainActivity_securityCheck函数,双击进入此数据段,发现改变后的v6参数值;

1056783226.png

4、关掉IDA,使用kill -18 2555恢复进程;

注意:最好不要直接使用IDA来附加进程,当然也是可以,但附加后寻找flag需要找到指定数据段;

1、打开IDA,Go,Debugger,Attach,Remote ARMLinux/Android Debugger,设置host、port,附加进程;

2、同样定位到crackme.so段的securityCheck函数,但flag并没有展示出来,F5打开伪C页面,找到给参数v6赋值的数据段,双击进入;

1719723518.png

3、其数据段还是个指针,继续双击进入;

7919089.png

4、发现flag;

2113429032.png

5、BTW,碰到这种保存了flag字符串指针的指针形式,双击进入后,如果发现不是指针形式而是单个字符,可以在这个地址上按三下【D键】,将这里的数据格式从字符转化为指针形式。

巧用日志打印函数

最初通过静态分析so,发现在两字符串比较前调用了_android_log_print()函数,其作用是打印log日志。为了验证,先打开DDMS,加上应用过滤条件,看下在输入字符点击确定后应用打印了什么日志;

193089584.png

在应用中每点击一下确定,会打印一条log,tag为“yaotong”,内容为“SecurityCheck Started…”;

1、使用IDA打开so,进行静态分析;

直接定位到Java_com_yaotong_crackme_MainActivity_securityCheck函数段,斜线直接将伪C代码显示在汇编页面中,看到R0,R1,R2分别为_android_log_print()函数的三个参数,最后v6变换后的参数值赋给R2寄存器,那么可以将最后的R2寄存器的内容利用log函数输出;

2933873092.png

即:

.text:0000126C ; 21:   _android_log_print(4, &unk_6304, &unk_636C);
.text:0000126C
.text:0000126C loc_126C                                ; CODE XREF: Java_com_yaotong_crackme_MainActivity_securityCheck+80↑j
.text:0000126C                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1278)
.text:00001270                 ADD     R7, PC, R0      ; _GLOBAL_OFFSET_TABLE_
.text:00001274                 ADD     R0, R6, R7      ; unk_6290
.text:00001278                 ADD     R1, R0, #0x74
.text:0000127C                 ADD     R2, R0, #0xDC
.text:00001280                 MOV     R0, #4
.text:00001284                 BL      __android_log_print

.text:000012A0 ; 23:   v6 = off_628C;
.text:000012A0                 LDR     R1, =(off_628C - 0x5FBC)
.text:000012A4                 LDR     R2, [R1,R7]     ; off_628C ...

2、0x1270处是将GLOBAL_OFFSET_TABLE地址写入R0寄存器,因为取密码所在的off_628C段也会用到所以不能动;能动的只有0x1274-0x1280共16个字节,而R0为固定值#4故也不动,即只能动前12个字节;

0x1274处相对v6变量赋值的代码段的偏移为0x12A0-0x1274=0x2C,即需要0x12A0处的偏移加上0x2C构成总体偏移。使用capstone将0x12A0处机器码反汇编ldr r1, [pc, #0x60],0x60+0x2C=0x8C即ldr r1, [pc, #0x8c]为v6变量值相对GLOBAL_OFFSET_TABLE的总体偏移,由于R1寄存器在下面会使用,为了程序段间调用不混乱,将此R1寄存器改为R6寄存器,即ldr r6, [pc, #0x8c]

由于不知道log打印函数的tag和内content分别使用的哪个寄存器,为了方便直接将R1和R2的内容替换成off_628c指向的v6变量值,即0x1278-0x127C替换为LDR R1, [R6,R7]和LDR R2, [R6,R7]

由于IDA无法直接patch ARM,需要使用keypatch来打补丁,最终patch如下:

4266878098.png

3、应用保存补丁后的so,重新打包签名安装,打开DDMS查看log,发现成功打印出flag;

686319732.png

Reference

1、https://www.52pojie.cn/forum.php?mod=viewthread&tid=559205

2、http://www.blogfshare.com/ailibaba-crack02.html

3、https://bbs.pediy.com/thread-197119.htm