工具与环境
Xposed
IDA 6.8
JEB 2.2.5
Fiddler2
010Editor
NEXUS 5 Android 4.4
好久不玩逆向怕调试器生锈,拿出来磨磨!
高手莫要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!
在我们拿到一个APP准备破解时一般得安装运行,程序运行后须要注册用户,随便注册一个用户登录,以下是APP须要购买vip才能使用的大概情况。
通过简单的查看可以知道是要通过网络支付后才能使用VIP,可以使用Fiddler进行抓包分析,但是请求体与返回值都是加密了的,目前还看不懂。
用JEB反编译app发现被加固了。
通过上面简单的分析后,该款应用为了防止被破解,主要做了以下几点防护。
利用第三方加固将app加固,网络验证是否为VIP权限。
通过第一部分的介绍,发现软件被加固了,接下来就是要脱掉壳才能更好地分析下去,挂上IDA在JNI_Onload下断点,反调试主要有,获取rtld_db_dlactivity判断是否为空,过反调试将获取到的内容清零就成了、time时间比较,将返回值清零,过/proc/self/status反调试将open函数返回0,过/proc/net/tcp也是将open函数返回0,文件监控不用管,还有一处比较隐藏的反调试raise,直接将函数改成返回。
1 LOAD:75A6A038 F0 41 2D E9 STMFD SP!, {R4-R8,LR}
2 LOAD:75A6A03C F0 81 BD E8 LDMFD SP!, {R4-R8,PC};改成返回
3 LOAD:75A6A040 ; ---------------------------------------------------------------------------
4 LOAD:75A6A040 70 50 9F E5 LDR R5, =(off_75A72ED4 - 0x75A6A05C)
5 LOAD:75A6A044 70 20 9F E5 LDR R2, =(is_androidserver_prot - 0x75A6A058)
6 LOAD:75A6A048 70 60 9F E5 LDR R6, =0xE7F001F0
7 LOAD:75A6A04C 10 D0 4D E2 SUB SP, SP, #0x10
8 LOAD:75A6A050 02 20 8F E0 ADD R2, PC, R2 ; is_androidserver_prot
9 LOAD:75A6A054 05 50 9F E7 LDR R5, [PC,R5] ; off_75A72ED4
10 LOAD:75A6A058 03 30 9F E7 LDR R3, [PC,R3] ; off_75A72ED0
11 LOAD:75A6A05C 04 50 8D E5 STR R5, [SP,#0x10+var_C]
12 LOAD:75A6A060 08 30 8D E5 STR R3, [SP,#0x10+var_8]
13 LOAD:75A6A064 0C 20 8D E5 STR R2, [SP,#0x10+var_4]
14 LOAD:75A6A068 04 70 8D E2 ADD R7, SP, #4
15 LOAD:75A6A06C 0C 80 8D E2 ADD R8, SP, #0x10+var_4
16 LOAD:75A6A070
17 LOAD:75A6A070 loc_75A6A070 ; CODE XREF: Anti_raise+70j
18 LOAD:75A6A070 04 40 45 E2 SUB R4, R5, #4
19 LOAD:75A6A074 24 50 85 E2 ADD R5, R5, #0x24
20 LOAD:75A6A078 01 00 00 EA B loc_75A6A084
21 LOAD:75A6A07C ; ---------------------------------------------------------------------------
22 LOAD:75A6A07C
23 LOAD:75A6A07C loc_75A6A07C ; CODE XREF: Anti_raise+54j
24 LOAD:75A6A07C 05 00 54 E1 CMP R4, R5
25 LOAD:75A6A080 06 00 00 0A BEQ loc_75A6A0A0
26 LOAD:75A6A084
27 LOAD:75A6A084 loc_75A6A084 ; CODE XREF: Anti_raise+40j
28 LOAD:75A6A084 ; Anti_raise+64j
29 LOAD:75A6A084 04 30 B4 E5 LDR R3, [R4,#4]!
30 LOAD:75A6A088 06 00 53 E1 CMP R3, R6
31 LOAD:75A6A08C FA FF FF 1A BNE loc_75A6A07C
32 LOAD:75A6A090 09 00 A0 E3 MOV R0, #9 ; sig
33 LOAD:75A6A094 25 F6 FF EB BL raise
34 LOAD:75A6A098 05 00 54 E1 CMP R4, R5
35 LOAD:75A6A09C F8 FF FF 1A BNE loc_75A6A084
36 LOAD:75A6A0A0
37 LOAD:75A6A0A0 loc_75A6A0A0 ; CODE XREF: Anti_raise+48j
38 LOAD:75A6A0A0 08 00 57 E1 CMP R7, R8
39 LOAD:75A6A0A4 04 50 B7 15 LDRNE R5, [R7,#0xC+var_8]!
40 LOAD:75A6A0A8 F0 FF FF 1A BNE loc_75A6A070
41 LOAD:75A6A0AC 10 D0 8D E2 ADD SP, SP, #0x10
42 LOAD:75A6A0B0 F0 81 BD E8 LDMFD SP!, {R4-R8,PC}
43 LOAD:75A6A0B0 ; End of function Anti_raise
44 LOAD:75A6A0B0
以上就是过掉所有主要的反调试了
过掉反调试后在Case 29 与case33下好断点,代码如下:
1 //case 29 文件偏移:9DB8
2 /data/app-lib/com.txy.anywhere-1/libjiagu_old.so 75A66000 00050000
3 LOAD:75A6FDB8
4 LOAD:75A6FDB8 loc_75A6FDB8 ; CODE XREF: __fun_a_18(char *,uint):loc_75A6F8CCj
5 LOAD:75A6FDB8 ; __fun_a_18(char *,uint):loc_75A6F8DCj ...
6 LOAD:75A6FDB8 01 30 8C E2 ADD R3, R12, #1 ; jumptable 75A6F8DC case 29
7 LOAD:75A6FDBC 03 20 D4 E7 LDRB R2, [R4,R3]
8 LOAD:75A6FDC0 1C 30 8D E5 STR R3, [SP,#0xCC+var_B0]
9 LOAD:75A6FDC4 00 00 52 E3 CMP R2, #0
10 LOAD:75A6FDC8 C9 01 00 0A BEQ loc_75A704F4
11 LOAD:75A6FDCC 80 05 95 E9 LDMIB R5, {R7,R8,R10}
12 LOAD:75A6FDD0 0C 20 84 E0 ADD R2, R4, R12
13 LOAD:75A6FDD4 05 B0 92 E5 LDR R11, [R2,#5]
14 LOAD:75A6FDD8 38 20 95 E5 LDR R2, [R5,#0x38]
15 LOAD:75A6FDDC 03 30 94 E7 LDR R3, [R4,R3]
16 LOAD:75A6FDE0 09 C0 8C E2 ADD R12, R12, #9
17 LOAD:75A6FDE4 1C C0 8D E5 STR R12, [SP,#0xCC+var_B0]
18 LOAD:75A6FDE8 02 B0 8B E0 ADD R11, R11, R2
19 LOAD:75A6FDEC 0B B0 63 E0 RSB R11, R3, R11
20 LOAD:75A6FDF0 00 C0 95 E5 LDR R12, [R5]
21 LOAD:75A6FDF4 0C 00 A0 E1 MOV R0, R12
22 LOAD:75A6FDF8 07 10 A0 E1 MOV R1, R7
23 LOAD:75A6FDFC 08 20 A0 E1 MOV R2, R8
24 LOAD:75A6FE00 0A 30 A0 E1 MOV R3, R10
25 LOAD:75A6FE04 0B E0 A0 E1 MOV LR, R11
26 LOAD:75A6FE08 3E FF 2F E1 BLX LR
27 LOAD:75A6FE0C 00 70 A0 E1 MOV R7, R0
28 LOAD:75A6FE10 01 C0 A0 E1 MOV R12, R1
29 LOAD:75A6FE14 00 70 85 E5 STR R7, [R5] ; jumptable 000098CC case 28
30 LOAD:75A6FE18 04 C0 85 E5 STR R12, [R5,#4]
31 LOAD:75A6FE1C 1C C0 9D E5 LDR R12, [SP,#0xCC+var_B0]
32 LOAD:75A6FE20 A9 FE FF EA B loc_75A6F8CC
1 //case 33 执行解压函数与执行解密后第二个so的JNI_OnLoad函数 文件偏移: 9B44
2 /data/app-lib/com.txy.anywhere-1/libjiagu_old.so 75A66000 00050000
3 LOAD:75A6FB44 loc_75A6FB44 ; CODE XREF: __fun_a_18(char *,uint):loc_75A6F8CCj
4 LOAD:75A6FB44 ; __fun_a_18(char *,uint):loc_75A6F8DCj ...
5 LOAD:75A6FB44 01 30 8C E2 ADD R3, R12, #1 ; jumptable 75A6F8DC case 33
6 LOAD:75A6FB48 03 20 D4 E7 LDRB R2, [R4,R3]
7 LOAD:75A6FB4C 1C 30 8D E5 STR R3, [SP,#0xCC+var_B0]
8 LOAD:75A6FB50 00 00 52 E3 CMP R2, #arg_0
9 LOAD:75A6FB54 66 02 00 0A BEQ loc_75A704F4
10 LOAD:75A6FB58 02 30 8C E2 ADD R3, R12, #2
11 LOAD:75A6FB5C 03 20 94 E7 LDR R2, [R4,R3]
12 LOAD:75A6FB60 1C 30 8D E5 STR R3, [SP,#0xCC+var_B0]
13 LOAD:75A6FB64 06 C0 8C E2 ADD R12, R12, #6
14 LOAD:75A6FB68 02 B1 95 E7 LDR R11, [R5,R2,LSL#2]
15 LOAD:75A6FB6C 1C C0 8D E5 STR R12, [SP,#0xCC+var_B0]
16 LOAD:75A6FB70 80 15 95 E8 LDMIA R5, {R7,R8,R10,R12}
17 LOAD:75A6FB74 07 00 A0 E1 MOV R0, R7
18 LOAD:75A6FB78 08 10 A0 E1 MOV R1, R8
19 LOAD:75A6FB7C 0A 20 A0 E1 MOV R2, R10
20 LOAD:75A6FB80 0C 30 A0 E1 MOV R3, R12
21 LOAD:75A6FB84 0B E0 A0 E1 MOV LR, R11
22 LOAD:75A6FB88 3E FF 2F E1 BLX LR
23 LOAD:75A6FB8C 00 C0 A0 E1 MOV R12, R0 ; jumptable 000098CC case 32
24 LOAD:75A6FB90 00 C0 85 E5 STR R12, [R5]
25 LOAD:75A6FB94 1C C0 9D E5 LDR R12, [SP,#0x50+var_34]
下好断点后一直F9会发现主是逻辑就是获取解压函数(uncompressc)解压第二个so数据,解压后就是解密第二个SO了。接下来就是在内存中加载第二个SO并获取JNI_OnLoad函数。
1 LOAD:401030A8 F0 40 2D E9 STMFD SP!, {R4-R7,LR}
2 LOAD:401030AC C8 40 9F E5 LDR R4, =(off_40109EC0 - 0x401030C0)
3 LOAD:401030B0 9C D0 4D E2 SUB SP, SP, #0x9C
4 LOAD:401030B4 00 70 A0 E1 MOV R7, R0
5 LOAD:401030B8 04 40 9F E7 LDR R4, [PC,R4]
6 LOAD:401030BC 00 30 94 E5 LDR R3, [R4]
7 LOAD:401030C0
8 LOAD:401030C0 loc_401030C0 ; DATA XREF: LOAD:4010317Co
9 LOAD:401030C0 01 60 A0 E1 MOV R6, R1
10 LOAD:401030C4 94 20 A0 E3 MOV R2, #0x94 ; '
11 LOAD:401030C8 00 10 A0 E3 MOV R1, #0
12 LOAD:401030CC 0D 00 A0 E1 MOV R0, SP
13 LOAD:401030D0 94 30 8D E5 STR R3, [SP,#0x94]
14 LOAD:401030D4 B5 ED FF EB BL memset
15 LOAD:401030D8 A0 30 9F E5 LDR R3, =0x6F732E2A
16 LOAD:401030DC 00 20 A0 E3 MOV R2, #0
17 LOAD:401030E0 0D 00 A0 E1 MOV R0, SP
18 LOAD:401030E4 0C 30 8D E5 STR R3, [SP,#0xC]
19 LOAD:401030E8 10 20 CD E5 STRB R2, [SP,#0x10]
20 LOAD:401030EC 00 70 8D E5 STR R7, [SP]
21 LOAD:401030F0 04 60 8D E5 STR R6, [SP,#4]
22 LOAD:401030F4 EB F2 FF EB BL sub_400FFCA8
23 LOAD:401030F8 84 30 9F E5 LDR R3, =(dword_4014D740 - 0x40103108)
24 LOAD:401030FC 84 10 9F E5 LDR R1, =(aMakekey - 0x4010310C)
25 LOAD:40103100 03 30 8F E0 ADD R3, PC, R3
26 LOAD:40103104 01 10 8F E0 ADD R1, PC, R1
27 LOAD:40103108
28 LOAD:40103108 loc_40103108 ; DATA XREF: LOAD:40103184o
29 LOAD:40103108 00 00 83 E5 STR R0, [R3]
30 LOAD:4010310C
31 LOAD:4010310C loc_4010310C ; DATA XREF: LOAD:40103188o
32 LOAD:4010310C F2 F2 FF EB BL my_dlsym
33 LOAD:40103110 00 30 50 E2 SUBS R3, R0, #0
34 LOAD:40103114 08 00 00 0A BEQ loc_4010313C
35 LOAD:40103118 FF 0E C3 E3 BIC R0, R3, #0xFF0
36 LOAD:4010311C 0F 00 C0 E3 BIC R0, R0, #0xF
37 LOAD:40103120 01 1A A0 E3 MOV R1, #0x1000
38 LOAD:40103124 03 20 A0 E3 MOV R2, #3
39 LOAD:40103128 7D 70 A0 E3 MOV R7, #0x7D ; '}'
40 LOAD:4010312C 00 00 00 EF SVC 0
41 LOAD:40103130 54 20 9F E5 LDR R2, =(loc_40102C0C - 0x4010313C)
42 LOAD:40103134 02 20 8F E0 ADD R2, PC, R2
43 LOAD:40103138 00 20 83 E5 STR R2, [R3]
44 LOAD:4010313C
45 LOAD:4010313C loc_4010313C ; DATA XREF: LOAD:4010318Co
46 LOAD:4010313C 4C 30 9F E5 LDR R3, =(dword_4014D740 - 0x4010314C)
47 LOAD:40103140 4C 10 9F E5 LDR R1, =(aJni_onload - 0x40103150)
48 LOAD:40103144 03 30 8F E0 ADD R3, PC, R3
49 LOAD:40103148 01 10 8F E0 ADD R1, PC, R1
50 LOAD:4010314C
51 LOAD:4010314C loc_4010314C ; DATA XREF: LOAD:40103190o
52 LOAD:4010314C 00 00 93 E5 LDR R0, [R3]
53 LOAD:40103150
54 LOAD:40103150 loc_40103150 ; DATA XREF: LOAD:40103194o
55 LOAD:40103150 E1 F2 FF EB BL my_dlsym ; 获取第二个SO的JNI_OnLoad函数
56 LOAD:40103154 94 10 9D E5 LDR R1, [SP,#0x94]
57 LOAD:40103158 38 30 9F E5 LDR R3, =(dword_4014D744 - 0x40103168)
58 LOAD:4010315C 00 20 94 E5 LDR R2, [R4]
59 LOAD:40103160 03 30 8F E0 ADD R3, PC, R3
60 LOAD:40103164 02 00 51 E1 CMP R1, R2
61 LOAD:40103168
62 LOAD:40103168 loc_40103168 ; DATA XREF: LOAD:40103198o
63 LOAD:40103168 00 00 83 E5 STR R0, [R3]
64 LOAD:4010316C 01 00 00 1A BNE loc_40103178
65 LOAD:40103170 9C D0 8D E2 ADD SP, SP, #0x9C
66 LOAD:40103174 F0 80 BD E8 LDMFD SP!, {R4-R7,PC}
67 LOAD:40103178
68 LOAD:40103178 loc_40103178
获取到第二个SO的JNI_OnLoad函数地址后走到Case 33处跳到第二个SO的JNI_OnLoad去执行了,到此第一个SO的主要工作就基本完成了。
第二个SO的JNI_OnLoad主要工作就是注册壳本身的 Native函数与被Native的onCreate函数
1 //注册 Native onCreate 文件偏移 C252
2 debug190 76174000 761DA000 R . X D . byte 00 public CODE 32 00 01
3 debug190:76180252 loc_76180252 ; CODE XREF: debug190:7618028Aj
4 debug190:76180252 ; debug190:76180292j ...
5 debug190:76180252 D7 23 MOVS R3, #0xD7 ; '
6 debug190:76180254 09 98 LDR R0, [SP,#0x24]
7 debug190:76180256 9B 00 LSLS R3, R3, #2
8 debug190:76180258 01 68 LDR R1, [R0]
9 debug190:7618025A CB 58 LDR R3, [R1,R3]
10 debug190:7618025C 21 1C MOVS R1, R4
11 debug190:7618025E 9C 46 MOV R12, R3
12 debug190:76180260 01 23 MOVS R3, #1
13 debug190:76180262 E0 47 BLX R12 ; Native onCreate R2:存放注册信息,签名等
14 debug190:76180264 5B 46 MOV R3, R11
15 debug190:76180266 01 22 MOVS R2, #1
16 debug190:76180268 1B 78 LDRB R3, [R3]
17 debug190:7618026A 1A 42 TST R2, R3
18 debug190:7618026C 00 D0 BEQ loc_76180270
19 debug190:7618026E A7 E1 B loc_761805C0
下面是我通过hook注册函数打印出来的对应类的Native函数,在后面SO劫持会有说:
1 class-Ldalvik/system/DexFile; ->name=getClassNameList address=75FE10F9
2 class-Lcom/qihoo/bugreport/javacrash/CrashReportDataFactory; ->name=interface9 address=7600917D
3 class-Lcom/qihoo/util/StubApp296881940; ->name=mark address=75FF75FD
4 class-Lcom/qihoo/util/StubApp296881940; ->name=mark address=75FFBE45
5 class-Lcom/qihoo/util/StubApp296881940; ->name=mark address=75FFBD91
6 class-Lcom/qihoo/util/StubApp296881940; ->name=interface10 address=75FFE819
7 class-Lcom/qihoo/util/StubApp296881940; ->name=interface5 address=75FE0375
8 class-Lcom/qihoo/util/StubApp296881940; ->name=interface6 address=75FDD95D
9 class-Lcom/qihoo/util/StubApp296881940; ->name=interface7 address=75FE1801
10 class-Lcom/qihoo/util/StubApp296881940; ->name=interface8 address=75FDDCFD
11 class-Lcom/mob/tools/MobUIShell; ->name=onCreate address=75FDF729
12 class-Lcom/txy/anywhere/activity/GreenHandGuideActivity; ->name=onCreate address=75FDF6F1
13 class-Lcom/txy/anywhere/activity/MainActivity; ->name=onCreate address=75FDF6B9
14 class-Lcom/txy/anywhere/activity/PanoramaActivity; ->name=onCreate address=75FDF681
15 class-Lcom/txy/anywhere/activity/move/MoveDetailActivity; ->name=onCreate address=75FDF649
16 class-Lcom/txy/anywhere/activity/move/MoveSettingsMapChoiceActivity; ->name=onCreate address=75FDF611
17 class-Lcom/txy/anywhere/activity/specific/SpecificProgramDetailActivity; ->name=onCreate address=75FDF5D9
18 class-Lcom/txy/anywhere/wxapi/WXPayEntryActivity; ->name=onCreate address=75FDF5A1
19 class-Lcom/common/customp/PullEntry; ->name=getAppkey address=75FE1439
接下来就是解密原始DEX了,解密逻辑如下:
1 //解密dex
2 密钥 A0 EC E6 CC FB A6 F1 A2 EC A3 E7 AF E1 5A EA 2F
3 debug190:761BB0E0 ; ---------------------------------------------------------------------------
4 debug190:761BB0E0 F0 B5 PUSH {R4-R7,LR}
5 debug190:761BB0E2 57 46 MOV R7, R10
6 debug190:761BB0E4 4E 46 MOV R6, R9
7 debug190:761BB0E6 45 46 MOV R5, R8
8 debug190:761BB0E8 E0 B4 PUSH {R5-R7}
9 debug190:761BB0EA 16 1C MOVS R6, R2
10 debug190:761BB0EC 80 22 MOVS R2, #0x80 ; '€'
11 debug190:761BB0EE 80 46 MOV R8, R0
12 debug190:761BB0F0 24 4C LDR R4, =(dword_761DCD20 - 0x761BB0FA)
13 debug190:761BB0F2 C2 B0 SUB SP, SP, #0x108
14 debug190:761BB0F4 1F 1C MOVS R7, R3
15 debug190:761BB0F6 7C 44 ADD R4, PC ; dword_761DCD20
16 debug190:761BB0F8 4A 9B LDR R3, [SP,#0x128]
17 debug190:761BB0FA 24 68 LDR R4, [R4]
18 debug190:761BB0FC 01 AD ADD R5, SP, #4
19 debug190:761BB0FE 99 46 MOV R9, R3
20 debug190:761BB100 23 68 LDR R3, [R4]
21 debug190:761BB102 8A 46 MOV R10, R1
22 debug190:761BB104 28 1C MOVS R0, R5
23 debug190:761BB106 00 21 MOVS R1, #0
24 debug190:761BB108 52 00 LSLS R2, R2, #1
25 debug190:761BB10A 41 93 STR R3, [SP,#0x104]
26 debug190:761BB10C 10 F0 4E FA BL j_j_memset_1
27 debug190:761BB110 43 46 MOV R3, R8
28 debug190:761BB112 59 68 LDR R1, [R3,#4]
29 debug190:761BB114 00 29 CMP R1, #0
30 debug190:761BB116 2D D0 BEQ loc_761BB174
31 debug190:761BB118 28 1C MOVS R0, R5
32 debug190:761BB11A 10 22 MOVS R2, #0x10
33 debug190:761BB11C 00 F0 94 F8 BL Rc4_Key_Init ; A0 EC E6 CC FB A6 F1 A2 EC A3 E7 AF E1 5A EA 2F
34 debug190:761BB120 3B 68 LDR R3, [R7]
35 debug190:761BB122 00 2B CMP R3, #0
36 debug190:761BB124 11 D0 BEQ loc_761BB14A
37 debug190:761BB126 28 1C MOVS R0, R5
38 debug190:761BB128 51 46 MOV R1, R10
39 debug190:761BB12A 32 1C MOVS R2, R6
40 debug190:761BB12C 00 F0 D6 F8 BL Rc4_Dec
41 debug190:761BB130 4B 46 MOV R3, R9
42 debug190:761BB132 00 20 MOVS R0, #0
43 debug190:761BB134 1E 60 STR R6, [R3]
44 debug190:761BB136
45 debug190:761BB136 loc_761BB136 ; CODE XREF: debug190:761BB172j
46 debug190:761BB136 ; debug190:761BB178j ...
47 debug190:761BB136 41 9A LDR R2, [SP,#0x104]
48 debug190:761BB138 23 68 LDR R3, [R4]
49 debug190:761BB13A 9A 42 CMP R2, R3
50 debug190:761BB13C 20 D1 BNE loc_761BB180
51 debug190:761BB13E 42 B0 ADD SP, SP, #0x108
52 debug190:761BB140 1C BC POP {R2-R4}
53 debug190:761BB142 90 46 MOV R8, R2
54 debug190:761BB144 99 46 MOV R9, R3
55 debug190:761BB146 A2 46 MOV R10, R4
56 debug190:761BB148 F0 BD POP {R4-R7,PC}
57 debug190:761BB14A ; ---------------------------------------------------------------------------
58 debug190:761BB14A
59 debug190:761BB14A loc_761BB14A ; CODE XREF: debug190:761BB124j
60 debug190:761BB14A 30 1C MOVS R0, R6
61 debug190:761BB14C 10 F0 96 FA BL malloc_0
62 debug190:761BB150 80 46 MOV R8, R0
63 debug190:761BB152 00 28 CMP R0, #0
64 debug190:761BB154 11 D0 BEQ loc_761BB17A
65 debug190:761BB156 51 46 MOV R1, R10
66 debug190:761BB158 32 1C MOVS R2, R6
67 debug190:761BB15A 10 F0 17 FA BL memcpy_0 ; 拷贝加密的dex内容
68 debug190:761BB15E 28 1C MOVS R0, R5
69 debug190:761BB160 41 46 MOV R1, R8
70 debug190:761BB162 32 1C MOVS R2, R6
71 debug190:761BB164 00 F0 BA F8 BL Rc4_Dec ; 解密Dex内容
72 debug190:761BB168 4B 46 MOV R3, R9
73 debug190:761BB16A 1E 60 STR R6, [R3]
74 debug190:761BB16C 43 46 MOV R3, R8
75 debug190:761BB16E 00 20 MOVS R0, #0
76 debug190:761BB170 3B 60 STR R3, [R7]
77 debug190:761BB172 E0 E7 B loc_761BB136
78 debug190:761BB174 ; ---------------------------------------------------------------------------
79 debug190:761BB174
80
81 //再次解密dex 文件偏移 EDF4
82 debug190 76174000 761DA000 R . X D . byte 00 public CODE 32 00 01
83 debug190:76182DF4 loc_76182DF4 ; CODE XREF: debug190:76182DB6j
84 debug190:76182DF4 51 46 MOV R1, R10
85 debug190:76182DF6 34 F0 F3 FA BL loc_761B73E0
86 debug190:76182DFA 00 28 CMP R0, #0
87 debug190:76182DFC DC DB BLT loc_76182DB8
88 debug190:76182DFE 53 46 MOV R3, R10
89 debug190:76182E00 1B 68 LDR R3, [R3]
90 debug190:76182E02 58 46 MOV R0, R11
91 debug190:76182E04 9A 46 MOV R10, R3
92 debug190:76182E06 5B 46 MOV R3, R11
93 debug190:76182E08 1B 68 LDR R3, [R3]
94 debug190:76182E0A 05 99 LDR R1, [SP,#0x14]
95 debug190:76182E0C 06 9A LDR R2, [SP,#0x18]
96 debug190:76182E0E DB 68 LDR R3, [R3,#0xC]
97 debug190:76182E10 9C 46 MOV R12, R3
98 debug190:76182E12 53 46 MOV R3, R10
99 debug190:76182E14 E0 47 BLX R12 ; 又是解密
100 debug190:76182E16 00 28 CMP R0, #0
101 debug190:76182E18 CE DB BLT loc_76182DB8
102 debug190:76182E1A 05 98 LDR R0, [SP,#0x14]
103 debug190:76182E1C 48 F0 0E FC BL free_0
104 debug190:76182E20 5B 46 MOV R3, R11
105 debug190:76182E22 1B 68 LDR R3, [R3]
106 debug190:76182E24 58 46 MOV R0, R11
107 debug190:76182E26 5B 68 LDR R3, [R3,#4]
108 debug190:76182E28 98 47 BLX R3
109 debug190:76182E2A 03 9B LDR R3, [SP,#0xC]
110 debug190:76182E2C 4D 44 ADD R5, R9
111 debug190:76182E2E 53 44 ADD R3, R10
112 debug190:76182E30 18 1C MOVS R0, R3
113 debug190:76182E32 9D 42 CMP R5, R3
114 debug190:76182E34 05 D0 BEQ loc_76182E42 ; 解密dex
115 debug190:76182E36 00 2E CMP R6, #0
116 debug190:76182E38 03 D0 BEQ loc_76182E42 ; 解密dex
117 debug190:76182E3A 29 1C MOVS R1, R5
118 debug190:76182E3C 32 1C MOVS R2, R6
119 debug190:76182E3E FE F7 49 FF BL loc_76181CD4
120
121
122 //解密dex 头0x70字节 文件偏移 EE42
123 debug190 76174000 761DA000 R . X D . byte 00 public CODE 32 00 01
124 debug190:76182E42 loc_76182E42 ; CODE XREF: sub_76182C48+1ECj
125 debug190:76182E42 ; sub_76182C48+1F0j
126 debug190:76182E42 7A 69 LDR R2, [R7,#0x14] ; 解密dex
127 debug190:76182E44 50 46 MOV R0, R10
128 debug190:76182E46 70 21 MOVS R1, #0x70 ; 'p'
129 debug190:76182E48 38 F0 90 F8 BL sub_761BAF6C ; 解密dex头70字节
130 debug190:76182E4C 01 25 MOVS R5, #1
131 debug190:76182E4E B9 E7 B loc_76182DC4
这时就可以将DEX数据dump出来,其实后面每个onCreate中也会出现明文的DEX。
将dump出来的dex通过JDE反编译后发现很多Activity中的onCreate函数变成了Native了,从上面打印的类与对应函数可以看得出。
在上面分析到注册Native函数时就对onCreate函数下好了断点,直接F9来到onCreate断下。
1 //出现解密后dex 文件偏移 A910
2 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
3 debug098:75DD0910 loc_75DD0910 ; CODE XREF: debug098:75DD08F2j
4 debug098:75DD0910 05 9B LDR R3, [SP,#0x14]
5 debug098:75DD0912 1C 20 MOVS R0, #0x1C
6 debug098:75DD0914 1F 68 LDR R7, [R3] ; 出现解密后dex
7 debug098:75DD0916 B3 68 LDR R3, [R6,#8]
8 debug098:75DD0918 9C 46 MOV R12, R3
9 debug098:75DD091A 67 44 ADD R7, R12
一直单步走到定位指令的地方。
1 //定位到dex中的onCreate方法在内存中的指令,获取dex中的onCreate指令并解密。 文件偏移 2F9F8
2 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
3 debug098:75DF59F8
4 debug098:75DF59F8 loc_75DF59F8 ; CODE XREF: debug098:75DF5988j
5 debug098:75DF59F8 ; debug098:75DF59CAj
6 debug098:75DF59F8 A3 68 LDR R3, [R4,#8]
7 debug098:75DF59FA 10 33 ADDS R3, #0x10 ; 定位到onCreate指令
8 debug098:75DF59FC 1E 1C MOVS R6, R3
9 debug098:75DF59FE 0C 93 STR R3, [SP,#0x30]
10 debug098:75DF5A00
11 debug098:75DF5A00 loc_75DF5A00 ; CODE XREF: debug098:75DF5E54j
12 debug098:75DF5A00 ; debug098:75DF5EF4j ...
13 debug098:75DF5A00 0C 9B LDR R3, [SP,#0x30]
14 debug098:75DF5A02 00 21 MOVS R1, #0
15 debug098:75DF5A04 F0 1A SUBS R0, R6, R3
16 debug098:75DF5A06 43 10 ASRS R3, R0, #1
17 debug098:75DF5A08 30 1C MOVS R0, R6
18 debug098:75DF5A0A 06 93 STR R3, [SP,#0x18]
19 debug098:75DF5A0C FE F7 29 FE BL GetCode ; 获取dex中的onCreate指令并解密
20 debug098:75DF5A10 05 90 STR R0, [SP,#0x14]
21 debug098:75DF5A12 00 06 LSLS R0, R0, #0x18
22 debug098:75DF5A14 00 0E LSRS R0, R0, #0x18
23 debug098:75DF5A16 01 38 SUBS R0, #1 ; switch 255 cases
24 debug098:75DF5A18 FE 28 CMP R0, #0xFE ; ' ;判断是否超出表示指令范围
25 debug098:75DF5A1A 01 D9 BLS loc_75DF5A20
26 debug098:75DF5A1C 02 F0 8D FF BL def_75DF5A20 ; jumptable 75DF5A20 default case
比如 下面是dex中OnCreate的指令,
读取上面的指令并解密。
1 //获取指令,并解密 文件偏移 2E662
2 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
3 debug098:75DF4662 GetCode ; CODE XREF: debug098:75DF5A0Cp
4 debug098:75DF4662 ; debug098:75DF5E40p ...
5 debug098:75DF4662 38 B5 PUSH {R3-R5,LR}
6 debug098:75DF4664 05 1C MOVS R5, R0
7 debug098:75DF4666 0C 1C MOVS R4, R1
8 debug098:75DF4668 FD F7 D6 FC BL loc_75DF2018
9 debug098:75DF466C 00 7E LDRB R0, [R0,#0x18] ; 密钥
10 debug098:75DF466E 64 00 LSLS R4, R4, #1
11 debug098:75DF4670 02 1C MOVS R2, R0
12 debug098:75DF4672 03 02 LSLS R3, R0, #8
13 debug098:75DF4674 1A 43 ORRS R2, R3
14 debug098:75DF4676 63 5B LDRH R3, [R4,R5] ; 取指令
15 debug098:75DF4678 5A 40 EORS R2, R3 ;解密
16 debug098:75DF467A 10 1C MOVS R0, R2
17 debug098:75DF467C 38 BD POP {R3-R5,PC}
解密后判断指令类型,获取执行须要的数据,每一种操作码(OPCode)都对应有一个处理逻辑(是一段代码,但不一定是函数),然后跳转到对应的处理逻辑去处理它,简单说一个正常没加壳的指令格式。
71 20 06 00 02 00 invoke-static {v2, v0}, int aurora.view.AuroraTest.Test2(int, java.lang.String)
71操作码
2 参数个数
0006 method idx
02参数V2
00参数 V0
对应Android官方指令表
但是加壳后的指令被变成了自己定义的了,我第一次想法是想通过分析加壳前与加壳后指令对应关系,只要找到足够多的指令就能将其还原,我简单加了两个apk测试,从第一个中找到了如下的指令对应关系。
真实指令 自定义指令
6f = 74
6E = 22
0c = cc
1f = 9c
60 = 5a
39 = 9c
1a = 89
22 = 4B
71 = 0b
70 = 33
15 = 5b
5b = e4
54 = 5c
6f = 74
14 = 08
0e = 8e
将第一个apk中找到的指令来修复第二个指令,不知道是免费版的原因,还是巧合,我成功修复了第二个apk,但是当我用这个关系来尝试修复要破解的程序时根本不行,后来想了想,这种方法应当不行,都自己实现了解释器,为什么还要让指令固定呢?不科学呀,放弃了这种想法。
其实壳读取并解密一条指令后主要是使用JNI接口函数来实现解析执行。
如果指令类型是调用函数的话就会做如下动作,其它的调用其它接口(简单示例)
1 jclass clazz=(*env)->FindClass(env,"com/example/test");
2 jmethodID methodId=(*env)->GetMethodID(env,clazz,"Add","(II)I");
3 // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
4 (*env)->CallIntMethod(env,jobject,methodId,3,5);
主要流程如下:
1 //获取dex中的类名、函数名、签名 文件偏移 2E648
2 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
3 debug098:75DF4648
4 debug098:75DF4648 GetString ; CODE XREF: debug098:75DF4C52p
5 debug098:75DF4648 ; debug098:75DF4C78p ...
6 debug098:75DF4648 03 69 LDR R3, [R0,#0x10]
7 debug098:75DF464A 02 68 LDR R2, [R0]
8 debug098:75DF464C 89 00 LSLS R1, R1, #2
9 debug098:75DF464E DB 6B LDR R3, [R3,#0x3C]
10 debug098:75DF4650 C9 18 ADDS R1, R1, R3
11 debug098:75DF4652 8B 58 LDR R3, [R1,R2]
12 debug098:75DF4654 D0 18 ADDS R0, R2, R3
13 debug098:75DF4656
14 debug098:75DF4656 loc_75DF4656 ; CODE XREF: debug098:75DF465Ej
15 debug098:75DF4656 01 30 ADDS R0, #1
16 debug098:75DF4658 43 1E SUBS R3, R0, #1
17 debug098:75DF465A 1B 78 LDRB R3, [R3]
18 debug098:75DF465C 7F 2B CMP R3, #0x7F ; ''
19 debug098:75DF465E FA D8 BHI loc_75DF4656
20 debug098:75DF4660 70 47 BX LR
21
22 FindClass 文件偏移 18956
23 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
24 debug098:75DDE956 sub_75DDE956 ; CODE XREF: debug098:75DF4CBAp
25 debug098:75DDE956 ; debug098:75DF5092p ...
26 debug098:75DDE956 08 B5 PUSH {R3,LR}
27 debug098:75DDE958 03 68 LDR R3, [R0]
28 debug098:75DDE95A 9B 69 LDR R3, [R3,#0x18]
29 debug098:75DDE95C 98 47 BLX R3 ; FindClass
30 debug098:75DDE95E 08 BD POP {R3,PC}
31
32 ExceptionCheck 文件偏移 1802E
33 debug096 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
34 debug096:75DDE02E loc_75DDE02E ; CODE XREF: debug096:75DDE046p
35 debug096:75DDE02E ; debug096:75DF4BECp ...
36 debug096:75DDE02E 08 B5 PUSH {R3,LR}
37 debug096:75DDE030 E4 23 MOVS R3, #0xE4 ; '
38 debug096:75DDE032 02 68 LDR R2, [R0]
39 debug096:75DDE034 9B 00 LSLS R3, R3, #2
40 debug096:75DDE036 D3 58 LDR R3, [R2,R3]
41 debug096:75DDE038 98 47 BLX R3 ; ExceptionCheck
42 debug096:75DDE03A 08 BD POP {R3,PC}
43
44 //GetMethodID 文件偏移
45 debug098 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
46 debug098:75DFB11E loc_75DFB11E ; CODE XREF: debug098:75DFB10Ej
47 debug098:75DFB11E 00 98 LDR R0, [SP]
48 debug098:75DFB120 04 99 LDR R1, [SP,#0x10]
49 debug098:75DFB122 01 9A LDR R2, [SP,#4]
50 debug098:75DFB124 B0 47 BLX R6 ; GetMethodID
51 debug098:75DFB126 06 1C MOVS R6, R0
52 debug098:75DFB128 28 1C MOVS R0, R5
53
54 //DeleteLocalRef 文件偏移 18018
55 debug096 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
56 debug096:75DDE018 loc_75DDE018 ; CODE XREF: debug096:75DDE062p
57 debug096:75DDE018 ; debug096:75DF528Ap ...
58 debug096:75DDE018 08 B5 PUSH {R3,LR}
59 debug096:75DDE01A 03 68 LDR R3, [R0]
60 debug096:75DDE01C DB 6D LDR R3, [R3,#0x5C]
61 debug096:75DDE01E 98 47 BLX R3 ; DeleteLocalRef
62 debug096:75DDE020 08 BD POP {R3,PC}
63
64 //CallNonvirtualVoidMethodA 文件偏移 2EFEC
65 debug096 75DC6000 75E2C000 R . X D . byte 00 public CODE 32 00 01
66 debug096:75DF4FEC loc_75DF4FEC ; CODE XREF: sub_75DF4B50:loc_75DF4F0Cj
67 debug096:75DF4FEC 23 68 LDR R3, [R4] ; jumptable 75DF4F0C case 86
68 debug096:75DF4FEE 02 9A LDR R2, [SP,#0x108+var_100]
69 debug096:75DF4FF0 20 1C MOVS R0, R4
70 debug096:75DF4FF2 FC 33 ADDS R3, #0xFC ; '
71 debug096:75DF4FF4 00 92 STR R2, [SP,#0x108+var_108]
72 debug096:75DF4FF6 9D 6F LDR R5, [R3,#0x78]
73 debug096:75DF4FF8 04 99 LDR R1, [SP,#0x108+var_F8]
74 debug096:75DF4FFA 18 9A LDR R2, [SP,#0x108+var_A8]
75 debug096:75DF4FFC 03 9B LDR R3, [SP,#0x108+var_FC]
76 debug096:75DF4FFE A8 47 BLX R5 ; CallNonvirtualVoidMethodA
77 debug096:75DF5000 4D E1 B def_75DF4F0C ; jumptable 75DF4F0C default case
通过上面的流程分析后,我猜想能不能hook或者用其它方法来监控JNI接口,得到对应的执行流程与调用了那那些JNI函数,在对应着Android Dalvik bytecode指令表来反推出真实的指令,其实就是看一条指令执行完成后调用的JNI接口能不能确定是什么指令,目前还没有试,如果大牛们有什么好办法还请多多指教,目的是破解程序,静态分析己经可以了,先不考虑还原。(^_^)
来回顾下壳的主要流程:
壳主soà反调试à解压并解密第二个soà从主so中跳到第二个so中执行à第二人so注册native函数à解密原始DEXà程序跑起来后到Native onCreateà定位到自定义的指令à读取指令并解密à解析指令格式à获取执行指令须要的参数à调用JNI接口执行。
我想要是你玩过windows上破解都知道DLL劫持吧,先伪造一个同名的DLL,提供同样的输出函数,每个输出函数中转向真正的DLL函数,不错,我也是用这种方法来实现注入SO来Hook函数,过掉反调试,会省去很多时间反调试上面,直接到你想要分析的关键函数。
分析壳java层代码就知道它主要是通过读取从资源目中把壳so拷到指定目录中加载,
我的做法是hook getAssets().open()函数,将资源中的so替换成我的so,然后将壳so改名放在app-lib目录中给我的so加载。
1 XposedHelpers.findAndHookMethod(
2 AssetManager.class, //被Hook函数所在的类
3 "open", //被Hook函数的名称
4 String.class,
5 new XC_MethodHook(){
6 @Override
7 protected void beforeHookedMethod(MethodHookParam param)
8 throws Throwable {
9 // Hook函数之前执行的代码
10 String path = param.args[0].toString();
11 if(path.equals("libjiagu.so")){
12 param.args[0] = "/data/local/tmp/libjiagu.so";
13 }
14 Yfw_DebugLog.d("open beforeHookedMethod--->0 "+param.args[0].toString());
15 }
16
17 @Override
18 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
19
20 File file = new File(param.args[0].toString());
21 InputStream input = new FileInputStream(file);
22 param.setResult(input);
23 Yfw_DebugLog.d("open afterHookedMethod--->"+param.getResult().toString());
24
25 }
26 });
27
28 So的代码也很简单:
29 /*
30 调用原始JNI_OnLoad
31 */
32 jint LoadSo_InitJni(char* SoFilePath, JavaVM* vm, void* reserved)
33 {
34 jint ret;
35 void *pHandle = NULL;
36 pHandle = dlopen(SoFilePath, RTLD_LAZY);
37 if (NULL == pHandle)
38 {
39 LOGD("dlopen %s error!", SoFilePath);
40 return NULL;
41 }
42 _JNI_OnLoad old_JNI_OnLoad = NULL;
43 old_JNI_OnLoad = (_JNI_OnLoad)dlsym(pHandle, "JNI_OnLoad");
44 if (NULL == old_JNI_OnLoad)
45 {
46 LOGD("old_JNI_OnLoad error!");
47 return NULL;
48 }
49 LOGD("old_JNI_OnLoad address=%X env...%X", old_JNI_OnLoad, g_env);
50 ret = old_JNI_OnLoad(vm, reserved);
51 LOGD("old_JNI_OnLoad end...%d", ret);
52 return ret;
53
54 }
55
56 Hook open函数过反调试
57 int new_open(const char *pathname, int oflag, mode_t mode)
58 {
59 if (NULL == pathname)
60 {
61 goto exitret;
62 }
63
64 LOGD("new_open..%s", pathname);
65 if (strstr(pathname, "net/tcp") != NULL)
66 {
67 return 0;
68 }
69 if (strstr(pathname, "status") != NULL)
70 {
71 return 0;
72 }
73 if (strstr(pathname, "stat") != NULL)
74 {
75 return 0;
76 }
77 if (strstr(pathname, "schedstat") != NULL)
78 {
79 return 0;
80 }
81
82 exitret:
83 return old_open(pathname, oflag, mode);
84 }
85 Hook time过反调试
86 time_t new_time(time_t * timer) {
87 LOGD("new_time------->");
88 return 0;
89 }
90 Hook dvmUseJNIBridge 方便在 native下断点,打印出对应类的方法
91 void new_dvmUseJNIBridge(Method* method, void* func)
92 {
93 LOGD("my_dvmUseJNIBridge class-%s ->name=%s address=%08X", method->clazz->descriptor, method->name, func);
94 //LOGD("my_dvmUseJNIBridge nativeFunc-%s", method->nativeFunc);
95 if (0 == strcmp(method->name, "getClassNameList"))
96 {
97 sleep(8); //等待附加调试器
98 }
99 return old_dvmUseJNIBridge(method, func);
100 }
这样当我们的so加载时用IDA附加上就可以在new_dvmUseJNIBridge下断点,就能在想要分析的Native函数上下断点了。
上面的代码就完成了整个劫持的过程,开心地调试吧!!!!!!
通过JEB反编译该应用dump出来的的classes.dex文件,直接搜索登录时用到的网址字符串 7658/api/entrance,找到如下的字符串,双击第一个字符串进去 。
其中的a函数是返回网址的,当参数为10时是要找到网址。
1 public static String a(int arg1) {
2 switch(arg1) {
3 case 10: {
4 goto label_4; //返回登录地址
5 }
6 case 11: {
7 goto label_6;
8 }
9 case 12: {
10 goto label_8;
11 }
12 case 13: {
13 goto label_10;
14 }
15 case 14: {
16 goto label_12;
17 }
18 case 15: {
19 goto label_14;
20 }
21 case 16: {
22 goto label_16;
23 }
24 case 17: {
25 goto label_18;
26 }
27 case 19: {
28 goto label_20;
29 }
30 case 20: {
31 goto label_22;
32 }
33 }
按JEB快捷键 x 找到传入参数为10调用上面函数的地方,找到如下的类:
1 public class g extends j {
2 private static String login;
3 private ThreadPoolExecutor d;
4 private static g e;
5
6 static {
7 g.login = i.a(10);
8 g.e = null;
9 }
10 在这个类中找到了网络登录请求并解密返回值的函数。
11 private String b(h arg5, String arg6) {
12 String v0 = null;
13 if(arg5 != null && (arg6 != null && (AndroidHelper.isNetworkActive(this.b)))) {
14 String v1 = this.a(arg6, arg5.a()); // 组合请求参数
15 if(v1 == null) {
16 return v0;
17 }
18
19 try {
20 JSONObject v2 = new JSONObject(a.a(g.login_address, v1.getBytes("UTF-8"), this.c)); // 传入网址,应该就是网络请求了
21 if(v2.getInt("status") != 0) {
22 return v0;
23 }
24
25 v0 = j.b(v2.getString("data")); // 解密登录返回值
26 }
27 catch(Exception v0_1) {
28 d.a(((Throwable)v0_1));
29 v0 = "server_error";
30 }
31 }
32
33 return v0;
34 }
分析下请求参数都有些什么东西,组合请求参数的函数实现在父类j中。
1 String a(String arg9, int arg10) {
2 String v0 = null;
3 if(arg9 != null) {
4 String v1 = j.a(this.c(arg9)); // aes加密后再base64
5 if(TextUtils.isEmpty(((CharSequence)v1))) {
6 return v0;
7 }
8
9 try {
10 StringBuffer v2 = new StringBuffer();
11 v2.append("body=");
12 v2.append(URLEncoder.encode(v1, "UTF-8"));
13 v2.append("&t1=");
14 v2.append(System.currentTimeMillis() / 1000);
15 v2.append("&t2=");
16 v2.append(this.d(arg9));
17 v2.append("&type=");
18 v2.append(arg10);
19 v2.append("&flag=");
20 v2.append(4);
21 v0 = v2.toString();
22 }
23 catch(UnsupportedEncodingException v1_1) {
24 d.a(((Throwable)v1_1));
25 }
26 }
27
28 return v0;
29 }
用Xposed hook 加函数打印参数,
1 //aes加密参数
2 XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
3 @Override
4 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
5 ClassLoader cl = ((Context)param.args[0]).getClassLoader();
6 Class<?> hookclass = null;
7 try {
8 hookclass = cl.loadClass("com.txyapp.client.j");
9 } catch (Exception e) {
10 Yfw_DebugLog.d("寻找com.txyapp.client.j 报错"+ e);
11 return;
12 }
13 Yfw_DebugLog.d("寻找com.txyapp.client.j成功");
14
15 XposedHelpers.findAndHookMethod(
16 hookclass, //被Hook函数所在的类
17 "a", //被Hook函数的名称
18 String.class,
19 new XC_MethodHook(){
20 @Override
21 protected void beforeHookedMethod(MethodHookParam param)
22 throws Throwable {
23 // Hook函数之前执行的代码
24 Yfw_DebugLog.d("a_Aesenc_data beforeHookedMethod--->0 "+param.args[0].toString());
25 }
26
27 @Override
28 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
29 Yfw_DebugLog.d("a_Aesenc_data afterHookedMethod--->"+param.getResult().toString());
30 }
31 });
32 }
33 });
主要是将用户名密码,还有一些机器信息aes与base加密后请求过去,格式如下:
{"data":"{\"clientid\":\"7e7884f241c96051958cb776d6f780d9\",\"password\":\"xxxxxxxxxx\",\"username\":\"crack8888\"}","header":{"model":"Nexus 5","root":1,"mcc":"460","uptime":1492303101,"sysapi":19,"corever":546,"vername":"13.1.2","un":"","cn":"guanfang","cl":"zh-CN","brand":"google","mnc":"02","uuid":"7FDC501E4B3xxxxx","cpuabi1":"armeabi-v7a","cpuabi2":"armeabi","vercode":322}}
然后服务器返回一些加密后的数据,从上面的g类b函数中可以看出,判断如果请求成功,就解密返回数据,通过hook该解密函数得到解密后的数据格式为json
1 XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
2 @Override
3 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
4 ClassLoader cl = ((Context)param.args[0]).getClassLoader();
5 Class<?> hookclass = null;
6 try {
7 hookclass = cl.loadClass("com.txyapp.client.j");
8 } catch (Exception e) {
9 Yfw_DebugLog.d("寻找com.txyapp.client.j 报错"+ e);
10 return;
11 }
12 Yfw_DebugLog.d("寻找com.txyapp.client.j成功");
13 //解密vip
14 XposedHelpers.findAndHookMethod(
15 hookclass, //被Hook函数所在的类
16 "b", //被Hook函数的名称
17 String.class,
18 new XC_MethodHook(){
19 @Override
20 protected void beforeHookedMethod(MethodHookParam param)
21 throws Throwable {
22 // Hook函数之前执行的代码
23 Yfw_DebugLog.d("b_Aesdec_data beforeHookedMethod--->0 "+param.args[0].toString());
24 }
25
26 @Override
27 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
28 Yfw_DebugLog.d("b_Aesdec_data afterHookedMethod--->"+param.getResult().toString());
29
30 JSONObject jsonObj = new JSONObject(param.getResult().toString());
31 if(0 == (int)jsonObj.get("vip") ){
32 jsonObj.put("vip", 1);
33 jsonObj.put("expiretime", 1999077441);// 2033-05-07
34
35 Yfw_DebugLog.d("b_Aesdec_data result--->"+jsonObj.toString());
36 viptag = 1;
37 param.setResult(jsonObj.toString());
38 }
39
40 }
41 });
42 }
43 });
44 //解密后数据
45 {"status":0,"token":"49436a44d85f46c7e458a6b071af470a","username":"crack8888","vip":0,"expiretime":0}
从上面格式可以看出 vip:=0代表不是vip, expiretime:到期时间,所以我的在hook函数中将这两个参数修改如下。
{"vip":1,"username":"crack8888","token":"db9ea6e12936463d722fb7bcda7e97ab","status":0,"expiretime":1999077441}
这样就成功破解了,会员功能也正常使用。
还有一个地方就是显示个人信息,hook函数进行修改
1 //刷新个人信息显示
2 XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
3 @Override
4 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
5 ClassLoader cl = ((Context)param.args[0]).getClassLoader();
6 Class<?> hookclass = null;
7 try {
8 hookclass = cl.loadClass("com.txy.anywhere.activity.login.PersonalInfoActivity");
9 } catch (Exception e) {
10 Yfw_DebugLog.d("寻找com.txy.anywhere.activity.login.PersonalInfoActivity报错"+ e);
11 return;
12 }
13 Yfw_DebugLog.d("寻找com.txy.anywhere.activity.login.PersonalInfoActivity成功");
14 //判断是否为vip
15 XposedHelpers.findAndHookMethod(
16 hookclass, //被Hook函数所在的类
17 "a", //被Hook函数的名称
18 String.class,
19 new XC_MethodHook(){
20 @Override
21 protected void beforeHookedMethod(MethodHookParam param)
22 throws Throwable {
23 // Hook函数之前执行的代码
24 Yfw_DebugLog.d("login beforeHookedMethod--->0 "+param.args[0].toString());
25
26
27 JSONObject container1 = new JSONObject();
28
29 JSONObject v0_1 = new JSONObject(param.args[0].toString());
30 JSONObject v0_2 = new JSONObject(param.args[0].toString());
31 v0_2 = v0_1.getJSONObject("user_info");
32 v0_2.put("vip", 1);//是否为vip 0:为普通会员, 1:为vip
33 v0_2.put("expire_time", 1999077441);//到期时间
34 v0_2.put("phonenumber", "110");
35 v0_1.put("user_info", v0_2);
36 param.args[0] = v0_1.toString();
37 Yfw_DebugLog.d("login beforeHookedMethod 1--->0 "+param.args[0].toString());
38
39 }
40
41 @Override
42 protected void afterHookedMethod(MethodHookParam param) throws Throwable {
43
44 Yfw_DebugLog.d("login afterHookedMethod--->");
45 }
46 });
47 }
48 });
到这里就算破解完成了,简单测试了会员功能,都能正常使用。
由于水平有限,未能更好的分析与表达,不过,通过前面几部分的简单分析,其实我们已经知道只是Native onCreate函数是不够的,主要还是得保护好关键函数不被分析。
百度网盘 链接: https://pan.baidu.com/s/1c2KiYsg 密码: 2rft