GDB アンチデバッギング

GDB アンチデバッギングについて調べたのでメモ。
ptraceを使う検知法とファイルディスクリプタの増加による検知法の2通りの方法を発見したけど、後者は古いバージョンでしか動かないっぽい。

ptraceを使って検知

ptraceとはプロセス追跡をするシステムコールである。1回目の呼び出しでは正常にプロセスを追跡できるが、2回目以降、同一プロセスを追跡しようとすると失敗し、-1を返す。ptraceで自分自身を追跡すると、gdbから実行された場合(既にptraceされている場合)は、-1を返すので検知できる。

#include <stdio.h>
#include <sys/ptrace.h>

int main() {
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
        printf("inside gdb !!\n");
        return 1;
    }

    printf("outside gdb.\n");

    return 0;
}

攻略法

ptrace()をnopで潰したり、ptraceを呼んだ後に set $eax = 0 したりすることで攻略できる。

ファイルディスクリプタの増加による検知

ファイルディスクリプタとは、プロセスがアクセスするファイルや標準入出力などをOSが識別するために用いる識別子である。

最初からプロセスは、0(stdin)、1(stdout)、2(stderr)の3つのファイルディスクリプタを持っていて、これらはプロセス終了時に自動で閉じられるが、ファイルを開いたり、socketを使うときに生成されるものは自分で閉じる必要がある。

以下のコードでファイルディスクリプタの割り当てが確認できる。

#include <stdio.h>
#include <unistd.h>


int main(void) {
    printf("stdin: %d\n", fileno(stdin)); //出力は 0
    printf("stdout: %d\n", fileno(stdout)); //出力は 1
    printf("stderr: %d\n", fileno(stderr)); //出力は 2

    FILE *new_fd = fopen("/tmp", "r");
    printf("new fd: %d\n", fileno(new_fd)); //出力は 3
    fclose(new_fd);

    return 0;
}

gdbでプログラムを実行する際、gdbが3、4、5のファイルディスクリプタを開くので、ファイルディスクリプタの増加によってgdbを検知できるらしいが、私の環境(gdb (Ubuntu 7.7-0ubuntu3.1) 7.7, gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2)では再現できなかった。gdbがファイルディスクリプタを新たに開かないようになったっぽい。

#include <stdio.h>
#include <unistd.h>

int main(){
    FILE* fd = fopen("/tmp", "r");
    if(fileno(fd) > 3){
        printf("fuck you gdb !!\n");
        _exit(1);
    }
    printf("outside gdb.\n");
    return 0;
}

攻略法

break __libc_start_mainして、close()を使ってファイルディスクリプタを閉じればいい。