Skip to main content
Glama
tzhtdx

delphi-mcp

by tzhtdx

delphi-mcp

Delphi 逆向符号恢复 MCP 服务:把多个 Delphi 反编译引擎统一到一个 MCP 接口后面,为 AI agent 提供静态符号知识,并与 IDA MCP(ida-mcp)协同工作。

在真实恶意样本上验证:Delphi 2 ~ 13 Florence 全版本、32/64 位,纯 Python,零编译依赖。

想了解"做了什么、为什么能做、二进制变成了什么"——见 docs/TECHNICAL.md。 想知道"能干什么、输出长什么样、怎么用"——见 docs/USAGE.md(使用手册,含真实输出样例)。 性能演进与卡死复盘——见 docs/PERFORMANCE.md。 文件职责速查——见 docs/FILES.md。

为什么

老 Delphi 样本(尤其恶意软件)在 IDA/Ghidra 里函数名全是 sub_xxxx,分析极度依赖符号恢复。现有工具要么只有 GUI(IDR)、要么绑定特定平台(DelphiReSym 依赖 Ghidra)、要么恢复能力弱。delphi-mcp 把它们的分析能力抽取为统一的、可脚本化的、给 agent 用的服务。

Related MCP server: RE-MCP Server

特性

  • 三引擎自动路由:按 Delphi 版本选择最合适的分析引擎,输出统一的符号模型

  • 12 个 MCP 工具:从版本判定到符号查询、行为情报、模型生命周期、IDA 协同

  • 纯 Python 引擎:classic / modern 两个引擎无编译依赖(仅需 pefile、numpy、capstone)

  • IDR 知识库匹配:直接解析 kb*.bin(4 万+ RTL/VCL 函数字节模式),命名虚表槽与调用目标

  • 行为情报:字符串×引用、敏感 API 指纹、DFM 表单→事件、XRef 调用图

  • 为 agent 设计:stdout=JSON、结果按需分页、大产物落盘只回传路径、磁盘缓存

架构

                     delphi_detect ─── 版本 / 架构判定
                          │
        ┌─────────────────┼──────────────────┐
        ▼                 ▼                  ▼
   Delphi 2..2007    Delphi 2009+       (idr-cli 强制)
   classic 引擎       modern 引擎        legacy 引擎
   (纯 Python)        (纯 Python)        (C++ 子进程)
   RTTI+KB 匹配       VMT/MDT/RTTI       syskb 19 步流水线
        │                 │                  │
        └─────────────────┴──────────────────┘
                          ▼
             统一符号模型 SymbolModel (JSON)
                          ▼
      query / strings / imports / forms / xrefs / renames / export
                          ▼
                    ida-mcp 深挖(可选)

三个引擎都产出同一套 SymbolModel(类/方法/参数/字段/单元/接口/变量),后端与 agent 无需关心用的是哪个引擎。

引擎职责

引擎

版本范围

实现

恢复内容

classic

Delphi 2~2007(老格式,负偏移 VMT)

纯 Python

类/RTTI、虚表槽(KB 命名)、动态消息方法、接口、字段、调用目标发现、单元

modern

Delphi 2009~13(新格式,正偏移 VMT)

纯 Python(DelphiReSym 衍生)

类/VMT/MDT、方法名+参数+返回类型、RTTI 命名空间

legacy

Delphi 2~2007

C++ idr-cli 子进程

IDR 完整 19 步流水线(可选深度增强/兜底)

安装

依赖(Python ≥ 3.11):

pip install mcp pefile numpy capstone

legacy 引擎需要 idr-cli.exe(可选,缺失时 classic 引擎仍然工作):

# 见 D:\Code\IDR\idr-cli,或设 IDR_CLI_EXE

IDR 知识库(可选,启用 KB 匹配):从 IDR 安装目录取得 kb2.bin … kb2014.bin,放入 IDR_KB_DIR。

运行:

PYTHONPATH=src python -m delphi_mcp.server

MCP 工具

工具

作用

delphi_detect(file)

秒级判定版本/架构/引擎路由

delphi_analyze(file, version?, refresh?, section?, offset?, limit?, with_methods?)

全量分析 → 符号模型;按 section 分页(summary/classes/methods/units/forms/vars/full,默认 summary,避免大 JSON)

delphi_query(file, class_name?/addr?/addrs?)

查符号:类名子串 / 单地址 / 地址批量查表(pull,给 IDA 的地址回名)

delphi_list()

列出当前常驻内存的模型(类比 ida-mcp list_databases)

delphi_close(file, drop_sidecar?)

卸载该样本的缓存模型(释放内存),可选删 .delphi_model.json

delphi_triage(file, include_noise?)

一键初筛:版本+规模+信号串(表驱动分类 persistence/creds_path/network/exec/mutex,去重;噪声默认隐藏)+ 敏感 API + DLL 导出

delphi_strings(file, pattern?, with_refs?)

字符串(带 Delphi 头分类)+ 引用函数归因

delphi_imports(file, only_sensitive?)

导入表 + 行为分类(process/persist/net/crypto/evasion)

delphi_forms(file, with_addresses?)

DFM 表单控件树 + 事件处理函数→地址

delphi_xrefs(file, addr)

调用者/跳转者/数据读者(静态调用图查询)

delphi_renames(file, base?, class_filter?, limit?, offset?, save_idc?, addrs?, overwrite?, min_confidence?)

生成 [{addr,name,confidence}] / .idc;addrs=只改你选的地址;默认不覆盖导出、min_confidence 可过滤低可信名

delphi_export(file, fmt?)

导出 idc/lst/pas/idp

与 IDA / ida-mcp 协同

核心资产是地址 → 符号名映射。两侧地址对齐:Delphi PE image_base 由 delphi_detect 输出(32-bit 通常 0x400000),与 ida-mcp survey_binary 的 base_address 核对即可(rebased 时用 delphi_renames(base=…))。

两种模式

  • 批量灌入(plan):delphi_renames(file, min_confidence="medium") → 落盘 .idc → 在 IDA 里 File > Script file 执行一次。

  • 按需查表(pull,推荐):让 IDA 先看,遇到关注的地址再问 delphi——不把全量改名拉进上下文,也不会误灌低可信名。

pull 闭环(推荐流程)

1. IDA 定位   (ida-mcp) survey_binary / decompile / xrefs  → 关注地址集合
2. 查表       delphi_query(file, addrs=[...])              → 每个地址的名字(含 kind/confidence)
              模型未命中 → 回退 PE 导出表 (export:<name>)
3. 筛选       按 confidence 取舍 (high=MDT/RTTI, medium=KB虚表, low=调用图)
4. 出计划     delphi_renames(file, addrs=[...], min_confidence="medium")
              → 只含你选的地址:小 .idc + [{addr,name,confidence}](默认不动导出)
5. 回灌       IDA 执行小 .idc  /  对 [{addr,name}] 逐条 ida_mcp_rename

名字来源与可信度:high=编译器元数据(MDT/RTTI)、medium=KB 字节匹配(虚表槽)、low=调用图发现。min_confidence 只回灌高可信名;改名默认跳过 PE 导出(IDA 已命名),overwrite=true 才强制覆盖。

把改名应用到 IDA(当前无原生 batch rename)

.idc 落盘后两种落地方式:

  • IDA GUI:File > Script file 选该 .idc(一次性;小 .idc 即 pull 模式产物)。

  • ida-mcp 会话内:用 execute_python 解析后 set_name(用 SN_NOWARN,不覆盖 IDA 已在名):

import re, ida_name
txt = open(r"...\sample.renames.idc", encoding="utf-8").read()
for a, n in re.findall(r'MakeNameEx\((0x[0-9A-Fa-f]+),\s*"(.*?)"', txt):
    ida_name.set_name(int(a,16), n.replace('\\\\','\\').replace('\\"','"'), ida_name.SN_NOWARN)

实测(GRenam,Delphi 2007)

  • delphi_renames(min_confidence="medium") → 1522 条;IDA 应用 1510。

  • 3834 函数中 1185 个由 sub_ 变为真实符号;反编译 uMain.TfrmMain.FormCreate 可见 ExtCtrls.TTimer.Create(...)。

  • 病毒自写函数无 Delphi 元数据 → 仍为 sub_:delphi 给“地图”,ida 给“地形”。

详见 docs/TECHNICAL.md §5.1。

配置

环境变量

默认

说明

IDR_CLI_EXE

D:\Code\IDR\idr-cli\modern\build\Release\idr-cli.exe

legacy 引擎二进制

IDR_KB_DIR

<IDR_DIR>

kb*.bin 知识库目录

DELPHI_LEGACY_ENGINE

空

设为 idr-cli 强制老版本用 C++ 引擎

DELPHI_NO_KB_CACHE

空

设为 1 禁用 KB 进程内缓存

DELPHI_TRACE

空

设为 1 打印分析各阶段耗时

DELPHI_MODEL_CACHE_MAX

3

内存中常驻符号模型数上限(LRU 淘汰)

DELPHI_GC_ON_EVICT

1

设为 0 时淘汰/关闭模型不触发 gc.collect()

DELPHI_IDRCLI_TIMEOUT

900

idr-cli analyze/export 子进程超时(秒),用于兜底 C++ 路径

DELPHI_IDRCLI_FALLBACK

1

设为 0 关闭 classic→idr-cli 兜底(classic 0 类时直接返回,不跑 C++)

DELPHI_SIGNAL_RULES

空

delphi_triage 信号规则覆盖表(JSON 路径,含 signal/noise/framework_prefixes,与内置表合并)

opencode.json(stdio 本地,每会话各起一份进程):

"delphi-mcp": {
  "type": "local",
  "command": ["python", "-m", "delphi_mcp.server"],
  "environment": { "PYTHONPATH": "D:\\Code\\IDR\\delphi-mcp\\src" },
  "enabled": true
}

常驻远程模式(推荐,避免“每会话一份 python”)

type: local 下 opencode 会按会话/窗口各起一份进程(多窗口 = 多份 python)。改成一个常驻服务 + remote 连接,所有会话共用一份(模型 LRU 也共享):

# 启动一次并保持运行(Windows 可用 scripts/serve-delphi-mcp.bat)
python -m delphi_mcp.serve
# 默认 127.0.0.1:8910 (SSE);可用环境变量调整:
#   DELPHI_MCP_TRANSPORT=sse|streamable-http  DELPHI_MCP_HOST  DELPHI_MCP_PORT
"delphi-mcp": { "type": "remote", "url": "http://127.0.0.1:8910/sse", "enabled": true }

开机自启:用任务计划程序在登录时运行 scripts/serve-delphi-mcp.bat。SSE 与 streamable-http 两种传输都支持(后者路径为 /mcp)。

性能

实测(本机,含 KB 索引一次性构建):

样本

版本

类/方法

耗时

GRenam

2006

109 / 3148

3.0s

Rekram

2006

144 / 3761

3.1s

DanaBot

10.2

300 / 973

3.5s

Culebra

XE2-4

738 / 3864

4.2s

libcef.dll

2014

1854 / 12190

9.4s

卡死问题与修复(开发笔记)

早期版本的 classic 引擎在较大样本(如 Culebra)上会假死(CPU 100%、长时间无输出)。根因是三处缺陷叠加:

  1. 主因:build_anchor_index 只构建单字节索引 _anchor,从不构建两级索引 _anchor2,且无幂等守卫——match_slot 每次因 _anchor2 is None 触发全量 4.8 万条 KB 记录索引重建。Culebra 场景 7554 次匹配 × 4.8 万解析 ≈ 3.6 亿次操作。

  2. 掩码 bug:匹配掩码用 1 << (k*8)(每字节只 1 bit)而非 0xFF << (k*8),导致一级预筛恒假、虚表命名静默归零。

  3. 低效扫描:类名回溯对每个类名做一次全 .text find;索引构建用重型 proc_info(解码函数名/参数)。

修复(见 prepare_matching / match_slot_fast):

  • 单遍构建两级锚点索引 + 头 8 字节掩码 + 全长掩码,用 _proc_dump(仅解析 dump 段)替代重型解析

  • match_slot_fast 用 numpy 对候选桶做向量化头字节预筛,仅对幸存者做全长比对

  • 类名回溯改为单遍 numpy u32 扫描收集引用

  • IntfTable 解析延后到虚表命名之后(槽名查询命中缓存)

  • 结果:Culebra 56s → 4.2s(13×),结果不变

注意:cProfile 对千万级 Python 调用有 10~50× 放大,用它剖析此类热点会放大"卡死"观感——用 DELPHI_TRACE=1 的分阶段计时更合适。

MCP 调用超时与修复(开发笔记)

早期版本首次调用 delphi_triage(或任何触发冷分析的 delphi_*)会在 MCP 客户端侧报 -32001: Request timed out。

  • 根因:classic 引擎的 KB 匹配器(engines/kb.py 的 prepare_matching)懒加载 numpy。FastMCP 把同步工具放到 anyio 工作线程执行、且此时事件循环已在运行——在该上下文里加载 numpy 的 C 扩展 _multiarray_umath 会死锁,工具永不返回,客户端超时。

  • 修复:在主线程、事件循环启动之前预加载 numpy(delphi_mcp/__init__.py 包导入时 + server.py main() 里兜底)。缺 numpy 时仍优雅降级(仅失去 KB 快速匹配)。

idr-cli 兜底耗时

classic 引擎若解析出 0 个类或抛异常,router 会在确认是 Delphi 二进制(Delphi 指纹:.itext 段 / .rsrc 的 TPF0 DFM / RTL 标记串)后,才回退到 legacy(C++ idr-cli)——这是正常兜底设计,不是 Python 瓶颈。子进程超时由 DELPHI_IDRCLI_TIMEOUT(默认 900s)限定;可用 DELPHI_IDRCLI_FALLBACK=0 完全关闭兜底。回退时 model.warnings 会带 classic parser yielded 0 classes; fell back to idr-cli (C++, X.Xs)。

非 Delphi 二进制的误报与快速返回

idr-cli 的快速版本判定对非 Delphi 二进制(MSVC/MinGW/Qt 等)可能误报 version=2。此时 detect 用 Delphi 指纹预检,若无任何 Delphi 标记则返回 engine=not-delphi、triage 直接回报 delphi:false、analyze 立即报错返回——不再触发 idr-cli 兜底(例:Qt 5.1.0 的 UninstallTool.exe 由 ~12s 降至 ~0.3s)。若样本确为加壳 Delphi(元数据被抹除),会被一并判为 not-delphi——加壳样本本就在能力边界之外。

能力边界

  • 加壳样本需先脱壳(所有引擎相同)

  • 字段类型名:用户自定义类型可解析,导入/外部类型为 None(typesTab 语义限制)

  • 老格式 MDT 不含参数/返回类型信息(classic 通过 KB 原型补全命中的函数)

  • 64 位:modern 引擎完整支持(符号 + RIP-relative 的字符串/导入引用归因,lea/mov/call [rip+disp32]、movabs);classic/legacy 仅 32 位

  • idr-cli 弱判定:对非 Delphi(MSVC/MinGW/Qt)可能误报 version=2。detect 会做 Delphi 指纹预检,无可信标记则返回 engine=not-delphi(快速返回,不跑分析)

  • 非 PE / 截断文件:detect 返回 engine=not-pe,analyze 给出明确错误

测试

cd tests
python smoke_all.py        # 工具 × 双引擎 + MCP 协议层
python classic_test.py     # 老格式语料
python verify_64bit.py     # 64 位路径
python download_paper_corpus.py   # 从 VT 拉取论文语料

tests/regression_corpus.json 记录了回归用的本地样本、论文 hash 与拒绝模式清单。

目录结构

src/delphi_mcp/
├── server.py           MCP 组装(12 工具注册)
├── router.py           版本判定 + 引擎路由
├── model.py            SymbolModel 统一模型
├── shared.py           缓存 / 路径 / idc 写出
├── xref.py             统一交叉引用收集
├── intel.py            情报 API 再导出
├── strings_api.py      字符串提取 + 分类 + VA 归属
├── imports_api.py      IAT 指纹 + 敏感 API 分类
├── dfm.py              DFM 资源提取 + TPF0 二进制解析
├── engines/
│   ├── classic.py      老格式引擎(Delphi 2~2007)
│   ├── modern.py       新格式引擎(Delphi 2009+,DelphiReSym 衍生)
│   ├── legacy.py       idr-cli 子进程封装
│   ├── kb.py           IDR 知识库解析 + 匹配
│   ├── units.py        单元边界/初始化表
│   ├── pe_memory.py     PE 文件内存视图
│   ├── vmt_scan.py     VMT 扫描
│   ├── mdt.py          MDT/方法/参数遍历
│   ├── rtti.py         RTTI 对象遍历
│   └── fqn.py          模板感知 FQN 解析
└── tools/              MCP 工具模块(按域拆分)

致谢

  • crypto2011/IDR — Interactive Delphi Reconstructor (MIT)。VMT 偏移表、FieldTable/MethodTable/DynamicTable/IntfTable 解析、单元/初始化表算法均移植自其源码。

  • ajsb85/idr-cli — IDR 的 VCL-free CLI 移植(知识库格式与匹配器参考)。

  • WenzWenzWenz/DelphiReSym — 新格式(VMT/MDT/RTTI)解析与论文《Towards Ghidra Reverse Engineering Tools for Delphi Executables》(BSD-3)。modern 引擎为其衍生作品。

License

MIT(本项目代码)。衍生组件见 LICENSE:modern 引擎为 DelphiReSym (BSD-3) 衍生作品,legacy 引擎运行时依赖 idr-cli (MIT)。

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    A multi-backend MCP server that exposes binary analysis capabilities from IDA Pro and Ghidra, allowing LLMs to directly drive reverse-engineering tools via natural language.
    11
    172
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    A local, privacy-first MCP server that orchestrates Ghidra, Binary Ninja, x64dbg, and system tools to provide AI assistants with powerful binary analysis capabilities.
    Academic Free v1.1
  • F
    license
    Not graded
    quality
    C
    maintenance
    MCP server that exposes the complete Delphi 7 RTL/VCL index as tools for AI agents, enabling precise lookups for symbols, class members, unit interfaces, and source contexts to generate accurate legacy Delphi 7 code.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI-powered reverse engineering in IDA Pro by letting MCP clients inspect decompilation and disassembly, rename symbols, convert integers, and otherwise interact with the database through natural language.
    2
    MIT