feat : better browser spoofing

This commit is contained in:
martin legrand
2025-05-15 15:13:25 +02:00
parent 637ca0f826
commit 38b1e17628
2 changed files with 83 additions and 184 deletions
+17 -19
View File
@@ -127,21 +127,22 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
user_data_dir = tempfile.mkdtemp()
user_agent = get_random_user_agent()
chrome_options.add_argument(f"--user-data-dir={user_data_dir}")
chrome_options.add_argument("accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
chrome_options.add_argument("--accept-lang=fr-FR,fr;q=0.9")
chrome_options.add_argument("--timezone=Europe/Paris")
chrome_options.add_argument("--use-gl=egl")
chrome_options.add_argument("--enable-webgl")
chrome_options.add_argument("--enable-webgl") # no webgl is red flag for some anti-bot
chrome_options.add_argument("--enable-3d-apis")
chrome_options.add_argument("--use-gpu-driver-bug-workaround")
chrome_options.add_argument("--allow-webgl-developer-extensions")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--mute-audio")
chrome_options.add_argument("--disable-notifications")
chrome_options.add_argument("--autoplay-policy=user-gesture-required")
chrome_options.add_argument("--disable-features=SitePerProcess,IsolateOrigins")
chrome_options.add_argument("--enable-features=NetworkService,NetworkServiceInProcess")
# Essential WebGL arguments
chrome_options.add_argument("--ignore-gpu-blocklist")
chrome_options.add_argument("--enable-webgl")
chrome_options.add_argument("--enable-webgl-developer-extensions")
chrome_options.add_argument("--enable-webgl-draft-extensions")
chrome_options.add_argument("--disable-webgl-anti-fingerprinting")
chrome_options.add_argument("--allow-webgl-developer-extensions")
chrome_options.add_argument("--font-render-hinting=none")
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_argument(f'user-agent={user_agent["ua"]}')
@@ -237,16 +238,13 @@ class Browser:
actions.click().perform()
def human_scroll(self):
scroll_pixels = random.randint(200, 800)
scroll_time = random.uniform(0.5, 2.0)
self.driver.execute_script(f"""
window.scrollBy({{
top: {scroll_pixels},
behavior: 'smooth',
duration: {scroll_time}
}});
""")
time.sleep(scroll_time + random.uniform(0.2, 0.5))
for _ in range(random.randint(1, 3)):
scroll_pixels = random.randint(150, 1200)
self.driver.execute_script(f"window.scrollBy(0, {scroll_pixels});")
time.sleep(random.uniform(0.5, 2.0))
if random.random() < 0.4:
self.driver.execute_script(f"window.scrollBy(0, -{random.randint(50, 300)});")
time.sleep(random.uniform(0.3, 1.0))
def patch_browser_fingerprint(self) -> None:
script = self.load_js("spoofing.js")
@@ -258,6 +256,7 @@ class Browser:
try:
initial_handles = self.driver.window_handles
self.driver.get(url)
time.sleep(random.uniform(0.01, 0.3))
try:
wait = WebDriverWait(self.driver, timeout=10)
wait.until(
@@ -269,6 +268,7 @@ class Browser:
except TimeoutException:
self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'")
#self.apply_web_safety()
time.sleep(random.uniform(0.01, 0.2))
self.human_scroll()
self.logger.log(f"Navigated to: {url}")
return True
@@ -702,8 +702,6 @@ if __name__ == "__main__":
input("press enter to continue")
print("AntiCaptcha / Form Test")
browser.go_to("https://bot.sannysoft.com/")
time.sleep(50)
browser.go_to("https://antoinevastel.com/bots/")
time.sleep(5)
#txt = browser.get_text()
+59 -158
View File
@@ -1,55 +1,35 @@
// Core automation masking
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
window.RTCPeerConnection = undefined;
window.webkitRTCPeerConnection = undefined;
window.mozRTCPeerConnection = undefined;
window.Notification = class Notification {
constructor(title, options = {}) {
this.title = title;
this.options = options;
}
static permission = 'granted';
static requestPermission = () => Promise.resolve('granted');
close() {}
onclick = null;
onerror = null;
onclose = null;
onshow = null;
};
Object.keys(window).forEach((key) => {
if (key.includes("webdriver") || key.includes("selenium") || key.includes("driver")) {
delete window[key];
}
});
// Forceful chrome object spoofing
try {
Object.defineProperty(window, 'chrome', {
writable: true,
configurable: true,
value: {
app: {isInstalled: false},
webstore: {onInstallStageChanged: {}, onDownloadProgress: {}},
runtime: {
PlatformOs: {MAC: 'mac', WIN: 'win', ANDROID: 'android'},
PlatformArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'},
PlatformNaclArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'},
RequestUpdateCheckStatus: {
THROTTLED: 'throttled',
NO_UPDATE: 'no_update',
UPDATE_AVAILABLE: 'update_available'
},
OnInstalledReason: {
INSTALL: 'install',
UPDATE: 'update',
SHARED_MODULE_UPDATE: 'shared_module_update'
},
OnRestartRequiredReason: {
APP_UPDATE: 'app_update',
OS_UPDATE: 'os_update',
PERIODIC: 'periodic'
}
}
}
});
} catch (e) {
console.log("Error in defining window.chrome: ", e);
// Fallback: direct assignment
window.chrome = window.chrome || {};
window.chrome.app = {isInstalled: false};
window.chrome.webstore = {onInstallStageChanged: {}, onDownloadProgress: {}};
}
// Randomize plugins
const pluginsList = [
{type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', filename: 'internal-pdf-viewer', name: 'Chrome PDF Plugin'},
{type: 'application/x-nacl', description: 'Native Client Executable', filename: 'internal-nacl-plugin', name: 'Native Client'},
@@ -59,7 +39,8 @@ Object.defineProperty(navigator, 'plugins', {
get: () => pluginsList.slice(0, Math.floor(Math.random() * pluginsList.length) + 1)
});
// Font spoofing with randomized font list
// Font spoofing
const fontList = ['Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana'];
Object.defineProperty(document, 'fonts', {
value: {
@@ -74,64 +55,53 @@ Object.defineProperty(document, 'fonts', {
});
// Canvas fingerprint spoofing
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function() {
const ctx = this.getContext('2d');
ctx.fillStyle = `rgba(${Math.random() * 10},${Math.random() * 10},${Math.random() * 10},0.01)`;
ctx.fillRect(0, 0, 1, 1);
// Add varied noise to avoid consistent fingerprints
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgba(${Math.random() * 5}, ${Math.random() * 5}, ${Math.random() * 5}, 0.005)`;
ctx.fillRect(Math.random() * this.width, Math.random() * this.height, 1, 1);
}
return originalToDataURL.apply(this, arguments);
};
// Screen resolution spoofing
const resolutions = [[1920, 1080], [1366, 768], [1440, 900]];
const [w, h] = resolutions[Math.floor(Math.random() * resolutions.length)];
const [w, h] = [1920, 1080];
Object.defineProperty(window, 'screen', {
value: {
width: w,
height: h,
availWidth: w,
availHeight: h - 50,
availWidth: w - 20,
availHeight: h - 100,
colorDepth: 24,
pixelDepth: 24
}
});
// Timezone and language spoofing
Object.defineProperty(navigator, 'language', {get: () => 'en-US'});
Intl.DateTimeFormat = function() {
return {resolvedOptions: () => ({timeZone: 'America/New_York'})};
// ===== WebGL Consistency =====
const os = navigator.userAgent.includes('Windows') ? 'Windows' : 'Mac';
const webGLParams = {
'Windows': {
37445: 'Google Inc. (NVIDIA)', // VENDOR
37446: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060)', // RENDERER
36349: 'NVIDIA Corporation', // UNMASKED_VENDOR_WEBGL
37444: 'NVIDIA GeForce RTX 3060', // UNMASKED_RENDERER_WEBGL
35661: 'WebGL 2.0' // VERSION
},
'Mac': {
37445: 'Apple Inc.',
37446: 'Apple M1 Pro',
36349: 'Apple',
37444: 'Apple M1 Pro',
35661: 'WebGL 2.0 (Metal)'
}
};
// AudioContext spoofing
const originalCreate = window.AudioContext || window.webkitAudioContext;
window.AudioContext = window.webkitAudioContext = function() {
const context = new originalCreate();
const dest = context.createAnalyser();
dest.fake = true;
return context;
};
// WebRTC spoofing
Object.defineProperty(navigator, 'mediaDevices', {
get: () => undefined
});
Object.defineProperty(navigator, 'getUserMedia', {
get: () => undefined
});
// web gl spoofing
const getParameter = WebGLRenderingContext.prototype.getParameter;
// replace WebGL parameters
WebGLRenderingContext.prototype.getParameter = function(parameter) {
// Common WebGL parameters
const params = {
37445: 'Intel Open Source Technology Center', // VENDOR
37446: 'Mesa DRI Intel® HD Graphics 4000', // RENDERER
34076: 'WebKit WebGL', // SHADING_LANGUAGE_VERSION
35661: '2.1 INTEL-16.4.5', // VERSION
36349: 'Intel Inc.' // UNMASKED_VENDOR_WEBGL
return webGLParams[os][parameter] || getParameter.call(this, parameter);
};
return params[parameter] || getParameter.call(this, parameter);
};
// Performance API spoofing
if ('performance' in window) {
@@ -145,81 +115,12 @@ if ('performance' in window) {
});
}
// Battery API spoofing
if ('getBattery' in navigator) {
Object.defineProperty(navigator, 'getBattery', {
value: () => Promise.resolve({
level: 0.85,
charging: true,
chargingTime: 1800,
dischargingTime: Infinity,
onchargingchange: null,
onchargingtimechange: null,
ondischargingtimechange: null,
onlevelchange: null
}),
configurable: true
});
}
window.RTCPeerConnection = undefined;
window.webkitRTCPeerConnection = undefined;
window.mozRTCPeerConnection = undefined;
Object.defineProperty(navigator, 'permissions', {
value: {
query: () => Promise.resolve({ state: 'denied' })
}
});
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en'],
});
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (gl) {
// Store original getParameter function
const originalGetParameter = gl.getParameter;
// Override getParameter to spoof values
gl.getParameter = function(parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) return 'Intel Inc.';
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) return 'Intel Iris Pro OpenGL Engine';
// Other common fingerprint points
if (parameter === gl.VERSION) return 'WebGL 2.0 (OpenGL ES 3.0 Intel)';
if (parameter === gl.SHADING_LANGUAGE_VERSION) return 'WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0)';
// Default to original behavior
return originalGetParameter.call(gl, parameter);
};
// Spoof debug renderer info extension
const debugInfo = {
getExtension: function(name) {
if (name === 'WEBGL_debug_renderer_info') {
return {
UNMASKED_VENDOR_WEBGL: 37445,
UNMASKED_RENDERER_WEBGL: 37446,
__proto__: WebGLExtension.prototype
};
}
return null;
},
__proto__: WebGLRenderingContext.prototype
};
// Override getExtension
const originalGetExtension = gl.getExtension;
gl.getExtension = function(name) {
if (name === 'WEBGL_debug_renderer_info') {
return debugInfo;
}
return originalGetExtension.call(gl, name);
};
// Freeze objects to prevent detection
Object.freeze(gl);
Object.freeze(debugInfo);
}
const originalCreate = window.AudioContext || window.webkitAudioContext;
window.AudioContext = window.webkitAudioContext = function() {
const context = new originalCreate();
const analyser = context.createAnalyser();
analyser.fake = true; // Mark as spoofed
// Spoof common methods
analyser.getFloatFrequencyData = () => new Float32Array(1024).fill(Math.random() * -100);
return context;
};