Back
Featured image of post EVM bytecode to Graph using Crytic CFG Builder

EVM bytecode to Graph using Crytic CFG Builder

Decompiling EVM bytecode to some visual representational graph easily

Table of Content

For this experiment, I choose to test the Crytic tool called evm_cfg_builder which is opensource and is available at https://github.com/crytic/evm_cfg_builder.

The next step, is to find a contract to analyze and see how it works. What a better way to try it out than using their own example contracts.

Finding a contract to analyze

The source code is extracted from evm-cfg-builder at https://github.com/crytic/evm_cfg_builder/blob/master/examples/token.sol

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
// from https://github.com/ConsenSys/Tokens/blob/fdf687c69d998266a95f15216b1955a4965a0a6d/contracts/eip20/

pragma solidity ^0.4.25;

contract EIP20Interface {
    /* This is a slight change to the ERC20 base standard.
    function totalSupply() constant returns (uint256 supply);
    is replaced with:
    uint256 public totalSupply;
    This automatically creates a getter function for the totalSupply.
    This is moved to the base contract since public getter functions are not
    currently recognised as an implementation of the matching abstract
    function by the compiler.
    */
    /// total amount of tokens
    uint256 public totalSupply;

    /// @param _owner The address from which the balance will be retrieved
    /// @return The balance
    function balanceOf(address _owner) public view returns (uint256 balance);

    /// @notice send `_value` token to `_to` from `msg.sender`
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return Whether the transfer was successful or not
    function transfer(address _to, uint256 _value) public returns (bool success);

    /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
    /// @param _from The address of the sender
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return Whether the transfer was successful or not
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);

    /// @notice `msg.sender` approves `_spender` to spend `_value` tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @param _value The amount of tokens to be approved for transfer
    /// @return Whether the approval was successful or not
    function approve(address _spender, uint256 _value) public returns (bool success);

    /// @param _owner The address of the account owning tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @return Amount of remaining tokens allowed to spent
    function allowance(address _owner, address _spender) public view returns (uint256 remaining);

    // solhint-disable-next-line no-simple-event-func-name
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

contract EIP20 is EIP20Interface {

    uint256 constant private MAX_UINT256 = 2**256 - 1;
    mapping (address => uint256) public balances;
    mapping (address => mapping (address => uint256)) public allowed;
    /*
    NOTE:
    The following variables are OPTIONAL vanities. One does not have to include them.
    They allow one to customise the token contract & in no way influences the core functionality.
    Some wallets/interfaces might not even bother to look at this information.
    */
    string public name;                   //fancy name: eg Simon Bucks
    uint8 public decimals;                //How many decimals to show.
    string public symbol;                 //An identifier: eg SBX

    function EIP20(
        uint256 _initialAmount,
        string _tokenName,
        uint8 _decimalUnits,
        string _tokenSymbol
    ) public {
        balances[msg.sender] = _initialAmount;               // Give the creator all initial tokens
        totalSupply = _initialAmount;                        // Update total supply
        name = _tokenName;                                   // Set the name for display purposes
        decimals = _decimalUnits;                            // Amount of decimals for display purposes
        symbol = _tokenSymbol;                               // Set the symbol for display purposes
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value);
        balances[msg.sender] -= _value;
        balances[_to] += _value;
        emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        uint256 allowance = allowed[_from][msg.sender];
        require(balances[_from] >= _value && allowance >= _value);
        balances[_to] += _value;
        balances[_from] -= _value;
        if (allowance < MAX_UINT256) {
            allowed[_from][msg.sender] -= _value;
        }
        emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
        return true;
    }

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
    }

    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars
        return true;
    }

    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }
}

Getting the compiled code version

The source code is based on previous example and it is based on https://github.com/crytic/evm_cfg_builder/blob/master/examples/token-runtime.evm

1
0x6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a7230582076ce5edca0cb79a34f44093c5ee1fcd5fadde8c358ba33d53c447da4f1d4e7ef0029

Setting up the context

Before running the evm_cfg_builder tool, we have some requirements to met.

1
virtualenv -p /usr/bin/python3.6 venv
1
2
3
4
5
Running virtualenv with interpreter /usr/bin/python3.6
Using base prefix '/usr'
New python executable in /tmp/cfg_test/evm_cfg_builder/examples/venv/bin/python3.6
Also creating executable in /tmp/cfg_test/evm_cfg_builder/examples/venv/bin/python
Installing setuptools, pip, wheel...done.

Activate the virtualenv

1
source venv/bin/activate

Installing EVM CFG Builder

With the virtualenv activated, install the crytic evm-cfg-builder tool

1
pip3 install evm-cfg-builder

A success installation process will download and setup the required dependencies of the toolkit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Defaulting to user installation because normal site-packages is not writeable
Collecting evm-cfg-builder
  Downloading evm-cfg-builder-0.3.1.tar.gz (1.3 MB)
     |████████████████████████████████| 1.3 MB 1.3 MB/s            
  Preparing metadata (setup.py) ... done
Collecting crytic-compile>=0.1.13
  Downloading crytic_compile-0.2.3-py3-none-any.whl (87 kB)
     |████████████████████████████████| 87 kB 3.8 MB/s             
Collecting pyevmasm>=0.1.1
  Downloading pyevmasm-0.2.3-py3-none-any.whl (15 kB)
Collecting pysha3>=1.0.2
  Downloading pysha3-1.0.2-cp36-cp36m-manylinux1_x86_64.whl (127 kB)
     |████████████████████████████████| 127 kB 2.9 MB/s            
Collecting future
  Downloading future-0.18.2.tar.gz (829 kB)
     |████████████████████████████████| 829 kB 3.0 MB/s            
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: evm-cfg-builder, future
  Building wheel for evm-cfg-builder (setup.py) ... done
  Created wheel for evm-cfg-builder: filename=evm_cfg_builder-0.3.1-py3-none-any.whl size=1266352 sha256=728c18abb3c9bc88187114ee252bc2083171fd0c89b354497c27f893e7e6214d
  Stored in directory: /home/r00t/.cache/pip/wheels/79/61/a6/44d291f111ea801eb83cd667d0c049f605d054c8ad8c065aa4
  Building wheel for future (setup.py) ... done
  Created wheel for future: filename=future-0.18.2-py3-none-any.whl size=493275 sha256=fd0110fe805d02f9ce1c0c91312dbaced731711b64f1c24245794d5cc667bd7e
  Stored in directory: /home/r00t/.cache/pip/wheels/6e/9c/ed/4499c9865ac1002697793e0ae05ba6be33553d098f3347fb94
Successfully built evm-cfg-builder future
Installing collected packages: pysha3, future, pyevmasm, crytic-compile, evm-cfg-builder
  WARNING: The scripts futurize and pasteurize are installed in '/home/r00t/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script evmasm is installed in '/home/r00t/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script crytic-compile is installed in '/home/r00t/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script evm-cfg-builder is installed in '/home/r00t/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed crytic-compile-0.2.3 evm-cfg-builder-0.3.1 future-0.18.2 pyevmasm-0.2.3 pysha3-1.0.2

Run the crytic evm-cfg-builder

To launch the evm-cfg-builder tool, a one line command is needed.

1
evm-cfg-builder --export-dot . token-runtime.evm

The crytic evm-cfg-builder output in this analysis is shown below

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
INFO:evm-cfg-builder:Analyze token-runtime.evm
INFO:evm-cfg-builder:_fallback, 1 #bbs , payable,view,pure
INFO:evm-cfg-builder:name(), 10 #bbs , view
INFO:evm-cfg-builder:approve(address,uint256), 4 #bbs 
INFO:evm-cfg-builder:totalSupply(), 4 #bbs , view
INFO:evm-cfg-builder:transferFrom(address,address,uint256), 10 #bbs 
INFO:evm-cfg-builder:balances(address), 4 #bbs , view
INFO:evm-cfg-builder:decimals(), 4 #bbs , view
INFO:evm-cfg-builder:allowed(address,address), 4 #bbs , view
INFO:evm-cfg-builder:balanceOf(address), 4 #bbs , view
INFO:evm-cfg-builder:symbol(), 10 #bbs , view
INFO:evm-cfg-builder:transfer(address,uint256), 6 #bbs 
INFO:evm-cfg-builder:allowance(address,address), 4 #bbs , view
INFO:evm-cfg-builder:_dispatcher, 12 #bbs 
ERROR:evm-cfg-builder:Missing branches name():0x5b8
ERROR:evm-cfg-builder:Missing branches approve(address,uint256):0x6aa
ERROR:evm-cfg-builder:Missing branches totalSupply():0x6b0
ERROR:evm-cfg-builder:Missing branches transferFrom(address,address,uint256):0x94a
ERROR:evm-cfg-builder:Missing branches balances(address):0x962
ERROR:evm-cfg-builder:Missing branches decimals():0x975
ERROR:evm-cfg-builder:Missing branches allowed(address,address):0x99a
ERROR:evm-cfg-builder:Missing branches balanceOf(address):0x9e3
ERROR:evm-cfg-builder:Missing branches symbol():0xa81
ERROR:evm-cfg-builder:Missing branches transfer(address,uint256):0xbda
ERROR:evm-cfg-builder:Missing branches allowance(address,address):0xc61

Output analysis

Since we configure evm-cfg-builder analysis to generate Graphviz .dot file with the option --export-dot, the files are created in the same directory as our source code. We can find the FULL graph and a file representing each function in the code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-rw-rw-r-- 1 r00t r00t 1,5K jul 16 16:34 'token-runtime.evm_allowance(address,address).dot'
-rw-rw-r-- 1 r00t r00t 1,2K jul 16 16:34 'token-runtime.evm_allowed(address,address).dot'
-rw-rw-r-- 1 r00t r00t 2,0K jul 16 16:34 'token-runtime.evm_approve(address,uint256).dot'
-rw-rw-r-- 1 r00t r00t 1,1K jul 16 16:34 'token-runtime.evm_balanceOf(address).dot'
-rw-rw-r-- 1 r00t r00t  847 jul 16 16:34 'token-runtime.evm_balances(address).dot'
-rw-rw-r-- 1 r00t r00t  477 jul 16 16:34 'token-runtime.evm_decimals().dot'
-rw-rw-r-- 1 r00t r00t 1,9K jul 16 16:34  token-runtime.evm__dispatcher.dot
-rw-rw-r-- 1 r00t r00t   75 jul 16 16:34  token-runtime.evm__fallback.dot
-rw-rw-r-- 1 r00t r00t  25K jul 16 16:34  token-runtime.evm_FULL_GRAPH.dot
-rw-rw-r-- 1 r00t r00t 2,0K jul 16 16:34 'token-runtime.evm_name().dot'
-rw-rw-r-- 1 r00t r00t 2,0K jul 16 16:34 'token-runtime.evm_symbol().dot'
-rw-rw-r-- 1 r00t r00t  360 jul 16 16:34 'token-runtime.evm_totalSupply().dot'
-rw-rw-r-- 1 r00t r00t 2,8K jul 16 16:34 'token-runtime.evm_transfer(address,uint256).dot'
-rw-rw-r-- 1 r00t r00t 4,8K jul 16 16:34 'token-runtime.evm_transferFrom(address,address,uint256).dot'

Measure the time

In this example, for a contract with 112 source code lines and 6431 bytes in compiled code, the output analysis time is 0.81s. Quite fast!

1
evm-cfg-builder --export-dot . token-runtime.evm  0,75s user 0,06s system 100% cpu 0,811 total

Sample views of created graphs

Some images of the output analysis. Enjoy.

Graph of the allowance() function

Graph of allowance function
Graph of allowance function

Graph of the balanceof(address) function

Graph of balanceOf function
Graph of balanceOf function

Graph of the decimals() function

Graph of decimals function
Graph of decimals function

Graph of the dispatcher() function

Graph of built-in dispatcher function
Graph of built-in dispatcher function

Graph of the fallback() function

Graph of fallback function
Graph of fallback function

Graph of the totalSupply() function

Graph of totalSupply function
Graph of totalSupply function

Graph of the transferFrom() function

Graph of transferFrom function
Graph of transferFrom function

Conclusion

The Crytic evm-cfg-builder tool is one of the available ways to convert the compiled code into a visual representation. However, it still provides a very basic overview of the smart contract code blocks without applying any of the available optimizations to final graph result. As a consecuence, the final graph contains more edges, nodes and overall complexity is increased.

References



💬 Share this post in social media

Thanks for checking this out and I hope you found the info useful! If you have any questions, don't hesitate to write me a comment below. And remember that if you like to see more content on, just let me know it and share this post with your colleges, co-workers, FFF, etc.

Please, don't try to hack this website servers. Guess why...