毕设开发 -- binarymcp
开发 pwn 37
image-zJeh.png

项目链接:https://github.com/kiki1e/binary-mcp (将在毕业答辩之后开源,欢迎师傅们来交流与指导)

BinaryMCP 技术详解:

在二进制安全研究中,大语言模型(LLM)具备较强的代码理解与逻辑分析能力,但由于无法直接与逆向分析环境交互,其在动态验证和实际操作方面存在局限性。

BinaryMCP 项目旨在解决这一问题。通过引入 Model Context Protocol (MCP),项目将 LLM 作为决策中心,同时封装 IDA Pro 和 GDB 等专业工具作为执行模块,使模型能够直接调用底层工具进行分析。

本文将介绍 BinaryMCP 的架构设计及其在静态与动态分析中的具体实现。

1. 设计理念与架构分层

为了适应不同逆向工具的运行环境差异,项目采用了模块化设计,将系统拆分为两个独立的部分:

  • idamcp (静态分析)

    • 定位:专注于控制流图分析、伪代码生成等静态任务。

    • 架构:考虑到 IDA Pro 通常运行在 Windows GUI 环境下,该模块采用了 Client-Server 架构,通过 HTTP 桥接 LLM 与 IDA 内部环境。

  • pwnmcp (动态分析)

    • 定位:专注于运行时状态监控与漏洞利用。

    • 架构:部署于 Linux/WSL 环境,直接封装 GDB 和 pwndbg,负责与系统底层进行交互。

2. 静态分析模块:idamcp 的实现

在开发 idamcp 时,最大的技术挑战在于 IDA Pro 的线程模型。由于 IDA 的大部分 API 是非线程安全的,如果外部 HTTP 请求线程直接调用 API,极易导致宿主程序(IDA)直接崩溃。

2.1 核心难点:线程同步机制

为了解决这一问题,项目在 idamcp/scripts/ida_server.py 中实现了一个基于 BaseHTTPRequestHandler 的轻量级服务器,并引入了 idaapi.execute_sync 装饰器模式来进行线程调度。

代码实现:

# ida_server.py 中的核心同步逻辑
def do_POST(self):
    # ... 解析请求 ...
    
    # 1. 定义闭包,封装所有的 IDA API 调用逻辑
    def safe_execution():
        nonlocal response, status
        try:
            if path == "/decompile":
                response = self.handle_decompile(payload)
            # ... 分发其他路由 ...
        except Exception as e:
            # ... 异常处理 ...
            pass

    # 2. 关键机制:将 safe_execution 调度到 IDA 主 UI 线程执行
    # MFF_READ 标志表示这是一个读取操作,不涉及数据库修改,效率更高
    idaapi.execute_sync(safe_execution, idaapi.MFF_READ)
    
    # 3. 返回结果
    self._send_response(response, status)

机制解析:该设计保证了无论 HTTP 请求由何种后台线程发起,实际的数据访问操作均被强制串行化至 IDA 的主 UI 线程执行,从而完美解决了插件的稳定性问题。

2.2 数据清洗:伪代码预处理

Hex-Rays 反编译器的原始输出中包含大量用于语法高亮的隐藏控制字符(Color Tags)。这些字符对 LLM 来说不仅是噪音,还会显著增加 Token 消耗并引发模型幻觉。

代码在 handle_decompile 中引入了清洗步骤:

cfunc = idaapi.decompile(f)
sv = cfunc.get_pseudocode()

# 使用 idaapi.tag_remove 彻底移除颜色标签,只保留纯文本代码
code_lines = [idaapi.tag_remove(s.line) for s in sv]

3. 动态分析模块:pwnmcp 的实现

pwnmcp 的核心目标是将交互式的 GDB 命令行操作,抽象为 LLM 可理解、可调用的无状态原子操作

3.1 策略规划 (StrategyPlanner)

LLM 有时会陷入“不知从何下手”的困境。在 pwnmcp/pwnmcp/strategy/__init__.py 中,模块内置了一个确定性的规则引擎。它不依赖生成式模型的概率,而是基于经典的漏洞利用判据进行硬编码引导。

def _determine_approach(self, protections, dangerous_funcs, suspicions):
    # 规则 1: 栈溢出判定 (存在溢出函数 且 未开启 Canary)
    has_overflow_funcs = any(f in ['strcpy', 'gets', ...] for f in dangerous_funcs)
    
    if has_overflow_funcs and not protections.get("Canary"):
        # 规则 2: Ret2Libc 判定 (存在 system 函数 且 未开启 PIE)
        if has_system and not protections.get("PIE"):
            return "ret2libc"
        # 规则 3: Shellcode 注入判定 (未开启 NX)
        elif not protections.get("NX"):
            return "shellcode injection"

通过联合分析 checksec 的安全防护状态(NX, PIE, Canary)与导入表中的危险函数,该逻辑能自动输出最符合当前上下文的利用路径(Exploit Path),为 LLM 提供准确的初始引导。

3.2 自动化:一键计算溢出偏移

为了赋予 LLM 高效的“调试”能力,我们将繁琐的人工调试步骤封装为原子函数。以栈溢出偏移计算为例,run_with_gdb_pattern 函数实现了全自动化:

  1. 序列生成:自动生成指定长度的 De Bruijn 循环序列(Cyclic Pattern)。

  2. 执行注入:通过 GDB Python API 启动目标程序并将序列注入输入流。

  3. 异常捕获:监听 SIGSEGV 信号,精准捕获崩溃现场。

  4. 偏移演算:读取崩溃时的 RIP/EIP 寄存器值,反向计算其在原序列中的索引。

这一封装将原本需要数分钟的人工操作(生成->运行->查看->计算)压缩为单次工具调用,大幅提升了分析效率。

4. MCP 协议集成与状态管理

在协议适配层,项目使用 fastmcp 库暴露功能接口。

鉴于 MCP 协议本身是无状态的,而调试任务具有极强的上下文依赖(例如:需要记住上一步分析出的基地址),server.py 中引入了 SessionState 机制。

@mcp.tool()
def analyze_binary(path: str, deep: Optional[bool] = None) -> str:
    """静态分析二进制文件,并保存上下文"""
    
    # 1. 调用 StaticAnalyzer 获取信息
    facts = analyzer.analyze_binary(path)
    
    # 2. 持久化事实数据至会话状态
    # 这使得后续的策略规划工具可以直接读取本次分析的结果
    session_state.save_facts(facts.to_dict())
    
    return _json_ok(facts.to_dict())

通过文件系统对分析事实(Facts)、策略(Strategy)和调试偏移(Offsets)进行持久化存储,系统成功在无状态的 RPC 调用间维持了完整的调试会话上下文。

毕设开发 -- binarymcp
https://www.kiki1e.top/archives/bi-she-kai-fa----binarymcp
作者
kiki1e
发布于
更新于
许可