免责声明:本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

前言

webpack抠JS的一个案例,用来熟练如何从webpack打包的JS代码中抠关键的JS代码。

抠JS过程

网址:aHR0cHM6Ly9zeW5jb25odWIuY29zY29zaGlwcGluZy5jb20v

目标:登录接口的密码加密JS代码分析。

老规矩,先抓包。

image-20220404150944421
image-20220404150944421

可以看到登录请求的密码这个参数是加密的。

搜索关键词password,找到一个疑似加密的函数,如下:

image-20220404162426120
image-20220404162426120

点进去看到加密的地方:

image-20220404163306525
image-20220404163306525

接着点进去看这个o.a函数:

image-20220404163401493
image-20220404163401493

可以看到,对密码采用了RSA加密,然后进行Base64编码。然后看下代码结构,很显然是按照webpack模块化编程进行组织的。

按照前面介绍的——JS逆向之webpack扣JS思路——这篇文章总结的方法:

  1. 找到模块加载器。

快速定位模块加载器,一般可以通过搜索}({,迅速定位到,但是包含加密函数的JS文件中并未找到模块加载器,没有找到的话,我们自己写一个好了。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
function b(n) {
if (e[n])
return e[n].exports;
var u = e[n] = {
i: n,
l: !1,
exports: {}
};
return c[n].call(u.exports, u, u.exports, b),
u.l = !0,
u.exports
}
  1. 构造自执行。

这个也比较简单,我们把上边的代码,稍微做修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
!function(c) {
var e = {};
function b(n) {
if (e[n])
return e[n].exports;
var u = e[n] = {
i: n,
l: !1,
exports: {}
};
return c[n].call(u.exports, u, u.exports, b),
u.l = !0,
u.exports
}
encode = b;
}({
// TODO
});
  1. 找到并抠出需要的模块。

抠JS就变成了一道填空题,把加密方法依赖到的模块抠出来,作为参数填到上边自执行函数中去。我们先抠出加密方法, 观察下依赖哪些模块:

1
2
3
4
5
6
7
8
9
var r = n("XBrZ");
var t = r.pki.publicKeyFromPem(
"-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy4xppPDUT2eAOR5h0cyydzxtKB9O80A\n GjUT6FmDgg6CwelpnE0C2h2JQyP1gCveJs6GDwSDn20RVVpD67f//YPYErjaH/CBOxNG3k5IkW1o\n Qx04uqFNMtWvjzk0aFh2eJLsBi7Ha4elw3WySg00B8oZCL4VBay4ML9kyOAjjCj5jHCX8a2yxIMJ\n IF+EjW3kBR68IMwBvuDL45Qa0oB24vTffaSEs+hGjMTQvoCciOfti3pmEAlVc438/cBgAhK5cIMf\n IMElxYAVvmsDy0I7RCUTrajetKjX94Q+JuQUxnIHNC3IVtYsl1x0lNRtb93IhlRCkZ9djOu350eq\n hZIOXQIDAQAB\n -----END PUBLIC KEY-----").encrypt(e, "RSA-OAEP", {
md: r.md.sha256.create(),
mgf1: {
md: r.md.sha1.create()
}
});
return window.btoa(t)

通过debug知e是我们填入的密码——即123456,唯一用到的模块是键为XBrZ的模块。我们全局搜索XBrZ: 找到对应的模块定义:

image-20220404165620085
image-20220404165620085

点进去发现这个模块又引用了很多其它的模块,如果按照模块一个一个的抠的话,比较费时。所以我们将这整个文件中定义的模块全部抠下来,作为我们上边定义的自执行函数的参数。

image-20220404165643735
image-20220404165643735

整理完了之后,我们将代码放到浏览器中检验一下,防止出错:

image-20220404170102015
image-20220404170102015

不出意外的话就不会出意外,没有报错。

  1. 导出相应的模块。

模块通过模块加载器加载,所以想要得到加密方法依赖的模块,只需要导出模块函数即可。定义一个全局变量,比如encode,然后将上边实现的模块加载函数赋值给这个全局变量即可。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var encode;

!function(c) {
var e = {};
function b(n) {
if (e[n])
return e[n].exports;
var u = e[n] = {
i: n,
l: !1,
exports: {}
};
return c[n].call(u.exports, u, u.exports, b),
u.l = !0,
u.exports
}
encode = b;
}({
// 此处省略若干行模块函数的定义
});
  1. 编写测试代码。

对于这个案例而言,测试代码就是那一串加密代码,看能否如期的得到类似的加密字符串,就证明我们抠的JS没有问题,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
function get_pass(passwd) {
var r = encode("XBrZ"); // 通过模块加载器加载XBrZ模块。
var t = r.pki.publicKeyFromPem("-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy4xppPDUT2eAOR5h0cyydzxtKB9O80A\n GjUT6FmDgg6CwelpnE0C2h2JQyP1gCveJs6GDwSDn20RVVpD67f//YPYErjaH/CBOxNG3k5IkW1o\n Qx04uqFNMtWvjzk0aFh2eJLsBi7Ha4elw3WySg00B8oZCL4VBay4ML9kyOAjjCj5jHCX8a2yxIMJ\n IF+EjW3kBR68IMwBvuDL45Qa0oB24vTffaSEs+hGjMTQvoCciOfti3pmEAlVc438/cBgAhK5cIMf\n IMElxYAVvmsDy0I7RCUTrajetKjX94Q+JuQUxnIHNC3IVtYsl1x0lNRtb93IhlRCkZ9djOu350eq\n hZIOXQIDAQAB\n -----END PUBLIC KEY-----").encrypt(passwd, "RSA-OAEP", {
md: r.md.sha256.create(),
mgf1: {
md: r.md.sha1.create()
}
});
return window.btoa(t)
}

console.log(get_pass("123456"));

运行测试代码,正常的输出了密码加密之后的密文:

image-20220404170927135
image-20220404170927135

总结

从webpack组织的JS代码中抠JS,虽然看起来比较简单,但是如果遇到复杂一点的案例,并且对JS语法不太熟的话,还是有一定的难度的的,所以需要对这一块多多练习。后边关于webpack的,还会再出一些案例,一些更加复杂的案例。

关于代码的获取

若需要代码,扫描加微信即可。

image-20230517010053227