大语言模型辅助智能合约静态审计:利用 AST 语法树解析与 LLM 提示词链漏洞扫描实战

发布时间:2026/6/6 21:17:31
大语言模型辅助智能合约静态审计:利用 AST 语法树解析与 LLM 提示词链漏洞扫描实战
大语言模型辅助智能合约静态审计利用 AST 语法树解析与 LLM 提示词链漏洞扫描实战Web3 安全是去中心化金融DeFi生态的生命线。传统的智能合约静态审计工具如 Slither, Mythril主要依赖预设的特征规则或符号执行Symbolic Execution虽然能够快速筛查出经典的逻辑漏洞但对于复杂的业务逻辑漏洞与协议级复合攻击却显得力不从心。同时随着大语言模型LLM的发展直接将几千行的智能合约源码塞入大模型容易受到上下文窗口限制、关键信息稀释以及严重“幻觉Hallucination”的困扰。本文将提出一种创新的静态审计范式——通过AST抽象语法树预筛选与函数切片提取精确锁定敏感执行路径再结合LLM 提示词链Chain of Thought进行深度漏洞推理与分类审计。一、 智能合约审计的局限与演进传统的静态代码分析工具通过遍历代码并生成中间表示如 Slither 的 SlithIR基于控制流图CFG和数据流依赖关系查找漏洞。然而传统静态审计往往存在以下局限性误报率False Positive Rate极高因为工具无法识别真实的业务语义例如将正常的升级重置逻辑误判为权限丢失。多合约级联调用难以追踪无法理解跨 DeFi 协议的闪电贷Flashloan套利与操纵预言机Oracle Manipulation等金融攻击。大模型LLM的引入打破了这一僵局。LLM 拥有强大的代码语义理解能力能够识别高层次的代码设计意图。但其核心痛点在于如何确保 LLM 只专注于真正有风险的敏感逻辑因此“AST 预分析精密制导 LLM 语义判断精确打击”是当前提升代码审计精度和效率的最佳路径。二、 审计流水线架构设计本系统的基本审计逻辑如下AST 解析阶段将 Solidity 合约文件输入 AST 解析器生成结构化的抽象语法树。敏感操作码检测与提取遍历 AST 树搜索潜在的高风险操作码例如外部转账call、自毁指令selfdestruct、修改所有权的transferOwnership等。漏洞相关上下文切片Slicing追踪包含上述高风险节点的完整函数定义提取该函数的参数定义、状态修改语句以及调用逻辑。LLM 提示词链审计利用提示词链分步推理评估是否存在重入漏洞Reentrancy、重入锁失效或状态更改后置等逻辑缺陷。flowchart TD Source[Solidity 源代码] -- Parser[AST 语法树解析器] Parser -- AST[AST 结构化 JSON] AST -- Detector{高风险操作检测} Detector --|发现 call.value / selfdestruct| Slicer[提取漏洞函数切片] Detector --|无风险节点| Pass[忽略/通过] Slicer -- SystemPrompt[构造 Step-by-Step 推理提示词] SystemPrompt -- LLM[LLM 推理引擎] LLM -- Step1[步骤 1: 追踪外部调用前后的状态变量修改] Step1 -- Step2[步骤 2: 分析重入攻击的可能性与利用链] Step2 -- Report[生成结构化漏洞诊断报告]三、 AST 节点结构与重入风险特征在 Solidity 的 AST 中一个重入漏洞通常具有以下拓扑特征存在一个FunctionDefinition节点。该函数内部包含一个ExpressionStatement其底层是一个MemberAccess为call且附带value参数的成员函数调用即外部转账操作。在上述call操作之后存在对StateVariable的写入或修改操作违反了“检查-效果-交互”规范。四、 工业级 AST 预处理与 LLM 审计管道 Python 实现下面提供一个完全闭环、手写的 Python 审计脚本。该脚本包含一个轻量级 Solidity 语法扫描器模拟 AST 节点遍历提取可能存在重入攻击的函数切片并构造高控制度的 Chain-of-Thought思维链提示词结合大模型客户端完成漏洞分析。代码中不包含任何占位符。import json import re # # 模拟 Solidity AST 树结构数据 (用于演示无需安装 solc 的完整闭环运行) # MOCK_SOL_CODE contract VulnerableBank { mapping(address uint256) public balances; function withdraw(uint256 _amount) public { uint256 bal balances[msg.sender]; require(bal _amount); // 外部转账操作容易遭到重入攻击 (bool success, ) msg.sender.call{value: _amount}(); require(success); // 状态更新后置违反了 Checks-Effects-Interactions 规范 balances[msg.sender] - _amount; } } MOCK_AST_DATA { nodeType: SourceUnit, children: [ { nodeType: ContractDefinition, name: VulnerableBank, children: [ { nodeType: FunctionDefinition, name: withdraw, parameters: [_amount], body: [ uint256 bal balances[msg.sender];, require(bal _amount);, (bool success, ) msg.sender.call{value: _amount}(\\);, require(success);, balances[msg.sender] - _amount; ] } ] } ] } class SoliditySmartAuditor: def __init__(self, raw_code: str, ast_json: dict): self.raw_code raw_code self.ast_json ast_json self.findings [] def scan_sensitive_nodes(self) - list: 遍历 AST查找包含敏感操作例如外部 call 调用的函数切片 vulnerable_slices [] contracts [node for node in self.ast_json.get(children, []) if node.get(nodeType) ContractDefinition] for contract in contracts: functions [node for node in contract.get(children, []) if node.get(nodeType) FunctionDefinition] for func in functions: func_body func.get(body, []) has_external_call False has_state_modification_after_call False call_index -1 # 遍历函数体语句 for idx, stmt in enumerate(func_body): # 匹配外部 call 发送以太币的操作 if .call{ in stmt or transfer( in stmt or send( in stmt: has_external_call True call_index idx break # 如果存在外部调用检查之后是否进行了状态变更 if has_external_call and call_index ! -1: for idx in range(call_index 1, len(func_body)): stmt_after func_body[idx] # 简单匹配状态变量修改操作如 -, , 赋值 if re.search(rbalances\[.*?\]\s*[\-\*], stmt_after) or in stmt_after: has_state_modification_after_call True break if has_external_call: # 提取函数上下文切片 func_slice { contract: contract.get(name), function: func.get(name), code: \n.join(func_body), has_risk_state_order: has_state_modification_after_call } vulnerable_slices.append(func_slice) return vulnerable_slices def build_llm_prompt(self, func_slice: dict) - str: 构造高控制度的 Chain-of-Thought 推理提示词 prompt f 你是一名资深的智能合约安全审计专家。请针对以下提取的 Solidity 函数代码切片进行静态审计。 [合约名称] {func_slice[contract]} [函数定义] {func_slice[function]} [提取的代码切片] solidity {func_slice[code]}请遵循以下步骤逐步进行分析步骤 1: 确定函数中所有执行的外部调用External Call操作并评估它们是否会将控制权转移给不受信任的外部合约。步骤 2: 追踪所有在外部调用之后执行的状态变量修改操作。步骤 3: 分析此函数是否容易遭到“重入漏洞Reentrancy Attack”的攻击如果是请指出攻击者可能实施的具体利用链。步骤 4: 评估上述状态顺序不当的潜在危害是否会导致资金被盗、重置错误等。最终请以 JSON 格式输出审计结论要求包含以下字段{{is_vulnerable: true/false,vulnerability_type: 漏洞名称,reasoning_steps: [步骤1结论, 步骤2结论, ...],remediation: 修复方案建议}}return prompt.strip()def run_audit(self): 执行审计流程 print([静态扫描] 开始解析 AST 语法结构树...) slices self.scan_sensitive_nodes() if not slices: print([静态扫描] 未发现任何包含敏感外部调用的函数。) return print(f[静态扫描] 提取出 {len(slices)} 个敏感函数切片进行大模型定向投递。) for s in slices: print(f\n--- 针对函数 {s[contract]}.{s[function]} 进行审计 ---) if s[has_risk_state_order]: print([警告] 静态检查发现该函数疑似违反了 Checks-Effects-Interactions 规范状态修改滞后。) prompt self.build_llm_prompt(s) print([大模型投递准备] 提示词链构建完毕。提示词预览) print(- * 60) print(\n.join(prompt.split(\n)[:12]) \n... (省略后续详细引导步骤) ...) print(- * 60) # 模拟 LLM 响应 (为了保证没有外部 API Key 依赖依然可以闭环运行) mock_llm_response { is_vulnerable: True, vulnerability_type: Reentrancy (重入漏洞), reasoning_steps: [ 步骤 1: msg.sender.call{value: _amount} 确实会将执行控制权转交给调用者地址。, 步骤 2: balances[msg.sender] - _amount 减法更新余额的操作放在了外部 call 调用之后执行。, 步骤 3: 攻击者可以在 fallback 或 receive 函数中反复调用 withdraw由于余额未被及时扣除会不断提取合约中的余额直至耗尽。, 步骤 4: 该漏洞会直接导致项目合约的全部资金被非法洗劫。 ], remediation: 将 balances[msg.sender] - _amount; 语句移动到 msg.sender.call 调用之前执行即先修改状态后发起外部交互。 } print([大模型审计结果]) print(json.dumps(mock_llm_response, indent2, ensure_asciiFalse))执行主程序ifname main:auditor SoliditySmartAuditor(MOCK_SOL_CODE, MOCK_AST_DATA)auditor.run_audit()