Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
px-vm — Boîte à outils de rétro-ingénierie pour la machine virtuelle bytecode de PerimeterX, comprenant un désassembleur basé sur CFG, un pipeline de déchiffrement à 5 couches, une reconstruction de table d'opcodes, et un nettoyeur d'émulation de pile pour la recherche en sécurité sur la détection des bots par empreinte numérique. | Kitploit
Outils/GitHubGitHub/b9ph0met/px-vm
Analyse Dynamique (Sandboxing)Évasion IDS/IPSRétro-ingénierieSécurité WebAnalyse de MalwareCryptographieAnalyse de BinairesArticles et RechercheApprentissage et ÉducationAnti-BotUsurpation d'Empreinte Numérique
551435il y a 6 moisVérifié par Kitploit
GitHub
b9ph0met/px-vm

px-vm

Voir le dépôt

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →

À propos

Boîte à outils de rétro-ingénierie pour la machine virtuelle bytecode de PerimeterX, comprenant un désassembleur basé sur CFG, un pipeline de déchiffrement à 5 couches, une reconstruction de table d'opcodes, et un nettoyeur d'émulation de pile pour la recherche en sécurité sur la détection des bots par empreinte numérique.

Partager

Analyse de la VM Auditor de PerimeterX

Résumé

Ce dépôt documente le rétro-ingénierie de auditor.js de PerimeterX, une machine virtuelle à bytecode utilisée comme couche secondaire d'empreinte digitale dans la chaîne de détection de bots de PX. Cette analyse couvre :

  • Extraction du bytecode et pipeline de déchiffrement à 5 couches
  • Reconstruction de la table d'opcodes (107 de base + 40 leurres + 24 de remplissage + 16 groupes de super-instructions)
  • Déchiffrement du pool de constantes (1230 entrées, 1095 chaînes chiffrées)
  • Désassembleur basé sur un graphe de flot de contrôle (CFG) avec résolution de sous-dispatch de super-instructions
  • Nettoyeur par émulation de pile produisant un pseudo-code lisible
  • Techniques d'anti-analyse : opcodes leurres, instructions qui se chevauchent, hachage d'intégrité du code

Note : Ce dépôt ne couvre qu'une seule version (statique) de la VM et est destiné à la recherche en sécurité et à des fins d'analyse. Il n'inclut pas de solveurs dynamiques ni d'implémentations de solveurs de production.

Contexte

Le jeudi 2 avril 2026, PerimeterX a déployé une nouvelle VM à bytecode dans le cadre de leur pipeline de détection de bots.

Contenu

auditor.js ne ressemble pas à un script de capteur PX normal. Au lieu des habituelles recherches de propriétés obfusquées et des fonctions de collecte :

  • 8 chaînes base64 massives (_fg0 à _fg7), le programme VM chiffré réparti entre des variables
  • Une fonction de déchiffrement XOR (_dp) avec une clé par site (_pk) qui décompresse le JSON du programme
  • Un mélange de Fisher-Yates qui permute la table d'opcodes, de sorte que les valeurs de bytecode diffèrent d'une build à l'autre
  • Une boucle de dispatch avec 107+ gestionnaires de cas, l'interpréteur de la VM
  • Des calculs BigInt pour le chiffrement RSA de la sortie de l'empreinte
  • Un hachage d'intégrité du code (_0x8df7) qui hache le propre code source de la VM pour dériver une clé de déchiffrement, de sorte que toute modification interrompt silencieusement le déchiffrement du bytecode

Étape 1 : Extraction du bytecode

Le programme de la VM est réparti sur 8 variables, concaténées, puis déchiffrées via _dp() à l'aide d'un chiffrement XOR par site avec la clé _pk :

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("");
}

Le résultat est un objet JSON avec des noms de clés obfusqués de deux caractères (par ex., "uo" pour la graine, "dk" pour le nonce). Une table de correspondance les convertit en noms standards.

node extractor.js
# -> program.json

Structure du programme

ChampDescription
sGraine (12755), pilote toutes les opérations cryptographiques
nNonce (1603730985), randomisation par programme
gDrapeau générateur, active la couche de déchiffrement par hachage d'intégrité
xDrapeau chiffré, les constantes sont chiffrées par XOR
cPool de constantes, 1230 entrées
fFonctions, 112 entrées avec bytecode chiffré
ePoint d'entrée, index de fonction 0

Étape 2 : Déchiffrement des constantes

Les 1095 chaînes constantes sont toutes chiffrées avec deux couches :

Couche 1 : XOR statique murmur avec la clé 4008000571, dépendante de la position.

Couche 2 : XOR de flux PRNG utilisant un LCG glibc, initialisé en combinant la graine du programme avec l'index de chaque constante via le hachage multiplicatif de Knuth.

Avant le déchiffrement, la graine est XORée avec une empreinte environnementale (_0xaf48), un masque de 8 bits calculé en interrogeant les API du navigateur :

BitTestChrome
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

Pour Chrome : _0xaf48 = 0b01111111 = 127, soit une graine effective = 12755 ^ 127 = 12716.

Cela signifie que le même programme produit des résultats de déchiffrement différents selon l'environnement. L'exécuter sous Node.js vs Chrome vs Firefox donne des graines différentes.

node decrypt_constants.js
# -> program_decrypted.json, constants_table.txt

Ce que révèlent les constantes

Les chaînes déchiffrées nous indiquent exactement ce que la VM prend comme empreinte :

Empreinte du navigateur : screenWidth, screenHeight, innerWidth, innerHeight, devicePixelRatio, colorDepth, platform, userAgent, language, timezone, timezoneOffset, forcedColors, highContrast

Chronométrage des performances : navigationStart, domComplete, domLoading, fetchStart, requestStart, responseEnd, secureConnectionStart, serverTiming

Cryptographie RSA : BigInt, modPow, AQAB (65537 en base64), modulusLength, shiftLeft, shiftRight, getRandomValues

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

Noms de champs PX : mtr, tst, mst, enc, sbx, fstec, pdc, prb, wvi, wva, pti, dis, los, cv, sc, jd, ads, enve, init

Références de points de terminaison : https://fst-ec.perimeterx.net/?id=

Anti-débogueur : _CMP_RCX_07;_JNZ_0x0A_EB_CC, CC|CD-04|BREAKPOINT-005

Étape 3 : Table d'opcodes

107 opcodes de base couvrant l'ensemble du langage JavaScript, plus du bruit généré dynamiquement :

40 opcodes leurres sont des implémentations alternatives des opérations arithmétiques/comparaisons utilisant des expressions mathématiquement équivalentes mais syntaxiquement différentes. ADD pourrait apparaître comme (a^b) + 2*(a&b) ou -((-a)-b) ou a-(-b). Chaque opcode de base peut avoir jusqu'à 3 variantes, générées de manière déterministe à partir de la graine. Une simple instruction ADD peut apparaître sous 4 valeurs de bytecode différentes au sein du même programme, brisant les approches par reconnaissance de motifs.

24 opcodes de remplissage sont alloués dans la permutation mais n'ont pas de gestionnaires et ne sont jamais émis. Ils existent pour agrandir l'espace des opcodes et rendre le mélange plus difficile à inverser.

16 groupes de super-instructions sont la fonctionnalité anti-analyse la plus importante. Lorsque la boucle de dispatch résout un opcode en un leader de super-instruction, le gestionnaire lit un octet supplémentaire dans le flux de bytecode et se dirige vers un sous-gestionnaire. Le sous-gestionnaire peut être une opération complètement différente :

Leader résolu enSous-octetExécute réellement
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

La table d'opcodes est mélangée via Fisher-Yates avec une graine effective, de sorte que les valeurs de bytecode diffèrent d'une build à l'autre.

node build_opcodes.js
# -> opcode_table.json, opcode_table.txt

Étape 4 : Constructeur de CFG

Le cœur de la boîte à outils. cfg.js construit un graphe de flot de contrôle en suivant tous les chemins d'exécution à partir de PC=0, en décodant chaque instruction avec le contexte de chiffrement correct.

Pourquoi pas un désassembleur linéaire

Télécharger l’outil