Hello Friend,

From past few days i am learning how evm works under the hood.

In this writeup, I’ll share the solutions of evm puzzles which i have solved while understanding EVM Opcodes

For a visual representation and interactive experience, be sure to checkout Playground.

Introduction

Each puzzle consists on sending a successful transaction to a contract.

The bytecode of the contract is provided, and we need to fill the transaction data that won’t revert the execution.

In some puzzles we only need to provide the value that will be sent to the contract, in others the calldata, and in others both values.

Initial Setup

git clone https://github.com/fvictorio/evm-puzzles.git
cd evm-puzzles/
npm install
npx hardhat play

Puzzle 1

00      34      CALLVALUE	// push call value in wei
01      56      JUMP		// jump to position on stack 
02      FD      REVERT		// revert execution
03      FD      REVERT		// revert execution
04      FD      REVERT		// revert execution
05      FD      REVERT		// revert execution
06      FD      REVERT		// revert execution
07      FD      REVERT		// revert execution
08      5B      JUMPDEST	// mark a valid jump destination
09      00      STOP		// halt execution
3456FDFDFDFDFDFD5B00 // bytecode

CALLVALUE pushes callvalue in wei onto the stack.

JUMPDEST is marked at 0x08 to solve the challenge we need to provide a CALLVALUE equal to 8.

Solution

Enter the value to send: 8

Puzzle solved!


Puzzle 2

00      34      CALLVALUE	// push call value in wei
01      38      CODESIZE	// push code size
02      03      SUB			// subtract top two values on stack
03      56      JUMP		// jump to position on stack
04      FD      REVERT		// revert execution
05      FD      REVERT		// revert execution
06      5B      JUMPDEST	// mark a valid jump destination
07      00      STOP		// halt execution
08      FD      REVERT		// revert execution
09      FD      REVERT		// revert execution
34380356FDFD5B00FDFD // bytecode

To successfully jump to JUMPDEST at position 0x06, the top of the stack must equal 6.

CODESIZE is 10 (since the bytecode is 20 hex characters, which is 10 bytes).

len("34380356FDFD5B00FDFD")/2	#10

We need the result of CODESIZE - CALLVALUE to be 6.

10 - CALLVALUE = 6
CALLVALUE = 10 - 6
CALLVALUE = 4

Solution

Enter the value to send: 4

Puzzle solved!


Puzzle 3

00      36      CALLDATASIZE	// push size of calldata onto the stack
01      56      JUMP			// jump to position on stack 
02      FD      REVERT			// revert execution
03      FD      REVERT			// revert execution
04      5B      JUMPDEST		// mark a valid jump destination
05      00      STOP			// halt execution
3656FDFD5B00 // bytecode

To solve this, we need to send 4 bytes of calldata.

This will make the bytecode execution jump to the JUMPDEST at position 0x04 and then stop the execution, thereby solving the challenge.

An example of such calldata is 0x01010101.

Solution

Enter the calldata: 0x01010101

Puzzle solved!


Puzzle 4

00      34      CALLVALUE	// push call value in wei
01      38      CODESIZE	// push code size
02      18      XOR			// bitwise XOR of top two values on stack
03      56      JUMP		// jump to position on stack 
04      FD      REVERT		// revert execution
05      FD      REVERT		// revert execution
06      FD      REVERT		// revert execution
07      FD      REVERT		// revert execution
08      FD      REVERT		// revert execution
09      FD      REVERT		// revert execution
0A      5B      JUMPDEST	// mark a valid jump destination
0B      00      STOP		// hault execution
34381856FDFDFDFDFDFD5B00 // bytecode

CODESIZE is 12 (since the bytecode is 24 hex characters, which is 12 bytes).

To solve the challenge, the result of CODESIZE XOR CALLVALUE must be 0x0A.

	1100 (12)
XOR	1010 (0x0A)
----------------
	0110 (6)

Solution:

Enter the value to send: 6

Puzzle solved!


Puzzle 5

00      34          CALLVALUE	// push call value in wei
01      80          DUP1		// duplicate the top stack item
02      02          MUL			// multiply the top two values on the stack
03      610100      PUSH2 0100	// push 0x0100 to the stack
06      14          EQ			// check if the top two values are equal
07      600C        PUSH1 0C	// push 0x0C to the stack
09      57          JUMPI		// conditional jump to the location on top of the stack if the second item on the stack is non-zero
0A      FD          REVERT		// revert execution
0B      FD          REVERT		// revert execution
0C      5B          JUMPDEST	// mark a valid jump destinatin
0D      00          STOP		// hault execution
0E      FD          REVERT		// revert execution
0F      FD          REVERT		// revert execution
34800261010014600C57FDFD5B00FDFD // bytecode

To solve the challenge, the result of CALLVALUE * CALLVALUE must equal 0x0100 (256).

To find CALLVALUE, solve the equation CALLVALUE * CALLVALUE = 256.

CALLVALUE = sqrt(256) = 16

Solution:

Enter the value to send: 16

Puzzle solved!


Puzzle 6

00      6000      PUSH1 00		// push 0x00 onto the stack 
02      35        CALLDATALOAD	// load 32 bytes of calldata starting at position specified by top of the stack
03      56        JUMP			// jump to position on stack
04      FD        REVERT		// revert execution
05      FD        REVERT		// revert execution
06      FD        REVERT		// revert execution
07      FD        REVERT		// revert execution
08      FD        REVERT		// revert execution
09      FD        REVERT		// revert execution
0A      5B        JUMPDEST		// mark a valid jump destination
0B      00        STOP			// halt execution
60003556FDFDFDFDFDFD5B00 // bytecode

To solve the challenge, the value at the start of the calldata must be 0x0A (decimal 10).

As the CALLDATALOAD load the 32 bytes of calldata we need the pad left with 31 bytes of zeros.

Solution

Enter the calldata: 0x000000000000000000000000000000000000000000000000000000000000000a

Puzzle solved!


Puzzle 7

00      36        CALLDATASIZE	// pushes size of calldata onto the stack
01      6000      PUSH1 00		// push 0x00 onto the stack
03      80        DUP1			// duplicate the top stack item
04      37        CALLDATACOPY	// copy calldata to memory
05      36        CALLDATASIZE	// pushes size of calldata onto the stack
06      6000      PUSH1 00		// push 0x00 onto the stack
08      6000      PUSH1 00		// push 0x00 onto the stack
0A      F0        CREATE		// create a new contract
0B      3B        EXTCODESIZE	// check size of the code at the address
0C      6001      PUSH1 01		// push 0x01 onto the stack
0E      14        EQ			// check if the top two values are equal
0F      6013      PUSH1 13		// push 0x13 onto the stack
11      57        JUMPI			// conditional jump to the location on top of the stack if the second item on the stack is non-zero
12      FD        REVERT		// revert execution
13      5B        JUMPDEST		// mark a valid jump destination
14      00        STOP			// hault execution
36600080373660006000F03B600114601357FD5B00 // bytecode

To solve the challenge, the we needs to create a valid contract whose size is 1 byte.

The runtime code is the code that is returned from the contract creation code.

Return data is taken from memory, so push one byte into memory

push1 01 - push 0x01 into stack
push1 00 - push 0x00 into stack
mstore8   - store 0x01 at offset of 0x00 into the memory
push1 01 - // return size
push1 00 - // from where in the memory returning data start
return

Solution

Enter the calldata: 0x600160005360016000f3

Puzzle solved!


Puzzle 8

00      36        CALLDATASIZE	// pushes size of calldata onto the stack
01      6000      PUSH1 00		// push 0x00 onto the stack
03      80        DUP1			// duplicate the top stack item
04      37        CALLDATACOPY	// copy calldata to memory
05      36        CALLDATASIZE	// pushes size of calldata onto the stack
06      6000      PUSH1 00		// push 0x00 onto the stack
08      6000      PUSH1 00		// push 0x00 onto the stack
0A      F0        CREATE		// create a new contract
0B      6000      PUSH1 00		// push 0x00 onto the stack
0D      80        DUP1			// duplicate the top stack item
0E      80        DUP1			// duplicate the top stack item
0F      80        DUP1			// duplicate the top stack item
10      80        DUP1			// duplicate the top stack item
11      94        SWAP5			// swap the top stack item with the sixth item
12      5A        GAS			// push remaining gas onto the stack 
13      F1        CALL			// call another contract
14      6000      PUSH1 00		// push 0x00 onto the stack
16      14        EQ			// check if the top two values are equal
17      601B      PUSH1 1B		// push 0x1B onto the stack
19      57        JUMPI			// conditional jump to the location on top of the stack if the second item on the stack is non-zero
1A      FD        REVERT		// revert execution
1B      5B        JUMPDEST		// mark a valid jump destination
1C      00        STOP			// halt execution
36600080373660006000F0600080808080945AF1600014601B57FD5B00 // bytecode

From 00 to 0A the bytecode load the calldata into memory and create a contract with that calldata.

From 0B to 13 it call the contract that was created with the calldata.

To solve the return data of the called contract must be equal to zero.

The contract return zero if any revert happen but wait it also revert the whole execution.

For that we need to just return the REVERT(FD) opcode.

push1 0xfd  // push revert opcode into stack
push1 0x00	
mstore8		// store 1byte data into memory
push1 0x01	
push1 0x00
return	// returning 1 byte from memory

Solution

Enter the calldata: 0x60fd60005360016000f3


Puzzle - 9

00      36        CALLDATASIZE	// pushes size of calldata onto the stack
01      6003      PUSH1 03		// push `0x03` onto the stack
03      10        LT			// less than comparison
04      6009      PUSH1 09		// push `0x09` onto the stack 
06      57        JUMPI			// conditional jump
07      FD        REVERT		// reverts execution
08      FD        REVERT		// reverts execution
09      5B        JUMPDEST		// marks a valid jump destination
0A      34        CALLVALUE		// pushes call value onto the stack
0B      36        CALLDATASIZE	// pushes size of calldata onto the stack
0C      02        MUL			// multiplication
0D      6008      PUSH1 08		// push `0x08` onto the stack
0F      14        EQ			// equality comparison
10      6014      PUSH1 14		// push `0x14` onto the stack
12      57        JUMPI			// conditional jump
13      FD        REVERT		// reverts execution
14      5B        JUMPDEST		// marks a valid jump destination
15      00        STOP			// halt execution
36600310600957FDFD5B343602600814601457FD5B00 // bytecode

From 00 to 06 CALLDATASIZE must be greater that 3.

From 0A to 10, we understand the multiplication of CALLDATA and CALLDATASIZE must be 8.

To solve the challenge,

If we take CALLDATASIZE of size 4 bytes the CALLVALUE must be 2.

If we take CALLDATASIZE of size 8 bytes the CALLVALUE must to 1.

Solution

Enter the value to send: 2

Enter the calldata: 0x00000000


Puzzle 10

00      38          CODESIZE		// pushes the size of the code onto the stack
01      34          CALLVALUE		// pushes the call value onto the stack
02      90          SWAP1			// swaps the top two stack elements
03      11          GT				// greater than comparison
04      6008        PUSH1 08		// push `0x08` onto the stack
06      57          JUMPI			// marks a valid jump destination
07      FD          REVERT			// reverts execution
08      5B          JUMPDEST		// marks a valid jump destination
09      36          CALLDATASIZE	// pushes the size of the calldata onto the stack
0A      610003      PUSH2 0003		// push `0x0003` onto the stack
0D      90          SWAP1			// swaps the top two stack elements
0E      06          MOD				// modulus operation
0F      15          ISZERO			// checks if the top stack element is zero
10      34          CALLVALUE		// pushes the call value onto the stack
11      600A        PUSH1 0A		// push `0x0A` onto the stack
13      01          ADD				// adds the top two stack elements
14      57          JUMPI			// conditional jump
15      FD          REVERT			// reverts execution
16      FD          REVERT			// reverts execution
17      FD          REVERT			// reverts execution
18      FD          REVERT			// reverts execution
19      5B          JUMPDEST		// marks a valid jump destination
1A      00          STOP			// hault execution
38349011600857FD5B3661000390061534600A0157FDFDFDFD5B00 // bytecode

From 00 to 06, we understand that CODESIZE must be greater than CALLVALUE.

From 09 to 0F, we understand that CALLDATASIZE modulus 3 must be equal to 0.

To need to satisfy the modulus operation, let us take CALLDATA of size 3 bytes.

From 10 to 14, we understand CALLVALUE + 0x0A need to be equal to 0x19(25 in decimal)

CALLVALUE + 0x0A = 0x19
CALLVALUE = 0x19 - 0x0A
CALLVALUE = 15

Solution

Enter the value to send: 15

Enter the calldata: 0x000000

Puzzle solved!


Do you want to play the next puzzle? Yes

All puzzles are solved!

Thanks you!