mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-06-02 22:20:35 +08:00
149 lines
4.5 KiB
JavaScript
149 lines
4.5 KiB
JavaScript
"use strict";
|
||
|
||
const { PuppeteerExtraPlugin } = require("puppeteer-extra-plugin");
|
||
async function handle(p) {
|
||
await p.evaluateOnNewDocument(() => {
|
||
(() => {
|
||
"use strict";
|
||
/* -------------------------------------------------------
|
||
* 1. 保存原生 Function.prototype.toString
|
||
* ----------------------------------------------------- */
|
||
const nativeFunctionToString = Function.prototype.toString;
|
||
|
||
/* -------------------------------------------------------
|
||
* 2. WeakMap:函数 → 伪原生源码
|
||
* ----------------------------------------------------- */
|
||
const nativeSourceMap = new WeakMap();
|
||
|
||
/* -------------------------------------------------------
|
||
* 3. 注册伪原生源码
|
||
* ----------------------------------------------------- */
|
||
const registerNativeSource = (fn, source) => {
|
||
try {
|
||
nativeSourceMap.set(fn, source);
|
||
} catch (_) {}
|
||
};
|
||
|
||
/* -------------------------------------------------------
|
||
* 4. 劫持 Function.prototype.toString
|
||
* ----------------------------------------------------- */
|
||
Object.defineProperty(Function.prototype, "toString", {
|
||
configurable: true,
|
||
writable: true,
|
||
value: function toString() {
|
||
if (nativeSourceMap.has(this)) {
|
||
return nativeSourceMap.get(this);
|
||
}
|
||
return nativeFunctionToString.call(this);
|
||
},
|
||
});
|
||
|
||
/* -------------------------------------------------------
|
||
* 5. 伪装 Function.prototype.toString 自身
|
||
* ----------------------------------------------------- */
|
||
registerNativeSource(
|
||
Function.prototype.toString,
|
||
nativeFunctionToString.toString(),
|
||
);
|
||
|
||
/* -------------------------------------------------------
|
||
* 6. stealthify:包装函数但保持“原生外观”
|
||
* ----------------------------------------------------- */
|
||
const stealthify = (obj, prop, handler) => {
|
||
const original = obj[prop];
|
||
if (typeof original !== "function") return;
|
||
|
||
const wrapped = function (...args) {
|
||
return handler.call(this, original, args);
|
||
};
|
||
const namePropertyDescriptor = Object.getOwnPropertyDescriptor(
|
||
wrapped,
|
||
"name",
|
||
);
|
||
// 处理函数 name 属性
|
||
Object.defineProperty(wrapped, "name", {
|
||
...namePropertyDescriptor,
|
||
value: prop,
|
||
});
|
||
// 保留 prototype(某些函数有)
|
||
try {
|
||
Object.setPrototypeOf(wrapped, Object.getPrototypeOf(original));
|
||
} catch (_) {}
|
||
|
||
// 注册伪原生源码(直接复用原函数的 native 表现)
|
||
registerNativeSource(wrapped, nativeFunctionToString.call(original));
|
||
|
||
// 用 defineProperty 保持 descriptor 接近原生
|
||
const desc = Object.getOwnPropertyDescriptor(obj, prop);
|
||
Object.defineProperty(obj, prop, {
|
||
...desc,
|
||
value: wrapped,
|
||
});
|
||
};
|
||
|
||
/* -------------------------------------------------------
|
||
* 7. 示例:stealth console.log / debug / info
|
||
* ----------------------------------------------------- */
|
||
const filterConsoleArgs = (args) =>
|
||
args.map((arg) => {
|
||
if (arg && typeof arg === "object") {
|
||
// 防止 getter / Proxy / 大对象触发
|
||
return {};
|
||
}
|
||
return arg;
|
||
});
|
||
|
||
[
|
||
"log",
|
||
"debug",
|
||
"info",
|
||
"warn",
|
||
"error",
|
||
"dir",
|
||
"table",
|
||
"debug",
|
||
].forEach((name) => {
|
||
stealthify(console, name, (original, args) => {
|
||
// ❗不传递原始对象,避免 DevTools / CDP 展开
|
||
return original.apply(console, filterConsoleArgs(args));
|
||
});
|
||
});
|
||
|
||
/* -------------------------------------------------------
|
||
* 8. 防御性补丁(可选但强烈建议)
|
||
* ----------------------------------------------------- */
|
||
|
||
// 防止检测 toString 被替换
|
||
registerNativeSource(
|
||
registerNativeSource,
|
||
"function registerNativeSource() { [native code] }",
|
||
);
|
||
})();
|
||
});
|
||
}
|
||
|
||
class Plugin extends PuppeteerExtraPlugin {
|
||
constructor() {
|
||
super();
|
||
}
|
||
|
||
get name() {
|
||
return "laodeng";
|
||
}
|
||
|
||
async onBrowser(browser) {
|
||
const pages = await browser.pages();
|
||
for (const p of pages) {
|
||
await handle(p);
|
||
}
|
||
}
|
||
|
||
async onPageCreated(p) {
|
||
await handle(p);
|
||
}
|
||
}
|
||
|
||
module.exports = function (pluginConfig) {
|
||
return new Plugin(pluginConfig);
|
||
};
|