BypasslessHookはCRCによるメモリ改竄チェックを迂回するための手法としてよく利用されます。
CRCによって改竄チェックできるのはメモリ上に展開されたプログラムの実行領域のみであり、それ以外のデータ領域(静的変数やヒープ)やスタック領域の改竄はチェックできません。
BypasslessHookでは実行領域の改竄を行わず、それ以外の領域の値を改竄することによってフックを行うことでCRCによる改竄検知を迂回しています。
BypasslessHookを構築するにはまず改竄するアドレス周辺にある関数ポインタコール・ジャンプ命令を見つけます。
例えば以下のような命令となります。
jmp eax
jmp [********]
call eax
call [********]
基本的にはこのジャンプあるいはコール先を書き換えてヒープ領域(CRCの影響を受けないアドレス)に作成した独自のコードに処理を移します。
そこで任意の処理を終えたら元のジャンプ先・コール先に復帰させます。
BypasslessHookの構築例
実際のコードからBypasslessHookを構築してみましょう。
imageプラグインエラー : ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (bypassless_1.PNG)
上記画像の0040103Bhにある条件ジャンプ命令(je)をジャンプ命令(jmp)に置き換えるとします。
今回は都合が良いことに条件ジャンプ命令のすぐ上、00401033hに関数ポインタをコールしている命令があります。
これは即ち、アドレス01A900E4hには格納されているサブルーチンをコールする命令となっています。
imageプラグインエラー : ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (bypassless_2.PNG)
01A900E4hの値を参照すると00C0B820hとなりこれは呼び出される関数の先頭アドレスとなります。
メモリビューアで当該命令を右クリックしてFollowすることで呼び出されるサブルーチンを参照することができます。
今回の例では呼び出されるサブルーチンは00C0B820hと被CRC領域に格納されているためサブルーチン側を書き変えることは不可能です。
そこでこの関数ポインタの値を書き換えることにしましょう。
アドレス01A900E4hはCRCによるメモリ改竄検出の影響を受けない静的領域に位置しているため書き変えても改竄検出されることはありません。
そこで、01A900E4hの値を書き変えることで独自のルーチンをコールさせる事が可能となります。
imageプラグインエラー : ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (bypassless_3.PNG)
ただし、関数ポインタで呼び出されるルーチンは必ずしも一ヶ所だけからコールされているとは限りません。
CEの静的アドレス検索機能を利用したところ今回の例では数千ヶ所からこのルーチンがコールされていることが分かります。
したがって、単純にサブルーチンを置き換えただけでは他の呼び出しが干渉して問題が生じることでしょう。
そこでスタックから戻りアドレスを参照することで呼び出し元を判別して処理を分岐させるようにします。
コール命令が呼び出された直後に戻りアドレスがスタックに格納されますのでそれを置き換えたサブルーチン側で参照させます。
今回の例ではアドレス00401039hがスタックに格納されており、ESPレジスタからこれを参照することができます。
今までの話を図にまとめてみました。
青色の部分は被CRC領域ですが紫色の部分はCRCの影響を受けない領域ですので書き変えても問題ありません。
CEのスクリプトでBypasslessHookを構築する際の一例を示します。
[ENABLE]
alloc(BypasslessHook, 512)
label(HookProc)
BypasslessHook:
cmp [esp],00401039
jne 00C0B820
mov [esp],HookProc
jmp 00C0B820
HookProc:
// test eax,eax
// je 00401061
jmp 00401061
01A900E4:
dd BypasslessHook
[DISABLE]
01A900E4:
dd 00C0B820
スタックが指定した呼び出し元と一致する場合にのみ戻りアドレスを書き変えて自身のフックプロシージャに遷移させます。
このようにBypasslessHookでは被CRC領域以外(静的領域・スタック領域・ヒープ領域)で目的の操作を実行させています。
最終更新:2014年08月04日 06:12