457 lines
14 KiB
JavaScript
457 lines
14 KiB
JavaScript
/**
|
|
* ============================================================================
|
|
* HCaptcha Bypass Module (Modularized Version)
|
|
* ============================================================================
|
|
* 功能:
|
|
* 1. 攔截 hCaptcha 的網絡請求 (Fetch & XHR)
|
|
* 2. 向 hCaptcha HTML 注入自動點擊腳本
|
|
* 3. 模擬用戶行為繞過檢測
|
|
* 4. 支援配置熱更新
|
|
*
|
|
* 使用方式:
|
|
* const bypass = new HCaptchaBypassModule(config);
|
|
* bypass.init();
|
|
* ============================================================================
|
|
*/
|
|
|
|
class HCaptchaBypassModule {
|
|
constructor(config = {}) {
|
|
// 默認配置
|
|
this.config = {
|
|
enabled: true, // 是否啟用繞過
|
|
autoClick: true, // 是否自動點擊
|
|
clickDelay: 400, // 點擊檢測間隔 (ms)
|
|
maxAttempts: 200, // 最大嘗試次數
|
|
debug: false, // 調試模式
|
|
settingsKey: '__HCAPTCHA_BYPASS_SETTINGS__', // 全局配置鍵名
|
|
...config
|
|
};
|
|
|
|
// 運行時狀態
|
|
this.isInterceptorLoaded = false;
|
|
this.isInjectorActive = false;
|
|
this.activeIntervals = [];
|
|
|
|
// 綁定方法上下文
|
|
this.checkSettings = this.checkSettings.bind(this);
|
|
}
|
|
|
|
/**
|
|
* 初始化模塊
|
|
*/
|
|
init() {
|
|
this.log('Initializing HCaptcha Bypass Module...');
|
|
|
|
// 1. 將配置暴露到全局 (供 iframe 內部讀取)
|
|
this.exposeSettings();
|
|
|
|
// 2. 啟動攔截器 (劫持網絡請求)
|
|
this.setupInterceptors();
|
|
|
|
// 3. 啟動配置熱更新
|
|
this.startSettingsWatcher();
|
|
|
|
// 4. 監聽跨域消息
|
|
this.setupMessageListener();
|
|
|
|
this.log('Module initialized successfully');
|
|
}
|
|
|
|
/**
|
|
* 銷毀模塊
|
|
*/
|
|
destroy() {
|
|
this.activeIntervals.forEach(id => clearInterval(id));
|
|
this.activeIntervals = [];
|
|
this.log('Module destroyed');
|
|
}
|
|
|
|
/**
|
|
* 暴露配置到全局
|
|
*/
|
|
exposeSettings() {
|
|
window[this.config.settingsKey] = {
|
|
autoCaptcha: this.config.autoClick,
|
|
enabled: this.config.enabled,
|
|
captchaService: 'hCaptcha'
|
|
};
|
|
|
|
// 同時嘗試暴露到父窗口
|
|
try {
|
|
if (window.parent && window.parent !== window) {
|
|
window.parent[this.config.settingsKey] = window[this.config.settingsKey];
|
|
}
|
|
} catch (e) {
|
|
// 跨域錯誤忽略
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 配置熱更新監聽
|
|
*/
|
|
startSettingsWatcher() {
|
|
const intervalId = setInterval(this.checkSettings, 1000);
|
|
this.activeIntervals.push(intervalId);
|
|
}
|
|
|
|
checkSettings() {
|
|
try {
|
|
const settings = window[this.config.settingsKey];
|
|
if (settings) {
|
|
this.config.enabled = settings.enabled ?? this.config.enabled;
|
|
this.config.autoClick = settings.autoCaptcha ?? this.config.autoClick;
|
|
}
|
|
} catch (e) {
|
|
// Ignore
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 跨域消息監聽
|
|
*/
|
|
setupMessageListener() {
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data?.type === 'HCAPTCHA_BYPASS_UPDATE_SETTINGS') {
|
|
Object.assign(this.config, event.data.settings);
|
|
this.exposeSettings();
|
|
this.log('Settings updated via postMessage:', event.data.settings);
|
|
}
|
|
|
|
if (event.data?.msg === 'hCaptcha Injector Loaded') {
|
|
this.log('Injector script successfully loaded in iframe');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* ========================================================================
|
|
* 攔截器部分:劫持網絡請求並注入腳本
|
|
* ========================================================================
|
|
*/
|
|
setupInterceptors() {
|
|
if (this.isInterceptorLoaded) {
|
|
this.log('Interceptor already loaded, skipping...');
|
|
return;
|
|
}
|
|
|
|
this.isInterceptorLoaded = true;
|
|
|
|
// 目標 URL 匹配
|
|
this.targetUrlPattern = /newassets\.hcaptcha\.com\/captcha\/v1\/[^/]+\/static\/hcaptcha\.html/;
|
|
|
|
// 需要替換的代碼模式
|
|
this.replacementPatterns = [
|
|
/this\.\$radio\.setAttribute\("aria-checked",\s*!1\)/g,
|
|
/this\.\$radio\.setAttribute\("aria-checked",\s*false\)/g,
|
|
/this\.\$radio\.setAttribute\("aria-checked",\s*0\)/g,
|
|
/setAttribute\("aria-checked",!1\)/g
|
|
];
|
|
|
|
// 注入的 Payload (自動點擊腳本)
|
|
this.injectedPayload = this.generateInjectorScript();
|
|
|
|
// Hook Fetch & XHR
|
|
this.hookFetch();
|
|
this.hookXHR();
|
|
|
|
this.log('Network interceptors installed');
|
|
}
|
|
|
|
/**
|
|
* 生成注入腳本
|
|
*/
|
|
generateInjectorScript() {
|
|
const config = this.config;
|
|
|
|
// 這裡用模板字符串生成完整的注入代碼
|
|
// 將之前的 injector 邏輯封裝成字符串
|
|
return `
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ===== 配置讀取 =====
|
|
let isAutoClickEnabled = false;
|
|
let settingsKey = '${config.settingsKey}';
|
|
|
|
function initSettings() {
|
|
try {
|
|
if (window.parent && window.parent[settingsKey]) {
|
|
isAutoClickEnabled = window.parent[settingsKey].autoCaptcha || false;
|
|
} else if (window.top && window.top[settingsKey]) {
|
|
isAutoClickEnabled = window.top[settingsKey].autoCaptcha || false;
|
|
}
|
|
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data?.type === 'HCAPTCHA_BYPASS_UPDATE_SETTINGS') {
|
|
isAutoClickEnabled = event.data.settings?.autoClick || false;
|
|
}
|
|
});
|
|
} catch (err) {
|
|
isAutoClickEnabled = true; // 跨域限制時默認啟用
|
|
}
|
|
}
|
|
|
|
initSettings();
|
|
|
|
// ===== 核心點擊邏輯 =====
|
|
const CHECKBOX_SELECTOR = '#checkbox';
|
|
let attemptCount = 0;
|
|
let isSolved = false;
|
|
let clickInterval = null;
|
|
|
|
function attemptClick() {
|
|
if (attemptCount >= ${config.maxAttempts} || isSolved) {
|
|
if (clickInterval) clearInterval(clickInterval);
|
|
return;
|
|
}
|
|
|
|
const checkbox = document.querySelector(CHECKBOX_SELECTOR);
|
|
if (!checkbox) {
|
|
attemptCount++;
|
|
return;
|
|
}
|
|
|
|
const isChecked = checkbox.getAttribute('aria-checked');
|
|
const isVisible = checkbox.offsetParent !== null;
|
|
|
|
if (isVisible && isChecked === 'false' && isAutoClickEnabled) {
|
|
try {
|
|
checkbox.click();
|
|
console.log('[HCaptcha Bypass] Checkbox clicked');
|
|
|
|
setTimeout(() => {
|
|
const newState = checkbox.getAttribute('aria-checked');
|
|
if (newState === 'true') {
|
|
isSolved = true;
|
|
if (clickInterval) clearInterval(clickInterval);
|
|
|
|
// 通知父窗口
|
|
window.parent.postMessage({
|
|
type: 'HCAPTCHA_BYPASS_SOLVED',
|
|
msg: 'hCaptcha solved successfully'
|
|
}, '*');
|
|
}
|
|
}, 1000);
|
|
} catch (e) {
|
|
console.error('[HCaptcha Bypass] Click failed:', e);
|
|
}
|
|
}
|
|
|
|
attemptCount++;
|
|
}
|
|
|
|
// 啟動點擊循環
|
|
clickInterval = setInterval(attemptClick, ${config.clickDelay});
|
|
|
|
// 冗餘觸發
|
|
setTimeout(attemptClick, 500);
|
|
setTimeout(attemptClick, 1500);
|
|
setTimeout(attemptClick, 3000);
|
|
|
|
// 通知父窗口注入成功
|
|
setTimeout(() => {
|
|
window.parent.postMessage({
|
|
type: 'HCAPTCHA_BYPASS_INJECTOR_LOADED',
|
|
msg: 'hCaptcha Injector Loaded'
|
|
}, '*');
|
|
}, 2000);
|
|
|
|
})();
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Hook Fetch API
|
|
*/
|
|
hookFetch() {
|
|
const originalFetch = window.fetch;
|
|
const self = this;
|
|
|
|
window.fetch = function(...args) {
|
|
const url = typeof args[0] === 'string' ? args[0] : args[0]?.url;
|
|
|
|
// 檢查是否為目標 URL
|
|
const isTarget = url && self.targetUrlPattern.test(url);
|
|
|
|
if (!isTarget || !self.config.enabled) {
|
|
return originalFetch.apply(this, args);
|
|
}
|
|
|
|
self.log('Fetch intercepted:', url);
|
|
|
|
// 攔截並修改響應
|
|
return originalFetch.apply(this, args).then(response => {
|
|
return response.text().then(html => {
|
|
const modifiedHtml = self.injectScript(html);
|
|
return new Response(modifiedHtml, {
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
headers: response.headers
|
|
});
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook XMLHttpRequest
|
|
*/
|
|
hookXHR() {
|
|
const originalOpen = XMLHttpRequest.prototype.open;
|
|
const originalSend = XMLHttpRequest.prototype.send;
|
|
const self = this;
|
|
|
|
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
this._interceptedUrl = url;
|
|
return originalOpen.apply(this, [method, url, ...rest]);
|
|
};
|
|
|
|
XMLHttpRequest.prototype.send = function(...args) {
|
|
const url = this._interceptedUrl;
|
|
const isTarget = url && self.targetUrlPattern.test(url);
|
|
|
|
if (!isTarget || !self.config.enabled) {
|
|
return originalSend.apply(this, args);
|
|
}
|
|
|
|
self.log('XHR intercepted:', url);
|
|
|
|
const originalOnReadyStateChange = this.onreadystatechange;
|
|
|
|
this.onreadystatechange = function() {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
let html = this.responseText;
|
|
if (html) {
|
|
html = self.injectScript(html);
|
|
|
|
Object.defineProperty(this, 'responseText', {
|
|
get: () => html,
|
|
configurable: true
|
|
});
|
|
|
|
Object.defineProperty(this, 'response', {
|
|
get: () => html,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
|
|
if (originalOnReadyStateChange) {
|
|
originalOnReadyStateChange.apply(this, arguments);
|
|
}
|
|
};
|
|
|
|
return originalSend.apply(this, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 注入腳本到 HTML
|
|
*/
|
|
injectScript(html) {
|
|
let modified = html;
|
|
|
|
// 1. 替換檢測邏輯 (將 false 改為 true)
|
|
this.replacementPatterns.forEach(pattern => {
|
|
modified = modified.replace(pattern, match => {
|
|
return match.replace(/!1|false|0/g, '!0'); // !0 === true
|
|
});
|
|
});
|
|
|
|
// 2. 注入自動點擊腳本
|
|
const scriptTag = `<script>${this.injectedPayload}</script>`;
|
|
|
|
if (modified.includes('</body>')) {
|
|
modified = modified.replace('</body>', scriptTag + '</body>');
|
|
} else if (modified.includes('</html>')) {
|
|
modified = modified.replace('</html>', scriptTag + '</html>');
|
|
} else {
|
|
modified += scriptTag;
|
|
}
|
|
|
|
this.log('Script injected into hCaptcha HTML');
|
|
return modified;
|
|
}
|
|
|
|
/**
|
|
* 日誌輸出
|
|
*/
|
|
log(...args) {
|
|
if (this.config.debug) {
|
|
console.log('[HCaptcha Bypass Module]', ...args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新配置
|
|
*/
|
|
updateConfig(newConfig) {
|
|
Object.assign(this.config, newConfig);
|
|
this.exposeSettings();
|
|
this.log('Config updated:', this.config);
|
|
}
|
|
|
|
/**
|
|
* 手動觸發點擊 (從外部調用)
|
|
*/
|
|
triggerClick() {
|
|
const iframes = document.querySelectorAll('iframe[src*="hcaptcha.com"]');
|
|
|
|
iframes.forEach(iframe => {
|
|
try {
|
|
iframe.contentWindow.postMessage({
|
|
type: 'HCAPTCHA_BYPASS_TRIGGER_CLICK'
|
|
}, '*');
|
|
} catch (e) {
|
|
this.log('Cannot access iframe:', e);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ============================================================================
|
|
* 全局 API 暴露
|
|
* ============================================================================
|
|
*/
|
|
window.HCaptchaBypassModule = HCaptchaBypassModule;
|
|
|
|
// 自動初始化選項
|
|
if (typeof HCAPTCHA_BYPASS_AUTO_INIT !== 'undefined' && HCAPTCHA_BYPASS_AUTO_INIT) {
|
|
const instance = new HCaptchaBypassModule({
|
|
enabled: true,
|
|
autoClick: true,
|
|
debug: true
|
|
});
|
|
instance.init();
|
|
window.__hcaptchaBypass = instance;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Extension Compatibility Wrapper
|
|
// ============================================================================
|
|
window.addEventListener('message', (event) => {
|
|
if (event.source !== window) return;
|
|
const message = event.data;
|
|
|
|
if (message && message.type === 'INIT_MODULE' && message.moduleName === 'hcaptchaBypass') {
|
|
if (window.__hcaptchaBypassInstance) return;
|
|
try {
|
|
const instance = new HCaptchaBypassModule(message.config);
|
|
instance.init();
|
|
window.__hcaptchaBypassInstance = instance;
|
|
console.log('[Extension] HCaptcha Bypass initialized');
|
|
} catch (error) {
|
|
console.error('[Extension] HCaptcha Bypass init failed:', error);
|
|
}
|
|
}
|
|
|
|
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__hcaptchaBypassInstance') {
|
|
const instance = window.__hcaptchaBypassInstance;
|
|
if (instance && typeof instance.destroy === 'function') {
|
|
instance.destroy();
|
|
delete window.__hcaptchaBypassInstance;
|
|
}
|
|
}
|
|
});
|