我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Slither API 开发指南:基于 Solidity/Vyper 静态分析器的六层对象模型编程实战

Slither API 开发指南:基于 Solidity/Vyper 静态分析器的六层对象模型编程实战 应用安全区块链【免费下载链接】slitherStatic 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 层对象组成Slither—— 主入口对象代表一次对整个目标项目目录、单文件或链上地址的分析会话SlitherCompilationUnit—— 一次 solc或 vyper调用所编译的一组文件即一个编译单元Contract—— 合约层级封装合约名、函数、修饰器、状态变量与继承关系Function—— 函数及Modifier修饰器层级封装 CFG 节点与变量读写信息Node—— 控制流图CFG中的基本块节点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)完成编译再为每个编译单元构建SlitherCompilationUnitslither/slither.py。路径可以是本地目录、单个.sol文件也可以是 Hardhat/Truffle/Foundry 等任意 crytic-compile 支持的工程结构。构造时还可传入大量关键字参数来控制分析行为源码 docstring 明确给出的常用项包括slither/slither.py参数默认值作用solcsolcsolc 可执行文件路径或版本字符串disable_solc_warningsFalse是否关闭 solc 警告输出solc_args传给 solc 的额外参数ast_format--ast-compact-jsonsolc 输出的 AST 格式filter_paths[]需要从结果中过滤掉的路径列表exclude_dependenciesFalse是否排除仅与依赖相关的分析结果generate_patchesFalse是否生成补丁仅 JSON 输出有效change_line_prefix#展示源码位置时使用的行前缀如file.sol#12.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.pyget_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.pyModifier对象同样适用提供以下属性name: str函数名contract: Contract声明该函数的合约nodes: list[Node]构成函数/修饰器 CFG 的节点列表entry_point: NodeCFG 的入口节点[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 什么是 SlithIRSlithIR 是 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(fExternal 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 contracts name print(fContract: {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.inheritanceC3 线性化继承列表、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.txtContract: 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 到检测器进阶方向掌握了六层对象模型之后就可以在此基础上构建更复杂的分析。仓库提供了大量真实用例可供参考检测器DetectorSlither 自带的上百个检测器均基于该 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.pyplugin_example目录提供了完整的插件工程样例。建议的实践路径是先用本文第二节到第七节的对象模型写出“能跑通”的遍历脚本再对照 examples/scripts 中的成熟示例逐步引入数据依赖、CFG 遍历与 SlithIR 指令级分析最终沉淀为自己的检测器或审计工具。赞分享应用安全区块链【免费下载链接】slitherStatic Analyzer for Solidity and Vyper项目地址https://gitcode.com/gh_mirrors/sl/slither点击查看免费下载相关推荐从 Solidity 到 SlithIR构建实用智能合约静态分析器Slither 的设计与实践指南从 Solidity 到 SlithIR构建实用智能合约静态分析器Slither 的设计与实践指南 本篇技术指南以 Trail of Bits 在 202Infer Cost 分析器实战指南基于抽象解释与符号计数的 WCET 静态成本分析Infer Cost 分析器实战指南基于抽象解释与符号计数的 WCET 静态成本分析 本指南以 Infer 开源仓库version 1.1.0官方文档 c静态分析代码质量开发工具GraalVM Truffle 静态对象模型Static Object Model完全指南从 API 入门到 Shape 层次与安全优化GraalVM Truffle 静态对象模型Static Object Model完全指南从 API 入门到 Shape 层次与安全优化 本篇指南围绕 G编译器JIT编译语言运行时高性能计算内存管理上一篇Prometheus告警规则监控告警系统集成awesome-prometheus-alerts与Dynatrace Alerts下一篇终极Linux服务器键盘记录防护指南使用libinput与evtest构建安全防线创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表