拾い物のコンパス

まともに書いたメモ

ELFバイナリのデバッグ時の関数名を読めなくする方法

Defconとかでは問題のELFバイナリをデバッグすると,ユーザ定義関数名だけでなく,ライブラリ関数名も読めなくなっている.どうやるとこんなバイナリを作れるのかを調べてみた.結論から言えば,静的リンクでコンパイルして,stripでデバッグシンボルを消すだけだった.

ライブラリ関数名の読めないプログラム

例えば,/bin/idexecve関数で実行するだけの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すればいいのだろうか.
次は消した情報を復元する方法を調べたい.情報提供募集中.

参考

ありがとう: 知人