用于在反编译的二进制文件中识别漏洞的BianryNinja插件,同时支持程序化扫描和LLM辅助。
LLM 辅助的漏洞研究,用于 Binary Ninja。
VulnFanatic-NG 添加了一个侧边栏面板,扫描当前二进制文件,并询问一个 LLM — 默认是本地托管的与 OpenAI 兼容的模型,或 Anthropic Claude、Google Gemini、或 Azure OpenAI(见 LLM backends)— 来判断可疑代码是否真的存在漏洞。它主要基于 Binary Ninja 的反编译器输出(HLIL),必要时回退到汇编代码,并且只报告已确认的问题,附带可点击的代码引用。
扫描运行最多三个阶段(第三阶段是可选且仅在线):
查找在 rules/phase1_rules.json 中定义的危险函数的调用点 — strcpy、memcpy、sprintf/格式字符串、system、alloca、scanf、命令/执行 API、弱随机数生成、free/delete 系列(释放后使用 / 双重释放)、将不可信输入读入固定缓冲区(recv/read/fread/ReadFile)、SQL 注入(sqlite3_exec/mysql_query/PQexec)、禁用的 TLS 证书验证(SSL_CTX_set_verify/curl)、SSRF,以及不正确的权限管理(setuid/setresgid)、memset/bzero 系列,还有与攻击者控制长度进行比较(memcmp/strncmp → 认证绕过),涵盖 C/C++、Win32 和(尽力而为的)Rust FFI。覆盖范围包括加固后的 _chk(FORTIFY)和 Annex-K _s 变体。有界格式化输出函数(snprintf 及其变体)有自己的默认安全规则,因此正确的 size 参数不会报告为溢出。调用点通过三种方式查找:直接调用命名符号;通过转发桩 / PLT 存根路由的调用(恢复真正的调用者,因此不会漏掉仅通过存根访问的导入);以及 — 除非 vulnfanatic.scanIndirectCalls 关闭 — 通过函数指针或虚表分派的间接调用,Binary Ninja 已将其解析为危险函数。对于每个调用点,它构建一个过程间、以反编译器为中心的上下文,预算到一个 token 限制(默认 100k):
__*_chk 和边界检查的 *_s 变体带有额外的前导参数,会改变格式/大小/目标的位置,s->buf 这样的结构体字段解析为该字段的真实数组大小,而不是 s 的指针大小;类型部分中的 struct 定义也会携带每个字段的字节大小,0x40 或范围限定为 [0, 0xff]),模型将其用作将大小与缓冲区容量进行比较时的真实依据,而不是猜测,vulnfanatic.includeStackLayout),if/循环/switch 条件),MAIN→ABCD→strcpy,还包含 MAIN 和 ABCD 在其他地方调用的函数),因为它们可能包含约束危险值的边界/验证检查(vulnfanatic.includeCallPathSiblings,在预算允许时填充),以及recv/read/getenv)。该上下文加上特定于规则的提示被发送给模型,模型返回一个结构化的判定。非问题被丢弃。提示针对强大的本地代码模型(例如 Qwen2.5-Coder)进行了调整,并指示其分析整个流程并仅输出 JSON。
模型被指示偏好召回 — 报告合理的、与安全相关的问题,并通过 置信度 表达不确定性,而不是丢弃任何无法完全证明的内容。它在一个草稿框中展示其工作,其中引用了它所依赖的逐字代码片段(输入的源代码、每个守卫、大小/长度、相关类型和接收点),该草稿框存储在发现项中,以便您可以审计推理。
每个发现项都带有置信度(高/中/低):高 = 整个链都在上下文中显示;中 = 可能,推断出一个或两个链接;低 = 值得手动审查的线索。这是主要指标(模型的严重性估计是次要字段)。设置 vulnfanatic.minConfidence 以丢弃低于阈值的任何内容。
默认情况下,VulnFanatic-NG 偏向召回(捕捉真实问题)。如果误报太多,请使用以下任一方式收紧:
vulnfanatic.validationPass(默认关闭)— 运行第二次 LLM 检查,对每个标记的问题针对相同上下文进行双重检查(验证草稿框片段并重新追踪流程),并可以纠正判定或置信度。对标记的候选者,LLM 调用次数翻倍。
vulnfanatic.validatorModel(加上 validatorProvider / validatorBaseUrl / validatorApiKey)以在不同模型上运行第二次检查。来自独立模型的第二意见要有用得多——它共享更少的盲点,并且不太可能盲目赞同第一个判定(模型往往偏好自己的答案)。一个好的模式是级联:一个快速模型作为分析器(广泛召回),您最强的模型作为验证器,只对标记的候选者运行。留空验证模型以使用分析器模型进行验证。验证器应至少与分析器一样有能力——较弱的验证器大多会增加误拒。除 provider/base URL/key/model 外,所有内容都从分析器连接设置继承;空白的验证器密钥重用分析器密钥;如果验证端点不可达,则保留第一个判定(发现项永远不会因验证器中断而丢失)。vulnfanatic.minConfidence(默认 low)— 提升到 medium/high 以仅报告更强的发现。vulnfanatic.skipConstantArgCalls(默认关闭)— 跳过参数全部为编译时常量的溢出类调用点。速度。 大多数每次调用的延迟在于书面的推理,因此 vulnfanatic.verdictReasoning 控制模型写入多少:
concise(默认)— 简短的 1–3 句理由,无逐字代码。比 full 快得多且几乎没有精度损失;您还可以降低 vulnfanatic.maxResponseTokens。full — 带有引用片段的详细草稿框(最可审计,最慢)。none — 仅判定。最快;配合具有推理能力的后端(vulnfanatic.reasoningEffort),以便模型的内部思考完成工作。在普通的本地模型上,none 会失去精度(完全没有思维链)。始终开启的精度支持功能(它们告知模型而不抑制发现):
_s(Annex K)和 _chk(FORTIFY)变体以及长度限制的 API 视为安全,除非 size 参数本身错误。离线扫描按钮运行第一阶段且无模型——完全基于 phase1_rules.json 中每个规则的 offline 块中声明的编程启发式。它标记危险的调用点并消除明显安全的那些,分配启发式的置信度:
memcpy/memmove、来自常量字符串的 strcpy、具有常量格式的 printf、具有常量命令的 system 等——其控制参数是编译时常量因此不能是攻击者可控的调用。“常量”包括 Binary Ninja 的值集分析已将其固定为上游固定值的情况,而不仅仅是字面参数。strlen/大小比较、if (len < …))——包括在路径上调用的函数中——因此可能已经被处理。(仅提及变量而不进行比较的分支不再计入,消除了虚假降级的一个来源。)启发式使用规则中的一个小型声明性词汇表(constant_safe_args、eliminate_if_all_args_constant、format_arg_lookup、length_guard_vars、base_confidence、skip),由 Python 谓词评估——无需嵌入代码来 exec。大多数规则都有离线定义(溢出、格式字符串、命令执行、scanf、路径处理、弱随机数、弱数字解析、权限更改、分配大小等)。只有两个确实需要语义分析的类别在离线时被跳过,留给 LLM:free/delete 系列(释放后使用 / 双重释放,需要指针生命周期跟踪)和 TLS 验证(漏洞是一个特定的常量值,如 SSL_VERIFY_NONE)。离线摘要报告有多少站点被标记 / 消除 / 跳过(需要 LLM) / 失败,因此计数相加。这是一个快速分类;如需真实判断——以及针对跳过的类别——运行完整的 LLM 扫描。
离线发现仍然构建在线扫描会发送的相同完整的过程间上下文(仅针对标记的站点)并存储它,因此一旦您分类它们,它们可以像在线发现一样导出为微调数据。如果希望最大离线速度,请使用 vulnfanatic.offlineBuildContext 禁用。
仅在二进制文件看起来具有真实符号/变量名称时运行。定位在 rules/phase2_rules.json 中定义的安全敏感函数 — 认证、加密(包括弱算法)、签名/证书验证、会话/令牌处理、访问控制、秘密/密钥处理、输入验证、非常量时间秘密比较,以及不安全反序列化 — 通过函数名称和引用的字符串匹配,然后由模型审计。
一种固件加固审计,针对故障注入(电压/时钟/电磁故障注入)和侧信道(时序/功耗)攻击,基于硬件攻击缓解指南。与第一、二阶段(寻找漏洞)不同,第三阶段报告安全关键函数上的缺失或违反的加固控制 — 例如:默认失败分支、双重检查的安全决策、循环后的计数器验证、高汉明距离状态常量(相对于普通 0/1)、常量时间全长秘密比较、随机偏移的秘密访问/清除、先加密后验证(防 DFA)、控制流完整性计数器、避免用户态加密,以及不直接处理原始密钥材料(rules/phase3_rules.json)。
因为编译器优化可以剥离源代码级保护,这些控制最好在编译后的二进制文件上验证——正是此功能检查的内容。第三阶段是仅 LLM(在线)、符号门控,并且默认禁用;在新扫描选项卡上使用第三阶段复选框逐次启用(它从不以离线模式运行)。
发现项列在表格中(状态、置信度、阶段、CWE、函数、地址、标题),带有详细信息面板,显示解释、分析草稿框和验证注释。双击一行导航二进制视图到代码。
每个发现项从未分类开始。右键单击一行以设置其状态 — 标记为真实问题、标记为误报或标记为未分类。每次状态更改都会弹出一个**“提供原因:”文本框(原因与发现项一起存储)。表格使状态一目了然:真实问题为绿色/粗体并排序到顶部**,误报为灰色/删除线并排序到底部,未分类位于中间,带有其置信度颜色。摘要行显示计数。
每个结果选项卡都有一个导出已分类(微调)… 按钮,该按钮导出仅已分类的发现项(真实问题 + 误报)为 OpenAI 聊天格式 JSONL 以进行微调:每个示例将原始系统+用户提示与人工纠正的判定配对作为助手目标(误报教导 is_vulnerable=false 并带有您的原因;真实问题强化 is_vulnerable=true),因此您可以迭代改进模型在您的二进制文件上的准确性。
详细信息面板中显示的每个发现项的上下文(并用于重建微调提示)默认完整保留 — 由 vulnfanatic.storedContextChars 控制(0 = 无限制;设置一个正的上限,例如 4000,以限制 BNDB 增长,但代价是上下文保真度)。
面板是选项卡式的。第一个选项卡始终是新扫描,您可以在此设置:
<timestamp> <mode>,例如 2026-06-15 14:03:50 offline),然后按开始扫描或离线扫描。每次运行都会打开自己的结果选项卡,发现项实时流式传输到其中。所有扫描都存储在 BNDB 中,因此例如您可以保留离线扫描,稍后添加在线扫描,或并排比较使用不同规则集的运行 — 当您重新打开数据库时,它们会作为选项卡重新出现。关闭选项卡将永久从 BNDB 中删除该扫描 — 为防止意外,它会弹出一个确认框,需要勾选 “我确认我将永远失去来自 的结果。” 后才能激活 永久删除结果 按钮。导出当前扫描… 将选中的选项卡写入 Markdown/JSON。
每个打开的二进制文件都有其自己的独立面板状态 — 自己的扫描选项卡和正在运行的扫描。在一个二进制文件中启动扫描并切换到另一个二进制文件会显示第二个二进制文件的结果(并允许您单独扫描它);第一个二进制文件的扫描在后台继续运行,并在切换回来时保持不变。
此插件的包文件夹名为 vulnfanatic_ng(一个有效的 Python 标识符 — Binary Ninja 将插件文件夹名称作为模块导入,因此像 VulnFanatic-NG 这样的连字符名称将无法加载)。
(可选) 将准确的 token 计数安装到 Binary Ninja 的 Python 中: ``` pip install tiktoken
符号链接或复制 vulnfanatic_ng 文件夹到你的 Binary Ninja 用户插件
目录:
~/Library/Application Support/Binary Ninja/plugins/~/.binaryninja/plugins/%APPDATA%\Binary Ninja\plugins\例如,在 macOS 上: ``` ln -s "$(pwd)/vulnfanatic_ng" "$HOME/Library/Application Support/Binary Ninja/plugins/vulnfanatic_ng"
重启 Binary Ninja(或运行 重新加载插件)。右侧边栏会出现一个 VF 图标。
打开 设置(齿轮图标 / 编辑 ▸ 首选项 ▸ 设置)并搜索 vulnfanatic。至少设置以下项: