😾
gdbによるプログラムの動的解析を防ぐ手法の紹介とその手法の回避方法
想定環境
x64GNU/Linux
序
この記事ではgdbなどのデバッガによる動的解析を防ぐためのアンチデバッグ手法を解説します。
またその手法による解析拒否を回避する方法も解説します。
ptraceによるアンチデバッグ手法
ptraceはシステムコールです。
gdbなどのデバッガはptraceを用いて他のプログラムにアタッチ(接続)しています。
ここで紹介するアンチデバッグ手法は、デバッガが使われていると判断したら、すぐにプログラムを終了する手法です。
具体的なデバッガ検知のコードを見てみます。
anti_debug.c
#include <stdio.h>
#include <sys/ptrace.h>
int main(void) {
if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
printf("デバッガ使うな!!!!\n");
return 1;
} else {
printf("デバッガを使用していないことを確認したので通常の処理を続行します\n");
return 0;
}
}
このプログラムはptraceで自分自身にアタッチし、ptraceが失敗するかを確認します。
失敗すれば、デバッガが使われていると判断できるのでぶちギレメッセージとともにプログラムを終了しています。
デバッガを使わず普通に実行してみましょう。
gcc anti_debug.c
./a.out
"デバッガを使用していないことを確認したので通常の処理を続行します"と出力されましたね。
ではgdbで実行してみましょう。
gdb a.out
break main
run
next
"デバッガ使うな!!!!"とキレらました、、、
アンチでバッグ手法の回避方法
ではどうしてもこのアンチでバッグ手法を回避したい場合どうすればよいのでしょうか。
回避方法としては、パッチを当てることです。
バイナリの一部を変更することをパッチを当てると表現します。
例えば16進数エディタであるhexeditなどを用いて、デバッガを検知している部分の機械語をnop命令で塗りつぶします。
では実際にやってみましょう。
まずobjdumpを用いて逆アセンブルします。
objdump -M x86-64 -M intel -d a.out
objdumpの出力の一部
0000000000001145 <main>:
1145: 55 push rbp
1146: 48 89 e5 mov rbp,rsp
1149: b9 00 00 00 00 mov ecx,0x0
114e: ba 01 00 00 00 mov edx,0x1
1153: be 00 00 00 00 mov esi,0x0
1158: bf 00 00 00 00 mov edi,0x0
115d: b8 00 00 00 00 mov eax,0x0
1162: e8 d9 fe ff ff call 1040 <ptrace@plt>
1167: 48 83 f8 ff cmp rax,0xffffffffffffffff
116b: 75 13 jne 1180 <main+0x3b>
116d: 48 8d 3d 94 0e 00 00 lea rdi,[rip+0xe94] # 2008 <_IO_stdin_used+0x8>
1174: e8 b7 fe ff ff call 1030 <puts@plt>
1179: b8 01 00 00 00 mov eax,0x1
117e: eb 11 jmp 1191 <main+0x4c>
1180: 48 8d 3d a9 0e 00 00 lea rdi,[rip+0xea9] # 2030 <_IO_stdin_used+0x30>
1187: e8 a4 fe ff ff call 1030 <puts@plt>
118c: b8 00 00 00 00 mov eax,0x0
1191: 5d pop rbp
1192: c3 ret
1193: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
119a: 00 00 00
119d: 0f 1f 00 nop DWORD PTR [rax]
e8 d9 fe ff ffというところを90 90 90 90 90 にしてみましょう。
そしてgdbで実行すると
"デバッガを使用していないことを確認したので通常の処理を続行します"と表示され回避することができました。
Discussion