ELFバイナリのデバッグ時の関数名を読めなくする方法
Defconとかでは問題のELFバイナリをデバッグすると,ユーザ定義関数名だけでなく,ライブラリ関数名も読めなくなっている.どうやるとこんなバイナリを作れるのかを調べてみた.結論から言えば,静的リンクでコンパイルして,stripでデバッグシンボルを消すだけだった.
ライブラリ関数名の読めないプログラム
例えば,/bin/id
をexecve
関数で実行するだけのid.c
を考える.
/* id.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { char *env[] = {NULL, NULL}; char *args[] = {"/bin/id", NULL}; execve("/bin/id", args, env); return 0; } /* E.O.F. */
ここで使われているライブラリ関数はexecve
だけ.これの関数名消去がうまく行くと,以下のようなアセンブラになる.
$ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 4 xref: 54 sect: 550756 code: 1139 covr: 0 % / (fcn) main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040082d (main) | 0x0040092e 55 push rbp | 0x0040092f 4889e5 mov rbp, rsp | 0x00400932 4883ec20 sub rsp, 0x20 | 0x00400936 48c745f00000. mov qword [rbp - local_10h], 0 | 0x0040093e 48c745f80000. mov qword [rbp - local_8h], 0 | 0x00400946 48c745e04477. mov qword [rbp - local_20h], str._bin_id | 0x0040094e 48c745e80000. mov qword [rbp - local_18h], 0 | 0x00400956 488d55f0 lea rdx, [rbp - local_10h] | 0x0040095a 488d45e0 lea rax, [rbp - local_20h] | 0x0040095e 4889c6 mov rsi, rax | 0x00400961 bf44774800 mov edi, str._bin_id ; "/bin/id" @ 0x487744 | 0x00400966 e8450e0300 call fcn.004317b0 ; !!! 本来 "call sym.imp.execve"とでる !!! | 0x0040096b b800000000 mov eax, 0 | 0x00400970 c9 leave \ 0x00400971 c3 ret
0x00400966
の行を見ると,sym.imp.execve
と普通にコンパイルしたら出るはずがfcn.004317b0
になっている.
普段こんなバイナリが来ると,引数と演算結果を見てどの関数かを特定しているが(それしか知らない),ライブラリ関数を20種類以上使われていると心にヒビが入ってくる.
こいつの関数名復元方法を知るためにまずは作り方を調べた.
この方法では消えない
関数名の削除はデバッグシンボルの削除でできると考えて,まずは以下のコマンドを試してみた.
$ gcc -o id id.c $ strip --strip-all ./id # シンボル情報の全削除 $ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 13 xref: 22 sect: 434 code: 470 covr: 108 % ;-- main: / (fcn) sym.main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040040d (sym.main) | 0x004004e6 55 push rbp | 0x004004e7 4889e5 mov rbp, rsp | 0x004004ea 4883ec20 sub rsp, 0x20 | 0x004004ee 48c745f00000. mov qword [rbp - local_10h], 0 | 0x004004f6 48c745f80000. mov qword [rbp - local_8h], 0 | 0x004004fe 48c745e0b405. mov qword [rbp - local_20h], str._bin_id | 0x00400506 48c745e80000. mov qword [rbp - local_18h], 0 | 0x0040050e 488d55f0 lea rdx, [rbp - local_10h] | 0x00400512 488d45e0 lea rax, [rbp - local_20h] | 0x00400516 4889c6 mov rsi, rax | 0x00400519 bfb4054000 mov edi, str._bin_id ; "/bin/id" @ 0x4005b4 | 0x0040051e e8adfeffff call sym.imp.execve ; !!! 消えてない !!! | 0x00400523 b800000000 mov eax, 0 | 0x00400528 c9 leave \ 0x00400529 c3 ret
0x0040051e
を見ると,関数名が消えてない.
そもそもstrip
で消えているものはあるかを確認すると
$ gcc -o id id.c $ file ./id id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, not stripped # stripされてない $ readelf -a ./id > nonstrip $ strip --strip-all ./id $ file ./id ./id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, stripped # stripされた $ readelf -a ./id > stripall $ diff nonstrip stripall 13c13 < Start of section headers: 4784 (bytes into file) --- > Start of section headers: 2592 (bytes into file) 19c19 < Number of section headers: 31 --- > Number of section headers: 29 81,86c81,82 < [28] .shstrtab STRTAB 0000000000000000 000011a4 < 000000000000010c 0000000000000000 0 0 1 < [29] .symtab SYMTAB 0000000000000000 00000928 < 0000000000000660 0000000000000018 30 48 8 < [30] .strtab STRTAB 0000000000000000 00000f88 < 000000000000021c 0000000000000000 0 0 1 --- > [28] .shstrtab STRTAB 0000000000000000 00000921 > 00000000000000fc 0000000000000000 0 0 1 170,240d165 < < Symbol table '.symtab' contains 68 entries: < Num: Value Size Type Bind Vis Ndx Name < 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND < 1: 0000000000400200 0 SECTION LOCAL DEFAULT 1 < 2: 000000000040021c 0 SECTION LOCAL DEFAULT 2 < 3: 000000000040023c 0 SECTION LOCAL DEFAULT 3 < 4: 0000000000400260 0 SECTION LOCAL DEFAULT 4 < 5: 0000000000400280 0 SECTION LOCAL DEFAULT 5 < 6: 00000000004002e0 0 SECTION LOCAL DEFAULT 6 < 7: 0000000000400320 0 SECTION LOCAL DEFAULT 7 < 8: 0000000000400328 0 SECTION LOCAL DEFAULT 8 < 9: 0000000000400348 0 SECTION LOCAL DEFAULT 9 < 10: 0000000000400360 0 SECTION LOCAL DEFAULT 10 < 11: 0000000000400390 0 SECTION LOCAL DEFAULT 11 < 12: 00000000004003b0 0 SECTION LOCAL DEFAULT 12 < 13: 00000000004003e0 0 SECTION LOCAL DEFAULT 13 < 14: 00000000004003f0 0 SECTION LOCAL DEFAULT 14 < 15: 00000000004005a4 0 SECTION LOCAL DEFAULT 15 < 16: 00000000004005b0 0 SECTION LOCAL DEFAULT 16 < 17: 00000000004005bc 0 SECTION LOCAL DEFAULT 17 < 18: 00000000004005f0 0 SECTION LOCAL DEFAULT 18 < 19: 00000000006006e8 0 SECTION LOCAL DEFAULT 19 < 20: 00000000006006f0 0 SECTION LOCAL DEFAULT 20 < 21: 00000000006006f8 0 SECTION LOCAL DEFAULT 21 < 22: 0000000000600700 0 SECTION LOCAL DEFAULT 22 < 23: 00000000006008d0 0 SECTION LOCAL DEFAULT 23 < 24: 00000000006008d8 0 SECTION LOCAL DEFAULT 24 < 25: 0000000000600900 0 SECTION LOCAL DEFAULT 25 < 26: 0000000000600910 0 SECTION LOCAL DEFAULT 26 < 27: 0000000000000000 0 SECTION LOCAL DEFAULT 27 < 28: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c < 29: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c < 30: 00000000006006f8 0 OBJECT LOCAL DEFAULT 21 __JCR_LIST__ < 31: 0000000000400420 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones < 32: 0000000000400460 0 FUNC LOCAL DEFAULT 14 register_tm_clones < 33: 00000000004004a0 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux < 34: 0000000000600910 1 OBJECT LOCAL DEFAULT 26 completed.6945 < 35: 00000000006006f0 0 OBJECT LOCAL DEFAULT 20 __do_global_dtors_aux_fin < 36: 00000000004004c0 0 FUNC LOCAL DEFAULT 14 frame_dummy < 37: 00000000006006e8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_init_array_ < 38: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c < 39: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c < 40: 00000000004006e0 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__ < 41: 00000000006006f8 0 OBJECT LOCAL DEFAULT 21 __JCR_END__ < 42: 0000000000000000 0 FILE LOCAL DEFAULT ABS < 43: 00000000006006f0 0 NOTYPE LOCAL DEFAULT 19 __init_array_end < 44: 0000000000600700 0 OBJECT LOCAL DEFAULT 22 _DYNAMIC < 45: 00000000006006e8 0 NOTYPE LOCAL DEFAULT 19 __init_array_start < 46: 00000000004005bc 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR < 47: 00000000006008d8 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_ < 48: 00000000004005a0 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini < 49: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab < 50: 0000000000600900 0 NOTYPE WEAK DEFAULT 25 data_start < 51: 0000000000600910 0 NOTYPE GLOBAL DEFAULT 25 _edata < 52: 00000000004005a4 0 FUNC GLOBAL DEFAULT 15 _fini < 53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_ < 54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND execve@@GLIBC_2.2.5 < 55: 0000000000600900 0 NOTYPE GLOBAL DEFAULT 25 __data_start < 56: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ < 57: 0000000000600908 0 OBJECT GLOBAL HIDDEN 25 __dso_handle < 58: 00000000004005b0 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used < 59: 0000000000400530 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init < 60: 0000000000600918 0 NOTYPE GLOBAL DEFAULT 26 _end < 61: 00000000004003f0 42 FUNC GLOBAL DEFAULT 14 _start < 62: 0000000000600910 0 NOTYPE GLOBAL DEFAULT 26 __bss_start < 63: 00000000004004e6 68 FUNC GLOBAL DEFAULT 14 main < 64: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses < 65: 0000000000600910 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__ < 66: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable < 67: 0000000000400390 0 FUNC GLOBAL DEFAULT 11 _init
サイズは明らかに減っているし,.symtab
シンボル情報が消されているようだ.しかし,関数名は消えない.
なんでだろうと思って,知り合いに聞いてみたらあっさり答えが返ってきた.
それによると,execve
などのライブラリ関数は./id
外の共有ライブラリに実体がある../id
が共有ライブラリの関数を使う場合,プログラム起動時に関数名などのシンボル名を使ってアドレス解決をする.だから,共有ライブラリを使う限りシンボル名が残ってしまう.つまり,静的リンクでコンパイルすればシンボル名を消してもアドレス解決できるようになるということだった.
これがわかれば話は早い.
消し方
$ gcc -static -o id id.c $ strip -strip-all ./id $ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 4 xref: 54 sect: 550756 code: 1139 covr: 0 % / (fcn) main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040082d (main) | 0x0040092e 55 push rbp | 0x0040092f 4889e5 mov rbp, rsp | 0x00400932 4883ec20 sub rsp, 0x20 | 0x00400936 48c745f00000. mov qword [rbp - local_10h], 0 | 0x0040093e 48c745f80000. mov qword [rbp - local_8h], 0 | 0x00400946 48c745e04477. mov qword [rbp - local_20h], str._bin_id | 0x0040094e 48c745e80000. mov qword [rbp - local_18h], 0 | 0x00400956 488d55f0 lea rdx, [rbp - local_10h] | 0x0040095a 488d45e0 lea rax, [rbp - local_20h] | 0x0040095e 4889c6 mov rsi, rax | 0x00400961 bf44774800 mov edi, str._bin_id ; "/bin/id" @ 0x487744 | 0x00400966 e8450e0300 call fcn.004317b0 ; !!! 消えた !!! | 0x0040096b b800000000 mov eax, 0 | 0x00400970 c9 leave \ 0x00400971 c3 ret
というわけで読めなくなった.
読めないバイナリはこうやって作られるのだろう.
最後に
作成したバイナリは静的リンク時に共有ライブラリに含まれていたのであろう文字列が色々含まれて,本来のユーザ定義文字列が探しにくくなる.これはどうにかできないのだろうか.共有ライブラリを片っ端から--strip-all
すればいいのだろうか.
次は消した情報を復元する方法を調べたい.情報提供募集中.
参考
ありがとう: 知人