工具與環境
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:殼反調試分析
通過第一部分的介紹,發現軟件被加固了,接下來就是要脫掉殼才能更好地分析下去,掛上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
以上就是過掉所有主要的反調試了
0x02:殼大致流程分析與Dump出解密后的dex
過掉反調試后在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了,從上面打印的類與對應函數可以看得出。
0x03:被Native后的onCreate分析, 嘗試修復與猜想
在上面分析到注冊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接口執行。
0x04:通過SO劫持跳過煩人的反調試快速到達要分析的函數
我想要是你玩過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函數上下斷點了。
上面的代碼就完成了整個劫持的過程,開心地調試吧!!!!!!
0x05:靜態分析APP的注冊驗證流程與編寫Xposed插件。
通過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 });
到這里就算破解完成了,簡單測試了會員功能,都能正常使用。
0x06:總結
由於水平有限,未能更好的分析與表達,不過,通過前面幾部分的簡單分析,其實我們已經知道只是Native onCreate函數是不夠的,主要還是得保護好關鍵函數不被分析。
百度網盤 鏈接: https://pan.baidu.com/s/1c2KiYsg 密碼: 2rft