Security Warrior (§3 Linux Reverse Engineering)

セキュリティウォーリア


Security Warrior: Know Your Enemy

暫く積読状態だった 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されているか知る方法があります。

  1. ptraceシステムコールを用いて調べる
  2. SIGTRAPがデバッガに横取りされるか調べる
  3. デバッグ時に自バイナリの要所要所に埋め込まれるトラップ(int3命令*3など)を検出する

だそうです。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;
}

となります。バイパス方法としては、判定を潰す前述の方法か、

  • gdbを使っている場合、(gdb) signal SIGTRAP として当該プロセスに明示的にシグナルを送ってやる

だそうです。

*1:Linux版はβテスト中…なのかな

*2:aldもいけそう

*3:SIGTRAPを発生させるx86のインストラクション

*4:書籍に載っているままのコードではコンパイル通らないですし…。ナニゲに誤植が多いですこの本