
智能合约自动审计用 LLM 扫描 Solidity 代码的安全红线一、审计一家 DeFi 协议需要三周上线只用了三天智能合约安全事件每年造成数十亿美元损失。传统审计依赖人工逐行审查周期长、成本高。一个中等复杂度的 DeFi 合约审计报价 5-15 万美元。对于小型项目这笔费用劝退了很多人。LLM 的代码理解能力为自动审计提供了新可能。大模型可以识别常见的安全漏洞模式。但直接让 LLM 审计合约有两个问题。一是幻觉可能报告不存在的漏洞。二是覆盖不全不熟悉的漏洞模式会被遗漏。自动化审计的正确姿态是辅助而非替代。LLM 做第一轮扫描人工做终审确认。二、自动化审计的工作流规则引擎 LLM 的双层审计架构。规则引擎捕获确定性漏洞整数溢出、重入攻击。LLM 处理需要语义理解的逻辑漏洞。flowchart TB A[Solidity 源码] -- B[AST 解析与代码切分] B -- C[规则引擎扫描] B -- D[LLM 语义分析] C -- E[确定性漏洞: 重入/溢出/时间戳依赖] D -- F[逻辑漏洞: 权限/业务逻辑/Token 经济] E -- G[漏洞汇总与去重] F -- G G -- H[生成审计报告] H -- I[人工复核] I -- J{确认?} J --|是| K[标记已确认漏洞] J --|误报| L[标记误报并反馈]三、Python 实现的合约审计工具 contract_auditor.py - 智能合约自动审计 import json import re import logging from dataclasses import dataclass, field from typing import List, Optional, Dict from enum import Enum logger logging.getLogger(__name__) class Severity(Enum): CRITICAL critical HIGH high MEDIUM medium LOW low INFO info dataclass class Finding: 审计发现 id: str title: str severity: Severity line_number: int code_snippet: str description: str recommendation: str source: str # rule_engine | llm_analysis confirmed: bool False dataclass class AuditReport: 审计报告 contract_name: str findings: List[Finding] field(default_factorylist) summary: Dict field(default_factorydict) audit_time: str def critical_count(self) - int: return sum(1 for f in self.findings if f.severity Severity.CRITICAL) def high_count(self) - int: return sum(1 for f in self.findings if f.severity Severity.HIGH) class RuleEngine: 确定性规则扫描引擎 # 重入攻击模式 REENTRANCY_PATTERN ( r\.call\s*\{.*?\}\s*\(\s*[\]?\s*[\]?\s*\) ) # 未检查的 call 返回值 UNCHECKED_CALL r(\.call|\.delegatecall|\.staticcall)\s*\([^)]*\)(?!\s*(require|if|assert)) # tx.origin 授权检查 TX_ORIGIN_PATTERN rtx\.origin\s*\s* # 整数溢出0.8 版本 UNCHECKED_MATH runchecked\s*\{ # 时间戳依赖 TIMESTAMP_DEPEND rblock\.timestamp\s*[] def scan(self, source: str) - List[Finding]: 规则扫描 findings [] lines source.split(\n) for i, line in enumerate(lines, 1): # 检测重入攻击 if re.search(self.REENTRANCY_PATTERN, line): # 检查调用后是否有状态修改 context \n.join(lines[max(0, i-2):min(len(lines), i5)]) if not self._has_state_update_after(context): findings.append(Finding( idfRE-{i:04d}, title潜在重入攻击风险, severitySeverity.CRITICAL, line_numberi, code_snippetline.strip()[:80], description( 外部调用后缺少状态变更保护。 攻击者可能在 call 回调中重复执行该函数。 ), recommendation( 将外部调用移到函数末尾 或使用 ReentrancyGuard 的 nonReentrant 修饰符。 ), sourcerule_engine, )) # 检测 tx.origin 滥用 if re.search(self.TX_ORIGIN_PATTERN, line): findings.append(Finding( idfTX-{i:04d}, titletx.origin 授权检查漏洞, severitySeverity.HIGH, line_numberi, code_snippetline.strip()[:80], description( 使用 tx.origin 进行权限检查可能导致钓鱼攻击。 中间合约可伪装成用户调用目标合约。 ), recommendation使用 msg.sender 代替 tx.origin。, sourcerule_engine, )) # 检测时间戳依赖 if re.search(self.TIMESTAMP_DEPEND, line): findings.append(Finding( idfTM-{i:04d}, titleblock.timestamp 依赖风险, severitySeverity.MEDIUM, line_numberi, code_snippetline.strip()[:80], description( 区块时间戳可被矿工小范围操纵±15秒。 依赖精确时间的逻辑可能被利用。 ), recommendation( 避免使用 block.timestamp 做随机数源。 如果需要时间比较接受 ±30 秒的误差。 ), sourcerule_engine, )) return findings def _has_state_update_after(self, context: str) - bool: 检查 call 调用后是否有状态修改 # 简化检查在 call 之后是否有赋值操作 call_pos context.find(.call) if call_pos -1: return False after context[call_pos:] return bool(re.search(r\s*, after)) class LLMAuditor: LLM 语义审计器 # 审计 Prompt 模板 AUDIT_PROMPT 你是一名智能合约安全审计专家。 请审计以下 Solidity 代码片段识别安全漏洞。 审计要点 1. 访问控制是否有越权风险 2. 逻辑漏洞业务逻辑是否可被操纵 3. Token 经济铸币/销毁逻辑是否有溢出 4. 闪电贷攻击是否依赖可被操纵的链上数据 5. 授权风险approve 是否无限授权 代码 solidity {code}请以 JSON 格式返回审计结果。如果没有发现问题返回空数组。[{{title: 漏洞标题,severity: critical|high|medium|low,line: 行号(估算),description: 详细说明,recommendation: 修复建议}}]def __init__(self, llm_client): llm_client: 需要实现 chat() 方法的 LLM 客户端 self.client llm_client def audit_segment(self, code: str, segment_name: str) - List[Finding]: 审计代码片段 prompt self.AUDIT_PROMPT.format(codecode) try: response self.client.chat(prompt) findings_data json.loads(response) findings [] for item in findings_data: severity Severity(item.get(severity, low)) findings.append(Finding( idfLLM-{segment_name}-{len(findings)}, titleitem[title], severityseverity, line_numberitem.get(line, 0), code_snippetcode[ max(0, item.get(line, 1)-2)*80: max(0, item.get(line, 1)3)*80 ], descriptionitem[description], recommendationitem.get(recommendation, ), sourcellm_analysis, )) return findings except json.JSONDecodeError as e: logger.error(fLLM 返回格式异常: {e}) return [] except Exception as e: logger.error(fLLM 审计异常: {e}) return []class ContractAuditor:合约审计器规则引擎 LLM 双层def __init__(self, llm_clientNone): self.rule_engine RuleEngine() self.llm LLMAuditor(llm_client) if llm_client else None def audit(self, source: str, contract_name: str Unknown) - AuditReport: 执行完整审计 report AuditReport(contract_namecontract_name) # 第一层规则引擎扫描 rule_findings self.rule_engine.scan(source) report.findings.extend(rule_findings) # 第二层LLM 语义分析分函数审计 if self.llm: functions self._extract_functions(source) for func_name, func_code in functions.items(): llm_findings self.llm.audit_segment( func_code, func_name ) report.findings.extend(llm_findings) # 去重 report.findings self._deduplicate(report.findings) # 汇总 report.summary { total: len(report.findings), critical: sum( 1 for f in report.findings if f.severity Severity.CRITICAL ), high: sum( 1 for f in report.findings if f.severity Severity.HIGH ), medium: sum( 1 for f in report.findings if f.severity Severity.MEDIUM ), low: sum( 1 for f in report.findings if f.severity Severity.LOW ), } return report def _extract_functions(self, source: str) - Dict[str, str]: 提取合约中的函数定义 functions {} pattern rfunction\s(\w)\s*\([^)]*\)[^{]*\{ matches list(re.finditer(pattern, source)) for i, match in enumerate(matches): start match.start() func_name match.group(1) # 找匹配的闭合大括号 brace_count 0 end start for j, ch in enumerate(source[start:]): if ch {: brace_count 1 elif ch }: brace_count - 1 if brace_count 0: end start j 1 break functions[func_name] source[start:end] return functions def _deduplicate(self, findings: List[Finding]) - List[Finding]: 去重相同行号、相同严重级别的合并 seen set() unique [] for f in findings: key (f.line_number, f.severity.value, f.title[:30]) if key not in seen: seen.add(key) unique.append(f) return unique## 四、自动审计的局限性 LLM 的审计准确率在 60-80% 之间。 可能遗漏逻辑复杂的新型漏洞。 可能误报不存在的安全问题。 绝不可替代人工审计只能作为第一轮筛查。 Token 成本需要考虑。 一个 500 行的合约分函数审计约消耗 5-10K Token。 使用 GPT-4 完整审计一次约 $0.15-0.3。 不适合的场景 处理大额资金的主网合约必须人工审计 包含复杂数学模型的合约LLM 数学能力有限 使用非 Solidity 语言如 Rust/ink!训练数据较少。 ## 五、总结 智能合约自动审计采用规则引擎LLM 双层架构。 规则引擎捕获重入、溢出等确定性漏洞。 LLM 处理权限、业务逻辑等语义漏洞。 自动审计准确率约 60-80%只能作为辅助筛查。 处理大额资金的合约仍需专业人工审计。