3、静态分析工具Slither:Slither安装与配置、基础规则扫描、自定义规则编写

静态分析,说白了就是在代码还没跑起来之前,先拿放大镜把源码翻个底朝天。我最早接触智能合约安全时,总觉得部署上去跑一跑就能发现问题,后来被一个隐藏的整数溢出坑惨了——嗯,从那以后,Slither就成了我工具箱里的常客。

Slither是Trail of Bits团队开发的Solidity静态分析框架。它不执行合约,而是把代码解析成抽象语法树和控制流图,然后基于这些结构做模式匹配和数据流分析。你想想看,这相当于给合约做了一次全身CT扫描。

核心能力一览:

  • 检测常见漏洞(重入、未检查的call、整数溢出等)
  • 分析合约继承关系和函数调用图
  • 支持自定义检测规则(Python编写)
  • 输出JSON、SARIF等结构化报告
Slither 静态分析 安装与配置 基础规则扫描 自定义规则编写 pip install solc-select slither . --json 报告 --exclude 过滤 继承AbstractDetector 编写detect()方法 输出:漏洞报告 + 代码质量建议

3.1 安装与配置

安装Slither其实很简单,但有几个坑我得提前跟你说。我个人习惯用pip安装,干净利落:

pip install slither-analyzer

不过光装Slither还不够,它依赖solc编译器。你想想看,没有编译器,它怎么解析Solidity源码?所以还得装solc-select来管理编译器版本:

pip install solc-select
solc-select install 0.8.19
solc-select use 0.8.19

我曾经踩过的坑:有一次我直接装了最新版solc,结果项目用的还是0.6.12的旧语法。Slither解析时直接报错,说语法不兼容。后来我学乖了,每次跑分析前先用solc-select切换到目标版本。

验证安装是否成功,跑个版本号看看:

slither --version

如果看到版本号输出,恭喜你,环境搭好了。如果报错说找不到solc,多半是环境变量没配好。检查一下~/.solc-select/artifacts目录下有没有对应的solc二进制文件。

3.2 基础规则扫描

安装完第一件事,当然是找个合约试试水。我建议你从最简单的ERC20合约开始:

slither my_token.sol

输出结果会分成几个部分:

  • INFO:常规信息,比如合约名称、版本
  • WARNING:潜在风险,需要人工确认
  • ERROR:确定性的漏洞,必须修复

举个例子,Slither检测到未检查的call返回值时,会这样提示:

WARNING: Low-level call (my_token.sol#L42) is used without checking return value.
Consider using transfer() or checking the return value.

我个人习惯把结果输出成JSON,方便后续集成到CI/CD流水线:

slither my_token.sol --json report.json

JSON格式长这样:

{
  "success": true,
  "results": {
    "detectors": [
      {
        "check": "unchecked-lowcall",
        "impact": "Medium",
        "confidence": "High",
        "description": "Low-level call without return value check..."
      }
    ]
  }
}

小技巧:如果你只想看某个特定类型的漏洞,可以用--detect参数过滤。比如只看重入漏洞:slither . --detect reentrancy。我经常在审计大项目时这么干,先扫一遍所有规则,再针对高风险项单独排查。

Slither内置了90多条检测规则,覆盖了SWC(Smart Contract Weakness Classification)中的大部分类别。常用的有:

规则ID 检测内容 严重程度
reentrancy 重入漏洞 High
unchecked-lowcall 未检查的底层调用 Medium
timestamp 依赖区块时间戳 Low
tx-origin 使用tx.origin做身份验证 Medium
pragma 浮点编译指示版本 Informational

3.3 自定义规则编写

内置规则虽然多,但总有覆盖不到的场景。比如你想检测合约中所有使用block.number的地方,或者想检查某个特定命名模式的函数。这时候就得自己写规则了。

Slither的自定义规则用Python写,核心是继承AbstractDetector类。我带你写一个最简单的例子——检测合约中是否使用了selfdestruct

from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification

class SelfdestructDetector(AbstractDetector):
    ARGUMENT = "selfdestruct-check"
    HELP = "检测合约中是否包含selfdestruct"
    IMPACT = DetectorClassification.HIGH
    CONFIDENCE = DetectorClassification.HIGH

    def detect(self):
        results = []
        for contract in self.slither.contracts_derived:
            for function in contract.functions:
                for node in function.nodes:
                    for ir in node.irs:
                        if ir.name == "SELFDESTRUCT":
                            info = f"合约 {contract.name} 中的 {function.name} 函数调用了 selfdestruct\n"
                            json = self.generate_result(info)
                            results.append(json)
        return results

写完之后,保存成selfdestruct_detector.py,然后通过--detect参数加载:

slither my_token.sol --detect selfdestruct-check --detectors-path ./

关键点解析:

  • ARGUMENT:命令行中使用的规则名称
  • IMPACTCONFIDENCE:影响程度和置信度,分为HIGH/MEDIUM/LOW/INFORMATIONAL
  • detect()方法:核心逻辑,返回检测结果列表
  • self.slither.contracts_derived:获取所有合约对象
  • function.nodes:函数的控制流图节点
  • node.irs:节点中的中间表示(Intermediate Representation)

你可能会问,怎么知道有哪些IR类型?Slither提供了完整的API文档。我个人常用的IR包括:

  • CALL:外部调用
  • DELEGATECALL:委托调用
  • SLOAD/SSTORE:状态变量读写
  • BALANCE:余额查询
  • TRANSFER/SEND:转账操作

注意:自定义规则写完后,一定要先在测试合约上跑一遍。我曾经写过一个检测未初始化变量的规则,结果因为没处理好继承关系,误报率高达80%。后来加上了contract.is_interface过滤,才把误报压下去。

如果你想把自定义规则做成永久性的,可以把它放到Slither的插件目录下:

~/.slither/plugins/

这样每次运行Slither时,它会自动加载所有插件。我个人习惯把常用的审计规则打包成一个插件集合,团队里其他人也能直接用。

进阶技巧:Slither还支持数据流分析。比如你想追踪某个变量是否被用户输入污染,可以用slither.data_dependency模块。我曾在审计一个DeFi项目时,用这个功能找到了一个通过闪电贷操纵预言机的漏洞——那是我用自定义规则抓到的最值钱的一个bug。

好了,Slither的基本用法就这些。从安装到基础扫描,再到自定义规则,这套流程我用了两年多,审计了上百个合约。说白了,静态分析工具就是你的第二双眼睛,它能帮你看到人眼容易漏掉的东西。但记住,工具只是辅助,最终的判断还得靠你自己。


公众号:蓝海资料掘金营,微信deep3321