webpack逆向

一个使用了webpack的网站sign参数加密破解

编程 LEO 2021-11-14

webpack逆向(参考教程)

[toc]

此网站 网站为例进行说明

1. 定位加密参数在js文件中的位置

请求中的sign参数是加密的,可以通过触发链下断点找到相应位置

image-20211114165501963

image-20211114165713828

显然,上面的t后面的函数就是加密的函数,通过断点定位到函数的具体定义

2. 找到webpack的分发器

点step into 就进入一个js文件,仔细看发现这里就是webpack的分发器,代码有如下特点(这里只保留了一些反应分发器特点的代码):

  1. 这是个立即执行函数(IIFE)
  2. 有一个语句是e[r].call(t.exports, t, t.exports, l),就是有它进行各种模块的调用,其中最后一个就是调用的函数。
  3. 这里传入的参数是个空数组,具体实现时可以放入需要调用的模块对象
  4. 最后的 t()我也不知道干啥的,后面要替换成自定定义的全局函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
!function(e) {
}
function l(r) {
if (n[r])
return n[r].exports;
var t = n[r] = {
i: r,
l: !1,
exports: {}
}
, o = !0;
try {
e[r].call(t.exports, t, t.exports, l),
o = !1
} finally {
o && delete n[r]
}
return t.l = !0,
t.exports
}
t()
}([]);

3. 测试能否成功调用

把分发器的代码复制一份,定义好几个全局变量,window和你要在外部使用的去调用某个模块的函数,这里是get_sign,然后把上面提到的t()替换成get_sign = l ;这里这个 l 就是e[r].call(t.exports, t, t.exports, l)最后一个参数。然后就可以在分发器中初入一个测试对象,如下图中的,再调用get_sign("0")就可以执行这个函数了。

1
2
3
4
{
0: function () {
console.log("HHHH");
}

image-20211114171410546

4. 找到加密函数所在模块

image-20211114172356722

上面的u函数是关键,再定位到u函数:

u函数在 aCH8模块,所以只需要吧下面的这段代码放到前面和测试代码一样的位置,稍候就可以调用这个函数了。

这里注意到aCH8里面又调用了ANhw,mmNF等模块,所以需要把这些模块的代码也找到,之后定位到一个存放了各种模块的文件,直接

copy保存到一个文件modules.js(这里面也要顶一个window全局变量),再到主函数文件里用 require(./modules.js)导入。

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
aCH8: function(e, t, n) {
!function() {
var t = n("ANhw")
, r = n("mmNF").utf8
, o = n("BEtg")
, a = n("mmNF").bin
, u = function(e, n) {
e.constructor == String ? e = n && "binary" === n.encoding ? a.stringToBytes(e) : r.stringToBytes(e) : o(e) ? e = Array.prototype.slice.call(e, 0) : Array.isArray(e) || e.constructor === Uint8Array || (e = e.toString());
for (var i = t.bytesToWords(e), l = 8 * e.length, f = 1732584193, c = -271733879, s = -1732584194, d = 271733878, p = 0; p < i.length; p++)
i[p] = 16711935 & (i[p] << 8 | i[p] >>> 24) | 4278255360 & (i[p] << 24 | i[p] >>> 8);
i[l >>> 5] |= 128 << l % 32,
i[14 + (l + 64 >>> 9 << 4)] = l;
var v = u._ff
, h = u._gg
, y = u._hh
, m = u._ii;
for (p = 0; p < i.length; p += 16) {
var g = f
, b = c
, _ = s
, w = d;
}
return t.endian([f, c, s, d])
};
u._ff = function(e, t, n, r, o, a, u) {
var i = e + (t & n | ~t & r) + (o >>> 0) + u;
return (i << a | i >>> 32 - a) + t
}
,
u._gg = function(e, t, n, r, o, a, u) {
var i = e + (t & r | n & ~r) + (o >>> 0) + u;
return (i << a | i >>> 32 - a) + t
}
,
u._hh = function(e, t, n, r, o, a, u) {
var i = e + (t ^ n ^ r) + (o >>> 0) + u;
return (i << a | i >>> 32 - a) + t
}
,
u._ii = function(e, t, n, r, o, a, u) {
var i = e + (n ^ (t | ~r)) + (o >>> 0) + u;
return (i << a | i >>> 32 - a) + t
}
,
u._blocksize = 16,
u._digestsize = 16,
e.exports = function(e, n) {
if (void 0 === e || null === e)
throw new Error("Illegal argument " + e);
var r = t.wordsToBytes(u(e, n));
return n && n.asBytes ? r : n && n.asString ? a.bytesToString(r) : t.bytesToHex(r)
}
}()
},

5. 传入参数进行调用

定位到 u 这个加密函数后,我们就可以在外部利用get_sign调用它了(实际是调用aCH8),如何传入明文参数呢,可以利用全局变量

在外部声明一个 params 变量, 然后在u(e)函数定义的前面直接把params赋值给e,就可以实现传参了;最后再调用这个函数把结果传

给全局变量,之后拿到这个结果就可以进行进一步处理然后得到加密参数了。

1
var params = "fjc_xhup好";

image-20211114173734343

image-20211114173832587

6. 最后的处理

image-20211114174410361

把从u得到的结果再通过两个函数处理就得到最后的sign值了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var misc = {
wordsToBytes: function (e) {
for (var t = [], n = 0; n < 32 * e.length; n += 8)
t.push((e[n >>> 5] >>> (24 - (n % 32))) & 255);
return t;
},
bytesToHex: function (e) {
for (var t = [], n = 0; n < e.length; n++)
t.push((e[n] >>> 4).toString(16)), t.push((15 & e[n]).toString(16));
return t.join("");
},
};

var params = "fjc_xhup好";
get_sign("aCH8");

var r = misc["wordsToBytes"](window.temp);
var sign = misc["bytesToHex"](r);
console.log(sign);

总结

  • 找到webpack分发器很关键,替换掉其中需要执行的函数
  • 找到相关的加密模块以及依赖的其他模块
  • 通过全局变量传入或传出参数