为什么要做这个东西
用 AI agent 做动态逆向,绕不开一个结构性矛盾:调试器是需要长期保持会话的有状态程序,而 agent 的每一次工具调用都是一个全新进程。
x64dbg 是 GUI 程序,agent 既开不了它,也读不懂它的输出。之前的办法是让 agent 调一次命令就重启一次调试器——断点、内存布局、寄存器状态全部丢失,逆向根本没法往下走。
jindbg 的答案是把调试引擎拆成两层:常驻的 daemon 持有会话,一次性客户端负责通信。
架构
AI agent (git bash)
│ ./jindbg.exe disasm cip 10 --compact ← 每次都是新进程
▼
jindbg 客户端 (Go) ──JSON Lines / Named Pipe──▶ jindbgd64 / jindbgd32 (C++)
│
▼
x64dbg / x32dbg 引擎
jindbgd64/jindbgd32常驻后台持有调试会话,跨任意多次 bash 调用状态不丢jindbg客户端每条命令一进一出,拿一个 JSON 结果就退出- 按目标架构自动路由到 64 位或 32 位引擎,两个会话可以并存
几个关键设计
机器友好的确定性输出。 稳定 JSON schema、稳定错误码。报错时内嵌 suggest(下一步该敲什么)和 daemon_state(会话快照),agent 在报错当下就知道怎么恢复,而不是卡死重来。
安全默认。 只读侦查优先,写内存、改寄存器这类破坏性操作一律要 --force 显式门控。
交互式程序注入。 宿主接管目标的 stdin/stdout:能喂输入、捞输出,支持 --grep 过滤与 --encoding gbk,输出可落盘不丢。这是为了 CTF 逆向里最常见的「菜单狂刷 + flag 淹没」场景。
无 PDB 也能起步。 stripped 二进制没有符号,通常连 main 都找不到。jindbg 提供一条引导链:
./jindbg.exe crt scan # IAT CRT 符号化
./jindbg.exe func scan # prologue 启发式找函数
./jindbg.exe run --to main # CRT 链反查,直达用户代码
./jindbg.exe find --strings # 运行时枚举解密后的字符串
批处理。 batch 一条调用串行执行多条协议命令,减少 agent 的往返次数。
实际用法
# 宿主(需 VS2022+ / CMake 3.20+)
cmake -S jindbg -B jindbg/build64 -A x64
cmake --build jindbg/build64 --config Release --target jindbgd64
# 客户端(需 Go 1.21+)
cd jindbg/client && go build -o jindbg.exe
# 调一个程序
./jindbg.exe launch ../test/test64.exe
./jindbg.exe run
./jindbg.exe disasm cip 10 --compact
./jindbg.exe break test64.exe:main
./jindbg.exe stop
配套还有一份 agent 技能手册(skills/jindbg/SKILL.md),内含决策树与工作流,可以直接装到 agent 的技能目录里。
为什么协议要单独写文档
daemon 与客户端之间走 JSON Lines 命名管道,协议规范独立成文(jindbg/proto/README.md)。把它拆出来是因为:一旦协议稳定,客户端换语言重写、或者让别的 agent 框架来接,都不用动宿主。
许可
GPL-3.0。宿主以只读方式编译引用 x64dbg 引擎源码(同为 GPL-3.0),构成派生作品,因此整体随 GPLv3 发布;上游引擎源码不入库、不随本项目分发。