kasada代码分析&&插桩点&&反编译
目标值
tl请求中的headers
tl请求的payload
一共五个值需要分析
X-Kpsdk-Ct: 07iChv9SxCLnT7OemaEKTzDEKaFind2jkzzK27UsTFDAsf3txKvUwB4CSN90iFA4VGY8XBtzl5qx8wBIEHz7GfdpYCNSARh6p9OcYOFGu6RcYBHEpoCAzFda8bHUf3wcJAGIAqxkrZBTQsdrntc6GD8xRTXAytJ
X-Kpsdk-Dt: 111bfw75y34x02cnx2ez4l4x6b1x5d
X-Kpsdk-Im: CiQ1ZTA2Y2QzNy1iZGFlLTQ2NTEtYTBhMy0zNDVkZjY1YjJjNzg
X-Kpsdk-V: j-0.0.0
payload: Bxß>6C@{Â1#Õ½gè
请求流程
fp?x-kpsdk-v=j-0.0.0
method:GET
url: https://api.nike.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fp?x-kpsdk-v=j-0.0.0
response: 429
响应中返回 ips.js 链接,ips.js链接中有x-kpsdk-v和x-kpsdk-im
ips.js
method:GET
url: https://api.nike.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08Qg5binjfx6YVwZmPu8936KnheNmp9z3H42P9MGtKP3OkjFUaKy78PjU523S3FE3JKfqlBWrHhyF96TH7ghQxH4ZKvxR5OxXU5O1MZCuHmN8SoioQRepPfQvk23Y9MEN6Z4yVqNaC19hcJN2&x-kpsdk-v=j-0.0.0&x-kpsdk-im=CiQ1ZGNkZTZhZC0xMjg5LTQ3MDUtYTY2My05NGM5Yjk2NTU3NjA
response: 200
响应用于生成payload、X-Kpsdk-Ct、X-Kpsdk-Dt,是我们需要逆向的主要文件
2d206a39-8ed7-437e-a3be-862e0f06eea3/tl
"""核心请求"""
cookies = { # cookies有akamai
'_abck': 'xxxxxx',
'bm_sz': 'xxxxxx'
}
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Content-Type': 'application/octet-stream',
'Origin': 'https://api.nike.com',
'Referer': 'https://api.nike.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fp?x-kpsdk-v=j-0.0.0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',,
'x-kpsdk-ct': '07jjjkZdFYduVyLRdmpSSNbaFEb7gEPdLO9qEK2GhzxmATbL5FZylEoQaEjucSZg3cErKqvzsomznrTQIoz408MUKYH11Hae80eaAcAT4uwROJvsaei0Z7I8c5AlokjtSf7kf946Mw2h7xrrMphWJUEOHSJPRnI',
'x-kpsdk-dt': '11q9w02paw59y35x41f5w73w6eky2n',
'x-kpsdk-im': 'CiQ1ZGNkZTZhZC0xMjg5LTQ3MDUtYTY2My05NGM5Yjk2NTU3NjA',
'x-kpsdk-v': 'j-0.0.0',
}
# 有删减
data = '\x00\x02BxÜ\x00f0\x10\x00\x14~\x98\x94½l\x19¤õ¹\x11\x10\x02\x87\x91\f\x10»z³%\x91Ç\x80Õ\x04Ã\x9fË$\t?>ósaé¢ä\x9a*\x8e\x14\x99\x8e\x95(û\x06¢Åg\t±¨BAÝÉ9Û.\x1e_\x9aöAñ|"'.encode()
response = requests.post(
'https://api.nike.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/tl',
cookies=cookies,
headers=headers,
data=data,
)
"""
核心响应:
cookies:
ak_bmsc_nke-2.3-ssn: 07Pa4lqGCMrMKksS7KrEqa7Kzp8igR5QjVGQ8QjRjiVa3oLDwRqj45IqD0R6ZZHf59xb4agcILXyQrszQfhqsqrem3rnl1fLErRPvYvHtUaEkOMCoNfFcdTjotv1CnmgrIt6IJeGPCLdrgaEy52QFdTJBABhXkz
ak_bmsc_nke-2.3: 07Pa4lqGCMrMKksS7KrEqa7Kzp8igR5QjVGQ8QjRjiVa3oLDwRqj45IqD0R6ZZHf59xb4agcILXyQrszQfhqsqrem3rnl1fLErRPvYvHtUaEkOMCoNfFcdTjotv1CnmgrIt6IJeGPCLdrgaEy52QFdTJBABhXkz
X-Kpsdk-Ct: 07Pa4lqGCMrMKksS7KrEqa7Kzp8igR5QjVGQ8QjRjiVa3oLDwRqj45IqD0R6ZZHf59xb4agcILXyQrszQfhqsqrem3rnl1fLErRPvYvHtUaEkOMCoNfFcdTjotv1CnmgrIt6IJeGPCLdrgaEy52QFdTJBABhXkz
# 更新headers, 用于下次请求
X-Kpsdk-St: 1708323663441
# 生成workTime
"""
追栈分析
tl是xhr,直接xhr断点
往前看几步,可以看到
需要注意的是body是一个Uint8Array类型数据
尝试追payload值
搜索new Uint8Array
发现搜不到
搜索new,发现一个可疑的地方
在这里把Function.bind.apply(_, u)输出日志断点
看了一下,确实是有Uint8Array,而且有很多。
这里可以继续打条件断电看值,这里我就不打了,因为我已经打过了,这里确实能看到值,但是不是生成值的地方
// 注意打条件断点的时机,因为并不是只有一个Uint8Array
typeof Function.bind.apply(_, u) == 'function' && Function.bind.apply(_, u).toString().indexOf('Uint8Array') == 9
然后看堆栈,不难看出这是一个循环
再仔细看,发现是一个jsvmp,emm...,追站部分结束。。
vmp代码分析
ips.js代码分析,把它的代码扒下来,放本地分析一下
发现是一个自执行里面包裹一个大循环,循环28次,初始化d,d='cdknvozapzrdlosqqbevzuzzleiq'(不固定)
初始化f数组流程
数组f生成逻辑
p = function () {
var r = function () {
var n = 4294967295 * Math.random() | 0
, u = 4294967295 * Math.random() | 0;
return function () {
return n = n + 1640531527 | 0,
u = (u >> 16) + (u << 16) + n | 0,
(u & 2147483647) / 2147483648
}
}();
// TODO 这里有代码,省略了一点
// TODO 第三
function a() {
// 这里循环次数为随机数,循环u次,直到i.push(t())大于u退出循环
for (var n = 1 + Math.floor(8 * r() * r()), u = Math.max(Math.floor(4 * r()), n), i = []; i.length < u;)
i.push(t());
// 上面得到一个u长度的i
// 这里开始一个新的循环,循环
// TODO 第五
for (
var c = [
Math.floor((1 + 1e3 * r()) * Math.exp(7 * r())),
e(), // TODO 第六
void 0,
function () { // TODO 把i数组,转成一个arguments对象
return arguments
}.apply(void 0, i)
];
i.length < n; // 满足条件就循环,不满足不循环
)
i.push(t());
return c.push.apply(c, i), // 将c和i进行数组进行合并
c
}
// TODO:入口 f = $(o, a) 第一
for (
var o = 20,
f = $(o, a), // 这里得到一个长度为20的数组
E = function () {
// 返回一个随机数
return f[Math.floor(o * r())]
},
h = function () {
return E()[1]
},
l = 0;
l < o; // TODO 循环20次
l++ // 索引
) {
var I = (l + 1) % o; // 索引
// TODO 这里对初始化的f进行赋值,注意这个W
// // TODO 第七
f[l][1].W = M(f[I], E); // TODO 这里返回得是一个函数,函数返回一个 f[l()] 意味着每一个的f[l][1].W中都有一个方法用于返回
f[l][1].q = M(f[o - I - 1][1], h);
}
// TODO f初始化完成
return {
e: h,
n: E
}
}()
function $(r, t) {
/*
* r: 20固定
* t: p函数中的函数a
* */
// TODO 第二
for (var e = [], a = 0; a < r; a++)
// a从0开始,小于20
// t函数不接收参数,单纯的循环20次
e.push(t(a));
// 这里得到一个长度是20的数组
return e
}
// TODO 第四
function t() {
var n = r(); // 随机数
// 随机走不同流程
// 返回不同值
if (n < .29)
// 返回随机数
return r() < .2 ? -Math.floor(Math.exp(2 * r())) : r() < .15 ? 0 : Math.floor(Math.exp(7 * r() * r()));
if (!(n < .32)) {
if (n < .62) {
// 三个函数
var u = [e, t, a];
// 返回随机一个函数
return u[Math.floor(u.length * r())]
}
if (n < .87) {
// 随机往i中push(t()) 自身
for (var i = []; r() < .7;)
// TODO 递归
i.push(t());
return i
}
if (n < .97)
// 返回随机布尔值
return r() < .25;
var c = ["create", "offset", "start", "rand", "mv", "xyz", "count"];
// 返回c中随机的一个元素
return c[Math.floor(c.length * r())]
}
}
// TODO 第六
function e() {
// 这里也是循环i次,得到一个数组n
for (var n = [], u = Math.floor(Math.exp(4 + 4 * r())), i = 1 + Math.floor(8 * r()), c = 0; c < i; c++)
n[u++] = t(); // 这里也是走t()
return {
F: void 0,
T: n, // TODO 赋值n来这里
P: y, // 这个y是一个固定函数,先不管
W: function () {
return [0]
},
R: function () {
return [0]
},
q: function () {
}
}
}
// TODO 第七
var M = function () {
var r = 0;
return function (t, e) {
/*
* t: 20个数组中的其中一个
* e: 返回一个随机数
* */
// 循环三次,得到一个新的f数组
for (var a = 17 * r++ | 0, o = 3, f = [], E = 0; E < o; E++)
// TODO 第八
/*
e() -->
E = function() {
return f[Math.floor(o * r())] 返回f中的随机一个元素
}
*/
// TODO 前提 E === a % o 返回t,否则重新从20个元素中选
f.push(E === a % o ? t : e());
return function () {
var h = Math.floor(20 * Math.random());
var l = function () {
return a % (this + h)
}.bind(o - h); // o - h --》 3减一个随机数
return function () { // 返回一个函数,函数里面返回f的值
return f[l()]
}
}()
}
}()
初始化对象C
对象C生成逻辑
function _() {
var r = [
1,
{
F: T, // windows
P: null,
T: [],
W: function () {
return [0]
},
R: function () {
return [0]
},
q: function () {
}
},
void 0
];
return {
R: g(), // 随机获取一个 f中的元素的W是个方法
W: r,
B: void 0
}
}
g = function () {
return f[Math.floor(o * r())] // TODO 这里的f中的元素的W是个方法
}
// L = [8, 42, 28, 22, 2, 38]
// // S是大循环的很长的字符串,得到一个229660长的数组
// v = F(S, "9nWLIft=bBXmA3F6rZOYGTgoiw7N8s2MjSHduV4ezxDvc0pU^yQ$|+~qa5lJCkP1REhK><", 40)
function D(r) {
for (var t = [T, [w, b], v], e = [Y, O, _, D, d, M, x, g, z]; ;) {
var a = U[v[r.W[0]++]];
try {
var o = a(r, y, V, m, t, e);
if (o === null)
break
} catch (f) {
O(r, f)
}
}
}
var C = _();
{
s.a = function (r, t) {
return s.r.slice(r, r + t)
}
;
var A = v[N + d.indexOf(".")] ^ N
, B = v.splice(A, v[A + C.W[0]] + 2);
s.r = R(B, C.W[1].W(), L) // 这里得到一个很长的字符串
}
// TODO 上述代码就是获得了一个C一个s
// 递归调用
D(C)
主体(vm)函数
function D(r) {
for (var t = [T, [w, b], v], e = [Y, O, _, D, d, M, x, g, z]; ;) {
var a = U[v[r.W[0]++]];
try {
var o = a(r, y, V, m, t, e);
if (o === null) // 结束条件
break
} catch (f) {
O(r, f)
}
}
}
通过进入里面的a方法,通过参数来控制,进入不同的方法(指令集)
直到生成结果
vmp插桩分析
通过动态调试加静态分析的方法,找到被多次调用的方法,然后打上日志断点,让日志输出。
我已经调试过就不走弯路了
通过调试,找到这里s.r.slice(r, r + t),可以将所有的字符串进行输出,而且都是string类型
需要注意的是
不能直接打日志断点,因为浏览器会卡死,应该采用 "" + ybb 的方式,然后复制到本地进行分析
vmp用的都是切割字符串的方式,因此不能console.log(s.r.slice(r, r + t)),这样会改变原来的字符串,应该修改代码结构,如上图
将日志打印完成之后效果
还有另一个重要的指令集
在这里把t给输出到本地,方法同上,但是要注意打日志断点的时机(结合上面的全部日志进行判断,控制window.islog),否则也会导致浏览器卡死/报错,我这里只输出了 typeof t == 'string' 类型的,其实也可以把数组之类的也输出,但是需要判断一下长度之类的,否则会报错
输出之后发现几个要找的值都在里面
x-kpsdk-ct
上图看 x-kpsdk-ct 值,通过最长的那个字符串切分出来的= =
x-kpsdk-dt
上图看 x-kpsdk-dt 值,很明显是有在做加法运算,最终得到结果,这个逻辑就要调试代码才能知道了
payload
接下来找payload
找第一份日志文件中最后的几个Uint8Array
因为已经找过了,所以就直接这样断点
断住后,直接到这里断点
发现环境数组
环境数组
[
"0KPEbwJyHcmY0JMH0C27b8WSXfTm70pvAwwpB5UrpFw7u0oszEolbdCzXuHhCouw8cTZJnzFzxQNN1vBQHQr5HoLSZ1ODUQwthqP48YCCPHPJh8vqgXukdG86N8NcX1EZvXm8zzL2OSTy9ZvQhRdd5VtTC1q53yNsWprQyjKNYoJUd8RFrB7LeQeqhLi1qn8niGxMnibWMVQRX2bipiKbDk84xYPfJsQuSLRDX6Ank4EhENQ53wYZeaM5aMXd3g6j7zhq16tlkMF0Su871UsnHT8x7vFg5t5hW1NuItEvk0o0O0qjhfItWTEfz8nwAeelQNBidez34ExKOHPs1ZWgMLcNg5z1MutheVXPbTU80XpULToGhOhfwvvQN7cLm0HCeMlAhTzLeHCh3P7XwPQHUBJo5Yo6Dfg1wyo1NgMehMGi3mcGpuuT8rmuSt4wvZjqTCSG6JrOUeC2co8hLh2xlwcT3hQ6DpHMSxN0dVfh4U55UqVLtIYQR4uGPTE5oQuohAMFdQTYzPpHHmIWPGqwvnPSAt18Mg3f3uQUIxLhU5wD62dTpCQIU0dHJ7I7kd1LbYMlYlzUUcP9z07X2Opaxn7RxmplqI3yk86QCLQ5rAMxK3x1RSk1vhkPVSHlw0dj97Ua9uRwtRCEqFFdhFgxSwR5LASrwI8f5TBRs3olIw7M1nKjAiD0GPEeqBi2QcnJiIrPslIJkXU1RxJJarSwUCZbqRJM6C5IKlBYXAYvIacLaAueXQQusxAf75LnipJHGqoUjSzuEwJ02ohXKZlP6CFHzVtBqR283piS5LduDxb6YvTnzZhIpohB9gjnj0q3k4ZjS9Bo2QHbJOQnmDPxU0DYLmScgKVuvOvBz3KqDdZamEoqnoWGMWPy4YeRvca4Qxk0XUxO5MukCO54peJiDkbROhaP8vcfsXRlCj8J2f7fKonz4H28CMXayFQ5Ernu7QC5jzLGGPYoPnuvMu04rhpjkyUjB6XWHBGJhYAvNPnpD5ymgQOnhD5qQCEUFf6qv10bROWAGADAVKmPqakCfqVEkOTXfw2WHON8UBdBGYNpteO2os9gl8m4RVkEmqVad2P3c07ts57lW2zzT712iwkGqKGvzSZmCW8GmuEvEbqyeNDQw4SQj6rY7yj7SwlPcVaOqlFZoMeauIdeuPRhNiBKYXMwiygw0oMvgKf1zpcoDPhQBxR7ftBFiBiMIE9MqckHM3h6Nqn8cBe0ngUaSVXrAWWgbvUBmuwnjywGeIQUQjOxGIzAURqm6Wcu70iF8yXOxuUI7NJUY9uNSKrZgW2lxSmKKd0pSOV1E1hj5c7xSx6R9eTBatD1ldvPeon1dWYDnXkKCgqhO4wviDdsIZBzsn82r4tKxhTIbyXqVkkHtVV7OGI4ydO6b90AziJxOtf7h9iGAFfOjuSXOCOPMiUiyYSIB95F1xqBi3hTx8N76w6huMRWyTvtfQGmLvjzhyXFGckL8Y8egWiOXLSuBE7O9Z3DmBLEqieLEGDXK6gF7P5eNcMVuLf2hiavXeTb4X6ppyt6RKhph8QRjWbKXUIyj8ljXbZDStKD7D7hlcLEJzFqRi9V8qrQbjYFk7Sw5GM6tgBtfYuuaXIlEom47U0aKnTWUJjnnM582K0yUsFskuRc1DkLilMQ1WjveNliQldfXzh7SrZ4SlYuijfqKJVy1CjYpY9DNCV9okaCDRmlxkGx0HImlK7Bifs2TbC00UB2jvdmcGdDl0QTqRLPH3zaz099KJODcMceXtQzWBnlOlEOseNz9lTRMcb1pFQVB7yBkazUs6l5P6Mj9T5KLwcAIHL0BxoZ6K4YhFNyNgNLgvoZWqoD56VhImXeeKZQufdcmUeAr",
"TITAyxTYkM5MTN=k",
[
0
],
[
"5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
],
[
0
],
[
1130,
2.2
],
[
"maybe"
],
[
3
],
[
0
],
[
"0"
],
[
"Win32"
],
[
"probably"
],
[
"中国标准时间"
],
[
"ANGLE (Intel, Intel(R) UHD Graphics 770 (0x00004680) Direct3D11 vs_5_0 ps_5_0, D3D11)"
],
[
false
],
[
""
],
[
"Google Inc. (Intel)"
],
[
"onscrollend"
],
[
3
],
[
true
],
[
2
],
[
0
],
[
true
],
[
0
],
[
0
],
[
0
],
[
0
],
[
9
],
[
2
],
[
10
],
[
"webkitSpeechRecognition"
],
[
0
],
[
null
],
[
"probably"
],
[
""
],
[
""
],
[
4
],
[
{
"value": [
283174996032000,
"cUWuCi%)K6q&3Or"
]
}
],
[
"zh-CN"
],
[
null
],
[
"中国标准时间"
],
[
false
],
[
0
],
[
""
],
[
0
],
[
true
],
[
1
],
[
22
],
[
"Number"
],
[
"webkitResolveLocalFileSystemURL"
],
[
""
],
[
20
],
[
null
],
[
200704
],
[
null
],
[
"document"
],
[
"TypeError: Function.prototype.toString requires that 'this' be a Function\n at Number.toString (<anonymous>)\n at eval (eval at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:716:14), <anonymous>:3:16)\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:909:24)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)\n at https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:852:13\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:993:14)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)\n at https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:852:13\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:909:24)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)"
],
[
2
],
[
6
],
[
4
],
[
4
],
[
null
],
[
true
],
[
4
],
[
"KPSDK"
],
[
17
],
[
"0"
],
[
7
],
[
0
],
[
0
],
[
232
],
[
"zh-CN"
],
[
false
],
[
9
],
[
true
],
{
"t": [
"85d.19999a",
false,
166.2,
"3.0"
]
},
[
"Array"
],
[
"Google Inc. (Intel)"
],
[
4
],
[
24
],
[
true
],
[
0
],
[
"ANGLE (Intel, Intel(R) UHD Graphics 770 (0x00004680) Direct3D11 vs_5_0 ps_5_0, D3D11)"
],
[
3
],
[
8
],
[
"webkitResolveLocalFileSystemURL"
],
[
225
],
[
"probably"
],
[
false
],
[
""
],
[
0
],
[
"zh-CN"
],
[
"1,1024"
],
[
"maybe"
],
[
"2164560f-8a68-427a-a750-4d5e60b1e108"
],
[
"probably"
],
[
[
"getRandomValues",
"constructor",
"subtle",
"randomUUID",
"seedRandomValue",
"generate256BitKey",
"regenerate256BitKey",
"setRandomBits",
"getLatestHMACVersion",
"__defineGetter__",
"__defineSetter__",
"hasOwnProperty",
"__lookupGetter__",
"__lookupSetter__",
"isPrototypeOf",
"propertyIsEnumerable",
"toString",
"valueOf",
"__proto__",
"toLocaleString"
]
],
[
"3"
],
[
"5Az!0023"
],
[
0
],
[
"credentialless"
],
[
6
],
[
1920
],
[
24
],
[
null
],
[
null
],
[
"Class extends value #<MimeTypeArray> is not a constructor or null"
],
[
"self"
],
[
"webkitSpeechRecognitionEvent"
],
[
null
],
[
0
],
[
"webkitRequestFileSystem"
],
[
""
],
[
"KPSDK"
],
[
"Object"
],
[
1080
],
[
""
],
[
1
],
[
1920
],
[
"landscape-primary"
],
[
"probably"
],
[
"webkitResolveLocalFileSystemURL"
],
[
"webkitRequestFileSystem"
],
[
false
],
[
5
],
[
null
],
[
false
],
[
2
],
[
"probably"
],
[
""
],
[
"Function"
],
[
"speechSynthesis"
],
[
1040
],
[
"WebGL 2.0 (OpenGL ES 3.0 Chromium)"
],
[
0
],
[
"1,1"
],
[
""
],
[
[
[
"PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Chrome PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Chromium PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Microsoft Edge PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"WebKit built-in PDF",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
]
]
],
[
0
],
[
"onscrollend"
],
[
"50"
],
[
"probably"
],
[
"1709202759153-1921326662-Win32-4047ghi"
],
[
"Illegal invocation"
],
[
""
],
[
"Class extends value #<PluginArray> is not a constructor or null"
],
[
"webkitSpeechRecognitionEvent"
],
[
29
],
[
3
],
[
0
],
[
"zh-CN"
],
[
4
],
[
"Class extends value function createElement() { [native code] } is not a constructor or null"
],
[
"parseFloat"
],
[
true
],
[
"0"
],
[
-480
],
[
null
],
[
13
],
[
"vilame_setter"
],
[
7
],
[
-480
],
[
"maybe"
],
[
1
],
[
"中国标准时间"
],
[
"3"
],
[
false
],
[
"probably"
],
[
"1"
],
[
0
],
[
1920
],
[
0
],
[
24
],
[
false
],
[
5
],
[
3
],
[
1040
],
[
""
],
[
true
],
[
10
],
[
3
],
[
"2"
],
[
8
],
[
"Class extends value function query() { [native code] } is not a constructor or null"
],
[
5
],
[
1080
],
[
4
],
[
"webkitRequestFileSystem"
],
[
"50"
],
[
2
],
[
""
],
[
11
],
[
"0"
],
[
[
4128,
16638
]
],
[
"probably"
],
[
5
],
[
""
],
[
""
],
[
1
],
[
"probably"
],
[
1137
],
[
null
],
[
0
],
[
"2"
],
[
null
],
[
1920
],
[
24
],
[
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
],
[
"1"
],
[
"maybe"
],
[
3
],
[
60
],
[
"Illegal invocation"
],
[
20
],
[
"Illegal invocation"
],
[
null
],
[
"5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
],
[
"Win32"
],
[
"0"
],
[
0
],
[
null
],
[
"4"
],
[
null
],
[
3
],
[
0
],
[
"Function.prototype.toString requires that 'this' be a Function"
],
[
""
],
[
1024
],
[
true
],
[
3
],
[
null
],
[
"webkitSpeechRecognitionError"
],
[
1
],
[
1
],
[
"webkitResolveLocalFileSystemURL"
],
[
null
],
[
"name"
],
[
""
],
[
24
],
[
24
],
[
""
],
[
0
],
[
""
],
[
false
],
[
"Function.prototype.toString requires that 'this' be a Function"
],
[
true
],
[
0
],
[
2
],
[
"window"
],
[
3
],
[
24
],
[
2
],
[
[
[
"PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Chrome PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Chromium PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"Microsoft Edge PDF Viewer",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
],
[
"WebKit built-in PDF",
"Portable Document Format",
[
[
"application/pdf",
"pdf"
],
[
"text/pdf",
"pdf"
]
]
]
]
],
[
"probably"
],
[
true
],
[
""
],
[
2
],
[
"Illegal invocation"
],
[
"vilame_setter"
],
[
212992
],
[
2
],
[
0
],
[
"0"
],
[
"location"
],
[
"probably"
],
[
151
],
[
"TypeError: Function.prototype.toString requires that 'this' be a Function\n at toString (<anonymous>)\n at eval (eval at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:716:14), <anonymous>:3:29)\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:909:24)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)\n at https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:852:13\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:993:14)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)\n at https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:852:13\n at U (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:909:24)\n at D (https://api.nike.com/[path]/ips.js?ak_bmsc_nke-2.3=07HMQh4SbW8Nxg08[...]:1144:25)"
],
[
0
],
[
"4"
],
[
"webkitRequestFileSystem"
],
[
0
],
[
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
],
[
"landscape-primary"
],
[
"probably"
],
[
0
],
[
"MzMzMjMyeERORFUxeEQzMTMxMzh4RE5EQTB4RDMyMzIzN3hETmpVdw==",
142.3
]
]
环境数组通过TextEncoder后,变成Unit8Array
搜索a(n, _(u, r, l))
u: [123, 144, 165, 23, 49, 34, 196, 213, 61, 127, 128, 195, 188, 38, 231, 103]
r: [83, 204, 246, 249, 160, 248, 155, 27]
l: 是上面那个Uint8Array
接着进入VM
得到:
VM代码
(function anonymous() {
return (function() {
function e(e, t) {
var r = 8 * t;
return [n(e, r), n(e, r + 4)];
}
function t(e, _ref7, _ref8, i, c) {
var t = _ref7[0]
, n = _ref7[1];
var r = _ref8[0]
, o = _ref8[1];
var a = function(e, t) {
var n = t[0]
, r = t[1]
, o = 0;
for (var _t4 = 0; _t4 < 32; _t4 += 1)
n = n + ((r << 4 ^ r >> 5) + r ^ o + e[3 & o]) | 0,
o = o + 2654435769 | 0,
r = r + ((n << 4 ^ n >> 5) + n ^ o + e[o >> 11 & 3]) | 0;
return [n, r];
}(e, [r ^ t, o ^ n]);
return function(e, t, _ref9) {
var n = _ref9[0]
, r = _ref9[1];
var o = 8 * t;
e[o] = n >> 24 & 255,
e[o + 1] = n >> 16 & 255,
e[o + 2] = n >> 8 & 255,
e[o + 3] = 255 & n,
e[o + 4] = r >> 24 & 255,
e[o + 5] = r >> 16 & 255,
e[o + 6] = r >> 8 & 255,
e[o + 7] = 255 & r;
}(i, c, a),
a;
}
function n(e, t) {
return e[t] << 24 | e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3] << 0;
}
return [e, t, function(r, o, i) {
var c = function(e) {
if (16 !== e.length)
throw new Error("key must be 128-bit");
return [n(e, 0), n(e, 4), n(e, 8), n(e, 12)];
}(r)
, a = function(e) {
if (8 !== e.length)
throw new Error("iv must be 64-bit");
return [n(e, 0), n(e, 4)];
}(o)
, s = []
, u = Math.ceil(i.length / 8);
var l = 0
, d = 0;
var f = [];
var p = t(c, [0, 0], a, f, d++);
for (s.push([0, i.length]); s.length < 5 && l < u; )
s.push(e(i, l++));
var y = 0;
for (; s.length > 0; ) {
y = (y + p[0]) % s.length,
y < 0 && (y += s.length);
p = t(c, p, s[y], f, d++),
l < u ? s[y] = e(i, l++) : s.splice(y, 1);
}
return f;
}
, function(e) {
var t = [];
for (var _n2 = 0; _n2 < e.length; _n2 += 1) {
var _r3 = e.charCodeAt(_n2);
_r3 <= 255 ? t.push(_r3) : t.push(255);
}
return t;
}
];
}
)
}
)
接着,一个长度为2的和一个长度为8数字进行拼接,得到长度为10的数组
最终,长度10的数字和VM出来的大数组拼接得到最终的payload,拼接上长度是8706
发起请求时,长度是8706
反编译
首先要搞清楚vm运行流程,写起ast就会事半功倍。
vm运行流程
vm主体函数
先看vm主体函数
可以看到,传入vm的参数只有一个r,v是字节码数组,r.W[0]是即将执行的字节码索引。原代码中U为指令集,这里将U改为了和腾讯vmp同样处理手段的function。
分析指令集发现大多数指令都是简单的二元运算:
堆栈
简单跟栈后可以看出,n可以看做vm的堆栈,e(n)为pop(这里的pop只取值,不从栈中删元素),a(n, xxx)为push(push也不是简单的放到栈前或栈后,而是stack[index] = xxx)。
其中入栈为:
G为下:
也就是说,以入栈操作码的下一位为索引,取字节码相应位置数字后右移5来储存入栈元素。
出栈:
R:
根据传入的索引,取用到的常量(数字、字符串、布尔值等)或者入栈的变量。
指令
17为变量初始化,78为函数初始化,52为对象属性绑定,65、42、93等对n.W[0]有操作的为跳转、if/for等,含有a(n, _(的如82、102、103等为函数执行。与r[index]相关为vm外部函数或者变量的调用。
ast编写
跟tdc区别不大,多了个内存池出来储存一些object的属性,由于对每个obj的命名都是唯一的,将每个a[b]转化为了obj_xxx = a[b],将obj_xxx放入堆栈而不是把a[b]直接放入堆栈,所以不需要做详细的作用域管理。
修改出栈类型:
重写指令:
修改vm函数,还原函数体、判断等:
这里借助了遗心大佬反编译美团vmp的思路,加了一个start和end索引(这里的start为r.W[0]就不用再加了),判断、循环、函数还原简单了不少。
坑
虚假分支,kasada的偏简单,根据特征直接eval即可判断真假,剔除错误分支。
结束
emm.............................