radare2为开源的逆向工程框架,具体代码见:
其包含多个组件,如:
此外,还有额外的插件:radare2-extras。
EVM为图灵完备的,基于stack的虚拟机。与经典的图灵完备VM不同,在EVM中执行的每条指令都需要以gas征税。
EVM中的指令集可分为2部分:
举例,将两个数相加的指令为:
PUSH1 32 | Stack: [ 32 ]
PUSH1 42 | Stack: [ 42, 32 ]
ADD | Stack: [ 74 ]
每一步所要执行的指令由PC register控制。JUMP指令将take the destination addr from the top of the stack,并会修改PC register。EVM中,仅当dst address包含了JUMPDEST指令,JUMP指令才会生效,这是一种控制流保护。
PUSH1 01 | Stack: [ 1 ], PC: 0
JUMPDEST | Stack: [ 1 ], PC: 2
PUSH1 02 | Stack: [ 1, 2 ], PC: 3
ADD | Stack: [ 3 ], PC: 5
PUSH1 0x2 | Stack: [ 3, 2 ], PC: 6
JUMP | Stack: [ 3 ], PC: 7
PUSH1 02 | Stack: [ 3, 2 ], PC: 3
ADD | Stack: [ 5 ], PC: 5
.....
此处,有一个无限循环,在每次迭代中向stack顶部的值加2。
EVM中有一些特有的command,详细可参加以太坊黄皮书 ETHEREUM: A SECURE DECENTRALISED GENERALISED TRANSACTION LEDGER ISTANBUL VERSION 80085f7 – 2021-07-11。
主要有:
安装完radare2之后,需安装evm插件:【在此之前需安装 libjansson-dev和curl4-openssl-dev 】
r2pm update
r2pm install evm
安装evm插件过程中如报一下错误,将相应的lseek修改为seek即可安装成功:
io_evm.c:765:3: error: field designator 'lseek' does not refer to any field in
type 'RIOPlugin' (aka 'struct r_io_plugin_t'); did you mean 'seek'?
.lseek = __lseek,
^~~~~
seek
简单举例为:
$ cat ./example1.sol
pragma solidity ^0.4.0;
contract Example1 {
uint a = 0;
function setA(uint b) {
a = b + 0x42;
}
}
$ solc ./example1.sol --bin-runtime -o ./out/
$ ls ./out/
Example1.bin-runtime
--bin-runtime
flag创建的binary code与合约加载进链上是一致的:
$ cat out/Example1.bin-runtime
608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ee919d50146044575b600080fd5b348015604f57600080fd5b50d38015605b57600080fd5b50d28015606757600080fd5b506084600480360381019080803590602001909291905050506086565b005b60428101600081905550505600a165627a7a7230582091f0675ac5f9ce9b61e7b21b35d15da09c6c12c54b19ef72ecfb72a50d9f59810029
以上为十六进制,可使用rax2转换为二进制并使用r2工具来解析:
$ rax2 -s < ./out/Example1.bin-runtime > ./out/Example1.bin-runtime.bin
$ r2 -a evm out/Example1.bin-runtime.bin
WARNING: No calling convention defined for this file, analysis may be inaccurate.
-- What about taking a break? Here, have this nice 0xCC.
[0x00000000]> aa
[Warning: set your favourite calling convention in `e anal.cc=?`
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x00000000]> pd 30
┌ 145: fcn.00000000 ();
│ 0x00000000 6080 push1 0x80
│ 0x00000002 6040 push1 0x40
│ 0x00000004 52 mstore
│ 0x00000005 6004 push1 0x04
│ 0x00000007 36 calldatasize
│ 0x00000008 10 lt
│ 0x00000009 603f push1 0x3f
│ ┌─< 0x0000000b 57 jumpi
│ │ 0x0000000c 6000 push1 0x00
│ │ 0x0000000e 35 calldataload
│ │ 0x0000000f 7c0100000000. push29 0x0100000000000000000000
│ │ 0x0000002d 90 swap1
│ │ 0x0000002e 04 div
│ │ 0x0000002f 63ffffffff push4 0xffffffff
│ │ 0x00000034 16 and
│ │ 0x00000035 80 dup1
│ │ 0x00000036 63ee919d50 push4 0xee919d50
│ │ 0x0000003b 14 eq
│ │ 0x0000003c 6044 push1 0x44
│ ┌──< 0x0000003e 57 jumpi
│ │└─> 0x0000003f 5b jumpdest
│ │ 0x00000040 6000 push1 0x00
│ │ 0x00000042 80 dup1
│ │ 0x00000043 fd revert
│ └──> 0x00000044 5b jumpdest
[1] Reversing EVM bytecode with radare2
[2] Radare2 book
[3] Radare2 and EVM Bytecode