2015ali android挑战赛第二题之反调试技术解析

part 3 反调试原理分析

承接上一篇博文,破解时遗留了两个问题,一个是静态密码什么时候被修改的,一个是反调试部分内容。个人觉得反调试部分内容比较关键,所以就来扒一扒其背后的秘密。这里主要以汇编代码分析为主,结合动态调试,我的目标是训练阅读arm代码,提高效率也是我们值得花时间的。

还是从JNI_OnLoad入手分析,OnLoad函数主要做2件事情:

1、动态解密dlsym、getpid、sprintf、fopen、fgets、strstr、sscanf、kill、sleep、pthread_create、mprotect、cacheflush、lrand48等api的字符串,并调用dlsym函数,获取上诉api的地址,然后填写got(GLOBAL_OFFSET_TABLE)表。

.text:00001BDC                 ADD     R7, R5, #4
.text:00001BE0                 MOV     R6, #0
.text:00001BE4
.text:00001BE4 loc_1BE4                                ; CODE XREF: JNI_OnLoad+5Cj
.text:00001BE4                 LDR     R0, [R7,R6,LSL#2] ; 取得函数指针
.text:00001BE8                 BLX     R0 ; _GLOBAL_OFFSET_TABLE_ ; 调用initIATTable函数 解密字符串,初始化got表函数,函数调用完毕后getpid sprintf api地址准备完毕

2、api准备完成之后,调用pthread_create(),启动反调试检测线程。

.text:00001C28                 SUB     R5, SP, #8
.text:00001C2C                 MOV     SP, R5
.text:00001C30                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1C48)
.text:00001C34                 LDR     R1, =(ThreadCallback - 0x5FBC)
.text:00001C38                 MOV     R3, #0
.text:00001C3C                 STR     R8, [R5]
.text:00001C40                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001C44                 ADD     R2, R1, R0 ; ThreadCallback
.text:00001C48                 ADD     R0, R9, R0 ; unk_6290
.text:00001C4C                 MOV     R1, #0          ; name
.text:00001C50                 LDR     R7, [R0,#(pthread_create_IAT - 0x6290)]
.text:00001C54                 SUB     R0, R11, #-handle ; handle
.text:00001C58                 BLX R7 ;  pthread_create(r0,r1,r2);启动反调试线程

下面是Jni_OnLoad函数代码

.text:00001B9C JNI_OnLoad
.text:00001B9C
.text:00001B9C handle          = -0x20
.text:00001B9C
.text:00001B9C                 STMFD   SP!, {R4-R9,R11,LR}
.text:00001BA0                 ADD     R11, SP, #0x18
.text:00001BA4                 SUB     SP, SP, #8
.text:00001BA8                 MOV     R4, R0
.text:00001BAC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1BC0)
.text:00001BB0                 LDR     R9, =(unk_6290 - 0x5FBC)
.text:00001BB4                 MOV     R8, #0
.text:00001BB8                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001BBC                 ADD     R0, R9, R0 ; unk_6290;获得got表中的unk_6290函数地址
这里相当于got表的一个"首地址"
.text:00001BC0                 STR     R8, [R0,#(dword_62C8 - 0x6290)];初始化全局变量dword_62C8的值为0
.text:00001BC4                 LDR     R5, [R0,#(dword_62C4 - 0x6290)] ; 
.text:00001BC8                 CMP     R5, #0
.text:00001BCC                 BEQ     loc_1C28 if(!dword_62C4) {}
.text:00001BD0
.text:00001BD0 loc_1BD0                                ; CODE XREF: JNI_OnLoad+74j
.text:00001BD0                 LDR     R0, [R5]
.text:00001BD4                 CMP     R0, #1
.text:00001BD8                 BLT     loc_1BFC
.text:00001BDC                 ADD     R7, R5, #4
.text:00001BE0                 MOV     R6, #0
.text:00001BE4
.text:00001BE4 loc_1BE4                                ; CODE XREF: JNI_OnLoad+5Cj
.text:00001BE4                 LDR     R0, [R7,R6,LSL#2] ; 取得函数指针
.text:00001BE8                 BLX     R0 ; _GLOBAL_OFFSET_TABLE_ ; 调用initIATTable函数 解密字符串,初始化got表函数,函数调用完毕后getpid sprintf api地址准备完毕
.text:00001BEC                 LDR     R0, [R5]
.text:00001BF0                 ADD     R6, R6, #1
.text:00001BF4                 CMP     R6, R0
.text:00001BF8                 BLT     loc_1BE4        ; 
.text:00001BFC
.text:00001BFC loc_1BFC                                ; CODE XREF: JNI_OnLoad+3Cj
.text:00001BFC                 LDR     R6, [R5,#0x2C]
.text:00001C00                 MOV     R0, R5          ; ptr
.text:00001C04                 BL      free_0
.text:00001C08                 MOV     R5, R6
.text:00001C0C                 CMP     R6, #0
.text:00001C10                 BNE     loc_1BD0
.text:00001C14                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1C24)
.text:00001C18                 MOV     R1, #0
.text:00001C1C                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001C20                 ADD     R0, R9, R0 ; unk_6290
.text:00001C24                 STR     R1, [R0,#(dword_62C4 - 0x6290)]
.text:00001C28
.text:00001C28 loc_1C28                                ; CODE XREF: JNI_OnLoad+30j
.text:00001C28                 SUB     R5, SP, #8
.text:00001C2C                 MOV     SP, R5
.text:00001C30                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1C48)
.text:00001C34                 LDR     R1, =(ThreadCallback - 0x5FBC)
.text:00001C38                 MOV     R3, #0
.text:00001C3C                 STR     R8, [R5]
.text:00001C40                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001C44                 ADD     R2, R1, R0 ; ThreadCallback
.text:00001C48                 ADD     R0, R9, R0 ; unk_6290
.text:00001C4C                 MOV     R1, #0          ; name
.text:00001C50                 LDR     R7, [R0,#(pthread_create_IAT - 0x6290)]
.text:00001C54                 SUB     R0, R11, #-handle ; handle
.text:00001C58                 BLX R7 ;  pthread_create(r0,r1,r2);启动反调试线程

initIATTable()函数
主要工作:
1、拿解密key调用DecrptionIATTable,或者DecrptionIATTable1,或者DecrptionIATTable2,DecrptionIATTable3解密出dlsym、getpid、sprintf、fopen、fgets、strstr、sscanf、kill、sleep、pthread_create、mprotect、
cacheflush、lrand48字符串,并标记已经解密过。这里的解密算法用到4个,有些奇葩啊。

如下代码 dlsym的key为”9HbB”,调用DecrptionIATTable ()解密出”dlsym”字符串。isDlsymInited全局变量用来设置已经解密标志位,也就是下次解密前会先判断字符串是否解密,如果解密了,就不用再次解密了。

.text:00001CDC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1CEC)
.text:00001CE0                 LDR     R1, =(encrpty_dlsym - 0x5FBC)
.text:00001CE4                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001CE8                 ADD     R2, R1, R0
.text:00001CEC                 LDR     R1, =(a9hbb - 0x5FBC)key = "9HbB"
.text:00001CF0                 ADD     R4, R5, R0 ; unk_6290
.text:00001CF4                 ADD     R3, R1, R0      ; dlsym的解密key
.text:00001CF8                 ADD     R0, R4, #0x47
.text:00001CFC                 MOV     R1, #6
.text:00001D00                 BL      DecrptionIATTable ;解密算法1
.text:00001D04                 MOV     R0, #1
.text:00001D08                 STRB    R0, [R4,#(isDlsymInited - 0x6290)]

2、调用dlsym(),挨个获取实际的内存地址,并填写到got表中

.text:00001D74                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1D80)
.text:00001D78                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001D7C                 ADD     R6, R5, R0 ; unk_6290
.text:00001D80                 MOV     R0, #0xFFFFFFFF
.text:00001D84                 ADD     R1, R6, #0x5F
.text:00001D88                 BLX     R4 ; _GLOBAL_OFFSET_TABLE_
.text:00001D8C                 STR     R0, [R6,#(getpid_IAT - 0x6290)]存储到getpid_IAT 地址中

initIATTable()函数代码如下

 initIATTable                            ; DATA XREF: sub_2378+10o
.text:00001CA8                                         ; .text:off_2398o
.text:00001CA8
.text:00001CA8 var_20          = -0x20
.text:00001CA8 var_1C          = -0x1C
.text:00001CA8
.text:00001CA8                 STMFD   SP!, {R4-R7,R11,LR}
.text:00001CAC                 SUB     SP, SP, #8
.text:00001CB0                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1CC0)
.text:00001CB4                 LDR     R5, =(unk_6290 - 0x5FBC)
.text:00001CB8                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001CBC                 ADD     R0, R5, R0 ; unk_6290
.text:00001CC0                 LDRB    R0, [R0,#(isDlsymInited - 0x6290)]
.text:00001CC4                 CMP     R0, #0
.text:00001CC8                 BNE     loc_1D0C
.text:00001CCC                 MOV     R1, #4
.text:00001CD0                 MOV     R0, #0xC5
.text:00001CD4                 STR     R1, [SP,#0x20+var_20]
.text:00001CD8                 STR     R0, [SP,#0x20+var_1C]
.text:00001CDC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1CEC)
.text:00001CE0                 LDR     R1, =(encrpty_dlsym - 0x5FBC)
.text:00001CE4                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001CE8                 ADD     R2, R1, R0
.text:00001CEC                 LDR     R1, =(a9hbb - 0x5FBC)
.text:00001CF0                 ADD     R4, R5, R0 ; unk_6290
.text:00001CF4                 ADD     R3, R1, R0      ; dlsym的解密key
.text:00001CF8                 ADD     R0, R4, #0x47
.text:00001CFC                 MOV     R1, #6
.text:00001D00                 BL      DecrptionIATTable ;解密算法1
.text:00001D04                 MOV     R0, #1
.text:00001D08                 STRB    R0, [R4,#(isDlsymInited - 0x6290)]
.text:00001D0C
.text:00001D0C loc_1D0C                                ; CODE XREF: initIATTable+20j
.text:00001D0C                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1D18)
.text:00001D10                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001D14                 ADD     R6, R5, R0 ; unk_6290
.text:00001D18                 MOV     R0, #0xFFFFFFFF ; handle
.text:00001D1C                 ADD     R1, R6, #0x47   ; name
.text:00001D20                 BL      dlsym_0
.text:00001D24                 MOV     R4, R0          ; RO为dlsym的代码地址
.text:00001D28                 LDRB    R0, [R6,#(isGetpidInited - 0x6290)]
.text:00001D2C                 CMP     R0, #0
.text:00001D30                 BNE     loc_1D74
.text:00001D34                 MOV     R1, #2
.text:00001D38                 MOV     R0, #0xD5
.text:00001D3C                 STR     R1, [SP,#0x20+var_20]
.text:00001D40                 STR     R0, [SP,#0x20+var_1C]
.text:00001D44                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1D54)
.text:00001D48                 LDR     R1, =(unk_448B - 0x5FBC)
.text:00001D4C                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001D50                 ADD     R2, R1, R0
.text:00001D54                 LDR     R1, =(aM7 - 0x5FBC)
.text:00001D58                 ADD     R6, R5, R0 ; unk_6290
.text:00001D5C                 ADD     R3, R1, R0      ; getpid的解密key
.text:00001D60                 ADD     R0, R6, #0x5F
.text:00001D64                 MOV     R1, #7
.text:00001D68                 BL      DecrptionIATTable
.text:00001D6C                 MOV     R0, #1
.text:00001D70                 STRB    R0, [R6,#(isGetpidInited - 0x6290)]
.text:00001D74
.text:00001D74 loc_1D74                                ; CODE XREF: initIATTable+88j
.text:00001D74                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1D80)
.text:00001D78                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001D7C                 ADD     R6, R5, R0 ; unk_6290
.text:00001D80                 MOV     R0, #0xFFFFFFFF
.text:00001D84                 ADD     R1, R6, #0x5F
.text:00001D88                 BLX     R4 ; _GLOBAL_OFFSET_TABLE_
.text:00001D8C                 STR     R0, [R6,#(getpid_IAT - 0x6290)]
.text:00001D90                 LDRB    R0, [R6,#(isSprintfInited - 0x6290)]
.text:00001D94                 CMP     R0, #0
.text:00001D98                 BNE     loc_1DDC
.text:00001D9C                 MOV     R1, #4
.text:00001DA0                 MOV     R0, #0xE9
.text:00001DA4                 STR     R1, [SP,#0x20+var_20]
.text:00001DA8                 STR     R0, [SP,#0x20+var_1C]
.text:00001DAC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1DBC)
.text:00001DB0                 LDR     R1, =(unk_44F3 - 0x5FBC)
.text:00001DB4                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001DB8                 ADD     R2, R1, R0
.text:00001DBC                 LDR     R1, =(aLnat - 0x5FBC)
.text:00001DC0                 ADD     R6, R5, R0 ; unk_6290
.text:00001DC4                 ADD     R3, R1, R0      ; 解密sprintf的key
.text:00001DC8                 ADD     R0, R6, #0x7C
.text:00001DCC                 MOV     R1, #8
.text:00001DD0                 BL      DecrptionIATTable1解密算法2
.text:00001DD4                 MOV     R0, #1
.text:00001DD8                 STRB    R0, [R6,#(isSprintfInited - 0x6290)]
.text:00001DDC
.text:00001DDC loc_1DDC                                ; CODE XREF: initIATTable+F0j
.text:00001DDC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1DE8)
.text:00001DE0                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001DE4                 ADD     R6, R5, R0 ; unk_6290
.text:00001DE8                 MOV     R0, #0xFFFFFFFF
.text:00001DEC                 ADD     R1, R6, #0x7C
.text:00001DF0                 BLX     R4 ; _GLOBAL_OFFSET_TABLE_
.text:00001DF4                 STR     R0, [R6,#(sprintf_IAT - 0x6290)]
.text:00001DF8                 LDRB    R0, [R6,#(isFopenInited - 0x6290)]
.text:00001DFC                 CMP     R0, #0
.text:00001E00                 BNE     loc_1E44
.text:00001E04                 MOV     R1, #4
.text:00001E08                 MOV     R0, #0xE7
.text:00001E0C                 STR     R1, [SP,#0x20+var_20]
.text:00001E10                 STR     R0, [SP,#0x20+var_1C]
.text:00001E14                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1E24)
.text:00001E18                 LDR     R1, =(a0WE - 0x5FBC)
.text:00001E1C                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001E20                 ADD     R2, R1, R0      ; "0磢w湧"
.text:00001E24                 LDR     R1, =(aCoxt - 0x5FBC)
.text:00001E28                 ADD     R6, R5, R0 ; unk_6290
.text:00001E2C                 ADD     R3, R1, R0      ; 解密fopen的key
.text:00001E30                 ADD     R0, R6, #0x4D
.text:00001E34                 MOV     R1, #6
.text:00001E38                 BL      DecrptionIATTable1
.text:00001E3C                 MOV     R0, #1
.text:00001E40                 STRB    R0, [R6,#(isFopenInited - 0x6290)]
.text:00001E44
.text:00001E44 loc_1E44                                ; CODE XREF: initIATTable+158j
.text:00001E44                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1E50)
.text:00001E48                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001E4C                 ADD     R6, R5, R0 ; unk_6290
.text:00001E50                 MOV     R0, #0xFFFFFFFF
.text:00001E54                 ADD     R1, R6, #0x4D
.text:00001E58                 BLX     R4 ; _GLOBAL_OFFSET_TABLE_
.text:00001E5C                 STR     R0, [R6,#(fopen_IAT - 0x6290)]
.text:00001E60                 LDRB    R0, [R6,#(isFgetsdInited - 0x6290)]
.text:00001E64                 CMP     R0, #0
.text:00001E68                 BNE     loc_1EA4
.text:00001E6C                 MOV     R6, #1
.text:00001E70                 MOV     R0, #3
.text:00001E74                 LDR     R1, =(aIU - 0x5FBC)
.text:00001E78                 STMEA   SP, {R0,R6}
.text:00001E7C                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1E88)
.text:00001E80                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001E84                 ADD     R2, R1, R0      ; "┑阜罸"
.text:00001E88                 LDR     R1, =(aBmt - 0x5FBC)
.text:00001E8C                 ADD     R7, R5, R0 ; unk_6290
.text:00001E90                 ADD     R3, R1, R0      ; 解密fgets的key
.text:00001E94                 ADD     R0, R7, #0x53
.text:00001E98                 MOV     R1, #6
.text:00001E9C                 BL      DecrptionIATTable
.text:00001EA0                 STRB    R6, [R7,#(isFgetsdInited - 0x6290)]
......
.text:000021D8                 ADD     SP, SP, #8
.text:000021DC                 LDMFD   SP!, {R4-R7,R11,PC}

上诉代码执行完成后got表内容如下:
这里写图片描述

前面准备工作完成后,就开始调用pthread_create(),函数体在ThreadCallback中。
上篇文件还原过这里的汇编代码,就一个while循环,一个调用反调试检测(AnitDeubging)代码,另外一个是sleep(3),线程休眠3秒钟。

.text:000016A4 ThreadCallback                          ; DATA XREF: JNI_OnLoad+A8o
.text:000016A4                                         ; .text:off_1CA4o
.text:000016A4                 STMFD   SP!, {R4,LR}
.text:000016A8                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x16B8)
.text:000016AC                 LDR     R1, =(unk_6290 - 0x5FBC)
.text:000016B0                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:000016B4                 ADD     R4, R1, R0 ; unk_6290
.text:000016B8
.text:000016B8 loc_16B8                                ; CODE XREF: ThreadCallback+24j
.text:000016B8                 BL      AnitDeubging
.text:000016BC                 LDR     R1, [R4,#(sleep_IAT - 0x6290)]
.text:000016C0                 MOV     R0, #3
.text:000016C4                 BLX     R1              ; 调用sleep(3),休眠3秒钟
.text:000016C8                 B       loc_16B8

线程函数c代码还原

while(true){
     AnitDeubging();
     sleep(3);//调用函数 
}

接下来是重点了,我们马上要解开反调试秘密了
主要工作:
1、fopen(“/proc/pid/status”)
2、循环fgets()读取文件内容
3、调用strstr定位”TracerPid”,获取后面的TracerPid的值
4、TracerPid为 0,代表程序没有被调试,大于1,则代表有进程附加了。
5、当TracerPid不为0,执行kill(myself_pid),程序退出

调试器检测业务逻辑1

调用getpid api,获得当前app的进程id,紧接着判断”/proc/%d/status”是否已经解密,没有解密,则调用解密函数动态解密,并设置已经解密标志位。

.text:0000130C                 STMFD   SP!, {R4-R11,LR}
.text:00001310                 SUB     SP, SP, #0x324
.text:00001314                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1328) ; 动态调试变成了0xF3CA6328
.text:00001318                 MOV     R1, #0x64
.text:0000131C                 MOV     R2, #0
.text:00001320                 ADD     R4, PC, R0 ; _GLOBAL_OFFSET_TABLE_ ; got表的一个"首地址"
.text:00001324                 LDR     R0, =(__stack_chk_guard_ptr - 0x5FBC)
.text:00001328                 LDR     R0, [R0,R4] ; __stack_chk_guard
.text:0000132C                 LDR     R0, [R0]
.text:00001330                 STR     R0, [SP,#0x348+checkStack] ; 将chk_guard指向的内容,存入当前栈中
.text:00001334                 ADD     R0, SP, #0x348+lpstrArry
.text:00001338                 BL      __aeabi_memset_0 ; 调用 memset 初始化100大小的堆栈空间
.text:0000133C                 LDR     R7, =(unk_6290 - 0x5FBC)
.text:00001340                 ADD     R5, R7, R4 ; unk_6290
.text:00001344                 LDR     R0, [R5,#(getpid_IAT - 0x6290)] ; 动态调试发现这里是getpid的地址(导入表 0xF7010D1B)
.text:00001348                 BLX     R0              ; call getpid,返回当前进程的pid
.text:0000134C                 MOV     R8, R0
.text:00001350                 LDR     R4, [R5,#(sprintf_IAT - 0x6290)] ; sprintf(0xF703E34B)
.text:00001354                 LDRB    R0, [R5,#(byte_635B - 0x6290)]
.text:00001358                 CMP     R0, #0          ; byte_635b内容是否为1
.text:0000135C                 BNE     loc_13A0        ; if(byte_635b == 0){
.text:0000135C                                         ; }
.text:00001360                 MOV     R1, #4
.text:00001364                 MOV     R0, #0x57
.text:00001368                 STR     R1, [SP,#0x348+local1]
.text:0000136C                 STR     R0, [SP,#0x348+local2]
.text:00001370                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1380)
.text:00001374                 LDR     R1, =(dword_4550 - 0x5FBC)
.text:00001378                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:0000137C                 ADD     R2, R1, R0 ; dword_4550
.text:00001380                 LDR     R1, =(aSL - 0x5FBC)
.text:00001384                 ADD     R5, R7, R0 ; unk_6290
.text:00001388                 ADD     R3, R1, R0      ; "s!#L"
.text:0000138C                 ADD     R0, R5, #0xB9
.text:00001390                 MOV     R1, #0x10
.text:00001394                 BL      DecrptionIATTable1 ; 解密字符串"/proc/%d/status"
.text:00001398                 MOV     R0, #1          ; 设置全局变量 byte_635b 标志位为1
.text:0000139C                 STRB    R0, [R5,#(byte_635B - 0x6290)]

调试器检测业务逻辑2

调用sprintf(“/proc/%d/status”,PID),构建当前app进程的状态文件全路径字符串。紧接着解密
“TracerPid”字符串,并设置已经解密过标志位

.text:000013A0 loc_13A0                                ; CODE XREF: AnitDeubging+50j
.text:000013A0                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x13B0)
.text:000013A4                 MOV     R2, R8
.text:000013A8                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:000013AC                 ADD     R6, R7, R0 ; unk_6290
.text:000013B0                 ADD     R0, SP, #0x348+lpstrArry
.text:000013B4                 ADD     R1, R6, #0xB9
.text:000013B8                 BLX     R4              ; call sprintf(lpstrArry,"/proc/%d/status",r2)
.text:000013BC                 LDR     R5, [R6,#(fopen_IAT - 0x6290)]
.text:000013C0                 LDRB    R0, [R6,#(byte_635C - 0x6290)]
.text:000013C4                 CMP     R0, #0
.text:000013C8                 BNE     loc_1404
.text:000013CC                 MOV     R6, #1
.text:000013D0                 MOV     R0, #0
.text:000013D4                 LDR     R1, =(unk_454A - 0x5FBC)
.text:000013D8                 STMEA   SP, {R0,R6}
.text:000013DC                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x13E8)
.text:000013E0                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:000013E4                 ADD     R2, R1, R0 ; unk_454A
.text:000013E8                 LDR     R1, =(unk_44FC - 0x5FBC)
.text:000013EC                 ADD     R4, R7, R0 ; unk_6290
.text:000013F0                 ADD     R3, R1, R0 ; unk_44FC
.text:000013F4                 MOV     R0, R4
.text:000013F8                 MOV     R1, #2
.text:000013FC                 BL      DecrptionIATTable2 ; 解密TracerPid字符串
.text:00001400                 STRB    R6, [R4,#(byte_635C - 0x6290)]
.text:00001404
.text:00001404 loc_1404

调试器检测业务逻辑3

调用fopen打开”/proc/34352/status”文件,并读取0x200大小的内容.

.text:00001404 loc_1404                                ; CODE XREF: AnitDeubging+BCj
.text:00001404                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1414)
.text:00001408                 STR     R8, [SP,#0x348+var_338]
.text:0000140C                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001410                 ADD     R4, R7, R0 ; unk_6290
.text:00001414                 ADD     R0, SP, #0x348+lpstrArry
.text:00001418                 MOV     R1, R4
.text:0000141C                 BLX     R5              ; fopen("/proc/34352/status")
.text:00001420                 ADD     R6, SP, #0x348+var_28C
.text:00001424                 MOV     R5, R0          ; 函数返回结果
.text:00001428                 MOV     R1, #0x200
.text:0000142C                 MOV     R2, #0
.text:00001430                 MOV     R0, R6
.text:00001434                 STR     R5, [SP,#0x348+statusFileFd]
.text:00001438                 BL      __aeabi_memset_0 ; memset(local_28c,0x200,0);感觉这里应该是memset(local_28c,0,0x200)才对
.text:0000143C                 LDR     R3, [R4,#(fgets_IAT - 0x6290)]
.text:00001440                 MOV     R0, R6
.text:00001444                 MOV     R1, #0x200
.text:00001448                 MOV     R2, R5
.text:0000144C
.text:0000144C fgets__                                 ; fgets
.text:0000144C                 BLX     R3
.text:00001450                 MOV     R6, R7          ; r6 = unk_6290
.text:00001454                 CMP     R0, #0          ; 比较结果是否为0
.text:00001458                 BEQ     loc_161C        ; 从跳转来看,不跳转执行了很多代码,不得不怀疑这里到关键点了

调试器检测 业务逻辑4

循环调用fgets读取status文件内容,然后调用strstr(“Name:.yaotong.crackme”,”TracerPid”)查找子串,如果每次查找到,继续读取status文件内容,直到文件末尾。如果查找到TracerPid,则取出”TracerPid: 13654”中的进程ID,这里实际上是./android_server进程的id.判断进程ID是否大于等于1,大于1走 退出程序流程

.text:000014F8 loc_14F8                                ; CODE XREF: AnitDeubging+2ECj
.text:000014F8                 LDR     R5, [R8,#(strstr_IAT - 0x6290)]
.text:000014FC                 LDRB    R0, [R8,#(byte_635D - 0x6290)]
.text:00001500                 CMP     R0, #0
.text:00001504                 BNE     loc_154C        ; 解密"%s,%d"字符串,如果解密过,则跳过
.text:00001508                 MOV     R0, #2
.text:0000150C                 LDR     R2, [SP,#0x348+var_324]
.text:00001510                 LDR     R3, [SP,#0x348+var_328]
.text:00001514                 MOV     R11, R4
.text:00001518                 MOV     R4, R6
.text:0000151C                 MOV     R1, #0xA
.text:00001520                 STR     R0, [SP,#0x348+local1]
.text:00001524                 MOV     R0, #0x9D
.text:00001528                 STR     R0, [SP,#0x348+local2]
.text:0000152C                 LDR     R0, [SP,#0x348+var_320]
.text:00001530                 ADD     R6, R4, R0
.text:00001534                 ADD     R0, R6, #0x95
.text:00001538                 BL      DecrptionIATTable
.text:0000153C                 MOV     R0, #1
.text:00001540                 STRB    R0, [R6,#0xCD]
.text:00001544                 MOV     R6, R4
.text:00001548                 MOV     R4, R11
.text:0000154C
.text:0000154C loc_154C                                ; CODE XREF: AnitDeubging+1F8j
.text:0000154C                 MOV     R0, R9
.text:00001550                 MOV     R1, R7
.text:00001554                 BLX     R5              ; example strstr("Name:.yaotong.crackme","TracerPid")
.text:00001558                 CMP     R0, #0          ; if(r0 != 0){}
.text:0000155C                 BEQ     loc_15DC        ; 查找TracerPid字符串
.text:00001560                 MOV     R0, R4
.text:00001564                 MOV     R1, #0x80
.text:00001568                 MOV     R2, #0
.text:0000156C                 BL      __aeabi_memset_0
.text:00001570                 MOV     R0, #0
.text:00001574                 STR     R0, [SP,#0x348+local_key]
.text:00001578                 LDR     R11, [R10,#(sscanf_IAT - 0x6290)]
.text:0000157C                 LDRB    R0, [R10,#(byte_635E - 0x6290)]
.text:00001580                 CMP     R0, #0
.text:00001584                 BNE     loc_15BC
.text:00001588                 MOV     R0, #3
.text:0000158C                 LDR     R2, [SP,#0x348+var_330]
.text:00001590                 LDR     R3, [SP,#0x348+var_334]
.text:00001594                 MOV     R1, #6
.text:00001598                 STR     R0, [SP,#0x348+local1]
.text:0000159C                 MOV     R0, #0x93
.text:000015A0                 STR     R0, [SP,#0x348+local2]
.text:000015A4                 LDR     R0, [SP,#0x348+var_32C]
.text:000015A8                 ADD     R5, R6, R0
.text:000015AC                 ADD     R0, R5, #0x41
.text:000015B0                 BL      DecrptionIATTable1 ; 解密字符串
.text:000015B4                 MOV     R0, #1
.text:000015B8                 STRB    R0, [R5,#0xCE]
.text:000015BC
.text:000015BC loc_15BC                                ; CODE XREF: AnitDeubging+278j
.text:000015BC                 LDR     R1, [SP,#0x348+var_31C]
.text:000015C0                 MOV     R0, R9
.text:000015C4                 MOV     R2, R4
.text:000015C8                 ADD     R3, SP, #0x348+local_key
.text:000015CC                 BLX     R11             ; 3个参数函数
.text:000015D0                 LDR     R0, [SP,#0x348+local_key]
.text:000015D4                 CMP     R0, #1          ; TracerPid :(./android_server)
.text:000015D8                 BGE     loc_1600        ; TracerPid >= 1 说明正在被调试
.text:000015DC
.text:000015DC loc_15DC                                ; CODE XREF: AnitDeubging+250j
.text:000015DC                 LDR     R0, [SP,#0x348+fgetsOffsetAddr] ; r0 < 1 继续执行
.text:000015E0                 LDR     R2, [SP,#0x348+statusFileFd]
.text:000015E4                 MOV     R1, #0x200
.text:000015E8                 LDR     R3, [R0,#0x10]
.text:000015EC                 MOV     R0, R9          ; szBuffer
.text:000015F0                 BLX     R3              ; fgets()
.text:000015F4                 CMP     R0, #0          ; 结果不等于 0 继续循环,否则跳出循环
.text:000015F8                 BNE     loc_14F8
.text:000015FC                 B       loc_161C

调试器检测业务逻辑5

如果TracerPid 的值大于等于1,执行kill(pid),退出自身程序,如果等于0,则正常结束流程,等待下一次检测。

.text:00001600 loc_1600                                ; CODE XREF: AnitDeubging+2CCj
.text:00001600                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x1610)
.text:00001604                 MOV     R1, #9
.text:00001608                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:0000160C                 ADD     R0, R6, R0
.text:00001610                 LDR     R2, [R0,#gotOffsetOfKill]
.text:00001614                 LDR     R0, [SP,#0x348+var_338]
.text:00001618                 BLX     R2 ; __imp___stack_chk_fail ; 实际上调用的是kill(pid)函数
.text:0000161C ; ---------------------------------------------------------------------------
.text:0000161C
.text:0000161C loc_161C                                ; CODE XREF: AnitDeubging+14Cj
.text:0000161C                                         ; AnitDeubging+2F0j
.text:0000161C                 LDR     R0, =(_GLOBAL_OFFSET_TABLE_ - 0x162C)
.text:00001620                 LDR     R1, =(__stack_chk_guard_ptr - 0x5FBC)
.text:00001624                 ADD     R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00001628                 LDR     R0, [R1,R0] ; __stack_chk_guard
.text:0000162C                 LDR     R0, [R0]
.text:00001630                 LDR     R1, [SP,#0x348+checkStack]
.text:00001634                 SUBS    R0, R0, R1
.text:00001638                 ADDEQ   SP, SP, #0x324
.text:0000163C                 LDMEQFD SP!, {R4-R11,PC}
.text:00001640                 BL      __stack_chk_fail_0

程序在调试时,我们来看看crackme的status文件的内容
cat /proc/pid/status
注意图中的第6行TracerPid: 16364

接着验证一下16364到底是哪个进程
./android_server
图中第3行,PID 16364,正是android_server进程。

反调试技术小结

crackme通过循环读取”/proc/self/status”文件中的”TracerPid”的值,如果值大于1则说明进程被附加了,也就是有程序在调试本app,然后使用kill(self_pid)发送杀死信号给系统,让系统结束掉app的进程。

技术总结

从整个so库代码上来看,crackme的安全编程思想应用的很到位,字符串都是加密保存的,用的时候才解密,这个静态分析带来一定的困啦,解密算法也不只一个,而且解密算法,感觉带混淆。不难猜测破解密码也是加密保存的,运行时再解密,这里我就不分析了,感兴趣的同学可以去分析一下。

发布了4 篇原创文章 · 获赞 0 · 访问量 3619
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览