|
本帖最后由 执着 于 2016-4-29 17:48 编辑
这是我给我学生准备的一个例子。
音悦台在下载的时候会发一个包:
http://mapi.yinyuetai.com/download/statistics.json?D-A=0&s=3dcfae0fd9ff76e83ef2c2eef357ebdebK37u4&id=2553946
其中id是下载文件的id,s是一串在SO中经过变换的参数。
交流群:471525564
通过搜索download关键字能很快定位到下载位置,然后能找到com/yinyuetai/utils/S2K这个类完成了加密工作。
这个类中有一个native函数:
private static native s2k(Ljava/lang/String;)Ljava/lang/String;
SO库名是:
s2k_chris
用IDA载入这个SO,在导出表中搜索“Java”,能快速筛选所有对Java层的接口。
导出函数:
Java_com_yinyuetai_utils_S2K_s2k
这个函数的流程很简单:
1、Log输出输入参数。
2、调用s2k函数。
3、Log输出s2k函数返回结果
4、对char * 字符串进行转换并返回。
既然这个SO已经为我们提供了输出功能,那就再好不过了,打开logcat直接看输出:
注意“+”不属于参数的一部分,这是开发者为了好看吧。
输入参数是一串我们看不懂的东西,其实这个是服务器返回的,查询这个字符串的封包如下(id就是下载文件的ID了):
GET http://mapi.yinyuetai.com/download/statistics.json?D-A=0&id=2553946
协yi头自行抓包处理,返回json数据,s字段就是输入参数。
s2k(const char *a1, char *a2)才是核心函数,我们继续分析:
a1是输入参数,a2应该是一个很长的缓冲区,关键位置已经给出详细注释- .text:0000190C ; int __fastcall s2k(const char *a1, char *a2)
- .text:0000190C EXPORT s2k
- .text:0000190C s2k ; CODE XREF: Java_com_yinyuetai_utils_S2K_s2k+5Ap
- .text:0000190C
- .text:0000190C var_E0 = -0xE0
- .text:0000190C var_DC = -0xDC
- .text:0000190C var_D8 = -0xD8
- .text:0000190C var_D4 = -0xD4
- .text:0000190C var_D0 = -0xD0
- .text:0000190C var_CC = -0xCC
- .text:0000190C var_C8 = -0xC8
- .text:0000190C var_C4 = -0xC4
- .text:0000190C var_C0 = -0xC0
- .text:0000190C var_BC = -0xBC
- .text:0000190C var_B8 = -0xB8
- .text:0000190C var_B4 = -0xB4
- .text:0000190C var_B0 = -0xB0
- .text:0000190C var_AC = -0xAC
- .text:0000190C var_A4 = -0xA4
- .text:0000190C var_4C = -0x4C
- .text:0000190C dest = -0x44
- .text:0000190C var_24 = -0x24
- .text:0000190C
- .text:0000190C PUSH {R4-R7,LR}
- .text:0000190E MOV R7, R9
- .text:00001910 MOV R6, R8
- .text:00001912 PUSH {R6,R7}
- .text:00001914 LDR R3, =(__stack_chk_guard_ptr - 0x191E)
- .text:00001916 SUB SP, SP, #0xC4
- .text:00001918 ADD R5, SP, #0x94
- .text:0000191A ADD R3, PC ; __stack_chk_guard_ptr
- .text:0000191C LDR R6, [R3] ; __stack_chk_guard
- .text:0000191E MOVS R2, #0x4A
- .text:00001920 MOV R9, R0
- .text:00001922 LDR R3, [R6]
- .text:00001924 MOV R8, R1
- .text:00001926 ADD R0, SP, #0x9C ; dest
- .text:00001928 STR R3, [SP,#0xE0+var_24]
- .text:0000192A STRB R2, [R5]
- .text:0000192C MOVS R2, #0x37 ; MD5求值前缀J7k$x*U5
- .text:0000192E STRB R2, [R5,#1] ; R5指向最终MD5计算的字符串缓冲区
- .text:00001930 MOVS R2, #0x6B
- .text:00001932 STRB R2, [R5,#2]
- .text:00001934 MOVS R2, #0x24
- .text:00001936 STRB R2, [R5,#3]
- .text:00001938 MOVS R2, #0x78
- .text:0000193A STRB R2, [R5,#4]
- .text:0000193C MOVS R2, #0x2A
- .text:0000193E STRB R2, [R5,#5]
- .text:00001940 MOVS R2, #0x55
- .text:00001942 STRB R2, [R5,#6]
- .text:00001944 MOVS R2, #0x35
- .text:00001946 MOVS R3, #0
- .text:00001948 STRB R2, [R5,#7]
- .text:0000194A MOV R1, R9 ; src
- .text:0000194C MOVS R2, #0x10 ; n
- .text:0000194E STR R3, [SP,#0xE0+dest] ; 初始化缓冲区
- .text:00001950 STR R3, [SP,#0xE0+dest.field_0+4]
- .text:00001952 STR R3, [SP,#0xE0+dest.field_0+8]
- .text:00001954 STR R3, [SP,#0xE0+dest.field_0+0xC]
- .text:00001956 STR R3, [SP,#0xE0+dest.field_0+0x10]
- .text:00001958 STR R3, [SP,#0xE0+dest.field_0+0x14]
- .text:0000195A STR R3, [SP,#0xE0+dest.field_0+0x18]
- .text:0000195C STR R3, [SP,#0xE0+dest.field_0+0x1C]
- .text:0000195E BLX strncat ; 把输入字符串拼接到R0
- .text:0000195E ; R5=SP+#0x94 初始串“J7k$x*U5”8字节
- .text:0000195E ; R0=SP+#0x9C
- .text:0000195E ; 实际相当于 “J7k$x*U5”+输入字符串
- .text:00001962 LDR R0, =(aRandom_seedS - 0x196C)
- .text:00001964 MOVS R1, R5
- .text:00001966 ADD R7, SP, #0xE0+var_A4
- .text:00001968 ADD R0, PC ; "random_seed= -%s-\n"
- .text:0000196A BLX printf
- .text:0000196E MOVS R0, R7 ; R7=MD5的ctx结构
- .text:00001970 BL MD5Init
- .text:00001974 MOVS R0, R5 ; s
- .text:00001976 BLX strlen
- .text:0000197A ADD R4, SP, #0xE0+dest.field_0+0x10
- .text:0000197C MOVS R2, R0
- .text:0000197E MOVS R1, R5 ; R5 = 刚才拼接的字符串
- .text:00001980 MOVS R0, R7
- .text:00001982 BL MD5Update
- .text:00001986 MOVS R0, R7
- .text:00001988 MOVS R1, R4
- .text:0000198A BL MD5Final
- .text:0000198E LDRB R0, [R4,#2]
- .text:00001990 LDRB R3, [R4,#1]
- .text:00001992 LDRB R2, [R4]
- .text:00001994 STR R0, [SP,#0xE0+var_E0]
- .text:00001996 LDRB R0, [R4,#3]
- .text:00001998 LDR R1, =(a02x02x02x02x02 - 0x19A2)
- .text:0000199A STR R0, [SP,#0xE0+var_DC]
- .text:0000199C LDRB R0, [R4,#4]
- .text:0000199E ADD R1, PC ; "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02"...
- .text:000019A0 STR R0, [SP,#0xE0+var_D8]
- .text:000019A2 LDRB R0, [R4,#5]
- .text:000019A4 STR R0, [SP,#0xE0+var_D4]
- .text:000019A6 LDRB R0, [R4,#6]
- .text:000019A8 STR R0, [SP,#0xE0+var_D0]
- .text:000019AA LDRB R0, [R4,#7]
- .text:000019AC STR R0, [SP,#0xE0+var_CC]
- .text:000019AE LDRB R0, [R4,#8]
- .text:000019B0 STR R0, [SP,#0xE0+var_C8]
- .text:000019B2 LDRB R0, [R4,#9]
- .text:000019B4 STR R0, [SP,#0xE0+var_C4]
- .text:000019B6 LDRB R0, [R4,#0xA]
- .text:000019B8 STR R0, [SP,#0xE0+var_C0]
- .text:000019BA LDRB R0, [R4,#0xB]
- .text:000019BC STR R0, [SP,#0xE0+var_BC]
- .text:000019BE LDRB R0, [R4,#0xC]
- .text:000019C0 STR R0, [SP,#0xE0+var_B8]
- .text:000019C2 LDRB R0, [R4,#0xD]
- .text:000019C4 STR R0, [SP,#0xE0+var_B4]
- .text:000019C6 LDRB R0, [R4,#0xE]
- .text:000019C8 STR R0, [SP,#0xE0+var_B0]
- .text:000019CA LDRB R0, [R4,#0xF]
- .text:000019CC STR R0, [SP,#0xE0+var_AC]
- .text:000019CE MOV R0, R8 ; s
- .text:000019D0 BLX sprintf ; 把md5格式化成文本流
- .text:000019D4 MOV R0, R8
- .text:000019D6 MOVS R2, #6 ; n
- .text:000019D8 MOV R1, R9 ; src
- .text:000019DA ADDS R0, #0x20 ; dest // R0=R8+32就是跳过32个字符,在MD5文本结果的后面追加6个字符。
- .text:000019DA ; 源字符串指针R9,也就是第一个参数。
- .text:000019DC BLX strncat
- .text:000019E0 LDR R0, =(aFlash_encrypt_ - 0x19E8)
- .text:000019E2 MOV R1, R8
- .text:000019E4 ADD R0, PC ; "flash_encrypt_k= -%s-\n"
- .text:000019E6 BLX printf
- .text:000019EA LDR R2, [SP,#0xE0+var_24]
- .text:000019EC LDR R3, [R6]
- .text:000019EE MOVS R0, #0
- .text:000019F0 CMP R2, R3
- .text:000019F2 BNE loc_19FE
- .text:000019F4 ADD SP, SP, #0xC4
- .text:000019F6 POP {R2,R3}
- .text:000019F8 MOV R8, R2
- .text:000019FA MOV R9, R3
- .text:000019FC POP {R4-R7,PC}
复制代码
因此,这个算法的过程很简单: 取MD5("J7k$x*U5"+输入文本)+取文本左边(输入文本,6)
经验总结:
我一开始是不知道"J7k$x*U5"+输入文本是如何拼接的,因为IDA把一个缓冲区给分成了多个变量标号,所以看着很晕,动态调试的时候反应过来了。
R5=SP+#0x94 初始串“J7k$x*U5”8字节
R0=SP+#0x9C R0是参数拼接的位置
差值正好是8所以R5最后指向的就是“J7k$x*U5”+参数
其他关于音悦台的文章:
视频教程,音悦台算法分析
http://bbs.125.la/thread-13887196-1-1.html
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|