More EVM Puzzles Writeup
Hello Friend,
This is writeup containing notes while i am solving more evm puzzles. i am not interested in explaining every opcode, where you can find them in EVM Code in detail.
Intial Setup
git clone https://github.com/daltyboy11/more-evm-puzzles.git
npm install
npx hardhat play
Puzzle 1
00 36 CALLDATASIZE
01 34 CALLVALUE
02 0A EXP
03 56 JUMP
04 FE INVALID
05 FE INVALID
06 FE INVALID
07 FE INVALID
08 FE INVALID
09 FE INVALID
0A FE INVALID
0B FE INVALID
0C FE INVALID
0D FE INVALID
0E FE INVALID
0F FE INVALID
10 FE INVALID
11 FE INVALID
12 FE INVALID
13 FE INVALID
14 FE INVALID
15 FE INVALID
16 FE INVALID
17 FE INVALID
18 FE INVALID
19 FE INVALID
1A FE INVALID
1B FE INVALID
1C FE INVALID
1D FE INVALID
1E FE INVALID
1F FE INVALID
20 FE INVALID
21 FE INVALID
22 FE INVALID
23 FE INVALID
24 FE INVALID
25 FE INVALID
26 FE INVALID
27 FE INVALID
28 FE INVALID
29 FE INVALID
2A FE INVALID
2B FE INVALID
2C FE INVALID
2D FE INVALID
2E FE INVALID
2F FE INVALID
30 FE INVALID
31 FE INVALID
32 FE INVALID
33 FE INVALID
34 FE INVALID
35 FE INVALID
36 FE INVALID
37 FE INVALID
38 FE INVALID
39 FE INVALID
3A FE INVALID
3B FE INVALID
3C FE INVALID
3D FE INVALID
3E FE INVALID
3F FE INVALID
40 5B JUMPDEST
41 58 PC
42 36 CALLDATASIZE
43 01 ADD
44 56 JUMP
45 FE INVALID
46 FE INVALID
47 5B JUMPDEST
48 00 STOP
JUMPDEST is located at 0x47
(71 in decimal).
From 00
to 03
, we understand that CALLVALUE
** CALLDATASIZE
must to equal to 71
.
To achieve that, let us take CALLDATASIZE
equal to 1
CALLVALUE ** 1 = 71
CALLVALUE = 71
Solution
Enter the value to send: 71
Enter the calldata: 0x00
Puzzle solved!
Puzzle 2
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 80 DUP1
12 94 SWAP5
13 5A GAS
14 F1 CALL
15 3D RETURNDATASIZE
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
From 00
to 05
, the bytecode is just copying the calldata into memory.
From 06
to 0B
, it creates a contract with the calldata.
From 0C
to 15
, it calling the contract that was created with the given calldata.
Inorder to solve this puzzle, we need to create a contract of size 0A
.
Let us create it,
runtime code
push1 0x0a // return data size
push1 0x00 // offset
return
contract creation code
push5 0x600a6000f3 // push runtime code opcodes
push1 0x00
mstore // store runtime code at 0x00
push1 0x0a // length of runtime code
push1 0x1b // offset
return
Solution
Enter the calldata: 0x64600a6000f3600052600a601bf3
Puzzle solved!
Puzzle 3
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
3660006000373660006000F06000808080935AF460055460aa14601e57fe5b00 // bytecode
From 00
to 0B
, it creating a contract with the calldata we have given.
From 0C
to 013
, the contract delegate calling the contract that is deployed with the given calldata.
From 14
to 19
, we observe it checking the storage slot having AA
(170 in decimal).
To solve this challange we need to create a contract that store AA
at storage slot 5.
let us create the run time code.
push1 0xAA // value to be stored
push1 0x05 // key where the value to store
sstore // store the value in the key slot
stop
// hault the execution
contract creation code
PUSH6 0x60aa60055500
PUSH1 0x00
MSTORE
PUSH1 0x06
PUSH1 0x1a
RETURN
Solution
Enter the calldata: 0x6560aa600555006000526006601af3
Puzzle solved!
Puzzle 4
00 30 ADDRESS
01 31 BALANCE
02 36 CALLDATASIZE
03 6000 PUSH1 00
05 6000 PUSH1 00
07 37 CALLDATACOPY
08 36 CALLDATASIZE
09 6000 PUSH1 00
0B 30 ADDRESS
0C 31 BALANCE
0D F0 CREATE
0E 31 BALANCE
0F 90 SWAP1
10 04 DIV
11 6002 PUSH1 02
13 14 EQ
14 6018 PUSH1 18
16 57 JUMPI
17 FD REVERT
18 5B JUMPDEST
19 00 STOP
From 00
to 01
, byte code push the callvalue onto the stack.
From 02
to 0D
the bytecode creating the contract with callvalue.
From 0E
to 11
, it is checking the balance of create contract, the balance must be 1/2 the of the callvalue.
Let us create the contract
push1 0x00
dup1
dup1
dup1
push1 0x01 // as i am sending callvalue as 2, 1/2 of 2 is 1
dup2
gas
call
stop
Solution
Enter the value to send: 2
Enter the calldata: 0x60008080806001815af100
Puzzle 5
00 6020 PUSH1 20
02 36 CALLDATASIZE
03 11 GT
04 6008 PUSH1 08
06 57 JUMPI
07 FD REVERT
08 5B JUMPDEST
09 36 CALLDATASIZE
0A 6000 PUSH1 00
0C 6000 PUSH1 00
0E 37 CALLDATACOPY
0F 36 CALLDATASIZE
10 59 MSIZE
11 03 SUB
12 6003 PUSH1 03
14 14 EQ
15 6019 PUSH1 19
17 57 JUMPI
18 FD REVERT
19 5B JUMPDEST
1A 00 STOP
calldatasize > 32 bytes
msize which is multiple of 32 bytes
msize - calldatasize = 3
64 - calldatasize = 3
calldatasize = 64 - 3
calldatasize = 61
'0x'+('00'*61) // python code to generate calldata
Solution
Enter the calldata: 0x00000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000
Puzzle solved!
Puzzle 6
00 7F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0 PUSH32 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
21 34 CALLVALUE
22 01 ADD
23 6001 PUSH1 01
25 14 EQ
26 602A PUSH1 2A
28 57 JUMPI
29 FD REVERT
2A 5B JUMPDEST
2B 00 STOP
In this puzzle, a number of 32 bytes is push onto stack.
we need to find a callvalue to add with that 32 bytes ((2**256)-16) where that result should be equal to 1.
by adding 0xf(16) to 32 bytes on the stack can overflow and result will become 0.
to get the result 1 we need to provide callvalue of 17.
Solution
Enter the value to send: 17
Puzzle solved!
Puzzle 7
00 5A GAS // 2 | 2 gas
01 34 CALLVALUE // 2 | 2 gas
02 5B JUMPDEST // 1 |----
03 6001 PUSH1 01 // 3 | |
05 90 SWAP1 // 3 | |
06 03 SUB // 3 | |
07 80 DUP1 // 3 | | loop 43 gas
08 6000 PUSH1 00 // 3 | |
0A 14 EQ // 3 | |
0B 6011 PUSH1 11 // 3 | |
0D 57 JUMPI // 10 |--------
0E 6002 PUSH1 02 // 3 | | |
10 56 JUMP // 8 ---- |
11 5B JUMPDEST // 1 -------- 1 gas
12 5A GAS // 2
13 90 SWAP1 // 3
14 91 SWAP2 // 3
15 03 SUB // 3
16 60A6 PUSH1 A6 // 3
18 14 EQ // 3
19 601D PUSH1 1D // 3
1B 57 JUMPI // 8
1C FD REVERT // 0
1D 5B JUMPDEST // 1
1E 00 STOP // 0
Out of loop 5 gas.
Inside loop 43 gas.
Final loop 43 - 11 = 32 gas, skipping PUSH, JUMP.
5 + 32 + (43 * (CALLVALUE - 1)) = 166
(CALLVALUE - 1) * 43 = 129
CALLVALUE - 1 = 3
CALLVALUE = 4
Solution
Enter the value to send: 4
Puzzle solved!
Puzzle 8
00 34 CALLVALUE
01 15 ISZERO
02 19 NOT
03 6007 PUSH1 07
05 57 JUMPI
06 FD REVERT
07 5B JUMPDEST
08 36 CALLDATASIZE
09 6000 PUSH1 00
0B 6000 PUSH1 00
0D 37 CALLDATACOPY
0E 36 CALLDATASIZE
0F 6000 PUSH1 00
11 6000 PUSH1 00
13 F0 CREATE
14 47 SELFBALANCE
15 6000 PUSH1 00
17 6000 PUSH1 00
19 6000 PUSH1 00
1B 6000 PUSH1 00
1D 47 SELFBALANCE
1E 86 DUP7
1F 5A GAS
20 F1 CALL
21 6001 PUSH1 01
23 14 EQ
24 6028 PUSH1 28
26 57 JUMPI
27 FD REVERT
28 5B JUMPDEST
29 47 SELFBALANCE
2A 14 EQ
2B 602F PUSH1 2F
2D 57 JUMPI
2E FD REVERT
2F 5B JUMPDEST
30 00 STOP
looks like complicate but not. let us break it.
From 00
to 05
, we observe to move further the callvalue must be 0.
From 08
to 13
, the bytecode copy the calldata into memory and create a contract with it.
From 14
to 20
, calling the contract that was deployed with calldata.
From 29
to 2D
checking balance before and after are same or not.
Inorder to solve this we challenge, just creation code is enough without need of runtime code.
let us create a contract that returns nothing.
push1 00
push1 00
return
Solution
Enter the calldata: 0x60006000f3
Puzzle solved!
Puzzle 9
00 34 CALLVALUE
01 6000 PUSH1 00
03 52 MSTORE
04 6020 PUSH1 20
06 6000 PUSH1 00
08 20 SHA3
09 60F8 PUSH1 F8
0B 1C SHR
0C 60A8 PUSH1 A8
0E 14 EQ
0F 6016 PUSH1 16
11 57 JUMPI
12 FD REVERT
13 FD REVERT
14 FD REVERT
15 FD REVERT
16 5B JUMPDEST
17 00 STOP
From 00
to 08
, computing the keccak256 hash of the callvalue.
From 09
to 0B
, right shifting 0xF8
(31 bytes) the keccak256 hash.
we left with the left most 1 bytes and that must be A8
.
a bruteforce approach.
keccak256(abi.encode(47))
Solution
Enter the value to send: 47
Puzzle solved!
Puzzle 10
00 6020 PUSH1 20
02 6000 PUSH1 00
04 6000 PUSH1 00
06 37 CALLDATACOPY
07 6000 PUSH1 00
09 51 MLOAD
0A 7F F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0 PUSH32 F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0
2B 16 AND
2C 6020 PUSH1 20
2E 6020 PUSH1 20
30 6000 PUSH1 00
32 37 CALLDATACOPY
33 6000 PUSH1 00
35 51 MLOAD
36 17 OR
37 7F ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB PUSH32 ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
58 14 EQ
59 605D PUSH1 5D
5B 57 JUMPI
5C FD REVERT
5D 5B JUMPDEST
5E 00 STOP
From 00
to 2B
, the bytecode loads the first 32 bytes of the calldata, done AND
operation with F0F0...
From 2c
to 36
, the bytecode just loads second 32 bytes from the calldata and done OR
operation with previous result.
To solve this puzzle, we need to provide 64 bytes of calldata, such that after two operation the result must be in ABAB..
.
FO
AND ?? // probably (A,anything less than B)'s
-----------
?? - A0
OR ?? - 0B
-----------
AB
"0x" + "A4"*32 + "0B"*32 // python code to create calldata
Solution
Enter the calldata: 0xA4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A40B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B
Puzzle solved!
Do you want to play the next puzzle? Yes
All puzzles are solved!
Thanks you.