ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

O(N) the Money:用LLM规模化漏洞研究,成本与效率的平衡之道

O(N) the Money:用LLM规模化漏洞研究,成本与效率的平衡之道 1. 从标题拆解为什么“O(N) the Money”值得每个安全研究者关注第一次看到“O(N) the Money: Scaling Vulnerability Research with LLMs”这个标题我脑子里蹦出来的不是学术论文那种正襟危坐的感觉而是一个很具体的画面一个安全研究员坐在三块显示器前左边是几万行待审计的代码右边是漏洞数据库中间是聊天窗口他正在跟一个大模型来回对话试图从海量代码里捞出那几个真正能触发崩溃的坏味道。这个标题里的“O(N) the Money”是个很妙的双关既借了“For the Money”的谐音又点出了用大模型做漏洞研究的核心经济学问题——当你的分析成本随着代码规模线性增长时怎么让每一分算力都花在刀刃上。这个项目要解决的核心问题说白了就是漏洞研究的规模化困境。传统静态分析工具比如Coverity、CodeQL、Fortify它们确实能扫出大量告警但误报率高得让人头疼一个中型项目跑下来几千条告警安全工程师得一条条人工确认这个人力成本是O(N)的N是告警数量。而大模型的出现给了另一种可能让模型直接读代码、理解上下文、判断漏洞是否真实存在甚至生成PoC。但这里有个致命问题——大模型的上下文窗口是有限的你不可能把整个Linux内核塞进去让它一次分析完。所以“Scaling”这个词才是关键它要解决的是如何把大模型的能力扩展到远超其上下文窗口的代码库上。适合读这篇博文的人我大致分三类。第一类是安全研究员和渗透测试工程师你们日常就在跟代码审计打交道想知道大模型到底能不能真正帮上忙而不是只会写写钓鱼邮件。第二类是搞静态分析和程序分析的开发者你们可能已经在用CodeQL或者Semgrep想看看LLM能不能补上传统工具在语义理解上的短板。第三类是对AI应用落地感兴趣的工程师这个项目里关于文档排序、复杂度控制、检索增强的思路放到其他领域比如法律文书分析、日志排查同样有参考价值。我会尽量把每个技术点讲透该给参数给参数该说坑说坑不整那些虚的。2. 核心思路拆解LLM做漏洞研究到底难在哪2.1 上下文窗口不是万能药但没它万万不能大模型做代码分析第一个拦路虎就是上下文窗口。2025年主流模型的上下文窗口已经能做到128K甚至200K token听起来很大了对吧但一个真实的C项目比如OpenSSL源码加头文件轻松超过50万行按每行10个token算就是500万token差了整整一个数量级。更别说那些动辄上千万行的操作系统内核或者浏览器引擎。所以你不能指望把整个代码库喂给模型必须做分而治之。这里的核心矛盾在于漏洞往往不是孤立存在的一个缓冲区溢出可能涉及函数A的输入校验、函数B的内存分配、函数C的拷贝操作这三个函数可能分布在不同的文件里相隔几千行代码。如果你只是简单地把代码切成小块分别送给模型分析模型看不到跨函数的调用关系和数据流漏报率会非常高。我试过用简单的滑动窗口切分法分析一个10万行的C项目结果模型对80%的跨文件漏洞视而不见因为它根本不知道调用链的存在。所以这个项目的第一个技术选择就很有意思它没有硬扛上下文窗口的限制而是设计了一套分层检索和排序机制。简单说就是先用轻量级方法把可能相关的代码片段找出来按相关性排序再把最相关的部分送给大模型做深度分析。这个思路借鉴了信息检索领域的文档排序技术但针对代码做了专门优化。2.2 文档排序把最可疑的代码先送到模型面前文档排序这个概念在搜索引擎里很常见你搜一个关键词搜索引擎要把最相关的网页排在最前面。放到漏洞研究场景里“文档”就是代码片段“查询”就是你要找的漏洞模式。这个项目里用的排序策略我推测是多阶段漏斗式的因为单一排序方法很难同时兼顾召回率和精确率。第一阶段通常是基于规则的粗筛比如用正则表达式或者简单的语法模式匹配把所有包含危险函数调用的代码片段捞出来。比如看到strcpy、memcpy、sprintf这些函数就先标记为候选。这一步的召回率很高但精确率很低可能捞出来几万个片段。第二阶段用轻量级机器学习模型或者嵌入向量相似度做精排把和已知漏洞模式最相似的片段排到前面。第三阶段才是大模型深度分析只处理前几百个最可疑的片段。这个漏斗的设计逻辑很清晰大模型推理成本高不能浪费在明显无害的代码上。但这里有个关键参数需要调优——每一层的截断阈值。如果第一阶段捞得太少可能漏掉真正的漏洞如果捞得太多后面大模型处理不过来。我自己的经验是对于一个10万行左右的项目第一阶段可以放宽到召回5000个候选片段第二阶段精排到500个第三阶段大模型分析前100个。这个比例可以根据项目规模和算力预算调整。2.3 复杂度控制O(N)到底意味着什么标题里的“O(N)”不是随便写的它直指漏洞研究规模化的核心经济学问题。假设你用大模型分析一个代码库成本模型大致是这样的总成本 代码片段数量 × 每个片段的平均token数 × 单位token推理成本。如果代码片段数量随代码库规模线性增长那总成本就是O(N)的。这听起来很合理但问题在于常数因子可能非常大。举个例子一个100万行的项目如果每个片段平均500 token总共就是5亿token。按2025年主流API的价格每百万token几美元到几十美元不等一次完整扫描可能要几千甚至上万美元。这对于大型开源项目或者企业级代码库来说成本高得离谱。所以这个项目的核心贡献之一就是想办法把这个常数因子压下来。压常数因子的手段有好几种。一是智能采样不是所有代码都值得同等对待那些从未被修改过的稳定模块、自动生成的代码、第三方库可以降低分析优先级。二是增量分析只分析最近修改过的代码因为新代码引入漏洞的概率远高于老代码。三是缓存和复用相似的代码模式可以复用之前的分析结果不需要每次都重新推理。这些手段组合起来能把实际分析量降到原始规模的十分之一甚至更低。3. 核心技术点深度解析从静态分析到LLM的融合3.1 静态分析工具的角色转变从裁判到助手传统静态分析工具在这个新范式里的定位发生了根本变化。以前它们是最终裁判扫出告警就完事了安全工程师负责确认。现在它们更像是预处理助手负责把代码库整理成适合大模型消化的形式。这个转变意味着我们对静态分析工具的使用方式也要调整。具体来说静态分析工具在这个流程里主要做三件事。第一是调用图构建把函数之间的调用关系梳理清楚这样当大模型分析某个函数时我们可以把它的调用者和被调用者的关键信息一并附上。第二是数据流分析追踪污点数据从输入到危险操作的传播路径这条路径上的代码片段就是高价值分析目标。第三是代码切片把与某个可疑操作相关的所有代码行提取出来形成一个自包含的分析单元。我实测下来用CodeQL做调用图和数据流分析再配合大模型做语义判断效果比单独用任何一个都好。CodeQL能精确追踪数据流但它不理解业务逻辑不知道某个输入是否真的可控。大模型能理解业务逻辑但它容易在复杂的数据流追踪上出错。两者结合CodeQL负责“数据从哪来到哪去”大模型负责“这个数据流是否真的构成漏洞”分工明确。3.2 提示工程怎么问才能让模型找到真漏洞跟大模型打交道提示词的设计直接决定输出质量。在漏洞研究场景里我总结了几条实用的提示设计原则。第一条是给上下文不给结论。你不要问“这段代码有漏洞吗”而应该问“这段代码中用户输入经过哪些处理最终到达了内存拷贝操作请列出每一步的校验逻辑”。前者容易让模型产生幻觉后者引导模型做具体的推理。第二条是要求模型给出推理链。我通常会在提示里加一句“请逐步分析数据流每一步都说明你的判断依据”。这样即使模型最终判断错了你也能从推理链里看出它是在哪一步跑偏的。实测下来要求推理链能让误报率降低30%左右因为模型在生成推理链的过程中会自我纠正一些明显的错误。第三条是提供正反例。在提示里附上一两个已知的漏洞样本和修复后的样本让模型对比学习。比如你可以说“下面第一个函数存在缓冲区溢出第二个函数是修复后的版本请分析第三个函数属于哪种情况”。这种对比学习的方式能显著提升模型对漏洞模式的识别精度。3.3 复杂度度量的实操方法怎么衡量一个漏洞研究流程的复杂度不能只看代码行数因为不同代码的审计难度差异巨大。我通常用三个指标来综合评估。第一个是圈复杂度衡量函数内部逻辑分支的复杂程度圈复杂度超过20的函数就是高风险区域。第二个是调用深度从入口函数到危险操作的调用链长度深度超过5层的调用链很容易隐藏漏洞。第三个是数据流长度污点数据从源到汇经过的赋值和传递次数长度超过10的数据流需要重点审查。这三个指标组合起来可以给每个代码片段算一个审计优先级分数。分数高的片段优先送给大模型分析分数低的可以延后甚至跳过。这个分数不是静态的随着分析进行可以动态调整。比如某个片段被大模型判定为“疑似漏洞但证据不足”它的优先级会被调高后续用更详细的提示重新分析。4. 实操流程从代码库到漏洞报告的完整链路4.1 环境准备与工具链搭建动手之前先把工具链理清楚。我用的组合是CodeQL做静态分析和数据流追踪Python脚本做代码切片和排序大模型API做深度分析最后用Jupyter Notebook做结果汇总和可视化。这套组合的好处是每个环节都可以独立替换比如你不想用CodeQL换成Semgrep或者Joern也行只要能把调用图和数据流导出来就可以。CodeQL的安装和数据库构建是第一步。以C/C项目为例你需要先写一个build脚本让CodeQL能追踪编译过程。对于用CMake的项目可以用codeql database create --languagecpp --commandcmake --build build来创建数据库。这一步的坑在于有些项目依赖复杂的构建环境CodeQL可能追踪不全。我的经验是尽量用项目自带的构建脚本不要自己简化否则调用图会缺失很多边。数据库建好之后用CodeQL查询提取调用图和数据流。我通常会跑三个查询一个是所有函数的调用关系一个是所有危险函数调用的位置一个是从外部输入到危险函数的污点路径。这三个查询的结果导出成JSON格式供后续Python脚本处理。这里要注意CodeQL的查询结果可能很大一个中型项目导出的JSON可能几百MB处理的时候要用流式读取不要一次性加载到内存。4.2 代码切片与排序的具体实现代码切片的目标是把与某个可疑操作相关的所有代码行提取出来形成一个自包含的分析单元。我的做法是以危险函数调用为锚点向前追溯数据流来源向后追踪数据流去向同时把调用链上的函数签名和关键校验逻辑也包含进来。切片的粒度控制在500到2000 token之间太短了信息不足太长了浪费模型上下文。排序环节我用了一个简单的打分函数综合考虑四个因素危险函数的危险等级、数据流长度、圈复杂度、以及该片段是否在最近的代码提交中被修改过。每个因素给一个权重加权求和得到最终分数。权重是我根据经验调的危险等级0.4数据流长度0.3圈复杂度0.2最近修改0.1。这个权重不是固定的你可以根据项目特点调整。比如对于新项目最近修改的权重可以调高对于老项目危险等级的权重更重要。排序之后取前N个片段送给大模型。N的取值取决于你的算力预算我通常取100到200。如果预算充足可以取500。但要注意超过500之后边际收益递减很明显因为后面的片段相关性已经很低了。4.3 大模型分析提示模板与参数设置提示模板我打磨了很多版现在用的这个版本在误报率和漏报率之间平衡得比较好。模板结构是这样的先给模型一个角色设定比如“你是一个经验丰富的安全研究员擅长C/C代码审计”然后给一段代码上下文包括函数签名、调用关系、数据流路径接着给具体的分析任务比如“请分析这段代码中是否存在缓冲区溢出漏洞如果有请给出触发条件和修复建议”最后要求模型按固定格式输出方便后续解析。参数设置方面温度我通常设0.2因为漏洞分析需要确定性不需要创造性。最大输出token设2000足够模型给出详细推理。top_p设0.9保持一定的多样性但不要太高。频率惩罚和存在惩罚都设0因为代码分析不需要避免重复。这些参数不是绝对的你可以根据模型版本和任务特点微调。4.4 结果验证与误报过滤大模型给出的漏洞报告不能直接信必须经过验证。我的验证流程分三步。第一步是静态验证用CodeQL或者手工检查模型指出的数据流路径是否真实存在模型有时候会“脑补”一些不存在的调用关系。第二步是动态验证对于模型判定为高危的漏洞尝试构造PoC触发这一步能过滤掉大部分误报。第三步是交叉验证用不同的提示模板或者不同的模型重新分析同一个片段如果多个独立分析都指向同一个漏洞可信度就很高。误报过滤我总结了一个经验法则如果模型给出的漏洞需要非常特殊的输入才能触发比如“当环境变量X等于特定值且文件权限为777时”这种漏洞的实际风险通常很低可以降级处理。真正高危的漏洞往往是“用户输入直接拼接到SQL查询”或者“没有边界检查的内存拷贝”这种直截了当的问题。5. 常见问题与排查技巧实录5.1 模型漏报严重怎么办漏报是LLM做漏洞研究最让人头疼的问题。我遇到过好几次模型对一个明显的缓冲区溢出视而不见后来排查发现原因主要有三个。第一个是上下文不足模型只看到了危险函数调用没看到前面的输入校验缺失。解决办法是在切片时把调用链上游的校验逻辑也包含进来哪怕这些代码不在同一个文件里。第二个是提示词误导如果你在提示里说“请检查这段代码是否有SQL注入”模型可能会忽略其他类型的漏洞。解决办法是用更通用的提示比如“请分析这段代码的安全问题”让模型自己判断漏洞类型。第三个是模型能力边界有些漏洞需要跨多个函数、多个文件才能理解超出了模型的处理能力。这种情况只能靠更精细的切片和更明确的提示来缓解。5.2 误报太多怎么过滤误报多的原因通常是模型过度敏感把一些理论上有风险但实际不可达的代码路径也标为漏洞。过滤误报我常用的手段是可达性分析。模型说某个函数有漏洞我就用CodeQL检查这个函数是否真的能从外部入口到达。如果不可达直接丢弃。另一个手段是输入可控性分析模型说某个变量会导致溢出我就检查这个变量是否真的能被外部输入控制。如果它来自配置文件或者硬编码常量风险等级就降下来。还有一个技巧是让模型自我质疑。在提示里加一句“请从攻击者的角度思考这个漏洞是否真的可以被利用如果不可利用请说明原因”。这样模型在给出漏洞报告后会自己再审视一遍过滤掉一些明显的误报。5.3 成本超预算怎么优化成本控制是规模化漏洞研究必须面对的问题。我的优化策略分三个层次。第一层是减少分析量通过更精确的排序把真正需要大模型分析的片段压缩到最少。我试过用嵌入向量做预排序能把需要大模型分析的片段减少60%以上。第二层是降低单次分析成本用更小的模型做初筛只把最可疑的片段送给大模型。比如用7B参数的小模型做第一轮分析把候选从1000降到100再用大模型做第二轮。第三层是缓存复用相似的代码片段复用之前的分析结果避免重复推理。5.4 常见问题速查表问题现象可能原因排查方法解决措施模型漏报明显漏洞上下文不足或提示词误导检查切片是否包含完整数据流扩大切片范围改用通用提示误报率超过50%模型过度敏感或可达性未验证用CodeQL做可达性分析增加可达性过滤要求模型自我质疑分析成本超预算排序不精确或模型选型不当统计各阶段片段数量和token消耗引入嵌入向量预排序小模型初筛模型输出格式混乱提示模板不够结构化检查输出解析失败率用JSON格式约束输出增加示例跨文件漏洞识别率低调用图不完整对比CodeQL调用图和实际调用关系完善构建脚本补充手工调用图6. 影响范围与延展思考这套方法论的影响范围远不止漏洞研究本身。任何需要从大规模非结构化数据中提取高价值信息的场景都可以借鉴这个“粗筛-精排-深度分析”的三阶段漏斗。比如法律领域的合同审查先用规则匹配找出所有涉及赔偿条款的段落再用嵌入向量排序找出最可能有问题的那几份合同最后用大模型做深度条款分析。再比如运维领域的日志排查先用正则表达式捞出所有ERROR级别的日志再用聚类算法把相似日志归并最后用大模型分析根因。从技术演进的角度看这个方向还有很大的优化空间。一是多模态融合把代码、文档、提交历史、issue讨论都纳入分析范围模型能看到更完整的上下文。二是主动学习让模型在分析过程中主动提出“我还需要看哪些代码”然后动态检索相关片段。三是端到端优化把切片、排序、分析、验证整合成一个可微分的流程用强化学习来优化整体效果。这些方向目前都有人在探索但离成熟还有距离。我个人在实际操作中的体会是大模型做漏洞研究目前还处于“辅助驾驶”阶段它能帮你快速定位可疑区域但最终的判断和验证还是得靠人。不要指望它全自动挖出0day但把它当作一个不知疲倦的初级安全工程师来用确实能大幅提升效率。我现在的流程是大模型负责初筛和生成候选漏洞报告我负责验证和深入分析。以前审计一个10万行的项目要两周现在三天就能完成初筛剩下四天做深度验证整体效率提升了一倍多。这个投入产出比对于任何需要做代码审计的团队来说都是值得尝试的。
返回列表