为啥新注册的号还不能在技术版块发文章?
好吧,只好先在茶余饭后版块做吃瓜群众了
站长呢,快出来给我个权限啊啊啊啊啊啊啊!
PS:最近在研究Android内核的水滴漏洞,有点搞不定,有兴趣的可以一起交流一下。
自从做了Android,我开始觉得自己就是一个苦逼的程序猿...
没错,之前我一直认为自己是一个虽然菜鸡但是快乐的黑客的
觉得自己一天天的不快乐,所以只好写点文章来找找存在感,希望能够回到最初的状态。
某陌,先给你道个歉,对不起了...
任意抓取两个HTTPS数据包
第一个数据包
Post请求部分:
header中的未知字段:
X-SIGN: ABkKoAlXiGOixtT1tVgWMeKU27c=
X-LV: 1
X-KV: 31e0d37e
携带session值:cookie: SESSIONID=47E50390-FD1A-1FCB-78C1-D1406A9FCAEC
X-SIGN: ABkKoAlXiGOixtT1tVgWMeKU27c=
X-LV: 1
X-KV: 31e0d37e
携带session值:cookie: SESSIONID=47E50390-FD1A-1FCB-78C1-D1406A9FCAEC
post参数为:
mzip=AgOEsqhrADZqZE%2B%2BQEM1Fq5RSTda0PrL9sPUxKU7RPmQwkPe3%2BDRzuWDj%2FpsculdJUzWZni9L1ClNvptmUS65a......
mzip=AgOEsqhrADZqZE%2B%2BQEM1Fq5RSTda0PrL9sPUxKU7RPmQwkPe3%2BDRzuWDj%2FpsculdJUzWZni9L1ClNvptmUS65a......
Post应答部分内容为:
jP K A
b6 rt+B , 0 d U) y b7w ' I ? &AI.2 z ٹ T ......
jP K A
b6 rt+B , 0 d U) y b7w ' I ? &AI.2 z ٹ T ......
查看十六进制
02 03 20 6A 50 20 20 20 20 4B 20 20 20 08 41 0D
0A 62 36 20 72 74 2B 17 42 20 20 20 20 2C 20 30
20 64 20 7F 13 55 29 20 20 79 20 62 37 77 20 27
20 49 1F 20 ......
02 03 20 6A 50 20 20 20 20 4B 20 20 20 08 41 0D
0A 62 36 20 72 74 2B 17 42 20 20 20 20 2C 20 30
20 64 20 7F 13 55 29 20 20 79 20 62 37 77 20 27
20 49 1F 20 ......
第二个数据包:
Post请求部分:
header中的未知字段:
X-SIGN: NJX937HCIey888zMUntRbxCfgLo=
X-LV: 1
X-KV: 31e0d37e
X-SIGN: NJX937HCIey888zMUntRbxCfgLo=
X-LV: 1
X-KV: 31e0d37e
携带session值:
cookie: SESSIONID=47E50390-FD1A-1FCB-78C1-D1406A9FCAEC
cookie: SESSIONID=47E50390-FD1A-1FCB-78C1-D1406A9FCAEC
post参数为:
mzip=AgNiOLZhALY595shFYVA14Xykx%2B8NpJGPbfB3g%2FQ9fG799C6RDvpwJCgTvW1%2FnNe6%2BBWrKRUhSAQ1lR......
mzip=AgNiOLZhALY595shFYVA14Xykx%2B8NpJGPbfB3g%2FQ9fG799C6RDvpwJCgTvW1%2FnNe6%2BBWrKRUhSAQ1lR......
Post应答部分内容为:
n U Q = t 呵 X %- "i !|J% r# , B & yg +& i V
\j Y K 4 o U ( w ;oQ ഀ / ƾ $ [ { J~ >G O , p 5 y G SLViӧ.....
n U Q = t 呵 X %- "i !|J% r# , B & yg +& i V
\j Y K 4 o U ( w ;oQ ഀ / ƾ $ [ { J~ >G O , p 5 y G SLViӧ.....
查看十六进制:
02 03 20 6E 20 55 20 51 20 12 20 20 3D 20 20 74
20 BA C7 20 13 58 20 20 20 20 20 25 2D 20 20 22
69 20 20 20 20 20 20 20 21 17 7C 4A 0E 25 20 72
23 7F 20 2C 20 14 ......
02 03 20 6E 20 55 20 51 20 12 20 20 3D 20 20 74
20 BA C7 20 13 58 20 20 20 20 20 25 2D 20 20 22
69 20 20 20 20 20 20 20 21 17 7C 4A 0E 25 20 72
23 7F 20 2C 20 14 ......
基于以上两个https包猜测:
1.header中的X-SIGN是整个包的校验值
2.session为登录时服务器分配的一个id
3.重新登录后发现X-KV的值发生变化,猜测是在登录时生成的一个标识
4.post参数采用为某种加密后,Base64再URLEncode得到
5.仔细观察两个应答包的十六进制数据后发现,数据的开头格式均为0x02,0x03,0x20
接下来开始验证这些猜想
第一步:构造mzip包
对程序进行分析后,在com.immomo.momoenc.e.d()函数中,发现有代码段 const-string/jumbo v3, "mzip"
在此下断进行动态调试后发现在https包中的参数,与在此处put到Map中的mzip一致,动态调试public void d();
//在次只关注mzip,以下片段已剔除函数中与mzip计算无关的代码
public void d() throws Exception {
String v0_2;
CharSequence v1 = null;
if(f.b()) {
this.h();
if(this.g) {
if(this.i != null && !this.i.isEmpty()) { //this.i为post参数的Map对象
JSONObject v1_1 = new JSONObject();
Iterator v2 = this.i.keySet().iterator();
while(v2.hasNext()) {
Object v0 = v2.next();
try {
v1_1.put(((String)v0), this.i.get(v0));
}
catch(Exception v0_1) {
com.immomo.mmutil.b.a.a().a(((Throwable)v0_1));
}
}
//将Map参数转为Json字符串v0_2
v0_2 = v1_1.toString();
String v2_1 = this.c; //this.c取出一个长度为48的字符串:GallR7c5B85g5Cy4ew+Cf9xboJnBystfGallR7c5B85g5Cy4
try {
byte[] v1_2 = v0_2.getBytes();
byte[] v3 = v2_1.getBytes();
byte[] v5 = new byte[Coded.getInstance().computeOutputLength(v1_2.length, 1)]; //computeOutputLength函数计算得到加密结果长度
int v2_2 = Coded.getInstance().aesEncode(v1_2, v1_2.length, v3, v3.length, v5); //采用aes加密算法,其中v1_2为内容,v3为密钥
v1_2 = new byte[v2_2];
int v0_3;
for(v0_3 = 0; v0_3 < v2_2; ++v0_3) { //将加密结果循环赋值给v1_2
v1_2[v0_3] = v5[v0_3]; //不同的明文,v1_2的开头结构为均为0203
}
v0_2 = com.immomo.mmutil.a.a(v1_2); //进入a函数观察得,a为Base64的encode函数
goto label_50;
}
}
label_50:
this.i.clear();
if(!TextUtils.isEmpty(((CharSequence)v0_2))) {
this.i.put("mzip", v0_2); //将v0_2关联mzip
}
}
}
}
public void d() throws Exception {
String v0_2;
CharSequence v1 = null;
if(f.b()) {
this.h();
if(this.g) {
if(this.i != null && !this.i.isEmpty()) { //this.i为post参数的Map对象
JSONObject v1_1 = new JSONObject();
Iterator v2 = this.i.keySet().iterator();
while(v2.hasNext()) {
Object v0 = v2.next();
try {
v1_1.put(((String)v0), this.i.get(v0));
}
catch(Exception v0_1) {
com.immomo.mmutil.b.a.a().a(((Throwable)v0_1));
}
}
//将Map参数转为Json字符串v0_2
v0_2 = v1_1.toString();
String v2_1 = this.c; //this.c取出一个长度为48的字符串:GallR7c5B85g5Cy4ew+Cf9xboJnBystfGallR7c5B85g5Cy4
try {
byte[] v1_2 = v0_2.getBytes();
byte[] v3 = v2_1.getBytes();
byte[] v5 = new byte[Coded.getInstance().computeOutputLength(v1_2.length, 1)]; //computeOutputLength函数计算得到加密结果长度
int v2_2 = Coded.getInstance().aesEncode(v1_2, v1_2.length, v3, v3.length, v5); //采用aes加密算法,其中v1_2为内容,v3为密钥
v1_2 = new byte[v2_2];
int v0_3;
for(v0_3 = 0; v0_3 < v2_2; ++v0_3) { //将加密结果循环赋值给v1_2
v1_2[v0_3] = v5[v0_3]; //不同的明文,v1_2的开头结构为均为0203
}
v0_2 = com.immomo.mmutil.a.a(v1_2); //进入a函数观察得,a为Base64的encode函数
goto label_50;
}
}
label_50:
this.i.clear();
if(!TextUtils.isEmpty(((CharSequence)v0_2))) {
this.i.put("mzip", v0_2); //将v0_2关联mzip
}
}
}
}
经过以上分析可得,mzip的构造依赖与aesEncode函数
参数依次为:明文,明文长度,密钥,密钥长度,输出长度
public int aesEncode(byte[] arg2, int arg3, byte[] arg4, int arg5, byte[] arg6) {
return this.a49kdEba83h(arg2, arg3, arg4, arg5, arg6);
}
public int aesEncode(byte[] arg2, int arg3, byte[] arg4, int arg5, byte[] arg6) {
return this.a49kdEba83h(arg2, arg3, arg4, arg5, arg6);
}
经过几次调试后发现,传入aesEncode相同的明文和密钥,得到的加密结果不同,猜测该aes为带IV值的加密,或者在aes的基础上略有改动
进入aesEncode函数发现native函数a49kdEba83h调用
public native int a49kdEba83h(byte[] arg1, int arg2, byte[] arg3, int arg4, byte[] arg5) {
}
public native int a49kdEba83h(byte[] arg1, int arg2, byte[] arg3, int arg4, byte[] arg5) {
}
接下来开始对native函数a49kdEba83h进行分析
起初猜测该函数在libcoded.so中,反编译该so文件后并未找到
再反编译libcoded_jni.so,找到函数:native_a49kdEba83h 000018E0
在函数开始处,通过检测/proc/PID/status中TracerPid的状态来做反调试
向下分析发现,函数调用了外部函数aesEncode,而这个函数刚才在分析libcoded.so恰好见到过
.............
BLX getpid
LDR R1, =(aProcDStatus - 0x190E)
ADD.W R4, SP, #0x518+s
MOV R2, R0
ADD R1, PC ; "/proc/%d/status" //轻量反调试
MOV R0, R4 ; s
BLX sprintf
LDR R1, =(aR - 0x191A)
MOV R0, R4 ; filename
ADD R1, PC ; "r"
BLX fopen
LDR.W R9, [R7,#arg_0]
MOV R6, R0
.............
LDR R4, =(aTracerpid - 0x192E)
MOV R10, R5
ADD R5, SP, #0x518+var_510
ADD R4, PC ; "TracerPid"
............
.............
BLX getpid
LDR R1, =(aProcDStatus - 0x190E)
ADD.W R4, SP, #0x518+s
MOV R2, R0
ADD R1, PC ; "/proc/%d/status" //轻量反调试
MOV R0, R4 ; s
BLX sprintf
LDR R1, =(aR - 0x191A)
MOV R0, R4 ; filename
ADD R1, PC ; "r"
BLX fopen
LDR.W R9, [R7,#arg_0]
MOV R6, R0
.............
LDR R4, =(aTracerpid - 0x192E)
MOV R10, R5
ADD R5, SP, #0x518+var_510
ADD R4, PC ; "TracerPid"
............
对aesEncode代码进行分析发现,数据进行加密运算前,对一个指针参数进行了操作(猜测该指针为数据的输出起点),
首先在指针前两个字节进行赋值0203,再对第七个字节赋值00,将该指针加2作为参数传入IV生成算法,并传入长度4,
随后调用标准的aes加密
........
ADD R4, SP, #0x40+var_30
MOV R1, R2
MOV R2, R3
MOV R0, R4
BLX __aeabi_memcpy
MOVW R0, #0x302 // 02 03
ADD.W R6, R10, #2
STRH.W R0, [R10] //0203作为数据的开头,即数据的byte形式为:02 03 .....
MOVS R0, #0
STRB.W R0, [R10,#6] //00作为第七个字节,数据的byte形式为:02 03 xx xx xx xx 00.....
MOV R0, R6
MOVS R1, #4
BLX j_my_RAND_pseudo_bytes
ADD.W R9, SP, #0x40+var_20
MOV R0, R6 //R6此刻指向数据byte第三个字节
MOVS R2, #4 //R2为4,猜测是一个长度值,及bytes[2-5]
MOV R1, R9
BLX j_iv_generate //IV生成算法
ADD.W R3, R10, #7 //R3指向数据的第8个字节
MOV R0, R11
MOV R1, R9
MOV R2, R4
STR.W R8, [SP,#0x40+var_40]
BLX j_aesEncrypt
ADDS R0, #7
........
........
ADD R4, SP, #0x40+var_30
MOV R1, R2
MOV R2, R3
MOV R0, R4
BLX __aeabi_memcpy
MOVW R0, #0x302 // 02 03
ADD.W R6, R10, #2
STRH.W R0, [R10] //0203作为数据的开头,即数据的byte形式为:02 03 .....
MOVS R0, #0
STRB.W R0, [R10,#6] //00作为第七个字节,数据的byte形式为:02 03 xx xx xx xx 00.....
MOV R0, R6
MOVS R1, #4
BLX j_my_RAND_pseudo_bytes
ADD.W R9, SP, #0x40+var_20
MOV R0, R6 //R6此刻指向数据byte第三个字节
MOVS R2, #4 //R2为4,猜测是一个长度值,及bytes[2-5]
MOV R1, R9
BLX j_iv_generate //IV生成算法
ADD.W R3, R10, #7 //R3指向数据的第8个字节
MOV R0, R11
MOV R1, R9
MOV R2, R4
STR.W R8, [SP,#0x40+var_40]
BLX j_aesEncrypt
ADDS R0, #7
........
于是现在的目标转换为IV的生成算法
IV的生成与两个函数有关,一个是j_my_RAND_pseudo_bytes,另一个是j_iv_generate
j_my_RAND_pseudo_bytes函数,只是得到了一个随机数
j_iv_generate函数,将随机数进行sha1运算,取加密结果的前十六个字节作为IV
...............
MOVS R0, #0x14 ; size
BLX malloc
MOV R5, R0 //开辟一块空间存放sha1的值
MOV R0, R6
MOV R1, R5
MOV R2, R4
BLX j_momoEnc_sha1
MOV R0, R8 //R8为参数传进来的指针
MOV R1, R5
MOVS R2, #0x10 //取16字节
BLX __aeabi_memcpy
MOV R0, R5 ; ptr
BLX free
...........
...............
MOVS R0, #0x14 ; size
BLX malloc
MOV R5, R0 //开辟一块空间存放sha1的值
MOV R0, R6
MOV R1, R5
MOV R2, R4
BLX j_momoEnc_sha1
MOV R0, R8 //R8为参数传进来的指针
MOV R1, R5
MOVS R2, #0x10 //取16字节
BLX __aeabi_memcpy
MOV R0, R5 ; ptr
BLX free
...........
至此可得结论,mzip的输出形式为: Base64.encode( 2字节(0203)+4字节(用于计算IV的随机值)+1字节(00)+aes的结果 )
先不考虑aes的key值
想要构造一个完整的包,还需要得到X-SIGN的值
X-SIGN在mzip构造后出现
this.j.put("X-SIGN", this.a(v2_3, this.j, this.c));
即现在的目标为this.a()函数
经调试得参数依次为:mzip的byte数组,XKV和XLV的Map对象,aes的key值
函数内部取得user Agent的值,再将其与mzip拼接,经过sign函数计算得到XSIGN的值
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-6-3 11:37
被kanxue编辑
,原因: