当前位置: 首页 > 工具软件 > Radare > 使用案例 >

radare2 和 EVM

苏振国
2023-12-01

1. 引言

radare2为开源的逆向工程框架,具体代码见:

其包含多个组件,如:

  • radare2:为主要工具,其核心为hexadecimal editor and debugger。
  • rabin2:从二进制可执行文件中提取信息。支持的二进制格式有ELF, PE, Java CLASS, Mach-O 以及其他支持r2 plugins的格式。
  • rasm2:为汇编和反汇编工具,支持多种架构,如Intel x86, x86-64, MIPS, ARM, PowerPC, Java等。
  • rahash2:为block-based hash tool,支持大小文件,支持MD4, MD5, CRC16, CRC32, SHA1, SHA256等算法。可用于验证大文件的完整性,或跟踪大文件的修改,memory dumps or disks。
  • radiff2:为二进制比对工具,支持byte-level 或 delta diff。
  • rafind2:用于查找文件中的特定字符。
  • ragg2:将高级语言编写程序编译为tiny binaries for x86, x86-64 and ARM。
  • rarun2:支持在不同环境,以不同参数,不同权限等启动程序运行。
  • rax2:为支持各种进制数字、ASCII、String转换的工具。

此外,还有额外的插件:radare2-extras

2. 将radare2用于EVM反向分析

2.1 EVM: stack-based machine

EVM为图灵完备的,基于stack的虚拟机。与经典的图灵完备VM不同,在EVM中执行的每条指令都需要以gas征税。
EVM中的指令集可分为2部分:

  • 通用的指令(如push, pop, jump等等)。
  • 以太坊特有的指令(如call to external contracts, read the address of the caller等等)。

举例,将两个数相加的指令为:

PUSH1 32      | Stack: [ 32 ]
PUSH1 42      | Stack: [ 42, 32 ]
ADD           | Stack: [ 74 ]

2.2 EVM中的Flow-control

每一步所要执行的指令由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。

2.3 EVM特有的command

EVM中有一些特有的command,详细可参加以太坊黄皮书 ETHEREUM: A SECURE DECENTRALISED GENERALISED TRANSACTION LEDGER ISTANBUL VERSION 80085f7 – 2021-07-11
主要有:

  • 1)Memory:EVM为非冯诺依曼架构,其memory和storage是分离的。不同的指令将向memory读写数据。memory only lives during a single execution of the contract。
  • 2)Storage:storage在合约的整个生命周期都是persistent的。storage中存储的是合约的state。local variables也存储在storage中。

2.4 借助radare2实现EVM disassembler and debuger

安装完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

 类似资料: