深入理解 EVM 存储机制及安全问题

EVM 是一个轻量级的虚拟机,其设计初衷就是提供一种可以忽略硬件、操作系统等兼容性的虚拟的执行环境供以太坊网络运行智能合约。

简单来说 EVM 是一个完全独立的沙盒,在 EVM 中运行的代码是无法访问网络、文件系统和其他进程的,以此来避免错误的代码能让智能合约毁灭或者影响外部环境。

在此基础上,知道创宇区块链安全实验室带大家一起深入理解 EVM 的存储机制和安全问题。

可以看到 EVM 存储数据分为两类:

存储在 code 和 storage 里的数据是 non-volatile (不容易丢失的)

存储在 stack,args,memory 里数据是volatile(容易丢失的)

Code

code 部署合约时储存 data 字段也就是合约内容的空间,即专门存储智能合约的二进制源码的空间

Storage

Storage 是一个可以读写修改的持久存储的空间,也是每个合约持久化存储数据的地方。Storage 是一个巨大的 map,一共 2^256 个插槽 (slot),每个插糟有 32byte,合约中的“状态变量”会根据其具体类型分别保存到这些插槽中。

Stack

stack 即所谓的“运行栈",用来保存 EVM 指令的输入和输出数据。可以免费使用,没有 gas 消耗,用来保存函数的局部变量,数量被限制在 16 个。stack 的最大深度为 1024 ,其中每个单元是 32 byte。

Args

args 也叫 calldata,是一段只读的可寻址的保存函数调用参数的空间,与栈不同的地方的是,如果要使用 calldata 里面的数据,必须手动指定偏移量和读取的字节数。

美股区块链板块盘前走高,嘉楠科技(CAN.O)涨超5%:行情显示,美股区块链板块盘前走高,嘉楠科技(CAN.O)涨超5%,SOS(SOS.N)涨4.85%,第九城市(NCTY.O)涨3.61%,Bit Digital(BTBT.O)涨3.57%,MicroStrategy(MSTR.O)涨3.3%,亿邦国际(EBON.O)涨2.92%。[2021/6/23 0:00:36]

Memory

Memory 一个简单的字节数组,主要是在运行期间存储数据,将参数传递给内部函数。基于 32byte 进行寻址和扩展。

前面已经说过 Storage 是每个合约持久化存储数据的地方其储存数据的方式是通过插槽来实现的,现在就具体介绍它是怎么实现的:

1.对于大小在 32 字节以内的变量(常量),以其定义的顺序作为它的索引值来存储。即第一个变量的索引为 key(0),第二个变量的索引为 key(1)...

2.对于连续较小的值,可能被优化存储在同一个位置,比如:合约中前四个状态变量都是 uint64 类型的,则四个状态变量的值会被打包成一个 32 字节的值存储在 0 位置。

未优化:

pragma solidity ^0.4.11;contract C {? ?uint256 a = 12;? ?uint256 c = 12;? ?uint256 b = 12;? ?uint256 d = 12;? ?function m() view public returns(uint256,uint256,uint256,uint256){? ? ? ?return (a,b,c,d);? ?}}

优化后:

pragma solidity ^0.4.11;contract C {? ?uint64 a = 12;? ?uint64 c = 12;? ?uint64 b = 12;? ?uint64 d = 12;? ?function m() view public returns(uint64,uint64,uint64,uint64){? ? ? ?return (a,b,c,d);? ?}}

Bitcoin ABC 0.22.14版本已发布:2月24日,Bitcoin ABC官方宣布已推出Bitcoin ABC 0.22.14版本,该版本增加了对Tor v3地址和BIP155中定义的其他地址的支持。[2021/2/24 17:48:58]

对于大小在 32 字节以内的结构体同样也是顺序存储,例如结构体变量索引定义在位置 0,结构体内部有两个成员,则这两个成员的依序为 0 和 1。

pragma solidity ^0.4.11;contract C {struct Info {? ?uint256 a ;? ?uint256 b ;}? ?function m() ?external returns(uint256,uint256){? ? ? ?Info storage info;? ? ? ?info.a = 12 ;? ? ? ?info.b = 24 ;? ? ? ?return(info.a,info.b);? ?}}

map 存储位置是通过 keccak256 (bytes32(key) + bytes32(position) ) 计算得到的,position 表示 key 对应 storage 类型变量存储的位置。

pragma solidity ^0.4.11;contract Test {?mapping(uint256 => uint256) knownsec;?function go() public {? ? ?knownsec[0x60] = 0x40;?}}

Gate.io中午12点上线STAR交易,已为FIL用户完成首次空投:据官方公告,Gate.io将于11月3日(今日)12:00上线文曲星Filestar(STAR)交易,按照计划,Gate.io已按照1FIL:0.1STAR比例完成首次STAR代币空投,第二次空投快照截取时间为 2020年11月10日08:00。[2020/11/3 11:30:37]

同上,只要在 32 字节以内也是顺序存储,不过在编译时编译器会进行边界检查防止越界。

pragma solidity ^0.4.11;contract C {? ?uint256 a = [12,24,48] ;? ?? ?function m() public view returns(uint256,uint256,uint256){? ? ? ?return (a,a,a);? ?}? ?}

由于可变长度数组长度不定,一般在编译可变长度数组时会提前预留存储空间,所以就会使用状态变量的位置存储可变长度数组的长度。

而具体的数据地址会通过计算 keccak256 (bytes32(position)) 算得数组首地址,再加数组长度偏移量获得具体的元素。

pragma solidity ^0.4.11;contract C {? ?uint256[] a = [12,24,48] ;? ?? ?function m() public view returns(uint256,uint256,uint256){? ? ? ?return (a,a,a);? ?}? ?}

如果长度小于等于31字节 :

1.对于定长字节数组则是同定长数组一样;

2.对于可变字节数组和字符串,会在存储值位置补0一直到32字节,并用补0的最后一个字节存储字符串的编码长度。

pragma solidity ^0.4.4;contract A{? ?string public name0 = "knownsec";?? ?bytes8 public name=0x6b6e6f776e736563;? ?bytes public g ;? ?? ?function test() public {? ? ? ?g.push(0xAA);? ? ? ?g.push(0xBB);? ? ? ?g.push(0xCC);? ?}? ?function go() public view returns(bytes){? ? ? ?return g;? ?}}

当节数组和字符串长度大于31字节时

1.变量位置存储编码长度,并且编码长度公式更换为编码长度 = 字符数 * 2 + 1

2.真实存储值第一个位置通过公式 keccak256(bytes32(position)) 获取,剩余值在获取到的位置顺序存储,同样在最后存储位置补0到32字节。

string public name = "knownsecooooooooooooooooooooooooo";

前面已经讲到EVM的存储结构及存储机制,现在我们再来探讨其安全问题。

漏洞原理:

在官方手册中提到结构体,数组和映射的局部变量默认是放在 storage 中的,而 solidity 语言中函数中设置的局部变量的默认类型取决于它们本身的类型。

因此如果在函数内部设置以上 storage 类型变量却没有进行初始化,他们就相当于存储指针指向合约中的其他变量,当我们对其进行改变时改变的就是其指向的变量。漏洞合约,目的修改 owner 为自己地址:

pragma solidity ^0.4.0;contract testContract{? ?bool public unlocked = false;? ?address public owner = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;struct Person {? ?bytes32 name;? ?address mappedAddress;}? ?function test(bytes32 _name , address ?_mappedAddress) public{? ? ? ?Person person;? ? ? ?person.name = _name;? ? ? ?person.mappedAddress = _mappedAddress;? ? ? ?require(unlocked);? }}

漏洞合约分析:

可以看到该合约在函数部分创建新的结构体时没有进行初始化,由此我们可以利用该函数进行对owner的修改。不过使用该函数我们还要通过require验证,不过这也不难因为状态变量unlocked也同样在我们可控的范围内。

具体操作:

调用test函数分别传入向_name 传入:0x0000000000000000000000000000000000000000000000000000000000000001(真值)

_mappedAddress 传入:0xfB89eCb0188cb83c220aADDa1468C1635208e821(个人地址)

传参前:

传参后:

可以看到已经成功更改了地址。

可以看到 EVM 的存储器就是一个 key=>value 的健值数据库,存储的数据可以通过校验和来确保一致。但是其也是和智能合约语言进行交互的,当其中一些规则发生冲突很可能就被别有用心的人用来作恶,所以规范的使用智能合约语言是避开漏洞的必要条件。

郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。

金智博客

XLM金色早报 | 中国与澳大利亚2021女篮亚洲杯比赛出现BTC广告

头条 ▌中国与澳大利亚2021女篮亚洲杯比赛出现BTC广告 金色财经报道,在CCTV 5转播的中国女篮与澳大利亚女篮2021女篮亚洲杯小组赛中,一家BTC公司广告出现在本次比赛的地板上,据悉,该公司是一家名为Bitci的土耳其数字货币公司,根据其官网信息显示,他们与国际篮联是合作伙伴关系。

欧易交易所金色观察 | 发改委、央行等连发通知整治挖矿及虚拟货币交易炒作 加密市场应声下跌

9月24日晚间,发改委、央行等连发通知提及整治挖矿及虚拟货币交易炒作。通知发出后,加密市场应声大跌。 其中,国家发展改革委等部门发布关于整治虚拟货币“挖矿”活动的通知,提及强化新增虚拟货币“挖矿”项目能耗双控约束。 通知还指出,将虚拟货币“挖矿”活动列为淘汰类产业。严禁以数据中心名义开展虚拟货币“挖矿”活动。加强数据中心类企业信用监管。

瑞波币NFT:新技术的前夜还是迅速膨胀的泡沫?

今年以来,一系列近乎天价的拍卖让“NFT”这一基于区块链技术的新名词走到了聚光灯下。进入NFT领域的显赫玩家可以拉出一串长长的名单,其中还不乏腾讯、阿里巴巴等中国互联网企业的身影。 随着各类巨头不断涌入,NFT价格不断飙涨、玩法花样百出。

[0:0ms0-7:437ms