拾い物のコンパス

まともに書いたメモ

radare2によるバイナリ編集

radare2はデバッガであり,バイナリエディタでもある.バイナリエディタとして使えると何かと便利だと思い,調べてみた.
基本的なことを書き残す.

環境

$ uname -a
Linux poppycompass 4.11.3-1-ARCH #1 SMP PREEMPT Sun May 28 10:40:17 CEST 2017 x86_64 GNU/Linux

$ r2 -v
radare2 1.6.0-git 15021 @ linux-x86-64 git.1.4.0-472-g7512396ab commit: 7512396ab5ae5f7292f41a80dc0b4dca09c4d6f2 build: 2017-06-11__02:50:45

準備

radare2がインストールされていればOK.

# Ubuntu
$ sudo apt install radare2

# Arch Linux
$ sudo pacman -S radare2

# 最新好きの人
$ git clone https://github.com/radare/radare2 && cd radare2 && sys/install.sh
$ r2 -v

でそれっぽいのが出れば良い. 次に,適当なバイナリファイルを用意する.

$ echo -en "\xaa\xbb\xcc\xdd" > test.bin

基本

write modeでファイルを開いて編集していく

$ r2 -w ./test.bin
 -- It's working! Look at the door!
シェルを既に起動した場合は
[0x00000000]> o+ <file>

[0x00000000]> px 4 # ファイルの内容を確認
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  aabb ccdd                                ....


[0x00000000]> w? # ヘルプ
|Usage: w[x] [str] [<file] [<<EOF] [@addr]
| w[1248][+-][n]       increment/decrement byte,word..
| w foobar             write string 'foobar'
| w0 [len]             write 'len' bytes with value 0x00
| w6[de] base64/hex    write base64 [d]ecoded or [e]ncoded string
| wa[?] push ebp       write opcode, separated by ';' (use '"' around the command)
| waf file             assemble file and write bytes
| wao[?] op            modify opcode (change conditional of jump. nop, etc)
| wA[?] r 0            alter/modify opcode at current seek (see wA?)
| wb 010203            fill current block with cyclic hexpairs
| wB[-]0xVALUE         set or unset bits with given value
| wc                   list all write changes
| wc[?][ir*?]          write cache undo/commit/reset/list (io.cache)
| wd [off] [n]         duplicate N bytes from offset at current seek (memcpy) (see y?)
| we[?] [nNsxX] [arg]  extend write operations (insert instead of replace)
| wf -|file            write contents of file at current offset
| wh r2                whereis/which shell command
| wm f0ff              set binary mask hexpair to be used as cyclic write mask
| wo[?] hex            write in block with operation. 'wo?' fmi
| wp[?] -|file         apply radare patch file. See wp? fmi
| wr 10                write 10 random bytes
| ws pstring           write 1 byte for length and then the string
| wt[f][?] file [sz]   write to file (from current seek, blocksize or sz bytes)
| wts host:port [sz]   send data to remote host:port via tcp://
| ww foobar            write wide string 'f\x00o\x00o\x00b\x00a\x00r\x00'
| wx[?][fs] 9090       write two intel nops (from wxfile or wxseek)
| wv[?] eip+34         write 32-64 bit value
| wz string            write zero terminated string (like w + \x00)

以降,編集していく.

16進数の書き込み

[0x00000000]> wx eeff # 最初の2バイトを"eeff"に書き換え
[0x00000000]> px 4
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  eeff ccdd                                ....

[0x00000000]> wx 1122 @ 0x2 # オフセットを指定し,0x2(3バイト目)から0x1122を書き込み
[0x00000000]> px 4
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  eeff 1122                                ...

書き込みはオフセットを指定しない限り,現在のアドレスからの書き込みになる.
アドレスを変更したいときは> s <addr>で移動できる.

文字列の書き込み

[0x00000000]> w poppycompass # "poppycompass"という文字列を書き込み
[0x00000000]> px 20
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  706f 7070 7963 6f6d 7061 7373 ffff ffff  poppycompass....
0x00000010  ffff ffff                                ....

どうやら,ファイルサイズより大きく出力するとffが入るようだ.

Visual modeで常に結果を見ながら編集する

実際に編集するときは,変更を加えてから毎回px 20みたいなコマンドを打つのは手間だ.この問題は簡単に解決できる,そうVisual modeならね!

[0x00000000]> V # Visual modeに移行
[0x00000000 0% 504 ./bin]> xc
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF  comment
0x00000000  aabb ccdd ffff ffff ffff ffff ffff ffff  ................

この状態で:を押して,通常通りコマンドを入力するだけ.

Press <enter> to return to Visual mode.
:px 11223344<enter><enter>
[0x00000000 0% 392 ./bin]> xc
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF  comment
0x00000000  1122 3344 ffff ffff ffff ffff ffff ffff  ."3D............

トルエンディアンで書き込みたい!

> wv 0xaabbccdd         # \xdd\xcc\xbb\xaaとして格納
> wv 0xaabbccddeeff0102 # \x02\x01\xff...として格納

1-4bytesの数値は32ビットとして,5-8bytesの数値は64ビットの値として取り扱われ,少ないバイトには00が書き込まれる(0xaabbだと\xbb\xaa\x00\x00となる).
9bytes以上の値を書き込もうとすると,失敗して全桁がffになる.

アセンブルして書き込みたい

以下のファイルを用意する.
$ echo "push eax\npop eax" > asm.txt
$ r2 -w ./test.bin
[0x00000000]> waf ./asm.txt
もしくは
[0x00000000]> "wa pop eax;push eax"

注意点は,> wa "pop eax;push eax"じゃないこと.

Visualmodeで直接編集

Vimっぽく編集する方法がある.Stirlingなどに慣れた人はこっちの方が親しみやすいかも.

[0x00000000]> V

cを押すと,16進ダンプが囲まれるはず.ここからはvim
移動はhklj,編集場所にカーソルが来たらiでインサートモード.
数値をいれたら上書きされる.終わったらEscを押す.

おまけ

Visual modeの編集機能を使うと,通常のエディタっぽく使えなくもない. 以下はCでHello, Worldプログラムを書いた例.使いみちは無さそうだ.

$ r2 -- # ファイルを指定しないでシェルを立ち上げる
> o+ hello.c # 書き込みモードで hello.c を開く
> V # ヴィジュアルモードに移行して,書き込みを逐次見られるようにする

cを押すと,16進ダンプが囲まれる.ここでタブを押すと,ASCIIの方が囲まれる.この状態でiを押して,書くだけ. 改行(\x0a)とダブルクウォート(\x22)は16進からしか書けないのが難点.

[0x00000000 + 30> * INSERT MODE *
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F |0123456789ABCDEF| comment
0x00000000  2369 6e63 6c75 6465 203c 7374 6469 6f2e |#include <stdio.|
0x00000010  683e 0a69 6e74 206d 6169 6e28 766f 6964 |h>.int main(void|
0x00000020  2920 7b20 7072 696e 7466 2822 4865 6c6c |) { printf("Hell|
0x00000030  6f2c 2057 6f72 6c64 2122 293b 207d ffff |o, World!"); }..|
$ cat hello.c
#include <stdio.h>
int main(void) { printf("Hello, World!"); }