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