恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

基于规则的智能合约漏洞检测系统:从AST到源码级审计实践

  • 首页
  • 资讯中心
  • /
  • 基于规则的智能合约漏洞检测系统:从AST到源码级审计实践

相关资讯

知识图谱可视化设计与实现:从图数据库到前端交互优化 2026/10/14 12:08:45
SimpleDateFormat宽容模式解析非法日期?setLenient()严格模式实战指南 2026/10/14 12:08:45
金融科技信用评估系统架构设计与实战优化 2026/10/14 12:03:44

最新资讯

安卓老虎机动画实战:自定义View滚动、对齐与性能优化
STM32外挂DS1302实时时钟驱动实战与调试解析
基于Django与协同过滤的电影推荐系统实践
HASP加密狗驱动安装与V2C授权导入全指南
不会还有人既当运动员又当裁判吧?审核门禁必须分角色 | 主 Agent 与审核
AI学术工具真机大盲测主流论文降AI软件全景横评谁才是真正的实测效果第一名

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

基于规则的智能合约漏洞检测系统:从AST到源码级审计实践

发布时间:2026/10/14 12:08:45
基于规则的智能合约漏洞检测系统:从AST到源码级审计实践 做链上合约安全审计的人应该都经历过这种场景一个合约文件三四百行审核时间只剩两天白天人工逐行看逻辑晚上用扫描器批量跑问题跑完一屏警告一半是误报另一半看着很可疑却说不清哪里可疑。后来我们自己动手搭建了一套基于规则的智能合约漏洞检测系统就是为了把这堆问题按规则拆到源码行、拆到调用路径上让机器先粗筛、人来复核。这篇文章会把整套系统的设计思路、规则抽象方法、误报压制手段和一次完整排查过程写清楚。适合正在开发合约项目的工程师、想转安全审计方向的人以及准备从零搭内部审计平台的小团队参考。规则系统的定位不是替代人工审计而是把审计中最耗时间的“找可疑位置”这件事自动化。1. 在智能合约审计现场规则系统到底解决了什么1.1 三条检测路线的取舍为什么我先选了规则做检测方案之前我把动态模糊测试、形式化验证、规则静态分析放一起做了一次对比。动态模糊测试思路是拿大量随机输入去触发合约异常能找到逻辑层面的漏判但覆盖面看运气很多分支条件因为构造不出特定参数就永远走不到。形式化验证精度高要把业务约束写成数学命题成本非常高普通项目根本等不起。基于规则的静态分析是基于AST、控制流图做模式匹配速度是用毫秒计算的结果可以直接指向源码行。生活里打个比方形式化验证相当于对整栋楼做结构力学验算模糊测试相当于拿大锤到处敲墙面基于规则的扫描相当于保安手持安全检查清单逐项对照门窗、消防通道和监控死角。大多数日常合约审计需要的恰恰是这张清单。所以我的结论是先落地规则系统后面有需要再补符号执行和模糊测试。1.2 规则扫描是“审计三明治”最厚实的中段我们团队内部把审计流程叫做三明治最上层是有经验的人做需求和业务逻辑梳理最下层用符号推理或者人工深度推演处理疑难问题中间那一层就是规则扫描器。规则扫描器的职责是把“可疑点定位到具体行”。没有这一层人要从头到尾读几千行代码有了这一层人可以从警告行出发反向确认整条数据流是否真的有害。这决定了规则系统有一个硬性要求输出必须可解释。只给一个风险等级和危险函数地址是不够的。我要求报告里必须写出“命中哪条规则、在哪一行发生了哪类节点匹配、这行代码和哪个状态变量相关”。这样人工复核时不需要翻回引擎日志找上下文。1.3 市面工具的三大痛点也是自研的直接动机不是没试过现成工具但频繁踩到三堵墙。第一堵墙是黑盒工具给出“高危”结论但规则逻辑不可查审计师写进报告都没法跟客户解释。第二堵墙是规则不可扩展想做业务私有规则只能改工具源码升级一次就冲突一次。第三堵墙是误报太不讲理一跑上百条警告人工筛选成本等于没扫。自研的出发点就对应成三个原则规则可编辑、输出可解释、报告一键定位源码行。后续所有架构决策都是围绕这三点展开的。2. 规则引擎的整体设计从源码到报告的完整链路2.1 六层处理管线每一层都解决一个具体问题系统的处理流程可以拆成六层。第一层是获取合约源码兼容单个文件和整个项目目录。第二层是源码预处理处理注释、编译指令和import路径。第三层是调用编译器的AST导出接口拿到语法树。第四层是从AST构建控制流图CFG和函数调用关系。第五层是可达性分析把不可达的死代码先标注出来。第六层是规则匹配引擎把规则模板和中间表示做比对最后输出带源码引用的警告报告。建议把第三、四层单独做成缓存。一个工程里几十个合约文件很多文件被多个规则重复读取没有缓存的话每加一条规则就要重新解析一遍整个项目。我当时踩过这个坑规则数加到15条之后单次扫描从几十秒涨到几分钟加了AST缓存之后回到了秒级。做系统设计时先把缓存机制想进去后面会省非常多事。2.2 选择AST而不是字节码核心是“可解释”三个字很多成熟的扫描器能做字节码层面的分析覆盖未开源合约。但字节码分析有两个问题一是需要反编译能力栈操作符和跳转目标可读性差二是难以映射回源码行警告定位只能到一个函数级别不符合我们“可解释”的原则。我们最终选择AST优先。AST里保留变量名、字面量、语义上下文能直接报告“第42行的减法操作可能是整数溢出”。而且Solidity编译器本身就能输出标准JSON AST不需要自研解析器接入成本低。未开源合约的场景我们单独保留了字节码反编译通道不放在主路径上。绝大数审计场景拿到的都是源码或客户自持代码AST路线足够用。2.3 规则语言不要过度设计模板加参数最务实一开始团队想设计一套接近自然语言的DSL花了两周写语法解析器结果规则编辑者根本不愿意学。后来推翻重来改成了“规则模板加参数表”的结构。一个模板描述模式比如“外部调用后状态写操作”参数表说明匹配哪些函数名、哪些语句类型、需要跳过哪些白名单修饰器。规则存储在数据库或JSON文件中系统运行期间可以热加载不需要重启进程。这个决定直接加快了迭代速度。因为规则是给审计人员看的越接近他们的思维习惯越好一份模板加上几个参数比任何花哨的DSL都好维护。3. 高危规则如何抽象成可执行的模式3.1 重入漏洞把“状态变更顺序”变成可判断的条件重入漏洞的本质是外部调用触发之后合约内部的关键状态还没有更新攻击者利用这个窗口再次进入合约重复操作。规则检测的目标不是“有没有外部调用”而是“外部调用和状态写操作的先后顺序”。拿经典提现函数举例function withdraw(uint256 amount) external { require(balances[msg.sender] amount); (bool ok, ) msg.sender.call{value: amount}(); require(ok); // 危险状态在外部调用之后才更新 balances[msg.sender] - amount; }规则会表达成如果函数节点内存在调用表达式CallExpression并且同一执行路径上存在对存储变量StorageVariable的写操作出现在该调用之后就触发“疑似重入”。把这个模式写成AST节点顺序的匹配规则比直接用字符串搜索要可靠得多。这个规则有一个常见误报函数上挂了nonReentrant锁时外部调用之后的写操作其实是安全的。处理办法是让规则引擎支持“修饰器感知”凡是被识别为锁型修饰器保护的函数自动降级处理或忽略。3.2 未检查的低级别调用返回值低级别调用call、send返回执行结果如果返回值被丢弃转账失败时合约不会感知轻则业务中断重则资产永久锁定。规则特征很简单方法名为call或send且返回值没有参与if判断、没有赋值给变量、没有被require包裹。function transferOut(address to, uint256 amount) external { to.call{value: amount}(); // 返回值未检查 }单模式匹配会误伤一种情况返回值先赋给一个中间变量经过某个条件判断再使用。因此规则引擎不能只做“当前节点”匹配还要做一段极轻的数据流追踪。把返回值变量记录下来跟随它的引用链看最终是否进入了条件表达式的操作数列表里。这也是我把规则引擎拆成“模式匹配器”加“数据流插件”两个层次的原因。3.3 tx.origin作为唯一认证的权限缺陷tx.origin是调用链最初始发起者的地址。攻击者可以诱导一个用户去调用恶意合约再由恶意合约回调目标合约这时候tx.origin依然是用户地址而权限判断就失效了。正确的鉴权应该用msg.sender判断直接调用者。规则匹配为require或if表达式里左操作数或右操作数是tx.origin成员表达式同时该分支没有同时校验msg.sender。比如下面这个修饰器就会命中modifier onlyOwner() { require(tx.origin owner); _; }规则输出层需要额外提示“建议改为msg.sender鉴权”方便开发者直接对照修改。3.4 整数溢出和除零保护给数值范围加约束对于0.8以下版本乘除加减运算如果没有使用带检查的库函数就可能溢出。规则匹配是找到算术操作节点往回查它是否被SafeMath类的add、sub、mul、div包裹或者合约版本是否启用了内置溢出检查。0.8以上版本重点要看unchecked代码块里有潜在风险的运算。这里有个经验不要把规则写成“遇到减号就是漏洞”。同一段减法如果两个操作数都来自状态变量且经过大小约束实际可被利用的程度很低。规则参数表里应加入“操作数来源”的字段来源是用户输入、链上外部数据、时间戳等不可信来源时才提升威胁等级。做个规则汇总方便看清整体设计规则标识漏洞形态规则特征主要误报原因RE-001重入状态写操作在外部调用之后存在nonReentrant锁RE-006返回值未检查call返回值未参与条件判断故意忽略且后续有兜底RE-010tx.origin鉴权使用tx.origin做唯一认证合约无外部调用链风险有限RE-014整数溢出低版本算术无检查数值范围受限实际不可溢RE-017硬编码价格价格来源于常量内部测试合约或预言机白名单3.5 业务型漏洞也会抽象成规则只是维护成本更高很多人认为规则系统只能做通用漏洞检测其实业务型缺陷也可以抽象。比如众筹合约的规则“目标金额在close之后被修改”可以抽象成“close函数内存在对target存储变量的写操作”。再比如锁仓合约的“解锁时间被管理员任意延长”可以抽象成“管理员函数内对unlockTime的赋值来源不是常量”。这类规则价值高但维护成本也高因为它们依赖具体业务上下文。我们要求每条业务规则必须附带业务场景说明写清楚“为什么这个模式不合理”防止后人误解。这类规则既是检测工具也是团队知识库的一部分。4. 把误报率打下来的三个关键细节4.1 从“语法匹配”升级到“必须走得到”的可达性分析初版系统直接对AST做节点类型匹配误报率惨不忍睹。后来加入调用图和可达性分析效果立刻不一样。拿模拟项目X的测试合约举例它一共有30个外部函数旧版规则引擎报出140个问题吓人一跳加了可达性剪枝之后过滤掉那些不可达节点后只剩38个。不可达代码的来源多种多样事件定义里的模拟调用、内部测试函数、被极端参数条件挡住的路径。操作思路是对每个危险点做反向可达性分析看入口函数是否存在一条路径可以到达该节点。到不了就直接丢弃最后只保留“既危险又可达”的警告。4.2 置信度打分让报告自动排序没有打分的报告等于没有重点。我们的置信度公式是置信度 静态规则权重 × 上下文加强因子。上下文加强因子包括危险函数是否处于转账路径、是否影响余额类存储变量、是否存在权限修饰器、是否出现在循环体内。高置信度的进主报告低置信度的合并到筛选建议。规则权重不是拍脑袋给的。每一条规则上线前团队会拿历史真实漏洞样本和正常合约样本做回归根据命中真实漏洞的次数反推权重。某条规则如果经常在正常合约上触发权重就会往下调直到它不再淹没主报告。4.3 白名单三要素宁可麻烦也要把理由留住白名单机制很容易变成“把警告消掉”的偷懒工具。我们的要求是每一条白名单记录必须包含三个要素规则标识、合约地址或文件行范围、废弃原因。例如这样一条记录“目标合约某众筹合约规则RE-001原因函数存在nonReentrant锁联系人A同学”。没有原因的白名单不允许添加。此外还做了基线回归机制每次规则更新后用历史真实漏洞样本库跑一遍确保新版本没有漏掉旧漏洞。这两条机制保证了规则库可以持续进化而不是越改越钝。5. 用它发现真实漏洞的完整排查过程5.1 一个模拟众筹锁仓合约一次扫出三条警告我拿一个手工构造的众筹锁仓合约验证引擎这个合约的业务是用户投入资金后获得份额管理员到达解锁时间后允许用户提走对应资金。扫描结果命中了三条规则格式如下[RE-001] 疑似重入 file: crowdfunding_simulated.sol line: 87-94 evidence: external_call at line 88, storage_write at line 92 [RE-006] 疑似未检查返回值 file: crowdfunding_simulated.sol line: 121 [RE-010] 疑似tx.origin鉴权 file: crowdfunding_simulated.sol line: 30拿到这种报告时团队内部有一个铁律不要急着给结论。警告只是嫌疑需要人工确认。5.2 从警告到确认漏洞的走读链路确认一个警告的流程是固定的。第一步看命中的路径是否真正可达有没有修饰器或前置条件把路径挡住。第二步确认外部重入入口是否存在也就是外部调用是否来自用户可控地址。第三步顺着资产流入方向确认攻击者能控制的输入有哪些。第四步把这些点串成一条调用序列验证攻击顺序。这个模拟合约的RE-001最后被确认为真实漏洞。原因很典型合约在外部转账之后才更新用户提款份额而且提款函数没有锁保护。攻击序列可以这样构造攻击者先调用deposit合法存入一笔资金成为持份地址攻击者调用withdraw合约先触发外部转账外部回调攻击者合约再次调用withdraw此时旧份额状态还没更新攻击者可以重复提取重复多次后合约份额数据与真实余额严重脱节。5.3 三种重入形态验证时都要覆盖重入不一定发生在同一个函数里跨函数重入和跨合约重入也常见。跨函数重入指A函数调外部合约B函数里也有状态写裸奔跨合约重入指两个合约之间互相调用状态同步存在缝隙。验证步骤如下先做本地模拟复现攻击序列再检查跨合约交互地址是否可能由攻击者控制最后估算攻击收益和gas成本是否匹配。这次排查里RE-006实际是安全的合约虽然忽略了call返回值但后续有兜底require转账失败会直接回滚没有资金风险。RE-010是业务正常写法合约没有外部调用链可被诱导属于低风险。这个案例正好说明扫描器给出的是“嫌疑”而不是“定案”。6. 从规则引擎到自动化审计工作台的扩展6.1 和编译管线直接集成AST也交给编译器生成实际落地时不需要自研完整的Solidity语言解析器。源码进入系统后直接调用Solidity编译器的接口导出标准JSON AST规则引擎只消费AST数据。这样即维护成本最低又能始终跟上语法更新。这里要提醒一个坑部分特殊语法或版本不匹配会导致编译失败。我们的处理是先做一次编译预热凡是编译失败的文件直接标记为“语法不合格”不再进入规则扫描流程。这能避免后续规则匹配遇到不完整AST时崩溃。6.2 规则版本管理和回滚要像维护代码一样维护规则规则库变更必须有审计记录谁改的、为什么改、影响了哪些规则标识。建议把规则脚本作为代码提交到版本库中走正常的代码评审和发布流程。线上系统通过一个配置接口加载指定版本的规则一旦新规则引发大面积误报可以立即回滚到上一个稳定版本。我在实践中体会很深的一点是规则不是越多越好。第一次启用规则系统时建议只开5到8条主规则并且先在一批已经人工审计过的合约上做回归。想一次开50条规则报告量爆炸不说规则之间的交叉误报会直接耗尽团队的耐心。6.3 对规则系统后续扩展的思考最后说一个细节规则引擎上线后最容易出问题的不是规则写不出来而是规则上得太快。我现在要求团队每引入一条新规则必须附带3个历史样本和1个合成样本样本不过关就不允许合入。这个机制看起来麻烦却省下了后面大量的误报处理时间。如果要继续扩展这个系统我建议下一步不是加更多漏洞规则而是先做审计报告格式标准化。等报告格式稳定了规则再多也只是往固定结构里填内容后续接工单系统、统计看板都会很方便。规则引擎的价值终究是让审计师把精力花在真正需要思考的地方而不是耗在逐行翻代码上。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号