done all
This commit is contained in:
@@ -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) */
|
||||
|
||||
Reference in New Issue
Block a user