06. Slither中间语言SlitherIR理论介绍

  • 小驹
  • 更新于 2023-06-30 10:41
  • 阅读 2207

节点、表达式与IR的是什么样的关系?如何根据代码生如何提取出IR指令?在遍历节点时,有哪些对象经常用到。

1.直观理解节点、表达式与IR的关系

SlithIR 是 Slither 中用来表示 Solidity 代码的混合中间表示。控制流图的每个节点最多可以包含一个 Solidity 表达式,该表达式被转换为一组 SlithIR 指令。这种表示使得实施分析更容易,而不会丢失 Solidity 源代码中包含的关键语义信息。

  • 节点最多包含一个表达式
  • 一个表达式包含一组SlithIR指令

直观理解下节点、表达式和IR的关系。

对于源代码为

abstract contract WhiteList is Ownable {
    constructor() {setWhiteList(_msgSender(),false);}
}

setWhiteList(_msgSender(),false),在slither处理时,处理到constructor函数时,会生成一个函数对象(假设该函数对象命名为f),该函数对象有nodes属性,可以取得函数对象所有的节点node,这里有2个node节点:

  • ENTRY_POINT
  • EXPRESSION setWhiteList(_msgSender(),false)

以第2个node节点为例,节点对象有个expression属性,对应的就是该节点的expression表达式为EXPRESSION setWhiteList(_msgSender(),false)。(为什么不用第1个node节点做为例子,因为第1个node节点为ENTRY_POINT,它对应的expression为空)。

节点对象还有个属性为irs,表示该节点中对应的IR指令。第2个node节点中的irs中包含两个ir,为

  • TMP_136(address) = INTERNAL_CALL, Context._msgSender()()
  • TMP_137(bool) = INTERNAL_CALL, WhiteList.setWhiteList(address,bool)(TMP_136,False)

上面的关系,在调试过程中的展示如下图

image.png

2.IR指令集合

SlithIR 使用的指令一共不到 40 条。它没有内部控制流表示,依赖于 Slither 的控制流图结构(SlithIR 指令与图中的每个节点相关联)。下面给出了一些重要指令的高级描述:

2.1.赋值

LV和RV分别表示一个被赋值的变量(left-value)和一个被读取的变量(rightvalue)。变量可以是 Solidity 变量或由中间表示创建的临时变量。

2.2.算术运算

表示为二进制或一元运算符:

  • LV = RV BINARY RV
  • LV = UNARY RV

2.3.映射和数组

Solidity 允许操作通过间接引用(dereferencing)访问的映射和数组。 SlithIR 使用一种特定的变量类型,称为 REF (ReferenceVariable) 来存储间接引用的结果。

index运算符允许间接引用一个变量:

  • REF ← Variable [Index]

2.44.结构体

对结构的访问是通过成员运算符完成的:

  • REF ← Variable · Member

2.5.调用

slither共有9种调用指令。

  • LV = L_CALL Destination Function[ARG..] (low-level Solidity call)
  • LV = H_CALL Destination Function[ARG..] (high-level Solidity call)
  • LV = LIB_CALL Destination Function[ARG..] (library call)
  • LV = S_CALL Function [ARG..] (call to a inbuilt-Solidity function)
  • LV = I_CALL Function [ARG..] (call to an internal function)
  • LV = DYN_CALL Variable [ARG..] (call to an internal dynamic function)
  • LV = E_CALL Event [ARG..] (event call)
  • LV = Send Destination (Solidity send)
  • Transfer Destination (Solidity transfer)

一些调用可以有额外的参数,例如,H_CALL、L_CALL、Send 和 Transfer 可以存在value值,代表交易的以太币数量。

2.6.附加指令

包括用于数组操作的 PUSH、用于类型转换的 CONVERT 以及用于操作元组的运算符

  • PUSH

    PUSH LVALUE RVALUE

    PUSH LVALUE Function (for dynamic function)

  • Conversion

    CONVERT LVALUE RVALUE TYPE

  • Unpack

    LVALUE = UNPACK TUPLEVARIABLE INDEX(:int)

3.示例:如何将Solidity转换成IR

slither/examples/scripts/slithIR.py文件中提供了转换功能。

转换功能的源代码:

import sys
from slither import Slither

if len(sys.argv) != 2:
    print("python slithIR.py contract.sol")
    sys.exit(-1)

# Init slither
slither = Slither(sys.argv[1])

# Iterate over all the contracts
for contract in slither.contracts:
    print(f"Contract: {contract.name}")
    # Iterate over all the functions
    for function in contract.functions:

        # Dont explore inherited functions
        if function.contract_declarer == contract:

            print(f"Function: {function.name}")

            # Iterate over the nodes of the function
            for node in function.nodes:

                # Print the Solidity expression of the nodes
                # And the SlithIR operations
                if node.expression:

                    print(f"\tSolidity expression: {node.expression}")
                    print("\tSlithIR:")
                    for ir in node.irs:
                        print(f"\t\t\t{ir}")

我们以下面的solidity源代码为例,看转换出来的IR代码是什么样子的。

using SafeMath for uint;
mapping(address => uint) balances;

function transfer(address to, uint val) public{
    balances[msg.sender] = balances[msg.sender].min(
val);
    balances[to] = balances[to].add(val);
}

下面为上面的源代码转换出来的对应的IR


Function transfer(address,uint256) 
Solidity: balances[msg.sender]=balances[msg.sender].sub(val)
SlithIR:
REF0(uint256)−>balances[msg.sender]
REF1(uint256)−>balances[msg.sender]
TMP1(uint256)=LIBCALLSafeMat...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
小驹
小驹
0xcD46...3461
weixin: xiaoju521区块链安全分析,欢迎私信沟通交流