第1节:汇编介绍
Reversing and debugging EVM Smart contracts: First steps in assembly-part1
合约代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Test {
function test() external { }
function test2() external { }
function test3() external { }
}
bytecode
0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80630a8e8e0114604157806366e41cb7146049578063f8a8fd6d146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea2646970667358221220d28f98515dc0855e1c6f5aa3747ff775f1b8ab6545f14c70641ff9af67c2465164736f6c63430008070033
执行test函数
在真正执行test之前,有很多前置操作
检查msg.value
005 CALLVALUE load msg.value
006 DUP1 duplicate msg.value
007 ISZERO verify if msg.value is equal to 0
008 PUSH1 0f push 0f in the Stack (the byte location after the REVERT byte location)
010 JUMPI jump to this location if msg.value is not equal to 0
011 PUSH1 00 push 00 in the Stack
013 DUP1 duplicate 00 in the Stack
014 REVERT revert the execution
In solidity, this is equivalent to:
if (msg.value > 0) {
revert();
} else {
// Jump to byte 15
}
检查CALLDATASIZE
我们调用的是test,没有参数,因此calldata就是test的signatrue,具体为4字节的数据:
015 JUMPDEST | 0x00 |
016 POP ||
017 PUSH1 04 | 0x04 |
019 CALLDATASIZE | msg.data.size | 0x04 |
020 LT | msg.data.size > 0x04 |
021 PUSH1 3c | 0x3c | msg.data.size > 0x04 |
023 JUMPI || (JUMPI takes 2 arguments)
060 JUMPDEST ||
061 PUSH1 00 |0x00|
063 DUP1 |0x00|0x00|
064 REVERT ||
if (msg.data.size < 4) { revert(); }
检查SELECTOR
上面已经确定selector的size,接下来要寻找目标函数,包括calldataload获取数据,偏移量去除填充的0字节,对比等,其中三个函数的selector分别为:
- 0xf8a8fd6d:test
- 0x0a8e8e01:test2
- 0x66e41cb7:test3
024 PUSH1 00 |0x00| (the stack was previously empty in byte 23)
026 CALLDATALOAD |0xf8a8fd6d0000000.60zeros.000000000|
027 PUSH1 e0 |0xe0|0xf8a8fd6d0000000.60zeros.000000000|
029 SHR |0xf8a8fd6d|
030 DUP1 |0xf8a8fd6d|0xf8a8fd6d|
031 PUSH4 0a8e8e01 |0x0a8e8e01|0xf8a8fd6d|0xf8a8fd6d| 《=== 通过对比calldata,发现不是test2
036 EQ |0x0|0xf8a8fd6d|0xf8a8fd6d|
037 PUSH1 41 |0x41|0x1|0xf8a8fd6d|
039 JUMPI |0xf8a8fd6d|
040 DUP1 |0xf8a8fd6d|0xf8a8fd6d|
041 PUSH4 66e41cb7 ||0xf8a8fd6d|0xf8a8fd6d| 《=== 通过对比calldata,发现不是test3
046 EQ |0x0|0xf8a8fd6d|
047 PUSH1 49 |0x49|0x1|0xf8a8fd6d|
049 JUMPI |0xf8a8fd6d|
050 DUP1 |0xf8a8fd6d|0xf8a8fd6d|
051 PUSH4 f8a8fd6d |0xf8a8fd6d|0xf8a8fd6d|0xf8a8fd6d| 《=== 通过对比calldata,发现确实是test,找到了!!
056 EQ |0x1|0xf8a8fd6d|
057 PUSH1 51 |0x51|0x1|0xf8a8fd6d|
059 JUMPI |0xf8a8fd6d|
最终反编译
mstore(0x40,0x80)
if (msg.value > 0) { revert(); }
if (msg.data.size < 4) { revert(); }
byte4 selector = msg.data[0x00:0x04]
switch (selector) {
case 0x0a8e8e01: // JUMP to 41 (65 in dec) stop()
case 0x66e41cb7: // JUMP to 49 (73 in dec) stop()
case 0xf8a8fd6d: // JUMP to 51 (85 in dec) stop()
default: revert();
stop()