区块链安全——以太坊智能合约静态分析
摘要
目前,以太坊智能合约的安全事件频发,从The DAO事件到最近的Fomo3D奖池被盗,每次安全问题的破坏力都是巨大的,如何正确防范智能合约的安全漏洞成了当务之急。本文主要讲解了如何通过对智能合约的静态分析进而发现智能合约中的漏洞。由于智能合约部署之后的更新和升级非常困难,所以在智能合约部署之前对其进行静态分析,检测并发现智能合约中的漏洞,可以最大限度的保证智能合约部署之后的安全。
本文包含以下五个章节:
智能合约的编译
智能合约汇编指令分析
从反编译代码构建控制流图
从控制流图开始约束求解
常见的智能合约漏洞以及检测方法
第一章 智能合约的编译
本章节是智能合约静态分析的第一章,主要讲解了智能合约的编译,包括编译环境的搭建、solidity编译器的使用。
1.1 编译环境的搭建
我们以Ubuntu系统为例,介绍编译环境的搭建过程。首先介绍的是go-ethereum的安装。
1.1.1 安装go-ethereum
通过apt-get安装是比较简便的安装方法,只需要在安装之前添加go-ethereum的ppa仓库,完整的安装命令如下:
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum
安装成功后,我们在命令行下就可以使用geth,evm,swarm,bootnode,rlpdump,abigen等命令。
当然,我们也可以通过编译源码的方式进行安装,但是这种安装方式需要提前安装golang的环境,步骤比较繁琐。
1.1.2 安装solidity编译器
目前以太坊上的智能合约绝大多数是通过solidity语言编写的,所以本章只介绍solidity编译器的安装。solidity的安装和go-ethereum类似,也是通过apt-get安装,在安装前先添加相应的ppa仓库。完整的安装命令如下:
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc
执行以上命令后,最新的稳定版的solidity编译器就安装完成了。之后我们在命令行就可以使用solc命令了。
1.2 solidity编译器的使用
1.2.1 基本用法
我们以一个简单的以太坊智能合约为例进行编译,智能合约代码(保存在test.sol文件)如下:
pragma solidity ^0.4.25;
contract Test {
}
执行solc命令:
solc --bin test.sol
输出结果如下:
======= test.sol:Test ======= Binary: 6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a72305820f633e21e144cae24615a160fcb484c1f9495df86d7d21e9be0df2cf3b4c1f9eb0029
solc命令的–bin选项,用来把智能合约编译后的二进制以十六进制形式表示。和–bin选项类似的是–bin-runtime,这个选项也会输出十六进制表示,但是会省略智能合约编译后的部署代码。接下来我们执行solc命令:
solc --bin-runtime test.sol
输出结果如下:
======= test.sol:Test ======= Binary of the runtime part: 6080604052600080fd00a165627a7a72305820f633e21e144cae24615a160fcb484c1f9495df86d7d21e9be0df2cf3b4c1f9eb0029
对比两次输出结果不难发现,使用–bin-runtime选项后,输出结果的开始部分少了6080604052348015600f57600080fd5b50603580601d6000396000f300,为何会少了这部分代码呢,看完接下来的智能合约编译后的字节码结构就明白了。
1.2.2 智能合约字节码结构
智能合约编译后的字节码,分为三个部分:部署代码、runtime代码、auxdata。
1.部署代码:以上面的输出结果为例,其中6080604052348015600f57600080fd5b50603580601d6000396000f300为部署代码。以太坊虚拟机在创建合约的时候,会先创建合约账户,然后运行部署代码。运行完成后它会将runtime代码+auxdata 存储到区块链上。之后再把二者的存储地址跟合约账户关联起来(也就是把合约账户中的code hash字段用该地址赋值),这样就完成了合约的部署。
2.runtime代码:该例中6080604052600080fd00是runtime代码。
3.auxdata:每个合约最后面的43字节就是auxdata,它会紧跟在runtime代码后面被存储起来。
solc命令的–bin-runtime选项,输出了runtime代码和auxdata,省略了部署代码,所以输出结果的开始部分少了
6080604052348015600f57600080fd5b50603580601d6000396000f300。
1.2.3 生成汇编代码
solc命令的–asm选项用来生成汇编代码,接下来我们还是以最初的智能合约为例执行solc命令,查看生成的汇编代码。
执行命令:
solc --bin --asm test.sol
输出结果如下:
======= test.sol:Test =======
EVM assembly:
... */ "test.sol":28:52 contract Test {
mstore(0x40, 0x80)
callvalue
/* "--CODEGEN--":8:17 */
dup1
/* "--CODEGEN--":5:7 */
iszero
tag_1
jumpi
/* "--CODEGEN--":30:31 */
0x0
/* "--CODEGEN--":27:28 */
dup1
/* "--CODEGEN--":20:32 */
revert
/* "--CODEGEN--":5:7 */
tag_1:
... */ "test.sol":28:52 contract Test {
pop
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x0
codecopy
0x0
return
stop
sub_0: assembly {
... */ /* "test.sol":28:52 contract Test {
mstore(0x40, 0x80)
0x0
dup1
摘要
目前,以太坊智能合约的安全事件频发,从The DAO事件到最近的Fomo3D奖池被盗,每次安全问题的破坏力都是巨大的,如何正确防范智能合约的安全漏洞成了当务之急。本文主要讲解了如何通过对智能合约的静态分析进而发现智能合约中的漏洞。由于智能合约部署之后的更新和升级非常困难,所以在智能合约部署之前对其进行静态分析,检测并发现智能合约中的漏洞,可以最大限度的保证智能合约部署之后的安全。
本文包含以下五个章节:
智能合约的编译
智能合约汇编指令分析
从反编译代码构建控制流图
从控制流图开始约束求解
常见的智能合约漏洞以及检测方法
第一章 智能合约的编译
本章节是智能合约静态分析的第一章,主要讲解了智能合约的编译,包括编译环境的搭建、solidity编译器的使用。
1.1 编译环境的搭建
我们以Ubuntu系统为例,介绍编译环境的搭建过程。首先介绍的是go-ethereum的安装。
1.1.1 安装go-ethereum
通过apt-get安装是比较简便的安装方法,只需要在安装之前添加go-ethereum的ppa仓库,完整的安装命令如下:
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum
安装成功后,我们在命令行下就可以使用geth,evm,swarm,bootnode,rlpdump,abigen等命令。
当然,我们也可以通过编译源码的方式进行安装,但是这种安装方式需要提前安装golang的环境,步骤比较繁琐。
1.1.2 安装solidity编译器
目前以太坊上的智能合约绝大多数是通过solidity语言编写的,所以本章只介绍solidity编译器的安装。solidity的安装和go-ethereum类似,也是通过apt-get安装,在安装前先添加相应的ppa仓库。完整的安装命令如下:
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc
执行以上命令后,最新的稳定版的solidity编译器就安装完成了。之后我们在命令行就可以使用solc命令了。
1.2 solidity编译器的使用
1.2.1 基本用法
我们以一个简单的以太坊智能合约为例进行编译,智能合约代码(保存在test.sol文件)如下:
pragma solidity ^0.4.25;
contract Test {
}
执行solc命令:
solc --bin test.sol
输出结果如下: www.wnhack.com
======= test.sol:Test ======= Binary: 6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a72305820f633e21e144cae24615a160fcb484c1f9495df86d7d21e9be0df2cf3b4c1f9eb0029
solc命令的–bin选项,用来把智能合约编译后的二进制以十六进制形式表示。和–bin选项类似的是–bin-runtime,这个选项也会输出十六进制表示,但是会省略智能合约编译后的部署代码。接下来我们执行solc命令:
solc --bin-runtime test.sol
输出结果如下:
======= test.sol:Test ======= Binary of the runtime part: 6080604052600080fd00a165627a7a72305820f633e21e144cae24615a160fcb484c1f9495df86d7d21e9be0df2cf3b4c1f9eb0029
对比两次输出结果不难发现,使用–bin-runtime选项后,输出结果的开始部分少了6080604052348015600f57600080fd5b50603580601d6000396000f300,为何会少了这部分代码呢,看完接下来的智能合约编译后的字节码结构就明白了。
1.2.2 智能合约字节码结构
智能合约编译后的字节码,分为三个部分:部署代码、runtime代码、auxdata。
1.部署代码:以上面的输出结果为例,其中6080604052348015600f57600080fd5b50603580601d6000396000f300为部署代码。以太坊虚拟机在创建合约的时候,会先创建合约账户,然后运行部署代码。运行完成后它会将runtime代码+auxdata 存储到区块链上。之后再把二者的存储地址跟合约账户关联起来(也就是把合约账户中的code hash字段用该地址赋值),这样就完成了合约的部署。 无奈人生安全网
2.runtime代码:该例中6080604052600080fd00是runtime代码。
3.auxdata:每个合约最后面的43字节就是auxdata,它会紧跟在runtime代码后面被存储起来。
solc命令的–bin-runtime选项,输出了runtime代码和auxdata,省略了部署代码,所以输出结果的开始部分少了
6080604052348015600f57600080fd5b50603580601d6000396000f300。
1.2.3 生成汇编代码
solc命令的–asm选项用来生成汇编代码,接下来我们还是以最初的智能合约为例执行solc命令,查看生成的汇编代码。
执行命令:
solc --bin --asm test.sol
输出结果如下:
======= test.sol:Test =======
EVM assembly:
... */ "test.sol":28:52 contract Test {
mstore(0x40, 0x80)
callvalue
/* "--CODEGEN--":8:17 */
dup1
/* "--CODEGEN--":5:7 */
iszero
tag_1
jumpi
/* "--CODEGEN--":30:31 */
0x0
/* "--CODEGEN--":27:28 */
dup1
/* "--CODEGEN--":20:32 */
revert
/* "--CODEGEN--":5:7 */
tag_1:
... */ "test.sol":28:52 contract Test {
pop
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x0
codecopy
0x0
return
stop
sub_0: assembly {
... */ /* "test.sol":28:52 contract Test {
mstore(0x40, 0x80)
0x0
dup1
copyright 无奈人生