Security Warrior (§3 Linux Reverse Engineering)
暫く積読状態だった Security Warrior: Know Your Enemy を読み始め。目に付いた章、Linux Reverse Engineering (全文のPDF) から。サポートページを眺めつつ。
面白かった話題1: antidisassembly (file/objdump/gdb で扱えないバイナリを作る)
ELF Kickersというツールに付属している sstrip というコマンドを用いると、objdumpコマンドやgdbコマンドで解析できないバイナリを作ることが出来るそうです。
例:
$ sstrip a.out $ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), corrupted section header size ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ $ gdb ./a.out (略) This GDB was configured as "i386-redhat-linux-gnu"... warning: no loadable sections found in added symbol-file (no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) b main No symbol table is loaded. Use the "file" command. (gdb) r Starting program: /home/yupo5656/a.out warning: shared library handler failed to enable breakpoint ...
こんな感じ。Fedora Core2 で実験しました。ただ、NASM, IDA Pro*1, biew, hte あたり*2ではサックリと解析できてしまうようなので、あくまで小手先の技とのこと。
面白かった話題2: antidebugging (gdbやstrace,ltraceでの解析を妨害する)
バイナリをgdbやstraceで解析しにくくする方法が書いてあります。
gdbやstraceなどでバイナリの実行を解析する場合、ptraceというシステムコールを使います。別のプロセス(gdbなど)が、ptrace(PTRACE_ATTACH, target_pid); して解析対象プロセスにアタッチし、ptrace(PTRACE_XXX, target_pid); でプロセスの情報を読んだり書いたり制御したりすることが可能です。
一方、解析される側も、自身がptraceされているか知る方法があります。
だそうです。3.はFC2ではうまくいかなかったので、1と2のコードを載せておきます*4。
まず1から。
#include <sys/ptrace.h> #include <stdio.h> int main(void) { if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0 ) { // ptraceされているので動作を停止 while(1); } printf("OK.\n"); return 0; }
でOKです。あるいは、ptrace呼び出しとif分岐、while無限ループを__asm__で置換してもっと分かり難くするなら、
#include <stdio.h> int main(void) { __asm__ __volatile__("\t xorl %ebx, %ebx \n" "\t movl $26, %eax \n" "\t int $0x80 \n" "\t test %eax, %eax \n" "\t inf_loop: \n" "\t jne inf_loop \n"); /* $26 の 根拠は /usr/include/asm/unistd.h の #define __NR_ptrace 26 です */ printf("OK.\n"); return 0; }
となります。バイパス方法としては、
- 判定部分を書き換え、NOPなどで潰してしまう
- ptraceをwrapする偽のptraceを、kernel module として作成し、insmodしてしまう
があるそうです。後者についてはサンプルコードが掲載されています。
次は2。
#include <stdio.h> #include <signal.h> static volatile sig_atomic_t being_debugged = 1; void int3_handler(int signo) { being_debugged = 0; } int main(void) { signal(SIGTRAP, int3_handler); __asm__ __volatile__("int3"); // SIGTRAP発生 if (being_debugged) { // ptraceされているので動作を停止 while(1); } printf("OK.\n"); return 0; }
となります。バイパス方法としては、判定を潰す前述の方法か、
だそうです。