☰
Slither API 开发指南:基于 Solidity/Vyper 静态分析器的六层对象模型编程实战
2026/10/8 7:53:26 网站建设 项目流程
  • 应用安全
  • 区块链

【免费下载链接】slither

Static Analyzer for Solidity and Vyper

项目地址:https://gitcode.com/gh_mirrors/sl/slither
点击查看免费下载

本文围绕开源静态分析器 Slither 的官方 API 文档展开,系统讲解其面向分析脚本开发的六层对象模型——从Slither顶层入口、SlitherCompilationUnit编译单元、Contract合约、Function函数,到Node控制流图节点与SlithIR中间表示。读者将掌握如何用 Python 加载本地项目或链上已验证合约、沿对象层级遍历并提取合约结构、读写变量与调用关系等关键信息,从而基于 slither/slither.py 提供的 API 编写自己的安全审计脚本、数据依赖分析工具或检测器插件。

一、API 概览:六层对象模型

Slither 的 API 允许开发者直接探查合约及其函数的基本属性。从高层视角看,整个分析管线由以下 6 层对象组成:

  1. Slither—— 主入口对象,代表一次对整个目标(项目目录、单文件或链上地址)的分析会话;
  2. SlitherCompilationUnit—— 一次 solc(或 vyper)调用所编译的一组文件,即一个编译单元;
  3. Contract—— 合约层级,封装合约名、函数、修饰器、状态变量与继承关系;
  4. Function—— 函数(及Modifier修饰器)层级,封装 CFG 节点与变量读写信息;
  5. Node—— 控制流图(CFG)中的基本块节点;
  6. SlithIR—— Slither 自有的中间表示,以指令(operation)粒度描述函数内逻辑。

这六层自顶向下层层细化:Slither持有多个编译单元,编译单元持有多个合约,合约持有函数与修饰器,函数体由 CFG 节点组成,而每个节点内部的每条语句又被翻译为若干 SlithIR 操作。理解这条对象链,是编写任何 Slither 分析脚本的基础。

二、Slither 对象:一切分析的入口

2.1 加载本地代码库

Slither类是分析入口。通过from slither import Slither导入后,只需把项目路径传给构造函数即可:

from slither import Slither slither = Slither('/path/to/project')

从源码看,Slither.__init__接受target(可以是路径字符串,也可以是已经构造好的CryticCompile对象),内部调用CryticCompile(target, **kwargs)完成编译,再为每个编译单元构建SlitherCompilationUnit(slither/slither.py)。路径可以是本地目录、单个.sol文件,也可以是 Hardhat/Truffle/Foundry 等任意 crytic-compile 支持的工程结构。

构造时还可传入大量关键字参数来控制分析行为,源码 docstring 明确给出的常用项包括(slither/slither.py):

参数默认值作用
solc'solc'solc 可执行文件路径或版本字符串
disable_solc_warningsFalse是否关闭 solc 警告输出
solc_args''传给 solc 的额外参数
ast_format'--ast-compact-json'solc 输出的 AST 格式
filter_paths[]需要从结果中过滤掉的路径列表
exclude_dependenciesFalse是否排除仅与依赖相关的分析结果
generate_patchesFalse是否生成补丁(仅 JSON 输出有效)
change_line_prefix'#'展示源码位置时使用的行前缀,如file.sol#1

2.2 加载链上已部署合约

如果目标合约已在 Etherscan 上开源验证,可以直接传入地址:

from slither import Slither slither = Slither('0x..') # assuming the code is verified on etherscan

传入地址时,Slither 会通过 crytic-compile 从 Etherscan 拉取源码。如需提供 Etherscan API Key(提高请求配额),使用etherscan_api_key参数:

slither = Slither('0x..', etherscan_api_key='..')

2.3 获取编译单元列表

分析完成后,通过compilation_units属性取得所有编译单元:

sl.compilation_units # array of SlitherCompilationUnit

三、SlitherCompilationUnit:一次 solc 调用的产物

3.1 为什么需要编译单元这一层

SlitherCompilationUnit代表“一次 solc 调用所编译的一组文件”。大多数目标只有一个编译单元,但这并不总是成立,常见例外包括:

  • 为优化而进行的部分编译(不同文件子集分多次编译);
  • 项目中混用多个 solc 版本;
  • 依赖与主工程分开编译等情况。

编译单元这一层之所以重要,是因为某些 API 的语义依赖它。例如“按名字查找合约”:同名合约可能在多个编译单元中各自存在,直接全局查找会得到歧义结果。对大多数分析脚本来说,直接使用第一个编译单元即可:

compilation_unit = sl.compilation_units[0]

3.2 核心属性与方法

SlitherCompilationUnit(实现见 slither/core/compilation_unit.py)提供以下核心接口:

  • contracts (list(Contract)):合约列表;
  • contracts_derived (list(Contract)):未被其他合约继承的合约(即继承链最末端/最派生的合约,是contracts的子集)。源码中通过“所有合约减去出现在任何inheritance列表中的合约”计算得出(slither/core/compilation_unit.py);
  • get_contract_from_name(str):按名字返回匹配的合约列表(可能多个),见 slither/core/compilation_unit.py;
  • [structures | enums | events | variables | functions]_top_level:文件/编译单元顶层的结构体、枚举、事件、变量、函数对象(对应structures_top_level、enums_top_level、events_top_level、variables_top_level、functions_top_level等属性,见 slither/core/compilation_unit.py)。

3.3 实战示例:分析 USDT 合约

以链上 USDT 合约地址为例,遍历其编译单元中的所有合约:

from slither import Slither sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") compilation_unit = sl.compilation_units[0] # Print all the contracts from the USDT address print([str(c) for c in compilation_unit.contracts]) # Print the most derived contracts from the USDT address print([str(c) for c in compilation_unit.contracts_derived])

运行输出:

% python test.py ['SafeMath', 'Ownable', 'ERC20Basic', 'ERC20', 'BasicToken', 'StandardToken', 'Pausable', 'BlackList', 'UpgradedStandardToken', 'TetherToken'] ['SafeMath', 'UpgradedStandardToken', 'TetherToken']

可见contracts列出全部 10 个合约(含库合约与所有被继承的中间合约),而contracts_derived只保留继承链顶端的SafeMath、UpgradedStandardToken与TetherToken三个最终合约。

四、Contract 对象:合约层信息

4.1 核心属性与方法

Contract对象(实现见 slither/core/declarations/contract.py)提供合约层的完整视图:

  • name: str:合约名称;
  • functions: list[Function]:合约函数列表;
  • modifiers: list[Modifier]:修饰器列表;
  • all_functions_called: list[Function/Modifier]:合约可达的所有内部函数(含经内部调用传递到达的);
  • inheritance: list[Contract]:被继承的合约列表,按 C3 线性化顺序排列;
  • derived_contracts: list[Contract]:由该合约派生出的合约;
  • get_function_from_signature(str): Function:按签名(如"transfer(address,uint256)")查找函数;
  • get_modifier_from_signature(str): Modifier:按签名查找修饰器;
  • get_state_variable_from_name(str): StateVariable:按名字查找状态变量;
  • state_variables: List[StateVariable]:合约可访问的状态变量列表;
  • state_variables_ordered: List[StateVariable]:按声明顺序排列的全部状态变量。

从源码看,state_variables返回“可访问”的变量,即不包含从被继承合约中继承的私有变量(slither/core/declarations/contract.py);而state_variables_ordered保留声明顺序,并且进一步派生出storage_variables_ordered(存储槽位中的变量按声明顺序)与transient_variables_ordered等细化视图(slither/core/declarations/contract.py)。若需区分“自己声明的”与“继承来的”状态变量,还可使用state_variables_declared与state_variables_inherited。

4.2 实战示例:打印 USDT 的状态变量

from slither import Slither sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") compilation_unit = sl.compilation_units[0] # Print all the state variables of the USDT token contract = compilation_unit.get_contract_from_name("TetherToken")[0] print([str(v) for v in contract.state_variables])

运行输出:

% python test.py ['owner', 'paused', '_totalSupply', 'balances', 'basisPointsRate', 'maximumFee', 'allowed', 'MAX_UINT', 'isBlackListed', 'name', 'symbol', 'decimals', 'upgradedAddress', 'deprecated']

注意get_contract_from_name返回的是列表,因此需要取[0]来获得合约对象。

五、Function 对象:函数与修饰器层

5.1 核心属性

Function对象(实现见 slither/core/declarations/function.py),Modifier对象同样适用,提供以下属性:

  • name: str:函数名;
  • contract: Contract:声明该函数的合约;
  • nodes: list[Node]:构成函数/修饰器 CFG 的节点列表;
  • entry_point: Node:CFG 的入口节点;
  • [state | local]_variable_[read | write]: list[StateVariable]:函数内读/写的状态变量与局部变量列表;
  • 上述所有属性均可加上all_前缀进行递归查找,例如all_state_variable_read会返回包括内部调用在内的全部被读状态变量;
  • slithir_operations: List[Operation]:函数对应的 IR 操作列表。

源码中state_variables_read、state_variables_written直接返回函数体内收集的读写集合(slither/core/declarations/function.py),而all_state_variables_read等递归版本则沿内部调用链展开(slither/core/declarations/function.py)。

5.2 实战示例:区分直接读写与递归读写

from slither import Slither sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") compilation_unit = sl.compilation_units[0] contract = compilation_unit.get_contract_from_name("TetherToken")[0] transfer = contract.get_function_from_signature("transfer(address,uint256)") # Print all the state variables read by the transfer function print([str(v) for v in transfer.state_variables_read]) # Print all the state variables read by the transfer function and its internal calls print([str(v) for v in transfer.all_state_variables_read])

运行输出:

% python test.py ['deprecated', 'isBlackListed', 'upgradedAddress'] ['owner', 'basisPointsRate', 'deprecated', 'paused', 'isBlackListed', 'maximumFee', 'upgradedAddress', 'balances']

对比两组输出可以直观看出all_前缀的作用:state_variables_read只包含transfer函数体直接读取的 3 个状态变量,而all_state_variables_read沿内部调用(如_transfer内部的读写)递归展开后,得到 8 个状态变量。这是做数据依赖分析、重入检测时最常用的 API 之一。

六、Node 对象:控制流图遍历

Node对象(实现见 slither/core/cfg/node.py)表示 CFG 中的一个节点。访问节点有两种方式:

  • 顺序无关:直接遍历function.nodes即可;
  • 顺序相关:需要沿 CFG 边手动遍历。

顺序相关的遍历需要自己写递归访问函数,例如:

def visit_node(node: Node, visited: List[Node]): if node in visited: return visited += [node] # custom action for son in node.sons: visit_node(son, visited)

其中node.sons是节点的后继节点列表。若需多次迭代(高级用法),可以:

  • 将迭代次数限制为 X 次;
  • 或建立不动点(fix-point),即构造抽象解释风格的迭代分析——重复遍历 CFG 直到信息不再发生变化。

这两种模式分别对应“有限深度展开”与“数据流不动点迭代”两类分析框架,是编写路径查找、污点分析等工具的基础。仓库中 examples/scripts/possible_paths.py 与 examples/scripts/export_to_dot.py 展示了基于节点遍历的实际应用。

七、SlithIR:中间表示层

7.1 什么是 SlithIR

SlithIR 是 Slither 为每个函数生成的自有中间表示,位于 slither/slithir 目录下。它把 Solidity 表达式降级为一系列规范化的指令(operation),每个 IR 操作都有自己专属的类与访问方法,便于程序化检查。

判断一个 IR 操作的类型,使用 Python 内置的isinstance:

  • 检查是否为某类操作:isinstance(ir, TYPE),例如isinstance(ir, Call);
  • 检查是否为加法运算:isinstance(ir, Binary) & ir.type == BinaryType.ADDITION;
  • 检查是否为对某合约的高层调用:isinstance(ir, HighLevelCall) & ir.destination == MyContract。

其中Call、Binary、HighLevelCall、BinaryType等类型统一从 slither/slithir/operations/init.py 导出,完整的操作类型还包括InternalCall、LibraryCall、LowLevelCall、SolidityCall、EventCall、Send、Transfer等,覆盖赋值、二元运算、条件跳转、数组初始化、类型转换、解包(unpack)等各类语句。

7.2 实战示例:提取 totalSupply 中的外部调用

from slither import Slither sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") compilation_unit = sl.compilation_units[0] contract = compilation_unit.get_contract_from_name("TetherToken")[0] totalSupply = contract.get_function_from_signature("totalSupply()") # Print the external call made in the totalSupply function for ir in totalSupply.slithir_operations: if isinstance(ir, HighLevelCall): print(f"External call found {ir} ({ir.node.source_mapping})")

运行输出:

% python test.py External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339)

每个 IR 操作还带有node.source_mapping,可以回溯到 Solidity 源码中的具体位置,实现“IR 指令 ↔ 源码行号”的双向映射,这对生成可审计、可定位的分析报告非常有用。

八、完整示例:打印项目基本信息

文档配套的示例脚本 docs/src/api/examples/print_basic_information.py 综合运用了上述多层 API,展示如何打印一个项目的基本信息:

from slither import Slither # Init slither slither = Slither("coin.sol") for contract in slither.contracts: # Print the contract's name print(f"Contract: {contract.name}") # Print the name of the contract inherited print(f"\tInherit from{[c.name for c in contract.inheritance]}") for function in contract.functions: # For each function, print basic information print(f"\t{function.full_name}:") print(f"\t\tVisibility: {function.visibility}") print(f"\t\tContract: {function.contract}") print(f"\t\tModifier: {[m.name for m in function.modifiers]}") print(f"\t\tIs constructor? {function.is_constructor}")

注意这里直接通过slither.contracts遍历(Slither对象汇总了所有编译单元的合约),并使用了contract.inheritance(C3 线性化继承列表)、function.full_name(含签名的完整函数名)、function.visibility、function.modifiers、function.is_constructor等属性。

该脚本针对仓库中的测试合约 docs/src/api/examples/coin.sol(一个带virtual/override继承与事件定义的Coin/MyCoin合约对)运行,预期输出见 docs/src/api/examples/expected_results_print_basic_information.txt:

Contract: Coin Inherit from[] _mint(address,uint256): Visibility: internal Contract: Coin Modifier: [] Is constructor? False mint(address,uint256): Visibility: public Contract: Coin Modifier: [] Is constructor? False ... Contract: MyCoin Inherit from['Coin'] _mint(address,uint256): Visibility: internal Contract: MyCoin Modifier: [] Is constructor? False ...

输出中值得注意的细节:

  • Coin的Inherit from[]为空,而MyCoin显示['Coin'],印证inheritance返回的是 C3 线性化顺序的父合约;
  • MyCoin中_mint出现了两次:一次是继承自Coin的虚函数,一次是MyCoin自己override的版本,且它们的Contract均指向MyCoin,说明function.contract返回的是“声明/解析后归属”的合约;
  • 脚本还会输出 Slither 内部生成的slitherConstructorVariables()辅助函数,这在查看自动生成的构造相关代码时是正常现象。

九、从 API 到检测器:进阶方向

掌握了六层对象模型之后,就可以在此基础上构建更复杂的分析。仓库提供了大量真实用例可供参考:

  • 检测器(Detector):Slither 自带的上百个检测器均基于该 API 实现,例如重入检测 slither/detectors/reentrancy/reentrancy.py、未检查返回值检测 slither/detectors/operations/unchecked_low_level_return_values.py 等;自定义检测器需继承AbstractDetector并实现_detect(参见 slither/detectors/abstract_detector.py);
  • 脚本工具:仓库 examples/scripts 目录下有大量可直接运行的 API 使用示例,例如 examples/scripts/functions_called.py(列出函数调用关系)、examples/scripts/variable_in_condition.py(分析条件中的变量)、examples/scripts/slithIR.py(遍历 IR 指令)、examples/scripts/data_dependency.py(数据依赖分析);
  • 插件机制:可通过slither.register_detector(...)/slither.register_printer(...)动态注册自定义分析组件(slither/slither.py),plugin_example目录提供了完整的插件工程样例。

建议的实践路径是:先用本文第二节到第七节的对象模型写出“能跑通”的遍历脚本,再对照 examples/scripts 中的成熟示例逐步引入数据依赖、CFG 遍历与 SlithIR 指令级分析,最终沉淀为自己的检测器或审计工具。

  • 应用安全
  • 区块链

【免费下载链接】slither

Static Analyzer for Solidity and Vyper

项目地址:https://gitcode.com/gh_mirrors/sl/slither
点击查看免费下载

相关推荐

上一篇:Prometheus告警规则监控告警系统集成:awesome-prometheus-alerts与Dynatrace Alerts
下一篇:终极Linux服务器键盘记录防护指南:使用libinput与evtest构建安全防线

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询