Từ Chromium 148, có thể nhận diện OS nền tảng bằng Math.tanh
(scrapfly.dev)- Từ Chrome 148,
Math.tanhcủa V8 gọistd::tanhcủa host thay vì fdlibm tích hợp, khiến cùng một đầu vào trả về bit cuối khác nhau giữa glibc trên Linux,libsystem_mtrên macOS và UCRT trên Windows Math.tanh(0.8)cho kết quả0.6640367702678491trên Linux,0.664036770267849trên macOS, và0.6640367702678489trên Windows, nên có thể phân biệt ba OS chỉ bằng một lần gọi; nếu kết quả khác với OS mà User-Agent khai báo thì việc giả mạo bị lộ- Đường rò rỉ khác nhau tùy engine: trong
Math.*của V8 chỉ cótanhdùng thư viện toán học của host, nhưng toàn bộ các hàm lượng giác CSS của Blink và một số phép toán Web Audio cũng đi qua thư viện theo từng OS - Nếu tùy ý làm nhiễu giá trị, kết quả sẽ không khớp với bất kỳ OS thực nào và còn phá vỡ tính quyết định; vì vậy phải tái hiện ở mức bit các hệ số, bảng, range reduction và hành vi FMA của thư viện mục tiêu, hoặc ánh xạ trực tiếp mã UCRT gốc
- Scrapfly đối chiếu 871.000 đầu vào với Mac thật và Chrome ở mỗi bản phát hành để kiểm chứng sự khớp bit của
Math.tanhvà 7 hàm lượng giác CSS; họ không chỉ khớp độ chính xác mà còn khớp cả khác biệt kiến trúc và thời gian thực thi ở mức trình duyệt thật
OS bị Math.tanh tiết lộ
- Kết quả của
Math.tanh(0.8)thay đổi theo thư viện toán học của host- glibc của Linux Chrome:
0.6640367702678491 libsystem_mcủa macOS Chrome:0.664036770267849- UCRT của Windows Chrome:
0.6640367702678489
- glibc của Linux Chrome:
- Apple và glibc nhìn chung lệch 1 ULP ở khoảng một phần tư toàn bộ đầu vào, còn Windows UCRT khác hai thư viện kia ở vài phần trăm đầu vào
- ULP (unit in the last place) là khoảng cách giữa hai số dấu phẩy động liên tiếp có thể biểu diễn ở một độ lớn nhất định; 1 ULP là chênh lệch nhỏ nhất mà
doublecó thể biểu diễn
- ULP (unit in the last place) là khoảng cách giữa hai số dấu phẩy động liên tiếp có thể biểu diễn ở một độ lớn nhất định; 1 ULP là chênh lệch nhỏ nhất mà
- Khi đo Chrome 150 thực tế trên Linux, macOS 26 chạy Apple Silicon và Windows 11 bằng DevTools Protocol, khả năng phân loại thay đổi theo đầu vào
tanh(0.5)giống nhau trên cả ba OS với giá trị0.46211715726000974, nên không dùng để phát hiện đượctanh(0.7)chỉ Linux lệch 1 ULPtanh(0.8)khác nhau trên cả ba OS, với toàn dải là 2 ULPtanh(0.9)chỉ Windows lệch 1 ULP
- Khoảng ba phần tư đầu vào cho cùng kết quả trên cả ba OS, nhưng chỉ cần một đầu vào phù hợp là có thể lấy được chữ ký theo OS
- Nếu trình duyệt tự nhận là macOS nhưng trả về bit toán học của Linux, kết quả
Math.tanhsẽ mâu thuẫn với User-Agent
Thay đổi xuất hiện trong Chrome 148
- Cho đến Chrome 147, V8 tính
Math.tanhbằng cách tích hợp một bản port của fdlibm, một triển khai toán học có tính di động, nên trả về cùng bit trên mọi OS - Commit V8
c1486295ae5đã thay triển khai tích hợp bằngstd::tanhcủa nền tảng- Thay đổi này lần đầu được đưa vào V8 14.8.57 và Chrome 148
- Chrome 148, 149 và 150 phơi bày khác biệt của
libmtrên host, nhưng Chrome 147 trở xuống không rò rỉ OS qua đường này
- IEEE 754 định nghĩa cách lưu trữ
double, nhưng không yêu cầu các hàm siêu việt nhưsin,cos,tanh,expphải luôn được làm tròn đúng - Thư viện toán học (libm) của mỗi OS đánh đổi giữa hiệu năng và sai số ULP, dùng các hệ số đa thức xấp xỉ minimax, bảng tra cứu và hằng số range reduction khác nhau
- Linux dùng glibc
- macOS dùng Apple
libsystem_m - Windows dùng
ucrtbase.dllcủa UCRT
- Bộ phát hiện không cần phân tích bản thân phép toán, chỉ cần so sánh giá trị với bảng kết quả theo từng đầu vào của Chrome thực tế
Bốn cạm bẫy khiến việc tái hiện trở nên khó khăn
-
Chỉ một số hàm của V8 bị rò rỉ
- V8 liên kết tĩnh hầu hết các triển khai toán học, nên cho cùng kết quả bất kể OS
Math.exp,Math.pow,Math.atanvà các hàm tương tự dùng triển khai llvm-libc tích hợpMath.sinvàMath.cosdùng routinedbl-64tích hợp có nguồn gốc từ glibc- Sau Chrome 148, chỉ
Math.tanh, vốn dùngstd::tanhcủa nền tảng, rò rỉ OS trong số các hàmMath.* - Nếu giả mạo cả các hàm không rò rỉ như thể chúng thuộc OS mục tiêu thì sẽ lệch với cấu trúc gọi thực của V8; chính tính bất đối xứng rằng chỉ
tanhkhác cũng có thể bị kiểm tra
-
JavaScript và CSS dùng các đường khác nhau
sin(),cos(),atan2()của CSS không chia sẻ mã vớiMath.sincủa JavaScript- Engine layout Blink trước hết thu gọn góc theo đơn vị độ, rồi gọi
std::sinvà các hàm tương tự của nền tảng trên giá trị đã thu gọn - Kết quả khác với việc tính trực tiếp đầu vào radian, và toàn bộ 7 hàm lượng giác CSS rò rỉ OS qua
libmcủa host - Tái hiện ở mức bit phải bao gồm không chỉ hàm toán học cuối cùng, mà cả range reduction theo đơn vị độ và quá trình chuyển đổi radian–độ
-
Ngay trong macOS cũng có hai thư viện khác nhau
- Trên Apple Silicon có cả
libsystem_mvô hướng và các routine vectorvvsin,vvtanhcủa Accelerate; hai triển khai này không giống nhau - Trên 1 triệu đầu vào, tùy hàm mà 10–89% kết quả khác nhau
cos(0)trong triển khai vô hướng chính xác là1.0- Trong Accelerate, nó trả về
0.9999999999999999 - Họ đo Chrome trên Mac thật bằng debugging protocol để phân biệt thư viện theo từng điểm gọi
Math.tanh, các hàm lượng giác CSS và các hàm siêu việt theo từng sample của bộ nén audio dùnglibsystem_mvô hướng- Web Audio DSP, FFT, toán vector và bộ lọc biquad trên Mac dùng Accelerate
- Các đường dẫn Chromium liên quan gồm
fft_frame_mac.cc,vector_math_mac.h,biquad.ccvàBUILDFLAG(IS_MAC) - Nếu chọn thư viện Apple không khớp với điểm gọi, phần lớn đầu vào có thể lệch 1 ULP
- Trên Apple Silicon có cả
-
Kiến trúc CPU cũng tham gia vào kết quả
- ARM và x86 khác nhau về fused multiply-add (FMA) và lan truyền dấu NaN
- Dù quy trình toán học đúng, nếu compiler chỉ hợp nhất phép nhân và phép cộng trên một kiến trúc thì bit kết quả sẽ khác
Đường rò rỉ theo engine và tính năng
Math.*của V8 trong JavaScript hầu như đều dùng triển khai tích hợp; điểm nối tớilibmcủa host chỉ cóMath.tanhsin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powdùng triển khai tích hợp của V8sqrt,absvà bốn phép toán số học là phép toán phần cứng
- Các hàm toán học trong CSS
calc()được Blink gọi trực tiếp qua thư viện nền tảngsin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powdùnglibmcủa host- CSS không có đường tương ứng cho
tanh
- Web Audio dùng pha trộn nhiều triển khai tùy điểm gọi
- Oscillator FFT, cộng/nhân/scale vector và FFT trên Mac dùng
vDSPcủa Accelerate - Các hàm siêu việt theo từng sample như
sin,exp,log10f,powfcủa DynamicsCompressor dùnglibsystem_mvô hướng - Một audio graph có thể đi qua ba thư viện: toán tích hợp của V8, thư viện vô hướng và Accelerate
- Oscillator FFT, cộng/nhân/scale vector và FFT trên Mac dùng
- WebAssembly không có lệnh hàm siêu việt
- Kết quả của
sinvà các hàm tương tự được quyết định bởilibmđược đóng gói trong module - Số học như
f64.sqrt,f64.mulchạy trên phần cứng nên giống nhau giữa các OS - Trục fingerprint còn lại là chuẩn hóa NaN giữa ARM và x86, cùng một số khác biệt làm tròn SIMD
- Kết quả của
- Tín hiệu phát hiện tập trung vào
Math.tanh, toàn bộ các hàm lượng giác CSS và Web Audio- Accelerate FFT của Web Audio tiết lộ kiến trúc CPU
libsystem_mvô hướng của bộ nén tiết lộ OS
Tái hiện chính xác thay vì làm nhiễu giá trị
-
Vì sao nhiễu thất bại
- Nếu thêm nhiễu vào kết quả, giá trị có thể không khớp với bất kỳ OS thực nào trong bảng chuẩn
- Nếu giá trị ngẫu nhiên thay đổi theo từng lần gọi, tính quyết định bị phá vỡ, và chính hiện tượng này trở thành một tín hiệu phát hiện riêng
- Mục tiêu không phải là giá trị gần giống, mà là kết quả giống đến từng bit với giá trị OS được khai báo trả về
-
Khôi phục mọi yếu tố của thuật toán mục tiêu
- Từ
libmmục tiêu, khôi phục các hệ số xấp xỉ minimax, bảng số mũ và hằng số range reduction rồi chuyển sang mã C có tính di động - Cần khớp cả những đầu vào mà thư viện mục tiêu làm tròn sai hướng
- Việc tái hiện
sincủa Apple dùng mẫu bit chính xác của các hệ số trích từlibsystem_mvà các lệnh gọifma()tường minh - Nếu chuyển hệ số sang dạng thập phân, chúng có thể bị làm tròn lại trong quá trình chép, nên được giữ ở dạng dấu phẩy động thập lục phân
- Mỗi phép nhân-cộng mà Apple hợp nhất cũng được hợp nhất tường minh trong mã
- Từ
-
Cố định FMA một cách quyết định
- Biên dịch với
-ffp-contract=offđể compiler không tự ý thêm hoặc loại bỏ FMA - Chỉ các
fma()được ghi tường minh trong mã mới chạy ở đúng vị trí như Apple, nên dù mô phỏng ARM trên server x86 vẫn lấy được cùng bit - FMA phần cứng và FMA phần mềm được làm tròn đúng trả về cùng bit
- Biên dịch với
Dùng mã nguồn gốc của Windows UCRT
- Windows UCRT dùng cùng ISA x86-64 với server Linux và độc lập vị trí, nên có thể ánh xạ
ucrtbase.dllthực vào bộ nhớ runtime và gọi trực tiếp các export hàm toán học - Vì chạy mã gốc, có thể lấy được bit UCRT thực tế mà không cần reverse engineering thuật toán toán học riêng
- Cần xử lý khác biệt giữa System V ABI của Linux và Windows x64 ABI
- Trong Windows x64, callee dùng shadow space 32 byte phía trên địa chỉ trả về
- Tập thanh ghi callee-preserved cũng khác System V
- Nếu không khai báo con trỏ hàm là
ms_abi, thao tác ghi vào shadow-space sẽ làm hỏng stack frame của clang và lời gọi gián tiếp có thể nhảy tới địa chỉ sai
- Mã DLL đã ánh xạ không phải là mục tiêu gọi gián tiếp đã đăng ký trong CFI
-fsanitize=cfi-icallở production có thể gây trap#UDvàSIGILLở mỗi lần gọi- Wrapper gọi con trỏ hàm cần
clang::no_sanitize("cfi-icall")
- Hàm toán học UCRT đọc cờ dispatch CPU bằng
mov eax, [rip+disp32]ở phần đầu để chọn đường vô hướng hoặc FMA/AVX2- Trong DLL mới ánh xạ, cờ bằng 0 nên chọn đường vô hướng chậm
- Kết quả của đường này khác bit kết quả trên hệ Windows hiện đại
- Cần tìm địa chỉ cờ trong prologue của
tanhvà ép sang đường FMA trước lần gọi đầu tiên để khớp từng bit với Windows thực
Vị trí patch và ràng buộc hiệu năng
- Hook vào điểm nghẽn duy nhất nơi engine gọi
libm, rồi chọn đường theo OS mà trình duyệt khai báo- Nếu khai báo Linux thì giữ glibc
- Nếu khai báo macOS thì dùng triển khai tái hiện Apple
- Dù kết quả chính xác, nếu thời gian thực thi khác trình duyệt thật thì vẫn có thể bị phát hiện
- Bản build đầu tiên có baseline x86 mặc định cũ hơn FMA phần cứng, nên hạ tất cả
fma()thành lời gọi phần mềm và chậm hơn native 2,5–6 lần - Khi so sánh tỷ lệ thời gian của vòng lặp
Math.tanhvàMath.sin, có thể lộ ra mẫu hiệu năng không tồn tại trong trình duyệt thật - Sau khi bật FMA phần cứng, mỗi phép hợp nhất trở thành một lệnh duy nhất, nhanh hơn khoảng 6 lần; kết quả bit vẫn giống hệt dù còn nhanh hơn glibc
Kiểm chứng bằng 871.000 đầu vào
- Harness kiểm chứng chạy 871.000 đầu vào trên mọi nhánh và miền xác định ở mỗi bản phát hành
- Lưới đầu vào dày
- Biên khoảng
- Subnormal
- Số 0 có dấu
- Vô cực
- NaN
- Dùng hai loại môi trường thực làm giá trị chuẩn
- Mac thật tính riêng kết quả vô hướng và Accelerate trên mọi đầu vào để xác định các điểm hai triển khai phân kỳ
- Chrome trên Mac thật được điều khiển bằng debugging protocol để thu kết quả đầy đủ độ chính xác của
Math.tanhvà mọi hàm lượng giác CSS
Math.tanhvà các hàm CSSsin,cos,tan,asin,acos,atan,atan2khớp từng bit với Chrome trên Mac thật- Họ cũng kiểm chứng triển khai tái hiện hoạt động giống hệt mã máy thực trong binary phát hành
- Cần khớp cả xử lý hậu kỳ của trình duyệt ở biên miền xác định
- Trên Mac thật, CSS
asin(2)nằm ngoài miền xác định nên thành NaN, rồi CSS giới hạn NaN về 0, nên giá trị cuối là0 - Một triển khai tái hiện đơn giản có thể trả sai thành 90 độ
- Trên Mac thật, CSS
Vì sao toán học quan trọng trong giả mạo trình duyệt
- Kết quả toán học có tính quyết định và rẻ để kiểm tra, nhưng muốn giả mạo chính xác phải hiểu nội bộ
libmcủa vendor và đường gọi theo từng engine - Để khớp trình duyệt thật, cần nắm được V8, Blink và Web Audio chọn thư viện toán học nào tại từng điểm gọi, đồng thời khớp cả bit cuối, hành vi theo kiến trúc và thời gian thực thi
- Scrapium của Scrapfly được cấu hình để khi được yêu cầu hiển thị như macOS, nó khớp cả bit làm tròn của cosine với lưu lượng macOS thực tế
1 bình luận
Các ý kiến trên Hacker News
Cách giải thích rằng chỉ cần gọi
tanhmột lần với đầu vào phù hợp thì kết quả sẽ trở thành chữ ký theo từng hệ điều hành đã bỏ sót khả năng nhận diện khoảng phiên bản trình duyệtHầu hết mọi người không giả mạo hệ điều hành trong User-Agent, và fingerprinting quan tâm đến tổ hợp các đặc tính gần như độc nhất hơn là bản thân hệ điều hành. Phát hiện này thú vị, nhưng bài viết có vẻ được viết bằng LLM quá nhiều nên làm giảm độ tin cậy
Làm vậy để dễ vượt qua phát hiện bot hơn và bán dữ liệu thu thập từ các website khác cho khách hàng
Việc bài viết được viết bằng LLM đã được công khai trong bài và trên blog, không hề che giấu hay giả vờ là người viết. Vì thiếu thời gian nên nếu không làm vậy thì có lẽ bài đã không được đăng, và tôi sẵn sàng chịu trách nhiệm cho lựa chọn này
Trình duyệt liên tục thêm tính năng và sửa lỗi, mà phần lớn trong số đó có thể phát hiện bằng JavaScript
Phân tích và công khai mọi kỹ thuật fingerprinting bằng AI, rồi sau tranh cãi thúc đẩy trình duyệt chặn chúng, là một chiến lược khôn ngoan vì nó giúp mảng scraping của chính họ kiếm được nhiều tiền hơn
Nếu không có những công ty như vậy, browser fingerprinting đã không tràn lan như hiện nay và Internet cũng đã tốt hơn. Tôi thà đọc bài từ phía đối lập có lợi ích rõ ràng như fingerprint.js còn hơn
Lại có thêm một lý do để thúc đẩy các hàm siêu việt được làm tròn chính xác
Gần đây tôi mới biết vấn đề này về cơ bản đã được giải quyết. Xem bài keynote thứ hai tại https://arith2026.org/program.html
libmđược làm tròn chính xác là rất tuyệt, nhưng không được có hiệu năng trường hợp xấu nhất kinh khủng nhưpowcủa glibc trước đâyCó thể thử cải thiện hiệu năng trường hợp xấu nhất bằng cách tự SLP-vector hóa đường dự phòng độ chính xác cao dùng khi gần biên làm tròn, nhưng hiện đã đủ tốt cho hầu hết mục đích sử dụng. Việc các engine JavaScript vẫn không dùng
fdlibmmà đặc tả ECMAScript khuyến nghị là khá bất ngờ, và nếuMath.tanhlà đường nóng gây nghẽn của JavaScript thì đó là đoạn mã khá đặc biệtTrong kỹ thuật, fixed-point thường được dùng vì chạy được trên phần cứng đơn giản hơn nhiều và dễ mô hình hóa sai số bằng toán học. Số thực dấu phẩy động IEEE 754 cũng đáng ngờ về mặt lý thuyết, và trong vấn đề mất độ chính xác, số nguyên nhỏ hơn phần trị — tức số nguyên dưới 24 bit — đôi khi còn tốt hơn số thực 32 bit
Mong kỹ thuật này được thêm vào https://coveryourtracks.eff.org/ để tôi có thể kiểm tra kết quả các hàm toán học của mình độc nhất đến mức nào trong một tập người dùng lớn hơn
Không rõ có đúng không, nhưng tôi nghĩ coveryourtracks.eff.org chỉ dùng khoảng 25 tín hiệu
Bài viết có dấu hiệu được Claude viết
Khi bấm liên kết Claude, prompt được gửi là
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 và Mullvad Browser cuối cùng cũng từ bỏ việc che giấu hệ điều hành, nhưng có lẽ họ không nên làm vậy
Có vẻ có quá nhiều đường fingerprinting như thế
Có quá nhiều khác biệt hành vi theo hệ điều hành cả bên trong lẫn bên ngoài trình duyệt, nên rất khó xử lý hết. Dù chặn trích xuất canvas hoặc thêm nhiễu, khác biệt render vẫn có thể bị lộ; các nhà phát triển Tor Browser cũng xác nhận rằng họ thậm chí không thể che giấu khác biệt giữa X11 và Wayland, chứ chưa nói đến các hệ điều hành hoàn toàn khác nhau. https://forum.torproject.org/t/linux-is-it-alright-to-run-th...
navigator.platform, nên rất dễ nhận ra môi trường không phải WindowsChỉ cần chèn đoạn mã sau bằng plugin inject JavaScript ưa thích của bạn:
let oldTanh = Math.tanh; Math.tanh = x => oldTanh(x) + Math.random()/10000000;Math.tanh = Math.random;glibc mới nhất dùng
tanhđược làm tròn chính xác của CORE-MATH, nên trả về kết quả khác với các giá trị được trích trong bàiHiện vẫn chưa rõ liệu các hàm siêu việt khác có thể triển khai làm tròn chính xác với hiệu năng hợp lý hay không, nên mỗi hàm vẫn để lại fingerprint riêng
Chrome có mã thực thi lên tới hàng trăm MB nên tôi cứ tưởng khoảng một nửa thư viện user-space đã được liên kết tĩnh
Ngoài ra, tôi từng nghĩ
tanhkhông phải là lời gọi hàm mà là phép toán tích hợp sẵn do JavaScript JIT phát ra thành lệnh CPU; vì vậy việc phải rẽ nhánh qua hàmdlsym()cho phép toán học là điều kỳ lạ. Bản thân lệnh CPU cũng có thể bị fingerprintMicrocode không được hưởng các lợi thế như dự đoán rẽ nhánh, nên thực tế chậm hơn triển khai bằng phần mềm
Tôi nghi ngờ liệu có thể thắng cuộc chiến này không
Nếu chạy đủ nhiều hàm, có lẽ có thể kết hợp tỷ lệ thời gian chạy và kết quả làm tròn để suy đoán không chỉ hệ điều hành và đúng mẫu máy, mà cả những tác vụ khác đang chạy trên cùng thiết bị. Có vẻ chỉ có thể làm nó khó hơn một chút, chứ không thể chặn hoàn toàn
Cuối cùng xã hội và pháp luật phải bắt kịp. Giống như khóa cửa không thể chặn hoàn toàn xâm nhập nhưng được bổ sung bằng sự lên án xã hội và xử phạt hình sự, cần hình sự hóa việc theo dõi cá nhân theo cách này và tẩy chay về mặt xã hội các công ty cũng như người lao động hưởng lợi từ nó
Ở những nơi như Nga, Myanmar, Triều Tiên, pháp quyền không vận hành, và chính quyền địa phương đôi khi còn chủ động bảo vệ tội phạm lừa đảo người nước ngoài, nên phép ví von với khóa cửa không còn đúng