Files
reverse-skills-max/vendor-new/xbsReverseSkill/web-js-env-patcher/references/hook-templates.md

235 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 浏览器 Hook 模板
本文件提供授权调试时用于定位加密参数的最小 Hook 模板。原则:只观察、不篡改、不批量请求;命中后用调用栈回到源码确认。
## 使用规则
- 只在用户授权的目标页面中使用。
- Hook 前先记录目标 API、参数名和页面 URL。
- Hook 代码只输出调用栈和关键值,不修改请求内容。
- 命中后尽快移除 Hook,避免影响目标页面逻辑。
- 不用 Hook 绕过登录、验证码、MFA 或风控。
## fetch
```javascript
(() => {
const targetParam = "sign";
const apiKeyword = "/api/";
const rawFetch = window.fetch;
window.fetch = function patchedFetch(input, init) {
const url = typeof input === "string" ? input : (input && input.url) || "";
const body = init && init.body;
if (url.includes(apiKeyword) || String(url).includes(targetParam) || String(body || "").includes(targetParam)) {
console.group("[fetch 命中]", url);
console.log("input =", input);
console.log("init =", init);
console.trace("fetch 调用栈");
console.groupEnd();
debugger;
}
return rawFetch.apply(this, arguments);
};
})();
```
## XMLHttpRequest
```javascript
(() => {
const targetParam = "sign";
const apiKeyword = "/api/";
const rawOpen = XMLHttpRequest.prototype.open;
const rawSend = XMLHttpRequest.prototype.send;
const rawSetHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.open = function patchedOpen(method, url) {
this.__reverse_method = method;
this.__reverse_url = String(url || "");
return rawOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.setRequestHeader = function patchedSetRequestHeader(name, value) {
if (String(name).includes(targetParam) || String(value).includes(targetParam)) {
console.group("[XHR Header 命中]", this.__reverse_url);
console.log(name, value);
console.trace("setRequestHeader 调用栈");
console.groupEnd();
debugger;
}
return rawSetHeader.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function patchedSend(body) {
const url = this.__reverse_url || "";
if (url.includes(apiKeyword) || url.includes(targetParam) || String(body || "").includes(targetParam)) {
console.group("[XHR send 命中]", this.__reverse_method, url);
console.log("body =", body);
console.trace("send 调用栈");
console.groupEnd();
debugger;
}
return rawSend.apply(this, arguments);
};
})();
```
## URLSearchParams / FormData
```javascript
(() => {
const targetParam = "sign";
for (const Ctor of [URLSearchParams, FormData].filter(Boolean)) {
for (const method of ["append", "set"]) {
const raw = Ctor.prototype[method];
if (typeof raw !== "function") continue;
Ctor.prototype[method] = function patchedParamWrite(name, value) {
if (String(name) === targetParam || String(value).includes(targetParam)) {
console.group(`[${Ctor.name}.${method} 命中]`, name);
console.log("value =", value);
console.trace("参数写入调用栈");
console.groupEnd();
debugger;
}
return raw.apply(this, arguments);
};
}
}
})();
```
## Cookie
```javascript
(() => {
const targetParam = "sign";
const proto = Document.prototype;
const desc = Object.getOwnPropertyDescriptor(proto, "cookie");
if (!desc || !desc.set || !desc.get) return;
Object.defineProperty(document, "cookie", {
configurable: true,
enumerable: desc.enumerable,
get() {
const value = desc.get.call(document);
if (String(value).includes(targetParam)) {
console.trace("document.cookie 读取命中");
}
return value;
},
set(value) {
if (String(value).includes(targetParam)) {
console.group("[cookie 写入命中]");
console.log(value);
console.trace("cookie 写入调用栈");
console.groupEnd();
debugger;
}
return desc.set.call(document, value);
}
});
})();
```
## localStorage / sessionStorage
```javascript
(() => {
const keys = ["token", "sign", "device", "fingerprint"];
for (const storageName of ["localStorage", "sessionStorage"]) {
const storage = window[storageName];
if (!storage) continue;
for (const method of ["getItem", "setItem", "removeItem"]) {
const raw = storage[method];
storage[method] = function patchedStorage(key, value) {
if (keys.some(k => String(key).includes(k))) {
console.group(`[${storageName}.${method} 命中]`, key);
console.log("value =", value);
console.trace("Storage 调用栈");
console.groupEnd();
}
return raw.apply(this, arguments);
};
}
}
})();
```
## 时间、随机数和动态代码
```javascript
(() => {
for (const [owner, name] of [[Date, "now"], [Math, "random"]]) {
const raw = owner[name];
owner[name] = function patchedTimeRandom() {
const ret = raw.apply(this, arguments);
console.log(`[${name}]`, ret);
console.trace(`${name} 调用栈`);
return ret;
};
}
const rawGetRandomValues = crypto && crypto.getRandomValues;
if (rawGetRandomValues) {
crypto.getRandomValues = function patchedGetRandomValues(arr) {
const ret = rawGetRandomValues.apply(this, arguments);
console.log("[crypto.getRandomValues]", Array.from(arr));
console.trace("crypto 随机数调用栈");
return ret;
};
}
const rawFunction = Function;
window.Function = new Proxy(rawFunction, {
construct(target, args) {
console.log("[new Function]", args);
console.trace("动态函数调用栈");
debugger;
return Reflect.construct(target, args);
},
apply(target, thisArg, args) {
console.log("[Function]", args);
console.trace("动态函数调用栈");
debugger;
return Reflect.apply(target, thisArg, args);
}
});
})();
```
## WebSocket、事件和 postMessage
```javascript
(() => {
const rawWS = window.WebSocket;
if (rawWS) {
window.WebSocket = new Proxy(rawWS, {
construct(target, args) {
console.log("[WebSocket 创建]", args);
console.trace("WebSocket 调用栈");
return Reflect.construct(target, args);
}
});
}
const rawPostMessage = window.postMessage;
if (rawPostMessage) {
window.postMessage = function patchedPostMessage(message, targetOrigin, transfer) {
console.log("[postMessage]", message, targetOrigin);
console.trace("postMessage 调用栈");
return rawPostMessage.apply(this, arguments);
};
}
})();
```
## 证据输出要求
命中 Hook 后至少记录:
- 命中的 API 或参数名。
- 调用栈截图或文本。
- 所在 JS 文件、行列号、chunk 名。
- 写入前后请求对象。
- 是否可复现。
- 是否需要 sourcemap 或动态 chunk。