Reversing and debugging EVM Smart contracts: First steps in assembly (part 1)

▶ 0. Introduction

illegible smart contract

▶ 1. Introduction

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Test {

function test() external { }

function test2() external { }

function test3() external { }
}
Compiling our Smart contract

▶ 2. What is byte-code/assembly?

0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80630a8e8e0114604157806366e41cb7146049578063f8a8fd6d146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea2646970667358221220d28f98515dc0855e1c6f5aa3747ff775f1b8ab6545f14c70641ff9af67c2465164736f6c63430008070033
000 PUSH 40041 PUSH1 00

056 DUP1

▶ 3. Memory in Solidity

▶ 4. How the LIFO Stack Works ?

Stack before (3 elems): |Place 0: 0x50|Place 1: 0x17|Place 2: 0x05|
----------------------------
Stack after PUSH ff: |Place 0: 0xff|Place 1: 0x50|Place 2: 0x17|Place 3: 0x05|
Stack before (0 elems, empty): ||
----------------------------
Stack after PUSH 33: |Place 0: 0x33|
Stack before (0 elems, empty): |Place 0: 0x33|
----------------------------
Stack after PUSH 00: |Place 0: 0x00|Place 1: 0x33|
Stack before (3 elems): |Place 0: 0x50|Place 1: 0x17|Place 2: 0x05|
----------------------------
Stack after POP (2 elems): |Place 0: 0x17|Place 1: 0x05|
Stack before (1 elems): |Place 0: 0x33|
----------------------------
Stack before POP (0 elems, empty): ||
How the stack (LIFO) works

▶ 5. First Lines of the Assembly

000 PUSH 80 | 0x80 |
002 PUSH 40 | 0x40 | 0x80 |
004 MSTORE ||

▶ 6. MSG.VALUE

005 CALLVALUE |msg.value|
006 DUP1 |msg.value|msg.value|
007 ISZERO |0x01|msg.value|
008 PUSH1 0f |0x0f|0x01|msg.value|
010 JUMPI |msg.value|
011 PUSH1 00 |0x00|msg.value| (if jumpi don't jump to 0f)
013 DUP1 |0x00|0x00|msg.value|
014 REVERT
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
}

▶ 7. CALLDATASIZE

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 ||
Raw transaction in Ethereum
015 JUMPDEST     
016 POP pop
017 PUSH1 04 store 0x04 in the stack
019 CALLDATASIZE get msg.data.size in the stack
020 LT verify if msg.data.size < 0x04
021 PUSH1 3c push 0x3c (60 in dec)
023 JUMPI jump to 60 if msg.data.size < 0x04
060 JUMPDEST
061 PUSH1 00
063 DUP1
064 REVERT revert the execution
if (msg.data.size < 4) { revert(); }

▶ 8. Function Selector

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|
036 EQ |0x0|0xf8a8fd6d|0xf8a8fd6d|
037 PUSH1 41 |0x41|0x1|0xf8a8fd6d|
039 JUMPI |0xf8a8fd6d|
040 DUP1 |0xf8a8fd6d|0xf8a8fd6d|
041 PUSH4 66e41cb7 |0x66e41cb7|0xf8a8fd6d|0xf8a8fd6d|
046 EQ |0x0|0xf8a8fd6d|
047 PUSH1 49 |0x49|0x1|0xf8a8fd6d|
049 JUMPI |0xf8a8fd6d|
050 DUP1 |0xf8a8fd6d|0xf8a8fd6d|
051 PUSH4 f8a8fd6d |0xf8a8fd6d|0xf8a8fd6d|0xf8a8fd6d|
056 EQ |0x1|0xf8a8fd6d|
057 PUSH1 51 |0x51|0x1|0xf8a8fd6d|
059 JUMPI |0xf8a8fd6d|
bytes4(keccak256(”test()”)) = 0xf8a8fd6d
A place in stack is of length 32 bytes = 256 bitsIn binary Stack(1) = 11111000101010001111110101101101 and 192 zeros after thatc0 = 192 in decimal, so we will shift 192 time to the right0 times   : 11111000101010001111110101101101..... + 192 zeros
1 times : 011111000101010001111110101101101.... + 191 zeros
2 times : 0011111000101010001111110101101101... + 190 zeros
192 times : 192 zeros + 0011111000101010001111110101101101...
= 0x00000000000000000000000000000000000000000000000000000f8a8fd6d
= 0x00..60zeros00f8a8fd6d
081 JUMPDEST |0xf8a8fd6d|
082 PUSH1 57 |0x57|0xf8a8fd6d|
084 PUSH1 5d |0x5d|0x57|0xf8a8fd6d|
086 JUMP |0x57|0xf8a8fd6d|
087 JUMPDEST |0xf8a8fd6d|
088 STOP ||
093 JUMPDEST |0x57|0xf8a8fd6d|
094 JUMP |0xf8a8fd6d|
060 JUMPDEST
061 PUSH1 00
063 DUP1
064 REVERT
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()

▶ 9. Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
TrustChain

Smart contract Auditor & Cybersecurity engineer, follow me on Twitter to get more value: https://twitter.com/TrustChain_DEFI