Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
px-vm — Kit de engenharia reversa para a máquina virtual de bytecode da PerimeterX, apresentando um desmontador baseado em CFG, pipeline de descriptografia de 5 camadas, reconstrução da tabela de opcodes e limpador de emulação de pilha para pesquisa de segurança em impressão digital de detecção de bots. | Kitploit
Ferramentas/GitHubGitHub/b9ph0met/px-vm
Análise Dinâmica (Sandboxing)Evasão de IDS/IPSEngenharia ReversaSegurança WebAnálise de MalwareCriptografiaAnálise de BináriosPapers e PesquisaAprendizado e EducaçãoAnti-BotFalsificação de Impressão Digital
5514há 4 mesesRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →

Sobre

Kit de engenharia reversa para a máquina virtual de bytecode da PerimeterX, apresentando um desmontador baseado em CFG, pipeline de descriptografia de 5 camadas, reconstrução da tabela de opcodes e limpador de emulação de pilha para pesquisa de segurança em impressão digital de detecção de bots.

GitHub
b9ph0met/px-vm

px-vm

Ver Repositório
Compartilhar

Análise da VM do Auditor PerimeterX

Resumo

Este repositório documenta a engenharia reversa do auditor.js da PerimeterX, uma máquina virtual bytecode usada como camada secundária de fingerprinting no pipeline de detecção de bots do PX. Esta análise cobre:

  • Extração de bytecode e pipeline de descriptografia de 5 camadas
  • Reconstrução da tabela de opcodes (107 base + 40 honey + 24 padding + 16 grupos de superinstruções)
  • Descriptografia do pool de constantes (1230 entradas, 1095 strings criptografadas)
  • Desmontador baseado em CFG com resolução de sub-dispatch de superinstruções
  • Limpeza por emulação de pilha produzindo pseudo-código legível
  • Técnicas anti-análise: opcodes honey, instruções sobrepostas, hash de integridade de código

Nota: Este repositório cobre apenas uma versão (estática) da VM e é destinado a fins de pesquisa de segurança e análise. Não inclui resolvedores dinâmicos ou implementações de resolvedores de produção.

Contexto

Na quinta-feira, 2 de abril de 2026, a PerimeterX implantou uma nova VM bytecode como parte de seu pipeline de detecção de bots.

O Que Tem Aqui

auditor.js não parece um script de sensor PX normal. Em vez das habituais buscas de propriedade ofuscadas e funções coletoras:

  • 8 enormes strings base64 (_fg0 a _fg7), o programa VM criptografado dividido entre variáveis
  • Uma função de descriptografia XOR (_dp) com uma chave por site (_pk) que descompacta o JSON do programa
  • Um shuffle Fisher-Yates que permuta a tabela de opcodes para que os valores bytecode difiram por build
  • Um loop de despacho com mais de 107 manipuladores de caso, o interpretador da VM
  • Aritmética BigInt para criptografia RSA da saída do fingerprint
  • Um hash de integridade de código (_0x8df7) que faz hash do próprio código-fonte da VM para derivar uma chave de descriptografia, de modo que qualquer modificação silenciosamente quebre a descriptografia do bytecode

Passo 1: Extração de Bytecode

O programa VM é dividido em 8 variáveis, concatenado e então descriptografado via _dp() usando uma cifra XOR por site chaveada por _pk:

root@kitploit:~
var _pk = 893686289;
function _dp(_b) {
    var _r = atob(_b), _o = new Array(_r.length);
    for (var _i = 0; _i < _r.length; _i++) {
        _o[_i] = String.fromCharCode(
            _r.charCodeAt(_i) ^ (((_pk >>> (8 * (_i % 4))) ^ Math.imul(_i + 1, 0x6B8B4567)) & 0xFF)
        );
    }
    return _o.join("");
}

O resultado é um objeto JSON com nomes de chave ofuscados de dois caracteres (ex.: "uo" para seed, "dk" para nonce). Uma tabela de mapeamento os converte para nomes padrão.

root@kitploit:~
node extractor.js
# -> program.json

Estrutura do Programa

Passo 2: Descriptografia de Constantes

Todas as 1095 constantes string são criptografadas com duas camadas:

Camada 1: Murmur XOR estático chaveado por 4008000571, dependente da posição.

Camada 2: XOR de fluxo PRNG usando um LCG glibc, semeado combinando a seed do programa com o índice de cada constante via hash multiplicativo de Knuth.

Antes da descriptografia, a seed é submetida a XOR com uma impressão digital do ambiente (_0xaf48), uma máscara de bits de 8 bits calculada sondando APIs do navegador:

Para Chrome: _0xaf48 = 0b01111111 = 127, resultando em seed efetiva = 12755 ^ 127 = 12716.

Isso significa que o mesmo programa produz diferentes resultados de descriptografia em diferentes ambientes. Executá-lo no Node.js vs Chrome vs Firefox produz seeds diferentes.

root@kitploit:~
node decrypt_constants.js
# -> program_decrypted.json, constants_table.txt

O Que as Constantes Revelam

As strings descriptografadas nos dizem exatamente o que a VM usa como impressão digital:

Fingerprinting de navegador: screenWidth, screenHeight, innerWidth, innerHeight, devicePixelRatio, colorDepth, platform, userAgent, language, timezone, timezoneOffset, forcedColors, highContrast

Temporização de desempenho: navigationStart, domComplete, domLoading, fetchStart, requestStart, responseEnd, secureConnectionStart, serverTiming

Criptografia RSA: BigInt, modPow, AQAB (65537 em base64), modulusLength, shiftLeft, shiftRight, getRandomValues

Sondagem DOM/SVG: http://www.w3.org/2000/svg, createElementNS, getBoundingClientRect, getTotalLength, getBBox

Nomes de campos PX: mtr, tst, mst, enc, sbx, fstec, pdc, prb, wvi, wva, pti, dis, los, cv, sc, jd, , ,

Referências de endpoint: https://fst-ec.perimeterx.net/?id=

Anti-debugger: _CMP_RCX_07;_JNZ_0x0A_EB_CC, CC|CD-04|BREAKPOINT-005

Passo 3: Tabela de Opcodes

107 opcodes base cobrindo toda a linguagem JavaScript, além de ruído gerado dinamicamente:

40 opcodes honey são implementações alternativas de operações aritméticas/comparação usando expressões matematicamente equivalentes mas sintaticamente diferentes. ADD pode aparecer como (a^b) + 2*(a&b) ou -((-a)-b) ou a-(-b). Cada opcode base pode ter até 3 variantes, geradas deterministicamente a partir da seed. Uma simples instrução ADD pode aparecer como 4 valores bytecode diferentes dentro do mesmo programa, quebrando abordagens de correspondência de padrões.

24 opcodes padding são alocados na permutação mas não possuem manipuladores e nunca são emitidos. Existem para expandir o espaço de opcodes e dificultar a inversão do shuffle.

16 grupos de superinstruções são a característica anti-análise mais importante. Quando o loop de despacho resolve um opcode para um líder de superinstrução, o manipulador lê um byte adicional do fluxo de bytecode e despacha para um sub-manipulador. O sub-manipulador pode ser uma operação completamente diferente:

A tabela de opcodes é embaralhada via Fisher-Yates semeado pela seed efetiva, de modo que os valores bytecode diferem por build.

root@kitploit:~
node build_opcodes.js
# -> opcode_table.json, opcode_table.txt

Passo 4: Construtor de CFG

O núcleo do kit de ferramentas. cfg.js constrói um grafo de fluxo de controle seguindo todos os caminhos de execução a partir de PC=0, decodificando cada instrução com o contexto de criptografia correto.

Por Que Não um Desmontador Linear

PX usa instruções sobrepostas nos limites dos blocos. Os mesmos bytes decodificam como operandos em um caminho de execução e como opcodes em outro, dependendo do contexto de criptografia do bloco. Uma varredura linear decodifica cada posição de byte uma vez e perde o caminho alternativo. O CFG segue arestas de passagem (fall-through) e de salto, decodificando cada caminho independentemente.

Criptografia de Bytecode de Cinco Camadas

  1. Camada 1 (_0x3ca8): Murmur XOR estático nos bytes base64 brutos, chaveado por 4008000571
  2. Camada 2 (_0xece1): XOR por função com duas subcamadas: chave estática dependente da posição + chave hash de integridade de código
  3. Camada 3 (_0x427d): XOR rotativo por bloco. Cada bloco de criptografia (definido por limites fn.bl) recebe um XOR adicional derivado da chave da função e do índice do bloco. O bloco 0 não é criptografado no primeiro acesso; blocos 1+ são criptografados. É por isso que um desmontador linear funciona para o primeiro bloco mas produz lixo para blocos subsequentes.
  4. Permutação de opcode: Shuffle Fisher-Yates + deslocamento dependente da posição + deslocamento dependente do bloco
  5. XOR de operando por instrução: Bytes de operando submetidos a XOR com uma chave derivada da posição inicial da instrução

O CFG aplica todas as cinco camadas de forma não destrutiva (XOR de operando calculado on-the-fly, não in-place) para que regiões de instrução sobrepostas não se corrompam mutuamente.

Resolução de Superinstrução

Para cada líder de superinstrução, o CFG lê o sub-byte, consulta o manipulador real em super_groups.json e decodifica o operando para o opcode real. Destinos de salto de opcodes de salto fundidos (ex.: o que parece ser ASSIGN_OP_VAR mas é na verdade JMP) são seguidos corretamente.

root@kitploit:~
node cfg.js        # todas as funções -> cfg_output/
node cfg.js 79     # função única para stdout

Verificado contra traces de execução do navegador: 600 instruções traçadas em 14 funções, 0 divergências nos deltas da pilha. Todos os 34 opcodes únicos validados. Despacho de superinstrução confirmado como correto para todos os 10 grupos líderes que executaram durante a inicialização.

Passo 5: Limpeza

Pega a saída do CFG e executa emulação de pilha para produzir comentários de expressão. Transforma bytecode bruto em pseudo-código legível.

root@kitploit:~
node cleaner.js 79     # fn79 para stdout

Percorre as instruções em ordem, rastreando uma pilha virtual. Cada push/pop/chamada constrói uma string de expressão:

root@kitploit:~
  0018  GET_VAR                 ; 0.0001
  001e  GET_VAR                 ; or
  0024  PUSH_CONST              ; "_0x166"
  002b  CALL_METHOD_C           ; 0.0001._0x88(or, "_0x166")
  ...
  0114  PUSH_CONST              ; "fontSize"
  011b  PUSH_CONST              ; "pdc"
  0122  CALL_METHOD_C           ; _0x18c.getHours("fontSize", "pdc")

Apenas ruído aritmético/comparação puro em uma pilha vazia é suprimido. Todo o resto é mantido, já que o CFG já filtrou honey e padding.

112 funções, 5435 instruções mantidas, 207 ruídos suprimidos. fn79 (o coletor de impressões digitais, 1109 instruções) tem 85% de cobertura de comentários de expressão.

Arquitetura da VM

VM baseada em pilha com pilha de 256 slots, cadeia de escopo, cadeia de manipuladores try/catch e pilha de iterador for-in. O loop de despacho lê opcodes de 2 bytes em little-endian, resolve através de permutação + XOR de posição + deslocamento de bloco, descriptografa operandos in-place, executa o manipulador e, em seguida, re-criptografa operandos para que o bytecode nunca seja totalmente descriptografado na memória.

Principais Descobertas

  • Criptografia RSA da saída de impressão digital usando BigInt, modPow, expoente 65537
  • Impressão digital de renderização SVG via getTotalLength() e getBBox() em caminhos construídos
  • Coleta completa do waterfall performance.timing
  • Marcadores de detecção anti-debugger (CC|CD-04|BREAKPOINT-005)
  • A função 79 é o coletor principal de impressões digitais (8361 bytes, ~1200 instruções)

Notas

IA foi usada para ajudar a documentar o código, escrever ferramentas e redigir este readme.

Aviso Legal

Puramente para fins educacionais/pesquisa de segurança. Sem resolvedores ou bypasses, apenas documentando como a VM funciona porque é genuinamente interessante.

Se alguém da PerimeterX/HUMAN Security tiver preocupações sobre este repositório, sinta-se à vontade para entrar em contato: [email protected]

Baixar ferramenta
CampoDescrição
sSeed (12755), direciona todas as operações criptográficas
nNonce (1603730985), randomização por programa
gFlag de gerador, ativa a camada de descriptografia por hash de integridade
xFlag criptografada, constantes são criptografadas com XOR
cPool de constantes, 1230 entradas
fFunções, 112 entradas com bytecode criptografado
ePonto de entrada, índice de função 0
BitTesteChrome
0typeof window.matchMedia === "function"1
1document.elementFromPoint existe1
2typeof window.requestAnimationFrame === "function"1
3typeof window.getComputedStyle === "function"1
4CSS.supports existe1
5navigator.sendBeacon existe1
6document.execCommand existe1
7process.versions.node existe (Node.js)0
ads
enve
init
Líder resolve comoSub-byteExecuta na verdade
FOR_IN_NEXT74FOR_IN_NEXT
FOR_IN_NEXT100MAKE_CLOSURE
ASSIGN_OP_VAR165ASSIGN_OP_VAR
ASSIGN_OP_VAR37JMP
GET_VAR_PROP_C143SET_VAR_POP
GET_VAR_PROP_C23JMP_NULLISH