欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

某地理位置模拟APP从壳流程分析到破解

某地理位置模拟APP从壳流程分析到破解

工具与环境

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,直接将函数改成返回,代码如下。

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下好断点,代码如下:

//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函数。

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函数。

//注册 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劫持会有说.

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了,解密逻辑如下:

//解密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 ; ' ?#39;

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断下。

//出现解密后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一直单步走到定位指令的地方。

//定位到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的指令,

读取上面的指令并解密。

//获取指令,并解密 文件偏移 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操作码

参数个数

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接口函数来实现解析执行。

如果指令类型是调用函数的话就会做如下动作,其它的调用其它接口(简单示例)

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);

主要流程如下:

//获取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加载。

                  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时是要找到网址。

  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调用上面函数的地方,找到如下的类:

public class g 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中。

 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 加函数打印参数,

            //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加密后请求过去,格式如下:

{"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。

       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函数中将这两个参数修改如下。

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

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

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

           //刷新个人信息显示

           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函数是不够的,主要还是得保护好关键函数不被分析。


上传的附件 bin.zip.003
bin.zip.002
bin.zip.001
bin.zip.004
bin.zip.005
bin.zip.006
bin.zip.007
bin.zip.008
bin.zip.009
bin.zip.013
bin.zip.011
bin.zip.010
bin.zip.012

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功