Skip to content
KitploitKITPLOIT
ToolsBlog
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.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
zyrox — Zyrox: LLVM based, compile-time obfuscator plugin. | Kitploit
Tools/GitHubGitHub/peterhackz/zyrox
Static AnalysisCode AnalysisReverse EngineeringBinary AnalysisPapers & ResearchLearning & EducationLearning Paths & Courses
GitHubpeterhackz/zyrox

zyrox

Zyrox: LLVM based, compile-time obfuscator plugin.

Repository anzeigen
9411vor 6 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
Webseite

Zyrox LLVM Obfuskator

llvm-Compile- und Link-Zeit-Plugin zur Obfuskierung von nativem Code

Warum

why not ¯\_(ツ)_/¯

Eines meiner größten Projekte, bei dem ich viel über LLVM-Interna, Binärformate, Assembly und Obfuskierungstechniken gelernt habe.

Ich glaube, dass Lernen durch Bauen der beste Weg ist, zu lernen, daher habe ich dieses Projekt gebaut, um mehr über diese Themen zu erfahren.

Forschung

Ich habe 4 Blogbeiträge geschrieben, die die Konzepte hinter Zyrox erklären:

  • Teil I: Bau von Zyrox: Ein benutzerdefinierter LLVM-Obfuskator
  • Teil II: Kontrollflussabflachung
  • Teil III: Verschlüsselte Sprungtabellen
  • Teil IV: Das Finale

Diese Teile gehen tiefer als diese README und sind auf jeden Fall lesenswert, wenn Sie sich für das Thema interessieren.

Build

Aus der Vorlage (Schnellstart, empfohlen)

Dies ist für diejenigen gedacht, die Zyrox schnell testen möchten oder lernen möchten, wie man es in ein CMake-Projekt integriert.

Folgen Sie den Schritten im Zyrox Template-Repository.

Aus dem Quellcode

llvm installieren:

root@kitploit:~
sudo apt update
sudo apt install llvm-18 llvm-18-dev clang-18

Zyrox klonen und kompilieren:

root@kitploit:~
git clone --recurse-submodules https://github.com/PeterHackz/zyrox.git
cd zyrox
cmake -S . -B build -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++
cmake --build build --parallel 4

Python (Post-Compile) Plugin-Einrichtung

Stellen Sie sicher, dass python3 und pip installiert sind.

Einrichten einer Umgebung (empfohlen)

root@kitploit:~
# Create a virtual environment
python3 -m venv .venv

# Activate the env
source .venv/bin/activate

pip install -r requirements.txt

Global installieren

root@kitploit:~
pip install -r requirements.txt

Verwendung

Schnellverwendung

root@kitploit:~
clang -O0 -flto=full -c main.c -o out/main.o
clang -flto=full -fuse-ld=lld -Wl,--load-pass-plugin=./build/libzyrox.so out/main.o -o out/main

Nach der Obfuskierung führen Sie PyPlugin.py aus, um Sprungtabellen zu verschlüsseln:

root@kitploit:~
# if you installed dependencies in a virtual environment, activate it first:
source .venv/bin/activate
#  then run with:
python PyPlugin.py --in=<input_file> [--out=<output_file>] [--tables=<zyrox_tables_file>] [--android]

Mit CMake

Schauen Sie sich das Zyrox Template-Repository für ein Beispiel einer CMake-Integration an.

Kontakte

Mir ist klar, dass dies ein komplexes Thema ist, und dieses Projekt diente hauptsächlich Bildungszwecken sowie dem BSD Brawl. Wenn Sie Fragen haben oder einfach nur plaudern möchten, können Sie mich gerne kontaktieren:

  • Discord: @s.b
  • Email: [email protected] or [email protected]
  • Discord Server

Jede Hilfe, sei es durch Pull Requests oder Issues, ist willkommen!

Wie es funktioniert

ZyroxPlugin.cpp registriert den Pass, verlinkt dann siphash (mehr dazu später) und ruft StringEncryption auf, um Strings zu verschlüsseln.

Der Grund, warum wir Strings früh verschlüsseln, ist, dass die Entschlüsselungslogik später ebenfalls obfuskiert wird.

dann ruft es ModuleUtils::ExpandCustomAnnotations und QuickConfig::RegisterPasses auf, um alle __attribute__((annotate("...")))-Ausdrücke zu parsen und die QuickJs-Konfiguration (in ZyroxConfig.js) auszuführen.

Jede Funktion wird durch Aufruf von Zyrox::RunOnFunction in ZyroxCore.cpp obfuskiert; weitere Dokumentation dazu wird in Zukunft bereitgestellt.

Zusätzliche Hilfsfunktionen

Switch-Anweisungen erzeugen Sprungtabellen und PHI-Knoten sind lästig zu handhaben, daher verwenden wir FunctionUtils und BasicBlockUtils, um diese zu glätten (in if-Anweisungen) bzw. herabzustufen.

Passes

oh Mann, wo soll ich anfangen

  • Basic Block Splitter
  • Control Flow Flattening
  • Indirect Branching
  • Simple Indirect Branching
  • Mixed Boolean Arithmetic

Alle js-Plugin-Argumente sind in index.d.ts und werden daher in dieser Dokumentation nicht behandelt.

für die Annotationsdokumentation klicken Sie hier

Basic Block Splitter

Dieser Pass teilt und mischt einen Basisblock in kleinere Blöcke auf. Angenommen, wir haben Folgendes:

root@kitploit:~
int __test_fn(int x)
{
    if (x == 2) {
        printf("x is 2\n");
    } else {
        printf("x is not 2!, x is: %d\n", x);
    }
    return x + 4 * x - 2 / 4;
}

was zu Folgendem kompiliert wird:

root@kitploit:~
define internal i32 @__test_fn(i32 noundef %0) #0 !zyrox !8 !obfuscated !11 {
  %2 = alloca i32, align 4
  store i32 %0, ptr %2, align 4
  %3 = load i32, ptr %2, align 4
  %4 = icmp eq i32 %3, 2
  br i1 %4, label %5, label %7

5:                                                ; preds = %1
  %6 = call i32 (ptr, ...) @printf(ptr noundef @.str.1)
  br label %10

7:                                                ; preds = %1
  %8 = load i32, ptr %2, align 4
  %9 = call i32 (ptr, ...) @printf(ptr noundef @.str.2, i32 noundef %8)
  br label %10

10:                                               ; preds = %7, %5
  %11 = load i32, ptr %2, align 4
  %12 = load i32, ptr %2, align 4
  %13 = mul nsw i32 4, %12
  %14 = add nsw i32 %11, %13
  %15 = sub nsw i32 %14, 0
  ret i32 %15
}

bei Verwendung des Basic Block Splitters mit dieser Konfiguration:

root@kitploit:~
z.RegisterPass(ObfuscationType.BasicBlockSplitter, {
    PassIterations: 1,
    "BasicBlockSplitter.SplitBlockChance": 100,
    "BasicBlockSplitter.SplitBlockMinSize": 2,
    "BasicBlockSplitter.SplitBlockMaxSize": 5,
});

wird daraus:

root@kitploit:~
define internal i32 @__test_fn(i32 noundef %0) #0 !zyrox !8 !obfuscated !11 {
  %2 = alloca i32, align 4
  store i32 %0, ptr %2, align 4
  %3 = load i32, ptr %2, align 4
  %4 = icmp eq i32 %3, 2
  br i1 %4, label %5, label %14

5:                                                ; preds = %1
  %6 = call i32 (ptr, ...) @printf(ptr noundef @.str.1)
  br label %7

7:                                                ; preds = %14, %5
  %8 = load i32, ptr %2, align 4
  %9 = load i32, ptr %2, align 4
  %10 = mul nsw i32 4, %9
  %11 = add nsw i32 %8, %10
  br label %12

12:                                               ; preds = %7
  %13 = sub nsw i32 %11, 0
  ret i32 %13

14:                                               ; preds = %1
  %15 = load i32, ptr %2, align 4
  %16 = call i32 (ptr, ...) @printf(ptr noundef @.str.2, i32 noundef %15)
  br label %7
}

Jetzt wird es für eine so kleine Funktion nicht viel anders aussehen, aber beachten Sie, wie es einen Basisblock aufgeteilt hat? Dies ist hilfreich in Kombination mit anderen Passes wie Control Flow Flattening.

Control Flow Flattening

Oh, Mann, dieser Pass hat die meisten Funktionen von allen lol. Ich werde zuerst erklären, wie es funktioniert, dann seine Konfiguration. Angenommen, wir haben diesen Code:

root@kitploit:~
LABEL_A: bool b = x == 2;
         IF EQ: goto LABEL_B
         goto LABEL_C
LABEL_B  do_stuff()
LABEL_C  do_other_stuff()
         goto LABEL_A

jeder Basisblock (A, B und C) erhält einen eindeutigen Dispatcher-Status, Beispiel: (vereinfacht)

root@kitploit:~
states = {
    1: LABEL_A,
    2: LABEL_B,
    3: LABEL_C,
};

dann fügen wir einen Dispatcher-Block ein, der alles steuert, und der Code wird zu:

root@kitploit:~
         int state = 0;
LABEL_D  goto LABEL_CA // dispatcher label jumps to first condition block, label condition A
LABEL_CA if state == 1: goto LABEL_A
         // if not 1, go to check if it is label B (fallback)
LABEL_CB if state == 2: goto LABEL_B
LABEL_CC if state == 3: goto LABEL_CC
         // unreachable
         goto LABEL_D
LABEL_A: bool b = x == 2;
         // IF EQ: goto LABEL_B
         // goto LABEL_C
         state = 2 if b else 3 // update state for the block we want and back to dispatcher
         goto LABEL_D
LABEL_B  do_stuff()
LABEL_C  do_other_stuff()
         state = 1
         goto LABEL_D

Nun hat dies einige Fehler, die der Obfuskator behebt. Wie Sie sehen, ist es aufgrund einer einzelnen Dispatcher-Variable einfach, dies zu deobfusken, da wir wissen, wohin ein Block geht, nachdem er den Status gesetzt hat. Einfach zu beheben!

root@kitploit:~
z.RegisterPass(ObfuscationType.ControlFlowFlattening, {
    PassIterations: 1,
    "ControlFlowFlattening.UseFunctionResolverChance": 60,
    "ControlFlowFlattening.UseGlobalStateVariablesChance": 60,
    "ControlFlowFlattening.UseOpaqueTransformationChance": 40,
    "ControlFlowFlattening.UseGlobalVariableOpaquesChance": 80,
    "ControlFlowFlattening.UseSipHashedStateChance": 40,
    "ControlFlowFlattening.CloneSipHashChance": 80,
});

Gehen wir die Optionen einzeln durch:

  • UseFunctionResolverChance: injiziert eine Funktion zur Überprüfung des Status, sodass statt if (state == expected_state) nun if (injected_resolver(state)) ausgeführt wird. Beispiel:
    root@kitploit:~
    bool __fastcall cff_resolve_state_check_3585(__int64 a1)
    {
        return a1 == 0x288A6154F8A5E3E2LL;
    }
    
  • UseGlobalStateVariablesChance: speichert den zu vergleichenden Statuswert in einer globalen Variable:
    root@kitploit:~
    bool __fastcall cff_resolve_state_check_506(__int64 a1)
    {
        return a1 == qword_1B20D8;
    }
    
  • UseOpaqueTransformationChance: obfuskiert die Prüfung in eine Transformation, die nur für einen bestimmten Status wahr wird:
    root@kitploit:~
    bool __fastcall cff_resolve_state_check_7901(__int64 a1)
    {
        return ((((a1 ^ 0xEA9E45BB6099BC6ELL) + qword_1C64D8) << qword_1A63F0)
                   | (((a1 ^ 0xEA9E45BB6099BC6ELL)
                       + qword_1C64D8) >> qword_1ACE98)) == qword_1B0B80;
    }
    
  • UseGlobalVariableOpaquesChance: verwendet eine globale Variable anstelle einer Zahl bei der Verwendung von , wie Sie im obigen Beispiel bemerkt haben (, , ).

Indirect Branching

Angenommen, wir haben diesen Code:

root@kitploit:~
if (x == 2) goto LABEL_A
            goto LABEL_B
LABEL_A:    do_stuff()
LABEL_B:    // ...

es würde transformiert werden in:

root@kitploit:~
@global jump_table = {0, &LABEL_A, &LABEL_B};
if (x == 2) goto jump_table[0] + @inline(decrypt(jump_table[1]));
            goto jump_table[0] + @inline(decrypt(jump_table[2]));
// ...

Wenn dieser Pass verwendet wird, gibt das Plugin eine Datei zyrox_tables.txt aus, die von PyPlugin.py verwendet wird.

PyPlugin.py verschlüsselt die Sprungtabellen und patcht Relocation-Einträge, dann wird der Relocator auf jump_table[0] jeder Tabelle zeigen. Ein Relocator macht im Grunde Folgendes: target.writePointer(base.add(value)), also setzen wir den Wert auf 0, damit der Relocator uns die Basisadresse gibt und sie zur Laufzeit in die Sprungtabelle einfügt, und wir verwenden sie zusammen mit dem goto, um die Laufzeitadresse zu generieren. Im arm32 Thumb-Modus fügt der Pass automatisch | 1 nach der Entschlüsselung hinzu.

Um PyPlugin.py zu verwenden, gehen Sie einfach wie folgt vor:

(If using venv, activate it first)

root@kitploit:~
python3 PyPlugin.py --in <out_obfuscated_file> --android

Die Übergabe von --android ist wichtig, wenn Sie die arm64-Version anvisieren, da die x86_64-Version eine andere Relocator-Signatur hat.

Sie können auch --out übergeben (standardmäßig wird dieselbe Datei wie bei --in verwendet) und --tables (standardmäßig zyrox_tables.txt).

Simple Indirect Branching

Während Indirect Branching großartig erscheint, geht es auch mit einem Leistungseinbußen einher, da es zur Laufzeit Zeiger entschlüsselt. Dies ist eine einfache Version, die die Leistung nicht beeinträchtigt, bei der Folgendes:

root@kitploit:~
if (x == 2) goto LABEL_A
            goto LABEL_B
LABEL_A:    do_stuff()
LABEL_B:    // ...

wird zu:

root@kitploit:~
            @stack jump_table = {&LABEL_B, &LABEL_A}
            goto jump_table[!(x == 2)]
LABEL_A:    do_stuff()
LABEL_B:    // ...

Obwohl dies einfach und leicht zu brechen erscheint (da stimme ich zu), reicht es aus, um IDA und Ghidra zu brechen, ohne die Leistung zu beeinträchtigen.

Mixed Boolean Arithmetic

auch bekannt als MBA Sub (Mixed Boolean Arithmetic Substitution), wandelt einfache Operationen in komplexe um, die dieselbe Ausgabe liefern. Es verwendet eine vordefinierte Menge. Beispiel:

root@kitploit:~
a ^ b = (~a & b) | (a & ~b)
b * c = (((b | c) * (b & c)) + ((b & ~c) * (c &  ~b)))
r = rand(); c = b + r; a = a + c; a = a - r

Die vollständige Liste finden Sie in Passes/MBASub.cpp, wenn Sie interessiert sind.

Zyrox Annotations

Schauen Sie einfach in index.d.ts. Der Annotation-Parser verwendet dieselbe Reihenfolge. Um eine Funktion zu markieren, gehen Sie einfach wie folgt vor:

root@kitploit:~
__attribute__((annotate("ibr:1,100"))) void hello_world () {
    some_hello ();
}

Annotationscodes:

  • Basic Block Splitter: bbs
  • Control Flow Flattening: cff
  • Indirect Branching: ibr
  • Simple Indirect Branching: sibr
  • Mixed Boolean Arithmetic: mba

Beispiel: in index.d.ts sehen wir:

root@kitploit:~
{
    "BasicBlockSplitter.SplitBlockMinSize"?: number;
    "BasicBlockSplitter.SplitBlockMaxSize"?: number;
    "BasicBlockSplitter.SplitBlockChance"?: number;
};

Nun kommt der wichtige Punkt: Das erste Argument und das gemeinsame für alle Passes ist PassIterations, daher wird es das erste Argument in den Annotationen sein. Um etwas mit bbs zu annotieren, machen wir Folgendes:

root@kitploit:~
__attribute__((annotate("bbs:1,15,30,100"))) void hello_world () {
    some_hello ();
}

Dies bedeutet: Basic Block Splitter einmal auf hello_world ausführen mit min-Größe = 15, max-Größe = 30 und Chance = 100.

Sie können auch Passes kombinieren:

root@kitploit:~
__attribute__((annotate("bbs:1,15,30,100 ibr:1,100 sibr:1,100"))) void hello_world () {
    some_hello ();
}

Dies bedeutet, Basic Block Splitter dann Indirect Branching und dann Simple Indirect Branching auf hello_world ausführen. Sie werden in der Reihenfolge der Definition von links nach rechts ausgeführt.

Tool herunterladen
UseOpaqueTransformationChance
qword_1C64D8
qword_1A63F0
qword_1ACE98
  • UseSipHashedStateChance: verwendet eine kleine angepasste siphash-Funktion zur Überprüfung des Status. Aus if (state == 23872) wird so etwas wie if (siphash(state) == 3874872081), was es schwieriger macht herauszufinden, wohin ein Block springt. Der Block würde state = 23872 setzen, wenn die Bedingung, wohin er geht, gehasht ist. Jeder siphash-Aufruf verwendet zufällige Werte, um die Emulation zu erschweren.
  • CloneSipHashChance: klont und versucht nach Möglichkeit, die siphash-Funktion zu inlinen, sodass mehr als ein Geschwisterchen davon entsteht, was das Hooking einer einzelnen Funktion unzureichend macht. Es ist sehr zu empfehlen, dies zu verwenden, da es nur die Binärgröße erhöht und die Leistung nicht beeinträchtigt.