最近一直在弄“电子数据取证百科”的一个 WIKI 内容,精力有限已经一个多月没更新文章了。以后还是坚持每月至少更新几篇技术文章。
这一次来做一个网站参数加解密分析,以注册接口为例:
抓包结果:
POST /TQL?Entry=PUL.register_account&RI= HTTP/1.1
Host: pul.tdx.com.cn
Connection: keep-alive
Content-Length: 148
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
sec-ch-ua-platform: "Windows"
Content-Type: text/plain
Accept: */*
Origin: https://pul.tdx.com.cn
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://pul.tdx.com.cn/site/app/pul/register.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: LST=10; ASPSessionID=12689960779600960594
[{"ATYPE":340,"AID":"@ec0:PvhToe2pW6FL0v1tQru05UKrc5uZJHC5","YZM":"1234","AUTOLOGIN":2,"APASD":"[email protected]","encFlg":"1"}]
返回如下:
HTTP/1.1 200 OK
Set-Cookie: ASPSessionID=12689961020119129188;path=/;HttpOnly
Set-Cookie: LST=10;path=/
Content-Length: 77
Content-Type: application/octet-stream
Pragma: no-cache
Cache-Control: no-cache
Connection: close
[[-12543,"éªè¯ç 䏿£ç¡®<==(S7102)PUL.register_account",0,"",0],[],[],[]]
请求参数中,AID与APASD均被加密,AID是填写的手机号,YZM是手机验证码,APASD是注册密码。
观察特点,[email protected]:,密码加密有前缀,MD5@,可以得到的结论是,至少有两种加密,密码大概率可能是md5的签名算法。
YhtRegister: function(o, i, s, r, a) {
var c = this
, u = this;
return new Promise(function(e, t) {
var n = {
ATYPE: 340,
AID: Object(m.b)(o),
YZM: i,
AUTOLOGIN: s || 1
};
r && (n.APASD = r),
a && (n.LABLESIGN = a),
n.encFlg = "1",
c.tdxYhtRegister([n], function(t) {
0 == t.ErrorCode && ("tdxios" != u.platform() && "tdxandroid" != u.platform() || 0 < Object.keys(t.tables[0][0]).length && t.tables[0][0].TDXID && (u.isNewRegister = !0),
e(t.Cookies))
})
}
)
},
在这里,参数o是手机号明文,r是密码加密后的值。先看手机号加密逻辑,在控制台输出加密函数Object(m.b),回车,定位到加密代码:
var o = function(t) {
return window.tdxEncrypt(t)
}
在控制台输出加密函数window.tdxEncrypt,回车,定位到window.tdxEncrypt函数代码:
window.tdxEncrypt = function(r) {
if (0 === r.indexOf("@ec0"))
return r;
r = e.encrypt(r += "@_bfstr");
return "@ec0:" + e.base64Encode(r)
}
再继续定位到e.encrypt函数代码:
encrypt: function(r, t) {
if ("ecb" === this.mode)
return this.encryptECB(r);
if ("cbc" === this.mode)
return this.encryptCBC(r, t);
throw new Error("Неизвестный режим шифрования.")
},
通过打断点进去,可以看见,this.mode=ecb,因此手机号是encryptECB加密函数:
encryptECB: function(r) {
r = this.utf8Decode(r);
for (var t = Math.ceil(r.length / 8), o = "", e = 0; e < t; e++) {
var n = r.substr(8 * e, 8);
if (n.length < 8)
for (var i = 8 - n.length; 0 < i--; )
n += "\0";
var h = this.split64by32(n)
, s = h[0]
, u = h[1];
s = (h = this.encipherEX(s, u))[0],
u = h[1],
o += this.num2block32(u) + this.num2block32(s)
}
return o
},
再把涉及到的一些调用函数抠出来:
split64by32: function(r) {
var t = r.substring(0, 4)
, r = r.substring(4, 8);
return [this.block32toNum(t), this.block32toNum(r)]
},
utf8Decode: function(r) {
for (var t = "", o = 0; o < r.length; o++) {
var e = r.charCodeAt(o);
e < 128 ? t += String.fromCharCode(e) : (127 < e && e < 2048 ? t += String.fromCharCode(e >> 6 | 192) : (t += String.fromCharCode(e >> 12 | 224),
t += String.fromCharCode(e >> 6 & 63 | 128)),
t += String.fromCharCode(63 & e | 128))
}
return t
},
encipherEX: function(r, t) {
var o = this
, t = t
, r = o.xor(r = r, o.pArray[0])
, t = o.round(t, r, 1);
return r = o.round(r, t, 2),
t = o.round(t, r, 3),
r = o.round(r, t, 4),
t = o.round(t, r, 5),
r = o.round(r, t, 6),
t = o.round(t, r, 7),
r = o.round(r, t, 8),
t = o.round(t, r, 9),
r = o.round(r, t, 10),
t = o.round(t, r, 11),
r = o.round(r, t, 12),
t = o.round(t, r, 13),
r = o.round(r, t, 14),
t = o.round(t, r, 15),
[r = o.round(r, t, 16), t = o.xor(t, o.pArray[17])]
},
xor: function(r, t) {
return this.fixNegative(r ^ t)
},
trimZeros: function(r) {
return r.replace(/\0+$/g, "")
},
wordbyte0: function(r) {
return Math.floor(Math.floor(Math.floor(r / 256) / 256) / 256) % 256
},
wordbyte1: function(r) {
return Math.floor(Math.floor(r / 256) / 256) % 256
},
wordbyte2: function(r) {
return Math.floor(r / 256) % 256
},
wordbyte3: function(r) {
return r % 256
},
round: function(r, t, o) {
var e = this;
return e.xor(r, e.xor(e.xor(e.sBox0[e.wordbyte0(t)] + e.sBox1[e.wordbyte1(t)], e.sBox2[e.wordbyte2(t)]) + e.sBox3[e.wordbyte3(t)], e.pArray[o]))
},
o对象的值:
{
"key": "w83c5~%%30nm5-+",
"sBox0": [
1401335157,
2656414153,
1572114622,
这里还有,省略掉,太长了,
839348932,
2117317013,
1753590868
],
"sBox1": [
2944530774,
611268753,
2480642903,
这里还有,省略掉,太长了,
1125867267
],
"sBox2": [
117595124,
794470125,
1939519107,
这里还有,省略掉,太长了,
1016397799,
2504754504
],
"sBox3": [
898774924,
这里还有,省略掉,太长了,
35895393,
1829467942
],
"pArray": [
4293367326,
2903747409,
这里还有,省略掉,太长了,
1985119492
]
}
num2block32: function(r) {
return String.fromCharCode(r << 24 >>> 24) + String.fromCharCode(r << 16 >>> 24) + String.fromCharCode(r << 8 >>> 24) + String.fromCharCode(r >>> 24)
},
block32toNum: function(r) {
return this.fixNegative(r.charCodeAt(3) << 24 | r.charCodeAt(2) << 16 | r.charCodeAt(1) << 8 | r.charCodeAt(0))
},
fixNegative: function(r) {
return r >>> 0
},
base64Encode: function(r) {
for (var t, o, e, n, i, h, s = "", u = 0; u < r.length; )
e = (h = r.charCodeAt(u++)) >> 2,
n = (3 & h) << 4 | (t = r.charCodeAt(u++)) >> 4,
i = (15 & t) << 2 | (o = r.charCodeAt(u++)) >> 6,
h = 63 & o,
isNaN(t) ? i = h = 64 : isNaN(o) && (h = 64),
s = s + this.keyStr.charAt(e) + this.keyStr.charAt(n) + this.keyStr.charAt(i) + this.keyStr.charAt(h);
return s
},
keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
把流程串起来之后得知手机号加密方式:”@ec0:” + base64(encrypt(手机号+@_bfstr))
将js流程代码组装起来后验证如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script type="text/javascript">
function num2block32(r) {
return String.fromCharCode(r << 24 >>> 24) + String.fromCharCode(r << 16 >>> 24) + String.fromCharCode(r << 8 >>> 24) + String.fromCharCode(r >>> 24)
}
var o_data ={
"key": "w83c5~%%30nm5-+",
"sBox0": [
1401335157,
2656414153,
这里还有,省略掉,太长了,
839348932,
2117317013,
1753590868
],
"sBox1": [
2944530774,
611268753,
这里还有,省略掉,太长了,
1410597869,
1965163803,
1125867267
],
"sBox2": [
117595124,
794470125,
这里还有,省略掉,太长了,
1016397799,
2504754504
],
"sBox3": [
898774924,
3133324463,
3959680370,
这里还有,省略掉,太长了,
35895393,
1829467942
],
"pArray": [
4293367326,
2903747409,
2513112869,
这里还有,省略掉,太长了,
1985119492
]
}
function xor(r, t) {
return fixNegative(r ^ t)
};
function trimZeros (r) {
return r.replace(/\0+$/g, "")
};
function wordbyte0(r) {
return Math.floor(Math.floor(Math.floor(r / 256) / 256) / 256) % 256
};
function wordbyte1(r) {
return Math.floor(Math.floor(r / 256) / 256) % 256
};
function wordbyte2(r) {
return Math.floor(r / 256) % 256
};
function wordbyte3 (r) {
return r % 256
};
function round(r, t, o) {
return xor(r, xor(xor(o_data.sBox0[wordbyte0(t)] + o_data.sBox1[wordbyte1(t)], o_data.sBox2[wordbyte2(t)]) + o_data.sBox3[wordbyte3(t)], o_data.pArray[o]))
};
function encipherEX(r, t) {
t = t
, r = xor(r = r, o_data.pArray[0])
, t = round(t, r, 1);
return r = round(r, t, 2),
t = round(t, r, 3),
r = round(r, t, 4),
t = round(t, r, 5),
r = round(r, t, 6),
t = round(t, r, 7),
r = round(r, t, 8),
t = round(t, r, 9),
r = round(r, t, 10),
t = round(t, r, 11),
r = round(r, t, 12),
t = round(t, r, 13),
r = round(r, t, 14),
t = round(t, r, 15),
[r = round(r, t, 16), t = xor(t, o_data.pArray[17])]
};
function fixNegative(r) {
return r >>> 0
};
function block32toNum(r) {
return fixNegative(r.charCodeAt(3) << 24 | r.charCodeAt(2) << 16 | r.charCodeAt(1) << 8 | r.charCodeAt(0))
};
function split64by32(r) {
var t = r.substring(0, 4)
, r = r.substring(4, 8);
return [block32toNum(t), block32toNum(r)]
};
function utf8Decode(r) {
for (var t = "", o = 0; o < r.length; o++) {
var e = r.charCodeAt(o);
e < 128 ? t += String.fromCharCode(e) : (127 < e && e < 2048 ? t += String.fromCharCode(e >> 6 | 192) : (t += String.fromCharCode(e >> 12 | 224),
t += String.fromCharCode(e >> 6 & 63 | 128)),
t += String.fromCharCode(63 & e | 128))
}
return t
};
function encryptECB(r) {
r = utf8Decode(r);
for (var t = Math.ceil(r.length / 8), o = "", e = 0; e < t; e++) {
var n = r.substr(8 * e, 8);
if (n.length < 8)
for (var i = 8 - n.length; 0 < i--; )
n += "\0";
var h = split64by32(n)
, s = h[0]
, u = h[1];
s = (h = encipherEX(s, u))[0],
u = h[1],
o += num2block32(u) + num2block32(s)
}
return o
};
var keyStr ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function base64Encode(r) {
for (var t, o, e, n, i, h, s = "", u = 0; u < r.length; )
e = (h = r.charCodeAt(u++)) >> 2,
n = (3 & h) << 4 | (t = r.charCodeAt(u++)) >> 4,
i = (15 & t) << 2 | (o = r.charCodeAt(u++)) >> 6,
h = 63 & o,
isNaN(t) ? i = h = 64 : isNaN(o) && (h = 64),
s = s + keyStr.charAt(e) + keyStr.charAt(n) + keyStr.charAt(i) + keyStr.charAt(h);
return s
};
debugger;
var aa = '15512345678'+'@_bfstr';
var bb = encryptECB(aa);
var cc = base64Encode(bb);
var dd = "@ec0:" + cc;
console.log(dd); //输出结果:@ec0:PvhToe2pW6FL0v1tQru05UKrc5uZJHC5,与抓包数据一致
</script>
</body>
</html>
密码的加密方式、以及返回值的解密方式,也能通过相同的方法获取到对应的处理方案。
学习了,可惜还是看不懂