# 加密日记【011】EVM谜题集PLUS **Published by:** [0x3c](https://paragraph.com/@gamfi/) **Published on:** 2023-04-07 **URL:** https://paragraph.com/@gamfi/011-evm-plus ## Content https://github.com/daltyboy11/more-evm-puzzles 这是难度加深的EVM谜题集 more-evm-puzzlesEVM Puzzle 1EXP:a**bPC:程序计数器 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 200 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和CALLcreate操作码执行结束后就只剩下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替代重复push00 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 [] 最终字节码:0x600A80600080396000F3EVM Puzzle 3补充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 ## Publication Information - [0x3c](https://paragraph.com/@gamfi/): Publication homepage - [All Posts](https://paragraph.com/@gamfi/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@gamfi): Subscribe to updates - [Twitter](https://twitter.com/pangmadee): Follow on Twitter