Capstone是反汇编最现实的选择,因其已覆盖x86、ARM64等十余架构,提供稳定C++接口、300KB静态库、零运行时依赖,三步即可集成:find_package、构造cs_engine、调用disasm返回std::vector。

用 capstone 是最现实的选择,别从零写
想靠自己手撸一个能正确 decode x86-64 / ARM64 机器码的反汇编器,实际不可行。指令编码规则极其琐碎:前缀长度可变、SIB/VEX/EVEX 编码嵌套、ModR/M 的三种寻址模式、立即数/位移字段字节对齐依赖操作码……哪怕只支持 x86-64 的 64 位模式,没几个月也调不通边界 case。真实项目里,capstone 是事实标准——它已覆盖 x86、ARM、MIPS、RISC-V 等十余种架构,且持续由逆向社区维护。直接集成比“轻量”更重要:它的静态库仅约 300KB,无运行时依赖,C 接口干净。
如何最小化接入 capstone(C++ 封装 + 零配置)
不用碰 C 风格回调或手动管理 cs_insn 数组。用官方 C++ 绑定 capstone.hpp,三步完事:
- 用
cmake添加find_package(Capstone REQUIRED),链接capstone::capstone - 构造
capstone::cs_engine时指定架构和模式,例如:cs_engine(CS_ARCH_X86, CS_MODE_64) - 调用
.disasm(),传入原始字节指针和长度,返回std::vector<:cs_insn>,每个元素含.mnemonic和.op_str
注意:输入字节必须是完整指令边界起始(比如不能把 0x4889d0(mov %rdx,%rax)截成两段喂给引擎),否则会返回 CS_ERR_ASM 或解出错误指令。
capstone 默认输出不够“干净”?控制格式的关键参数
默认 .op_str 会带寄存器大小后缀(如 rax → rax 没问题,但 mov eax, 1 可能显示为 mov rax, 1),这不是 bug,是 capstone 的语义一致性设计(统一按 64 位寄存器名输出)。若需严格匹配 NASM/YASM 风格,必须启用 CS_OPT_SYNTAX_NASM:
engine.option(CS_OPT_SYNTAX, CS_OPT_SYNTAX_NASM);
其他实用选项:
-
CS_OPT_DETAIL:开启后可访问.detail->regs_read等元信息,但增加开销,纯文本反汇编无需开启 -
CS_OPT_SKIPDATA:遇到无法识别的字节时,自动跳过并标记为.bytes = {0xXX, ...},避免整个流中断 - 不设置
CS_OPT_MODE会导致默认使用 32 位模式,x86-64 代码会解错(如把0x48c7c001000000解成mov eax, 1而非mov rax, 1)
为什么不用 llvm-mc --disassemble 或 objdump
它们不是库,是命令行工具。每次调用都要 fork 进程、写临时文件、读 stdout,延迟高且难集成进实时场景(比如调试器单步时动态反汇编)。更麻烦的是:不同版本输出格式不兼容(objdump -d 的列数、空格、注释符号都可能变),解析易崩;而 capstone 提供稳定 ABI 和明确的 C++ 对象模型,.mnemonic 和 .op_str 字段含义永远不变。
真正要警惕的不是“轻量”,而是误判指令边界——比如在函数 prologue 中把 push rbp(0x55)和后面紧跟的 mov rbp, rsp(0x4889e5)当成一条指令。这得靠符号信息或 CFG 分析辅助,反汇编引擎本身不解决这个问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











