前言
在【区块链安全 | 第三十七篇】合约审计之获取私有数据(一)中,介绍了私有数据、访问私有数据实例、Solidity 中的数据存储方式等知识,本文通过分析具体合约代码进行案例分析。
漏洞代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Vault {
// 公共变量,可以通过 getter 函数读取
// slot 0
uint public count = 123;
// 部署合约的地址会成为 owner
// slot 1
address public owner = msg.sender;
// 示例布尔值和 uint16 类型变量
// slot 1
bool public isTrue = true;
uint16 public u16 = 31;
// 私有密码变量,不能通过合约外部直接访问
// slot 2
bytes32 private password;
// 常量值,在部署时就固定,不可更改
// 编译时嵌入,不占 slot
uint public constant someConst = 123;
// 固定长度的 bytes32 数组,长度为 3
// slot 3、slot 4、slot 5
bytes32[3] public data;
// 用户结构体,包含用户 ID 和密码字段
struct User {
uint id;
bytes32 password;
}
// 用户动态数组,仅限内部访问
// slot 6
User[] private users;
// 映射:通过用户 ID 查找对应的用户结构体
mapping(uint => User) private idToUser;
// 构造函数,在部署时设置初始密码
constructor(bytes32 _password) {
password = _password;
}
// 添加新用户,自动分配 ID,并存储在数组和映射中
function addUser(bytes32 _password) public {
User memory user = User({
id: users.length,
password: _password
});
users.push(user);
idToUser[user.id] = user;
}
// 工具函数:用于计算数组中元素的 storage 位置
function getArrayLocation(
uint slot,
uint index,
uint elementSize
) public pure returns (uint) {
return uint(keccak256(abi.encodePacked(slot))) + (index * elementSize);
}
// 工具函数:用于计算映射中某个键的 storage 位置
function getMapLocation(uint slot, uint key) public pure returns (uint) {
return uint(keccak256(abi.encodePacked(key, slot)));
}
}