動作を解析されないようにするには
参考書籍 : たのしいバイナリの歩き方
・アンチデバッギングの手法
初歩的なデバッガ対策として、IsDebuggerPresent()やCheckRemoteDebuggerPresent()がある。
これらの関数で、デバッガを検知できる。
popfを使いTF(Trap Flag)を1にし、デバッガに次の1命令を実行してSINGLE_STEP例外を発生させる方法がある。
デバッガはSINGLE_STEP例外をキャッチするため、SINGLE_STEP例外がキャッチされなかった場合に(プログラムがキャッチした場合に)検知されたくない処理を行う
bool bNoDebugger = FALSE; __try{ _asm { pushfd or dword ptr[esp], 0x100 popfd // Trap Fragをセット nop } } __except (EXCEPTION_EXECUTE_HANDLER) { // 例外がデバッガにキャッチされない→デバッグされていない bNoDebugger = TRUE; } printf(bNoDebugger ? "No Debugger\n": "In Debugger\n"); return 0;
参考 : Anti-Debugging: Detecting System Debugger
また、int 2dhで割り込みを発生させると、デバッギングされていなければEIPがインクリメントされ、1バイト命令がスキップされる。
デバッギングされていると例外をデバッガがキャッチするため、命令のスキップは起こらない
__asm { xor eax, eax // set Z flag int 2dh inc eax // debugger might skip je being_debugged being_debugged: // ... }
参考 :
windows - INT 2D Anti-Forensic Method - Reverse Engineering Stack Exchange
Dr. Fu's Security Blog: Malware Analysis Tutorial 4: Int2dh Anti-Debugging (Part II)
・コードを難読化して解析されるのを防ぐ
IsDebuggerPresent呼び出し部分を難読化する。
呼び出し分のマシン語は、ff 15 00 20 40なので、先頭にebを付けeb ff 15 00 20 40とする。
eb ffがjmp short near ptr _main+1に置き換わるが、eb ffは1バイト先にジャンプするという命令なので、問題なくcallされる。
・実行ファイルを実行できる状態のまま圧縮する
圧縮するためのソフトをパッカーと言い、有名なパッカーとしてUPXがある。
また、アンチデバッギングを目的としたパッカーもあり、ASPackなどがある。
パッキングをすると、IDAなどで解析しづらくなる。
例えば、パックする前は処理の流れを簡単に追うことができるが、
パック後は処理を追うのがとても難しくなる。
・UPXを手動でアンパックして仕組みを理解する
ASLRなしでコンパイルした.exeファイルをUPXでパックし、OllyDbgで開く
最初にpushad命令でスタックに全レジスタの値をコピーしている
処理をすすめると、popad命令が見つかる。
その次のジャンプ命令を飛んだ先で、OllyDumpを呼び出す
ダンプされたファイルをIDAで開くと、ソースコードそのままの処理がされていることがわかる
処理を眺めると、pushadとpopadの間で展開処理を行い、popadでレジスタを復元し本処理を行っていると分かる。
・ハードウェアブレイクポイントを使ってASPackをアンパックする
ASPackでパックされたコードも、基本的にはpushadとpopadを探せば良い
今まではソフトウェアブレイクポイント(該当アドレスの命令をccに書き換え)を使ってきたが、今回はハードウェアブレイクポイントを使う。
ハードウェアブレイクポイントは、処理を停止する場所をDRレジスタに書き込むという仕組みで、
このアドレスに書き込み/読み込みが起きたら停止させるなど、ソフトウェアブレイクポイントよりも柔軟な使い方ができる。
DRレジスタには限りがあるので、ハードウェアブレイクポイントは4つまでしかセットできない点に注意する。
ASPackでパックされた.exeファイルを開くと、最初にpushadが見つかる。
今回はpopadの探索を省略し、00401000にハードウェアブレイクポイントをセットする。
セットしたら実行する。00401000で処理が止まるので、Ctrl+A(コードの再解析)を押して表示されたコードを確認する。