序言
EVM 是两个轻量的软件包,其内部结构设计本意是提供更多一类能忽视硬体、作业系统等相容性的交互式的继续执行自然环境供镜像坊互联网运转智能化合同。
单纯而言 EVM 是两个全然分立的沙箱,在 EVM 中运转的标识符是封禁互联网、磁盘和其它民主化的,以来防止严重错误的标识符能让智能化合同吞噬或是负面影响宏观经济自然环境。
在此基础上,晓得Nabugabo区块链安全可靠生物医学带我们一同深入细致认知 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 里面的统计数据,必须手动指定偏移量和读取的字节数。
Memory
Memory 两个单纯的字节数组,主要是在运转期间存储统计数据,将参数传递给内部函数。基于 32byte 进行寻址和扩展。
EVM 统计数据存储概述
前面已经说过 Storage 是每一合同长久化存储统计数据的地方性其存储统计数据的方式是通过插槽来实现的,现在就具体介绍它是怎么实现的:
状态变量
1.对于大小在 32 字节以内的变量(常量),以其定义的顺序作为它的索引值来存储。即第两个变量的索引为 key(0),第二个变量的索引为 key(1)…
2.对于连续较小的值,可能被优化存储在同两个边线,比如:合同中前四个状态变量都是 uint64 类型的,则四个状态变量的值会被打包成两个 32 字节的值存储在 0 边线。
未优化:
pragmasolidity ^0.4.11;contractC {uint256a = 12;uint256c = 12;uint256b = 12;uint256d = 12;functionm() view public returns(uint256,uint256,uint256,uint256){return(a,b,c,d);}}
优化后:
pragma solidity ^0.4.11;
contract C {
uint64a =12;uint64c =12;uint64b =12;uint64d =12;
function m() view public returns(uint64,uint64,uint64,uint64){return(a,b,c,d);
}
}
内部结构体
对于大小在 32 字节以内的内部结构体同样也是顺序存储,例如内部结构体变量索引定义在边线 0,内部结构体内部有两个成员,则这两个成员的依序为 0 和 1。
pragmasolidity ^0.4.11;contractC {structInfo {uint256a ;uint256b ;}functionm() external returns(uint256,uint256){Infostorage info;info.a=12 ;info.b=24 ;return(info.a,info.b);}}
映射(map)
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;
}
}
数组
定长数组
同上,只要在 32 字节以内也是顺序存储,不过在编译时编译器会进行边界检查防止越界。
pragma solidity ^0.4.11;
contract C {
uint256[3] a = [12,24,48] ;
function m() public view returns(uint256,uint256,uint256){
return (a[0],a[1],a[2]);
}
}
可变长度数组
由于可变长度数组长度不定,一般在编译可变长度数组时会提前预留存储空间,所以就会使用状态变量的位置存储可变长度数组的长度。
而具体的数据地址会通过计算 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[0],a[1],a[2]);
}
}
字节数组和字符串
如果长度小于等于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 的健值数据库,存储的数据可以通过校验和来确保一致。但是其也是和智能合约语言进行交互的,当其中一些规则发生冲突很可能就被别有用心的人用来作恶,所以规范的使用智能合约语言是避开漏洞的必要条件。
2.分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3.不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4.本站提供的源码、模板、插件等其他资源,都不包含技术服务请大家谅解!
5.如有链接无法下载或失效,请联系管理员处理!
6.本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!