以太坊在内部实现了一个基于栈的虚拟机,我们称之为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,元宇宙等业务方向。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!