猿人学 第一关:js 混淆 - 源码乱码

之前第一次做这道题的时候,卡了一晚上,现在重新来分析看看,首先是题目:

先打开开发者工具,看看数据来源,这里有作者设置了 setInterval 的反调试,每隔一段时间就会自动下断点,我们直接在 debugger 那一行 never pause here

然后点击 resume,以后就会自动跳过这一行了,来看一下数据接口

可以看到数据是以json的形式直接返回的,但这里有个 m参数,不确定是怎么来的

1
m: c204206e7e8953fbba7c33835c5478a9丨1654198904

长度是32,看上去好像是 md5 | 时间戳 的形式,下断点分析一波:

追栈到这里就差不多了,但源码稍微经过了混淆,不太容易看,还原出部分比较重要的源码:

1
2
3
4
5
window["url"] = "/api/" + "match" + "/1", request = function () {
var _0x2268f9 = Date["parse"](new Date()) + (16798545 + -72936737 + 156138192), _0x57feae = oo0O0(_0x2268f9["toStr" + "ing"]()) + window["f"];
const _0x5d83a3 = {};
_0x5d83a3["page"] = window["page"], _0x5d83a3["m"] = _0x57feae + "丨" + _0x2268f9 / (-1 * 3483 + -9059 + 13542);
var _0xb89747 = _0x5d83a3;

注意这一句 _0x5d83a3["m"] = _0x57feae + "丨" + _0x2268f9 / (-1 * 3483 + -9059 + 13542) ,其中:

  • (-1 * 3483 + -9059 + 13542) 的值是 1000

  • _0x2268f9 则是

    1
    Date["parse"](new Date()) + (16798545 + -72936737 + 156138192)

    其实就是当前的 毫秒时间戳 + 100000000,也就是

    1
    int(time.time()) * 1000 + 100000000

到这里就剩 _0x57feae 还没分析了,上面的源码里面有:

1
_0x57feae = oo0O0(_0x2268f9["toStr" + "ing"]()) + window["f"]

控制台打印一下,window["f"] 里面就有我们想要的值,那前面的 oo0O0 函数难道是多余的,用来掩人耳目?

在关键代码处打断点重新调试一下:

所以这里基本可以确定,执行完 oo0O0 函数后,window["f"] 的值发生了改变,进到函数内部看看:

代码被设置了防格式化,我们直接抠出来,用工具美化一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function oo0O0(mw) {
window.b = '';
for (var i = 0, len = window.a.length; i < len; i++) {
console.log(window.a[i]);
window.b += String[document.e + document.g](window.a[i][document.f + document.h]() - i - window.c)
}
var U = ['W5r5W6VdIHZcT8kU', 'WQ8CWRaxWQirAW=='];
var J = function (o, E) {
o = o - 0x0;
var N = U[o];
if (J['bSSGte'] === undefined) {
var Y = function (w) {
var m = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=',
T = String(w)['replace'](/=+$/, '');
var A = '';
for (var C = 0x0, b, W, l = 0x0; W = T['charAt'](l++); ~W && (b = C % 0x4 ? b * 0x40 + W : W, C++
% 0x4) ? A += String['fromCharCode'](0xff & b >> (-0x2 * C & 0x6)) : 0x0) {
W = m['indexOf'](W)
}
return A
};
var t = function (w, m) {
var T = [],
A = 0x0,
C, b = '',
W = '';
w = Y(w);
for (var R = 0x0, v = w['length']; R < v; R++) {
W += '%' + ('00' + w['charCodeAt'](R)['toString'](0x10))['slice'](-0x2)
}
w = decodeURIComponent(W);
var l;
for (l = 0x0; l < 0x100; l++) {
T[l] = l
}
for (l = 0x0; l < 0x100; l++) {
A = (A + T[l] + m['charCodeAt'](l % m['length'])) % 0x100, C = T[l], T[l] = T[A], T[A] = C
}
l = 0x0, A = 0x0;
for (var L = 0x0; L < w['length']; L++) {
l = (l + 0x1) % 0x100, A = (A + T[l]) % 0x100, C = T[l], T[l] = T[A], T[A] = C, b += String[
'fromCharCode'](w['charCodeAt'](L) ^ T[(T[l] + T[A]) % 0x100])
}
return b
};
J['luAabU'] = t, J['qlVPZg'] = {}, J['bSSGte'] = !![]
}
var H = J['qlVPZg'][o];
return H === undefined ? (J['TUDBIJ'] === undefined && (J['TUDBIJ'] = !![]), N = J['luAabU'](N, E), J[
'qlVPZg'][o] = N) : N = H, N
};
eval(atob(window['b'])[J('0x0', ']dQW')](J('0x1', 'GTu!'), '\x27' + mw + '\x27'));
return ''
}

把上面那段代码拿到 snippet 调试一下

注意需要和网站源码保持在同一页,不然有些参数是 undefined,比如 window.a 等等

调试到最后一行,这个地方有点可疑:

atob(window['b'])还原出来居然是一段代码,简直 amazing!原理的话其实就是把经过 base64编码的一整段代码放在 window['b'] 里面,再来看其他几个参数的还原:

这里是做一个字符串的替换,其实就等同于:

1
.replace('mwqqppz', "'" + mw + "'")

这里 mw的值是 1654200848000,也就是把 window.f = hex_md5(mwqqppz) 替换成了 window.f = hex_md5('1654200848000')

进入到 window[‘b’] 内部看一下,找到最后一行代码:

已经可以确定 window.f 的生成点了,来验证一下 hex_md5的真实性

对比发现,用 python标准库计算的md5值和 hex_md5的还不太一样,所以这里的md5应该是经过了作者的魔改

那没办法用python还原了,只能把 hex_md5 整个抠出来,用 node 子进程来执行,得到结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
var hexcase = 0;
var b64pad = "";
var chrsz = 16;

function hex_md5(a) {
return binl2hex(core_md5(str2binl(a), a.length * chrsz))
}

function b64_md5(a) {
return binl2b64(core_md5(str2binl(a), a.length * chrsz))
}

function str_md5(a) {
return binl2str(core_md5(str2binl(a), a.length * chrsz))
}

function hex_hmac_md5(a, b) {
return binl2hex(core_hmac_md5(a, b))
}

function b64_hmac_md5(a, b) {
return binl2b64(core_hmac_md5(a, b))
}

function str_hmac_md5(a, b) {
return binl2str(core_hmac_md5(a, b))
}

function md5_vm_test() {
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"
}

function core_md5(p, k) {
p[k >> 5] |= 128 << ((k) % 32);
p[(((k + 64) >>> 9) << 4) + 14] = k;
var o = 1732584193;
var n = -271733879;
var m = -1732584194;
var l = 271733878;
for (var g = 0; g < p.length; g += 16) {
var j = o;
var h = n;
var f = m;
var e = l;
o = md5_ff(o, n, m, l, p[g + 0], 7, -680976936);
l = md5_ff(l, o, n, m, p[g + 1], 12, -389564586);
m = md5_ff(m, l, o, n, p[g + 2], 17, 606105819);
n = md5_ff(n, m, l, o, p[g + 3], 22, -1044525330);
o = md5_ff(o, n, m, l, p[g + 4], 7, -176418897);
l = md5_ff(l, o, n, m, p[g + 5], 12, 1200080426);
m = md5_ff(m, l, o, n, p[g + 6], 17, -1473231341);
n = md5_ff(n, m, l, o, p[g + 7], 22, -45705983);
o = md5_ff(o, n, m, l, p[g + 8], 7, 1770035416);
l = md5_ff(l, o, n, m, p[g + 9], 12, -1958414417);
m = md5_ff(m, l, o, n, p[g + 10], 17, -42063);
n = md5_ff(n, m, l, o, p[g + 11], 22, -1990404162);
o = md5_ff(o, n, m, l, p[g + 12], 7, 1804660682);
l = md5_ff(l, o, n, m, p[g + 13], 12, -40341101);
m = md5_ff(m, l, o, n, p[g + 14], 17, -1502002290);
n = md5_ff(n, m, l, o, p[g + 15], 22, 1236535329);
o = md5_gg(o, n, m, l, p[g + 1], 5, -165796510);
l = md5_gg(l, o, n, m, p[g + 6], 9, -1069501632);
m = md5_gg(m, l, o, n, p[g + 11], 14, 643717713);
n = md5_gg(n, m, l, o, p[g + 0], 20, -373897302);
o = md5_gg(o, n, m, l, p[g + 5], 5, -701558691);
l = md5_gg(l, o, n, m, p[g + 10], 9, 38016083);
m = md5_gg(m, l, o, n, p[g + 15], 14, -660478335);
n = md5_gg(n, m, l, o, p[g + 4], 20, -405537848);
o = md5_gg(o, n, m, l, p[g + 9], 5, 568446438);
l = md5_gg(l, o, n, m, p[g + 14], 9, -1019803690);
m = md5_gg(m, l, o, n, p[g + 3], 14, -187363961);
n = md5_gg(n, m, l, o, p[g + 8], 20, 1163531501);
o = md5_gg(o, n, m, l, p[g + 13], 5, -1444681467);
l = md5_gg(l, o, n, m, p[g + 2], 9, -51403784);
m = md5_gg(m, l, o, n, p[g + 7], 14, 1735328473);
n = md5_gg(n, m, l, o, p[g + 12], 20, -1921207734);
o = md5_hh(o, n, m, l, p[g + 5], 4, -378558);
l = md5_hh(l, o, n, m, p[g + 8], 11, -2022574463);
m = md5_hh(m, l, o, n, p[g + 11], 16, 1839030562);
n = md5_hh(n, m, l, o, p[g + 14], 23, -35309556);
o = md5_hh(o, n, m, l, p[g + 1], 4, -1530992060);
l = md5_hh(l, o, n, m, p[g + 4], 11, 1272893353);
m = md5_hh(m, l, o, n, p[g + 7], 16, -155497632);
n = md5_hh(n, m, l, o, p[g + 10], 23, -1094730640);
o = md5_hh(o, n, m, l, p[g + 13], 4, 681279174);
l = md5_hh(l, o, n, m, p[g + 0], 11, -358537222);
m = md5_hh(m, l, o, n, p[g + 3], 16, -722881979);
n = md5_hh(n, m, l, o, p[g + 6], 23, 76029189);
o = md5_hh(o, n, m, l, p[g + 9], 4, -640364487);
l = md5_hh(l, o, n, m, p[g + 12], 11, -421815835);
m = md5_hh(m, l, o, n, p[g + 15], 16, 530742520);
n = md5_hh(n, m, l, o, p[g + 2], 23, -995338651);
o = md5_ii(o, n, m, l, p[g + 0], 6, -198630844);
l = md5_ii(l, o, n, m, p[g + 7], 10, 11261161415);
m = md5_ii(m, l, o, n, p[g + 14], 15, -1416354905);
n = md5_ii(n, m, l, o, p[g + 5], 21, -57434055);
o = md5_ii(o, n, m, l, p[g + 12], 6, 1700485571);
l = md5_ii(l, o, n, m, p[g + 3], 10, -1894446606);
m = md5_ii(m, l, o, n, p[g + 10], 15, -1051523);
n = md5_ii(n, m, l, o, p[g + 1], 21, -2054922799);
o = md5_ii(o, n, m, l, p[g + 8], 6, 1873313359);
l = md5_ii(l, o, n, m, p[g + 15], 10, -30611744);
m = md5_ii(m, l, o, n, p[g + 6], 15, -1560198380);
n = md5_ii(n, m, l, o, p[g + 13], 21, 1309151649);
o = md5_ii(o, n, m, l, p[g + 4], 6, -145523070);
l = md5_ii(l, o, n, m, p[g + 11], 10, -1120210379);
m = md5_ii(m, l, o, n, p[g + 2], 15, 718787259);
n = md5_ii(n, m, l, o, p[g + 9], 21, -343485551);
o = safe_add(o, j);
n = safe_add(n, h);
m = safe_add(m, f);
l = safe_add(l, e)
}
return Array(o, n, m, l)
}

function md5_cmn(h, e, d, c, g, f) {
return safe_add(bit_rol(safe_add(safe_add(e, h), safe_add(c, f)), g), d)
}

function md5_ff(g, f, k, j, e, i, h) {
return md5_cmn((f & k) | ((~f) & j), g, f, e, i, h)
}

function md5_gg(g, f, k, j, e, i, h) {
return md5_cmn((f & j) | (k & (~j)), g, f, e, i, h)
}

function md5_hh(g, f, k, j, e, i, h) {
return md5_cmn(f ^ k ^ j, g, f, e, i, h)
}

function md5_ii(g, f, k, j, e, i, h) {
return md5_cmn(k ^ (f | (~j)), g, f, e, i, h)
}

function core_hmac_md5(c, f) {
var e = str2binl(c);
if (e.length > 16) {
e = core_md5(e, c.length * chrsz)
}
var a = Array(16),
d = Array(16);
for (var b = 0; b < 16; b++) {
a[b] = e[b] ^ 909522486;
d[b] = e[b] ^ 1549556828
}
var g = core_md5(a.concat(str2binl(f)), 512 + f.length * chrsz);
return core_md5(d.concat(g), 512 + 128)
}

function safe_add(a, d) {
var c = (a & 65535) + (d & 65535);
var b = (a >> 16) + (d >> 16) + (c >> 16);
return (b << 16) | (c & 65535)
}

function bit_rol(a, b) {
return (a << b) | (a >>> (32 - b))
}

function str2binl(d) {
var c = Array();
var a = (1 << chrsz) - 1;
for (var b = 0; b < d.length * chrsz; b += chrsz) {
c[b >> 5] |= (d.charCodeAt(b / chrsz) & a) << (b % 32)
}
return c
}

function binl2str(c) {
var d = "";
var a = (1 << chrsz) - 1;
for (var b = 0; b < c.length * 32; b += chrsz) {
d += String.fromCharCode((c[b >> 5] >>> (b % 32)) & a)
}
return d
}

function binl2hex(c) {
var b = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var d = "";
for (var a = 0; a < c.length * 4; a++) {
d += b.charAt((c[a >> 2] >> ((a % 4) * 8 + 4)) & 15) + b.charAt((c[a >> 2] >> ((a % 4) * 8)) & 15)
}
return d
}

function binl2b64(d) {
var c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var f = "";
for (var b = 0; b < d.length * 4; b += 3) {
var e = (((d[b >> 2] >> 8 * (b % 4)) & 255) << 16) | (((d[b + 1 >> 2] >> 8 * ((b + 1) % 4)) & 255) << 8) | ((d[
b + 2 >> 2] >> 8 * ((b + 2) % 4)) & 255);
for (var a = 0; a < 4; a++) {
if (b * 8 + a * 6 > d.length * 32) {
f += b64pad
} else {
f += c.charAt((e >> 6 * (3 - a)) & 63)
}
}
}
return f
};

对比一下本地和网页的运行结果,结果一致,说明抠出来的代码是正确的

代码整理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import time
import subprocess


yuanrenxue_headers = {
"User-Agent": "yuanrenxue.project",
"sessionid": "ihybaybtfp3htl48jf5mk4kx5bj7xbk3",
}


def get_m():
ts_13 = int(time.time()) * 1000 + 100000000
process = subprocess.run(f"node 第一关.js {str(ts_13)}", stdout=subprocess.PIPE)
_result = process.stdout # 获得标准输出
m = _result.decode().strip()
return m + "丨" + str(int(ts_13 / 1000))


def main():
for page in range(1, 6):
params = {"page": page, "m": get_m()}
price_api = "http://match.yuanrenxue.com/api/match/1"
resp = requests.get(price_api, params=params, headers=yuanrenxue_headers)
# print(resp.request.url)
print(resp.json()['data'])


if __name__ == "__main__":
main()

  • 注意是 “丨” ,不是 “|” (被这玩意卡了一下午 😇)

  • 第一关.js 主要是在文件结尾补充了

    1
    2
    3
    4
    // 接受时间戳参数
    var arguments = process.argv.splice(2);
    var ts_13 = arguments[0];
    console.log(hex_md5(ts_13))

运行结果