Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
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.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
datadome-vm — Reverse Engineering der neuen Datadome VM 🔥 | Kitploit
Tools/GitHubGitHub/xkiian/datadome-vm
Dynamische Analyse (Sandboxing)Reverse EngineeringBinäranalyseLernen & BildungAnti-BotCAPTCHA-Umgehung
GitHubxkiian/datadome-vm

datadome-vm

Reverse Engineering der neuen Datadome VM 🔥

Repository anzeigen
1251961vor 7 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 →
Teilen

DataDome VM-Analyse

Zusammenfassung

Dieses Repository dokumentiert die erste öffentliche Version der In-Browser-JavaScript-VM (virtuelle Maschine) von DataDome, die in deren CAPTCHA/Interstitial-Ablauf verwendet wird. Diese Analyse umfasst:

  • Mechanismen zum Laden und Dekodieren von Bytecode
  • VM-Speicherlayout und -Architektur
  • Ein Proof-of-Concept-Disassembler
  • Notizen zur Kontrollflussanalyse

Hinweis: Dieses Repository behandelt nur eine (statische) VM-Version und ist für Sicherheitsforschung und Analysezwecke gedacht. Es enthält keine dynamischen Löser oder Produktionslöser-Implementierungen.

Hintergrund

Am 14. Januar 2026 begann DataDome, eine neue VM-basierte Komponente in ihrem Client-Tag auszuliefern.

Deobfuskierung

Der VM-Code wurde aus der Captcha-Herausforderung in vm.js extrahiert (in diesem Repository verfügbar).

Der erste Schritt war die Deobfuskierung des Skripts:

Die Obfuskierung ist unkompliziert: Jede Variable auswerten und durch ihren tatsächlichen Wert ersetzen. Ein Deobfuskierungs-Skript ist in deobf.js verfügbar.

Erste Analyse

Das Ausführen des deobfuskierten Codes (out.js) in DevTools zeigt die erwartete Ausgabe der VM:

Die Ausgabe ist ein JSON-Objekt, das zwei Zahlen und einen String enthält. Tauchen wir nun in die tatsächliche VM-Implementierung ein.

Bytecode-Dekodierung

Am Anfang der Funktion Q.exports sehen wir, wie der Bytecode dekodiert wird:

  1. Der Eingabestring wird base64-dekodiert
  2. Ein Array der Länge 129.263 wird erstellt
  3. Jeder Index wird gegen einen bestimmten Bereich geprüft:
    • Wenn der Index in den Bereich fällt, wird der Wert dekodiert
    • Andernfalls wird eine Zufallszahl zurückgegeben (mithilfe von B(), einem Pseudozufallszahlengenerator) -> D enthält den dekodierten Bytecode mit etwas zufälligem "Rauschen"

VM-Architektur

Weiter unten befindet sich der VM-Einstiegspunkt: eine Funktion mit zwei Parametern A (der Bytecode) und Q (ein leeres Wörterbuch für die Fehlerbehandlung).

Speicherlayout

Der interessanteste Aspekt dieser VM ist ihre Architektur: alles befindet sich in einem einzigen Array (A). Dieses Array enthält:

  • Den Stack
  • Register
  • Opcode
  • Den Bytecode selbst
  • Den Instruktionszeiger

Dieses Design spiegelt echte Computerarchitektur mit unterschiedlichen Speicherbereichen wider. Der nächste Schritt ist die Kartierung der einzelnen Offsets, um zu verstehen, was wo gespeichert ist:

var stack_pointer = 4593
var instruction_pointer = 4635
var frame_base_pointer = 4674
var last_result = 4633
var exit_flag = 4656
var current_opcode_handler = 4685
var current_opcode_id = 4675
var stack_offset = 124482
var vm_start = 5258

Mit diesen kartierten Offsets wird die VM-Struktur klar.

Hilfsfunktionen

Die VM beginnt mit einer Sammlung von Hilfsfunktionen, die Folgendes behandeln:

  • Einlesen typisierter Werte vom Stack
  • Verschieben von Daten zwischen dem Stack und "Registern"

VM-Initialisierung

Nach den Hilfsfunktionen initialisiert die VM Kernwerte:

  • Alle Zeiger (Stack, Instruktion, Frame-Basis)
  • Exit-Flag
  • Letztes Ergebnisregister

Unterhalb der Initialisierung befinden sich alle Instruktionshandler.

Die Dispatcher-Schleife

Der Dispatcher ist die Haupt-VM-Schleife, die läuft, bis `exit_flag` gesetzt ist:
  • I repräsentiert die aktuelle Instruktion
  • P ist der tatsächliche Offset in das Array (unter Berücksichtigung der Obfuskierung)
  • Die Schleife setzt die aktuelle Instruktion auf current_opcode_handler und aktualisiert current_opcode_id

Opcode-Implementierung

Wie Opcodes funktionieren

Hier ist ein einfaches Beispiel eines Opcode-Handlers:

  1. Holt einen unmittelbaren Wert aus dem Bytecode
  2. Holt den obersten Wert vom Stack
  3. Führt eine Operation aus (z. B. %= oder ^=)
  4. Ruft am Ende die Funktion fetch() auf

Interessante Opcodes

Opcode 4919: Funktions-/Closure-Erstellung

Einer der komplexesten Opcodes erstellt Closures/Funktionen:

A[4919] = function () {
    var Q = readUint8();  // Number of expected arguments
    var B = [];
    for (var E = readUint8(), D = 0; D < E; D++) {
        var g = readUint8();
        var a = A[A[frame_base_pointer] + g];
        B.push(a);  // Capture variables from current scope
    }
    var h = A[instruction_pointer] + 3;  // Save address of function body
    A[A[stack_pointer]++] = function (E) {
        // Set up new stack frame when called
        var e = A[stack_pointer] - E;
        while (E < Q) {
            A[e + E++] = undefined;  // Fill missing arguments with undefined
        }
        A[stack_pointer] = e + Q;
        for (var D = 0; D < B.length; D++) {
            var g = B[D];
            A[A[stack_pointer]++] = g;  // Push captured variables
        }
        A[e - 2] = A[frame_base_pointer];  // Save old frame pointer
        A[e - 1] = A[instruction_pointer];  // Save return address
        A[frame_base_pointer] = e;
        A[instruction_pointer] = h;  // Jump to function body
    };
    fetch();
};

Dieser Opcode:

  1. Liest die erwartete Argumentanzahl
  2. Erfasst Variablen aus dem aktuellen Gültigkeitsbereich (Closure)
  3. Erstellt eine Funktion, die einen neuen Stack-Frame mit korrekten Aufrufkonventionen einrichtet
  4. Behandelt fehlende Argumente, indem sie mit undefined gefüllt werden
  5. Speichert die Rücksprungadresse und den Frame-Zeiger für ordnungsgemäße Rückgaben

Opcode 5003: Dynamischer Funktionsaufruf

Dieser Opcode erstellt einen Wrapper für Funktionsaufrufe, der sowohl normale als auch Konstruktoraufrufe behandelt:

A[5003] = function () {
    var Q = A[--A[stack_pointer]];  // POP function
    var B = A[--A[stack_pointer]];  // POP 'this' context

    function E(e) {  // e = argument count
        var D = A[stack_pointer];
        var g = A.slice(D - e, D);  // Get arguments from stack
        if (this instanceof E) {
            // Constructor call (new E(...))
            g.unshift(null);
            var h = Function.prototype.bind.apply(Q, g);
            A[stack_pointer] -= e;
            try {
                a = new h();
            } catch (A) {
                a = A.message;
            }
            A[A[stack_pointer]++] = a;
        } else {
            // Regular function call
            var t;
            try {
                t = Q.apply(B, g);
            } catch (A) {
                t = A.message;
            }
            A[stack_pointer] -= e + 2;
            A[A[stack_pointer]++] = t;
        }
    }

    A[A[stack_pointer]++] = E;
    fetch();
};
Tool herunterladen