某地理位置模拟APP从壳流程分析到破解(劫持so过反调试)

转自:http://bbs.pediy.com/thread-217059.htm

工具与环境

Xposed

IDA 6.8

JEB 2.2.5

Fiddler2

010Editor

NEXUS 5  Android 4.4

好久不玩逆向怕调试器生锈,拿出来磨磨!

高手莫要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!

0x00: 程序大概情况分析

在我们拿到一个APP准备破解时一般得安装运行,程序运行后须要注册用户,随便注册一个用户登录,以下是APP须要购买vip才能使用的大概情况。


通过简单的查看可以知道是要通过网络支付后才能使用VIP,可以使用Fiddler进行抓包分析,但是请求体与返回值都是加密了的,目前还看不懂。

JEB反编译app发现被加固了。

通过上面简单的分析后,该款应用为了防止被破解,主要做了以下几点防护。

利用第三方加固将app加固,网络验证是否为VIP权限。

0x01:壳反调试分析

通过第一部分的介绍,发现软件被加固了,接下来就是要脱掉壳才能更好地分析下去,挂上IDAJNI_Onload下断点,反调试主要有,获取rtld_db_dlactivity判断是否为空,过反调试将获取到的内容清零就成了、time时间比较,将返回值清零,过/proc/self/status反调试将open函数返回0,过/proc/net/tcp也是将open函数返回0,文件监控不用管,还有一处比较隐藏的反调试raise,直接将函数改成返回,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
LOAD:75A6A038 F0 41 2D E9                 STMFD           SP!, {R4-R8,LR}
LOAD:75A6A03C F0 81 BD E8                 LDMFD           SP!, {R4-R8,PC};改成返回
LOAD:75A6A040             ; ---------------------------------------------------------------------------
LOAD:75A6A040 70 50 9F E5                 LDR             R5, =(off_75A72ED4 - 0x75A6A05C)
LOAD:75A6A044 70 20 9F E5                 LDR             R2, =(is_androidserver_prot - 0x75A6A058)
LOAD:75A6A048 70 60 9F E5                 LDR             R6, =0xE7F001F0
LOAD:75A6A04C 10 D0 4D E2                 SUB             SP, SP, #0x10
LOAD:75A6A050 02 20 8F E0                 ADD             R2, PC, R2 ; is_androidserver_prot
LOAD:75A6A054 05 50 9F E7                 LDR             R5, [PC,R5] ; off_75A72ED4
LOAD:75A6A058 03 30 9F E7                 LDR             R3, [PC,R3] ; off_75A72ED0
LOAD:75A6A05C 04 50 8D E5                 STR             R5, [SP,#0x10+var_C]
LOAD:75A6A060 08 30 8D E5                 STR             R3, [SP,#0x10+var_8]
LOAD:75A6A064 0C 20 8D E5                 STR             R2, [SP,#0x10+var_4]
LOAD:75A6A068 04 70 8D E2                 ADD             R7, SP, #4
LOAD:75A6A06C 0C 80 8D E2                 ADD             R8, SP, #0x10+var_4
LOAD:75A6A070
LOAD:75A6A070             loc_75A6A070                            ; CODE XREF: Anti_raise+70j
LOAD:75A6A070 04 40 45 E2                 SUB             R4, R5, #4
LOAD:75A6A074 24 50 85 E2                 ADD             R5, R5, #0x24
LOAD:75A6A078 01 00 00 EA                 B               loc_75A6A084
LOAD:75A6A07C             ; ---------------------------------------------------------------------------
LOAD:75A6A07C
LOAD:75A6A07C             loc_75A6A07C                            ; CODE XREF: Anti_raise+54j
LOAD:75A6A07C 05 00 54 E1                 CMP             R4, R5
LOAD:75A6A080 06 00 00 0A                 BEQ             loc_75A6A0A0
LOAD:75A6A084
LOAD:75A6A084             loc_75A6A084                            ; CODE XREF: Anti_raise+40j
LOAD:75A6A084                                                     ; Anti_raise+64j
LOAD:75A6A084 04 30 B4 E5                 LDR             R3, [R4,#4]!
LOAD:75A6A088 06 00 53 E1                 CMP             R3, R6
LOAD:75A6A08C FA FF FF 1A                 BNE             loc_75A6A07C
LOAD:75A6A090 09 00 A0 E3                 MOV             R0, #9  ; sig
LOAD:75A6A094 25 F6 FF EB                 BL               raise
LOAD:75A6A098 05 00 54 E1                 CMP             R4, R5
LOAD:75A6A09C F8 FF FF 1A                 BNE             loc_75A6A084
LOAD:75A6A0A0
LOAD:75A6A0A0             loc_75A6A0A0                            ; CODE XREF: Anti_raise+48j
LOAD:75A6A0A0 08 00 57 E1                 CMP             R7, R8
LOAD:75A6A0A4 04 50 B7 15                 LDRNE           R5, [R7,#0xC+var_8]!
LOAD:75A6A0A8 F0 FF FF 1A                 BNE             loc_75A6A070
LOAD:75A6A0AC 10 D0 8D E2                 ADD             SP, SP, #0x10
LOAD:75A6A0B0 F0 81 BD E8                 LDMFD           SP!, {R4-R8,PC}
LOAD:75A6A0B0             ; End of function Anti_raise
LOAD:75A6A0B0

以上就是过掉所有主要的反调试了!

0x02:壳大致流程分析与Dump出解密后的dex

过掉反调试后在Case 29 case33下好断点,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//case 29 文件偏移:9DB8
/data/app-lib/com.txy.anywhere-1/libjiagu_old.so 75A66000 00050000
LOAD:75A6FDB8
LOAD:75A6FDB8             loc_75A6FDB8                            ; CODE XREF: __fun_a_18( char  *,uint):loc_75A6F8CCj
LOAD:75A6FDB8                                                     ; __fun_a_18( char  *,uint):loc_75A6F8DCj ...
LOAD:75A6FDB8 01 30 8C E2 ADD             R3, R12, #1             ; jumptable 75A6F8DC  case  29
LOAD:75A6FDBC 03 20 D4 E7 LDRB            R2, [R4,R3]
LOAD:75A6FDC0 1C 30 8D E5 STR             R3, [SP,#0xCC+var_B0]
LOAD:75A6FDC4 00 00 52 E3 CMP             R2, #0
LOAD:75A6FDC8 C9 01 00 0A BEQ             loc_75A704F4
LOAD:75A6FDCC 80 05 95 E9 LDMIB           R5, {R7,R8,R10}
LOAD:75A6FDD0 0C 20 84 E0 ADD             R2, R4, R12
LOAD:75A6FDD4 05 B0 92 E5 LDR             R11, [R2,#5]
LOAD:75A6FDD8 38 20 95 E5 LDR             R2, [R5,#0x38]
LOAD:75A6FDDC 03 30 94 E7 LDR             R3, [R4,R3]
LOAD:75A6FDE0 09 C0 8C E2 ADD             R12, R12, #9
LOAD:75A6FDE4 1C C0 8D E5 STR             R12, [SP,#0xCC+var_B0]
LOAD:75A6FDE8 02 B0 8B E0 ADD             R11, R11, R2
LOAD:75A6FDEC 0B B0 63 E0 RSB             R11, R3, R11
LOAD:75A6FDF0 00 C0 95 E5 LDR             R12, [R5]
LOAD:75A6FDF4 0C 00 A0 E1 MOV             R0, R12
LOAD:75A6FDF8 07 10 A0 E1 MOV             R1, R7
LOAD:75A6FDFC 08 20 A0 E1 MOV             R2, R8
LOAD:75A6FE00 0A 30 A0 E1 MOV             R3, R10
LOAD:75A6FE04 0B E0 A0 E1 MOV             LR, R11
LOAD:75A6FE08 3E FF 2F E1 BLX             LR
LOAD:75A6FE0C 00 70 A0 E1 MOV             R7, R0
LOAD:75A6FE10 01 C0 A0 E1 MOV             R12, R1
LOAD:75A6FE14 00 70 85 E5 STR             R7, [R5]                ; jumptable 000098CC  case  28
LOAD:75A6FE18 04 C0 85 E5 STR             R12, [R5,#4]
LOAD:75A6FE1C 1C C0 9D E5 LDR             R12, [SP,#0xCC+var_B0]
LOAD:75A6FE20 A9 FE FF EA B               loc_75A6F8CC
 
//case 33 执行解压函数与执行解密后第二个so的JNI_OnLoad函数 文件偏移: 9B44
/data/app-lib/com.txy.anywhere-1/libjiagu_old.so 75A66000 00050000
LOAD:75A6FB44             loc_75A6FB44                            ; CODE XREF: __fun_a_18( char  *,uint):loc_75A6F8CCj
LOAD:75A6FB44                                                     ; __fun_a_18( char  *,uint):loc_75A6F8DCj ...
LOAD:75A6FB44 01 30 8C E2 ADD             R3, R12, #1             ; jumptable 75A6F8DC  case  33
LOAD:75A6FB48 03 20 D4 E7 LDRB            R2, [R4,R3]
LOAD:75A6FB4C 1C 30 8D E5 STR             R3, [SP,#0xCC+var_B0]
LOAD:75A6FB50 00 00 52 E3 CMP             R2, #arg_0
LOAD:75A6FB54 66 02 00 0A BEQ             loc_75A704F4
LOAD:75A6FB58 02 30 8C E2 ADD             R3, R12, #2
LOAD:75A6FB5C 03 20 94 E7 LDR             R2, [R4,R3]
LOAD:75A6FB60 1C 30 8D E5 STR             R3, [SP,#0xCC+var_B0]
LOAD:75A6FB64 06 C0 8C E2 ADD             R12, R12, #6
LOAD:75A6FB68 02 B1 95 E7 LDR             R11, [R5,R2,LSL#2]
LOAD:75A6FB6C 1C C0 8D E5 STR             R12, [SP,#0xCC+var_B0]
LOAD:75A6FB70 80 15 95 E8 LDMIA           R5, {R7,R8,R10,R12}
LOAD:75A6FB74 07 00 A0 E1 MOV             R0, R7
LOAD:75A6FB78 08 10 A0 E1 MOV             R1, R8
LOAD:75A6FB7C 0A 20 A0 E1 MOV             R2, R10
LOAD:75A6FB80 0C 30 A0 E1 MOV             R3, R12
LOAD:75A6FB84 0B E0 A0 E1 MOV             LR, R11
LOAD:75A6FB88 3E FF 2F E1 BLX             LR
LOAD:75A6FB8C 00 C0 A0 E1 MOV             R12, R0                 ; jumptable 000098CC  case  32
LOAD:75A6FB90 00 C0 85 E5 STR             R12, [R5]
LOAD:75A6FB94 1C C0 9D E5 LDR             R12, [SP,#0x50+var_34

下好断点后一直F9会发现主是逻辑就是获取解压函数(uncompressc)解压第二个so数据,解压后就是解密第二个SO了。接下来就是在内存中加载第二个SO并获取JNI_OnLoad函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
LOAD:401030A8 F0 40 2D E9                 STMFD           SP!, {R4-R7,LR}
LOAD:401030AC C8 40 9F E5                 LDR             R4, =(off_40109EC0 - 0x401030C0)
LOAD:401030B0 9C D0 4D E2                 SUB             SP, SP, #0x9C
LOAD:401030B4 00 70 A0 E1                 MOV             R7, R0
LOAD:401030B8 04 40 9F E7                 LDR             R4, [PC,R4]
LOAD:401030BC 00 30 94 E5                 LDR             R3, [R4]
LOAD:401030C0
LOAD:401030C0             loc_401030C0                            ; DATA XREF: LOAD:4010317Co
LOAD:401030C0 01 60 A0 E1                 MOV             R6, R1
LOAD:401030C4 94 20 A0 E3                 MOV             R2, #0x94 ; '
LOAD:401030C8 00 10 A0 E3                 MOV             R1, #0
LOAD:401030CC 0D 00 A0 E1                 MOV             R0, SP
LOAD:401030D0 94 30 8D E5                 STR             R3, [SP,#0x94]
LOAD:401030D4 B5 ED FF EB                 BL               memset
LOAD:401030D8 A0 30 9F E5                 LDR             R3, =0x6F732E2A
LOAD:401030DC 00 20 A0 E3                 MOV             R2, #0
LOAD:401030E0 0D 00 A0 E1                 MOV             R0, SP
LOAD:401030E4 0C 30 8D E5                 STR             R3, [SP,#0xC]
LOAD:401030E8 10 20 CD E5                 STRB            R2, [SP,#0x10]
LOAD:401030EC 00 70 8D E5                 STR             R7, [SP]
LOAD:401030F0 04 60 8D E5                 STR             R6, [SP,#4]
LOAD:401030F4 EB F2 FF EB                 BL              sub_400FFCA8
LOAD:401030F8 84 30 9F E5                 LDR             R3, =(dword_4014D740 - 0x40103108)
LOAD:401030FC 84 10 9F E5                 LDR             R1, =(aMakekey - 0x4010310C)
LOAD:40103100 03 30 8F E0                 ADD             R3, PC, R3
LOAD:40103104 01 10 8F E0                 ADD             R1, PC, R1
LOAD:40103108
LOAD:40103108             loc_40103108                            ; DATA XREF: LOAD:40103184o
LOAD:40103108 00 00 83 E5                 STR             R0, [R3]
LOAD:4010310C
LOAD:4010310C             loc_4010310C                            ; DATA XREF: LOAD:40103188o
LOAD:4010310C F2 F2 FF EB                 BL              my_dlsym
LOAD:40103110 00 30 50 E2                 SUBS            R3, R0, #0
LOAD:40103114 08 00 00 0A                 BEQ             loc_4010313C
LOAD:40103118 FF 0E C3 E3                 BIC             R0, R3, #0xFF0
LOAD:4010311C 0F 00 C0 E3                 BIC             R0, R0, #0xF
LOAD:40103120 01 1A A0 E3                 MOV             R1, #0x1000
LOAD:40103124 03 20 A0 E3                 MOV             R2, #3
LOAD:40103128 7D 70 A0 E3                 MOV             R7, #0x7D ;  '}'
LOAD:4010312C 00 00 00 EF                 SVC             0
LOAD:40103130 54 20 9F E5                 LDR             R2, =(loc_40102C0C - 0x4010313C)
LOAD:40103134 02 20 8F E0                 ADD             R2, PC, R2
LOAD:40103138 00 20 83 E5                 STR             R2, [R3]
LOAD:4010313C
LOAD:4010313C             loc_4010313C                            ; DATA XREF: LOAD:4010318Co
LOAD:4010313C 4C 30 9F E5                 LDR             R3, =(dword_4014D740 - 0x4010314C)
LOAD:40103140 4C 10 9F E5                 LDR             R1, =(aJni_onload - 0x40103150)
LOAD:40103144 03 30 8F E0                 ADD             R3, PC, R3
LOAD:40103148 01 10 8F E0                 ADD             R1, PC, R1
LOAD:4010314C
LOAD:4010314C             loc_4010314C                            ; DATA XREF: LOAD:40103190o
LOAD:4010314C 00 00 93 E5                 LDR             R0, [R3]
LOAD:40103150
LOAD:40103150             loc_40103150                            ; DATA XREF: LOAD:40103194o
LOAD:40103150 E1 F2 FF EB                 BL              my_dlsym ; 获取第二个SO的JNI_OnLoad函数
LOAD:40103154 94 10 9D E5                 LDR             R1, [SP,#0x94]
LOAD:40103158 38 30 9F E5                 LDR             R3, =(dword_4014D744 - 0x40103168)
LOAD:4010315C 00 20 94 E5                 LDR             R2, [R4]
LOAD:40103160 03 30 8F E0                 ADD             R3, PC, R3
LOAD:40103164 02 00 51 E1                 CMP             R1, R2
LOAD:40103168
LOAD:40103168             loc_40103168                            ; DATA XREF: LOAD:40103198o
LOAD:40103168 00 00 83 E5                 STR             R0, [R3]
LOAD:4010316C 01 00 00 1A                 BNE             loc_40103178
LOAD:40103170 9C D0 8D E2                 ADD             SP, SP, #0x9C
LOAD:40103174 F0 80 BD E8                 LDMFD           SP!, {R4-R7,PC}
LOAD:40103178
LOAD:40103178             loc_40103178

获取到第二个SOJNI_OnLoad函数地址后走到Case 33处跳到第二个SOJNI_OnLoad去执行了,到此第一个SO的主要工作就基本完成了。

第二个SOJNI_OnLoad主要工作就是注册壳本身的 Native函数与被NativeonCreate函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//注册 Native onCreate  文件偏移 C252
debug190 76174000 761DA000 R . X D . byte 00  public  CODE 32 00 01
debug190:76180252             loc_76180252                            ; CODE XREF: debug190:7618028Aj
debug190:76180252                                                     ; debug190:76180292j ...
debug190:76180252 D7 23       MOVS            R3, #0xD7 ; '
debug190:76180254 09 98       LDR             R0, [SP,#0x24]
debug190:76180256 9B 00       LSLS            R3, R3, #2
debug190:76180258 01 68       LDR             R1, [R0]
debug190:7618025A CB 58       LDR             R3, [R1,R3]
debug190:7618025C 21 1C       MOVS            R1, R4
debug190:7618025E 9C 46       MOV             R12, R3
debug190:76180260 01 23       MOVS            R3, #1
debug190:76180262 E0 47       BLX             R12                     ; Native onCreate R2:存放注册信息,签名等
debug190:76180264 5B 46       MOV             R3, R11
debug190:76180266 01 22       MOVS            R2, #1
debug190:76180268 1B 78       LDRB            R3, [R3]
debug190:7618026A 1A 42       TST             R2, R3
debug190:7618026C 00 D0       BEQ             loc_76180270
debug190:7618026E A7 E1       B               loc_761805C0

下面是我通过hook注册函数打印出来的对应类的Native函数,在后面SO劫持会有说.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class -Ldalvik/ system /DexFile; ->name=getClassNameList  address=75FE10F9
class -Lcom/qihoo/bugreport/javacrash/CrashReportDataFactory; ->name=interface9  address=7600917D
class -Lcom/qihoo/util/StubApp296881940; ->name=mark  address=75FF75FD
class -Lcom/qihoo/util/StubApp296881940; ->name=mark  address=75FFBE45
class -Lcom/qihoo/util/StubApp296881940; ->name=mark  address=75FFBD91
class -Lcom/qihoo/util/StubApp296881940; ->name=interface10  address=75FFE819
class -Lcom/qihoo/util/StubApp296881940; ->name=interface5  address=75FE0375
class -Lcom/qihoo/util/StubApp296881940; ->name=interface6  address=75FDD95D
class -Lcom/qihoo/util/StubApp296881940; ->name=interface7  address=75FE1801
class -Lcom/qihoo/util/StubApp296881940; ->name=interface8  address=75FDDCFD
class -Lcom/mob/tools/MobUIShell; ->name=onCreate  address=75FDF729
class -Lcom/txy/anywhere/activity/GreenHandGuideActivity; ->name=onCreate  address=75FDF6F1
class -Lcom/txy/anywhere/activity/MainActivity; ->name=onCreate  address=75FDF6B9
class -Lcom/txy/anywhere/activity/PanoramaActivity; ->name=onCreate  address=75FDF681
class -Lcom/txy/anywhere/activity/move/MoveDetailActivity; ->name=onCreate  address=75FDF649
class -Lcom/txy/anywhere/activity/move/MoveSettingsMapChoiceActivity; ->name=onCreate  address=75FDF611
class -Lcom/txy/anywhere/activity/specific/SpecificProgramDetailActivity; ->name=onCreate  address=75FDF5D9
class -Lcom/txy/anywhere/wxapi/WXPayEntryActivity; ->name=onCreate  address=75FDF5A1
class -Lcom/common/customp/PullEntry; ->name=getAppkey  address=75FE1439

接下来就是解密原始DEX了,解密逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//解密dex
  密钥 A0 EC E6 CC FB A6 F1 A2  EC A3 E7 AF E1 5A EA 2F
debug190:761BB0E0             ; ---------------------------------------------------------------------------
debug190:761BB0E0 F0 B5       PUSH            {R4-R7,LR}
debug190:761BB0E2 57 46       MOV             R7, R10
debug190:761BB0E4 4E 46       MOV             R6, R9
debug190:761BB0E6 45 46       MOV             R5, R8
debug190:761BB0E8 E0 B4       PUSH            {R5-R7}
debug190:761BB0EA 16 1C       MOVS            R6, R2
debug190:761BB0EC 80 22       MOVS            R2, #0x80 ;  '€'
debug190:761BB0EE 80 46       MOV             R8, R0
debug190:761BB0F0 24 4C       LDR             R4, =(dword_761DCD20 - 0x761BB0FA)
debug190:761BB0F2 C2 B0       SUB             SP, SP, #0x108
debug190:761BB0F4 1F 1C       MOVS            R7, R3
debug190:761BB0F6 7C 44       ADD             R4, PC ; dword_761DCD20
debug190:761BB0F8 4A 9B       LDR             R3, [SP,#0x128]
debug190:761BB0FA 24 68       LDR             R4, [R4]
debug190:761BB0FC 01 AD       ADD             R5, SP, #4
debug190:761BB0FE 99 46       MOV             R9, R3
debug190:761BB100 23 68       LDR             R3, [R4]
debug190:761BB102 8A 46       MOV             R10, R1
debug190:761BB104 28 1C       MOVS            R0, R5
debug190:761BB106 00 21       MOVS            R1, #0
debug190:761BB108 52 00       LSLS            R2, R2, #1
debug190:761BB10A 41 93       STR             R3, [SP,#0x104]
debug190:761BB10C 10 F0 4E FA BL              j_j_memset_1
debug190:761BB110 43 46       MOV             R3, R8
debug190:761BB112 59 68       LDR             R1, [R3,#4]
debug190:761BB114 00 29       CMP             R1, #0
debug190:761BB116 2D D0       BEQ             loc_761BB174
debug190:761BB118 28 1C       MOVS            R0, R5
debug190:761BB11A 10 22       MOVS            R2, #0x10
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
debug190:761BB120 3B 68       LDR             R3, [R7]
debug190:761BB122 00 2B       CMP             R3, #0
debug190:761BB124 11 D0       BEQ             loc_761BB14A
debug190:761BB126 28 1C       MOVS            R0, R5
debug190:761BB128 51 46       MOV             R1, R10
debug190:761BB12A 32 1C       MOVS            R2, R6
debug190:761BB12C 00 F0 D6 F8 BL              Rc4_Dec
debug190:761BB130 4B 46       MOV             R3, R9
debug190:761BB132 00 20       MOVS            R0, #0
debug190:761BB134 1E 60       STR             R6, [R3]
debug190:761BB136
debug190:761BB136             loc_761BB136                            ; CODE XREF: debug190:761BB172j
debug190:761BB136                                                     ; debug190:761BB178j ...
debug190:761BB136 41 9A       LDR             R2, [SP,#0x104]
debug190:761BB138 23 68       LDR             R3, [R4]
debug190:761BB13A 9A 42       CMP             R2, R3
debug190:761BB13C 20 D1       BNE             loc_761BB180
debug190:761BB13E 42 B0       ADD             SP, SP, #0x108
debug190:761BB140 1C BC       POP             {R2-R4}
debug190:761BB142 90 46       MOV             R8, R2
debug190:761BB144 99 46       MOV             R9, R3
debug190:761BB146 A2 46       MOV             R10, R4
debug190:761BB148 F0 BD       POP             {R4-R7,PC}
debug190:761BB14A             ; ---------------------------------------------------------------------------
debug190:761BB14A
debug190:761BB14A             loc_761BB14A                            ; CODE XREF: debug190:761BB124j
debug190:761BB14A 30 1C       MOVS            R0, R6
debug190:761BB14C 10 F0 96 FA BL              malloc_0
debug190:761BB150 80 46       MOV             R8, R0
debug190:761BB152 00 28       CMP             R0, #0
debug190:761BB154 11 D0       BEQ             loc_761BB17A
debug190:761BB156 51 46       MOV             R1, R10
debug190:761BB158 32 1C       MOVS            R2, R6
debug190:761BB15A 10 F0 17 FA BL              memcpy_0                ; 拷贝加密的dex内容
debug190:761BB15E 28 1C       MOVS            R0, R5
debug190:761BB160 41 46       MOV             R1, R8
debug190:761BB162 32 1C       MOVS            R2, R6
debug190:761BB164 00 F0 BA F8 BL              Rc4_Dec                 ; 解密Dex内容
debug190:761BB168 4B 46       MOV             R3, R9
debug190:761BB16A 1E 60       STR             R6, [R3]
debug190:761BB16C 43 46       MOV             R3, R8
debug190:761BB16E 00 20       MOVS            R0, #0
debug190:761BB170 3B 60       STR             R3, [R7]
debug190:761BB172 E0 E7       B               loc_761BB136
debug190:761BB174             ; ---------------------------------------------------------------------------
debug190:761BB174
  
//再次解密dex  文件偏移  EDF4
debug190 76174000 761DA000 R . X D . byte 00  public  CODE 32 00 01
debug190:76182DF4             loc_76182DF4                            ; CODE XREF: debug190:76182DB6j
debug190:76182DF4 51 46       MOV             R1, R10
debug190:76182DF6 34 F0 F3 FA BL              loc_761B73E0
debug190:76182DFA 00 28       CMP             R0, #0
debug190:76182DFC DC DB       BLT             loc_76182DB8
debug190:76182DFE 53 46       MOV             R3, R10
debug190:76182E00 1B 68       LDR             R3, [R3]
debug190:76182E02 58 46       MOV             R0, R11
debug190:76182E04 9A 46       MOV             R10, R3
debug190:76182E06 5B 46       MOV             R3, R11
debug190:76182E08 1B 68       LDR             R3, [R3]
debug190:76182E0A 05 99       LDR             R1, [SP,#0x14]
debug190:76182E0C 06 9A       LDR             R2, [SP,#0x18]
debug190:76182E0E DB 68       LDR             R3, [R3,#0xC]
debug190:76182E10 9C 46       MOV             R12, R3
debug190:76182E12 53 46       MOV             R3, R10
debug190:76182E14 E0 47       BLX             R12                     ; 又是解密
debug190:76182E16 00 28       CMP             R0, #0
debug190:76182E18 CE DB       BLT             loc_76182DB8
debug190:76182E1A 05 98       LDR             R0, [SP,#0x14]
debug190:76182E1C 48 F0 0E FC BL              free_0
debug190:76182E20 5B 46       MOV             R3, R11
debug190:76182E22 1B 68       LDR             R3, [R3]
debug190:76182E24 58 46       MOV             R0, R11
debug190:76182E26 5B 68       LDR             R3, [R3,#4]
debug190:76182E28 98 47       BLX             R3
debug190:76182E2A 03 9B       LDR             R3, [SP,#0xC]
debug190:76182E2C 4D 44       ADD             R5, R9
debug190:76182E2E 53 44       ADD             R3, R10
debug190:76182E30 18 1C       MOVS            R0, R3
debug190:76182E32 9D 42       CMP             R5, R3
debug190:76182E34 05 D0       BEQ             loc_76182E42            ; 解密dex
debug190:76182E36 00 2E       CMP             R6, #0
debug190:76182E38 03 D0       BEQ             loc_76182E42            ; 解密dex
debug190:76182E3A 29 1C       MOVS            R1, R5
debug190:76182E3C 32 1C       MOVS            R2, R6
debug190:76182E3E FE F7 49 FF BL              loc_76181CD4
  
  
//解密dex 头0x70字节  文件偏移  EE42
debug190 76174000 761DA000 R . X D . byte 00  public  CODE 32 00 01
debug190:76182E42             loc_76182E42                            ; CODE XREF: sub_76182C48+1ECj
debug190:76182E42                                                     ; sub_76182C48+1F0j
debug190:76182E42 7A 69       LDR             R2, [R7,#0x14]          ; 解密dex
debug190:76182E44 50 46       MOV             R0, R10
debug190:76182E46 70 21       MOVS            R1, #0x70 ;  'p'
debug190:76182E48 38 F0 90 F8 BL              sub_761BAF6C            ; 解密dex头70字节
debug190:76182E4C 01 25       MOVS            R5, #1
debug190:76182E4E B9 E7       B               loc_76182DC4

这时就可以将DEX数据dump出来,其实后面每个onCreate中也会出现明文的DEX

dump出来的dex通过JDE反编译后发现很多Activity中的onCreate函数变成了Native了,从上面打印的类与对应函数可以看得出。

0x03:被Native后的onCreate分析尝试修复与猜想

在上面分析到注册Native函数时就对onCreate函数下好了断点,直接F9来到onCreate断下。

1
2
3
4
5
6
7
8
9
//出现解密后dex 文件偏移 A910
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DD0910             loc_75DD0910                            ; CODE XREF: debug098:75DD08F2j
debug098:75DD0910 05 9B       LDR             R3, [SP,#0x14]
debug098:75DD0912 1C 20       MOVS            R0, #0x1C
debug098:75DD0914 1F 68       LDR             R7, [R3]                ; 出现解密后dex
debug098:75DD0916 B3 68       LDR             R3, [R6,#8]
debug098:75DD0918 9C 46       MOV             R12, R3
debug098:75DD091A 67 44       ADD             R7, R12

F8一直单步走到定位指令的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//定位到dex中的onCreate方法在内存中的指令,获取dex中的onCreate指令并解密。 文件偏移 2F9F8
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DF59F8
debug098:75DF59F8             loc_75DF59F8                            ; CODE XREF: debug098:75DF5988j
debug098:75DF59F8                                                     ; debug098:75DF59CAj
debug098:75DF59F8 A3 68       LDR             R3, [R4,#8]
debug098:75DF59FA 10 33       ADDS            R3, #0x10               ; 定位到onCreate指令
debug098:75DF59FC 1E 1C       MOVS            R6, R3
debug098:75DF59FE 0C 93       STR             R3, [SP,#0x30]
debug098:75DF5A00
debug098:75DF5A00             loc_75DF5A00                            ; CODE XREF: debug098:75DF5E54j
debug098:75DF5A00                                                     ; debug098:75DF5EF4j ...
debug098:75DF5A00 0C 9B       LDR             R3, [SP,#0x30]
debug098:75DF5A02 00 21       MOVS            R1, #0
debug098:75DF5A04 F0 1A       SUBS            R0, R6, R3
debug098:75DF5A06 43 10       ASRS            R3, R0, #1
debug098:75DF5A08 30 1C       MOVS            R0, R6
debug098:75DF5A0A 06 93       STR             R3, [SP,#0x18]
debug098:75DF5A0C FE F7 29 FE BL              GetCode                 ; 获取dex中的onCreate指令并解密
debug098:75DF5A10 05 90       STR             R0, [SP,#0x14]
debug098:75DF5A12 00 06       LSLS            R0, R0, #0x18
debug098:75DF5A14 00 0E       LSRS            R0, R0, #0x18
debug098:75DF5A16 01 38       SUBS            R0, #1                  ;  switch  255 cases
debug098:75DF5A18 FE 28       CMP             R0, #0xFE ; '                ;判断是否超出表示指令范围
debug098:75DF5A1A 01 D9       BLS             loc_75DF5A20
debug098:75DF5A1C 02 F0 8D FF BL              def_75DF5A20            ; jumptable 75DF5A20  default  case

比如 下面是dexOnCreate的指令,

读取上面的指令并解密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//获取指令,并解密 文件偏移 2E662
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DF4662             GetCode                                 ; CODE XREF: debug098:75DF5A0Cp
debug098:75DF4662                                                     ; debug098:75DF5E40p ...
debug098:75DF4662 38 B5       PUSH            {R3-R5,LR}
debug098:75DF4664 05 1C       MOVS            R5, R0
debug098:75DF4666 0C 1C       MOVS            R4, R1
debug098:75DF4668 FD F7 D6 FC BL              loc_75DF2018
debug098:75DF466C 00 7E       LDRB            R0, [R0,#0x18]          ; 密钥
debug098:75DF466E 64 00       LSLS            R4, R4, #1
debug098:75DF4670 02 1C       MOVS            R2, R0
debug098:75DF4672 03 02       LSLS            R3, R0, #8
debug098:75DF4674 1A 43       ORRS            R2, R3
debug098:75DF4676 63 5B       LDRH            R3, [R4,R5]             ; 取指令
debug098:75DF4678 5A 40       EORS            R2, R3                ;解密
debug098:75DF467A 10 1C       MOVS            R0, R2
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测试,从第一个中找到了如下的指令对应关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
真实指令     自定义指令
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
2
3
4
jclass clazz=(*env)->FindClass(env, "com/example/test" );
     jmethodID methodId=(*env)->GetMethodID(env,clazz, "Add" , "(II)I" );
     // jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
     (*env)->CallIntMethod(env,jobject,methodId,3,5);

主要流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//获取dex中的类名、函数名、签名  文件偏移 2E648
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DF4648
debug098:75DF4648             GetString                               ; CODE XREF: debug098:75DF4C52p
debug098:75DF4648                                                     ; debug098:75DF4C78p ...
debug098:75DF4648 03 69       LDR             R3, [R0,#0x10]
debug098:75DF464A 02 68       LDR             R2, [R0]
debug098:75DF464C 89 00       LSLS            R1, R1, #2
debug098:75DF464E DB 6B       LDR             R3, [R3,#0x3C]
debug098:75DF4650 C9 18       ADDS            R1, R1, R3
debug098:75DF4652 8B 58       LDR             R3, [R1,R2]
debug098:75DF4654 D0 18       ADDS            R0, R2, R3
debug098:75DF4656
debug098:75DF4656             loc_75DF4656                            ; CODE XREF: debug098:75DF465Ej
debug098:75DF4656 01 30       ADDS            R0, #1
debug098:75DF4658 43 1E       SUBS            R3, R0, #1
debug098:75DF465A 1B 78       LDRB            R3, [R3]
debug098:75DF465C 7F 2B       CMP             R3, #0x7F ;  ''
debug098:75DF465E FA D8       BHI             loc_75DF4656
debug098:75DF4660 70 47       BX              LR
  
FindClass  文件偏移 18956
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DDE956             sub_75DDE956                            ; CODE XREF: debug098:75DF4CBAp
debug098:75DDE956                                                     ; debug098:75DF5092p ...
debug098:75DDE956 08 B5       PUSH            {R3,LR}
debug098:75DDE958 03 68       LDR             R3, [R0]
debug098:75DDE95A 9B 69       LDR             R3, [R3,#0x18]
debug098:75DDE95C 98 47       BLX             R3                      ; FindClass
debug098:75DDE95E 08 BD       POP             {R3,PC}
  
ExceptionCheck 文件偏移  1802E
debug096 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug096:75DDE02E             loc_75DDE02E                            ; CODE XREF: debug096:75DDE046p
debug096:75DDE02E                                                     ; debug096:75DF4BECp ...
debug096:75DDE02E 08 B5       PUSH            {R3,LR}
debug096:75DDE030 E4 23       MOVS            R3, #0xE4 ; '
debug096:75DDE032 02 68       LDR             R2, [R0]
debug096:75DDE034 9B 00       LSLS            R3, R3, #2
debug096:75DDE036 D3 58       LDR             R3, [R2,R3]
debug096:75DDE038 98 47       BLX             R3                      ; ExceptionCheck
debug096:75DDE03A 08 BD       POP             {R3,PC}
  
//GetMethodID 文件偏移
debug098 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug098:75DFB11E             loc_75DFB11E                            ; CODE XREF: debug098:75DFB10Ej
debug098:75DFB11E 00 98       LDR             R0, [SP]
debug098:75DFB120 04 99       LDR             R1, [SP,#0x10]
debug098:75DFB122 01 9A       LDR             R2, [SP,#4]
debug098:75DFB124 B0 47       BLX             R6                      ; GetMethodID
debug098:75DFB126 06 1C       MOVS            R6, R0
debug098:75DFB128 28 1C       MOVS            R0, R5
  
//DeleteLocalRef 文件偏移 18018
debug096 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug096:75DDE018             loc_75DDE018                            ; CODE XREF: debug096:75DDE062p
debug096:75DDE018                                                     ; debug096:75DF528Ap ...
debug096:75DDE018 08 B5       PUSH            {R3,LR}
debug096:75DDE01A 03 68       LDR             R3, [R0]
debug096:75DDE01C DB 6D       LDR             R3, [R3,#0x5C]
debug096:75DDE01E 98 47       BLX             R3                      ; DeleteLocalRef
debug096:75DDE020 08 BD       POP             {R3,PC}
  
//CallNonvirtualVoidMethodA  文件偏移 2EFEC
debug096 75DC6000 75E2C000 R . X D . byte 00  public  CODE 32 00 01
debug096:75DF4FEC             loc_75DF4FEC                            ; CODE XREF: sub_75DF4B50:loc_75DF4F0Cj
debug096:75DF4FEC 23 68       LDR             R3, [R4]                ; jumptable 75DF4F0C  case  86
debug096:75DF4FEE 02 9A       LDR             R2, [SP,#0x108+var_100]
debug096:75DF4FF0 20 1C       MOVS            R0, R4
debug096:75DF4FF2 FC 33       ADDS            R3, #0xFC ; '
debug096:75DF4FF4 00 92       STR             R2, [SP,#0x108+var_108]
debug096:75DF4FF6 9D 6F       LDR             R5, [R3,#0x78]
debug096:75DF4FF8 04 99       LDR             R1, [SP,#0x108+var_F8]
debug096:75DF4FFA 18 9A       LDR             R2, [SP,#0x108+var_A8]
debug096:75DF4FFC 03 9B       LDR             R3, [SP,#0x108+var_FC]
debug096:75DF4FFE A8 47       BLX             R5                      ; CallNonvirtualVoidMethodA
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接口执行。

0x04:通过SO劫持跳过烦人的反调试快速到达要分析的函数

我想要是你玩过windows上破解都知道DLL劫持吧,先伪造一个同名的DLL,提供同样的输出函数,每个输出函数中转向真正的DLL函数,不错,我也是用这种方法来实现注入SOHook函数,过掉反调试,会省去很多时间反调试上面,直接到你想要分析的关键函数。

分析壳java层代码就知道它主要是通过读取从资源目中把壳so拷到指定目录中加载。

我的做法是hook getAssets().open()函数,将资源中的so替换成我的so,然后将壳so改名放在app-lib目录中给我的so加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
                   XposedHelpers.findAndHookMethod(
                                             AssetManager. class //被Hook函数所在的类
                                             "open" ,      //被Hook函数的名称
                                             String. class ,
                                             new  XC_MethodHook(){
                                                      @Override
                                                      protected  void  beforeHookedMethod(MethodHookParam param)
                                                                       throws Throwable {
                                                              // Hook函数之前执行的代码      
                                                              String path = param.args[0].toString();
                                                              if (path.equals( "libjiagu.so" )){
                                                                       param.args[0] =  "/data/local/tmp/libjiagu.so" ;
                                                              }
                                                              Yfw_DebugLog.d( "open  beforeHookedMethod--->0 " +param.args[0].toString());
                                                      }
                                                      
                                                      @Override
                                                      protected  void  afterHookedMethod(MethodHookParam param) throws Throwable {
                                                              
                                                              File file =  new  File(param.args[0].toString());  
                                                          InputStream input =  new  FileInputStream(file);
                                                          param.setResult(input);
                                                              Yfw_DebugLog.d( "open afterHookedMethod--->" +param.getResult().toString());
  
                                                      }
                                             });
  
So的代码也很简单:
/*
调用原始JNI_OnLoad
*/
jint LoadSo_InitJni( char * SoFilePath, JavaVM* vm,  void * reserved)
{
          jint ret;
          void  *pHandle = NULL;
          pHandle = dlopen(SoFilePath, RTLD_LAZY);
          if  (NULL == pHandle)
          {
                   LOGD( "dlopen %s error!" , SoFilePath);
                   return  NULL;
          }
          _JNI_OnLoad old_JNI_OnLoad = NULL;
          old_JNI_OnLoad = (_JNI_OnLoad)dlsym(pHandle,  "JNI_OnLoad" );
          if  (NULL == old_JNI_OnLoad)
          {
                   LOGD( "old_JNI_OnLoad error!" );
                   return  NULL;
          }
          LOGD( "old_JNI_OnLoad address=%X   env...%X" , old_JNI_OnLoad, g_env);
          ret = old_JNI_OnLoad(vm, reserved);
          LOGD( "old_JNI_OnLoad end...%d" , ret);
          return  ret;
  
}
  
Hook open函数过反调试
int  new_open( const  char  *pathname,  int  oflag, mode_t mode)
{
          if  (NULL == pathname)
          {
                   goto  exitret;
          }
  
          LOGD( "new_open..%s" , pathname); 
          if  ( strstr (pathname,  "net/tcp" ) != NULL) 
          {
                   return  0;
          }
          if  ( strstr (pathname,  "status" ) != NULL)
          {
                   return  0;
          }
          if  ( strstr (pathname,  "stat" ) != NULL)
          {
                   return  0;
          }
          if  ( strstr (pathname,  "schedstat" ) != NULL)
          {
                   return  0;
          }
  
exitret:
          return  old_open(pathname, oflag, mode);
}
Hook  time 过反调试
time_t  new_time( time_t  * timer) {
          LOGD( "new_time------->" );
          return  0;
}
Hook dvmUseJNIBridge 方便在 native下断点,打印出对应类的方法
void  new_dvmUseJNIBridge(Method* method,  void * func)
{
          LOGD( "my_dvmUseJNIBridge class-%s ->name=%s  address=%08X" , method->clazz->descriptor, method->name, func);
          //LOGD("my_dvmUseJNIBridge nativeFunc-%s", method->nativeFunc);
          if  (0 ==  strcmp (method->name,  "getClassNameList" ))
          {
                   sleep(8);  //等待附加调试器
          }
          return  old_dvmUseJNIBridge(method, func);
}

这样当我们的so加载时用IDA附加上就可以在new_dvmUseJNIBridge下断点,就能在想要分析的Native函数上下断点了。


上面的代码就完成了整个劫持的过程,开心地调试吧!!!!!!

0x05:静态分析APP的注册验证流程与编写Xposed插件。

通过JEB反编译该应用dump出来的的classes.dex文件,直接搜索登录时用到的网址字符串 7658/api/entrance,找到如下的字符串,双击第一个字符串进去 。


其中的a函数是返回网址的,当参数为10时是要找到网址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   public  static  String a( int  arg1) {
         switch (arg1) {
             case  10 : {
                 goto  label_4;  //返回登录地址
             }
             case  11 : {
                 goto  label_6;
             }
             case  12 : {
                 goto  label_8;
             }
             case  13 : {
                 goto  label_10;
             }
             case  14 : {
                 goto  label_12;
             }
             case  15 : {
                 goto  label_14;
             }
             case  16 : {
                 goto  label_16;
             }
             case  17 : {
                 goto  label_18;
             }
             case  19 : {
                 goto  label_20;
             }
             case  20 : {
                 goto  label_22;
             }
         }

JEB快捷键 x 找到传入参数为10调用上面函数的地方,找到如下的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public  class  extends  j {
     private  static  String login;
     private  ThreadPoolExecutor d;
     private  static  g e;
  
     static  {
         g.login = i.a( 10 );
         g.e =  null ;
}
在这个类中找到了网络登录请求并解密返回值的函数。
private  String b(h arg5, String arg6) {
         String v0 =  null ;
         if (arg5 !=  null  && (arg6 !=  null  && (AndroidHelper.isNetworkActive( this .b)))) {
             String v1 =  this .a(arg6, arg5.a());   // 组合请求参数
             if (v1 ==  null ) {
                 return  v0;
             }
  
             try  {
                 JSONObject v2 =  new  JSONObject(a.a(g.login_address, v1.getBytes( "UTF-8" ),  this .c));   // 传入网址,应该就是网络请求了
                 if (v2.getInt( "status" ) !=  0 ) {
                     return  v0;
                 }
  
                 v0 = j.b(v2.getString( "data" ));   // 解密登录返回值
             }
             catch (Exception v0_1) {
                 d.a(((Throwable)v0_1));
                 v0 =  "server_error" ;
             }
         }
  
         return  v0;
     }

分析下请求参数都有些什么东西,组合请求参数的函数实现在父类j中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  String a(String arg9,  int  arg10) {
         String v0 =  null ;
         if (arg9 !=  null ) {
             String v1 = j.a( this .c(arg9));   // aes加密后再base64
             if (TextUtils.isEmpty(((CharSequence)v1))) {
                 return  v0;
             }
  
             try  {
                 StringBuffer v2 =  new  StringBuffer();
                 v2.append( "body=" );
                 v2.append(URLEncoder.encode(v1,  "UTF-8" ));
                 v2.append( "&t1=" );
                 v2.append(System.currentTimeMillis() /  1000 );
                 v2.append( "&t2=" );
                 v2.append( this .d(arg9));
                 v2.append( "&type=" );
                 v2.append(arg10);
                 v2.append( "&flag=" );
                 v2.append( 4 );
                 v0 = v2.toString();
             }
             catch (UnsupportedEncodingException v1_1) {
                 d.a(((Throwable)v1_1));
             }
         }
  
         return  v0;
     }

Xposed hook 加函数打印参数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
             //aes加密参数
             XposedHelpers.findAndHookMethod(Application. class "attach" , Context. class new  XC_MethodHook() {
                 @Override
                 protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
                     ClassLoader cl = ((Context)param.args[ 0 ]).getClassLoader();
                     Class<?> hookclass =  null ;
                     try  {
                         hookclass = cl.loadClass( "com.txyapp.client.j" );
                     catch  (Exception e) {
                        Yfw_DebugLog.d( "寻找com.txyapp.client.j 报错" + e);
                         return ;
                     }
                     Yfw_DebugLog.d( "寻找com.txyapp.client.j成功" );
                    
                    XposedHelpers.findAndHookMethod(
                            hookclass,  //被Hook函数所在的类
                            "a" ,      //被Hook函数的名称
                            String. class ,
                            new  XC_MethodHook(){
                                @Override
                                protected  void  beforeHookedMethod(MethodHookParam param)
                                        throws  Throwable {
                                    // Hook函数之前执行的代码  
                                    Yfw_DebugLog.d( "a_Aesenc_data  beforeHookedMethod--->0 " +param.args[ 0 ].toString());
                                }
                                
                                @Override
                                protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
                                    Yfw_DebugLog.d( "a_Aesenc_data afterHookedMethod--->" +param.getResult().toString());
                                }
                            });
                 }
             });

主要是将用户名密码,还有一些机器信息aesbase加密后请求过去,格式如下:

1
{ "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 }}

然后服务器返回一些加密后的数据,从上面的gb函数中可以看出,判断如果请求成功,就解密返回数据,通过hook该解密函数得到解密后的数据格式为json。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
        XposedHelpers.findAndHookMethod(Application. class "attach" , Context. class new  XC_MethodHook() {
                 @Override
                 protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
                     ClassLoader cl = ((Context)param.args[ 0 ]).getClassLoader();
                     Class<?> hookclass =  null ;
                     try  {
                         hookclass = cl.loadClass( "com.txyapp.client.j" );
                     catch  (Exception e) {
                       Yfw_DebugLog.d( "寻找com.txyapp.client.j 报错" + e);
                         return ;
                     }
                     Yfw_DebugLog.d( "寻找com.txyapp.client.j成功" );
                 //解密vip
                 XposedHelpers.findAndHookMethod(
                        hookclass,  //被Hook函数所在的类
                        "b" ,      //被Hook函数的名称
                        String. class ,
                        new  XC_MethodHook(){
                           @Override
                           protected  void  beforeHookedMethod(MethodHookParam param)
                                  throws  Throwable {
                               // Hook函数之前执行的代码  
                               Yfw_DebugLog.d( "b_Aesdec_data  beforeHookedMethod--->0 " +param.args[ 0 ].toString());
                           }
                           
                           @Override
                           protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
                               Yfw_DebugLog.d( "b_Aesdec_data afterHookedMethod--->" +param.getResult().toString());
                    
                               JSONObject jsonObj =  new  JSONObject(param.getResult().toString());
                               if ( 0  == ( int )jsonObj.get( "vip" ) ){
                                  jsonObj.put( "vip" 1 );
                                   jsonObj.put( "expiretime" 1999077441 ); // 2033-05-07
                        
                                    Yfw_DebugLog.d( "b_Aesdec_data result--->" +jsonObj.toString());
                                    viptag =  1 ;
                                    param.setResult(jsonObj.toString());
                               }
                               
                           }
                        });
                 }
             });
//解密后数据
{ "status" : 0 , "token" : "49436a44d85f46c7e458a6b071af470a" , "username" : "crack8888" , "vip" : 0 , "expiretime" : 0 }

从上面格式可以看出 vip:=0代表不是vip, expiretime:到期时间,所以我的在hook函数中将这两个参数修改如下。

1
{ "vip" : 1 , "username" : "crack8888" , "token" : "db9ea6e12936463d722fb7bcda7e97ab" , "status" : 0 , "expiretime" : 1999077441 }

这样就成功破解了,会员功能也正常使用。

还有一个地方就是显示个人信息,hook函数进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
            //刷新个人信息显示
            XposedHelpers.findAndHookMethod(Application. class "attach" , Context. class new  XC_MethodHook() {
                 @Override
                 protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
                     ClassLoader cl = ((Context)param.args[ 0 ]).getClassLoader();
                     Class<?> hookclass =  null ;
                     try  {
                         hookclass = cl.loadClass( "com.txy.anywhere.activity.login.PersonalInfoActivity" );
                     catch  (Exception e) {
                       Yfw_DebugLog.d( "寻找com.txy.anywhere.activity.login.PersonalInfoActivity报错" + e);
                         return ;
                     }
                     Yfw_DebugLog.d( "寻找com.txy.anywhere.activity.login.PersonalInfoActivity成功" );
                 //判断是否为vip
                 XposedHelpers.findAndHookMethod(
                        hookclass,  //被Hook函数所在的类
                        "a" ,      //被Hook函数的名称
                        String. class ,
                        new  XC_MethodHook(){
                           @Override
                           protected  void  beforeHookedMethod(MethodHookParam param)
                                  throws  Throwable {
                               // Hook函数之前执行的代码  
                               Yfw_DebugLog.d( "login  beforeHookedMethod--->0 " +param.args[ 0 ].toString());
                               
                               
                               JSONObject container1 =  new  JSONObject();
  
                               JSONObject v0_1 =  new  JSONObject(param.args[ 0 ].toString());
                               JSONObject v0_2 =  new  JSONObject(param.args[ 0 ].toString());
                               v0_2 = v0_1.getJSONObject( "user_info" );
                               v0_2.put( "vip" 1 ); //是否为vip 0:为普通会员, 1:为vip
                               v0_2.put( "expire_time" 1999077441 ); //到期时间
                               v0_2.put( "phonenumber" "110" );
                               v0_1.put( "user_info" , v0_2);
                               param.args[ 0 ] = v0_1.toString();
                               Yfw_DebugLog.d( "login  beforeHookedMethod 1--->0 " +param.args[ 0 ].toString());
  
                           }
                           
                           @Override
                           protected  void  afterHookedMethod(MethodHookParam param)  throws  Throwable {
          
                               Yfw_DebugLog.d( "login afterHookedMethod--->" );
                           }
                        });
                 }
             });

到这里就算破解完成了,简单测试了会员功能,都能正常使用。

0x06:总结

由于水平有限,未能更好的分析与表达,不过,通过前面几部分的简单分析,其实我们已经知道只是Native onCreate函数是不够的,主要还是得保护好关键函数不被分析。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值