aHR0cHM6Ly9wYXNzcG9ydC55aGQuY29tLw==
各位老师傅早上、上午、中午、下午、晚上好啊,新人贴就发一个简单补环境吧
虽然简单,但是肥肠详细
1. 抓包
我们固定 username = "12333333333",password = "123456"
试登录多次会出现滑块,手动验证吧,本篇不搞滑块

login.png (164.46 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传
多发俩包发现,credentials.username 与 credentials.password 的长度与内容会变化,考虑非对称加密,即 RSA
2. 跟参数与扣代码

callstack.png (88.28 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传

send.png (219.2 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传

ajax.png (142.33 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传

b8.png (174.39 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传

params.png (147.17 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传
最终确定在 `double_submit` 这一栈
扣下代码:
var JSEncryptExports = {};
(function(a6) {
// ......
})(JSEncryptExports);
var JSEncrypt = JSEncryptExports.JSEncrypt;
var k = "12333333333";
var j = "123456";
var i = new JSEncrypt();
i.setPublicKey(pubkey);
j = i.encrypt(j);
k = i.encrypt(k);
console.log(k);
console.log(j);
直接跑,就是扑面而来的 navigator is not defined
3. 构建补环境的框架
补环境最好不要一股脑都补到源码头上,特别涉及原型链的时候,一大坨很难看,用框架思路补,清晰不易乱
本篇使用志远的开源补环境框架思路:框架仓库
、b站视频
框架本身志远老师已经讲的很详细了,本篇仅概括一下:
- envs
- Window.js -> 补上的环境
- Navigator.js
......
- env.js -> 作为环境汇总拼接
- tools
- proxy.js -> 工具函数
......
- tool.js -> 作为工具汇总拼接
- src.js -> 目标源码
- main.js -> 主入口
执行时,env.js 和 tool.js 读取 envs 和 tools 目录下的所有文本代码,与目标源码拼接成字符串,于 main.js 中使用 vm2 沙箱执行
4. 开补!
第 1 回合
万补始于 window,vm2 的全局对象使用 this,避免会检测 global
// Window.js
window = this;
window = vmProxy(window, "window"); // 套上 Proxy 代理
再补上 navigator,navigator 所在的原型链为 navigator.__proto__ -> Navigator.prototype -> Object.prototype, 简单好补
// Navigator.js
Navigator = function Navigator() {}; safefunction(Navigator);
Object.defineProperties(Navigator.prototype, { [Symbol.toStringTag]: { value: "Navigator", configurable: true } });
navigator = new Navigator();
navigator = vmProxy(navigator, "navigator");
执行看看日志
[navigator get appName] ==> undefined
[navigator get appName] ==> undefined
[window get crypto] ==> undefined
[window get addEventListener] ==> undefined
[window get attachEvent] ==> undefined
[navigator get cajaVersion] ==> undefined
[navigator get userAgent] ==> undefined
[window get location] ==> undefined
[ set window.Hex] ==> [object Object]
[ set window.Base64] ==> [object Object]
[ set window.ASN1] ==> function a(h, g, i, k, j) {
this.stream = h;
this.header = g;
this.length = i;
this.tag = k;
this.sub = j
}
第 2 回合
把 undefined 都补上,尽量考虑原型链
// Navigator.js
Navigator.prototype = {
appName: "Netscape",
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0'
};
// Crypto.js
Crypto = function Crypto() { }; safefunction(Crypto);
Object.defineProperties(Crypto.prototype, { [Symbol.toStringTag]: { value: "Crypto", configurable: true } });
SubtleCrypto = function SubtleCrypto() { }; safefunction(SubtleCrypto);
Object.defineProperties(SubtleCrypto.prototype, { [Symbol.toStringTag]: { value: "SubtleCrypto", configurable: true } });
Crypto.prototype.subtle = new SubtleCrypto();
crypto = new Crypto();
crypto = vmProxy(crypto, "crypto");
// Location.js
Location = function Location() {}; safefunction(Location);
Object.defineProperties(Location.prototype, { [Symbol.toStringTag]: { value: "Location", configurable: true } });
location = {
"ancestorOrigins": {},
"href": "https://passport.yhd.com/",
"origin": "https://passport.yhd.com",
"protocol": "https:",
"host": "passport.yhd.com",
"hostname": "passport.yhd.com",
"port": "",
"pathname": "/",
"search": "",
"hash": ""
};
Object.setPrototypeOf(location, Location.prototype);
location = vmProxy(location, "location");
事件监听器 addEventListener 传入事件类型和回调函数,事件触发时执行回调函数。addEventListener 在原型 EventTarget.prototype 上,window 完整原型链:window.__proto__ -> Window.prototype -> WindowProperties.prototype -> EventTarget.prototype -> Object.prototype
// Window.js
WindowProperties = function WindowProperties() { }; safefunction(WindowProperties);
Object.defineProperties(WindowProperties.prototype, { [Symbol.toStringTag]: { value: "WindowProperties", configurable: true } });
Object.setPrototypeOf(WindowProperties.prototype, EventTarget.prototype);
Object.defineProperty(WindowProperties.prototype, "constructor", { value: WindowProperties, writable: true, configurable: true });
Window = function Window() { }; safefunction(Window);
Object.defineProperties(Window.prototype, { [Symbol.toStringTag]: { value: "Window", configurable: true } });
Object.setPrototypeOf(Window.prototype, WindowProperties.prototype);
Object.defineProperty(Window.prototype, "constructor", { value: Window, writable: true, configurable: true });
Object.setPrototypeOf(window, Window.prototype);
// EventTarget.js
EventTarget = function EventTarget() {}; safefunction(EventTarget);
Object.defineProperties(EventTarget.prototype, { [Symbol.toStringTag]: { value: "EventTarget", configurable: true } });
Object.defineProperty(EventTarget.prototype, "_listeners",
{
get: function () {
if (!this.__listeners) {
this.__listeners = {};
};
return this.__listeners;
},
configurable: true
}
);
EventTarget.prototype.addEventListener = function addEventListener(type, callback) {
if (!(type in this._listeners)) {
this._listeners[type] = [];
}
this._listeners[type].push(callback); // 实际传入的回调函数我们先存着
}; safefunction(EventTarget.prototype.addEventListener);
执行看看日志
[navigator get appName] ==> Netscape
[navigator get appName] ==> Netscape
[window get crypto] ==> [object Crypto]
[window get crypto] ==> [object Crypto]
[crypto get getRandomValues] ==> undefined
[window get addEventListener] ==> function addEventListener() { [native code] }
[window get addEventListener] ==> function addEventListener() { [native code] }
[window get _listeners] ==> [object Object]
[window get _listeners] ==> [object Object]
[window get _listeners] ==> [object Object]
[navigator get cajaVersion] ==> undefined
[navigator get userAgent] ==> Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0
[window get location] ==> [object Location]
[location get href] ==> https://passport.yhd.com/
[ set window.Hex] ==> [object Object]
[ set window.Base64] ==> [object Object]
[ set window.ASN1] ==> function a(h, g, i, k, j) {
this.stream = h;
this.header = g;
this.length = i;
this.tag = k;
this.sub = j
}
第 3 回合
getRandomValues 是定义在 Crypto.prototype 上的方法,传入一个 TypedArray,返回一个随机值填充的 TypedArray
// Crypto.js
Crypto.prototype.getRandomValues = function getRandomValues(array) {
if (!ArrayBuffer.isView(array)) {
throw new TypeError('Argument 1 of Crypto.getRandomValues is not an object of a type that is assignable to one of the allowed types');
};
if (array.byteLength > 65536) {
throw new DOMException('The requested length exceeds 65,536 bytes', 'QuotaExceededError');
};
if (array instanceof Uint32Array) { // 本例测试只看到传入了 Uint32Array,其他类型暂时不处理
for (let i = 0; i
执行看看日志
[navigator get appName] ==> Netscape
[navigator get appName] ==> Netscape
[window get crypto] ==> [object Crypto]
[window get crypto] ==> [object Crypto]
[crypto get getRandomValues] ==> function getRandomValues() { [native code] }
[window get crypto] ==> [object Crypto]
[crypto get getRandomValues] ==> function getRandomValues() { [native code] }
[window get addEventListener] ==> function addEventListener() { [native code] }
[window get addEventListener] ==> function addEventListener() { [native code] }
[window get _listeners] ==> [object Object]
[window get _listeners] ==> [object Object]
[window get _listeners] ==> [object Object]
[navigator get cajaVersion] ==> undefined
[navigator get userAgent] ==> Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0
[window get location] ==> [object Location]
[location get href] ==> https://passport.yhd.com/
[ set window.Hex] ==> [object Object]
[ set window.Base64] ==> [object Object]
[ set window.ASN1] ==> function a(h, g, i, k, j) {
this.stream = h;
this.header = g;
this.length = i;
this.tag = k;
this.sub = j
}
pubkey is not defined
第 4 回合
熟悉 RSA 的老师傅已经看出来了,pubkey 是 RSA 加密公钥 publicKey,一般由服务器下发到客户端
刷新网页搜索 pubkey

pubkey.png (168.87 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传
复制下来补上,执行出结果,总共 4 回合补齐

result.png (141.09 KB, 下载次数: 0)
下载附件
2025-10-1 23:47 上传
yhdLogin.zip
(958.92 KB, 下载次数: 1)
2025-10-1 23:53 上传
点击文件名下载附件
整个源码
下载积分: 吾爱币 -1 CB