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
cfgrip — Disassembliert PE/ELF x86/x64-Binärdateien, löst indirekte Verzweigungen über GOT, Sprungtabellen und Registerverfolgung auf und exportiert den vollständigen Kontrollflussgraphen als strukturiertes JSON für statische Analyse und Patchen. | Kitploit
Tools/GitLabGitLab/binaryhardening/cfgrip
Statische AnalyseReverse EngineeringDebuggerBinäranalyse
GitLabbinaryhardening/cfgrip

cfgrip

Disassembliert PE/ELF x86/x64-Binärdateien, löst indirekte Verzweigungen über GOT, Sprungtabellen und Registerverfolgung auf und exportiert den vollständigen Kontrollflussgraphen als strukturiertes JSON für statische Analyse und Patchen.

Repository anzeigen
25vor 1 MonatNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Webseite
Teilen

cfgrip

PE/ELF x86/x64 CFG-Extraktor. Nimmt eine Binärdatei entgegen, zerlegt sie, löst jeden Sprung und Aufruf auf (GOT, Sprungtabellen, Registerverfolgung) und exportiert den vollständigen Kontrollflussgraphen als strukturiertes JSON.

warum

Du musst genau wissen, wohin jeder Zweig führt. Nicht zum Lesen – zum Patchen. Füttere das JSON in Zydis oder AsmJit, lokalisiere die genaue Anweisung, die du hooken oder ändern musst, und schreibe zurück. Anti-Cheat-Teams nutzen es, um Spielbinärdateien zu kartieren. RE-Leute verwenden es, um Code in ihre eigenen Analyse-Pipelines zu heben. Softwareanalysten verfolgen Ausführungspfade, ohne die Binärdatei auszuführen.

cfgrip gibt dir die Karte. Was du damit machst, bleibt dir überlassen.

was es extrahiert

Für jede Binärdatei, die cfgrip verarbeitet, erzeugt es:

  • Funktionsliste mit Adressen, optionaler Endadresse (aus .pdata), Namen (Einstiegspunkt, Exporte, entdeckte) und Thunk-Kennzeichnung (PLT-Stubs)
  • Basisblöcke pro Funktion – Anweisungssequenzen, die durch Verzweigungen, Aufrufe, Rückkehren oder Fallen beendet werden
  • Kontrollflusskanten – Nachfolger jedes Blocks (direkte Verzweigungen, Fall-through, indirekte Ziele)
  • Importtabelle – aufgelöste Bibliotheksimporte mit Adressen
  • Indirekte Ziele – GOT-aufgelöste Aufrufe, Sprungtabelleneinträge, registerverfolgte Verzweigungen und unaufgelöste (als solche gekennzeichnet)
  • Funktionsgrenzen – mehrere Erkennungsdurchläufe (Prologmuster, Aufrufziele, Tail-Calls, .pdata-Einträge, Datenbereichs-Funktionszeiger) für maximale Abdeckung

Mit --clean zusätzlich:

  • Springt über jmp → jmp-Ketten zu direkten Kanten
  • Entfernt tote Basisblöcke (keine eingehenden Kanten)
  • Kommentiert jede Anweisung mit stack_offset (RSP-Delta vom Funktionsanfang)
  • Erstellt einen xrefs-Abschnitt, der jedes Aufruf-/Sprungziel zurück zu seinen Aufrufern abbildet

Indirekte Verzweigungen werden behandelt durch:

  1. Überprüfen der GOT (Global Offset Table) auf bekannte Importe
  2. Rückwärtsscannen nach LEA-Anweisungen, um Sprungtabellen zu lokalisieren, dann Lesen der Tabelleneinträge
  3. Rückwärtige Registerverfolgung durch mov/lea-Ketten, um konkrete Adressen zu finden

Verwendung

root@kitploit:~
cfgrip [--subs-only] [--clean] <binary>

Übergib eine Binärdatei, erhalte <binary>.cfg als Ausgabe.
Verwende --subs-only, um nur Funktionen zu extrahieren, die vom Einstiegspunkt aus über den Aufrufgraphen erreichbar sind – überspringt unerreichbare Exporte und Prologkandidaten.
Verwende --clean, um Sprungdurchlauf, tote-Block-Entfernung, Stack-Offset-Verfolgung und Cross-Referenz-Analyse anzuwenden.


Beispiel:

root@kitploit:~
cfgrip.exe tests\example1.exe
format: PE
arch: x86-64
entry: 0x1400054bc
imports: 85
  0x140020000 EncodePointer (KERNEL32.dll)
  0x140020008 DecodePointer (KERNEL32.dll)
  0x140020010 EnterCriticalSection (KERNEL32.dll)
  0x140020018 LeaveCriticalSection (KERNEL32.dll)
  0x140020020 InitializeCriticalSectionEx (KERNEL32.dll)
  0x140020028 DeleteCriticalSection (KERNEL32.dll)
  0x140020030 MultiByteToWideChar (KERNEL32.dll)
  0x140020038 WideCharToMultiByte (KERNEL32.dll)
  0x140020040 LCMapStringEx (KERNEL32.dll)
  0x140020048 GetStringTypeW (KERNEL32.dll)
  0x140020050 GetCPInfo (KERNEL32.dll)
  0x140020058 RtlCaptureContext (KERNEL32.dll)
  0x140020060 RtlLookupFunctionEntry (KERNEL32.dll)
  0x140020068 RtlVirtualUnwind (KERNEL32.dll)
  0x140020070 UnhandledExceptionFilter (KERNEL32.dll)
  0x140020078 SetUnhandledExceptionFilter (KERNEL32.dll)
  0x140020080 GetCurrentProcess (KERNEL32.dll)
  0x140020088 TerminateProcess (KERNEL32.dll)
  0x140020090 IsProcessorFeaturePresent (KERNEL32.dll)
  0x140020098 QueryPerformanceCounter (KERNEL32.dll)
  0x1400200a0 GetCurrentProcessId (KERNEL32.dll)
  0x1400200a8 GetCurrentThreadId (KERNEL32.dll)
  0x1400200b0 GetSystemTimeAsFileTime (KERNEL32.dll)
  0x1400200b8 InitializeSListHead (KERNEL32.dll)
  0x1400200c0 IsDebuggerPresent (KERNEL32.dll)
  0x1400200c8 GetStartupInfoW (KERNEL32.dll)
  0x1400200d0 GetModuleHandleW (KERNEL32.dll)
  0x1400200d8 WriteConsoleW (KERNEL32.dll)
  0x1400200e0 RtlPcToFileHeader (KERNEL32.dll)
  0x1400200e8 RaiseException (KERNEL32.dll)
  0x1400200f0 RtlUnwindEx (KERNEL32.dll)
  0x1400200f8 GetLastError (KERNEL32.dll)
  0x140020100 SetLastError (KERNEL32.dll)
  0x140020108 InitializeCriticalSectionAndSpinCount (KERNEL32.dll)
  0x140020110 TlsAlloc (KERNEL32.dll)
  0x140020118 TlsGetValue (KERNEL32.dll)
  0x140020120 TlsSetValue (KERNEL32.dll)
  0x140020128 TlsFree (KERNEL32.dll)
  0x140020130 FreeLibrary (KERNEL32.dll)
  0x140020138 GetProcAddress (KERNEL32.dll)
  0x140020140 LoadLibraryExW (KERNEL32.dll)
  0x140020148 GetStdHandle (KERNEL32.dll)
  0x140020150 WriteFile (KERNEL32.dll)
  0x140020158 GetModuleFileNameW (KERNEL32.dll)
  0x140020160 ExitProcess (KERNEL32.dll)
  0x140020168 GetModuleHandleExW (KERNEL32.dll)
  0x140020170 GetCommandLineA (KERNEL32.dll)
  0x140020178 GetCommandLineW (KERNEL32.dll)
  0x140020180 HeapAlloc (KERNEL32.dll)
  0x140020188 HeapFree (KERNEL32.dll)
  0x140020190 FlsAlloc (KERNEL32.dll)
  0x140020198 FlsGetValue (KERNEL32.dll)
  0x1400201a0 FlsSetValue (KERNEL32.dll)
  0x1400201a8 FlsFree (KERNEL32.dll)
  0x1400201b0 VirtualProtect (KERNEL32.dll)
  0x1400201b8 CompareStringW (KERNEL32.dll)
  0x1400201c0 LCMapStringW (KERNEL32.dll)
  0x1400201c8 GetLocaleInfoW (KERNEL32.dll)
  0x1400201d0 IsValidLocale (KERNEL32.dll)
  0x1400201d8 GetUserDefaultLCID (KERNEL32.dll)
  0x1400201e0 EnumSystemLocalesW (KERNEL32.dll)
  0x1400201e8 GetFileType (KERNEL32.dll)
  0x1400201f0 CloseHandle (KERNEL32.dll)
  0x1400201f8 FlushFileBuffers (KERNEL32.dll)
  0x140020200 GetConsoleOutputCP (KERNEL32.dll)
  0x140020208 GetConsoleMode (KERNEL32.dll)
  0x140020210 ReadFile (KERNEL32.dll)
  0x140020218 GetFileSizeEx (KERNEL32.dll)
  0x140020220 SetFilePointerEx (KERNEL32.dll)
  0x140020228 ReadConsoleW (KERNEL32.dll)
  0x140020230 HeapReAlloc (KERNEL32.dll)
  0x140020238 FindClose (KERNEL32.dll)
  0x140020240 FindFirstFileExW (KERNEL32.dll)
  0x140020248 FindNextFileW (KERNEL32.dll)
  0x140020250 IsValidCodePage (KERNEL32.dll)
  0x140020258 GetACP (KERNEL32.dll)
  0x140020260 GetOEMCP (KERNEL32.dll)
  0x140020268 GetEnvironmentStringsW (KERNEL32.dll)
  0x140020270 FreeEnvironmentStringsW (KERNEL32.dll)
  0x140020278 SetEnvironmentVariableW (KERNEL32.dll)
  0x140020280 SetStdHandle (KERNEL32.dll)
  0x140020288 GetProcessHeap (KERNEL32.dll)
  0x140020290 HeapSize (KERNEL32.dll)
  0x140020298 CreateFileW (KERNEL32.dll)
  0x1400202a0 RtlUnwind (KERNEL32.dll)
functions: 1975
indirect targets: 3453
cfg written to: tests\example1.exe.cfg

das Ausgabeformat

Die .cfg-Datei ist strukturiertes JSON. So sieht sie aus:

root@kitploit:~
{
  "binary": "tests\\example1.exe",
  "mode": "full",
  "arch": "x86-64",
  "format": "PE",
  "entry_point": "0x1400054bc",
  "imports": [
    {
      "address": "0x140020000",
      "name": "EncodePointer",
      "library": "KERNEL32.dll"
    },
    {
      "address": "0x140020008",
      "name": "DecodePointer",
      "library": "KERNEL32.dll"
    },
    {
      "address": "0x140020010",
      "name": "EnterCriticalSection",
      "library": "KERNEL32.dll"
    },
    {
      "address": "0x140020018",
      "name": "LeaveCriticalSection",
      "library": "KERNEL32.dll"
    },
    {
      "address": "0x140020020",
      "name": "InitializeCriticalSectionEx",
      "library": "KERNEL32.dll"
    },
    ...
    ...
    ...
  "functions": [
    {
      "address": "0x1400054bc",
      "name": "entry",
      "blocks": [
        {
          "address": "0x1400054bc",
          "size": 4,
          "is_prolog": false,
          "is_epilog": false,
          "instructions": [
            {
              "address": "0x1400054bc",
              "size": 4,
              "mnemonic": "sub",
              "operands": "rsp, 0x28"
            },
            {
              "address": "0x1400054c0",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x140005d30"
            },
            {
              "address": "0x1400054c5",
              "size": 4,
              "mnemonic": "add",
              "operands": "rsp, 0x28"
            },
            {
              "address": "0x1400054c9",
              "size": 5,
              "mnemonic": "jmp",
              "operands": "0x140005340"
            }
          ],
          "successors": [
            "0x140005340"
          ]
        },
        {
          "address": "0x140005340",
          "size": 8,
          "is_prolog": false,
          "is_epilog": false,
          "instructions": [
            {
              "address": "0x140005340",
              "size": 5,
              "mnemonic": "mov",
              "operands": "qword ptr [rsp + 8], rbx"
            },
            {
              "address": "0x140005345",
              "size": 5,
              "mnemonic": "mov",
              "operands": "qword ptr [rsp + 0x10], rsi"
            },
            {
              "address": "0x14000534a",
              "size": 1,
              "mnemonic": "push",
              "operands": "rdi"
            },
            {
              "address": "0x14000534b",
              "size": 4,
              "mnemonic": "sub",
              "operands": "rsp, 0x30"
            },
            {
              "address": "0x14000534f",
              "size": 5,
              "mnemonic": "mov",
              "operands": "ecx, 1"
            },
            {
              "address": "0x140005354",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x14000550c"
            },
            {
              "address": "0x140005359",
              "size": 2,
              "mnemonic": "test",
              "operands": "al, al"
            },
            {
              "address": "0x14000535b",
              "size": 6,
              "mnemonic": "je",
              "operands": "0x140005497"
            }
          ],
          "successors": [
            "0x140005497",
            "0x140005361"
          ]
        },
        {
          "address": "0x140005497",
          "size": 15,
          "is_prolog": false,
          "is_epilog": false,
          "instructions": [
            {
              "address": "0x140005497",
              "size": 5,
              "mnemonic": "mov",
              "operands": "ecx, 7"
            },
            {
              "address": "0x14000549c",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x140005e44"
            },
            {
              "address": "0x1400054a1",
              "size": 1,
              "mnemonic": "nop",
              "operands": ""
            },
            {
              "address": "0x1400054a2",
              "size": 5,
              "mnemonic": "mov",
              "operands": "ecx, 7"
            },
            {
              "address": "0x1400054a7",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x140005e44"
            },
            {
              "address": "0x1400054ac",
              "size": 2,
              "mnemonic": "mov",
              "operands": "ecx, ebx"
            },
            {
              "address": "0x1400054ae",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x14000ec14"
            },
            {
              "address": "0x1400054b3",
              "size": 1,
              "mnemonic": "nop",
              "operands": ""
            },
            {
              "address": "0x1400054b4",
              "size": 2,
              "mnemonic": "mov",
              "operands": "ecx, ebx"
            },
            {
              "address": "0x1400054b6",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x14000ebcc"
            },
            {
              "address": "0x1400054bb",
              "size": 1,
              "mnemonic": "nop",
              "operands": ""
            },
            {
              "address": "0x1400054bc",
              "size": 4,
              "mnemonic": "sub",
              "operands": "rsp, 0x28"
            },
            {
              "address": "0x1400054c0",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x140005d30"
            },
            {
              "address": "0x1400054c5",
              "size": 4,
              "mnemonic": "add",
              "operands": "rsp, 0x28"
            },
            {
              "address": "0x1400054c9",
              "size": 5,
              "mnemonic": "jmp",
              "operands": "0x140005340"
            }
          ],
          "successors": [
            "0x140005340"
          ]
        },
        ...
        ...
        ...

Jede Funktion enthält jetzt optionale Felder:

  • end_address – präzise Funktionsende, wenn verfügbar (aus der PE .pdata-Ausnahmetabelle), andernfalls berechnet als maximale Anweisungsadresse über alle Blöcke
  • is_thunk – true für PLT-Stubs und Import-Thunks (Funktionen, die nur an eine andere Adresse weiterleiten)

Ausgabe von --subs-only

root@kitploit:~
{
  "binary": "C:\\binaries\\target.exe",
  "mode": "subs-only",
  "arch": "x86-64",
  "format": "PE",
  "entry_point": "0x1400054bc",
  "imports": [ ... ],
  "indirect_targets": [ ... ],
  "functions": [
    {
      "address": "0x1400054bc",
      "name": "entry",
      "blocks": [ ... ]
    },
    ...
  ]
}

Das Feld "mode": "subs-only" teilt nachgelagerten Tools mit, dass dieser CFG nur Funktionen enthält, die vom Einstiegspunkt aus erreichbar sind. Unerreichbare Exporte und Prologkandidaten sind ausgeschlossen – weniger Funktionen, sauberere Analyseoberfläche.

Ausgabe von --clean

root@kitploit:~
{
  "binary": "C:\\binaries\\target.exe",
  "mode": "full+clean",
  "arch": "x86-64",
  "format": "PE",
  "entry_point": "0x1400054bc",
  "imports": [ ... ],
  "indirect_targets": [ ... ],
  "functions": [
    {
      "address": "0x1400054bc",
      "name": "entry",
      "blocks": [
        {
          "address": "0x1400054bc",
          "size": 4,
          "is_prolog": false,
          "is_epilog": false,
          "instructions": [
            {
              "address": "0x1400054bc",
              "size": 4,
              "mnemonic": "sub",
              "operands": "rsp, 0x28",
              "stack_offset": 0
            },
            {
              "address": "0x1400054c0",
              "size": 5,
              "mnemonic": "call",
              "operands": "0x140005d30",
              "stack_offset": -40
            },
            {
              "address": "0x1400054c5",
              "size": 4,
              "mnemonic": "add",
              "operands": "rsp, 0x28",
              "stack_offset": -40
            },
            {
              "address": "0x1400054c9",
              "size": 5,
              "mnemonic": "jmp",
              "operands": "0x140005340",
              "stack_offset": 0
            }
          ],
          "successors": [ "0x140005340" ]
        },
        ...
      ]
    },
    ...
  ],
  "xrefs": [
    {
      "target": "0x140011b00",
      "callers": [
        { "address": "0x14001a30f", "type": "call" },
        { "address": "0x140019ec1", "type": "call" },
        ...
      ]
    },
    {
      "target": "0x140007394",
      "callers": [
        { "address": "0x14001a31a", "type": "call" },
        ...
      ]
    },
    ...
  ]
}

Jede Anweisung im --clean-Modus enthält stack_offset – das RSP-Delta vom Funktionsanfang an dieser Anweisungsposition. Der Abschnitt xrefs bildet jedes Aufruf-/Sprungziel zurück auf jede Anweisung, die darauf verweist.

Kombinierte Ausgabe von --subs-only --clean

root@kitploit:~
{
  "binary": "C:\\binaries\\target.exe",
  "mode": "subs-only+clean",
  ...
}

Gleiche Struktur wie --clean, aber mit "mode": "subs-only+clean", um anzuzeigen, dass beide Filter angewendet wurden. Die Funktionsanzahl ist auf nur vom Einstiegspunkt erreichbare Funktionen reduziert, und die verbleibenden Funktionen haben Stack-Offsets und Xrefs.

Funktionsgrenzerkennung

cfgrip entdeckt Funktionen durch mehrere Erkennungsdurchläufe:

DurchlaufWas erkannt wirdAbdeckung
Prolog-Scanpush rbp, push r15/r14/r13/r12/rbx/rdi/rsi, sub rsp, >=0x20, enterMSVC x64, GCC, Blattfunktionen, CET (endbr64)
AufrufzieleJedes call-Anweisungsziel ist ein FunktionsanfangDirekte und GOT-aufgelöste indirekte Aufrufe
Tail-Callsjmp-Anweisungen, die auf Prologkandidaten zielenOptimierte Tail-Call-Ketten
.pdata (PE)Laufzeitfunktionseinträge aus der AusnahmebehandlungstabellePräziser Start/Ende für jede x64-PE-Funktion
Datenzeiger8-Byte-Werte in .rdata/.data, die in ausführbaren Code zeigenFunktionszeiger, V-Tabellen, Callbacks

Funktionen mit is_thunk: true sind PLT-Stubs oder Import-Thunks – Ein-Block-Funktionen, die an eine andere Adresse weiterleiten.

Bauen

Erfordert CMake und einen C++17-Compiler. Capstone wird automatisch heruntergeladen.

root@kitploit:~
cmake -B build
cmake --build build --config Release
./build/cfgrip <binary>

Oder unter Windows mit Visual Studio:

root@kitploit:~
cmake -B build -S .
cmake --build build --config Release
.\build\Release\cfgrip.exe <binary>

alt text alt text

was es unterstützt

| | | |:-:| | Formate | PE (32/64-Bit) | JA | | | ELF (64-Bit) | JA | | Architekturen | x86 | JA | | | x86-64 | JA | | Indirekte Aufrufe | GOT-Auflösung | JA | | | Sprungtabellenerkennung | JA | | | Rückwärtige Registerverfolgung | JA | | Funktionserkennung | Einstiegspunkt | JA | | | Exporte | JA | | | call-Ziele | JA | | | Prolog-Scan (MSVC x64, GCC, CET endbr64) | JA | | | Tail-Call-Erkennung (jmp → Funktion) | JA | | | PE .pdata (Ausnahmebehandlungstabelle) | JA | | | Datenbereichs-Funktionszeiger-Scan | JA | | Thunk-Erkennung | PLT-Stubs / Import-Thunks (is_thunk) | JA | | Funktionsgrenzen | end_address aus .pdata oder maximale Anweisung | JA | | Subs-only-Modus | --subs-only-Flag | JA | | CFG-Bereinigung | --clean (Sprungdurchlauf, tote-Block-Entfernung, Stack-Deltas, Xrefs) | JA |

Forschung

Die Funktionsgrenzerkennung in diesem Tool basiert auf dem Ansatz, der in "Function Boundary Detection in Stripped Binaries" (Alves-Foss & Song, 2019) beschrieben wird, der einen Multi-Heuristik-Algorithmus zur Lokalisierung von Funktionsanfängen und -enden in gestrippten x86/x64-Binärdateien vorstellt.

Das Paper ist verfügbar unter papers/Function_Boundary_Detection_in_Stripped_Binaries.pdf.

Wie unsere Implementierung auf die Heuristiken des Papers abbildet:

HeuristikBeschreibung im PaperUnsere Implementierung
H1–H4Prolog-Signaturen (push rbp, callee-saved regs, stack sub, enter)isProlog() in disasm/engine.cpp – erkennt push rbp, push r15..rbx, sub rsp >= 0x20, enter
H5Call-Target-SeedingJedes direkte call-Ziel ist ein Funktionsanfang
H6Jump-to-Function (Tail-Call)-Erkennungjmp zu Prologkandidaten fügt Ziel zur Funktionswarteschlange hinzu
H7Ausnahmetabellen-ParsingPE .pdata RUNTIME_FUNCTION-Einträge liefern präzisen Start/Ende
H8DatenreferenzanalysescanDataPointers() durchläuft Datenabschnitte auf Codizeiger

Die Kernaussage des Papers ist, dass algorithmische Heuristiken – ohne maschinelles Lernen – eine hohe Genauigkeit bei gestrippten Binärdateien erreichen können. Unsere Implementierung folgt dieser Philosophie und verwendet einen Multi-Pass-Ansatz, bei dem jeder Durchlauf Funktionen erfasst, die die anderen übersehen könnten.

Tool herunterladen