1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • A partir do Chrome 148, o Math.tanh do V8 passou a chamar o std::tanh do host em vez do fdlibm embutido, e assim a mesma entrada retorna um último bit diferente no glibc do Linux, no libsystem_m do macOS e no UCRT do Windows
  • Math.tanh(0.8) resulta em 0.6640367702678491 no Linux, 0.664036770267849 no macOS e 0.6640367702678489 no Windows, permitindo distinguir os três sistemas operacionais com uma única chamada; se o resultado não bater com o SO declarado no User-Agent, a camuflagem é exposta
  • Como cada engine tem um caminho de vazamento diferente, no Math.* do V8 apenas o tanh usa a biblioteca matemática do host, mas todas as funções trigonométricas de CSS no Blink e algumas operações do Web Audio também passam por bibliotecas específicas do SO
  • Adicionar pequenas perturbações arbitrárias aos valores não os faz coincidir com nenhum SO real e ainda quebra o determinismo; por isso, é necessário reproduzir em nível de bit os coeficientes, tabelas, redução de intervalo e comportamento de FMA da biblioteca-alvo, ou mapear diretamente o código original do UCRT
  • A Scrapfly valida, a cada release, 871.000 entradas comparando com Macs reais e Chrome real para verificar a correspondência bit a bit de Math.tanh e de 7 funções trigonométricas de CSS, ajustando não só a precisão, mas também diferenças de arquitetura e tempo de execução ao nível de um navegador real

O SO revelado por Math.tanh

  • O resultado de Math.tanh(0.8) varia conforme a biblioteca matemática do host
    • glibc do Chrome no Linux: 0.6640367702678491
    • libsystem_m do Chrome no macOS: 0.664036770267849
    • UCRT do Chrome no Windows: 0.6640367702678489
  • Apple e glibc diferem em geral por 1 ULP em cerca de um quarto de todas as entradas, e o UCRT do Windows difere das duas bibliotecas em alguns por cento das entradas
    • ULP (unit in the last place) é o espaçamento entre números de ponto flutuante consecutivos representáveis em uma determinada magnitude, e 1 ULP é a menor diferença que um double pode representar
  • Ao medir o Chrome 150 real em Linux, macOS 26 com Apple Silicon e Windows 11 via DevTools Protocol, o poder de classificação varia conforme a entrada
    • tanh(0.5)0.46211715726000974 nos três SOs, então não serve para detecção
    • tanh(0.7) difere em 1 ULP apenas no Linux
    • tanh(0.8) é diferente nos três SOs, com amplitude total de 2 ULPs
    • tanh(0.9) difere em 1 ULP apenas no Windows
  • Em cerca de três quartos das entradas, os três SOs produzem o mesmo resultado, mas com uma entrada adequada é possível obter uma assinatura por SO
  • Se um navegador afirmar ser macOS, mas retornar os bits matemáticos do Linux, o resultado de Math.tanh entra em contradição com o User-Agent

A mudança introduzida no Chrome 148

  • Até o Chrome 147, o V8 embutia um port do fdlibm, uma implementação matemática portátil, para calcular Math.tanh, então todos os SOs retornavam os mesmos bits
  • O commit do V8 c1486295ae5 substituiu a implementação embutida pelo std::tanh da plataforma
    • Essa mudança entrou pela primeira vez no V8 14.8.57 e no Chrome 148
    • Chrome 148, 149 e 150 expõem as diferenças do libm do host, mas Chrome 147 ou anterior não vazam o SO por esse caminho
  • O IEEE 754 define a forma de armazenamento do double, mas não exige que funções transcendentais como sin, cos, tanh e exp sejam necessariamente arredondadas de forma correta
  • A biblioteca matemática (libm) de cada SO faz compromissos entre desempenho e erro em ULP, usando coeficientes diferentes de polinômios minimax, tabelas de consulta e constantes de redução de intervalo
    • Linux usa glibc
    • macOS usa o libsystem_m da Apple
    • Windows usa o ucrtbase.dll do UCRT
  • O detector não precisa analisar a operação matemática em si; ele pode comparar os valores com uma tabela de resultados por entrada obtida do Chrome real

Quatro armadilhas que dificultam a reprodução

  • Só algumas funções do V8 vazam

    • O V8 faz link estático da maior parte das implementações matemáticas, então elas produzem os mesmos resultados independentemente do SO
    • Math.exp, Math.pow, Math.atan e outras usam implementações embutidas do llvm-libc
    • Math.sin e Math.cos usam rotinas embutidas dbl-64 derivadas do glibc
    • Após o Chrome 148, entre Math.*, apenas Math.tanh usa o std::tanh da plataforma e por isso vaza o SO
    • Forjar até as funções que não vazam como se fossem do SO-alvo quebra a estrutura real de chamadas do V8, e a própria assimetria em que só o tanh difere também pode ser verificada
  • JavaScript e CSS usam caminhos diferentes

    • sin(), cos() e atan2() do CSS não compartilham código com Math.sin do JavaScript
    • O motor de layout Blink reduz primeiro os ângulos em graus e depois chama std::sin e afins da plataforma sobre o valor reduzido
    • Isso difere do resultado de calcular diretamente com entrada em radianos, e todas as 7 funções trigonométricas de CSS vazam o SO via libm do host
    • Para reproduzir em nível de bit, é preciso incluir não só a função matemática final, mas também a redução de intervalo em graus e a conversão entre radianos e graus
  • Existem duas bibliotecas diferentes até dentro do macOS

    • Em Apple Silicon, coexistem o libsystem_m escalar e as rotinas vetoriais do Accelerate, como vvsin e vvtanh, e as duas implementações não são iguais
    • Em 1 milhão de entradas, conforme a função, 10% a 89% dos resultados diferiram
    • cos(0) é exatamente 1.0 na implementação escalar
    • No Accelerate, retorna 0.9999999999999999
    • O Chrome real em Mac foi medido via protocolo de depuração para distinguir qual biblioteca é usada em cada ponto de chamada
    • Math.tanh, as funções trigonométricas de CSS e as funções transcendentais por amostra do compressor de áudio usam o libsystem_m escalar
    • O DSP do Web Audio no Mac, FFT, matemática vetorial e filtros biquad usam Accelerate
    • Caminhos relevantes no Chromium incluem fft_frame_mac.cc, vector_math_mac.h, biquad.cc e BUILDFLAG(IS_MAC)
    • Se a biblioteca da Apple escolhida não corresponder ao ponto de chamada, os resultados podem sair 1 ULP errados na maioria das entradas
  • A arquitetura da CPU também interfere nos resultados

    • ARM e x86 diferem em FMA (fused multiply-add) e na propagação do sinal de NaN
    • Mesmo que o procedimento matemático esteja correto, se o compilador fundir multiplicação e soma apenas em uma arquitetura, os bits de resultado mudam

Caminhos de vazamento por engine e recurso

  • O Math.* do V8 em JavaScript quase todo usa implementações embutidas, e há apenas um ponto que se conecta ao libm do host: Math.tanh
    • sin, cos, tan, asin, acos, atan, atan2, exp, log, log2, log10, pow usam implementações embutidas do V8
    • sqrt, abs e as quatro operações aritméticas são operações de hardware
  • As funções matemáticas do calc() no CSS fazem o Blink chamar diretamente a biblioteca da plataforma
    • sin, cos, tan, asin, acos, atan, atan2, exp, log, log2, log10, pow usam o libm do host
    • Não existe um caminho correspondente para tanh em CSS
  • O Web Audio mistura várias implementações dependendo do ponto de chamada
    • No Mac, FFT do oscilador, soma/multiplicação/escala vetorial e FFT usam vDSP do Accelerate
    • Funções transcendentais por amostra do DynamicsCompressor, como sin, exp, log10f, powf, usam o libsystem_m escalar
    • Um único grafo de áudio pode atravessar três bibliotecas diferentes: matemática embutida do V8, biblioteca escalar e Accelerate
  • WebAssembly não tem instruções de funções transcendentais
    • Resultados como sin dependem do libm incluído no módulo
    • Operações aritméticas como f64.sqrt e f64.mul são executadas em hardware e por isso são iguais entre SOs
    • O eixo de fingerprint restante está na normalização de NaN entre ARM e x86 e em algumas diferenças de arredondamento de SIMD
  • Os sinais de detecção se concentram em Math.tanh, em todas as funções trigonométricas de CSS e no Web Audio
    • O FFT do Accelerate no Web Audio revela a arquitetura da CPU
    • O libsystem_m escalar do compressor revela o SO

Em vez de mexer no valor, reproduzir exatamente

  • Por que o ruído falha

    • Adicionar ruído ao resultado pode fazer com que ele não corresponda ao valor de nenhum SO real na tabela de referência
    • Se o valor aleatório mudar a cada chamada, o determinismo se perde, e isso por si só vira outro sinal de detecção
    • O objetivo não é obter um valor parecido, mas sim exatamente o mesmo valor em nível de bit que o SO declarado retornaria
  • Restaurar todos os elementos do algoritmo-alvo

    • Recuperar do libm de destino os coeficientes de aproximação minimax, tabelas de expoentes e constantes de redução de intervalo, e portar isso para código C portátil
    • É preciso coincidir até com entradas em que a biblioteca-alvo arredonda na direção errada
    • A reprodução do sin da Apple usa o padrão exato de bits dos coeficientes extraídos do libsystem_m e chamadas explícitas a fma()
    • Se os coeficientes forem passados para decimal, podem ser arredondados de novo no processo de transcrição, por isso são preservados como valores hexadecimais de ponto flutuante
    • Cada multiplicação-soma fundida que a Apple faz também é fundida explicitamente no código
  • Fixar o FMA de forma determinística

    • Compilar com -ffp-contract=off para impedir que o compilador adicione ou remova FMA arbitrariamente
    • Assim, só o fma() explícito no código é executado nos mesmos pontos que na Apple, e é possível obter os mesmos bits mesmo rodando em um servidor x86 enquanto se imita ARM
    • FMA em hardware e FMA em software com arredondamento correto retornam os mesmos bits

Uso do código original do UCRT do Windows

  • O UCRT do Windows usa a mesma ISA x86-64 que servidores Linux e é position-independent, então é possível mapear o ucrtbase.dll real na memória em tempo de execução e chamar diretamente os exports das funções matemáticas
  • Como o código original é executado, obtêm-se os bits reais do UCRT sem precisar fazer engenharia reversa separada do algoritmo matemático
  • É necessário tratar a diferença entre a ABI System V do Linux e a ABI x64 do Windows
    • No Windows x64, a função chamada usa 32 bytes de shadow space acima do endereço de retorno
    • O conjunto de registradores preservados pelo callee também é diferente do System V
    • Se o ponteiro de função não for declarado com ms_abi, a escrita no shadow space pode corromper o stack frame do clang e fazer a chamada indireta saltar para um endereço incorreto
  • O código do DLL mapeado não é um alvo de chamada indireta registrado no CFI
    • Em produção, -fsanitize=cfi-icall pode causar traps #UD e SIGILL a cada chamada
    • O wrapper que chama o ponteiro de função precisa de clang::no_sanitize("cfi-icall")
  • As funções matemáticas do UCRT leem no início um flag de dispatch de CPU com mov eax, [rip+disp32] para escolher entre caminho escalar e caminho com FMA/AVX2
    • No DLL recém-mapeado, o flag fica em 0 e o caminho lento escalar é escolhido
    • Os bits produzidos por esse caminho diferem dos resultados de um Windows moderno
    • É preciso localizar o endereço do flag no prólogo do tanh e forçar o caminho com FMA antes da primeira chamada para coincidir bit a bit com o Windows real

Ponto de patch e restrições de desempenho

  • Faz-se hook no gargalo único em que a engine chama o libm, e escolhe-se o caminho conforme o SO que o navegador afirma ser
    • Se afirmar ser Linux, mantém-se o glibc
    • Se afirmar ser macOS, usa-se a implementação reconstituída da Apple
  • Mesmo que o resultado esteja correto, diferenças no tempo de execução em relação a um navegador real também podem ser detectadas
  • O primeiro build usava uma baseline x86 padrão antiga demais para FMA em hardware, então todo fma() era rebaixado a chamada de software, ficando 2,5 a 6 vezes mais lento que o nativo
  • Comparar a razão de tempo entre loops de Math.tanh e Math.sin pode revelar um padrão de desempenho que não existe no navegador real
  • Ao ativar FMA em hardware, cada operação fundida virou uma única instrução e ficou cerca de 6 vezes mais rápida, mantendo os mesmos bits e até superando o glibc em velocidade

Validação com 871.000 entradas

  • O harness de validação executa, a cada release, 871.000 entradas cobrindo todos os ramos e todo o domínio
    • grade densa de entradas
    • limites de intervalo
    • subnormais
    • zero com sinal
    • infinito
    • NaN
  • Dois tipos de ambiente real são usados como referência
    • Um Mac real calcula, para todas as entradas, os resultados escalar e Accelerate, permitindo identificar onde as duas implementações divergem
    • O Chrome real em Mac é executado pelo protocolo de depuração para coletar os resultados em precisão total de Math.tanh e de todas as funções trigonométricas de CSS
  • Math.tanh e sin, cos, tan, asin, acos, atan, atan2 do CSS coincidem bit a bit com o Chrome real no Mac
  • Também se verifica se a implementação reproduzida se comporta exatamente como o código de máquina real do binário distribuído
  • É necessário coincidir até com o pós-processamento do navegador nos limites de domínio
    • No Mac real, asin(2) no CSS está fora do domínio, vira NaN e o CSS restringe NaN para 0, então o valor final é 0
    • Uma implementação simplificada de reprodução pode retornar incorretamente 90 graus

Por que a matemática importa na camuflagem de navegador

  • Resultados matemáticos podem ser verificados de forma determinística e barata, mas para camuflá-los com precisão é preciso conhecer o interior do libm de cada fornecedor e os caminhos de chamada de cada engine
  • Para coincidir com um navegador real, é necessário descobrir qual biblioteca matemática V8, Blink e Web Audio escolhem em cada ponto de chamada, e ainda alinhar o último bit, o comportamento por arquitetura e o tempo de execução
  • O Scrapium da Scrapfly, quando solicitado a se apresentar como macOS, é configurado para coincidir com o tráfego real do macOS até no bit de arredondamento do cosseno

1 comentários

 
GN⁺ 4 시간 전
Comentários do Hacker News
  • A explicação de que uma única chamada de tanh com a entrada correta produz uma assinatura por sistema operacional deixou de lado a possibilidade de identificar um intervalo de versões do navegador
    A maioria não falsifica o sistema operacional no User-Agent, e fingerprinting se interessa mais por combinações de características quase únicas do que pelo sistema operacional em si. A descoberta é interessante, mas o texto parece excessivamente escrito por LLM, o que reduz sua credibilidade

    • A empresa que produziu esse texto na prática tenta disfarçar bots em VMs Linux como se fossem máquinas físicas Windows ou macOS
      Assim eles conseguem passar mais facilmente pela detecção de bots e vender aos clientes dados coletados de outros sites
    • Por enquanto, com este método só dá para identificar Chromium 148 ou superior, mas se você testar em JavaScript ou CSS recursos do V8 e do Blink adicionados em cada versão, dá para determinar com segurança a versão principal a partir de algo em torno da 120
      O fato de ter sido escrito com LLM foi divulgado no post e no blog; não foi escondido nem houve tentativa de se passar por humano. Eu estava sem tempo, então, se não fosse assim, eu provavelmente nem teria publicado o texto, e assumo essa escolha
    • Dá para identificar um intervalo de versões, mas já existem incontáveis meios para isso
      O navegador continua adicionando recursos e corrigindo bugs, e a maior parte disso pode ser detectada por JavaScript
    • Se o conteúdo estiver correto, não importa quem o escreveu, e a linha central do argumento da LLM também faz sentido
  • Estratégia esperta: analisar todas as técnicas de fingerprinting com IA e publicá-las, e depois da controvérsia induzir os navegadores a bloqueá-las, para que o próprio negócio de scraping da empresa ganhe mais dinheiro
    Se empresas assim não existissem, o fingerprinting de navegadores não estaria tão disseminado hoje e a internet seria melhor. Eu prefiro até textos do lado oposto, como os da fingerprint.js, onde o conflito de interesses é claro

    • Discordo, porque com ou sem scrapers, para rastrear humanos é preciso fingerprinting, então isso acabaria sendo usado de qualquer forma
  • Mais um motivo para defender funções transcendentais com arredondamento correto
    Recentemente descobri que esse problema está praticamente resolvido. Veja a segunda palestra principal em https://arith2026.org/program.html

    • Funções libm com arredondamento correto são ótimas, mas não podem ter um pior caso de desempenho terrível como o pow antigo da glibc
      Dá para tentar melhorar o pior caso vetorizando manualmente com SLP o caminho alternativo de alta precisão usado perto do limite de arredondamento, mas para a maioria dos usos isso já é suficiente. É surpreendente que engines JavaScript continuem sem usar o fdlibm recomendado pela especificação ECMAScript, e se Math.tanh for um gargalo em JavaScript, esse é um código bastante incomum
    • É difícil entender por que precisão fixa e aritmética inteira não são mais amplamente usadas
      Em engenharia, ponto fixo era usado com frequência porque roda em hardware muito mais simples e os erros podem ser modelados matematicamente com facilidade. IEEE 754 é teoricamente questionável, e quando há perda de precisão, inteiros pequenos, isto é, abaixo de 24 bits, às vezes são melhores do que ponto flutuante de 32 bits
    • Fico surpreso com esse esquema de registrar um novo domínio todo ano e renová-lo para sempre
  • Seria bom se essa técnica fosse adicionada ao https://coveryourtracks.eff.org/ para eu poder verificar quão únicos são os resultados das minhas funções matemáticas em uma população maior

    • Essa empresa afirma ter aplicado patch em mais de 550 arquivos C++ do Chromium para alterar mais de 4.000 sinais
      Não sei se isso é verdade, mas imagino que o coveryourtracks.eff.org use algo como 25 sinais
  • O texto tem cara de ter sido escrito pelo Claude

    • O link de resumo por IA no topo do texto é absurdo, porque pede ao provedor de IA escolhido não só um resumo do artigo, mas também propaganda do produto
      Ao clicar no link do Claude, é enviado o 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/
    • A descoberta do título é interessante, mas o resto é basicamente texto escrito pelo Claude
    • Tenho estado menos ativo no HN nos últimos meses, mas parece que a comunidade está rotulando conteúdo como ruim e obcecada de forma paranoica em detectar uso de LLM
  • O Tor Browser e o Mullvad Browser acabaram desistindo de ocultar o sistema operacional, mas talvez não devessem ter feito isso
    Parece haver caminhos de fingerprinting demais

    • Acho que foi a decisão correta, porque nem está claro se esconder o sistema operacional é possível
      Há diferenças demais de comportamento por sistema operacional dentro e fora do navegador, e é difícil cobrir tudo. Mesmo bloqueando extração de canvas ou adicionando ruído, diferenças de renderização ainda podem vazar, e os desenvolvedores do Tor Browser já confirmaram que não conseguem esconder nem sequer a diferença entre X11 e Wayland, quanto mais entre sistemas operacionais totalmente distintos. https://forum.torproject.org/t/linux-is-it-alright-to-run-th...
    • O Tor Browser nem sequer altera navigator.platform, então é muito fácil descobrir um ambiente que não seja Windows
  • Basta inserir o seguinte código com seu plugin favorito de injeção de JavaScript: let oldTanh = Math.tanh; Math.tanh = x => oldTanh(x) + Math.random()/10000000;

    • Eu prefiro a versão mais concisa: Math.tanh = Math.random;
    • Isso já é abordado no texto; basta procurar por “No noise”
    • Várias empresas anti-bot vão detectar essa substituição e usá-la como sinal de fingerprinting
    • Em vez de retornar o valor normal, agora você pode acabar se revelando como um usuário tentando esconder o fingerprint, o que torna a identificação ainda mais fácil
  • A glibc mais recente usa o tanh com arredondamento correto do CORE-MATH, então retorna resultados diferentes dos citados no texto
    Ainda não está claro se outras funções transcendentais também conseguem implementar arredondamento correto com desempenho razoável, então cada função continua deixando seu próprio fingerprint característico

  • O Chrome tem centenas de MB só de código executável, então eu achava que ele tinha linkado estaticamente pelo menos metade das bibliotecas de espaço de usuário
    Além disso, eu pensava que tanh não era uma chamada de função, mas uma operação embutida emitida pelo JIT de JavaScript como instrução de CPU, então é estranho ter de desviar para uma função via dlsym() para uma operação matemática. As próprias instruções de CPU também podem ser fingerprintadas

    • A x87 FPU implementava funções transcendentais em microcódigo, mas a maioria dos conjuntos de instruções não oferece isso
      O microcódigo não se beneficia de coisas como predição de desvio, e na prática pode ser mais lento do que uma implementação em software
    • Se bem me lembro, o Chrome é o único navegador que preserva os bits não usados de valores NaN em modo sem JIT, e quando o código é JITado esses bits viram 0
  • Fico em dúvida se dá para vencer essa briga
    Se você executar funções suficientes, parece possível combinar proporções de tempo de execução e resultados de arredondamento para inferir não só o sistema operacional e o modelo exato, mas até outras tarefas rodando na mesma máquina. Parece que no máximo dá para dificultar um pouco, não bloquear totalmente
    No fim, a sociedade e a lei precisam alcançar isso. Assim como fechaduras não impedem totalmente invasões, mas são complementadas por reprovação social e punição criminal, deveríamos tornar ilegal esse tipo de rastreamento pessoal e ostracizar socialmente as empresas que lucram com isso e quem trabalha nelas

    • No ciberespaço, muitas vezes quem faz coisas ilegais ou que deveriam ser ilegais está em jurisdições onde não há como aplicar a lei
      Em lugares como Rússia, Myanmar e Coreia do Norte, o Estado de direito não funciona, e as autoridades locais às vezes protegem ativamente criminosos que enganam estrangeiros, então a analogia com fechaduras não se sustenta