Files
youchat_proxy/you_providers/browserFingerprint.mjs
T

1627 lines
64 KiB
JavaScript

import path from 'path';
import {fileURLToPath} from 'url';
import crypto from 'crypto';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 操作系统
const osList = [
{
name: 'Windows',
versions: ['10.0', '11.0'],
platforms: ['Win32', 'Win64', 'x64']
},
{
name: 'Macintosh',
versions: ['Intel Mac OS X 10_15_7', 'Intel Mac OS X 11_6_0', 'Intel Mac OS X 12_3_1', 'Apple Mac OS X 13_2_1'],
platforms: ['MacIntel']
},
{
name: 'Linux',
versions: ['x86_64', 'i686'],
platforms: ['Linux x86_64', 'Linux i686']
},
{
name: 'Android',
versions: ['11', '12', '13', '14'],
platforms: ['Android']
},
{
name: 'iOS',
versions: ['15_4', '16_2', '17_0'],
platforms: ['iPhone', 'iPad']
}
];
// 浏览器
const browserVersions = {
'chrome': {
name: 'Chrome',
// 主版本.次版本.构建号.补丁号
majorVersions: [120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134],
minorVersions: [0, 1, 2, 3],
buildVersions: [5000, 5500, 6000, 6500, 6700, 6800, 6900, 7000, 7100],
patchVersions: [80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200],
brandName: "Google Chrome",
brandVersion: "1.0.0.0",
fullVersion: "1.0.0.0",
},
'edge': {
name: 'Edge',
majorVersions: [120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134],
minorVersions: [0, 1, 2, 3],
buildVersions: [5000, 5500, 6000, 6500, 6700, 6800, 6900, 7000, 7100],
patchVersions: [80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200],
brandName: "Microsoft Edge",
brandVersion: "1.0.0.0",
fullVersion: "1.0.0.0",
},
'firefox': {
name: 'Firefox',
majorVersions: [120, 121, 122, 123, 124],
minorVersions: [0, 1, 2],
buildVersions: [],
patchVersions: [],
brandName: "Firefox",
brandVersion: "1.0",
fullVersion: "1.0",
},
'safari': {
name: 'Safari',
majorVersions: [15, 16, 17],
minorVersions: [0, 1, 2, 3, 4, 5, 6],
buildVersions: [],
patchVersions: [],
brandName: "Safari",
brandVersion: "1.0.0",
fullVersion: "1.0.0",
},
'opera': {
name: 'Opera',
majorVersions: [96, 97, 98, 99, 100, 101],
minorVersions: [0, 1, 2, 3],
buildVersions: [1000, 2000, 3000, 4000],
patchVersions: [10, 20, 30, 40, 50],
brandName: "Opera",
brandVersion: "1.0.0.0",
fullVersion: "1.0.0.0",
}
};
// WebGL 供应商和渲染器映射表
const gpuInfo = {
'NVIDIA': [
'ANGLE (NVIDIA, NVIDIA GeForce GTX 1650 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 2060 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 2070 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 3050 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 3090 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 4080 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce RTX 4090 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)',
],
'AMD': [
'ANGLE (AMD, AMD Radeon RX 570 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 5500 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 5600 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 5700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 6600 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 6800 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 6900 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 7600 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 7700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 7800 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, AMD Radeon RX 7900 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
],
'Intel': [
'ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) UHD Graphics 730 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) UHD Graphics 750 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) Iris(TM) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) Iris(TM) Plus Graphics 655 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) Arc(TM) A380 Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)',
],
'Apple': [
'Apple M1',
'Apple M1 Pro',
'Apple M1 Max',
'Apple M1 Ultra',
'Apple M2',
'Apple M2 Pro',
'Apple M2 Max',
'Apple M3',
'Apple M3 Pro',
'Apple M3 Max',
],
'Mobile': [
'Mali-G78 MP12',
'Adreno 650',
'Adreno 660',
'Adreno 730',
'Apple GPU (Metal)',
]
};
// 设备名称列表
const deviceNames = [
// Windows
'DESKTOP-', 'LAPTOP-', 'PC-', 'WIN-', 'WORKSTATION-',
// Mac
'MacBook-Pro', 'MacBook-Air', 'iMac-Pro', 'Mac-mini', 'Mac-Studio',
// 通用
'DELL-', 'HP-', 'LENOVO-', 'ASUS-', 'ACER-', 'MSI-', 'ALIENWARE-', 'GIGABYTE-'
];
// 生成随机设备名称
function generateDeviceNameSuffix() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
const length = Math.floor(Math.random() * 6) + 4; // 4-9位
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// MAC地址
const macPrefixes = [
'E8-2A-EA', '00-1A-2B', 'AC-DE-48', 'B8-27-EB', 'DC-A6-32',
'00-50-56', '00-0C-29', '00-05-69', '00-25-90', 'BC-5F-F4',
'48-45-20', '6C-4B-90', '94-E9-79', '5C-F9-38', '64-BC-0C',
'B4-2E-99', '8C-85-90', '34-97-F6', 'A4-83-E7', '78-7B-8A'
];
// 区域语言
const localeSettings = {
'en-US': {
languages: ['en-US', 'en'],
timeZones: ['America/New_York', 'America/Chicago', 'America/Denver', 'America/Los_Angeles']
},
'en-GB': {
languages: ['en-GB', 'en-US', 'en'],
timeZones: ['Europe/London', 'Europe/Dublin']
},
'zh-CN': {
languages: ['zh-CN', 'zh', 'en-US', 'en'],
timeZones: ['Asia/Shanghai', 'Asia/Hong_Kong']
},
'zh-TW': {
languages: ['zh-TW', 'zh', 'en-US', 'en'],
timeZones: ['Asia/Taipei']
},
'ja-JP': {
languages: ['ja-JP', 'ja', 'en-US', 'en'],
timeZones: ['Asia/Tokyo']
},
'ko-KR': {
languages: ['ko-KR', 'ko', 'en-US', 'en'],
timeZones: ['Asia/Seoul']
},
'fr-FR': {
languages: ['fr-FR', 'fr', 'en-US', 'en'],
timeZones: ['Europe/Paris']
},
'de-DE': {
languages: ['de-DE', 'de', 'en-US', 'en'],
timeZones: ['Europe/Berlin']
},
'es-ES': {
languages: ['es-ES', 'es', 'en-US', 'en'],
timeZones: ['Europe/Madrid']
},
'ru-RU': {
languages: ['ru-RU', 'ru', 'en-US', 'en'],
timeZones: ['Europe/Moscow']
},
'pt-BR': {
languages: ['pt-BR', 'pt', 'en-US', 'en'],
timeZones: ['America/Sao_Paulo']
},
'nl-NL': {
languages: ['nl-NL', 'nl', 'en-US', 'en'],
timeZones: ['Europe/Amsterdam']
},
'it-IT': {
languages: ['it-IT', 'it', 'en-US', 'en'],
timeZones: ['Europe/Rome']
},
'pl-PL': {
languages: ['pl-PL', 'pl', 'en-US', 'en'],
timeZones: ['Europe/Warsaw']
},
'tr-TR': {
languages: ['tr-TR', 'tr', 'en-US', 'en'],
timeZones: ['Europe/Istanbul']
}
};
// CPU核心
const computerSpecs = [
{cores: 2, ram: [2, 4]},
{cores: 4, ram: [4, 8, 16]},
{cores: 6, ram: [8, 16, 32]},
{cores: 8, ram: [8, 16, 32, 64]},
{cores: 10, ram: [16, 32, 64]},
{cores: 12, ram: [16, 32, 64, 128]},
{cores: 16, ram: [32, 64, 128]},
{cores: 24, ram: [32, 64, 128]},
{cores: 32, ram: [64, 128, 256]}
];
// 插件
const browserPlugins = {
'chrome': [
{
name: 'Chrome PDF Plugin',
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
mimeTypes: ['application/pdf']
},
{
name: 'Chrome PDF Viewer',
description: '',
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
mimeTypes: ['application/pdf']
},
{
name: 'Native Client',
description: '',
filename: 'internal-nacl-plugin',
mimeTypes: ['application/x-nacl', 'application/x-pnacl']
}
],
'edge': [
{
name: 'Microsoft Edge PDF Plugin',
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
mimeTypes: ['application/pdf']
},
{
name: 'Microsoft Edge PDF Viewer',
description: '',
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
mimeTypes: ['application/pdf']
},
{
name: 'Native Client',
description: '',
filename: 'internal-nacl-plugin',
mimeTypes: ['application/x-nacl', 'application/x-pnacl']
}
],
'firefox': [
{name: 'Firefox PDF Viewer', description: 'PDF Viewer', filename: 'pdf.js', mimeTypes: ['application/pdf']}
],
'safari': [
{
name: 'QuickTime Plugin',
description: 'QuickTime Plug-in',
filename: 'QuickTime Plugin.plugin',
mimeTypes: ['video/quicktime']
},
{
name: 'WebKit built-in PDF',
description: 'PDF Viewer',
filename: 'internal-pdf-viewer',
mimeTypes: ['application/pdf']
}
]
};
/**
* 随机整数
* @param {number} min
* @param {number} max
* @returns {number}
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* @param {Array} array - 选项数组
* @returns {*} - 随机选择的项
*/
function randomChoice(array) {
if (!Array.isArray(array) || array.length === 0) {
return null;
}
return array[Math.floor(Math.random() * array.length)];
}
/**
* @param {number} percentChance - true概率百分比 (0-100)
* @returns {boolean} - 随机布尔值
*/
function randomChance(percentChance) {
return Math.random() * 100 < percentChance;
}
/**
* @returns {string} - 随机种子
*/
function createRandomSeed() {
return crypto.randomBytes(16).toString('hex');
}
/**
* @param {string} seed - 种子字符串
* @param {number} min - 最小值
* @param {number} max - 最大值
* @returns {number} - 伪随机数
*/
function seededRandom(seed, min, max) {
const hash = crypto.createHash('sha256').update(seed).digest('hex');
const decimal = parseInt(hash.substring(0, 8), 16) / 0xffffffff;
return Math.floor(decimal * (max - min + 1)) + min;
}
/**
* 随机MAC地址
* @returns {string}
*/
function generateRandomMAC() {
const prefix = randomChoice(macPrefixes);
const bytes = [];
for (let i = 0; i < 3; i++) {
bytes.push(Math.floor(Math.random() * 256).toString(16).padStart(2, '0').toUpperCase());
}
return `${prefix}-${bytes.join('-')}`;
}
/**
* 详细版本号
* @param {Object} browser - 浏览器
* @returns {string} - 如 "133.0.6834.110"
*/
function generateRealisticVersion(browser) {
if (!browser || !browser.majorVersions) {
return "100.0.0.0"; // 默认值
}
const majorVersion = randomChoice(browser.majorVersions);
const minorVersion = randomChoice(browser.minorVersions || [0]);
// Chrome/Edge 风格: 133.0.6834.110
if (browser.buildVersions && browser.buildVersions.length > 0 && browser.patchVersions && browser.patchVersions.length > 0) {
const buildVersion = randomChoice(browser.buildVersions);
const patchVersion = randomChoice(browser.patchVersions);
return `${majorVersion}.${minorVersion}.${buildVersion}.${patchVersion}`;
}
// Firefox 风格: 123.0.1
else if (browser.minorVersions && browser.minorVersions.length > 0) {
return `${majorVersion}.${minorVersion}`;
}
// 简单: 15.4
else {
return `${majorVersion}.${getRandomInt(0, 9)}`;
}
}
/**
* 随机用户代理
* @param {string} browserType - 浏览器类型
* @returns {string}
*/
function generateRealisticUserAgent(browserType = null) {
let browser;
if (browserType && browserVersions[browserType.toLowerCase()]) {
browser = browserVersions[browserType.toLowerCase()];
} else {
const browserKeys = Object.keys(browserVersions);
browser = browserVersions[randomChoice(browserKeys)];
}
// 系统
const os = randomChoice(osList);
const osVersion = randomChoice(os.versions);
const platform = randomChoice(os.platforms);
// 版本号
const version = generateRealisticVersion(browser);
browser.fullVersion = version;
// 分离版本号
const majorVersionPart = version.split('.')[0];
let userAgent;
if (browser.name === 'Chrome' || browser.name === 'Edge') {
if (os.name === 'Windows') {
userAgent = `Mozilla/5.0 (Windows NT ${osVersion}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
if (browser.name === 'Edge') {
userAgent += ` Edg/${majorVersionPart}.0.${getRandomInt(1000, 2000)}.${getRandomInt(10, 200)}`;
}
} else if (os.name === 'Macintosh') {
userAgent = `Mozilla/5.0 (${os.name}; ${osVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
if (browser.name === 'Edge') {
userAgent += ` Edg/${majorVersionPart}.0.${getRandomInt(1000, 2000)}.${getRandomInt(10, 200)}`;
}
} else if (os.name === 'Linux') {
userAgent = `Mozilla/5.0 (X11; Linux ${osVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
if (browser.name === 'Edge') {
userAgent += ` Edg/${majorVersionPart}.0.${getRandomInt(1000, 2000)}.${getRandomInt(10, 200)}`;
}
} else if (os.name === 'Android') {
userAgent = `Mozilla/5.0 (Linux; Android ${osVersion}; SM-${getRandomString(3, true)}${getRandomInt(10, 99)}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Mobile Safari/537.36`;
if (browser.name === 'Edge') {
userAgent += ` EdgA/${majorVersionPart}.0.${getRandomInt(1000, 2000)}.${getRandomInt(10, 200)}`;
}
}
} else if (browser.name === 'Firefox') {
if (os.name === 'Windows') {
userAgent = `Mozilla/5.0 (Windows NT ${osVersion}; Win64; x64; rv:${majorVersionPart}.0) Gecko/20100101 Firefox/${version}`;
} else if (os.name === 'Macintosh') {
userAgent = `Mozilla/5.0 (Macintosh; ${osVersion}; rv:${majorVersionPart}.0) Gecko/20100101 Firefox/${version}`;
} else if (os.name === 'Linux') {
userAgent = `Mozilla/5.0 (X11; Linux ${osVersion}; rv:${majorVersionPart}.0) Gecko/20100101 Firefox/${version}`;
} else if (os.name === 'Android') {
userAgent = `Mozilla/5.0 (Android ${osVersion}; Mobile; rv:${majorVersionPart}.0) Gecko/20100101 Firefox/${version}`;
}
} else if (browser.name === 'Safari') {
if (os.name === 'Macintosh') {
const webkitVersion = (parseInt(majorVersionPart) + 500) + `.${getRandomInt(1, 36)}.${getRandomInt(1, 15)}`;
userAgent = `Mozilla/5.0 (Macintosh; ${osVersion}) AppleWebKit/${webkitVersion} (KHTML, like Gecko) Version/${version} Safari/${webkitVersion}`;
} else if (os.name === 'iOS') {
const webkitVersion = (parseInt(majorVersionPart) + 500) + `.${getRandomInt(1, 36)}.${getRandomInt(1, 15)}`;
const device = randomChance(70) ? 'iPhone' : 'iPad';
userAgent = `Mozilla/5.0 (${device}; CPU OS ${osVersion.replace(/_/g, '_')} like Mac OS X) AppleWebKit/${webkitVersion} (KHTML, like Gecko) Version/${version} Mobile/15E148 Safari/${webkitVersion}`;
}
} else if (browser.name === 'Opera') {
if (os.name === 'Windows') {
userAgent = `Mozilla/5.0 (Windows NT ${osVersion}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36 OPR/${majorVersionPart}.0.${getRandomInt(2000, 5000)}.${getRandomInt(10, 200)}`;
} else if (os.name === 'Macintosh') {
userAgent = `Mozilla/5.0 (Macintosh; ${osVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36 OPR/${majorVersionPart}.0.${getRandomInt(2000, 5000)}.${getRandomInt(10, 200)}`;
} else if (os.name === 'Linux') {
userAgent = `Mozilla/5.0 (X11; Linux ${osVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36 OPR/${majorVersionPart}.0.${getRandomInt(2000, 5000)}.${getRandomInt(10, 200)}`;
}
}
// 如果没有匹配到任何合适的组合,提供一个默认UA
if (!userAgent) {
userAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`;
}
return userAgent;
}
/**
* 随机字符串
* @param {number} length - 长度
* @param {boolean} upperOnly - 是否仅大写字母
* @returns {string}
*/
function getRandomString(length, upperOnly = false) {
const chars = upperOnly
? 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
/**
* 一致浏览器指纹
* @param {Object} options - 配置选项
* @returns {Object} - 指纹数据
*/
export function generateFingerprint(options = {}) {
const seed = options.seed || createRandomSeed();
let browserType = options.browserType || null;
if (browserType && typeof browserType === 'string') {
browserType = browserType.toLowerCase();
if (!browserVersions[browserType]) {
console.warn(`未支持的浏览器类型: ${browserType},将使用随机浏览器类型`);
browserType = null;
}
}
if (!browserType) {
const browserKeys = Object.keys(browserVersions);
browserType = browserKeys[Math.floor(Math.random() * browserKeys.length)];
}
// 生成用户代理
const userAgent = options.userAgent || generateRealisticUserAgent(browserType);
// 选择地区/语言
const locale = options.locale || randomChoice(Object.keys(localeSettings));
const localeData = localeSettings[locale];
// 计算机规格
const computerSpec = options.computerSpec || randomChoice(computerSpecs);
// 选择GPU信息
const gpuVendor = options.gpuVendor || randomChoice(Object.keys(gpuInfo));
const gpuRenderer = options.gpuRenderer || randomChoice(gpuInfo[gpuVendor] || ['']);
// 设备名称
const deviceNameBase = options.deviceNameBase || randomChoice(deviceNames);
const deviceName = options.deviceName ||
(deviceNameBase.endsWith('-') ?
`${deviceNameBase}${generateDeviceNameSuffix()}` :
deviceNameBase);
let platform = options.platform;
if (!platform) {
if (userAgent.includes('Windows')) {
platform = 'Win32';
} else if (userAgent.includes('Macintosh') || userAgent.includes('Mac OS')) {
platform = 'MacIntel';
} else if (userAgent.includes('Linux')) {
platform = 'Linux x86_64';
} else if (userAgent.includes('Android')) {
platform = 'Android';
} else if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
platform = userAgent.includes('iPad') ? 'iPad' : 'iPhone';
} else {
platform = 'Win32'; // 默认
}
}
// 插件列表
const plugins = options.plugins || (browserPlugins[browserType] || []);
// 创建
return {
seed,
userAgent,
browserType,
platform,
osInfo: determineOsInfo(userAgent),
webRTC: options.webRTC !== undefined ? options.webRTC : false,
timezone: options.timezone || randomChoice(localeData.timeZones || ['UTC']),
geolocation: options.geolocation || 'prompt',
// 语言和区域设置
language: options.language || locale,
languages: options.languages || localeData.languages || [locale, 'en-US'],
// 指纹保护
canvas: options.canvas || 'noise',
webGL: options.webGL || 'noise',
audioContext: options.audioContext || 'noise',
mediaDevices: options.mediaDevices || 'noise',
// 硬件信息
webGLMetadata: {
vendor: `Google Inc. (${gpuVendor})`,
renderer: gpuRenderer,
vendorUnmasked: gpuVendor,
rendererUnmasked: gpuRenderer
},
// 系统资源
cpu: {
cores: Number(options.cpuCores || computerSpec.cores),
architecture: options.cpuArchitecture || 'x86-64'
},
ram: options.ram || randomChoice(computerSpec.ram),
deviceName: deviceName,
macAddress: options.macAddress || generateRandomMAC(),
// 其他设置
doNotTrack: options.doNotTrack !== undefined ? options.doNotTrack : randomChoice([null, '0', '1']),
hardwareAcceleration: options.hardwareAcceleration || 'default',
plugins: plugins,
screenOrientation: options.screenOrientation || 'landscape-primary',
// 版本信息
browserVersion: getBrowserVersionFromUA(userAgent),
// 指纹强度
noiseLevel: options.noiseLevel || 'medium', // low, medium, high
consistencyLevel: options.consistencyLevel || 'high', // low, medium, high
touchSupport: options.touchSupport !== undefined
? options.touchSupport
: (userAgent.includes('Mobile') || userAgent.includes('Android') || platform === 'iPhone' || platform === 'iPad'),
maxTouchPoints: options.maxTouchPoints || (userAgent.includes('Mobile') ? getRandomInt(1, 5) : 0),
pdfViewerEnabled: options.pdfViewerEnabled !== undefined ? options.pdfViewerEnabled : true
};
}
/**
* 从用户代理确定操作系统
* @param {string} userAgent - 用户代理
* @returns {Object} - 操作系统信息
*/
function determineOsInfo(userAgent) {
let name, version, archType;
if (userAgent.includes('Windows')) {
name = 'Windows';
if (userAgent.includes('Windows NT 10.0')) {
version = '10';
} else if (userAgent.includes('Windows NT 11.0')) {
version = '11';
} else {
version = '10'; // 默认Windows 10
}
archType = userAgent.includes('Win64') || userAgent.includes('x64') ? 'x64' : 'x86';
} else if (userAgent.includes('Mac OS X') || userAgent.includes('Macintosh')) {
name = 'Mac OS';
const macOSMatch = userAgent.match(/Mac OS X ([0-9_]+)/) ||
userAgent.match(/Macintosh; Intel Mac OS X ([0-9_]+)/);
version = macOSMatch ? macOSMatch[1].replace(/_/g, '.') : '10.15';
archType = userAgent.includes('Intel') ? 'x64' : 'arm64';
} else if (userAgent.includes('Linux')) {
name = 'Linux';
version = userAgent.match(/Linux ([^;)]+)/) ? userAgent.match(/Linux ([^;)]+)/)[1] : 'x86_64';
archType = userAgent.includes('x86_64') ? 'x64' : 'x86';
} else if (userAgent.includes('Android')) {
name = 'Android';
const androidMatch = userAgent.match(/Android ([0-9.]+)/);
version = androidMatch ? androidMatch[1] : '11';
archType = 'arm64';
} else if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
name = 'iOS';
const iosMatch = userAgent.match(/OS ([0-9_]+)/);
version = iosMatch ? iosMatch[1].replace(/_/g, '.') : '15.0';
archType = 'arm64';
} else {
name = 'Unknown';
version = 'Unknown';
archType = 'Unknown';
}
return {name, version, archType};
}
/**
* 从用户代理提取浏览器版本
* @param {string} userAgent - 用户代理
* @returns {Object} - 浏览器版本
*/
function getBrowserVersionFromUA(userAgent) {
let name, version, fullVersion;
if (userAgent.includes('Firefox/')) {
name = 'Firefox';
const match = userAgent.match(/Firefox\/([0-9.]+)/);
fullVersion = match ? match[1] : '100.0';
} else if (userAgent.includes('Edg/')) {
name = 'Edge';
const match = userAgent.match(/Edg\/([0-9.]+)/);
fullVersion = match ? match[1] : '100.0.0.0';
} else if (userAgent.includes('OPR/') || userAgent.includes('Opera/')) {
name = 'Opera';
const match = userAgent.match(/OPR\/([0-9.]+)/) || userAgent.match(/Opera\/([0-9.]+)/);
fullVersion = match ? match[1] : '100.0.0.0';
} else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome/')) {
name = 'Safari';
const match = userAgent.match(/Version\/([0-9.]+)/);
fullVersion = match ? match[1] : '15.0';
} else if (userAgent.includes('Chrome/')) {
name = 'Chrome';
const match = userAgent.match(/Chrome\/([0-9.]+)/);
fullVersion = match ? match[1] : '100.0.0.0';
} else {
name = 'Unknown';
fullVersion = '1.0.0';
}
version = fullVersion.split('.')[0]; // 主版本号
return {name, version, fullVersion};
}
/**
* 指纹应用到浏览器页面
* @param {Object} page - Puppeteer页面对象
* @param {Object} fingerprint - 指纹对象
* @returns {Promise<boolean>}
*/
export async function applyFingerprint(page, fingerprint) {
try {
// 设置用户代理
await page.setUserAgent(fingerprint.userAgent);
// 设置语言和时区
await page.setExtraHTTPHeaders({
'Accept-Language': fingerprint.languages.join(',')
});
await page.emulateTimezone(fingerprint.timezone);
await page.evaluateOnNewDocument((fp) => {
const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepClone(item);
return arr;
}, []);
}
if (obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepClone(obj[key]);
return newObj;
}, {});
}
};
// 保存原始navigator
const originalNavigator = window.navigator;
const properties = Object.getOwnPropertyDescriptors(window.navigator);
const resultNavigator = {};
// 允许的可写属性
const allowedToWrite = [
'userAgent', 'appVersion', 'platform', 'language', 'languages',
'deviceMemory', 'hardwareConcurrency', 'doNotTrack', 'webdriver',
'maxTouchPoints'
];
// 适当UA模拟
const browserInfo = fp.browserVersion;
for (const key in properties) {
let overrideValue;
switch (key) {
case 'userAgent':
overrideValue = fp.userAgent;
break;
case 'appVersion':
overrideValue = fp.userAgent.replace('Mozilla/', '');
break;
case 'platform':
overrideValue = fp.platform;
break;
case 'language':
overrideValue = fp.language;
break;
case 'languages':
overrideValue = [...fp.languages];
break;
case 'deviceMemory':
overrideValue = fp.ram;
break;
case 'hardwareConcurrency':
overrideValue = Number(fp.cpu.cores);
break;
case 'doNotTrack':
overrideValue = fp.doNotTrack;
break;
case 'webdriver':
overrideValue = false;
break;
case 'maxTouchPoints':
overrideValue = fp.maxTouchPoints || 0;
break;
case 'vendor':
overrideValue = browserInfo.name === 'Chrome' || browserInfo.name === 'Edge' ? 'Google Inc.' : '';
break;
case 'appName':
overrideValue = 'Netscape';
break;
case 'appCodeName':
overrideValue = 'Mozilla';
break;
}
// 如果有覆盖值
if (overrideValue !== undefined) {
Object.defineProperty(resultNavigator, key, {
value: overrideValue,
configurable: false,
enumerable: true,
writable: false
});
} else if (properties[key].configurable) {
// 从原始 navigator 获取
Object.defineProperty(resultNavigator, key, {
get: function () {
try {
return originalNavigator[key];
} catch (e) {
return properties[key].value;
}
},
enumerable: properties[key].enumerable,
configurable: false
});
} else {
// 不可配置的属性,保持原样
if (properties[key].writable) {
resultNavigator[key] = originalNavigator[key];
} else {
Object.defineProperty(resultNavigator, key, {
value: originalNavigator[key],
writable: properties[key].writable,
enumerable: properties[key].enumerable,
configurable: properties[key].configurable
});
}
}
}
// 创建代理以拦截任何新添加的属性
const navigatorProxy = new Proxy(resultNavigator, {
has: (target, key) => key in target || key in originalNavigator,
get: (target, key) => {
if (key in target) {
return target[key];
}
// 使用原始值
return originalNavigator[key];
},
set: (target, key, value) => {
if (allowedToWrite.includes(key)) {
target[key] = value;
return true;
}
return false;
},
getOwnPropertyDescriptor: (target, key) => {
return Object.getOwnPropertyDescriptor(target, key) ||
Object.getOwnPropertyDescriptor(originalNavigator, key);
},
defineProperty: (target, key, descriptor) => {
if (allowedToWrite.includes(key)) {
Object.defineProperty(target, key, descriptor);
return true;
}
return false;
}
});
// 替换 navigator
Object.defineProperty(window, 'navigator', {
value: navigatorProxy,
writable: false,
configurable: false,
enumerable: true
});
if ('userAgentData' in originalNavigator) {
// 创建伪造userAgentData
const brandsList = [];
if (fp.browserType === 'chrome') {
brandsList.push({brand: "Chromium", version: fp.browserVersion.version});
brandsList.push({brand: "Google Chrome", version: fp.browserVersion.version});
brandsList.push({brand: "Not;A=Brand", version: "99.0.0.0"});
} else if (fp.browserType === 'edge') {
brandsList.push({brand: "Microsoft Edge", version: fp.browserVersion.version});
brandsList.push({brand: "Chromium", version: fp.browserVersion.version});
brandsList.push({brand: "Not;A=Brand", version: "99.0.0.0"});
} else if (fp.browserType === 'firefox') {
brandsList.push({brand: "Firefox", version: fp.browserVersion.version});
brandsList.push({brand: "Not;A=Brand", version: "99.0.0.0"});
}
const uaDataValues = {
brands: brandsList,
mobile: fp.userAgent.includes('Mobile'),
platform: fp.osInfo.name
};
const platformVersion = [fp.osInfo.version, 0, 0, 0];
const uaData = {
brands: brandsList,
mobile: fp.userAgent.includes('Mobile'),
platform: fp.osInfo.name,
architecture: fp.cpu.architecture,
bitness: "64",
model: "",
platformVersion: platformVersion.join('.'),
getHighEntropyValues: function (hints) {
return new Promise(resolve => {
const result = {};
if (hints.includes('architecture')) {
result.architecture = fp.cpu.architecture;
}
if (hints.includes('bitness')) {
result.bitness = "64";
}
if (hints.includes('brands')) {
result.brands = deepClone(brandsList);
}
if (hints.includes('mobile')) {
result.mobile = fp.userAgent.includes('Mobile');
}
if (hints.includes('model')) {
result.model = "";
}
if (hints.includes('platform')) {
result.platform = fp.osInfo.name;
}
if (hints.includes('platformVersion')) {
result.platformVersion = platformVersion.join('.');
}
if (hints.includes('uaFullVersion')) {
result.uaFullVersion = fp.browserVersion.fullVersion;
}
if (hints.includes('fullVersionList')) {
result.fullVersionList = deepClone(brandsList);
}
resolve(result);
});
},
toJSON: function () {
return {
brands: this.brands,
mobile: this.mobile,
platform: this.platform
};
}
};
// 添加到 navigator
Object.defineProperty(navigatorProxy, 'userAgentData', {
value: uaData,
writable: false,
enumerable: true,
configurable: false
});
}
if (fp.webRTC === false) {
// 阻止 WebRTC 泄露
const origRTCPeerConnection = window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection;
if (origRTCPeerConnection) {
class CustomRTCPeerConnection extends origRTCPeerConnection {
constructor(configuration) {
// 过滤掉ICE服务器
if (configuration && configuration.iceServers) {
configuration = {
...configuration,
iceServers: []
};
}
super(configuration);
}
createOffer(...args) {
// 拦截createOffer
return new Promise((resolve, reject) => {
super.createOffer(...args)
.then(offer => {
if (offer && offer.sdp) {
offer.sdp = offer.sdp.replace(/IP4 \d+\.\d+\.\d+\.\d+/g, 'IP4 0.0.0.0');
}
resolve(offer);
})
.catch(reject);
});
}
createAnswer(...args) {
return new Promise((resolve, reject) => {
super.createAnswer(...args)
.then(answer => {
if (answer && answer.sdp) {
answer.sdp = answer.sdp.replace(/IP4 \d+\.\d+\.\d+\.\d+/g, 'IP4 0.0.0.0');
}
resolve(answer);
})
.catch(reject);
});
}
}
window.RTCPeerConnection = CustomRTCPeerConnection;
window.webkitRTCPeerConnection = CustomRTCPeerConnection;
window.mozRTCPeerConnection = CustomRTCPeerConnection;
}
// 禁用媒体设备
const safeMediaDevices = {
enumerateDevices: function () {
return Promise.resolve([]);
},
getSupportedConstraints: function () {
return {};
},
getUserMedia: function () {
return Promise.reject(new Error('Permission denied'));
},
getDisplayMedia: function () {
return Promise.reject(new Error('Permission denied'));
}
};
if (originalNavigator.mediaDevices) {
Object.defineProperty(navigatorProxy, 'mediaDevices', {
value: safeMediaDevices,
writable: false,
enumerable: true,
configurable: false
});
}
}
if (fp.canvas === 'noise' || fp.canvas === 'block') {
const originalGetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = function (type, attributes) {
if (fp.canvas === 'block' && (type === '2d' || type.includes('webgl'))) {
return null;
}
const context = originalGetContext.call(this, type, attributes);
if (!context) return null;
if (type === '2d') {
// 2D Canvas指纹保护
const origGetImageData = context.getImageData;
const origPutImageData = context.putImageData;
const origToDataURL = this.toDataURL;
const origToBlob = this.toBlob;
// 添加微小噪声的函数
const addNoise = function (data) {
const noise = Math.floor(Math.random() * 10) / 255;
for (let i = 0; i < data.data.length; i += 4) {
if (data.data[i + 3] > 0) {
if (Math.random() > 0.5) {
data.data[i + 3] -= noise;
} else {
data.data[i + 3] += noise;
}
}
}
return data;
};
context.getImageData = function (sx, sy, sw, sh) {
const imageData = origGetImageData.call(this, sx, sy, sw, sh);
return addNoise(imageData);
};
this.toDataURL = function (...args) {
const dataURL = origToDataURL.apply(this, args);
if (!dataURL) return dataURL;
// URL添加微小噪声 (改变最后几个字符)
const lastCommaIndex = dataURL.lastIndexOf(',');
if (lastCommaIndex !== -1) {
const prefix = dataURL.substring(0, lastCommaIndex + 1);
const data = dataURL.substring(lastCommaIndex + 1);
const noisyChar = String.fromCharCode(
data.charCodeAt(data.length - 2) + Math.round(Math.random() * 2 - 1)
);
return prefix + data.substring(0, data.length - 2) + noisyChar + data.substring(data.length - 1);
}
return dataURL;
};
this.toBlob = function (callback, ...args) {
origToBlob.call(this, (blob) => {
if (!blob) {
callback(blob);
return;
}
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
// 修改dataURL
const dataURL = reader.result;
const lastCommaIndex = dataURL.lastIndexOf(',');
if (lastCommaIndex !== -1) {
const prefix = dataURL.substring(0, lastCommaIndex + 1);
const data = dataURL.substring(lastCommaIndex + 1);
const noisyChar = String.fromCharCode(
data.charCodeAt(data.length - 2) + Math.round(Math.random() * 2 - 1)
);
const newDataURL = prefix + data.substring(0, data.length - 2) +
noisyChar + data.substring(data.length - 1);
const byteString = atob(newDataURL.split(',')[1]);
const mimeString = newDataURL.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
callback(new Blob([ab], {type: mimeString}));
}
};
callback(blob);
}, ...args);
};
} else if (type.includes('webgl') || type.includes('experimental-webgl')) {
// WebGL Canvas指纹保护
const origGetParameter = context.getParameter;
context.getParameter = function (parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) {
return fp.webGLMetadata.vendorUnmasked;
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return fp.webGLMetadata.rendererUnmasked;
}
return origGetParameter.call(this, parameter);
};
}
return context;
};
}
if (fp.audioContext === 'noise') {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) {
const origAudioContext = AudioContext;
// 创建带噪声AudioContext
window.AudioContext = window.webkitAudioContext = function () {
const ctx = new origAudioContext();
const origGetChannelData = ctx.createAnalyser().getFloatFrequencyData;
if (origGetChannelData) {
ctx.createAnalyser().getFloatFrequencyData = function (array) {
origGetChannelData.call(this, array);
// 添加微小噪声
for (let i = 0; i < array.length; i += 50) {
if (array[i]) {
array[i] += (Math.random() * 0.0001) - 0.00005;
}
}
};
}
return ctx;
};
}
}
// 创建自定义插件列表
const mimeTypeArray = [];
const pluginArray = [];
if (fp.plugins && Array.isArray(fp.plugins)) {
fp.plugins.forEach((plugin, pluginIndex) => {
if (!plugin || !plugin.name) return;
// 创建MimeTypes
const mimeTypes = {};
let mimeTypeCount = 0;
if (plugin.mimeTypes && Array.isArray(plugin.mimeTypes)) {
plugin.mimeTypes.forEach((type, index) => {
const mimeType = {
type,
description: plugin.description || '',
suffixes: plugin.name.toLowerCase().replace(/[^a-z0-9]/g, ''),
enabledPlugin: null
};
mimeTypes[mimeTypeCount] = mimeType;
mimeTypes[type] = mimeType;
mimeTypeCount++;
mimeTypeArray.push(mimeType);
});
}
// 创建插件对象
const pluginObj = {
name: plugin.name,
filename: plugin.filename || '',
description: plugin.description || '',
length: mimeTypeCount,
item: function (index) {
return this[index];
},
namedItem: function (name) {
return this[name];
}
};
// 扩展插件对象
for (let i = 0; i < mimeTypeCount; i++) {
pluginObj[i] = mimeTypes[i];
}
// mime设置enabledPlugin
Object.values(mimeTypes).forEach(mime => {
mime.enabledPlugin = pluginObj;
});
pluginArray.push(pluginObj);
});
// 自定义的navigator.plugins
const pluginsObj = {
length: pluginArray.length,
item: function (index) {
return this[index];
},
namedItem: function (name) {
return this[name] || null;
},
refresh: function () {
}
};
for (let i = 0; i < pluginArray.length; i++) {
const plugin = pluginArray[i];
pluginsObj[i] = plugin;
pluginsObj[plugin.name] = plugin;
}
// 自定义navigator.mimeTypes
const mimeTypesObj = {
length: mimeTypeArray.length,
item: function (index) {
return this[index];
},
namedItem: function (name) {
return this[name] || null;
}
};
for (let i = 0; i < mimeTypeArray.length; i++) {
const mimeType = mimeTypeArray[i];
mimeTypesObj[i] = mimeType;
mimeTypesObj[mimeType.type] = mimeType;
}
// plugins和mimeTypes附加到navigator
Object.defineProperty(navigatorProxy, 'plugins', {
value: pluginsObj,
writable: false,
enumerable: true,
configurable: false
});
Object.defineProperty(navigatorProxy, 'mimeTypes', {
value: mimeTypesObj,
writable: false,
enumerable: true,
configurable: false
});
}
// 隐藏自动化
Object.defineProperty(navigatorProxy, 'webdriver', {
get: () => false,
enumerable: true,
configurable: false
});
// 修复Chrome特征
if (window.chrome) {
const chromeObj = {};
const originalChrome = window.chrome;
// 复制原始chrome
for (const key in originalChrome) {
try {
if (key === 'runtime' && originalChrome.runtime) {
// 处理chrome.runtime
const runtimeObj = {};
for (const rKey in originalChrome.runtime) {
try {
runtimeObj[rKey] = originalChrome.runtime[rKey];
} catch (e) {
}
}
chromeObj.runtime = runtimeObj;
} else {
chromeObj[key] = originalChrome[key];
}
} catch (e) {
}
}
// 创建chrome.app
chromeObj.app = {
isInstalled: false,
InstallState: {
DISABLED: 'disabled',
INSTALLED: 'installed',
NOT_INSTALLED: 'not_installed'
},
RunningState: {
CANNOT_RUN: 'cannot_run',
READY_TO_RUN: 'ready_to_run',
RUNNING: 'running'
}
};
// 替换chrome
Object.defineProperty(window, 'chrome', {
value: chromeObj,
writable: false,
enumerable: true,
configurable: false
});
}
// 添加PDF查看器
if (fp.pdfViewerEnabled) {
for (const mimeType of ['application/pdf', 'text/pdf']) {
const pdfMime = {
type: mimeType,
suffixes: 'pdf',
description: 'Portable Document Format'
};
if (navigatorProxy.mimeTypes) {
const mimeTypesObj = navigatorProxy.mimeTypes;
const index = mimeTypesObj.length;
pdfMime.enabledPlugin = navigatorProxy.plugins[0];
mimeTypesObj[index] = pdfMime;
mimeTypesObj[mimeType] = pdfMime;
mimeTypesObj.length++;
}
}
}
// 噪声
if (fp.noiseLevel && (fp.noiseLevel === 'medium' || fp.noiseLevel === 'high')) {
const addNoise = (value, scale) => {
if (typeof value !== 'number') return value;
const noise = (Math.random() - 0.5) * scale;
return value + noise;
};
}
// 清理webdriver
delete window.__nightmare;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
const originalToString = Function.prototype.toString;
Function.prototype.toString = function () {
if (this === Function.prototype.toString) {
return originalToString.call(this);
}
const fnName = this.name;
if (fnName === 'getParameter' || fnName === 'getChannelData' || fnName === 'toDataURL' ||
fnName === 'toBlob' || fnName === 'getImageData') {
return "function " + fnName + "() { [native code] }";
}
return originalToString.call(this);
};
// 创建隐藏的指纹验证
window._fingerprintId = fp.seed;
const event = new CustomEvent('fingerprintApplied', {
detail: {success: true, fingerprintId: fp.seed}
});
document.dispatchEvent(event);
}, fingerprint);
const debugInfo = await page.evaluate((fp) => {
return {
hardwareConcurrency: navigator.hardwareConcurrency,
hardwareConcurrencyType: typeof navigator.hardwareConcurrency,
expectedCores: fp.cpu.cores,
expectedCoresType: typeof fp.cpu.cores
};
}, fingerprint);
// console.log('指纹应用调试信息:', debugInfo);
console.log(`已应用自定义浏览器指纹: ${fingerprint.userAgent}`);
return true;
} catch (error) {
console.error('应用浏览器指纹时出错:', error);
return false;
}
}
/**
* 为特定浏览器创建并应用指纹
* @param {Object} page - Puppeteer
* @param {string|Object} options - 浏览器或完整配置
* @returns {Promise<Object>} - 应用指纹
*/
export async function setupBrowserFingerprint(page, options = {}) {
try {
if (typeof options === 'string') {
options = {browserType: options};
}
// 生成完整的指纹
const fingerprint = generateFingerprint(options);
// 应用指纹
const success = await applyFingerprint(page, fingerprint);
// 验证指纹应用
if (success) {
try {
const appliedUserAgent = await page.evaluate(() => navigator.userAgent);
if (appliedUserAgent !== fingerprint.userAgent) {
console.warn('用户代理未正确应用:', {
expected: fingerprint.userAgent,
applied: appliedUserAgent
});
}
// 验证CPU核心数
// const hardwareConcurrency = await page.evaluate(() => navigator.hardwareConcurrency);
// if (Number(hardwareConcurrency) !== Number(fingerprint.cpu.cores)) {
// console.warn('CPU核心数未正确应用:', {
// expected: fingerprint.cpu.cores,
// applied: hardwareConcurrency,
// expectedType: typeof fingerprint.cpu.cores,
// appliedType: typeof hardwareConcurrency
// });
// }
// 验证WebGL
if (fingerprint.webGL !== 'block') {
const webglVendor = await page.evaluate(() => {
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
return gl ? gl.getParameter(gl.getParameter(37445)) : null;
} catch (e) {
return null;
}
});
if (webglVendor && !webglVendor.includes(fingerprint.webGLMetadata.vendorUnmasked)) {
console.warn('WebGL供应商信息未正确应用');
}
}
console.log('指纹验证成功');
} catch (verifyError) {
console.warn('指纹验证时出错:', verifyError);
}
}
return fingerprint;
} catch (error) {
console.error('设置浏览器指纹时出错:', error);
throw error;
}
}
/**
* 验证页面指纹是否正确应用
* @param {Object} page - Puppeteer
* @param {Object} fingerprint - 指定指纹
* @returns {Promise<boolean>}
*/
export async function verifyFingerprint(page, fingerprint) {
try {
const results = await page.evaluate((fp) => {
const checks = {
userAgent: navigator.userAgent === fp.userAgent,
platform: navigator.platform === fp.platform,
hardwareConcurrency: Number(navigator.hardwareConcurrency) === Number(fp.cpu.cores),
language: navigator.language === fp.language,
deviceMemory: navigator.deviceMemory === fp.ram,
doNotTrack: navigator.doNotTrack === fp.doNotTrack
};
return {
success: Object.values(checks).every(v => v),
details: checks
};
}, fingerprint);
console.log('指纹验证结果:', results);
return results.success;
} catch (error) {
console.error('验证指纹时出错:', error);
return false;
}
}
/**
* 获取随机指纹
* @param {string} browserFamily ('chrome', 'edge', 'firefox', 'safari')
* @returns {Object} - 指纹
*/
export function getRealisticFingerprint(browserFamily = 'chrome') {
// 标准化浏览器
browserFamily = browserFamily.toLowerCase();
// 选择合适操作系统
let osFamily;
const browserType = browserFamily;
if (browserFamily === 'safari') {
osFamily = Math.random() < 0.8 ? 'Macintosh' : 'iOS';
} else {
const osDistribution = {
'chrome': {'Windows': 0.65, 'Macintosh': 0.25, 'Linux': 0.08, 'Android': 0.02},
'edge': {'Windows': 0.80, 'Macintosh': 0.18, 'Linux': 0.02},
'firefox': {'Windows': 0.55, 'Macintosh': 0.25, 'Linux': 0.20}
};
const distribution = osDistribution[browserFamily] || {'Windows': 0.7, 'Macintosh': 0.25, 'Linux': 0.05};
const rand = Math.random();
let cumulative = 0;
for (const [os, probability] of Object.entries(distribution)) {
cumulative += probability;
if (rand <= cumulative) {
osFamily = os;
break;
}
}
}
// 选择规格
let computerSpec;
if (osFamily === 'Windows' || osFamily === 'Macintosh') {
computerSpec = {
cores: [4, 6, 8, 12, 16][Math.floor(Math.random() * 5)],
ram: [8, 16, 32, 64][Math.floor(Math.random() * 4)]
};
} else if (osFamily === 'Linux') {
computerSpec = {
cores: [4, 8, 16, 24, 32][Math.floor(Math.random() * 5)],
ram: [8, 16, 32, 64, 128][Math.floor(Math.random() * 5)]
};
} else {
computerSpec = {
cores: [2, 4, 6, 8][Math.floor(Math.random() * 4)],
ram: [4, 6, 8, 12][Math.floor(Math.random() * 4)]
};
}
// 语言
const commonLanguages = ['en-US', 'en-GB', 'zh-CN', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP', 'ru-RU'];
const locale = randomChoice(commonLanguages);
return {
browserType,
userAgent: null,
osFamily,
computerSpec,
locale,
timezone: null,
webRTC: false,
noiseLevel: 'medium',
consistencyLevel: 'high'
};
}