This commit is contained in:
dela
2026-02-22 15:05:49 +08:00
parent 5dc86ccfbf
commit 2154a648af
27 changed files with 3740 additions and 722 deletions

View File

@@ -8,15 +8,17 @@
* 使用 hsw.js 在 Node 沙盒中运行(全局污染方式)
*/
const vm = require('vm');
const { readFileSync } = require('fs');
const { join } = require('path');
const msgpack = require('msgpack-lite');
const { createBrowserEnvironment } = require('./sandbox/mocks/index');
const windowMock = require('./sandbox/mocks/window');
const { applySandboxPatches } = require('./sandbox/mocks/index');
const { Logger } = require('./utils/logger');
const logger = new Logger('hcaptcha_solver');
// 保存原始 fetch在全局被 mock 污染之前
// 保存原始 fetch供真实网络请求使用
const realFetch = globalThis.fetch;
// ── 常量 ──────────────────────────────────────────────────────
@@ -82,76 +84,86 @@ class HswBridge {
constructor() {
this.hswFn = null;
this.initialized = false;
this._savedGlobals = {};
this._ctx = null;
}
/**
* 将 mock 注入全局,加载并执行 hsw.js
* 构建 vm 沙盒上下文,将 mock window 注入隔离环境
* @param {object} fingerprint - 指纹覆盖
*/
_buildContext(fingerprint) {
const ctx = Object.create(null);
// 把 windowMock 上所有 key 复制进 ctx浅拷贝
for (const key of Reflect.ownKeys(windowMock)) {
try { ctx[key] = windowMock[key]; } catch (_) {}
}
// vm 必需的自引用
ctx.global = ctx;
ctx.globalThis = ctx;
ctx.window = ctx;
ctx.self = ctx;
// 透传 console调试用
ctx.console = console;
// 保证 Promise / 定时器在 vm 里可用
ctx.Promise = Promise;
ctx.setTimeout = setTimeout;
ctx.clearTimeout = clearTimeout;
ctx.setInterval = setInterval;
ctx.clearInterval = clearInterval;
ctx.queueMicrotask = queueMicrotask;
// 应用指纹覆盖
if (fingerprint.userAgent && ctx.navigator) {
ctx.navigator.userAgent = fingerprint.userAgent;
ctx.navigator.appVersion = fingerprint.userAgent.replace('Mozilla/', '');
}
if (fingerprint.platform && ctx.navigator) {
ctx.navigator.platform = fingerprint.platform;
}
if (fingerprint.host && ctx.location?.ancestorOrigins) {
ctx.location.ancestorOrigins[0] = `https://${fingerprint.host}`;
}
const vmCtx = vm.createContext(ctx);
// Apply escape defense + error stack rewriting AFTER context creation
applySandboxPatches(vmCtx);
return vmCtx;
}
/**
* 在 vm 沙盒中加载并执行 hsw.js
* @param {string} hswPath - hsw.js 文件路径
* @param {object} fingerprint - 指纹覆盖
*/
async init(hswPath, fingerprint = {}) {
if (this.initialized) return;
const env = createBrowserEnvironment(fingerprint);
// 保存原始全局
const keys = ['window', 'document', 'navigator', 'screen', 'location',
'localStorage', 'sessionStorage', 'crypto', 'performance',
'self', 'top', 'parent', 'fetch', 'XMLHttpRequest'];
for (const k of keys) {
if (k in globalThis) this._savedGlobals[k] = globalThis[k];
}
// 注入全局
const force = (obj, prop, val) => {
Object.defineProperty(obj, prop, {
value: val, writable: true, configurable: true, enumerable: true,
});
};
force(globalThis, 'window', env.window);
force(globalThis, 'document', env.document);
force(globalThis, 'navigator', env.navigator);
force(globalThis, 'screen', env.screen);
force(globalThis, 'location', env.location);
force(globalThis, 'localStorage', env.localStorage);
force(globalThis, 'sessionStorage', env.sessionStorage);
force(globalThis, 'crypto', env.crypto);
force(globalThis, 'performance', env.performance);
force(globalThis, 'self', env.window);
force(globalThis, 'top', env.window);
force(globalThis, 'parent', env.window);
// 浏览器 API
globalThis.fetch = env.window.fetch;
globalThis.btoa = env.window.btoa;
globalThis.atob = env.window.atob;
globalThis.setTimeout = env.window.setTimeout;
globalThis.setInterval = env.window.setInterval;
globalThis.clearTimeout = env.window.clearTimeout;
globalThis.clearInterval = env.window.clearInterval;
globalThis.TextEncoder = env.window.TextEncoder;
globalThis.TextDecoder = env.window.TextDecoder;
globalThis.requestAnimationFrame = env.window.requestAnimationFrame;
globalThis.cancelAnimationFrame = env.window.cancelAnimationFrame;
// 加载 hsw.js
const code = readFileSync(hswPath, 'utf-8');
logger.info(`hsw.js 已加载 (${(code.length / 1024).toFixed(1)} KB)`);
const ctx = this._buildContext(fingerprint);
this._ctx = ctx;
const script = new vm.Script(code, { filename: 'hsw.js' });
try {
const fn = new Function(`(function() { ${code} })();`);
fn();
script.runInContext(ctx, { timeout: 10000 });
} catch (err) {
logger.error(`hsw.js 执行失败: ${err.message}`);
throw err;
}
// 查找 hsw 函数
if (typeof globalThis.window?.hsw === 'function') {
this.hswFn = globalThis.window.hsw;
} else if (typeof globalThis.hsw === 'function') {
this.hswFn = globalThis.hsw;
if (typeof ctx.hsw === 'function') {
this.hswFn = ctx.hsw;
} else if (typeof ctx.window?.hsw === 'function') {
this.hswFn = ctx.window.hsw;
}
if (!this.hswFn) {
@@ -159,7 +171,7 @@ class HswBridge {
}
this.initialized = true;
logger.success('Bridge 已就绪');
logger.success('Bridge 已就绪 (vm 沙盒)');
}
/** 计算 PoW n 值: hsw(req_jwt_string) */