Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
cfgrip — 反汇编 PE/ELF x86/x64 二进制文件,通过 GOT、跳转表和寄存器追踪解析间接分支,并将完整控制流图导出为结构化 JSON,用于静态分析和补丁。 | Kitploit
工具/GitLabGitLab/binaryhardening/cfgrip
静态分析逆向工程调试器二进制分析
GitLabbinaryhardening/cfgrip

cfgrip

反汇编 PE/ELF x86/x64 二进制文件,通过 GOT、跳转表和寄存器追踪解析间接分支,并将完整控制流图导出为结构化 JSON,用于静态分析和补丁。

查看仓库网站
225天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

cfgrip

PE/ELF x86/x64 CFG 提取器。输入一个二进制文件,反汇编它,解析每个跳转和调用(GOT、跳转表、寄存器追踪),并将完整的控制流图导出为结构化 JSON。

为什么

你需要确切知道每个分支的去向——不是为了阅读,而是为了打补丁。将 JSON 输入 Zydis 或 AsmJit,定位你需要挂钩或修改的确切指令,然后写回。反作弊团队用它来映射游戏二进制文件;逆向人员用它来将代码提取到自己的分析管线中;软件分析师在不运行二进制文件的情况下追踪执行路径。

cfgrip 提供地图。怎么用取决于你。

它提取什么

对于每个 cfgrip 处理的二进制文件,它会生成:

  • 函数列表,包含地址、可选结束地址(来自 .pdata)、名称(入口点、导出、发现的)、以及桩标记(PLT 存根)
  • 每个函数的基本块——由分支、调用、返回或陷阱终止的指令序列
  • 控制流边——每个块的后继(直接分支、回退、间接目标)
  • 导入表——已解析的库导入及其地址
  • 间接目标——GOT 解析的调用、跳转表条目、寄存器追踪的分支,以及未解析的(标记为未解析)
  • 函数边界——多个检测遍(序言模式、调用目标、尾调用、.pdata 条目、数据段函数指针)以实现最大覆盖率

使用 --clean,它额外:

  • 跳线线程化 jmp → jmp 链为直接边
  • 剪枝 死基本块(无入边)
  • 为每条指令标注 stack_offset(从函数入口开始的 RSP 差值)
  • 构建 xrefs 部分,将每个调用/跳转目标映射回其调用者

它通过以下方式处理间接分支:

  1. 检查 GOT(全局偏移表)以查找已知导入
  2. 向后扫描 LEA 指令 以定位跳转表,然后读取表条目
  3. 向后追踪寄存器 通过 mov/lea 链以查找具体地址

用法

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

传入一个二进制文件,得到 <binary>.cfg 作为输出。
使用 --subs-only 只提取通过调用图从入口点可达的函数——跳过不可达的导出和序言候选。
使用 --clean 以应用跳线线程化、死块剪枝、栈偏移追踪和交叉引用分析。


示例:

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

输出格式

.cfg 文件是结构化的 JSON。其内容如下:

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"
          ]
        },
        ...
        ...
        ...

每个函数现在包含可选字段:

  • end_address — 精确的函数结束地址(当可用时,来自 PE .pdata 异常表),否则计算为所有块中的最大指令地址
  • is_thunk — 对于 PLT 存根和导入桩(仅重定向到另一个地址的函数)为 true

--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": [ ... ]
    },
    ...
  ]
}

"mode": "subs-only" 字段告诉下游工具此 CFG 仅包含从入口点可达的函数。不可达的导出和序言候选被排除——函数更少,分析表面更干净。

--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" },
        ...
      ]
    },
    ...
  ]
}

在 --clean 模式下,每条指令都包含 stack_offset——在该指令点相对于函数入口的 RSP 差值。xrefs 部分将每个调用/跳转目标映射回所有引用它的指令。

--subs-only --clean 组合输出

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

结构与 --clean 相同,但 "mode": "subs-only+clean" 表示同时应用了两种过滤器。函数数量减少到仅限入口点可达的函数,剩余函数具有栈偏移和交叉引用。

函数边界检测

cfgrip 通过多个检测遍发现函数:

is_thunk: true 的函数是 PLT 存根或导入桩——仅重定向到另一个地址的单块函数。

构建

需要 CMake 和 C++17 编译器。Capstone 会自动获取。

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

或者在 Windows 上使用 Visual Studio:

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

alt text alt text

支持的功能

研究

此工具中的函数边界检测基于 "Function Boundary Detection in Stripped Binaries" (Alves-Foss & Song, 2019) 中描述的方法,该论文介绍了一种用于在 stripped x86/x64 二进制文件中定位函数起始和结束的多启发式算法。

论文可在 papers/Function_Boundary_Detection_in_Stripped_Binaries.pdf 处获取。

我们的实现如何映射到论文的启发式方法:

论文的关键见解是,在没有机器学习的情况下,算法启发式可以在 stripped 二进制文件上达到高精度。我们的实现遵循这一理念,采用多遍方法,每一遍都能捕获其他遍可能遗漏的函数。

下载工具
检测遍检测内容覆盖范围
序言扫描push rbp、push r15/r14/r13/r12/rbx/rdi/rsi、sub rsp, >=0x20、enterMSVC x64、GCC、叶函数、CET (endbr64)
调用目标每个 call 指令的目标都是函数起始直接和 GOT 解析的间接调用
尾调用指向序言候选的 jmp 指令优化的尾调用链
.pdata (PE)来自异常处理程序表的运行时函数条目每个 x64 PE 函数的精确起始/结束
数据指针.rdata/.data 中指向可执行代码的 8 字节值函数指针、虚函数表、回调
格式PE (32/64-bit)是
ELF (64-bit)是
架构x86是
x86-64是
间接调用GOT 解析是
跳转表检测是
向后寄存器追踪是
函数发现入口点是
导出是
call 目标是
序言扫描 (MSVC x64、GCC、CET endbr64)是
尾调用检测 (jmp → 函数)是
PE .pdata (异常处理程序表)是
数据段函数指针扫描是
桩检测PLT 存根 / 导入桩 (is_thunk)是
函数边界来自 .pdata 的 end_address 或最大指令是
仅子集模式--subs-only 标志是
CFG 清理--clean(跳线线程化、死块剪枝、栈差值、交叉引用)是
启发式论文描述我们的实现
H1–H4序言签名 (push rbp、被调用者保存寄存器、栈子、enter)disasm/engine.cpp 中的 isProlog()——检测 push rbp、push r15..rbx、sub rsp >= 0x20、enter
H5调用目标播种每个直接 call 目标都是一个函数起始
H6跳转到函数(尾调用)检测jmp 到序言候选会将目标添加到函数队列
H7异常表解析PE .pdata RUNTIME_FUNCTION 条目提供精确的起始/结束
H8数据引用分析scanDataPointers() 遍历数据段以查找代码指针