以下都是来自我的新作《解密EVM机制及合约安全漏洞》里的内容
电子版PDF下载:https://download.youkuaiyun.com/download/softgmx/10800947
研究环境:
OS |
ubuntu 16.04 |
VM及合约语言 |
EVM/ solidity |
合约调试器 |
https://remix.ethereum.org |
Ethererum源码 |
go语言版本的 |
EVM机制原理
智能合约容易产生漏洞的主要原因:
- 开发人员对EVM的运行机制不了解
- stack、memory和storage是怎样存储数据的
- 合约间调用是怎样实现的,传参和返回值又是怎样在合约间传递的
- 用链上数据做随机数种子时,应注意伪随机的问题(链上数据是可见的,合约里定义的私有变量实际是公开可见的,且一些字段是可以被矿工操纵的)
- solidity封装好的区块访问方法实际上是读取区块链的哪部分数据
- 区块链中交易费用出价高者可以插队优先交易
- gas的消耗实现机制
- 开发人员对solidity编译器的一些内部处理不了解
- send、transfer和call底层转账方法的实现原理和区别
- fallback机制的实现原理
- storage变量的存储和索引原理
- 开发人员对solidity语言不熟悉
- 构造函数
- 鉴权方法
- 日志记录
- 算数溢出
这样我们分析问题可以抽象出的三个层次来研究,如下图:
图一 智能合约的层次
以太坊的智能合约机(EVM)构成及工作原理:
每次我们call一个合约方法时,在call的函数实现里,首先会创建一个contract 类对象,并填充对应的字段值(code,CallerAddress,Input,gas,value,selfAddress),然后把这个contract对象传入EVM的解释器Interpeter进行逐条指令的解释执行,但在开始解释之前,会生成一个新的stack和一个memory对象以用于后面程序的运行,并把结果写入合约地址对应的StateDB。
图二 EVM的工作原理
下面列出了EVM的几个关键类的定义:
图三 对应的类图
图四 EVM执行智能合约中function的过程
EVM三大核心部件:
- stack
- memory
- storage
Stack的实现:
type Stack struct {
data []*big.Int
}
- 最小单元32字节(最小对齐单位)
- 初始容量1024个元素
- 以动态数组方式实现,理论上可扩容(但实际上一个方法只有1024个元素可用)
- 主要指令push、pop、swap 、dup
- 数据不持久化
- 合约间调用,方法之间不共用栈(一个方法分配一个栈)
- 同一合约的方法调用不产生call, 直是简单的 jump
Memory的实现:
type Memory struct {
store []byte
lastGasCost uint64
}
- 最小单位有一个字节
- 以动态字节数组方式实现,可以扩容
- 类似x86架构里的堆,数据不具有持久性,合约执行完成,数据消失
- 主要指令mload、mstore
Storage的实现:
- 通过StateDB把数据存储在区块链上
- 主要指令sload、sstore
- 数据被持久化(写在链上了,并在所有矿机上同步)
- 容量是2的256次幂,storage[slot]=value, key和value都是32位对齐的
slot=[ 0x0000000000000000000000000000000000000000000000000000000000000000,……
,0xffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff]
命令集分类:
- 算术、逻辑运算(add、sub、mul、div、mod、lt ,gt、 not、xor、or、and等)
- 栈操作、内存操作、存储操作(push、pop、mload、mstore、sload、sstore)
- 区块链操作(address、balance、gas、caller、callvalue、origin、blockhash、timestamp、difficulty、gaslimit、coinbase、log等)
- 执行流操作(jump、jumpi、call、callcode、delegatecall、 staticcall)
命令集具体说明:
Instruction |
|
|
Explanation |
stop |
- |
F |
stop execution, identical to return(0,0) |
add(x, y) |
|
F |
x + y |