
前几年做路由器固件分析的时候最折磨人的环节永远是打补丁。厂商悄悄发一版固件修复了三四个漏洞但从不告诉你修了什么。想在几万行反编译代码里把修复点捞出来靠肉眼扫就是大海捞针。后来我试着把大模型引入补丁分析把“找修复点—判漏洞成因—追调用链—拼漏洞链”这套流程逐步固化成一个工作流实测下来速度提升明显踩坑也不少。这篇就把这套方法论、提示词模板、常见坑位完整拆出来给正在啃路由器固件、挖洞、做应急响应或者SRC的同行一个可复用的参考。这篇内容比较适合两类人一类是刚接触固件安全想系统搞懂补丁分析怎么下手的新手另一类是已经会用Ghidra/IDA但觉得纯逆向效率太低想借助大模型做自动化提升的老手。文章不会教你堆一整套复杂平台只讲一台普通电脑、一个开源反编译工具加上一个能跑清楚上下文的大模型怎么把一条高危漏洞链完整拆出来。1. 为什么补丁分析是大模型最适合干的脏活1.1 厂商补丁等于最精准的漏洞说明书很多人喜欢直接从流量、web接口、内核模块去逆整个路由器固件找洞这事不是不行但效率很低。真正高效的做法是盯补丁。厂商每次发布新固件本质上就是一份比漏洞公告详细得多的“漏洞说明书”——只不过这份说明书是用代码差异写成的。修复代码和漏洞代码之间的差值精确到函数级别、行级别甚至变量级别。你只要对比出新旧固件的diff就能在极小范围内定位到漏洞所在。这比从零开始逆几十MB二进制运气成分低得多。这也是很多安全研究员常说的N-day转0-day路径补丁分析是N-day的入口而理解了修复思路能推测出攻击者绕过修复的可能路径这就是0-day方向的思考。大模型在其中扮演的角色很微妙它不负责找洞但它能把“看懂代码差异”这件事的耗时压缩一个量级。1.2 传统分析流程的三个痛先看看没有大模型时补丁分析最痛苦的是什么。第一个痛是慢。拿到新老两个固件binwalk解包后在几十个二进制里定位到可疑diff这一步是体力活。用BinDiff或Diaphora比对目标文件会给出几百上千个函数差异人工逐一过一遍运气好一上午运气差一整天。第二个痛是“看得懂函数、看不懂语义”。Ghidra反编译出来的代码能看出它调用了strcmp、strlen这类库函数但搞不清楚这段代码在业务上干嘛——它检查的是用户登录态还是固件签名这个校验一旦缺失会造成什么后果这种上下文语料存在于整个固件的字符串引用、函数调用关系、SQLite数据库路径、web页面脚本这些分散信息里人工串起来非常费劲。第三个痛是输出。分析完成以后写一份带调用链、带影响评估的报告是硬性要求。但整理函数名、地址、调用关系、补丁差异描述这些琐碎信息消耗的是纯工时价值却不在工时本身上。大模型恰恰在这三个痛点上有天然优势处理长文本快擅长跨语言找语义联系而且聊天式交互天然适合“边看边问边整理”的工作习惯。把大模型当成一个读过几万份反编译代码的高级实习分析师你负责决策它负责快速阅读和整理整个流程顺很多。2. 整条工作流长什么样六步闭环拆解2.1 定边界先搞清楚你比对的到底是什么动手之前先把边界钉死。要明确三件事目标设备是哪个厂商哪个系列目标版本是多少比如固件A是受影响版本固件B是修复版本重点组件是哪个web后台守护进程、加密解密库、网络服务进程、内核模块还是第三方组件如嵌入式Web服务器、智能家居网关。边界不清晰是补丁分析中非常常见且致命的开始。我见过同行直接把两个跨大版本的固件丢进BinDiff结果diff出一千多个函数里面还混着大量架构升级、编译器优化选项调整带来的噪音差异。这样不但浪费时间diff结果也没法信。正确做法是按照CVE公告、厂商release note、甚至网友在论坛上的版本抱怨来锚定版本最好能拿到只差一两个修补项的相邻版本。版本粒度越细diff噪音越小后续越省力。2.2 固件获取与解包一个容易被低估的环节拿到正确固件本身就是门槛。厂商官网下载中心、开发者站点、路由器系统的固件检查更新接口这些都是常规合法渠道。你自己的设备也能通过命令行直接备份当前运行固件有些品牌的恢复模式也允许导出。需要注意合规边界分析的固件必须是你合法取得或有权分析的版本这一点没什么可含糊的。解包常用工具是老朋友binwalk但说实话现在很多新固件已经把binwalk默认签名库坑得差不多了需要加-Me递归解包或者换用unblob支持更多压缩格式。有些厂商会给固件加头部/加密这时就得先识别头部格式或者借助已知的密钥/工具去解密。遇到实在解不开的去GitHub上找固件解包工具集很多逆向爱好者已经踩平了坑直接站在别人肩膀上即可。解包后会得到一个或多个文件系统镜像squashfs、jffs2、ubifs用unsquashfs或sasquatch解开里面就是/bin、/usr/sbin、/www、/etc这些结构清晰的目录树。分析对象通常锁定在二进制文件上但/etc的配置文件和/www的web脚本往往藏着关键字符串和功能线索别忽略。2.3 二进制diff与函数定位确定要重点解剖的目标拿到新旧两个版本中对应的二进制文件比如/usr/sbin/webs或/bin/goahead用DiaphoraGhidra插件或BinDiffIDA插件做函数级别的对比。工具会把两个二进制里对应的函数按相似度归类相同函数、变更函数、新增函数、删除函数。这一阶段关注两类函数变更函数代码被修改了大概率是安全修复或功能调整新增函数可能引入新的校验模块或者对应的检查逻辑。一个典型的“补丁分析起步区”大概是这样的函数相似度在85%-99%之间的小改动很可能是加了一行校验、换了一个指针、补了一个长度检查相似度低于70%的大量改动多半是加了新的功能模块需要小心排除。拿到diff结果后优先在变更函数里找那些名字带check、auth、verify、token、valid、login、decode、parse的。这些函数名暗示了安全语义。但很多厂商会去符号表函数名变成FUN_00012345这时就需要结合字符串引用来判断函数用途。也别只盯着外部函数一个在main里悄悄多出来的if判断也许就是整个漏洞链的关键缺口。2.4 反编译与语义重建大模型的第一落点定位到目标函数后分别把新旧版本用Ghidra反编译成C伪代码。这一步是把二进制差异转化为可读逻辑差异的关键转换。大模型直接看二进制不行但看伪代码没问题。把新旧两个函数的伪代码、关键地址、涉及的全局变量、调用的库函数信息一起发送给大模型。提问框架一般是“这是同一个函数在打补丁前后的两个反编译版本。新版本增加了哪些行为这些新增行为对应什么安全检查如果补丁缺失攻击者可以利用什么漏洞请以JSON格式输出。”这种提问方式之所以有效是因为大模型能够“注意到”新旧代码之间的语义差异。比如旧版直接把输入字符串拼进sprintf新版改用snprintf并限制长度大模型会指出这是长度限制很可能缓解了缓冲区溢出如果新版加了一个对参数中;、、|等字符的过滤大模型会判断这是命令注入修复。这类模式识别很快你确认后记下来比人工逐行比对快得多。2.5 漏洞成因定性把diff翻译成漏洞类型这一步是把“代码差异”翻译成“漏洞语义”。常见补丁差异模式有这么多补丁差异模式可能的漏洞类型特征代码增加长度检查或边界校验缓冲区溢出、整数溢出、越界读写if (len MAX) return -1;增加字符串过滤函数命令注入system()前过滤;增加指针判空空指针解引用if (ptr NULL) exit(0);增加权限判断或加盐哈希认证绕过、密码学弱点多处校验session有效期更换更安全的函数格式化串漏洞、溢出sprintf→snprintfstrcpy→strncpy增加锁或原子操作竞态条件信号量、互斥锁的引入增加加载器的校验固件签名、命令执行、脏数据校验魔数、长度、校验和补丁分析的目标不是找出所有差异而是找出哪些差异是安全相关的。有经验的分析师会在这个环节形成肌肉记忆看到sprintf换成snprintf就警惕全函数所有字符串拼接看到system函数前新增的过滤函数就重点摸一遍这个system的调用路径和入参来源。大模型在这里的用法是拿来“头脑风暴”的——你把自己看到的关键差异发给它让它列出可能的漏洞方向再结合手工验证。2.6 从单个洞到漏洞链调用链分析与可达性判断如果一个洞无法被外部输入触达它就只是个理论缺陷不构成实际威胁漏洞链还差最后一环证明外部流量能到达函数入口。这个过程叫可达性判断。举个例子你发现补丁在某函数里加了strncmp(auth_token)的校验说明漏洞点是“缺少身份认证的数据操作”。但还需要回答几个前置问题这个函数的上级调用方是谁上级调用方又是被谁调用的外部输入是来自HTTP请求的POST参数还是来自UDP包的内容如果是后台网络管理页签是否需要登录把这些调用关系从上到下串起来就是完整调用链。传统做法是Ghidra里手动追找xref引用一层层往上翻。有了大模型以后你只需要把关键函数以及它引用的全局变量、字符串、被调函数列表发给大模型用“调用链分析模式”提问大模型就能根据以往训练见过的大量路由器代码库内容给出可能的调用路径建议你再回Ghidra里验证节点是否真实存在。多数情况下建议路径能命中70%以上剩下30%需要人工校正。需要特别注意的是大模型给的调用链不一定真实存在它更接近“根据上下文推断的最可能路径”所以每个节点的函数名和地址都必须在你的Ghidra工程里确实存在才行。3. 大模型辅助的四个关键环节3.1 第一步包装成“可回答”的上下文而不是直接扔代码直接给大模型扔一堆反编译代码然后问“哪里有问题”大概率得到一堆笼统建议。正确的姿势是结构化描述上下文。我常用的prompt框架长这样你是一名资深路由器固件安全研究员。下面我给你两段Ghidra反编译伪代码它们来自同版本软件打补丁前后的两个函数。补丁版本可能修复了一个或多个漏洞。 请按以下步骤分析 1. 对比两段代码的差异点 2. 判断每个差异点对应的漏洞类型若存在 3. 评估攻击者若利用此漏洞需要什么前置条件 4. 输出JSON字段包括diff_summary、vuln_type、attack_condition、risk_level、patch_effect。这个prompt的关键在于给出角色让大模型调用安全研究的专业知识储备明确任务对比差异、判漏洞、给条件指定输出格式JSON便于你自动提取信息、批量处理多个函数不预设答案避免大模型为了迎合你而硬编一个漏洞解释。实际执行时我发现大模型对“对比两段代码”的处理能力远强于“找某段代码里的漏洞”。因为对比任务明确圈定范围小模型容易聚焦而开放式找漏洞模型容易陷入“陷阱”在一堆无关代码里猜。所以一定要把差异先手动或半自动地锚定出来再让大模型做语义解读。3.2 第二步用结构化输出逼出确定答案大模型最大的缺点之一就是废话多跑题远。补丁分析是研究活不是聊天活所以我从第一天开始就强制所有分析问答必须带输出格式约束。一个我屡试不爽的JSON schema变体{ functions_diff: [ { function_name: sub_12345, new_func_offset: 0x12345, old_func_offset: 0x23345, patch_type: add_check, vuln_category: command_injection, root_cause: 未过滤payload中的分隔符, severity: high, pre_condition: 需要web后台认证, suggestion: 对所有input参数增加黑名单过滤并限制长度 } ] }强制结构化输出有几个好处一是防止模型出现“这个函数可能和内存安全有关”这类空泛结论二是方便后续写脚本批量汇总多个函数分析结果生成项目级报告三是在后续追踪调用链时可以直接用这个JSON作为下一个prompt的输入片段实现多轮分析的数据传递。如果模型输出格式不稳定在prompt里再补一句“只输出JSON不要任何解释”。实测能显著提升格式遵守率。对于中文支持不太好的模型可以考虑用英文prompt英文输出分析结论再翻译成中文逻辑一致性反而更好。3.3 第三步跨函数调用链的图推理单个函数分析完毕之后要开始拼接调用链。这个环节大模型的用法不是一次性扔几十个函数而是分层次递进。第一轮拿到入口函数比如web处理函数问它“这个函数的直接调用者是谁基于字符串引用和函数名猜测”第二轮把猜测的调用者回Ghidra验证把真实xref找到再问大模型“这个调用者主要接收什么输入数据”第三轮把输入数据格式、处理函数、漏洞函数三者放进一个prompt里让模型描述一条完整攻击链。这个递进式过程中有一个关键技巧中间结果以代码块形式回灌。比如第一轮的输出是entry_points: [http_request_handler_0021, system_config_apply]那么第二轮prompt里我会把这串输出原样粘贴进去让模型基于它继续推理而不是让它重新理解一遍上下文。这样多轮迭代后模型会越来越接近真实链路幻觉率也会下降。3.4 第四步交叉验证治大模型的幻觉用过几次大模型做安全分析就会发现它给出的函数名可能是它梦里编的函数逻辑可能张冠李戴。尤其是反编译代码里大量的FUN_命名、全局变量地址、没有符号的真实地址模型经常“自洽”地编出看起来很像真的假结构。对付幻觉的核心策略只有一条让模型给线索人工验证落地。举一个实际案例大模型分析某个补丁函数时断言“此处调用strcpy可能造成栈溢出”。我回Ghidra一查这个函数里的调用其实是memcpy而非strcpy长度参数还经过严格计算。模型是对的——它判断大方向“拷贝函数”但它把具体函数名搞错了。如果我盲信就会在报告里写一个错误的漏洞依据如果我只用它的结论当线索再去Ghidra里手动确认实际调用就能得到准确结论。所以我在所有prompt模板里都强调“如果某段代码没有在伪代码中出现请不要臆测”。同时我也会人工抽样检查10%-20%的模型结论确保整体分析质量。4. 实战回放一个路由器命令注入漏洞链的拆解过程4.1 发现入口配置导入功能的patch差异某品牌路由器发布了新固件版本比上一版只多了一个安全补丁。官方只写了一句“修复若干安全问题”。我把新旧固件用unblob解开提取/usr/sbin/lighttpd相关的后台组件用Diaphora比对后发现一个函数有显著修改。旧版伪代码大致长这样void handle_config_import(char *config_data, int len) { char command[512]; sprintf(command, echo %s /tmp/config.env, config_data); system(command); }新版伪代码void handle_config_import(char *config_data, int len) { char command[512]; if (strstr(config_data, ;) || strstr(config_data, ) || strstr(config_data, |) || strstr(config_data, )) { log_error(invalid config data); return; } snprintf(command, sizeof(command), echo %s /tmp/config.env, config_data); system(command); }这个diff极其典型新版加入了;、、|、\四个字符的过滤同时把sprintf换成snprintf。虽然snprintf限制长度是防溢出但根本的安全改动是输入过滤——说明这里是一处命令注入修复点。漏洞根因配置导入接口接收外部传入的字符串直接拼接进shell命令执行攻击者能通过注入特殊字符执行系统命令。此时漏洞链有了第一环。4.2 第一次大模型问答判断新代码块的安全本质我把这两个伪代码块发给大模型用的是前面提到的结构化prompt。模型返回的JSON里patch_type标记为input_filter_addedvuln_category标记为command_injection同时给出了它的理由“新版过滤了shell元字符说明旧版对config_data未做任何清洗。考虑到配置导入功能通常会由Web管理接口调用攻击者可能通过发送经过特殊构造的HTTP请求触发命令执行。不过此处system执行的是“echo”命令即使注入payload也需要能逃逸出拼接上下文。”模型判断准确吗准确得有点超出预期。它甚至提醒了payload逃逸问题——因为拼接格式是echo %s /tmp/config.env攻击者需要闭合掉echo命令的语法上下文才能执行自己的命令这可能要求payload里先使用;分隔或使用换行符。新版补丁恰恰把;过滤了连命令分隔的机会都堵死。这说明修复是有针对性的。4.3 追调用链外部输入一步步走到漏洞函数拿到第一环后下一步要确认攻击者如何触达该函数。我在Ghidra里对handle_config_import执行References → Find references to看到三个调用点/www/cgi-bin/admin/config.cgi的字符串引用lighttpd的URL分发表一个处理POST请求的process_post_data函数。这个链路意味着外部攻击者无需认证直接向某个开放的管理接口发送POST /admin/config.cgibody参数config_datax就可以把数据传进handle_config_import。实际测试时这种管理接口往往还需要登录token但有些厂商为了方便会把这个配置导入接口开放给局域网所有设备甚至不做session检查这种情况下漏洞可利用性会直接提升。我用第二轮大模型prompt把三个调用点名称、函数摘要、POST参数格式一起发给模型模型给出的结论是“该接口是配置备份恢复功能其参数可直接控制文件内容结合漏洞点命令注入条件完全满足无需特殊权限。”这个结论需要人工确认。我回去查了/www/cgi-bin/admin/config.cgi的shell脚本发现它确实只是调用了二进制里的函数没做任何额外的token检查或来源限制。至此调用链打通HTTP POST /admin/config.cgi→ 解析config_data参数 →handle_config_import→ 拼接命令 →system()执行。4.4 从低权限到root漏洞链的最后一环命令注入之后攻击者能拿到的是命令执行能力但它运行在什么权限下决定了这条链的最终影响。我查看了lighttpd进程的用户权限它以nobody身份运行。只拿到nobody命令执行权限连/etc/shadow都读不了影响力有限。于是继续追提权。在这里我看到旧版固件的lighttpd进程在启动时在某些版本中以root直接运行但新版本已经改为降权至nobody所以需要一条提权路径。我把这个过程也用大模型辅助了一遍让模型基于“设备以nobody运行、存在命令注入点、补丁固件中有setuid组件”这些前提推断可能的提权路径。模型提到几个常见方向固件中是否有setuid、setgid标志的root权限辅助二进制是否有可写的PATH目录存在无路径bash调用配合环境变量劫持是否开放了某些本地socket服务nobody可以往里面写入畸形消息触发root上下文。我在固件目录里翻了翻发现了/usr/sbin/upgrade_check这个二进制它带有setuid root权限且版本较老没有做输入过滤能接受命令行参数并把它直接拼进curl命令。这个逻辑几乎等于一个“本地小助手”普通用户权限下把任何参数传给upgrade_check就能以root的权限发起任意命令。实测时我确认upgrade_check以root运行它的逻辑是读取参数后调用system完成任何命令执行。配合前面命令注入拿到的nobody执行能力攻击者可以先创建恶意脚本文件到可写目录再调用upgrade_check以root权限执行脚本完成提权。至此漏洞链完整未认证的HTTP请求 → 命令注入nobody → 提权至root → 完全控制路由器。4.5 汇总报告输出让大模型写初稿分析完成后报告整理同样交给大模型。我把整个分析过程中的关键函数、地址、调用链、漏洞类型、补丁差异汇总成一个markdown段落让模型按“漏洞概要、根因分析、影响面、攻击路径、修复建议”的结构来组织。模型能在几十秒内产出一版正式报告我再人工审核补充证据链截图。报告里的技术细节必须准确但初稿生成这步已经能帮我省出半小时到一小时的时间。5. 实操中踩过的坑与排查清单5.1 固件解包失败与版本不匹配这是第一个翻车概率最高的地方。用binwalk解不开新固件时不要连续浪费时间硬抗换个思路用unblob跑一遍它支持更多魔数和压缩算法组合或者干脆看固件头常见是TRX(Broadcom)、D-Link等找到对应格式解析工具。版本不匹配是另一个高频坑有些人喜欢下载最新的官方固件和最早的版本对比结果diff爆炸函数相似度普遍低于30%全是无效工作。尽量找相邻版本、或者厂商大版本更新里单独的安全修复版对比效果才是最好的。5.2 diff噪音过滤就算版本选对了diff结果里仍然会混入大量编译器选项差异。路由器厂商经常换交叉编译工具链同一份源码在不同编译器下生成的函数布局、指令选择、栈帧安排都不同BinDiff会把这些也识别为“变更函数”。解决办法是跳过相似度低于40%的函数聚焦相似度85%-99%的微变函数同时对BinDiff的结果过一遍“函数内指令差异”只挑逻辑分支、条件跳转、比较操作、系统调用相关的变化。这一条经验能过滤掉至少一半噪音。5.3 大模型幻觉函数名与地址大模型特别擅长一本正经地编造不存在的函数名或地址。根源在于它把反编译代码当成了“训练时见过的海量代码的类比”来处理而不是真正的机械逻辑。防幻觉手段prompt中明确写“只能基于给定代码回答不要臆测函数名”每个它提到的函数名、偏移量必须回Ghidra验证结论用于推导不直接引用抽样交叉验证至少20%的模型结论。5.4 环境不一致导致的误判一种典型的误判场景分析的是MIPS架构的路由器固件我把伪代码逻辑直接等同于x86逻辑导致对“栈溢出是否可行”“格式化字符串可利用性”的判断出现偏差。路由器固件常见架构是MIPS、ARM、RISC-V、PowerPC大模型在识别架构相关的溢出字长、字节序、对齐规则时也经常出错。所以在prompt里必须标明“Target architecture is MIPS32r2, little-endian, noPIE, no canary.”清晰地告诉模型内存布局推送准确性会显著提升。5.5 合规边界必须划清固件是你合法拥有的设备上提取的或者从厂商官方渠道下载的分析结果用于自身安全研究、漏洞挖掘、漏洞报告这本身是正常的防御研究活动。但我必须提醒不要使用未经授权获取的固件、内部泄露包或超出授权范围的测试目标。固件分析这件事的底线和所有安全研究一样——边界清晰获取途径合法研究过程合规。分析结论公开传播时不要包含完整的攻击利用代码披露要遵循负责任的流程。6. 工具链与提示词速查表6.1 工具清单环节工具作用与说明固件获取厂商官方下载站、设备系统备份导出优先选择官方渠道合法合规固件解包binwalk、unblob、unsquashfs、sasquatchunblob对新固件兼容性更好解不开就查固件头格式二进制diffDiaphoraGhidra插件、BinDiffIDA插件优先选择相邻版本过滤低相似度噪音反编译Ghidra免费、IDA ProGhidra够用IDAPython脚本化更方便交叉验证Ghidra手动xref、strings、readelf、objdump每个大模型结论都必须落地验证大模型辅助分析本地部署Qwen等模型或商业API接口本地保护隐私代码API速度快、上下文长报告生成Markdown模板 LLM初稿初稿生成人工审核效率最高6.2 提示词基线模板补丁差异分析专用模板可直接复制改造你是一名资深嵌入式路由器固件安全研究专家。 请分析以下两段Ghidra反编译伪代码它们来自同一函数在补丁修复前后的版本。 要求 1. 对比差异点并按安全语义分类 2. 判断每个差异点对应的漏洞类型 3. 评估漏洞的可利用前提条件 4. 只输出JSON不要任何解释文字。 JSON字段function_name, changed_code_summary, vuln_type, risk_level, attack_condition, patch_effect。 如果某结论没有代码证据支撑请在patch_effect中明确写推测。 伪代码如下 [新版本代码] [旧版本代码]调用链分析模板请基于以下反编译函数片段和函数间引用关系推断从外部输入到脆弱函数的调用链。 只使用给定信息不要臆测未见函数。 输出格式调用链节点数组每个节点包含function_name、offset、input_source、notes。 上下文信息 [函数片段与引用关系列表]6.3 后续扩展把工作流接上自动化平台到这一步为止我在本地手工用命令行完成大部分操作。后续如果整个流程需要日常重复使用可以做成半自动管线用Ghidra的Python接口脚本化完成“解包→diff→函数筛选”输出候选函数列表再用脚本调用大模型API自动执行补丁差异分析最后把JSON报告汇总到Markdown或Notion页面。已经有不少人把这种流程接到n8n或Coze这类工作流平台上定时监控厂商发布新固件、自动跑一轮补丁分析、把结果推送到企业微信或钉钉群。这套自动化对做产品安全的团队尤其有价值能第一时间感知厂商安全动向。对我个人来说最实用的扩展是把这款流程嵌入到SRC项目日常监控中每收到一个厂商新版本固件就自动跑一遍diff如果发现有疑似安全修复点立即转人工深挖。长期运转下来收获远大于手工零碎分析。最后说点个人体会。用大模型做补丁分析真正改变的不是“找漏洞”的能力而是“分析节奏”。以往在Ghidra和IDA之间反复横跳、在几百个函数之间做语义判断的枯燥时间被压缩成了“验证—假设—再验证”的高频循环。大模型的每一次回答未必都对但它给了你一个非常快的起点你只需要判断方向、验证关键节点就能把漏洞链迅速拼完整。补丁分析没有银弹但大模型确实让这活儿从“体力活”变成了“脑力活”。我建议所有做固件安全的同行都花一个周末把这套流程跑一遍感受一下差别。