@@ -16,53 +16,100 @@ object FingerprintScriptGenerator {
1616 val vendor = preset.vendor
1717 val cores = preset.hardwareConcurrency
1818 val memory = preset.deviceMemory
19- val isStealth = preset == FingerprintPreset .ANONYMOUS_STEALTH
19+ val isMobile = preset.isMobile
20+ val uaPlatform = when {
21+ platform.contains(" Win" ) -> " Windows"
22+ platform.contains(" Mac" ) -> " macOS"
23+ platform.contains(" Chrome" ) -> " Chrome OS"
24+ else -> " Android"
25+ }
2026
2127 return """
2228 (function() {
2329 try {
24- if (window.__fp_injected) return;
25- window.__fp_injected = true;
26-
27- Object.defineProperty(navigator, 'platform', {
28- get: function() { return '$platform '; },
29- configurable: true
30- });
31-
32- Object.defineProperty(navigator, 'vendor', {
33- get: function() { return '$vendor '; },
34- configurable: true
35- });
36-
37- Object.defineProperty(navigator, 'hardwareConcurrency', {
38- get: function() { return $cores ; },
39- configurable: true
40- });
41-
42- if ('deviceMemory' in navigator) {
43- Object.defineProperty(navigator, 'deviceMemory', {
44- get: function() { return $memory ; },
45- configurable: true
46- });
47- }
30+ const targetPlatform = '$platform ';
31+ const targetVendor = '$vendor ';
32+ const targetCores = $cores ;
33+ const targetMemory = $memory ;
34+ const targetMobile = $isMobile ;
35+ const targetUaPlatform = '$uaPlatform ';
36+
37+ // Prototype-level override (clean, native-like, passes Object.hasOwnProperty checks)
38+ if (typeof Navigator !== 'undefined' && Navigator.prototype) {
39+ try {
40+ Object.defineProperty(Navigator.prototype, 'platform', {
41+ get: function() { return targetPlatform; },
42+ configurable: true,
43+ enumerable: true
44+ });
45+ } catch(e) {}
4846
49- Object.defineProperty(navigator, 'webdriver', {
50- get: function() { return false; },
51- configurable: true
52- });
47+ try {
48+ Object.defineProperty(Navigator.prototype, 'vendor', {
49+ get: function() { return targetVendor; },
50+ configurable: true,
51+ enumerable: true
52+ });
53+ } catch(e) {}
5354
54- ${if (isStealth) """
55- if (window.HTMLCanvasElement && HTMLCanvasElement.prototype.toDataURL) {
56- const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
57- HTMLCanvasElement.prototype.toDataURL = function() {
58- return origToDataURL.apply(this, arguments);
59- };
60- }
61- """ else " " }
55+ try {
56+ Object.defineProperty(Navigator.prototype, 'hardwareConcurrency', {
57+ get: function() { return targetCores; },
58+ configurable: true,
59+ enumerable: true
60+ });
61+ } catch(e) {}
6262
63- } catch(e) {
64- console.warn("FP protection notice", e);
65- }
63+ if ('deviceMemory' in Navigator.prototype) {
64+ try {
65+ Object.defineProperty(Navigator.prototype, 'deviceMemory', {
66+ get: function() { return targetMemory; },
67+ configurable: true,
68+ enumerable: true
69+ });
70+ } catch(e) {}
71+ }
72+
73+ // Client Hints synchronization (Crucial for modern Chromium sites)
74+ if (navigator.userAgentData) {
75+ try {
76+ const origUAD = navigator.userAgentData;
77+ const spoofedUAD = {
78+ brands: origUAD.brands || [
79+ { brand: 'Chromium', version: '131' },
80+ { brand: 'Google Chrome', version: '131' },
81+ { brand: 'Not_A Brand', version: '24' }
82+ ],
83+ mobile: targetMobile,
84+ platform: targetUaPlatform,
85+ getHighEntropyValues: function(hints) {
86+ return origUAD.getHighEntropyValues ? origUAD.getHighEntropyValues(hints).then(function(values) {
87+ return Object.assign({}, values, {
88+ platform: targetUaPlatform,
89+ mobile: targetMobile
90+ });
91+ }) : Promise.resolve({
92+ platform: targetUaPlatform,
93+ mobile: targetMobile
94+ });
95+ },
96+ toJSON: function() {
97+ return {
98+ brands: this.brands,
99+ mobile: this.mobile,
100+ platform: this.platform
101+ };
102+ }
103+ };
104+ Object.defineProperty(Navigator.prototype, 'userAgentData', {
105+ get: function() { return spoofedUAD; },
106+ configurable: true,
107+ enumerable: true
108+ });
109+ } catch(e) {}
110+ }
111+ }
112+ } catch(e) { }
66113 })();
67114 """ .trimIndent()
68115 }
@@ -78,7 +125,7 @@ object FingerprintScriptGenerator {
78125 if (window.__feather_bg_play_active) return;
79126 window.__feather_bg_play_active = true;
80127
81- // 1. Spoof Page Visibility API so websites (like YouTube) report visible and focused
128+ // 1. Spoof Page Visibility API so media sites report visible and focused
82129 ['hidden', 'webkitHidden'].forEach(function(prop) {
83130 try {
84131 Object.defineProperty(document, prop, { get: function() { return false; }, configurable: true, enumerable: true });
@@ -98,35 +145,6 @@ object FingerprintScriptGenerator {
98145 Document.prototype.hasFocus = function() { return true; };
99146 } catch(e) {}
100147
101- // 2. Prevent YouTube from auto-pausing on tab change / backgrounding
102- const blockedEvents = ['visibilitychange', 'webkitvisibilitychange', 'pagehide', 'blur', 'focusout', 'freeze'];
103- try {
104- const origAddEventListener = EventTarget.prototype.addEventListener;
105- EventTarget.prototype.addEventListener = function(type, listener, options) {
106- if (typeof type === 'string' && blockedEvents.indexOf(type.toLowerCase()) !== -1) {
107- return;
108- }
109- return origAddEventListener.apply(this, arguments);
110- };
111-
112- blockedEvents.forEach(function(evt) {
113- window.addEventListener(evt, function(e) {
114- if (e && e.stopImmediatePropagation) e.stopImmediatePropagation();
115- if (e && e.stopPropagation) e.stopPropagation();
116- }, true);
117- document.addEventListener(evt, function(e) {
118- if (e && e.stopImmediatePropagation) e.stopImmediatePropagation();
119- if (e && e.stopPropagation) e.stopPropagation();
120- }, true);
121- });
122-
123- try {
124- Object.defineProperty(window, 'onblur', { get: function() { return null; }, set: function(v) {}, configurable: true });
125- Object.defineProperty(window, 'onpagehide', { get: function() { return null; }, set: function(v) {}, configurable: true });
126- Object.defineProperty(document, 'onvisibilitychange', { get: function() { return null; }, set: function(v) {}, configurable: true });
127- } catch(e) {}
128- } catch(e) {}
129-
130148 // Track genuine user interaction vs automatic tab-switch pause
131149 let isUserAction = false;
132150 let userInteractionTimer = null;
0 commit comments