551 lines
18 KiB
JavaScript
551 lines
18 KiB
JavaScript
/**
|
||
* 虚拟身份池
|
||
* 用于生成随机持卡人姓名
|
||
*/
|
||
const FIRST_NAMES = ["John", "Michael", "David", "James", "Robert", "William", "Richard", "Joseph", "Charles", "Thomas", "Christopher", "Daniel", "Matthew", "Anthony", "Mark", "Donald", "Steven", "Paul", "Andrew", "Joshua", "Kenneth", "Kevin", "Brian", "Mary", "Patricia", "Jennifer", "Linda", "Barbara", "Elizabeth", "Susan", "Jessica", "Sarah", "Karen", "Nancy", "Lisa", "Betty", "Margaret", "Sandra"];
|
||
const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee", "Thompson", "White", "Harris", "Sanchez", "Clark", "Ramirez", "Lewis", "Robinson", "Walker", "Young"];
|
||
|
||
/**
|
||
* 默认地址配置
|
||
*/
|
||
const DEFAULT_ADDRESSES = [{
|
||
name: "John Smith",
|
||
firstName: "John",
|
||
lastName: "Smith",
|
||
address1: "69 Adams Street",
|
||
address2: "",
|
||
city: "Brooklyn",
|
||
state: "New York",
|
||
stateCode: "NY",
|
||
postal: "11201",
|
||
countryText: "United States",
|
||
countryValue: "US"
|
||
}, {
|
||
name: "Michael Johnson",
|
||
firstName: "Michael",
|
||
lastName: "Johnson",
|
||
address1: "3511 Carlisle Avenue",
|
||
address2: "",
|
||
city: "Covington",
|
||
state: "Kentucky",
|
||
stateCode: "KY",
|
||
postal: "41015",
|
||
countryText: "United States",
|
||
countryValue: "US"
|
||
}];
|
||
|
||
// ==========================================
|
||
// 算法工具函数 (Luhn Algorithm)
|
||
// ==========================================
|
||
|
||
function randomChoice(array) {
|
||
return array[Math.floor(Math.random() * array.length)];
|
||
}
|
||
|
||
/**
|
||
* 根据卡号前缀识别卡组织类型
|
||
*/
|
||
function getCardType(cardNumber) {
|
||
const patterns = {
|
||
Visa: /^4/,
|
||
Mastercard: /^5[1-5]/,
|
||
"American Express": /^3[47]/,
|
||
Discover: /^6(?:011|5)/,
|
||
JCB: /^35/,
|
||
"Diners Club": /^3(?:0[0-5]|[68])/,
|
||
Maestro: /^(?:5[0678]\d\d|6304|6390|67\d\d)/,
|
||
UnionPay: /^62/
|
||
};
|
||
for (const [type, regex] of Object.entries(patterns)) {
|
||
if (regex.test(cardNumber)) {
|
||
return type;
|
||
}
|
||
}
|
||
return "Unknown";
|
||
}
|
||
|
||
/**
|
||
* 计算 Luhn 校验位 (模10算法)
|
||
* 用于确保生成的卡号在数学上是有效的
|
||
*/
|
||
function calculateLuhnCheckDigit(digits) {
|
||
let sum = 0;
|
||
let shouldDouble = true;
|
||
for (let i = digits.length - 1; i >= 0; i--) {
|
||
let digit = parseInt(digits[i]);
|
||
if (shouldDouble) {
|
||
digit *= 2;
|
||
if (digit > 9) {
|
||
digit -= 9;
|
||
}
|
||
}
|
||
sum += digit;
|
||
shouldDouble = !shouldDouble;
|
||
}
|
||
return (10 - sum % 10) % 10;
|
||
}
|
||
|
||
/**
|
||
* 验证卡号是否符合 Luhn 算法
|
||
*/
|
||
function validateLuhn(number) {
|
||
const digits = number.replace(/\D/g, "");
|
||
let sum = 0;
|
||
let shouldDouble = false;
|
||
for (let i = digits.length - 1; i >= 0; i--) {
|
||
let digit = parseInt(digits[i]);
|
||
if (shouldDouble) {
|
||
digit *= 2;
|
||
if (digit > 9) {
|
||
digit -= 9;
|
||
}
|
||
}
|
||
sum += digit;
|
||
shouldDouble = !shouldDouble;
|
||
}
|
||
return sum % 10 === 0;
|
||
}
|
||
|
||
/**
|
||
* 生成符合校验规则的卡号
|
||
* @param {string} binPattern - BIN 格式 (如 "552461xxxxxxxxxx")
|
||
*/
|
||
function generateValidCardNumber(binPattern) {
|
||
let rawNumber = "";
|
||
// 1. 填充随机数
|
||
for (let i = 0; i < binPattern.length - 1; i++) {
|
||
if (binPattern[i] === "x" || binPattern[i] === "X") {
|
||
rawNumber += Math.floor(Math.random() * 10);
|
||
} else {
|
||
rawNumber += binPattern[i];
|
||
}
|
||
}
|
||
// 2. 计算最后一位校验码
|
||
const checkDigit = calculateLuhnCheckDigit(rawNumber);
|
||
rawNumber += checkDigit;
|
||
return rawNumber;
|
||
}
|
||
|
||
function generateExpiryDate() {
|
||
const now = new Date();
|
||
const currentYear = now.getFullYear();
|
||
const currentMonth = now.getMonth() + 1;
|
||
|
||
// 随机生成 1 到 60 个月后的日期
|
||
const randomMonths = Math.floor(Math.random() * 60) + 1;
|
||
let targetMonth = currentMonth + randomMonths;
|
||
let targetYear = currentYear;
|
||
|
||
while (targetMonth > 12) {
|
||
targetMonth -= 12;
|
||
targetYear += 1;
|
||
}
|
||
|
||
return {
|
||
month: targetMonth.toString().padStart(2, "0"),
|
||
year: targetYear.toString()
|
||
};
|
||
}
|
||
|
||
function generateCVV(length = 3) {
|
||
let cvv = "";
|
||
for (let i = 0; i < length; i++) {
|
||
cvv += Math.floor(Math.random() * 10);
|
||
}
|
||
return cvv;
|
||
}
|
||
|
||
// ==========================================
|
||
// 核心生成逻辑
|
||
// ==========================================
|
||
|
||
/**
|
||
* 本地生成模式:使用内置算法生成卡号
|
||
*/
|
||
function generateCardsLocally(bin, amount = 10) {
|
||
const cards = [];
|
||
const uniqueSet = new Set();
|
||
console.log(`🎲 Generating ${amount} valid cards from BIN: ${bin}`);
|
||
|
||
let attempts = 0;
|
||
const maxAttempts = amount * 10;
|
||
|
||
while (cards.length < amount && attempts < maxAttempts) {
|
||
attempts++;
|
||
const number = generateValidCardNumber(bin);
|
||
|
||
if (uniqueSet.has(number)) continue;
|
||
if (!validateLuhn(number)) {
|
||
console.warn("⚠️ Generated invalid card (should not happen):", number);
|
||
continue;
|
||
}
|
||
|
||
uniqueSet.add(number);
|
||
const expiry = generateExpiryDate();
|
||
const cvv = generateCVV(3);
|
||
const type = getCardType(number);
|
||
|
||
cards.push({
|
||
serial_number: cards.length + 1,
|
||
card_number: number,
|
||
expiry_month: expiry.month,
|
||
expiry_year: expiry.year,
|
||
cvv: cvv,
|
||
card_type: type,
|
||
full_format: `${number}|${expiry.month}|${expiry.year}|${cvv}`,
|
||
luhn_valid: true
|
||
});
|
||
}
|
||
|
||
console.log(`[cardbingenerator] Successfully generated ${cards.length} valid cards`);
|
||
|
||
// 最后的完整性检查
|
||
const invalid = cards.filter(c => !validateLuhn(c.card_number));
|
||
if (invalid.length > 0) {
|
||
console.error(`❌ Found ${invalid.length} invalid cards!`);
|
||
} else {
|
||
console.log("[cardbingenerator] All cards passed Luhn validation");
|
||
}
|
||
|
||
// 统计卡种分布
|
||
const stats = {};
|
||
cards.forEach(c => {
|
||
stats[c.card_type] = (stats[c.card_type] || 0) + 1;
|
||
});
|
||
console.log("📊 Card types:", stats);
|
||
|
||
return cards;
|
||
}
|
||
|
||
/**
|
||
* 简单生成模式:仅生成随机数,不进行 Luhn 校验 (用于某些不需要校验的测试场景)
|
||
*/
|
||
function generateCardsSimple(bin, amount = 10) {
|
||
const cards = [];
|
||
const uniqueSet = new Set();
|
||
console.log(`🎲 Generating ${amount} cards (no validation) from BIN: ${bin}`);
|
||
|
||
for (let i = 0; i < amount; i++) {
|
||
let number = "";
|
||
for (let j = 0; j < bin.length; j++) {
|
||
if (bin[j] === "x" || bin[j] === "X") {
|
||
number += Math.floor(Math.random() * 10);
|
||
} else {
|
||
number += bin[j];
|
||
}
|
||
}
|
||
|
||
if (uniqueSet.has(number)) {
|
||
i--;
|
||
continue;
|
||
}
|
||
uniqueSet.add(number);
|
||
const expiry = generateExpiryDate();
|
||
const cvv = generateCVV(3);
|
||
const type = getCardType(number);
|
||
|
||
cards.push({
|
||
serial_number: i + 1,
|
||
card_number: number,
|
||
expiry_month: expiry.month,
|
||
expiry_year: expiry.year,
|
||
cvv: cvv,
|
||
card_type: type,
|
||
full_format: `${number}|${expiry.month}|${expiry.year}|${cvv}`,
|
||
luhn_valid: false
|
||
});
|
||
}
|
||
console.log(`[cardbingenerator] Generated ${cards.length} cards (simple mode)`);
|
||
return cards;
|
||
}
|
||
|
||
// ==========================================
|
||
// 远程生成逻辑 (AKR-Gen)
|
||
// ==========================================
|
||
|
||
/**
|
||
* 远程生成模式:操控第三方网站 (akr-gen.bigfk.com) 生成卡号
|
||
* 当本地算法不满足需求时使用
|
||
*/
|
||
async function generateCardsFromAKR(bin, onComplete) {
|
||
let tab = null;
|
||
try {
|
||
console.log("[cardbingenerator] Opening AKR-gen tab...");
|
||
// 创建一个不激活的标签页(后台静默打开)
|
||
tab = await chrome.tabs.create({
|
||
url: "https://akr-gen.bigfk.com/",
|
||
active: false
|
||
});
|
||
|
||
console.log("[cardbingenerator] Waiting for page load...");
|
||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||
|
||
console.log("[cardbingenerator] Filling BIN and generating cards...");
|
||
// 注入脚本:自动填入BIN并点击生成
|
||
const fillResult = await chrome.scripting.executeScript({
|
||
target: { tabId: tab.id },
|
||
func: remotePage_fillAndClick,
|
||
args: [bin]
|
||
});
|
||
console.log("Fill result:", fillResult[0]?.result);
|
||
|
||
console.log("⏳ Waiting a moment before checking results...");
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
|
||
console.log("📥 Getting generated cards (will wait up to 10 seconds)...");
|
||
// 注入脚本:抓取结果
|
||
const scrapeResult = await chrome.scripting.executeScript({
|
||
target: { tabId: tab.id },
|
||
func: remotePage_scrapeResults
|
||
});
|
||
|
||
console.log("[cardbingenerator] Closing AKR-gen tab...");
|
||
await chrome.tabs.remove(tab.id);
|
||
tab = null;
|
||
|
||
if (scrapeResult && scrapeResult[0] && scrapeResult[0].result) {
|
||
const parsedCards = parseScrapedCards(scrapeResult[0].result);
|
||
console.log(`[cardbingenerator] Generated ${parsedCards.length} cards`);
|
||
|
||
if (parsedCards.length > 0) {
|
||
const randomAddr = await getRandomAddress();
|
||
// 存入 storage 供 Content Script 使用
|
||
chrome.storage.local.set({
|
||
generatedCards: parsedCards,
|
||
randomData: randomAddr
|
||
});
|
||
onComplete({ success: true, cards: parsedCards });
|
||
} else {
|
||
console.error("❌ No cards generated from AKR");
|
||
onComplete({ success: false, error: "No cards generated from AKR-gen" });
|
||
}
|
||
} else {
|
||
console.error("❌ Failed to retrieve cards from result");
|
||
onComplete({ success: false, error: "Failed to retrieve cards from page" });
|
||
}
|
||
|
||
} catch (err) {
|
||
console.error("❌ Error in generateCardsFromAKR:", err);
|
||
if (tab) {
|
||
try { await chrome.tabs.remove(tab.id); } catch (e) {}
|
||
}
|
||
onComplete({ success: false, error: err.message });
|
||
}
|
||
}
|
||
|
||
// -- 以下函数会被注入到目标页面执行,不能使用外部变量 --
|
||
|
||
function remotePage_fillAndClick(bin) {
|
||
return new Promise(resolve => {
|
||
function waitForElement(selector, retries = 10, delay = 300) {
|
||
return new Promise(res => {
|
||
let count = 0;
|
||
const check = () => {
|
||
const el = document.querySelector(selector) || document.getElementById(selector.replace("#", ""));
|
||
if (el) {
|
||
res(el);
|
||
} else if (count < retries) {
|
||
count++;
|
||
setTimeout(check, delay);
|
||
} else {
|
||
res(null);
|
||
}
|
||
};
|
||
check();
|
||
});
|
||
}
|
||
|
||
waitForElement("bin").then(input => {
|
||
if (input) {
|
||
console.log("[cardbingenerator] Found BIN input, filling with:", bin);
|
||
input.value = bin;
|
||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||
|
||
setTimeout(() => {
|
||
waitForElement("button[type=\"submit\"]").then(btn => {
|
||
if (btn) {
|
||
console.log("[cardbingenerator] Found generate button, clicking...");
|
||
btn.click();
|
||
resolve(true);
|
||
} else {
|
||
console.error("❌ Generate button not found");
|
||
resolve(false);
|
||
}
|
||
});
|
||
}, 500);
|
||
} else {
|
||
console.error("❌ BIN input not found");
|
||
resolve(false);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function remotePage_scrapeResults() {
|
||
return new Promise(resolve => {
|
||
function waitLoop(retries = 20, delay = 500) {
|
||
let count = 0;
|
||
const check = () => {
|
||
const resultArea = document.getElementById("result");
|
||
if (resultArea && resultArea.value.trim()) {
|
||
console.log("[cardbingenerator] Found generated cards:", resultArea.value.split("\n").length, "lines");
|
||
resolve(resultArea.value);
|
||
} else if (count < retries) {
|
||
count++;
|
||
console.log(`[cardbingenerator] Waiting for cards... attempt ${count}/${retries}`);
|
||
setTimeout(check, delay);
|
||
} else {
|
||
console.error("❌ Timeout waiting for cards");
|
||
resolve("");
|
||
}
|
||
};
|
||
check();
|
||
}
|
||
waitLoop();
|
||
});
|
||
}
|
||
|
||
// ----------------------------------------------------
|
||
|
||
function parseScrapedCards(text) {
|
||
if (!text) return [];
|
||
const lines = text.trim().split("\n");
|
||
const cards = [];
|
||
|
||
lines.forEach((line, index) => {
|
||
if (line.trim()) {
|
||
const parts = line.trim().split("|");
|
||
if (parts.length === 4) {
|
||
cards.push({
|
||
serial_number: index + 1,
|
||
card_number: parts[0],
|
||
expiry_month: parts[1],
|
||
expiry_year: parts[2],
|
||
cvv: parts[3],
|
||
full_format: line.trim()
|
||
});
|
||
}
|
||
}
|
||
});
|
||
return cards;
|
||
}
|
||
|
||
/**
|
||
* 辅助:获取随机地址
|
||
*/
|
||
async function getRandomAddress() {
|
||
return new Promise(resolve => {
|
||
chrome.storage.local.get(["customAddresses"], data => {
|
||
const custom = data.customAddresses || [];
|
||
const pool = [...custom, ...DEFAULT_ADDRESSES];
|
||
if (pool.length === 0) {
|
||
resolve(DEFAULT_ADDRESSES[0]);
|
||
} else {
|
||
const selected = randomChoice(pool);
|
||
resolve(selected);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// ==========================================
|
||
// 数据清理逻辑 (Browsing Data API)
|
||
// ==========================================
|
||
|
||
async function clearStripeBrowsingData(sendResponse) {
|
||
try {
|
||
const domains = ["stripe.com", "checkout.stripe.com", "js.stripe.com", "hooks.stripe.com"];
|
||
|
||
// 1. 深度清理 Cookies (针对特定域)
|
||
for (const domain of domains) {
|
||
const cookies = await chrome.cookies.getAll({ domain: domain });
|
||
for (const cookie of cookies) {
|
||
await chrome.cookies.remove({
|
||
url: "https://" + cookie.domain + cookie.path,
|
||
name: cookie.name
|
||
});
|
||
}
|
||
}
|
||
|
||
// 2. 清理浏览器缓存和存储 (针对特定 Origin)
|
||
await chrome.browsingData.remove({
|
||
origins: domains.map(d => "https://" + d)
|
||
}, {
|
||
cache: true,
|
||
cookies: true,
|
||
localStorage: true,
|
||
indexedDB: true,
|
||
serviceWorkers: true,
|
||
cacheStorage: true
|
||
});
|
||
|
||
console.log("[cardbingenerator] Deep clear completed for Stripe domains");
|
||
if (sendResponse) sendResponse({ success: true });
|
||
|
||
} catch (err) {
|
||
console.error("Error in deep clear:", err);
|
||
if (sendResponse) sendResponse({ success: false, error: err.message });
|
||
}
|
||
}
|
||
|
||
// ==========================================
|
||
// 消息监听与路由
|
||
// ==========================================
|
||
|
||
// 安装时初始化默认 BIN
|
||
chrome.runtime.onInstalled.addListener(() => {
|
||
chrome.storage.local.get(["currentBin", "binHistory"], data => {
|
||
if (!data.currentBin) {
|
||
chrome.storage.local.set({
|
||
currentBin: "552461xxxxxxxxxx",
|
||
binHistory: ["552461xxxxxxxxxx"]
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// 主消息处理器
|
||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||
if (request.action === "generateCards") {
|
||
// 路由到生成逻辑
|
||
generateCardsHandler(request.bin, request.useValidation, sendResponse);
|
||
return true; // 保持消息通道打开以进行异步响应
|
||
}
|
||
if (request.action === "clearBrowsingData") {
|
||
// 路由到清理逻辑
|
||
clearStripeBrowsingData(sendResponse);
|
||
return true;
|
||
}
|
||
});
|
||
|
||
async function generateCardsHandler(bin, useValidation = true, sendResponse) {
|
||
try {
|
||
console.log(`[cardbingenerator] Starting card generation... (Luhn: ${useValidation ? "ON" : "OFF"})`);
|
||
|
||
// 根据配置决定是验证 Luhn 还是简单生成
|
||
const cards = useValidation ?
|
||
generateCardsLocally(bin, 10) :
|
||
generateCardsSimple(bin, 10);
|
||
|
||
if (cards.length > 0) {
|
||
const address = await getRandomAddress();
|
||
|
||
// 将结果存入 storage,这样 Content Script (第一部分代码) 就能读到了
|
||
chrome.storage.local.set({
|
||
generatedCards: cards,
|
||
randomData: address
|
||
});
|
||
|
||
console.log(`[cardbingenerator] Generated and saved ${cards.length} cards`);
|
||
sendResponse({ success: true, cards: cards });
|
||
} else {
|
||
console.error("❌ No cards generated");
|
||
sendResponse({ success: false, error: "Failed to generate cards" });
|
||
}
|
||
} catch (err) {
|
||
console.error("❌ Error in generateCardsHandler:", err);
|
||
sendResponse({ success: false, error: err.message });
|
||
}
|
||
}
|