Linux Anti Debugging

Linuxにおけるアンチデバッグ(デバッグ検知)について調べながらまとめる.

ptrace

原理

一般的(?),原始的(?)なデバッグ検知.

デバッガは一般的にデバッグ対象のプロセス(debuggee)に対し,ptraceシステムコールを発行してattachする.

ptraceは同じプロセスに対し2度目に呼び出された時,-1を返し失敗する.

これを利用し,debuggee自身がptraceを呼び出す事で,その戻り値で現在attachされているか確認できる

PoC

src.c

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

void anti_debug() {
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0)
        fprintf(stderr, "debugger detected!!!!!!1\n"), exit(1);
}

void main() {
    anti_debug();
    puts("check passed!");
}
$ gcc src.c
$ ./a.out
check passed!
$ gdb -q ./a.out
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
(gdb) run
Starting program: /home/lilium/src/lab/anti_debug/a.out
debugger detected!!!!!!1
[Inferior 1 (process 1944964) exited with code 01]
(gdb)

anti anti debug

呼び出している箇所をNOPで潰す,呼び出し後にRAXを書き換える等の手法で検知を回避できる.

バイナリエディタでアンチデバッグ用関数を呼び出している命令をNOPに書き換える事で,簡単に回避できる.

また,以下の様にptrace呼び出した後に戻り値(RAX)を書き換える事でも回避できる.

$ gdb -q ./a.out
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
(gdb) i functions
All defined functions:

Non-debugging symbols:
0x0000000000001000  _init
0x0000000000001070  __cxa_finalize@plt
0x0000000000001080  puts@plt
0x0000000000001090  ptrace@plt
0x00000000000010a0  exit@plt
0x00000000000010b0  fwrite@plt
0x00000000000010c0  _start
0x00000000000010f0  deregister_tm_clones
0x0000000000001120  register_tm_clones
0x0000000000001160  __do_global_dtors_aux
0x00000000000011a0  frame_dummy
0x00000000000011a9  anti_debug
0x0000000000001201  main
0x0000000000001230  __libc_csu_init
0x00000000000012a0  __libc_csu_fini
0x00000000000012a8  _fini
(gdb) b anti_debug
Breakpoint 1 at 0x11a9
(gdb) run
Starting program: /home/lilium/src/lab/anti_debug/a.out

Breakpoint 1, 0x00005555555551a9 in anti_debug ()
(gdb) disass
Dump of assembler code for function anti_debug:
=> 0x00005555555551a9 <+0>:     endbr64
   0x00005555555551ad <+4>:     push   %rbp
   0x00005555555551ae <+5>:     mov    %rsp,%rbp
   0x00005555555551b1 <+8>:     mov    $0x0,%ecx
   0x00005555555551b6 <+13>:    mov    $0x1,%edx
   0x00005555555551bb <+18>:    mov    $0x0,%esi
   0x00005555555551c0 <+23>:    mov    $0x0,%edi
   0x00005555555551c5 <+28>:    mov    $0x0,%eax
   0x00005555555551ca <+33>:    callq  0x555555555090 <ptrace@plt>
   0x00005555555551cf <+38>:    test   %rax,%rax
   0x00005555555551d2 <+41>:    jns    0x5555555551fe <anti_debug+85>
   0x00005555555551d4 <+43>:    mov    0x2e45(%rip),%rax        # 0x555555558020 <stderr@@GLIBC_2.2.5>
   0x00005555555551db <+50>:    mov    %rax,%rcx
   0x00005555555551de <+53>:    mov    $0x19,%edx
   0x00005555555551e3 <+58>:    mov    $0x1,%esi
   0x00005555555551e8 <+63>:    lea    0xe15(%rip),%rdi        # 0x555555556004
   0x00005555555551ef <+70>:    callq  0x5555555550b0 <fwrite@plt>
   0x00005555555551f4 <+75>:    mov    $0x1,%edi
   0x00005555555551f9 <+80>:    callq  0x5555555550a0 <exit@plt>
   0x00005555555551fe <+85>:    nop
   0x00005555555551ff <+86>:    pop    %rbp
   0x0000555555555200 <+87>:    retq
End of assembler dump.
(gdb) b *0x00005555555551ca
Breakpoint 2 at 0x5555555551ca
(gdb) c
Continuing.

Breakpoint 2, 0x00005555555551ca in anti_debug ()
(gdb) i r
rax            0x0                 0
rbx            0x0                 0
rcx            0x0                 0
rdx            0x1                 1
rsi            0x0                 0
rdi            0x0                 0
rbp            0x7fffffffd2f0      0x7fffffffd2f0
rsp            0x7fffffffd2f0      0x7fffffffd2f0
r8             0x0                 0
r9             0x7ffff7fdffa0      140737354006432
r10            0xfffffffffffffa4a  -1462
r11            0x202               514
r12            0x5555555550c0      93824992235712
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x5555555551ca      0x5555555551ca <anti_debug+33>
eflags         0x246               [ PF ZF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) ni
0x00005555555551cf in anti_debug ()
(gdb) i r
rax            0xffffffffffffffff  -1
rbx            0x0                 0
rcx            0x0                 0
rdx            0xffffffffffffff88  -120
rsi            0x0                 0
rdi            0x0                 0
rbp            0x7fffffffd2f0      0x7fffffffd2f0
rsp            0x7fffffffd2f0      0x7fffffffd2f0
r8             0xffffffff          4294967295
r9             0x7ffff7fdffa0      140737354006432
r10            0x0                 0
r11            0x286               646
r12            0x5555555550c0      93824992235712
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x5555555551cf      0x5555555551cf <anti_debug+38>
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) set $rax=0
(gdb) i r
rax            0x0                 0
rbx            0x0                 0
rcx            0x0                 0
rdx            0xffffffffffffff88  -120
rsi            0x0                 0
rdi            0x0                 0
rbp            0x7fffffffd2f0      0x7fffffffd2f0
rsp            0x7fffffffd2f0      0x7fffffffd2f0
r8             0xffffffff          4294967295
r9             0x7ffff7fdffa0      140737354006432
r10            0x0                 0
r11            0x286               646
r12            0x5555555550c0      93824992235712
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x5555555551cf      0x5555555551cf <anti_debug+38>
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) c
Continuing.
check passed!
[Inferior 1 (process 1959403) exited with code 016]
(gdb)