読者です 読者をやめる 読者になる 読者になる

Twitterに書ききれないこと

イベントや技術的なことを記したい・・・

Windows Anti-Debug Reference まとめ その3

前回からの続き

Windows Anti-Debug Reference まとめ その1 - Twitterに書ききれないこと

Windows Anti-Debug Reference まとめ その2 - Twitterに書ききれないこと

SymantecのページにWindowsのアンチデバッグ技術について、いい感じにまとまったページがある。勉強のために、そのページを訳してまとめたり、追加で調べた。間違いがないように注意しているけれど間違っているかも・・・

www.symantec.com

CPU anti-debug

1. Rogue Int3

これは弱いデバッガを欺くための、古典的なアンチデバッグである。 プログラムの有効なシーケンスの途中で、INT3オペコードを挿入して実装する。 プログラムがデバッグされていない場合、INT3が実行されると制御は、例外ハンドラに渡される。

デバッガがINT3命令を実行したとき、制御が例外ハンドラに移らない。よってプログラムの流れが変更される。 これを利用してアンチデバッグを実装することができる。

INT3が0xCD、0x03のようにエンコードすることができることに注意しなければならない。

push offset @handler
push dword fs:[0]
mov fs:[0], esp
;...
db 0CCh
;if fall here, debugged
;...
@handler:
;continue execution
2. "Ice" Breakpoint

いわゆる"Ice breakpoint"は、インテルのドキュメント化されていない命令の一つで、オペコードは0xf1である。 これは、トレーサーを検出するために使用できる。

この命令を実行すると、SINGLE_STEP例外が生成される。 プログラムが既にトレースされている場合、デバッガは、フラグレジスタのSingleStepビットがセットされた命令の実行によって、生成された通常の例外だと思う。 このため、関連する例外ハンドラが実行されず、期待どおりに実行の続行ができない。

このトリックはシングルステップ実行することで、簡単にバイパスできる。 プログラムがトレースされていないため、例外が生成される。 デバッガは、例外ハンドラに制御を渡すことを理解する必要がある。

push offset @handler
push dword fs:[0]
mov fs:[0], esp
;...
db 0F1h
;if fall here, traced
;...
@handler:
;continue execution
3. Interrupt 2Dh

この割り込みを実行すると、デバッグしていない場合、ブレークポイント例外が発生する。

プログラムがデバッグされており、トレースフラグが無効な場合、 例外は生成されず、実行が正常に行われる。

プログラムがデバッグされており、トレースフラグが無効な場合、 次のバイトはスキップされ、実行が継続されます。

これを利用して、INT命令と同じようにアンチデバッグを実装する。 INT 2Dhは、強力なアンチデバッグ、及びアンチトレーサー機構として利用できる。

push offset @handler
push dword fs:[0]
mov fs:[0], esp
;...
db 02Dh
mov eax, 1 ;anti-tracing
;...
@handler:
;continue execution
4. Timestamp counters

マシンが起動してから現在までの、実行されたCPUサイクル数を記憶する高精度のカウンターは、RDTSC命令を使用して取得することができる。 古典的なアンチデバッグは、プログラム内の重要なポイント(通常例外ハンドラの周り)での経過時間を測定する。 差が大きすぎる場合、プログラムがデバッガの制御下で実行されていることを意味する。 (デバッガでの例外を処理し、デバッグ対象に制御を戻すのは、長時間のタスクである)

push offset handler
push dword ptr fs:[0]
mov fs:[0],esp
rdtsc
push eax
xor eax, eax
div eax ;trigger exception
rdtsc
sub eax, [esp] ;ticks delta
add esp, 4
pop fs:[0]
add esp, 4
cmp eax, 10000h ;threshold
jb @not_debugged
@debugged:
...
@not_debugged:
...
handler:
mov ecx, [esp+0Ch]
add dword ptr [ecx+0B8h], 2 ;skip div
xor eax, eax
ret
5. Popf and the trap flag

フラグレジスタにのトラップフラグが、プログラムのトレースを制御している。

EFLAGSの8ビット目がトラップフラグ(トレースフラグ)になっている。 このフラグが設定されている場合は、命令を実行するとSINGLE_STEPの例外が発生する。

トラップフラグは、トレーサーを阻止するために、操作することができる。 例えば、以下の命令はトラップフラグを設定する。

pushf
mov dword [esp], 0x100
popf

次のコードは戻り値が0なら正常、1ならデバッグされている状態である。

    pushad
    push ok
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    mov buff, esp
    push 100h
    popf
    jmp error

ok:
    mov esp, buff
    pop dword ptr fs:[0]
    add esp, 4
    popad
    xor eax eax
    ret

error:
    mov esp, buff
    pop dword ptr fs:[0]
    add esp, 4
    popad
    xor eax eax
    inc eax
    ret
6. Stack Segment register

これは非常にオリジナルなアンチトレーサー技術である。 (筆者はMarCryptと呼ばれるパッカーで遭遇した)

このアンチデバッグ技術は以下のコードようになる。

push ss
pop ss
pushf
nop

通常、デバッガ上でpop ssをステップ実行した時、次の命令が実行される。 しかし、この場合はデバッガは次の命令で停止できない。 よって、さらに次の命令(この場合はNOP)で停止する。

Marcryptは、以下のようにこのアンチデバッグ方法を使用している。

push ss
; junk
pop ss
pushf
; junk
pop eax
and eax, 0x100
or eax, eax
jnz @debugged
; carry on normal execution

デバッガが命令をトレースしている場合は、popfが暗黙的に実行される。 そのため、デバッガがスタックにプッシュ値したトラップフラグ設定を解除することができない。 トラップフラグが見つかった場合は、トラップフラグの保護チェックを行い、プログラムを終了する。

このアンチトレースを回避するためには、(TFフラグを使用しないように)POPFにブレークポイントを設定してプログラムを実行すればよい。

7. Debug registers manipulation

デバッグレジスタ(DR0-DR7)は、ハードウェアブレークポイントを設定するために使用される。 ハードウェアブレイクポイントは、デバッグレジスタに直接アドレスを入れることで、ブレークポイントを設定することができる。

tElockなどのパッカーは、リバースエンジニアリング防止の為、デバッグレジスタを使用する。

ユーザーモードから、デバッグレジスタは設定することはできない。特権命令の'mov drx, ...' を使用する。 他の方法もある。

-例外を生成し、スレッドコンテキストを変更(例外がスローされた時点での、CPUのレジスタを含む)したあと、新しいコンテキストと共にレジュームする。

ユーザモードでは、DRの情報がスレッドコンテキストに渡されているので、GetThreadContextで取得することができる。

-NtGetContextThreadとNtSetContextThreadシステムコールを使用する。 (GetThreadContextとSetThreadContextがKERNEL32で利用可能)

ほんどの場合、まず非公式の方法が使われる。

push offset handler
push dword ptr fs:[0]
mov fs:[0],esp
xor eax, eax
div eax ;generate exception
pop fs:[0]
add esp, 4
;continue execution
;...
handler:
mov ecx, [esp+0Ch] ;skip div
add dword ptr [ecx+0B8h], 2 ;skip div
mov dword ptr [ecx+04h], 0 ;clean dr0
mov dword ptr [ecx+08h], 0 ;clean dr1
mov dword ptr [ecx+0Ch], 0 ;clean dr2
mov dword ptr [ecx+10h], 0 ;clean dr3
mov dword ptr [ecx+14h], 0 ;clean dr6
mov dword ptr [ecx+18h], 0 ;clean dr7
xor eax, eax
ret
8. Context modification

デバッグレジスタの操作と同様に、コンテキストは、プログラムの実行の流れを変更するために使用することができる。

別のシステムコール、NtContinueが、現在のスレッドに新しいコンテキストをロードするために使用できることに注意しなければいけない。(例を挙げると、このシステムコールは、例外ハンドラ·マネージャによって使用される)

Uncategorized anti-debug

1. TLS-callback

このアンチデバッグはその数年前まで、よく知られていなかった。 プログラムの最初の実行場所はスレッドローカルストレージのエントリ(PEオプションヘッダー内のIMAGE_DATA_DIRECTORY[9])で参照されている。

IMAGE_DATA_DIRECTORY

     Offset      Description
     (PE/PE32+)
[0]  96/112  Export table address and size
[1]  104/120     Import table address and size
[2]  112/128     Resource table address and size
[3]  120/136     Exception table address and size
[4]  128/144     Certificate table address and size
[5]  136/152     Base relocation table address and size
[6]  144/160     Debugging information starting address and size
[7]  152/168     Architecture-specific data address and size
[8]  160/176     Global pointer register relative virtual address
[9]  168/184     Thread local storage (TLS) table address and size
[10] 176/192     Load configuration table address and size
[11] 184/200     Bound import table address and size
[12] 192/208     Import address table address and size
[13] 200/216     Delay import descriptor address and size
[14] 208/224     The CLR header address and size
[15] 216/232     Reserved

TLS-callbackを使うと、プログラムのエントリポイントは、最初に実行されない。 TLSのエントリは隠密な方法で、アンチデバッグチェックを実行することができる。

実際には、この技術は広く使用されていない。 (OllyDbgを含む)古いデバッガがTLSに対応していないが、カスタムパッチツールプラグインを用いて簡単に対応できる。

2. CC scanning

パッカーで使用される一般的な保護機能は、デバッガによって設定されたソフトウェアブレークポイントを検出することを目的とするCC-スキャンループある。

これを回避したい場合は、ハードウェアブレークポイントまたはソフトウェアブレークポイントのカスタムタイプのいずれかを使用する。 CLI(0xFA)は、古典的なINT3オペコードを交換するための候補である。 この命令には要件がある。 RING3プログラムによって実行された場合には、特権命令例外が発生し、1バイトの空間を占有する。

3. EntryPoint RVA set to 0

いくつかのパックされたファイルは、それらのエントリポイントのRVAは、0に設定されている。 つまり、"MZ"から実行される。対応する命令は 'dec ebx / pop edx ...'.

これは、それ自体がアンチデバッグトリックはないが、ソフトウェアブレークポイントを使用して、エントリポイントで中断したい場合に妨害することができる。 もしプロセスを中断した場合、INT3が設定されてマジックナンバーのMZが消えてしまう。 マジックナンバーは、プロセスが生成されるときにチェックされる。 プロセスが(エントリー·ポイントに到達するのを期待して)再開されるときも、NTDLLによって再びチェックされる。 この場合だと、INVALID_IMAGE_FORMAT例外が発生してしまう。

トレースやデバッグツールを作成する場合は、この問題を回避するため、ハードウェアブレークポイントを使用したほうがよい。

参考文献

Ero Carrera's blog: A PE trick, the Thread Local Storage

IMAGE_DATA_DIRECTORY structure (Windows)