ばぁどです。
現在、ハニーポットで採取したマルウェアを仮想環境内で静的解析して遊んでおります。
本当はマルウェアの解析結果をアウトプットできれば面白そうなのですが、諸事情により自作したC言語のソースコードをコンパイルして吐かれた実行ファイルに対して静的解析を行っていきます。
C言語
今回使うソースコード
#include<stdio.h> int main(void) { int a, b, total; a = 10; b = 5; total = a + b; printf("%d\n", total); }
コンパイル
$ cc sample.c $ ls -la total 24 drwxr-xr-x 2 root root 4096 Oct 30 19:47 . drwxr-xr-x 5 root root 4096 Oct 30 19:44 .. -rwxr-xr-x 1 root root 8424 Oct 30 19:47 a.out -rw-r--r-- 1 root root 114 Oct 30 19:47 sample.c
今回はこのa.out
を使っていきます。
実行
$ ./a.out 15
念の為実行結果。
まずは表層解析から
file コマンド
$ file a.out a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=079b654a11e71cd0b8af95851abb964f267f561b, not stripped
ELF形式ですね。Linux で動く実行形式のファイルです。
strings コマンド
$strings a.out /lib64/ld-linux-x86-64.so.2 libc.so.6 printf __cxa_finalize __libc_start_main GLIBC_2.2.5 _ITM_deregisterTMCloneTable __gmon_start__ _ITM_registerTMCloneTable AWAVA AUATL []A\A]A^A_ ;*3$" GCC: (Debian 7.2.0-12) 7.2.1 20171025 crtstuff.c deregister_tm_clones __do_global_dtors_aux completed.7001 __do_global_dtors_aux_fini_array_entry frame_dummy __frame_dummy_init_array_entry sample.c __FRAME_END__ __init_array_end _DYNAMIC __init_array_start __GNU_EH_FRAME_HDR _GLOBAL_OFFSET_TABLE_ __libc_csu_fini _ITM_deregisterTMCloneTable _edata printf@@GLIBC_2.2.5 __libc_start_main@@GLIBC_2.2.5 __data_start __gmon_start__ __dso_handle _IO_stdin_used __libc_csu_init __bss_start main __TMC_END__ _ITM_registerTMCloneTable __cxa_finalize@@GLIBC_2.2.5 .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .dynamic .got.plt .data .bss .comment
おおう。。なかなか大量にあるんだな・・・
静的解析で用いるコマンドの解説
今回、利用するコマンドはobjdump
です。
オブジェクトファイルの情報を表示してくれます。
コマンドのオプション
今回使用するのは、-d
オプションです。
オプション | 説明 |
---|---|
-d ( --disassemble) | 機械語に対応するアセンブラのニーモニックを表示する。逆アセンブルをするときに用いる。 |
要するに逆アセンブルするときに使われるオプションです。
もうこの時点で専門用語満載ですね。 一つ一つ解説します。
アセンブラ
アセンブリ言語
コンピュータが理解できる言葉であるマシン語を人間が理解できる言語に置き換えたもの。
ニーモニック
アセンブリ言語で用いられる英単語もしくは英単語の略語。 ADDやLD(LOAD)など様々なニーモニックコードが存在する。
アセンブル
人が理解できるプログラム言語から機械が理解できる機械語へ変換すること。
逆アセンブル
アセンブルの逆。 機械が理解できる機械語から人が理解できる形式へ変換すること。
objdumpの結果
※長いため省略します
$objdump -d a.out a.out: file format elf64-x86-64 Disassembly of section .init: 00000000000004f0 <_init>: 4f0: 48 83 ec 08 sub $0x8,%rsp 4f4: 48 8b 05 ed 0a 20 00 mov 0x200aed(%rip),%rax # 200fe8 <__gmon_start__> 4fb: 48 85 c0 test %rax,%rax 4fe: 74 02 je 502 <_init+0x12> 500: ff d0 callq *%rax 502: 48 83 c4 08 add $0x8,%rsp 506: c3 retq Disassembly of section .plt: 0000000000000510 <.plt>: 510: ff 35 f2 0a 20 00 pushq 0x200af2(%rip) # 201008 <_GLOBAL_OFFSET_TABLE_+0x8> 516: ff 25 f4 0a 20 00 jmpq *0x200af4(%rip) # 201010 <_GLOBAL_OFFSET_TABLE_+0x10> ・ ・ ・
$ objdump -d > sample.txt root@kali:~/work/c# wc -l sample.txt 189 sample.txt
全部で189行になりました。
initを調べてみる
全部解析しようとすると、時間がいくらあっても足りないのでinitメソッドのみ何をやっているかを調べてみます。
1行目 4f0: 48 83 ec 08 sub $0x8,%rsp 2行目 4f4: 48 8b 05 ed 0a 20 00 mov 0x200aed(%rip),%rax # 200fe8 <__gmon_start__> 3行目 4fb: 48 85 c0 test %rax,%rax 4行目 4fe: 74 02 je 502 <_init+0x12> 5行目 500: ff d0 callq *%rax 6行目 502: 48 83 c4 08 add $0x8,%rsp 7行目 506: c3 retq
1行目
4f0: 48 83 ec 08 sub $0x8,%rsp
%rsp
というのが肝ですね。通常RSP
と記述されるもので、レジスタと呼ばれているものです。RSP
はスタックに関するレジスタでありスタックの一番上のメモリアドレスを示しているとのこと。
また、sub
は引き算をする時に使われるニーモニックコードです。
第一オペランド($0x8)から第二オペランド(%rsp)を引いて、第一オペランドに格納するという動きをするとのこと。
なので、この一行は変数$0x8
からスタックの一番上のメモリアドレスの値を引くという動作になると読めそうです。
2行目
4f4: 48 8b 05 ed 0a 20 00 mov 0x200aed(%rip),%rax # 200fe8 <__gmon_start__>
mov
が使われています。
mov
は値のコピーをするときに使われるもの。
%rip
は次に実行される命令を指すレジスタ。
%rax
というのは%rsp
同様レジスタであり、アキュムレーターを指しているとのこと。
アキュムレーターとは、CPUの演算回路を構成するレジスタの一種で、論理演算や四則演算などによるデータの入出力と結果の保持に用いられるレジスタのことである。
3行目
4fb: 48 85 c0 test %rax,%rax
test
はビット01をテストしているとのこと。
4行目
4fe: 74 02 je 502 <_init+0x12>
je
は条件分岐のニーモニックコードとのこと。
前の結果が0なら分岐、ゼロフラグ=1なら分岐なので、3行目のtest
の結果次第で条件が分岐するということでしょうか。
5行目
ff d0 callq *%rax
callq
は関数呼び出しのニーモニックコードです。
また、呼び出しに使われているのも*
が使用されているのでポインタをさし示すことになるので、レジスタRAX
の番地にある処理を実行せよという表記になるのかな・・
6行目
48 83 c4 08 add $0x8,%rsp
今回はadd
ですね。
sub
とほぼ同じ挙動なのですが、$0x8
の値と%rsp
の値を足して、$0x8
に格納するという処理が行われます。
7行目
506: c3 retq
retq
は呼び出し元への処理の返却を表しています。
今回使われたニーモニックコードまとめ
ニーモニックコード | 意味 | 詳細 |
---|---|---|
add | add | 値の足し算。 |
sub | subtract | 値の引き算。 |
mov | move | 値のコピー。第一オペランドに第二オペランドの数値をコピーする。 |
test | test | ビットの01をテスト。 |
retq | return | 返り値。 |
まとめ
- 静的解析で利用するコマンドは
objdump
- 機械語から人間が理解できる言語に変換することを逆アセンブリという
- バイナリファイルを静的解析するには逆アセンブリが必要
- 逆アセンブリしたファイルの中身を知りたいときはニーモニックに関する知識が必要
- マルウェアの静的解析をブログ記事にするのは若干ビビる
所感
今回はバイナリファイルの静的解析の真似事をしてみました。
色々と抜け落ちている知識が多々あり、コンピュータ科学の基礎の基礎が抜け落ちていることを痛感しました。大学で何を学んでいたのでしょうか・・・うう・・・
今回大学の教科書などを引っ張り出して調査しながら記事を書きました。 至らない点もあると思います。何か間違った記述などあればコメントいただけると幸いです。