1
2
3
4
5
6import fs from 'fs';
7import path from 'path';
8import { fileURLToPath } from 'url';
9
10const __dirname = path.dirname(fileURLToPath(import.meta.url));
11const MAIN_JS_PATH = path.join(__dirname, '..', 'src', 'main.js');
12
13
14if (!fs.existsSync(MAIN_JS_PATH)) {
15 console.error(`Error: ${MAIN_JS_PATH} not found`);
16 process.exit(1);
17}
18
19
20let mainJsContent = fs.readFileSync(MAIN_JS_PATH, 'utf8');
21
22
23function applyFingerprintFixes(mainJsContent) {
24 console.log('Applying fingerprinting fixes...');
25
26
27 if (!mainJsContent.includes('fingerprint-generator') || !mainJsContent.includes('fingerprint-injector')) {
28 console.log('Adding fingerprinting modules...');
29
30
31 mainJsContent = mainJsContent.replace(
32 'import { fileURLToPath } from \'url\';',
33 'import { fileURLToPath } from \'url\';\nimport { FingerprintGenerator } from \'fingerprint-generator\';\nimport { FingerprintInjector } from \'fingerprint-injector\';'
34 );
35
36
37 const fingerprintGeneratorCode = `
38// Initialize fingerprint generator for more accurate browser emulation
39const fingerprintGenerator = new FingerprintGenerator({
40 browsers: [
41 { name: 'chrome', minVersion: 100, maxVersion: 123 },
42 { name: 'firefox', minVersion: 100, maxVersion: 123 },
43 { name: 'safari', minVersion: 15, maxVersion: 17 }
44 ],
45 devices: ['desktop'],
46 operatingSystems: ['macos', 'windows'],
47 locales: ['en-US', 'en-GB', 'de-DE'],
48 // Use consistent fingerprints per session for less detection
49 cache: true
50});
51
52// Fingerprint injector for applying fingerprints to browser
53const fingerprintInjector = new FingerprintInjector();`;
54
55
56 mainJsContent = mainJsContent.replace(
57 '// Generate new sessions if needed',
58 fingerprintGeneratorCode + '\n\n// Generate new sessions if needed'
59 );
60
61 console.log('Added fingerprint generator and injector');
62 }
63
64
65 if (!mainJsContent.includes('generateFingerprint()')) {
66 console.log('Adding fingerprint generation to sessions...');
67
68
69 const generateFingerprintCode = `
70// Generate consistent fingerprint for a session
71const generateFingerprint = (sessionId, profile) => {
72 // Generate fingerprint based on browser profile or random selection
73 const browserName = profile?.userAgent?.toLowerCase().includes('firefox') ? 'firefox' :
74 profile?.userAgent?.toLowerCase().includes('safari') ? 'safari' : 'chrome';
75
76 const osName = profile?.platform === 'MacIntel' ? 'macos' : 'windows';
77
78 const locale = profile?.locale || 'en-US';
79
80 return fingerprintGenerator.getFingerprint({
81 browserName,
82 browserVersion: parseInt(profile?.userAgent?.match(/Chrome\\/(\\d+)/)
83 || profile?.userAgent?.match(/Firefox\\/(\\d+)/)
84 || profile?.userAgent?.match(/Version\\/(\\d+)/)
85 || [0, Math.floor(Math.random() * 20) + 100])[1],
86 operatingSystem: osName,
87 operatingSystemVersion: osName === 'macos' ? '10.15.7' : '10.0',
88 deviceCategory: 'desktop',
89 locale
90 });
91};`;
92
93
94 mainJsContent = mainJsContent.replace(
95 '// Generate fake browsing history for a more authentic browser profile',
96 generateFingerprintCode + '\n\n// Generate fake browsing history for a more authentic browser profile'
97 );
98
99
100 mainJsContent = mainJsContent.replace(
101 'return {',
102 'const fingerprint = generateFingerprint(`session_${Date.now()}_${i}`, profile);\n return {'
103 );
104
105 mainJsContent = mainJsContent.replace(
106 'id: `session_${Date.now()}_${i}`,',
107 'id: `session_${Date.now()}_${i}`,\n fingerprint,'
108 );
109
110 console.log('Added fingerprint generation to sessions');
111 }
112
113
114 if (!mainJsContent.includes('injectFingerprint')) {
115 console.log('Adding fingerprint injection to navigation...');
116
117
118 const navHooksRegex = /preNavigationHooks: \[\s*\/\/ Hook pour ajouter des délais aléatoires entre requêtes et configurer le navigateur\s*async \(\{ page, request, session \}\) => \{([\s\S]*?)\},\s*\],/;
119 const navHooksMatch = mainJsContent.match(navHooksRegex);
120
121 if (navHooksMatch) {
122
123 const fingerPrintInjectionCode = `
124 // Apply fingerprint from session
125 if (sessionIndex !== -1 && sessions[sessionIndex].fingerprint) {
126 console.log(\`Injecting fingerprint for session \${sessionId}...\`);
127 try {
128 await fingerprintInjector.attachFingerprintToPuppeteer(page, sessions[sessionIndex].fingerprint);
129 console.log('Fingerprint successfully injected');
130 } catch (error) {
131 console.error('Error injecting fingerprint:', error.message);
132 }
133 }`;
134
135
136 mainJsContent = mainJsContent.replace(
137 'await page.setExtraHTTPHeaders(headers);',
138 'await page.setExtraHTTPHeaders(headers);\n' + fingerPrintInjectionCode
139 );
140
141 console.log('Added fingerprint injection to navigation hooks');
142 } else {
143 console.log('Warning: Could not find preNavigationHooks section');
144 }
145 }
146
147 return mainJsContent;
148}
149
150
151function enhanceHeaderOrdering(mainJsContent) {
152 console.log('Enhancing HTTP header ordering...');
153
154
155 if (mainJsContent.includes('naturalistic header ordering')) {
156 console.log('HTTP header enhancements already exist, skipping...');
157 return mainJsContent;
158 }
159
160
161 const headerOrderRegex = /const headerOrder = \[([\s\S]*?)\];/;
162 const headerOrderMatch = mainJsContent.match(headerOrderRegex);
163
164 if (headerOrderMatch) {
165
166 const improvedHeaderOrder = `const headerOrder = [
167 // More naturalistic header ordering based on TCPDump analysis
168 // Primary headers always come first in real browsers
169 'Host',
170 'Connection',
171 'Cache-Control',
172 'sec-ch-ua',
173 'sec-ch-ua-mobile',
174 'sec-ch-ua-platform',
175 'Upgrade-Insecure-Requests',
176 'User-Agent',
177 'Accept',
178 'Accept-Encoding',
179 'Accept-Language',
180 // Secondary headers might appear in this order
181 'Sec-Fetch-Site',
182 'Sec-Fetch-Mode',
183 'Sec-Fetch-User',
184 'Sec-Fetch-Dest',
185 'Referer',
186 'Cookie',
187 'DNT'
188 ].sort(() => {
189 // Maintain primary header ordering (first 10) but randomize others
190 if (Math.random() > 0.8) {
191 return Math.random() - 0.5;
192 }
193 return 0;
194 });`;
195
196 mainJsContent = mainJsContent.replace(headerOrderRegex, improvedHeaderOrder);
197
198 console.log('Enhanced header ordering pattern');
199 } else {
200 console.log('Warning: Could not find header ordering code to enhance');
201 }
202
203 return mainJsContent;
204}
205
206
207function enhanceBrowserFingerprinting(mainJsContent) {
208 console.log('Enhancing browser fingerprinting protection...');
209
210
211 if (mainJsContent.includes('enhanced fingerprinting protection')) {
212 console.log('Enhanced fingerprinting protection already exists, skipping...');
213 return mainJsContent;
214 }
215
216
217 const fingerprintRegex = /await page\.evaluateOnNewDocument\(\(\) => \{([\s\S]*?)\}\);/;
218 const fingerprintMatch = mainJsContent.match(fingerprintRegex);
219
220 if (fingerprintMatch) {
221
222 const enhancedFingerprinting = `await page.evaluateOnNewDocument(() => {
223 // Enhanced fingerprinting protection based on TCPDump analysis
224
225 // Hide automation fingerprints
226 delete Object.getPrototypeOf(navigator).webdriver;
227
228 // Override platform info with more accurate values
229 Object.defineProperty(navigator, 'platform', {
230 get: () => 'MacIntel',
231 });
232
233 // Override hardware concurrency
234 Object.defineProperty(navigator, 'hardwareConcurrency', {
235 get: () => 8,
236 });
237
238 // Override device memory
239 Object.defineProperty(navigator, 'deviceMemory', {
240 get: () => 8,
241 });
242
243 // Improve Chrome properties emulation
244 if (typeof window !== 'undefined') {
245 window.chrome = {
246 app: {
247 isInstalled: false,
248 InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' },
249 RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' }
250 },
251 runtime: {
252 OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', SHARED_MODULE_UPDATE: 'shared_module_update', UPDATE: 'update' },
253 OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' },
254 PlatformArch: { ARM: 'arm', ARM64: 'arm64', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },
255 PlatformNaclArch: { ARM: 'arm', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },
256 PlatformOs: { ANDROID: 'android', CROS: 'cros', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' },
257 RequestUpdateCheckStatus: { NO_UPDATE: 'no_update', THROTTLED: 'throttled', UPDATE_AVAILABLE: 'update_available' }
258 }
259 };
260 }
261
262 // Fix permissions behavior
263 const originalQuery = window.navigator.permissions?.query;
264 if (originalQuery) {
265 window.navigator.permissions.query = (parameters) => {
266 if (parameters.name === 'notifications') {
267 return Promise.resolve({ state: Notification.permission });
268 }
269 if (parameters.name === 'clipboard-read' || parameters.name === 'clipboard-write') {
270 return Promise.resolve({ state: 'prompt' });
271 }
272 return originalQuery(parameters);
273 };
274 }
275
276 // Fix WebGL fingerprinting - more accurate values from real browsers
277 if (window.WebGLRenderingContext) {
278 const getParameter = WebGLRenderingContext.prototype.getParameter;
279 WebGLRenderingContext.prototype.getParameter = function(parameter) {
280 // UNMASKED_VENDOR_WEBGL
281 if (parameter === 37445) {
282 return 'Intel Inc.';
283 }
284 // UNMASKED_RENDERER_WEBGL
285 if (parameter === 37446) {
286 return 'Intel Iris Pro OpenGL Engine';
287 }
288 return getParameter.call(this, parameter);
289 };
290 }
291
292 // Fix canvas fingerprinting - add subtle noise
293 const originalGetContext = HTMLCanvasElement.prototype.getContext;
294 HTMLCanvasElement.prototype.getContext = function(type, attributes) {
295 const context = originalGetContext.call(this, type, attributes);
296 if (context && type === '2d') {
297 const originalFillText = context.fillText;
298 context.fillText = function() {
299 // Add subtle variations to text rendering
300 const args = arguments;
301 if (Math.random() < 0.2 && args[0]) {
302 args[0] = args[0].split('').map(c => Math.random() < 0.05 ? c + String.fromCharCode(8203) : c).join('');
303 }
304 return originalFillText.apply(this, args);
305 };
306
307 // Affect toDataURL to add subtle noise
308 const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
309 HTMLCanvasElement.prototype.toDataURL = function() {
310 // Only add noise for likely fingerprinting scenarios
311 if (this.width === 16 && this.height === 16 ||
312 this.width <= 2 && this.height <= 2 ||
313 this.width <= 200 && this.height <= 50) {
314
315 const context = this.getContext('2d');
316 if (context) {
317 // Add minimal noise that's invisible to humans
318 const imageData = context.getImageData(0, 0, this.width, this.height);
319 const data = imageData.data;
320
321 // Only modify 1-2 pixels with minor variations
322 const pixelsToModify = Math.max(1, Math.floor(this.width * this.height * 0.01));
323 for (let i = 0; i < pixelsToModify; i++) {
324 const pixelIndex = Math.floor(Math.random() * data.length / 4) * 4;
325 // Only make very minor adjustments to color values
326 data[pixelIndex] = Math.min(255, Math.max(0, data[pixelIndex] + (Math.random() < 0.5 ? -1 : 1)));
327 data[pixelIndex + 1] = Math.min(255, Math.max(0, data[pixelIndex + 1] + (Math.random() < 0.5 ? -1 : 1)));
328 data[pixelIndex + 2] = Math.min(255, Math.max(0, data[pixelIndex + 2] + (Math.random() < 0.5 ? -1 : 1)));
329 }
330
331 context.putImageData(imageData, 0, 0);
332 }
333 }
334 return originalToDataURL.apply(this, arguments);
335 };
336 }
337 return context;
338 };
339
340 // Fix audio fingerprinting
341 const audioContext = window.AudioContext || window.webkitAudioContext;
342 if (audioContext) {
343 const originalGetChannelData = AudioBuffer.prototype.getChannelData;
344 AudioBuffer.prototype.getChannelData = function() {
345 const channelData = originalGetChannelData.apply(this, arguments);
346 // Only add noise for very short buffers (likely fingerprinting)
347 if (this.length < 100) {
348 const noise = 0.0001; // Very subtle noise
349 // Only modify a few random samples
350 const samplesToModify = Math.max(1, Math.floor(channelData.length * 0.001));
351 for (let i = 0; i < samplesToModify; i++) {
352 const index = Math.floor(Math.random() * channelData.length);
353 channelData[index] += (Math.random() * 2 - 1) * noise;
354 }
355 }
356 return channelData;
357 };
358 }
359 });`;
360
361 mainJsContent = mainJsContent.replace(fingerprintRegex, enhancedFingerprinting);
362
363 console.log('Enhanced browser fingerprinting protection');
364 } else {
365 console.log('Warning: Could not find fingerprinting code to enhance');
366 }
367
368 return mainJsContent;
369}
370
371
372let updatedContent = mainJsContent;
373updatedContent = applyFingerprintFixes(updatedContent);
374updatedContent = enhanceHeaderOrdering(updatedContent);
375updatedContent = enhanceBrowserFingerprinting(updatedContent);
376
377
378if (updatedContent !== mainJsContent) {
379
380 const backupPath = `${MAIN_JS_PATH}.bak.${Date.now()}`;
381 fs.writeFileSync(backupPath, mainJsContent);
382 console.log(`Original file backed up to: ${backupPath}`);
383
384
385 fs.writeFileSync(MAIN_JS_PATH, updatedContent);
386 console.log(`Successfully updated ${MAIN_JS_PATH} with fingerprinting fixes`);
387} else {
388 console.log('No changes were made to the file');
389}
390
391console.log('\nNext steps:');
392console.log('1. Run the capture_traffic.sh script to collect network data');
393console.log('2. Use the data to further refine the fingerprinting in this script');
394console.log('3. Re-run this script to apply improved fingerprinting settings');