以太坊在内部实现了一个基于栈的虚拟机,我们称之为EVM(Ethereum Virtual Machine),用户所有的操作最终都会转化为操作码(OPCODE)然后在EVM中执行,下图为整个执行流程,目前我们对EVM的讲解不多,后续会陆续补上。
以太坊在内部实现了一个基于栈的虚拟机,我们称之为EVM(Ethereum Virtual Machine),用户所有的操作最终都会转化为操作码(OPCODE)然后在EVM中执行,下图为整个执行流程,目前我们对EVM的讲解不多,后续会陆续补上。

本文将介绍10道EVM题目,每道题都会要求用户填写部分内容:使得程序正常退出,而不允许执行REVERT。
注意:本文针对道是有EVM基础的同学,对于尚未接触过OPCODE的同学,不要着急,在这里可以先看看EVM里面都有啥,有助于后续深入学习。
运行游戏:https://github.com/fvictorio/evm-puzzles
npm i
npx hardhat play
############
# Puzzle 1 #
############
00      34      CALLVALUE   #[msg.value]
01      56      JUMP    #跳转下一个evm字节,查看JUMPDEST,发现目的地是08,所以msg.value为8
02      FD      REVERT
03      FD      REVERT
04      FD      REVERT
05      FD      REVERT
06      FD      REVERT
07      FD      REVERT
08      5B      JUMPDEST
09      00      STOP
? Enter the value to send: 
############
# Puzzle 2 #
############
00      34      CALLVALUE   #[msg.value]
01      38      CODESIZE    #[10, msg.value],codesize为当前evm中的操作码的数量,每一个是1字节
02      03      SUB #[sub_result], 需要跳转到06,所以 10 - msg.value = 6
03      56      JUMP
04      FD      REVERT
05      FD      REVERT
06      5B      JUMPDEST
07      00      STOP
08      FD      REVERT
09      FD      REVERT
? Enter the value to send: 
############
# Puzzle 3 #
############
00      36      CALLDATASIZE    #[datasize]
01      56      JUMP    #跳转到04,所以我们只要保证calldata到size为4即可,内容不限。
02      FD      REVERT
03      FD      REVERT
04      5B      JUMPDEST
05      00      STOP
? Enter the calldata: 0x11223344
############
# Puzzle 4 #
############
00      34      CALLVALUE   #[msg.value]
01      38      CODESIZE    #[12, msg.value]
02      18      XOR
03      56      JUMP
04      FD      REVERT
05      FD      REVERT
06      FD      REVERT
07      FD      REVERT
08      FD      REVERT
09      FD      REVERT
0A      5B      JUMPDEST
0B      00      STOP
? Enter the value to send: 
############
# Puzzle 5 #
############
00      34          CALLVALUE   #[msg.value]
01      80          DUP1    #[msg.value, msg.value]
02      02          MUL #[mul_result]
03      610100      PUSH2 0100  #[0100, mul_result],反推mul_result为: 0100
06      14          EQ  #[0或1],反推:1
07      600C        PUSH1 0C    #[0C, 0或1],为了能跳转,此时必须为:1
09      57          JUMPI   #[],stack2为1时,跳转到stack1的位置
0A      FD          REVERT
0B      FD          REVERT
0C      5B          JUMPDEST
0D      00          STOP
0E      FD          REVERT
0F      FD          REVERT
? Enter the value to send: (0):
############
# Puzzle 6 #
############
00      6000      PUSH1 00  #[00]
02      35        CALLDATALOAD  #[calldata, 00]
03      56        JUMP  #跳转到0A,所以stack1为:0A
04      FD        REVERT
05      FD        REVERT
06      FD        REVERT
07      FD        REVERT
08      FD        REVERT
09      FD        REVERT
0A      5B        JUMPDEST
0B      00        STOP
############
# Puzzle 7 #
############
00      36        CALLDATASIZE  # [datasize]
01      6000      PUSH1 00  # [00, datasize]
03      80        DUP1      # [00, 00, datasize]
04      37        CALLDATACOPY  # [] data被copy到memory中,栈被清空
05      36        CALLDATASIZE  # [datasize]
06      6000      PUSH1 00      # [00, datasize]
08      6000      PUSH1 00      # [00, 00, datasize]
0A      F0        CREATE    # [deployed_address] 栈被清空,从内存中读取数据,创建合约,返回地址 
0B      3B        EXTCODESIZE   # [address_code_size] 输入地址,返回合约的size
0C      6001      PUSH1 01  # [01, address_code_size]
0E      14        EQ    # [1] address_code_size必须为1,后续的才成立
0F      6013      PUSH1 13
11      57        JUMPI
12      FD        REVERT
13      5B        JUMPDEST
14      00        STOP
? Enter the calldata: 

############
# Puzzle 8 #
############
00      36        CALLDATASIZE  # [datasize]
01      6000      PUSH1 00  # [00, datasize]
03      80        DUP1  # [00, 00, datasize]
04      37        CALLDATACOPY  # []  copy到内存中
05      36        CALLDATASIZE  # [datasize],直接生成数据,不需要栈参数
06      6000      PUSH1 00  # [00, datasize]
08      6000      PUSH1 00  # [00, 00, datasize]
0A      F0        CREATE    # [deployed_address]
0B      6000      PUSH1 00  # [00, deployed_address]
0D      80        DUP1  # [00, 00, deployed_address]
0E      80        DUP1  # [00, 00, 00, deployed_address]
0F      80        DUP1  # [00, 00, 00, 00, deployed_address]
10      80        DUP1  # [00, 00, 00, 00, 00, deployed_address]
11      94        SWAP5 # [deployed_address, 00, 00, 00, 00, 00],兑换1st 和 6th,你没有看错1和6,不是5
12      5A        GAS   # [gasAvail, deployed_address, 00, 00, 00, 00, 00] // 7个参数
13      F1        CALL  # [0或1]调用函数,需要是0,0表示失败,1表示成功!(反推的)
14      6000      PUSH1 00  # [00, 0或1],需要是0
16      14        EQ    # [0或1],需要是1
17      601B      PUSH1 1B  # [1B, 0或1],需要是1
19      57        JUMPI                     
1A      FD        REVERT
1B      5B        JUMPDEST
1C      00        STOP
? Enter the calldata:
############
# Puzzle 9 #
############
00      36        CALLDATASIZE  # [datasize]
01      6003      PUSH1 03  # [03, datasize]
03      10        LT    # [1], stack(1) < stack(2),
04      6009      PUSH1 09  # [09, 1]
06      57        JUMPI
07      FD        REVERT
08      FD        REVERT
09      5B        JUMPDEST  # [] 跳转到这里
0A      34        CALLVALUE     # [msgvalue]
0B      36        CALLDATASIZE  # [datasize, msgvalue]
0C      02        MUL       # [mul_result]
0D      6008      PUSH1 08  # [08, mul_result]
0F      14        EQ    # [1], 必须是0
10      6014      PUSH1 14  # [14, 1]
12      57        JUMPI
13      FD        REVERT
14      5B        JUMPDEST  # 跳转到这里,结束!
15      00        STOP
? Enter the value to send: 
? Enter the calldata: 
#############
# Puzzle 10 #
#############
00      38          CODESIZE    # [23]
01      34          CALLVALUE   # [msgvalue, 23]
02      90          SWAP1   # [23, msgvalue]
03      11          GT  # [1], msgvalue < 23
04      6008        PUSH1 08    # [08, 1]
06      57          JUMPI
07      FD          REVERT
08      5B          JUMPDEST    # 跳到这里
09      36          CALLDATASIZE    # [datasize]
0A      610003      PUSH2 0003  # [0003, datasize]
0D      90          SWAP1   # [datasize, 0003]
0E      06          MOD     # [N],取余数,N为余数,必须是:0
0F      15          ISZERO  # [0或1],必须是1
10      34          CALLVALUE   # [msgvalue, 0或1],必须是:1
11      600A        PUSH1 0A    # [0A,msgvalue, 0或1],反推:msgvalue = 0x0f
13      01          ADD # [add_result, 0或1],下面是跳转了,所以栈值为:[0x19, 1]
14      57          JUMPI
15      FD          REVERT
16      FD          REVERT
17      FD          REVERT
18      FD          REVERT
19      5B          JUMPDEST
1A      00          STOP
? Enter the value to send: 
? Enter the calldata: 
接下来会花几节课来讲解EVM相关内容,以及反汇编内容,敬请关注!
加V入群:Adugii,公众号:阿杜在新加坡,一起抱团拥抱web3,下期见!
关于作者:国内第一批区块链布道者;2017年开始专注于区块链教育(btc, eth, fabric),目前base新加坡,专注海外defi,dex,元宇宙等业务方向。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!
