- Chrome 148 से V8 का
Math.tanhबिल्ट-इन fdlibm के बजाय host केstd::tanhको call करने लगा है, जिससे एक ही input पर भी Linux के glibc, macOS केlibsystem_mऔर Windows के UCRT में अलग-अलग last bit लौटते हैं Math.tanh(0.8)Linux पर0.6640367702678491, macOS पर0.664036770267849, और Windows पर0.6640367702678489होता है, इसलिए एक ही call से तीनों OS अलग किए जा सकते हैं; अगर result User-Agent द्वारा बताए गए OS से अलग हो, तो spoofing पकड़ी जाती है- हर engine में leakage path अलग है: V8 के
Math.*में केवलtanhhost math library का इस्तेमाल करता है, लेकिन Blink की पूरी CSS trigonometric functions और Web Audio की कुछ operations भी OS-specific libraries से गुजरती हैं - values को मनमाने ढंग से बदलने पर वे किसी भी असली OS से match नहीं करतीं और determinism भी टूटता है; इसलिए target library के coefficients, tables, range reduction और FMA behavior को bit-by-bit reproduce करना होगा या original UCRT code को सीधे map करना होगा
- Scrapfly हर release में 871,000 inputs को असली Mac और Chrome से मिलाकर
Math.tanhऔर 7 CSS trigonometric functions की bit match verify करता है, और सिर्फ accuracy ही नहीं बल्कि architecture differences और execution time को भी असली browser के स्तर तक match करता है
Math.tanh से सामने आने वाला OS
Math.tanh(0.8)का result host math library के हिसाब से बदलता है- Linux Chrome का glibc:
0.6640367702678491 - macOS Chrome का
libsystem_m:0.664036770267849 - Windows Chrome का UCRT:
0.6640367702678489
- Linux Chrome का glibc:
- Apple और glibc पूरे input के लगभग एक-चौथाई हिस्से में आम तौर पर 1 ULP का अंतर दिखाते हैं, और Windows UCRT कुछ प्रतिशत inputs में दोनों libraries से अलग होता है
- ULP (unit in the last place) किसी खास magnitude पर represent किए जा सकने वाले लगातार floating-point numbers के बीच की दूरी है, और 1 ULP वह न्यूनतम अंतर है जिसे
doubleदिखा सकता है
- ULP (unit in the last place) किसी खास magnitude पर represent किए जा सकने वाले लगातार floating-point numbers के बीच की दूरी है, और 1 ULP वह न्यूनतम अंतर है जिसे
- असली Chrome 150 को Linux, Apple Silicon आधारित macOS 26, और Windows 11 पर DevTools Protocol से measure करने पर input के अनुसार classification power बदलती है
tanh(0.5)तीनों OS पर0.46211715726000974के समान है, इसलिए detection में इस्तेमाल नहीं किया जा सकताtanh(0.7)में केवल Linux 1 ULP अलग हैtanh(0.8)में तीनों OS अलग हैं और पूरी range 2 ULP हैtanh(0.9)में केवल Windows 1 ULP अलग है
- लगभग तीन-चौथाई inputs में तीनों OS एक जैसा result देते हैं, लेकिन एक सही input से OS-specific signature मिल सकता है
- अगर कोई macOS होने का दावा करते हुए Linux के math bits लौटाता है, तो
Math.tanhका result User-Agent से contradict करता है
Chrome 148 में आया बदलाव
- Chrome 147 तक V8
Math.tanhcalculate करने के लिए portable math implementation fdlibm का port built-in रखता था, इसलिए सभी OS पर वही bits लौटते थे - V8 commit
c1486295ae5ने built-in implementation को platform केstd::tanhसे replace कर दिया- यह बदलाव पहली बार V8 14.8.57 और Chrome 148 में शामिल हुआ
- Chrome 148, 149 और 150 host
libmके differences expose करते हैं, लेकिन Chrome 147 या उससे नीचे इस path से OS leak नहीं करते
- IEEE 754
doubleके storage format को define करता है, लेकिन यह require नहीं करता किsin,cos,tanh,expजैसे transcendental functions हमेशा correctly rounded हों - हर OS की math library (libm) performance और ULP error के बीच trade-off करती है और अलग-अलग minimax approximation polynomial coefficients, lookup tables और range reduction constants इस्तेमाल करती है
- Linux में glibc
- macOS में Apple
libsystem_m - Windows में UCRT का
ucrtbase.dll
- Detector को math operation itself analyze करने की जरूरत नहीं; वह असली Chrome के per-input result table से values compare कर सकता है
Reproduction को कठिन बनाने वाले चार pitfalls
-
V8 के केवल कुछ functions leak होते हैं
- V8 ज्यादातर math implementations को statically link करता है, इसलिए OS से independent होकर वही result देता है
Math.exp,Math.pow,Math.atanआदि built-in llvm-libc implementation इस्तेमाल करते हैंMath.sinऔरMath.cosglibc से निकले built-indbl-64routines इस्तेमाल करते हैं- Chrome 148 के बाद platform
std::tanhइस्तेमाल करने वालाMath.tanhहीMath.*में OS leak करता है - जो functions leak नहीं करते उन्हें भी target OS जैसा spoof करने पर V8 की actual call structure से mismatch होगा, और यह asymmetry कि सिर्फ
tanhअलग है, खुद भी check की जा सकती है
-
JavaScript और CSS अलग paths इस्तेमाल करते हैं
- CSS के
sin(),cos(),atan2()JavaScript केMath.sinके साथ code share नहीं करते - Blink layout engine angle को degrees में reduce करने के बाद reduced value पर platform
std::sinआदि call करता है - यह direct radian input calculate करने के result से अलग होता है, और सभी 7 CSS trigonometric functions host
libmके जरिए OS leak करते हैं - bit-level reproduction में सिर्फ final math function ही नहीं, बल्कि degree-based range reduction और radian-degree conversion process भी शामिल करना होगा
- CSS के
-
macOS के भीतर भी दो अलग libraries हैं
- Apple Silicon में scalar
libsystem_mऔर Accelerate की vector routinesvvsin,vvtanhसाथ मौजूद हैं, और दोनों implementations समान नहीं हैं - 10 लाख inputs में function के आधार पर 10–89% results अलग थे
cos(0)scalar implementation में बिल्कुल1.0है- Accelerate में यह
0.9999999999999999लौटाता है - असली Mac के Chrome को debugging protocol से measure करके call site के हिसाब से library अलग की जाती है
Math.tanh, CSS trigonometric functions और audio compressor के per-sample transcendental functions scalarlibsystem_mइस्तेमाल करते हैं- Mac के Web Audio DSP, FFT, vector math और biquad filters Accelerate इस्तेमाल करते हैं
- संबंधित Chromium paths में
fft_frame_mac.cc,vector_math_mac.h,biquad.ccऔरBUILDFLAG(IS_MAC)हैं - call site से match न करने वाली Apple library चुनने पर ज्यादातर inputs में 1 ULP mismatch हो सकता है
- Apple Silicon में scalar
-
CPU architecture भी result में दखल देता है
- ARM और x86 में fused multiply-add (FMA) और NaN sign propagation में differences होते हैं
- mathematical procedure सही होने पर भी अगर compiler केवल एक architecture पर multiply और add को fuse करे, तो result bits बदल जाते हैं
Engine और feature के हिसाब से leakage paths
- JavaScript का V8
Math.*लगभग पूरी तरह built-in implementation इस्तेमाल करता है, और hostlibmसे जुड़ने वाला point केवलMath.tanhहैsin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powV8 built-in implementation इस्तेमाल करते हैंsqrt,abs, arithmetic operations hardware operations हैं
- CSS
calc()के math functions में Blink platform library को सीधे call करता हैsin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powhostlibmइस्तेमाल करते हैं- CSS में corresponding
tanhpath नहीं है
- Web Audio call site के अनुसार कई implementations मिलाकर इस्तेमाल करता है
- Mac के oscillator FFT, vector addition/multiplication/scale और FFT Accelerate का
vDSPइस्तेमाल करते हैं - DynamicsCompressor के
sin,exp,log10f,powfजैसे per-sample transcendental functions scalarlibsystem_mइस्तेमाल करते हैं - एक audio graph V8 built-in math, scalar library और Accelerate जैसी तीन libraries में फैल सकता है
- Mac के oscillator FFT, vector addition/multiplication/scale और FFT Accelerate का
- WebAssembly में transcendental function instructions नहीं हैं
sinआदि के results module में शामिलlibmपर निर्भर करते हैंf64.sqrt,f64.mulजैसी arithmetic hardware में execute होती है, इसलिए OS के हिसाब से समान रहती है- बची हुई fingerprint axis ARM और x86 के बीच NaN normalization और कुछ SIMD rounding differences हैं
- Detection signals
Math.tanh, सभी CSS trigonometric functions और Web Audio पर केंद्रित हैं- Web Audio का Accelerate FFT CPU architecture दिखाता है
- compressor का scalar
libsystem_mOS दिखाता है
Values को बदलने के बजाय सटीक reproduce करना
-
Noise fail क्यों होता है
- result में noise जोड़ने पर वह reference table में मौजूद किसी भी असली OS value से match नहीं कर सकता
- हर call पर random value बदलने से determinism टूटता है, और यह phenomenon खुद एक अलग detection signal बन जाता है
- लक्ष्य similar value नहीं, बल्कि दावा किए गए OS द्वारा लौटाई जाने वाली value से bit-for-bit समान result है
-
Target algorithm के सभी elements restore करना
- target
libmसे minimax approximation coefficients, exponent tables और range reduction constants recover करके portable C code में ले जाए जाते हैं - target library जिन inputs को गलत direction में round करती है, उन्हें भी ठीक उसी तरह match करना होगा
- Apple
sinreproduction मेंlibsystem_mसे निकाले गए coefficients के exact bit patterns और explicitfma()calls इस्तेमाल होते हैं - coefficients को decimal में ले जाने पर transcription के दौरान फिर rounding हो सकती है, इसलिए उन्हें hexadecimal floating-point values के रूप में preserve किया जाता है
- Apple जिन multiply-adds को fuse करता है, code में भी उन्हें explicitly fuse किया जाता है
- target
-
FMA को deterministic बनाना
-ffp-contract=offसे compile करके compiler को मनमाने ढंग से FMA जोड़ने या हटाने से रोका जाता है- code में explicitly लिखे गए
fma()ही Apple जैसी जगहों पर execute होते हैं, इसलिए ARM mimic करते हुए x86 server पर चलाने पर भी वही bits मिलते हैं - hardware FMA और correctly rounded software FMA वही bits लौटाते हैं
Windows UCRT original code का इस्तेमाल
- Windows UCRT Linux server जैसी x86-64 ISA इस्तेमाल करता है और position-independent है, इसलिए actual
ucrtbase.dllको runtime memory में map करके math function exports को सीधे call किया जा सकता है - original code चलाने के कारण अलग से math algorithm reverse engineering के बिना actual UCRT bits मिलते हैं
- Linux के System V ABI और Windows x64 ABI के differences handle करने होंगे
- Windows x64 में callee return address के ऊपर 32-byte shadow space इस्तेमाल करता है
- callee-saved register set भी System V से अलग है
- function pointer को
ms_abiके रूप में declare न करने पर shadow-space writes clang stack frame को damage कर देती हैं और indirect call गलत address पर जा सकता है
- mapped DLL code CFI में registered indirect call target नहीं है
- production का
-fsanitize=cfi-icallहर call पर#UDtrap औरSIGILLपैदा कर सकता है - function pointer call करने वाले wrapper को
clang::no_sanitize("cfi-icall")चाहिए
- production का
- UCRT math functions शुरुआत में
mov eax, [rip+disp32]से CPU dispatch flag पढ़कर scalar या FMA/AVX2 path चुनते हैं- नए mapped DLL में flag 0 होता है, इसलिए slow scalar path चुना जाता है
- इस path का result modern Windows systems के result bits से अलग होता है
tanhprologue में flag address ढूंढकर first call से पहले FMA path force करना होगा ताकि actual Windows से bit-for-bit match हो
Patch location और performance constraints
- engine जिस single bottleneck point पर
libmcall करता है उसे hook करके, browser जिस OS का दावा करता है उसके अनुसार path चुना जाता है- Linux का दावा हो तो glibc कायम रखा जाता है
- macOS का दावा हो तो Apple reproduction implementation इस्तेमाल किया जाता है
- result exact हो, फिर भी execution time actual browser से अलग हो तो detect किया जा सकता है
- पहले build में default x86 baseline hardware FMA से पुराना था, इसलिए सभी
fma()software calls में lower हो गए और native से 2.5–6 गुना धीमे थे Math.tanhऔरMath.sinloops के timing ratio compare करने पर actual browser में मौजूद न होने वाला performance pattern सामने आ सकता है- hardware FMA enable करने पर हर fused operation single instruction बन गया, जिससे करीब 6 गुना speedup हुआ; यह glibc से तेज था, फिर भी result bits identical रहे
871,000 inputs से verification
- verification harness हर release में 871,000 inputs को सभी branches और domains में run करता है
- dense input grid
- interval boundaries
- subnormal numbers
- signed zero
- infinity
- NaN
- दो तरह के real environments को reference values के रूप में इस्तेमाल किया जाता है
- actual Mac सभी inputs पर scalar और Accelerate results अलग-अलग calculate करके उन points को identify करता है जहां दोनों implementations diverge करते हैं
- actual Mac के Chrome को debugging protocol से चलाकर
Math.tanhऔर सभी CSS trigonometric functions के full-precision results collect किए जाते हैं
Math.tanhऔर CSS केsin,cos,tan,asin,acos,atan,atan2actual Mac Chrome से bit-for-bit match करते हैं- यह भी verify किया जाता है कि reproduction implementation deployed binary में actual machine code की तरह ही behave करता है
- domain boundary पर browser post-processing भी match करनी होगी
- actual Mac पर CSS
asin(2)domain से बाहर होने के कारण NaN बनता है और CSS NaN को 0 तक clamp करता है, इसलिए final value0है - simple reproduction implementation इसे गलत तरीके से 90 degrees लौटा सकता है
- actual Mac पर CSS
Browser spoofing में math क्यों महत्वपूर्ण है
- math results deterministic और सस्ते में check किए जा सकते हैं, लेकिन सही spoofing के लिए vendor
libminternals और engine-specific call paths जानना जरूरी है - actual browser से match करने के लिए समझना होगा कि V8, Blink और Web Audio हर call site पर कौन-सी math library चुनते हैं, और last bit, architecture-specific behavior और execution time तक match करना होगा
- Scrapfly का Scrapium जब macOS के रूप में दिखने का request पाता है, तो cosine के rounding bits तक actual macOS traffic से match करने के लिए configured होता है
1 टिप्पणियां
Hacker News की राय
यह व्याख्या कि सही input के साथ
tanhको एक बार call करने का परिणाम OS-specific signature बन जाता है, browser version range पहचानने की संभावना को नज़रअंदाज़ करती हैज़्यादातर लोग User-Agent में OS को spoof नहीं करते, और fingerprinting OS itself से ज़्यादा quasi-unique traits के combination में रुचि रखती है। खोज दिलचस्प है, लेकिन article बहुत ज़्यादा LLM से लिखा हुआ लगता है, जिससे भरोसा कम होता है
ताकि वे bot detection को आसानी से पार कर सकें और दूसरे websites से scraped data customers को बेच सकें
यह बात कि इसे LLM से लिखा गया है, article और blog में disclose की गई है; इसे छिपाया नहीं गया और न ही इंसान होने का दिखावा किया गया। समय कम था, वरना article publish ही नहीं हो पाता, और इस choice की जिम्मेदारी लेने को तैयार हूं
browsers लगातार features add करते और bugs fix करते रहते हैं, और उनमें से ज़्यादातर को JavaScript से detect किया जा सकता है
सभी fingerprinting techniques को AI से analyze करके public कर देना, और विवाद के बाद browsers को इन्हें block करने की ओर धकेलना, एक चालाक strategy है क्योंकि इससे उनकी scraping business ज़्यादा पैसा कमा सकती है
ऐसी companies न होतीं तो browser fingerprinting आज जितनी फैली है उतनी नहीं होती, और internet भी बेहतर होता। बल्कि fingerprint.js जैसी clear opposing interest वाली तरफ़ की posts मुझे ज़्यादा पसंद हैं
correctly rounded transcendental functions को push करने की एक और वजह मिल गई
हाल ही में पता चला कि यह problem practically solve हो चुकी है। https://arith2026.org/program.html की दूसरी keynote देखें
libmfunctions शानदार हैं, लेकिन पुराने glibc केpowकी तरह worst-case performance भयानक नहीं होनी चाहिएrounding boundary के करीब इस्तेमाल होने वाले high-precision fallback path को खुद SLP vectorize करके worst-case performance improve करने की कोशिश की जा सकती है, लेकिन यह पहले ही ज़्यादातर uses के लिए काफी है। यह आश्चर्यजनक है कि JavaScript engines ECMAScript spec में recommended
fdlibmका इस्तेमाल जारी नहीं रखते, और अगरMath.tanhJavaScript का bottleneck path है तो वह काफी असामान्य code होगाengineering में fixed-point अक्सर इस्तेमाल होता था क्योंकि वह कहीं ज्यादा simple hardware पर चलता है और errors को mathematically आसानी से model किया जा सकता है। IEEE 754 floating-point theoretically भी संदिग्ध है, और precision loss के मामले में mantissa से छोटे integers, यानी 24-bit से कम integers, कभी-कभी 32-bit floating-point से बेहतर होते हैं
अच्छा होगा अगर यह technique https://coveryourtracks.eff.org/ में add हो जाए, ताकि मैं देख सकूं कि मेरी math function results बड़ी population में कितनी unique हैं
पता नहीं यह सच है या नहीं, लेकिन लगता है कि coveryourtracks.eff.org करीब 25 signals use करता है
article में Claude ने लिखा है, ऐसा साफ झलकता है
Claude link पर click करने से यह prompt भेजा जाता है:
summarize+this+article+and+explain+how+scrapfly+helps+me+scrape+any+website+at+scale+and+bypass+anti-bot+systems+for+my+use+case:+[https://scrapfly.dev/posts/browser-math-os-fingerprint/](<https://scrapfly.dev/posts/browser-math-os-fingerprint/>)Tor Browser और Mullvad Browser ने भी आखिरकार OS hiding छोड़ दी, लेकिन शायद उन्हें ऐसा नहीं करना चाहिए था
ऐसा लगता है कि fingerprinting paths बहुत ज्यादा हैं
browser के अंदर और बाहर OS-specific behavior differences बहुत ज्यादा होते हैं, सभी को handle करना मुश्किल है। canvas extraction block करने या noise add करने पर भी rendering differences expose हो सकते हैं, और Tor Browser developers ने confirm किया है कि वे पूरी तरह अलग OS तो दूर, X11 और Wayland के differences भी hide नहीं कर सकते। https://forum.torproject.org/t/linux-is-it-alright-to-run-th...
navigator.platformतक modify नहीं करता, इसलिए non-Windows environment पता लगाना बहुत आसान हैअपने पसंदीदा JavaScript injection plugin से यह code डाल दें:
let oldTanh = Math.tanh; Math.tanh = x => oldTanh(x) + Math.random()/10000000;Math.tanh = Math.random;पसंद हैlatest glibc CORE-MATH का correctly rounded
tanhuse करता है, इसलिए यह article में quoted values से अलग result return करता हैबाकी transcendental functions के लिए भी reasonable performance के साथ correct rounding implement की जा सकती है या नहीं, यह अभी clear नहीं है, इसलिए हर function अपनी unique fingerprint छोड़ता है
Chrome का executable code ही सैकड़ों MB का है, तो मुझे लगा था कि user-space libraries का लगभग आधा हिस्सा statically linked होगा
साथ ही मुझे लगा था कि
tanhfunction call नहीं, बल्कि JavaScript JIT द्वारा CPU instruction में emit किया गया built-in operation होगा; math operation के लिएdlsym()function से branch करना अजीब है। CPU instruction खुद भी fingerprinting में इस्तेमाल हो सकता हैmicrocode को branch prediction जैसे फायदे नहीं मिलते, इसलिए practically वह software implementation से धीमा होता है
शक है कि यह लड़ाई जीती जा सकती है
पर्याप्त functions run करने पर execution time ratios और rounding results को combine करके OS और exact model ही नहीं, उसी machine पर चल रहे दूसरे tasks तक estimate किए जा सकते हैं। पूरी तरह रोकने के बजाय शायद इसे थोड़ा मुश्किल बनाना ही possible है
आखिर society और law को catch up करना होगा। जैसे door locks intrusion को पूरी तरह नहीं रोकते, लेकिन social condemnation और criminal penalties उन्हें complement करते हैं, वैसे ही इस तरह की individual tracking को illegal बनाना चाहिए और इससे फायदा उठाने वाली companies और employees को socially ostracize करना चाहिए
Russia, Myanmar, North Korea जैसी जगहों पर rule of law काम नहीं करता, और local authorities कभी-कभी foreigners को ठगने वाले criminals को actively protect भी करती हैं, इसलिए door lock analogy लागू नहीं होती