Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
px-vm — Reverse-Engineering-Toolkit für den Bytecode-VM von PerimeterX, mit einem CFG-basierten Disassembler, einer 5-stufigen Entschlüsselungspipeline, Opcode-Tabellen-Rekonstruktion und Stack-Emulations-Bereinigung für Sicherheitsforschung zur Erkennung von Bot-Detection-Fingerprinting. | Kitploit
Tools/GitHubGitHub/b9ph0met/px-vm
Dynamische Analyse (Sandboxing)IDS/IPS-UmgehungReverse EngineeringWebsicherheitMalware-AnalyseKryptographieBinäranalysePapers & ForschungLernen & BildungAnti-BotFingerabdruck-Spoofing
551426vor 5 MonatenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
GitHub
b9ph0met/px-vm

px-vm

Repository anzeigen

Über

Reverse-Engineering-Toolkit für den Bytecode-VM von PerimeterX, mit einem CFG-basierten Disassembler, einer 5-stufigen Entschlüsselungspipeline, Opcode-Tabellen-Rekonstruktion und Stack-Emulations-Bereinigung für Sicherheitsforschung zur Erkennung von Bot-Detection-Fingerprinting.

Teilen

PerimeterX Auditor VM Analyse

Zusammenfassung

Dieses Repository dokumentiert das Reverse Engineering von PerimeterX auditor.js, einer Bytecode-Virtual Machine, die als sekundäre Fingerprinting-Ebene in der Bot-Erkennungspipeline von PX verwendet wird. Diese Analyse umfasst:

  • Bytecode-Extraktion und 5-stufige Entschlüsselungspipeline
  • Rekonstruktion der Opcode-Tabelle (107 Basis- + 40 Honig- + 24 Padding- + 16 Superinstruction-Gruppen)
  • Entschlüsselung des Konstanten-Pools (1230 Einträge, 1095 verschlüsselte Strings)
  • CFG-basierter Disassembler mit Auflösung von Superinstruction-Sub-Dispatch
  • Stack-Emulationsbereiniger, der lesbaren Pseudocode erzeugt
  • Anti-Analyse-Techniken: Honig-Opcodes, überlappende Instruktionen, Code-Integritäts-Hashing

Hinweis: Dieses Repository behandelt nur eine (statische) VM-Version und dient der Sicherheitsforschung und Analyse. Es enthält keine dynamischen Löser oder Produktionslöser-Implementierungen.

Hintergrund

Am Donnerstag, den 2. April 2026, hat PerimeterX eine neue Bytecode-VM als Teil seiner Bot-Erkennungspipeline eingesetzt.

Was enthalten ist

auditor.js sieht nicht wie ein normales PX-Sensor-Skript aus. Statt der üblichen obfuskierten Eigenschaftszugriffe und Collector-Funktionen:

  • 8 massive Base64-Strings (_fg0 bis _fg7), das verschlüsselte VM-Programm, aufgeteilt auf Variablen
  • Eine XOR-Entschlüsselungsfunktion (_dp) mit einem pro-Site-Schlüssel (_pk), die das Programm-JSON entpackt
  • Ein Fisher-Yates-Shuffle, das die Opcode-Tabelle permutiert, sodass Bytecode-Werte pro Build unterschiedlich sind
  • Eine Dispatch-Schleife mit 107+ Case-Handlern, der VM-Interpreter
  • BigInt-Arithmetik für die RSA-Verschlüsselung des Fingerprint-Outputs
  • Ein Code-Integritäts-Hash (_0x8df7), der den eigenen Quellcode der VM hasht, um einen Entschlüsselungsschlüssel abzuleiten, sodass jede Modifikation die Bytecode-Entschlüsselung stillschweigend bricht

Schritt 1: Bytecode-Extraktion

Das VM-Programm ist auf 8 Variablen aufgeteilt, verkettet und dann über _dp() mit einem pro-Site-XOR-Chiffre, das durch _pk geschlüsselt wird, entschlüsselt:

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

Das Ergebnis ist ein JSON-Objekt mit obfuskierten Zweizeichen-Schlüsselnamen (z.B. "uo" für Seed, "dk" für Nonce). Eine Mapping-Tabelle wandelt sie in Standardnamen um.

node extractor.js
# -> program.json

Programmstruktur

FeldBeschreibung
sSeed (12755), steuert alle kryptografischen Operationen
nNonce (1603730985), pro-Programm-Randomisierung
gGenerator-Flag, aktiviert die Integritäts-Hash-Entschlüsselungsebene
xVerschlüsseltes Flag, Konstanten sind XOR-verschlüsselt
cKonstanten-Pool, 1230 Einträge
fFunktionen, 112 Einträge mit verschlüsseltem Bytecode
eEinstiegspunkt, Funktionsindex 0

Schritt 2: Konstanten-Entschlüsselung

Alle 1095 String-Konstanten sind mit zwei Ebenen verschlüsselt:

Ebene 1: Statischer Murmur-XOR, geschlüsselt mit 4008000571, positionsabhängig.

Ebene 2: PRNG-Stream-XOR mittels eines glibc-LCG, gesät durch Kombination des Programm-Seeds mit dem Index jeder Konstante mittels Knuths multiplikativem Hash.

Vor der Entschlüsselung wird der Seed mit einem Umgebungs-Fingerabdruck (_0xaf48) XOR-verknüpft, einer 8-Bit-Bitmaske, die durch Abfragen von Browser-APIs berechnet wird:

BitTestChrome
0typeof window.matchMedia === "function"1
1document.elementFromPoint existiert1
2typeof window.requestAnimationFrame === "function"1
3typeof window.getComputedStyle === "function"1
4CSS.supports existiert1
5navigator.sendBeacon existiert1
6document.execCommand existiert1
7process.versions.node existiert (Node.js)0

Für Chrome: _0xaf48 = 0b01111111 = 127, ergibt effektiven Seed = 12755 ^ 127 = 12716.

Das bedeutet, dass dasselbe Programm in verschiedenen Umgebungen unterschiedliche Entschlüsselungsergebnisse liefert. Die Ausführung in Node.js vs. Chrome vs. Firefox ergibt unterschiedliche Seeds.

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

Was die Konstanten verraten

Die entschlüsselten Strings zeigen uns genau, was die VM fingerabdruckt:

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

Performance-Timing: navigationStart, domComplete, domLoading, fetchStart, requestStart, responseEnd, secureConnectionStart, serverTiming

RSA-Kryptografie: BigInt, modPow, AQAB (65537 in Base64), modulusLength, shiftLeft, shiftRight, getRandomValues

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

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

Endpunkt-Referenzen: https://fst-ec.perimeterx.net/?id=

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

Schritt 3: Opcode-Tabelle

107 Basis-Opcodes, die die gesamte JavaScript-Sprache abdecken, plus dynamisch generiertes Rauschen:

40 Honig-Opcodes sind alternative Implementierungen von arithmetischen/Vergleichsoperationen mit mathematisch äquivalenten, aber syntaktisch unterschiedlichen Ausdrücken. ADD könnte als (a^b) + 2*(a&b) oder -((-a)-b) oder a-(-b) erscheinen. Jeder Basis-Opcode kann bis zu 3 Varianten haben, die deterministisch aus dem Seed generiert werden. Eine einfache ADD-Instruktion kann als 4 verschiedene Bytecode-Werte innerhalb desselben Programms erscheinen und bricht Mustervergleichsansätze.

24 Padding-Opcodes werden in der Permutation zugewiesen, haben aber keine Handler und werden nie ausgegeben. Sie existieren, um den Opcode-Raum zu erweitern und das Shuffle schwerer umkehrbar zu machen.

16 Superinstruction-Gruppen sind das wichtigste Anti-Analyse-Merkmal. Wenn die Dispatch-Schleife einen Opcode als Superinstruction-Leader auflöst, liest der Handler ein zusätzliches Byte aus dem Bytecode-Stream und dispatcht an einen Sub-Handler. Der Sub-Handler kann eine völlig andere Operation sein:

Leader löst auf alsSub-ByteFührt tatsächlich aus
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

Die Opcode-Tabelle wird mittels Fisher-Yates-Shuffle durch den effektiven Seed gemischt, sodass Bytecode-Werte pro Build unterschiedlich sind.

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

Schritt 4: CFG-Builder

Das Kernstück des Toolkits. cfg.js erstellt einen Kontrollflussgraphen, indem es allen Ausführungspfaden ab PC=0 folgt, jede Instruktion mit dem richtigen Verschlüsselungskontext dekodiert.

Warum kein linearer Disassembler

PX verwendet überlappende Instruktionen an Blockgrenzen. Dieselben Bytes werden auf einem Ausführungspfad als Operanden und auf einem anderen als Opcodes dekodiert, abhängig vom Blockverschlüsselungskontext. Ein linearer Scan dekodiert jede Byteposition nur einmal und übersieht den alternativen Pfad. Der CFG folgt sowohl Fall-Through- als auch Sprungkanten und dekodiert jeden Pfad unabhängig.

Tool herunterladen