# 加密日记【011】EVM谜题集PLUS

By [0x3c](https://paragraph.com/@gamfi) · 2023-04-07

---

[https://github.com/daltyboy11/more-evm-puzzles](https://github.com/daltyboy11/more-evm-puzzles)

这是难度加深的EVM谜题集 more-evm-puzzles

### EVM Puzzle 1

![](https://storage.googleapis.com/papyrus_images/58142a1236d263c908b747a140f33ab5b3c54956d22447ef07b11e4d7ea3743f.png)

*   EXP：a\*\*b
    
*   PC：程序计数器
    

    
    00      36      CALLDATASIZE
    01      34      CALLVALUE
    02      0A      EXP //CALLVALUE**CALLDATASIZE=0x40=64 //CALLVALUE=2
    03      56      JUMP
    04      FE      INVALID
    ........................
    3F      FE      INVALID
    40      5B      JUMPDEST
    41      58      PC //41
    42      36      CALLDATASIZE // 6
    43      01      ADD //PC +CALLDATASIZE=47
    44      56      JUMP
    45      FE      INVALID
    46      FE      INVALID
    47      5B      JUMPDEST
    48      00      STOP
    

### EVM Puzzle 2

![](https://storage.googleapis.com/papyrus_images/8c5b2fa6967e87ac67399211be9922c74e872c2fb357c8e15c564935bae7523d.png)

    00      36        CALLDATASIZE
    01      6000      PUSH1 00
    03      6000      PUSH1 00
    05      37        CALLDATACOPY //将calldata写入memory
    06      36        CALLDATASIZE
    07      6000      PUSH1 00
    09      6000      PUSH1 00
    0B      F0        CREATE //将calldata写入合约
    0C      6000      PUSH1 00
    0E      80        DUP1
    0F      80        DUP1
    10      80        DUP1
    11      80        DUP1
    12      94        SWAP5
    13      5A        GAS
    14      F1        CALL //call(gas,address,0,0,0,0,0)
    15      3D        RETURNDATASIZE //return 返回数据的大小=0A
    16      600A      PUSH1 0A //
    18      14        EQ //
    19      601F      PUSH1 1F
    1B      57        JUMPI
    1C      FE        INVALID
    1D      FE        INVALID
    1E      FE        INVALID
    1F      5B        JUMPDEST
    20      00        STOP
    

### 理解CREATE和CALL

*   create操作码执行结束后就只剩下return返回的结果。
    
*   call是对合约的调用，也就是对运行时代码的执行
    

这道题是有点绕的：

call要返回10(0A)个字节个数据，下面的代码是调call要执行的

    00      600A      PUSH1 0A        [0x0A]
    02      6000      PUSH1 00        [0x00, 0x0A] (offset, size)
    04      F3        RETURN          []
    

上面的代码是create之后返回的，所以create return的内容是操作码 600A600F3

所以create合约的操作码：

    00      64600A6000F3      PUSH5 600A6000F3        [0x600A6000F3]
    06      6000              PUSH1 00                [0x0, 0x600A6000F3] (offset, value)
    08      52                MSTORE                  []
    00      600A              PUSH1 0A                [0x0A]
    02      601B              PUSH1 1B                [0x1B, 0x0A] (offset, size)
    04      F3                RETURN                  []
    

`0x1B`尽管我们将运行时字节码存储在`0x0`\- 那是因为它看起来像是 memory: `0x000000000000000000000000000000000000000000000000000000600A6000F3`。

我们必须跳过值开头的 27 个零才能到达字节码。我们本可以阻止使用`PUSH32 0x600A6000F3000000000000000000000000000000000000000000000000000000`，但这会显着增加构造字节码的大小。

这段构造字节码：0x64600A6000F3600052600A601BF3

用CODECOPY优化：

    00      600A      PUSH1 0A        [0x0A]
    02      6000      PUSH1 00        [0x00, 0x0A]
    04      6000      PUSH1 00        [0x00, 0x00, 0x0A] (destOffset, offset, size)
    06      39        CODECOPY        []
    07      600A      PUSH1 0A        [0x0A]
    09      6000      PUSH1 00        [0x00, 0x0A] (offset, size)
    0B      F3        RETURN          []
    

再优化，用DUP1替代重复push

    00      600A      PUSH1 0A        [0x0A]
    02      80        DUP1            [0x0A, 0x0A]
    03      6000      PUSH1 00        [0x00, 0x0A, 0x0A]
    05      80        DUP1            [0x00, 0x00, 0x0A, 0x0A] (destOffset, offset, size), size
    06      39        CODECOPY        [0x0A]
    07      6000      PUSH1 00        [0x00, 0x0A] (offset, size)
    09      F3        RETURN          []
    

最终字节码：0x600A80600080396000F3

### EVM Puzzle 3

![](https://storage.googleapis.com/papyrus_images/c58c5a2b022fc83bbbfe552dec0d7056269f33d99cc3d3cdeb7b020f8aef8bd6.png)

补充CALL和DELEGATECALL的区别，为什么CALL可以发value，DELEGATECALL不能发送value

`CALL` 指令是用于在当前合约和另一个地址之间进行消息传递的，可以发送以太币（value），并且可以选择在接收方合约执行的函数和参数。当执行 `CALL` 指令时，可以指定发送的以太币数量，这个值可以为0。

相比之下，`DELEGATECALL` 指令也是用于在当前合约和另一个地址之间进行消息传递的，但不同之处在于它不会创建新的执行环境，而是在当前合约的执行环境中执行被调用合约的代码。换句话说，`DELEGATECALL` 可以将当前合约的执行上下文传递给被调用的合约，并在其中执行被调用合约的代码。

因为 `DELEGATECALL` 不会创建新的执行环境，所以它不能像 `CALL` 一样发送以太币。如果在 `DELEGATECALL` 中发送以太币，那么这些以太币会被发送到被调用的合约地址，而不是当前合约的地址，这可能会导致意外的结果和安全问题。因此，`DELEGATECALL` 指令没有提供发送以太币的选项。

    00      36        CALLDATASIZE
    01      6000      PUSH1 00
    03      6000      PUSH1 00
    05      37        CALLDATACOPY
    06      36        CALLDATASIZE
    07      6000      PUSH1 00
    09      6000      PUSH1 00
    0B      F0        CREATE
    0C      6000      PUSH1 00
    0E      80        DUP1
    0F      80        DUP1
    10      80        DUP1
    11      93        SWAP4
    12      5A        GAS
    13      F4        DELEGATECALL
    14      6005      PUSH1 05
    16      54        SLOAD
    17      60AA      PUSH1 AA
    19      14        EQ
    1A      601E      PUSH1 1E
    1C      57        JUMPI
    1D      FE        INVALID
    1E      5B        JUMPDEST
    1F      00        STOP
    

通过DELEGATECALL调用，将值存储在Storage上，SLOAD从storage再取出存入的值与AA相同，则DELEGATECALL实现的操作码：

    00      60AA      PUSH1 AA        [0xAA]
    02      6005      PUSH1 05        [0x05, 0x0A] (key, value)
    04      55        SSTORE          []
    

字节码为60AA600555 那create构造字节码：

    00      6460AA600555      PUSH5 60AA600555        [0x60AA600555]
    06      6000              PUSH1 00                [0x0, 0x60AA600555] (offset, value)
    08      52                MSTORE                  []
    00      600A              PUSH1 0A                [0x0A]
    02      601B              PUSH1 1B                [0x1B, 0x0A] (offset, size)
    04      F3                RETURN                  []
    

我们将运行时字节码写入内存，这样我们就可以`RETURN`在启动时使用它

构造字节码：`0x6460AA600555600052600A601BF3`

---

*Originally published on [0x3c](https://paragraph.com/@gamfi/011-evm-plus)*
