OllyDbg Q&A
- 以下の解説はデバッガを用いたPCゲーム解析の初心者の方を対象として執筆しました。
- 以下の回答はVer1.10使用時をベースにしています。
- 以下ではメニュー等の項目名について、対応する日本語化パッチ適用後の項目名を黄色で併記しています。日本語化パッチは当サイトメインページで配布しています。
- 以下に挙げたキー操作は、標準メニューや右クリックによるポップアップメニューから同様の操作を行えるものもあります。
- 以下の「メインウィンドウ」とは、『OllyDbg』でアプリケーションを起動させると最初に表示される「CPUウィンドウ」のことです。特に指定のない場合、以下の「左上ウィンドウ」等はこの「CPUウィンドウ」内の分割されたウィンドウ(ペイン)を指します。
- デバッグ対象実行ファイルまたはプロセスのことを一般に「デバッギー」(Debuggee)と呼びます。
- 以下の回答は匿名希望のリバースエンジニア有志とうさぴょんで行っています。
| 予備知識 | 操作関連 | ゲーム解析関連 | その他 |
質問一覧
予備知識
- OllyDbgのWindows Vista上での動作について教えてください。
- 逆アセンブルコードリストについて教えて下さい。
- ブレークポイントの種類とその違いについて教えて下さい。
- デバッグ中にシステムDLL内でアクセス違反が生じるのはOSの問題ですか?
- デバッギーからデタッチすることはできませんか?
- デバッギーのエントリーポイントがコードセクション内ならウイルスには感染していませんか?
- ウイルス対策としてのデバッグ検出について教えて下さい
- デバッギーのウイルス感染やバグによるレジストリ破壊等に備えて、完全に独立した環境で解析を行いたい。
- 解析時にデバッギーが行ったファイル・レジストリ操作の結果だけを簡単に確認したい。
- デバッガ検出手法を教えてください。
- INT命令を用いたデバッガ検出が上手くいきません(環境:WindowsXP)。
- OllyDbgのプロセスを検出して解析を妨害するタイプのウイルスを解析したい
- OS上でデバッガが動作したことを検出する方法を教えてください
- アタッチしてブレークポイントを設定すると、ブレークポイントが機能しない上に強制終了するソフトウェアがあるのですが。
- デバッガで起動/アタッチすると、デバッギーが終了していないのにデバッガがデバッギー終了と認識します。
- 『OllyDbg』のようなデバッガが表示する「最後のエラー」情報はデバッギーのどこに格納されているのですか?
- 解析対象プロセスのPEB(Process Environment Block)の情報を取得したい(環境:WindowsXP)
- Windows XP Service Pack 2で実装されるデータ実行防止機能で注意すべき点があるそうですが…
- 『OllyDbg』のセキュリティホールについて教えてください
- 『OllyDbg』の改造版とはどのようなものですか?
- 逆アセンブルが正常に行えず、しかもエントリーポイントがNOP命令というソフトウェアがあるのですが。
- デバッガなどのプログラム解析関連ツールを自作してみたい
- プロセスメモリ上のモジュールエリアを実行ファイルとしてダンプ(ファイルに保存)しても、正常に動作しないのは何故ですか?
- 解析超初心者がデバッガで解析対象を起動する前に行うべきことは?
操作関連
- 『OllyDbg』の操作方法等を解説した参考書はありますか?
- 『OllyDbg』から解析対象を簡単に起動する方法を知りたい。
- 操作上で特に覚えておくと役立つポイントはありますか?
- 『OllyDbg』プラグインの入手先は?
- API関数呼び出しへのブレークポイント設定の仕方を知りたい。
- 条件付きブレークポイントを設定してみたい。
- ウィンドウメッセージにブレークポイントを設定してみたい。
- API関数呼び出し箇所ではない任意の複数コードに一括してブレークポイントを設定したい。
- リアルタイムアセンブラで注意すべきことは?
- メモリ内容の内容変動箇所を調べるには?
- プログラムが参照している文字列の一覧表示機能を使いたい。
- 特定文字列の参照コード一覧表示機能を使いたい。
- 特定のコードにワンタッチで戻る方法が知りたい。
- コール命令からサブルーチン及び、ジャンプ命令からジャンプ先にワンタッチで移動する方法はありませんか?
- F7キーでサブルーチンにステップインした後に、簡単にサブルーチンを抜ける方法はありませんか?
- システムDLL等、デバッギー実行ファイル以外のコードを表示した場合の対処法は?
- 逆アセンブルコードウィンドウ(左上ウィンドウ)でコード変更後の実行ファイルの保存が上手くいきません。
- 「Just-in-time debugging」(ジャスト・イン・タイム デバッグ)とは何ですか?
- OllyDbgでのソースレベルデバッグが上手くいきません
ゲーム解析関連
△予備知識
OllyDbgのWindows Vista上での動作について教えてください。
当サイトQuestions/Suggestionsの「ゲーム解析一般Q&A」を参照してください。逆アセンブルコードリストについて教えて下さい。
デバッガ「OllyDbg」で表示されたり、逆アセンブラ「PeRdr」で出力する、プログラムのコードを逆アセンブルと呼ばれる操作により人間がプログラムの処理の流れを視認できるようにしたものを、「逆アセンブルコードリスト」と呼びます。 OllyDbgが表示しているのは、対象アプリケーションの起動に伴いプロセスメモリ上に読み込まれた、対象アプリケーションのEXEファイルに含まれるプログラムのコードを、プロセスメモリから読み込んで逆アセンブルしたものです。一方、「PeRdr」は実行ファイルを実行されないままファイルからプログラムのコードを読み込んで逆アセンブルしています。 デバッガを用いたPCゲームの解析においては、逆アセンブルコードリストの読解は必須となります。そのため、逆アセンブルコードリストの構成要素である「レジスタ」、「ニーモニック」、「(アセンブリ言語の)命令」、「API関数」等を理解する必要があります。 デバッガを用いたPCゲーム解析を始める前に、ヘキサエディタやプロセスメモリエディタといった基本ツールの操作を理解しておくと、デバッガを用いたPCゲーム解析がよりスムーズに理解できるようになります。そのため、これからPCゲーム解析を始めようという方には、いきなりデバッガから入るのではなく、あらかじめプロセスメモリエディタの操作に慣れておくことを強くお勧めします。 以下では、32ビットアプリケーションの逆アセンブルコードリストの読解を始めるための端緒とすべく、必要最低限の基本事項を簡潔に解説します。あくまでプログラム解析という視点での解説であり、CPUアーキテクチャ全般の解説やプログラミングを目的とする解説ではありません。 以下はPCゲームの開発で一般的に使用されているC/C++言語で開発されたゲームの逆アセンブル例です。今後増えると予想されるC#言語で開発された.NET対応アプリケーションでは以下のような逆アセンブルは行えず、専用の逆コンパイラ等を使用して解析することになります。また、C言語で開発されたゲームでも、プログラムのコードに圧縮や暗号化を施すことで逆アセンブルを困難にしているケースもあります。 これら基本事項の詳細については、当サイトから以下のリンクにて資料や参考となる書籍を紹介していますので、入手・参照されることを強くお勧めします。特に、アセンブリ言語の命令の一覧とその詳細な解説を含む「IA-32 インテル・アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル」および、API関数の一覧とその詳細な解説を含む「マイクロソフト・プラットフォームSDK」は、逆アセンブルコードリストの読解を含むプログラム解析に大いに役立ちます。また、ネット上での検索も積極的に活用することをお勧めします。 逆アセンブルコードリスト読解等プログラム解析に関する各種Q&A 逆アセンブルコードリスト読解等プログラム解析に役立つ各種資料 プログラム解析に役立つ参考書籍 汎用プロセスメモリエディタ兼デバッガ『うさみみハリケーン』のヘルプ内「基礎用語解説」 逆アセンブルコードリストの基本的な読解ができるようになったら、次はデバッガが持つ「ブレークポイント機能」、「ステップ実行機能」、「レジスタ・プロセスメモリ編集機能」、「参照API関数と同呼び出し箇所の一覧表示機能」および「参照文字列一覧表示機能」と、デバッグ時の逆アセンブルコードリストとの関わりを理解されることをお勧めします。これは各種チュートリアルや実際の解析を通しての試行錯誤が、理解する上での近道になると考えられます。 ■逆アセンブルコードリスト出力例 ●OllyDbgでCPUウィンドウに表示 0040158F > 6A 10 PUSH 10 00401591 . E8 86010000 CALL <JMP.&USER32.GetAsyncKeyState> 00401596 . 80FC 80 CMP AH,80 00401599 75 55 JNZ SHORT UsaTest2.004015F0 0040159B B8 01000000 MOV EAX,1 004015A0 8305 34124000 64 ADD DWORD PTR DS:[401234],64 004015A7 813D 34124000 E8030000 CMP DWORD PTR DS:[401234],3E8 004015B1 7E 1D JLE SHORT UsaTest2.004015D0 004015B3 C705 34124000 E8030000 MOV DWORD PTR DS:[401234],3E8 ●PeRdrでファイルに出力 0040158F 6A10 push 10h * Reference to USER32.GetAsyncKeyState | 00401591 E886010000 call 0040171Ch 00401596 80FC80 cmp ah,80h 00401599 7407 jz 004015A2h 0040159B B800000000 mov eax,00000000h 004015A0 8B00 mov eax,[eax] ■逆アセンブルコードの構成要素 ●アドレス 上記の例では「40158F」といった16進数の値。これは、実行されるコードのプロセスメモリ上での番地を指し、32ビットアプリケーションでは4バイトの値となる。EXEファイル等の実行ファイルがプログラムとして実行される際には、まず実行ファイルがシステムにより用意された当該プロセスのためのプロセスメモリ上にロードされ、その実行ファイルに含まれるコードがプロセスメモリ上で実行される。基本的に、EXEファイルがロードされるプロセスメモリ上のアドレスは0x400000が起点となる。また、実行ファイルに付属する、Kernel32.DLL等のシステムファイルではないDLLファイルは基本的にアドレス0x10000000にロードされるが、複数のDLLが使用される場合は別のアドレスにもロードされる。この配置換えをリロケーションと呼ぶ。実行ファイルのおおまかな構造としては、PEヘッダと呼ばれる実行ファイルの基本設定を格納するデータブロックが先頭にあり、その後にセクションと呼ばれる、コードやデータ等の格納内容に応じて区切られたデータブロックが並んでいる。基本的に、実行されるコードが含まれるコードセクションが最初のセクションとなる。 ●バイナリデータ 上記の例では「6A 10」といった16進数で表示されるデータ。これは、プロセスメモリ上にある実行されるコードの実体で、CPUは0か1で指定された2進数のデータ(バイナリデータ)として読み込む。CPUはこのバイナリデータをコードとして直接解釈して対応する処理を実行するため、このコード対応バイナリデータはマシン語(機械語)と呼ばれる。プログラムの処理とは、このマシン語で指定された処理をCPUが1つ1つ順番に実行していくことを意味する。なお、バイナリデータを16進数で表示しているのは人間にとっての視認性や可読性向上のため。 ●ニーモニック 上記の例では「PUSH 10」といった、ニーモニックと呼ばれる英数字文字列。これはマシン語の内容を人間が理解できるよう分かりやすく翻訳したものでアセンブリ言語と呼ばれる。本来アセンブリ言語はマシン語と1対1で対応する処理をニーモニックの形式で記述して実行ファイルを作成(アセンブル)する開発言語であるため、マシン語からアセンブリ言語に翻訳する処理を「逆アセンブル」という。なお、アセンブリ言語で記述されたコードをアセンブルするソフトウェアをアセンブラと呼ぶ。 ●オペコード ニーモニックの最初にある「PUSH」、「MOV」、「CMP」および「J**」といった英字文字列はCPUが行う処理の種類を指すもので「(アセンブリ言語の)命令」と呼ぶ。ニーモニックの構成要素としてはオペコードと呼ばれる。 ●オペランド ニーモニックの中で、演算等、命令の処理対象となる数値やレジスタ(後述)をオペランドと呼ぶ。例として、ニーモニック「MOV EAX,1」においてはEAXレジスタが第1オペランド、数値1が第2オペランドと呼ばれる。オペランドが大括弧[ ]で指定されている場合は、[ ]の中のアドレスに格納された値を意味する。「DWORD PTR [402010]」や単に「[402010]」ならば、アドレス0x402010に格納されたDWORDの値となる。対象となる値のサイズがDWORD以外ならば、「BYTE PTR」や「WORD PTR」で指定する。 ●レジスタ オペランドとして使用される、CPUの内部にある一時記憶用メモリ。一般的な命令に使用される汎用レジスタや、浮動小数点数を用いた演算に使われるものおよび、マシン語レベルでのアプリケーションのデバッグ専用のもの等がある。32ビットCPUにおける汎用レジスタは、EAX、EBX、ECX、EDX、ESI、EDIがあり、他には特殊な用途に用いられるESP、EBP、EIP、EFLAGSがある。32ビットCPUにおける汎用レジスタのサイズは4バイト(32ビット)となるが、汎用レジスタE*Xは、4バイトのうちの2バイトや1バイトを処理対象として使用することもできる(EAXレジスタの下位2バイト=AX、AXの上位1バイト=AH、下位1バイト=AL)。レジスタ名先頭の「E」は16ビットCPUのレジスタとの対比で32ビットへ拡張(Extend)されたことを意味する。 E*Xレジスタは特に演算やポインタ(アドレスを指定するためにレジスタや特定アドレスに格納された値を使うやり方)およびカウンタ等を主用途とする。E*Xレジスタにはそれぞれ本来の用途(EAX:各種演算、EBX:ポインタ、ECX:カウンタ、EDX:データ一時記憶およびEAXとの連携による乗除算)が定められているがその用途に制限される訳ではない。ESIとEDIレジスタは汎用的な使用も可能だが、本来はデータ転送命令等でデータの転送元アドレス(SI:SourceIndex)や転送先アドレス(DI:DestinationIndex)が格納されるもの。ESP(SP:StackPointer)は現在のスタック(後述)のアドレスが格納される。EBP(BP:BasePointer)は基本的にスタック上のデータに対するポインタとして使用される。EIP(IP:InstructionPointer)は、次に実行される命令のアドレスが格納され、命令が実行されるたびに内容が更新される。 ●フラグ 演算命令や比較命令の実行後、演算結果がゼロか否かや、比較結果での大きいか否かなどを意味するフラグがビット単位の1か0でEFLAGSレジスタに格納される。EFLAGSレジスタにはゼロか否かなど特定条件に対応するビットがあらかじめ指定してあり、条件に合致したらフラグをセット(該当ビットに1を設定)、条件に合致しないならばフラグをクリア(該当ビットに0を設定)する。このフラグの状況を元にジャンプ命令を使ってプログラムの処理を条件分岐させることが可能になる。EFLAGSレジスタ内のこのようなフラグをステータス・フラグと呼ぶ。 ●数値 逆アセンブルコードリスト上では、アドレスやオペランドとして使用される数値は16進数で表示される。なお、一般的に16進数であることを明示する場合は数値の後に「h」を付加するか、数値の前に「0x」を付加することで行う。 ●スタック プログラムが一時的にデータを格納するメモリ領域。スタック内で新たにデータを格納するアドレスは常にESPレジスタに格納されており、プログラム側でアドレスを直接指定する必要がない。スタックにデータを格納する際はPUSH命令を用い、スタック内では、アドレス0x10FFF8にデータを格納したら次はアドレス0x10FFF4というように、アドレスの高い方から低い方へ向かって、まるで積み木を積むようにデータが格納されるため、これをスタックに積むという。スタックからデータを取り出す際にはPOP命令が用いられ、ESPレジスタのスタックポインタを用いるスタックのデータ格納方式により、後に積まれたデータが先に取り出されることになる。PUSH/POP命令実行時には自動的にESPレジスタの格納値が修正される。 また、汎用レジスタの内容をスタックに格納/取り出しするPUSHAD/POPAD命令や、EFLAGSレジスタの内容をスタックに格納/取り出しするPUSHFD/POPFD命令もある。 ●API関数 上記の例では「USER32.GetAsyncKeyState」。API関数は、アプリケーションがOSの機能を容易に使用することを可能にする、API(Application Programming Interface)の1要素。この例では、WindowsOSのシステムDLLであるUser32.dllが提供する、特定のキーが押されているか否かを判断する機能を持つGetAsyncKeyState関数を呼び出している。API関数の呼び出しにあたり、関数の処理を実行するために必要なパラメータ(引数:ひきすう)を指定する場合は、PUSH命令で数値そのものあるいはデータが格納されたアドレス等をスタックに積むことで指定する。上記の例では10hがShiftキーを意味する。 また、API関数の多くは、関数の処理が実行されると、関数の処理結果や、関数が成功したか否か等を意味する値(戻り値)がEAXレジスタに格納される。なお、NT系OSが内部的に用いる基本的に非公開のAPI関数を「ネイティブAPI」と呼ぶ。ネイティブAPIはユーザー側(ユーザーモード)とシステム側(カーネルモード)の間で処理の橋渡しを行う役目を持つ。 API関数はそれぞれが特定の機能を持つため、例えばCD/DVDチェックの解析においては逆アセンブルコードリスト上で同チェックに頻用されるAPI関数の呼び出しを探す等、PCゲーム解析において解析の手がかりとなることが少なくない。どのようなケースにどのようなAPI関数が使われるかは、当サイトのチュートリアルやQ&Aなどで解説しているので、参照されることをお勧めする。 ●<参考> プログラム解析において頻出する基本的な命令
|
ブレークポイントの種類とその違いについて教えて下さい。
デバッガがブレークポイントでのブレークを検出するためには、デバッギーに 「例外」 と呼ばれる通常のプログラムの処理では起こらない(筈の)特殊な状況を起こさせる必要があります。デバッギーに例外が生じた時点でデバッギーの制御はデバッガに移るため、デバッガはこの時点でデバッギーのスレッドにおける汎用レジスタやステータス制御レジスタの値等を取得・変更可能です。分かりやすく言えば、ブレークポイントの違いはデバッガが検出する 「例外」 の種類の違いです。また、この違いに応じてデバッギーへの異なる解析アプローチを行うことが可能になります。1.コード実行 ブレークポイント
これは、メモリ上に展開・実行されるコード中の任意の箇所のニーモニック先頭アドレス(1バイト)を、「INT3」(オペコードはCC)命令に書き換えるものです。この割り込み命令は実行するとデバッグ例外ハンドラを呼び出す特殊なもので、この呼び出しをデバッガ側で例外として検出します。デバッガはブレーク時に「INT3」で書き換えた箇所を一時的にオリジナルの命令に書き直して、オリジナルの命令を実行させます。
2.メモリアクセス ブレークポイント
これは、デバッギーの任意のメモリブロックのアクセス属性を読み(書き)不可に変更することで、デバッギーがそのメモリブロック内のアドレスに読み(書き)操作を行った際にアクセス違反を生じさせ、このアクセス違反をデバッガ側で例外として検出します。このブレークポイントはメモリブロックのアクセス属性を本来ありえない属性に変更するため、デバッギーをクラッシュさせることがあります。このブレークポイント設定によるアクセス違反と、デバッギーのバグ等によるアクセス違反は似て非なるものです。後者の場合は、基本的にデバッガはEIP該当ニーモニックを無効化あるいはEIPの値を次のニーモニック先頭のアドレスに書き換えて例外を処理しないと、例外から抜け出すことができません。ただし、アクセス違反によっては、その例外を無視して処理を続行可能なケースもあります。
3.ハードウェア ブレークポイント
「Intel 80386」以降のCPUに実装されているデバッグレジスタを使用して、コード実行及びメモリアクセス(読み書き)でブレークさせるものです。上記1及び2と異なり、デバッギーのコードやメモリブロックのアクセス属性は変更しません。ハードウェアブレークポイントはデバッギーのプロセスが持つスレッド毎に設定を行いますが、Windows95では自動的に全スレッドに一括設定されます(『OllyDbg』はデフォルトで全スレッドに一括設定)。デバッグレジスタ8つ(DR0〜DR7)のうち、ハードウェアブレークポイントの設定には最高5つを使用し、4箇所までブレークポイントを設定可能です。ブレークポイントの設定単位は1,2,4バイトですが、安全性を考えると1バイトでの設定が適切と思われます。
上記3種のブレークポイントについては、いずれもデバッギー側で設定されたことを検出可能です。また、ブレークポイントを設定されていなくとも、デバッギーは自分がデバッグされていることを容易に検出できます。つまり、ソフトウェア製作者はリバースエンジニアリング対策として、デバッガから起動・アタッチ或いはブレークポイントを設定された時点で実行される、何らかのリバースエンジニア向けの処理をソフトウェアに仕込むことも可能です(注:この様なケースではJITデバッガによるアタッチは考慮していない)。
デバッグ中にシステムDLL内でアクセス違反が生じるのはOSの問題ですか?
OSの不具合である可能性もありますが、大抵の場合はデバッギー側でのAPI関数呼び出し時の引数に原因があります。引数の指定内容(特に構造体のメンバ変数)に注意して下さい。ただし、プログラマがソフトウェア動作上で問題が無いと判断した上で、引数が不適切になるケースに対処していないこともあるため、デバッグ時のみ表面化するようなアクセス違反をソフトウェアのバグとは断定できません。デバッギーからデタッチすることはできませんか?
WindowsOSでは、基本的にデバッガが一旦デバッギーにアタッチすると、デタッチしてデバッギーのデバッグをOSに引き継ぐことはできません。そのため、デバッグ中にデバッガを終了させると、デバッギーは例外等への対処が不可能になるため必ず終了します。これを利用すれば、Windows2000/XPでのサービスプログラム等、特殊な権限を持ち通常は強制終了不可のソフトウェアでも強制終了が可能です(アタッチしてデバッガごと終了)。ただし、サービスプログラム等の強制終了はシステムに悪影響を与える可能性があるため、十分な注意が必要です。
なお、Windows XPで実装されたDebugSetProcessKillOnExit関数を用いれば、デバッガ終了時にデバッギーを終了させないことが可能です。ただし、このAPI関数はデバッガのスレッドとデバッギーのプロセスを切り離すものであり、デバッガのスレッドのうち、CreateProcess関数あるいはDebugActiveProcess関数でデバッギーと接続したスレッドのみがデタッチ処理対象です。つまり、デバッガがマルチスレッドで動作し、デバッガの機能とは関係ないスレッドがあれば、DebugSetProcessKillOnExit関数呼び出しでも完全なデタッチはできません。
関連して、デバッガにデバッグを停止させる、Windows XPで実装されたDebugActiveProcessStopというAPI関数もあります。なお、デバッギー側でネイティブAPIのZwSetInformationThread関数を用いてデバッガからデタッチを行う方法は、デタッチ後の例外処理の面で安全性に問題があります。
デバッギーのエントリーポイントがコードセクション内ならウイルスには感染していませんか?
ウイルスによってはコードセクションの空き領域にコードを埋め込むこともあり、この場合エントリーポイントはコードセクション内になります。ソフトウェアの入手先の信頼度等に関わらず、解析前にウイルスチェックは必ず行ってください。ウイルス対策としてのデバッグ検出について教えて下さい
実行ファイルを感染対象とするウイルスとしては、実行ファイルを書き換えてコードを埋め込むタイプがよく知られていますが、一部のウイルスには実行されているプロセスの制御を奪い、そのプロセスメモリに書き込んだウイルスのコードを実行させて動的に感染・破壊活動を行うものもあります。このプロセスの制御を奪う手法は複数ありますが、その内の一つに、デバッガとしてターゲットプロセスにアタッチする手法があります。そのため、ソフトウェア開発者によっては、自作ソフトの動的ウイルス感染に対処するために、デバッグされていることを検出する機能を自作ソフトに実装するケースがあります。一般的に、フリーウェアでデバッグ検出機能が実装されている場合は、対ウイルスの自己防御目的と考えて差し支えないと思われます。
デバッギーのウイルス感染やバグによるレジストリ破壊等に備えて、完全に独立した環境で解析を行いたい。
私の場合はPC/AT互換機エミュレータ(仮想マシン構築ソフト)の『VMware Workstation』(英語版)(日本語版)を使用しています。『VMware』ではセッションで行った全ての操作を元に戻す機能があるため、常にOSクリーンインストール直後の状態で解析することが可能です。なお、私が仮想マシン(ゲストOS:WindowsMe/2000)で試した限りでは、『OllyDbg』は問題なく動作します。通常の解析においてはVer4.xの旧バージョンでも支障はありません。また、同種のソフトとして『Virtual PC』や『Microsoft Virtual Server 2005 R2』もあります。
デバッギー側から仮想マシン構築ソフト上で実行されていることを検出することは、『VMware』と『Virtual PC』の一部のバージョンにおいては可能です。この場合、『VMware』ならばIN命令を使った『VMware』特有のポートの有無による検出か、「VMwareService.exe」等『VMware』特有のプロセスの有無による検出が想定されます。『Virtual PC』ならば、例外ハンドラを構築した上で『Virtual PC』が使用する特殊な命令でオペコード「0F3F070B」(x86としては不正な命令)を実行させた結果を元に検出が可能なケースもあります。
なお、『Virtual PC』以外でも、『VMware Workstation』開発元が公開している、『VMware Workstation』などで作成された仮想マシンを実行するフリーウェア『VMware Player』を用いることにより、仮想マシンを用いた解析環境を無償で構築可能です。『VMware Player』自体に仮想マシン作成機能はありませんが、PCエミュレータ『QEMU』を使用する等の方法で仮想マシンを作成することができます。この場合、仮想マシン作成方法についてはGoogle等検索サイトで「VMware Player QEMU Windows」などの検索結果を参照して下さい。
基本的に、仮想マシン構築ソフトのゲストOSには、自分が所有するOSを使用します。しかし、開発者のソフトウェア互換性確認用に配布されている、『Virtual PC』用のゲストOSイメージを使用することも可能です。
Internet Explorer Application Compatibility VPC Image(英語版)
『VMware Workstation』や『VMware Player』を用いた、ゲストOS上で実行されているアプリケーションをホストOS上のVMwareプロセスごと解析するアプローチについては、『「VMware」を用いたフルスクリーン型ゲームへの対処』を参照して下さい。
関連して、対象アプリケーション実行時のファイル・レジストリ書き換えなどを監視し、行われるシステムへの変更内容を仮想化することで、システムを同アプリケーション実行前の状態に復元可能なタイプのソフトも使用可能です。ただし、このようなソフトは、実行対象がマルウェアの場合に対抗策を講じられ、システムを元の状態に復元できなくなるケースも考えられます。解析対象アプリケーションの安全性が確認できないならば、仮想マシン構築ソフトを使用されることをお勧めします。
解析時にデバッギーが行ったファイル・レジストリ操作の結果だけを簡単に確認したい。
このようなアプローチでは、任意のタイミングでファイルやレジストリのスナップショットを作成し、異なるタイミングで作成した2つのスナップショットを比較することで、ファイルおよびレジストリの変更カ所を表示するソフトを使用します。私の場合は『FindSet』Ver1.6を使用しています。また、『SystemExplorer』が同種の機能を実装しています。レジストリへの操作を監視することに特化した『レジストリ番犬ロン』など、他にも同種のソフトがあります。なお、リアルタイムでファイル・レジストリ操作を監視したい場合は、『Process Monitor』あるいは『Filemon』・『Regmon』を使用してください。
デバッガ検出手法を教えてください。
代表的なデバッガ検出手法としては、カーネルモードデバッガ検出専用のものを含めて約20種類程が知られています。そのうち、プログラマが簡単に思いつくような検出例は以下。実際には、下の初歩的な検出手法をそのまま使用することは少ないと考えられます。ちなみに、ウイルスにはデバッガ対策を施しているケースが少なくないため、そのソースコードで実践的なデバッガ検出手法を学んでいるリバースエンジニアの方も実在します。1.IsDebuggerPresent関数
このWindows98で実装されたAPI関数を使えば、ソフトウェアは自分がデバッグされているか簡単に知ることが出来ます。この関数の戻り値がゼロでなければデバッグされています。Windows95対応ソフトでは、この関数の呼び出しにDLL動的ロードまたはモジュールハンドル取得に加え関数アドレスの取得が必要です。この検出手法は関数呼び出しが偽装されない限り、解析時に容易に発見可能です。 なお、WindowsXPサービスパック1で実装された、CheckRemoteDebuggerPresent関数で特定プロセスがデバッグされているか調べることも可能です。
IsDebuggerPresent関数を使用せずに、自分でプロセス関連情報を取得してデバッグされていることを検出することは可能ではありますが、OS毎に対応コードを用意しなければならないことや、外部アプリケーションから被デバッグ検出を簡単に無効化される等の問題があり、実装工数対効果の面で現実性に欠けます。なお、NT系OSでは、このプロセス関連情報を簡易に取得可能にするネイティブAPIがあります。
2.仮想デバイスドライバ検出
SoftICEやTRW2000の検出に使用されます。仮想デバイスドライバを、CreateFile関数でパス解析オフにして検出するものです。 仮想デバイスドライバの指定は "\\\\.\\仮想デバイスドライバ名"という形で行います。
BOOL IsDebuggerLoaded() { HANDLE hFile; hFile = CreateFile("パス解析オフの仮想デバイスドライバ名", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); return TRUE; } return FALSE; }
実行ファイル中で参照される仮想デバイスドライバ名は以下。参考までにFilemon・Regmon分も併記しました。
\\.\TRW
\\.\SICE
\\.\NTICE
\\.\FILEVXD
\\.\FILEMON
\\.\REGVXD
\\.\REGMON
3.ウィンドウクラス名取得
OllyDbgは、そのウィンドウクラス名が「OLLYDBG」となっているため、FindWindow関数(Window名はNULL)か、EnumWindows関数とGetClassName関数併用で検出が可能です。
4.INT3サーチ
ソフトウェアがプロセスメモリのモジュールエリアにあるコードセクションを、タイマ制御等で定期的に自分でメモリサーチして、バイナリデータがINT3(0xCC)となっている箇所の数をカウントし、実行ファイル中に保存しておいたオリジナルのカウント値と比較することで、INT3によるブレークポイント設定を検出します。同様に、 INT3はコードセクションのチェックサム(単純加算・XOR・CRC-32・MD5等)生成による自己改竄検出にもヒットします。
メモリサーチやチェックサム生成を行う以上、コードセクションへの読み込みメモリアクセスが発生する点に注目してください。
5.特定メモリエリアのアクセス属性取得
VirtualQuery関数を用いて自己プロセス内の特定メモリエリアのアクセス属性を取得し、本来ありえない属性になっている場合はメモリアクセスのブレークポイントが設定されている(あるいは何らかのメモリ改竄)と判断します。また、自己プロセスに対してWriteProcessMemory関数を使用してアクセス属性の異変を探ることも可能です。
6.構造化例外ハンドラを用いたデバッグ検出
この検出手法を使用する場合、逆アセンブルコードリスト上には構造化例外ハンドラの設定や解除のため「MOV EAX,DWORD PTR FS:[0]」といった「FS:[0]」を含むコードが現れます。
// C++ void CDebugTest::IsDebuggerWithSEH() { __try { //DebugBreak関数も使用可能だがあからさま過ぎる //EXCEPTION_SINGLE_STEPも可 RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL); } __except(GetExceptionCode() == EXCEPTION_BREAKPOINT ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { MessageBox("No debugger is attached.","Info",MB_OK); return; } //PEBのメンバ変数BeingDebuggedには依存しない MessageBox("Debugger is attached!!","Info",MB_OK); }7.GetThreadContext関数を用いたハードウェアブレークポイント検出
デバッグレジスタではなく汎用レジスタならばインラインアセンブラを用いることで簡単に取得可能です。
// C++ void CDebugTest::IsDebuggerWithContext() { HANDLE hThead = GetCurrentThread(); CONTEXT ct; ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; if(GetThreadContext(hThead, &ct)){ //実際はDr0からDr3までチェック //デバッグレジスタはシステムが使用することもある //そのため0か否かでチェックしてはいけない //このアドレス範囲はあくまで目安 if( (ct.Dr0 < 0x3F000000) && (ct.Dr0 >= 0x10000) ){ MessageBox("HBP(Dr0) is active!!","Info",MB_OK); //デバッグレジスタのクリア ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; ct.Dr0 = 0; SetThreadContext(hThead, &ct); } else MessageBox("HBP(Dr0) is inactive.","Info",MB_OK); } }8.ネイティブAPIのNtQueryInformationProcess関数を使用したデバッグ検出
この関数やZwQuerySystemInformation関数他のネイティブAPIでデバッグを検出可能ですが、これらの関数は将来的に互換性が失われる可能性があるため、一般的なアプリケーションへ実装される可能性は低いと考えられます。
// C++ //この手法はWin9x/Meでは不可 void CDebugTest::IsDebuggerWithDebugPort() { //CheckRemoteDebuggerPresent関数と同様の処理 HANDLE hProcess = GetCurrentProcess(); enum PROCESS_INFO_CLASS { ProcessDebugPort = 7 }; HANDLE hPort = 0; if(NT_SUCCESS(NtQueryInformationProcess (hProcess, ProcessDebugPort, &hPort, sizeof(hPort), NULL)) ){ if(hPort) //PEBのメンバ変数BeingDebuggedには依存しない MessageBox("Debugger is attached!!","Info",MB_OK); else MessageBox("No debugger is attached.","Info",MB_OK); } }9.INT命令によるデバッガ検出
INT3h(BCHK検出)、INT2Fh、INT41h、INT68h等を用います。デバッガ検出手法として必ずしも信頼性の高いものではありません。
INT命令を用いたデバッガ検出が上手くいきません(環境:WindowsXP)。
INT3h、INT2Fh、INT41h、INT68hを用いたデバッガ検出手法はOSのバージョンやその他の条件に依存するため、一般的なソフトウェアへの実装はお奨めしません。OllyDbgのプロセスを検出して解析を妨害するタイプのウイルスを解析したい
基本的なOllyDbgのプロセス検出手法としては、Windows上で実行されているプロセスの一覧を取得して、各プロセス名のチェックによりOllyDbgが起動されているかを判断します。API関数を用いてプロセス一覧を取得する方法は5種類ありますが、通常は以下の3つの方法のいずれかを使用します。・Process32First(Next)関数等のTool Help APIを使用
・EnumProcesses関数等のProcess status API(PSAPI)を使用
・OSが保持するパフォーマンスデータからレジストリの不可視キーとして取得
最も簡単なプロセス検出回避アプローチとしては、Windowsが把握するプロセス名はプロセスの実行ファイル名となるため、単純にOllyDbgの実行ファイル名を別のものに変更すれば、起動時にエラーメッセージは出るもののプロセス名も変更されOllyDbgのプロセスは検出されなくなります。
しかし、全プロセスのプロセスメモリ上のモジュールエリアで特定文字列を検索するケースなど、詳細なOllyDbgプロセス検出を行っており、かつウイルスがプロセス検出ルーチンの無効化を阻止する強固な改竄対策を実装している場合には、別のアプローチを行う必要があります。
このようなケースでは、rootkitと呼ばれるツールを用いて、Windowsが保持するプロセスのリストを改変したりプロセス一覧取得用のAPI関数を操作することで、OllyDbgのプロセスをWindows上で隠蔽してからウイルスの解析を行うというアプローチが有効と考えられます。任意のプロセスの隠蔽機能を持つrootkitは以下の2つなどが挙げられます。なお、プロセスの隠蔽はセキュリティ上深刻な問題を招きかねないため、セキュリティソフトによってはこのようなrootkit自体をウイルスやトロイの一種と判断することがあります。下記rootkitは、これらがどのようなツールであるかを十分理解した上で、必ず自己の責任において入手と使用を行って下さい。
・FU rootkit(DKOM: Direct Kernel Object Manipulation)
・Redkod Rootkit(Ring3ベースAPIフック)
なお、拙作の汎用プロセスメモリエディタ兼デバッガ『うさみみハリケーン』では、FU rootkitのようなDKOMで隠蔽されたプロセスのウィンドウ情報を元にプロセスIDを取得して、隠蔽されていないプロセス同様に解析することが可能です。また、『うさみみハリケーン』のパフォーマンスデータからプロセス一覧を取得する機能を使用すれば、Redkod Rootkitが用いるようなAPIフックで隠蔽されたプロセスでも、プロセスIDを取得・解析することができます。さらに、『うさみみハリケーン』Ver0.08では、プロセスメモリ上のKernel32.DLL・Ntdll.DLLモジュール書き替えや、カーネルスペースでの Service Descriptor Table (SDT) 書き替えによるAPIフックの解除機能を実装しており、rootkit等が実装しているAPIフックを用いた各種解析対策に対処可能なケースもあります。
ちなみに、ウイルスの一般的な解析アプローチについては、以下のページが参考になります。
「シマンテック、日本におけるウイルス解析ルームを公開」
星澤裕二のSecurityWatch : マルウエアを解析する(その1〜)
OS上でデバッガが動作したことを検出する方法を教えてください
この場合は、OS上で実行されている全てのプロセスで、APIフックを用いてデバッガに関連するAPI関数(DebugActiveProcess関数他)の呼び出しを監視します。APIフックにより、デバッガの動作に限らず、プロセスのオープンや強制終了、プロセスメモリの読み書き、プロセスやスレッドの作成他、API関数を使用する色々な処理を監視可能です。監視だけに止まらず、引数の操作等によるAPI関数呼び出しの無効化もできます。APIフック全般についてはケンジさんのサイト『KENJI'S HOMEPAGE』や彼の著書『ハッカー・プログラミング大全 攻撃編』が参考になります。他にもネット上でAPIフックに関する資料を見つけることもできます。
APIフックに関して、Windows2000/XP以降を動作環境とする一部のセキュリティソフトやトロイ等では、監視するプロセスの実行ファイルにパッカーが使用されているケース等に対処するために、監視するプロセスのメインモジュールのIATではなく、スタブDLL(各プロセスに読み込まれたシステムDLLのコピー)内にあるAPI関数の開始アドレス以降のバイナリデータを書き換えて、各プロセスにロードさせた監視用DLL内の処理にジャンプさせています。Windows2000/XP/Vistaにおいては全てのスタブDLLがアプリケーション有効アドレス範囲内に収まるため、このような書き換えが可能になります。Windows9xではKernel32.DLLなど一部のシステムDLLのモジュールアドレスはアプリケーション有効アドレス範囲外となるため、一般的なアプリケーションからそのシステムDLLに属するアドレスのバイナリデータは読み込みは可能ですが書き換えはできません。なお、このAPIフック手法において、API関数呼び出しを監視するためのコードは、監視用DLL内ではなくフック対象プロセスの仮想アドレス空間に書き込んだものを使用するという方法もあります。
もしこのAPIフックを行うアプリケーション側が逆アセンブラを実装している場合は、監視対象プロセスにおけるAPI関数の開始アドレスではなく、API関数の開始アドレスから4つめのニーモニックの先頭アドレスを書き換えて、監視用DLL内の処理にジャンプさせるといったことも可能です。この場合は監視用DLL内の処理でPUSHAD/POPAD+PUSHFD/POPFD命令を使って処理の安全性を高めると考えられます。必ずしもAPI関数の開始アドレス直後を書き換える必要はないことに注意して下さい。
ユーザーモードでのAPIフックを用いたOS上でのデバッガ動作検出は、どのような状況でも完璧に検出できる訳ではありません。基本的に、Windows9x環境でかつデバッガがパッカーで圧縮/暗号化されプロセスメモリ上でのIATの解析が必要なケースでは、このデバッガ動作検出は難しくなると考えられます。また、API関数の開始アドレス以降のバイナリデータを書き換える検出手法では、デバッガ側がデバッガ関連各API関数の開始アドレス以降のバイナリデータ16バイト程度を、『あらかじめAPIフックされていないクリーンな状況で取得しておいた』オリジナルのバイナリデータを用いて高頻度で書き戻すことにより、APIフックによるデバッガ動作検出を無効化させることも可能です。
ちなみに、一部のセキュリティソフトやトロイ等は、ユーザーモードでのAPIフックではなく、カーネルモードでドライバからSDT(Service Descriptor Table)を同ドライバ内の関数に繋がるよう書き換えています。この場合は、『Win2K/XP SDT Restore』を用いてSDTを書き戻すことで無効化が可能です。なお、x64用の64ビット版Windowsでは、その仕様上このようなSDT(およびIDT、GDT)の書き換えは行えません。
●<参考>セキュリティソフトのAPIフック対象リスト例(一部を抜粋)
[kernel32.dll]
CreateProcessInternalW
DebugActiveProcess
GetProcAddress
LoadLibraryExW
MapViewOfFile
MapViewOfFileEx
MoveFileW
OpenProcess
ReadProcessMemory
VirtualProtect
VirtualProtectEx
WriteProcessMemory
[user32.dll]
GetWindowThreadProcessId
PostMessageA
PostMessageW
SendInput
SendMessageA
SendMessageW
SetCursorPos
SetWindowsHookExA
SetWindowsHookExW
keybd_event
mouse_event
[ntdll.dll]
NtOpenProcess
NtQuerySystemInformation
NtSuspendThread
NtTerminateThread
RtlGetNativeSystemInformation
ZwOpenProcess
ZwQuerySystemInformation
ZwReadVirtualMemory
ZwSuspendThread
ZwTerminateThread
ZwWriteVirtualMemory
アタッチしてブレークポイントを設定すると、ブレークポイントが機能しない上に強制終了するソフトウェアがあるのですが。
実物を見ていないのであくまで推測ですが、構造化例外ハンドラ (SEH: Structured Exception Handler) を用いたデバッガ対策である可能性があります。逆アセンブルコードリスト上で、構造化例外ハンドラに特有のセグメントレジスタへのアクセスを追いかけてみてください。 なお、解析者が逆アセンブルコードリスト上で「FS:[0]」を検索することを想定して、意図的に「FS:[初期化した汎用レジスタ]」としているケースもありますので注意が必要です。デバッガで起動/アタッチすると、デバッギーが終了していないのにデバッガがデバッギー終了と認識します。
まれなケースですが、デバッガのデバッグアプローチの方法とデバッギーのデバッガ対策の組み合わせにより、この様な状況が生じることがあります。デバッガでのアプローチ手法を変えてみてください。『OllyDbg』のようなデバッガが表示する「最後のエラー」情報はデバッギーのどこに格納されているのですか?
NT系OSでは「Thread Environment Block (TEB)」、9x系OSでは「Thread Information Block (TIB)」と呼ばれる、スレッドのローカル格納領域です。このデータブロックの先頭アドレスはセグメントレジスタを用いて、「FS:[18]」で取得することができます。『OllyDbg』が表示する「最後のエラー」のエラーコードは「FS:[34]」(Win2k/XP/Vista)または「FS:[60]」(Win9x/Me)の値です。ただし、このエラーコードの値がGetLastError関数の戻り値と一致しないケースもあります。特にWindowsの一部のバージョンでのみGetLastError関数でエラーコードを取得可能なAPI関数に注意して下さい。なお、『スペシャルねこまんま57号』にはエラーコードを日本語の説明文に変換する機能を実装しています(「アプリ制御」ウィンドウ内)。
ちなみに、デバッギーのメインスレッドで「FS:[04]」と「FS:[08]」の値からスタックエリアを特定することもできます。
解析対象プロセスのPEB(Process Environment Block)の情報を取得したい(環境:WindowsXP)
システムが使用する、特定プロセスに関する情報を格納する構造体PEB(Process Environment Block)については、WindowsXP SP2で仕様が変更されたため、NT系OSにおいて必ずしも従来のように各プロセスの特定アドレス(0x7FFDF000)以降にあるとは限りません。PEBのアドレスは、ネイティブAPIであるNtQueryInformationProcess関数を使用することで取得可能ですが、プラットフォームSDK等の資料ではこの関数の仕様が将来的に変更される可能性を示唆しており、プログラム解析ツールへの実装は注意が必要です。
他のアプローチとしては、VirtualAllocEx関数、WriteProcessMemory関数およびCreateRemoteThread関数を用いて解析対象プロセスのスレッドとして以下の様なアセンブルコードを実行させることで、PEBのアドレスが格納されるFS:[30]の値を出力させ、ReadProcessMemory関数でその値を取得することも可能です。
●参考<PEBのアドレス取得例>
動作確認環境:WindowsXP 32bit SP2 (Windows9x/Meでは動作しません)
注意:アドレス403000は解析対象プロセス内に確保したメモリエリアのアドレス例
;TEB(Thread Environment Block)のアドレス+30hのDWORDの値 ;MOV EAX,FS:[30] 00403000-648B0530000000 ;MOV [403020],EAX 00403007-890520304000 ;RETN 0040300D-C3 ;TEBのアドレスFS:[18]を使用する例 ;プラットフォームSDKのwinternl.h内サンプルはこの方法を使用 ;MOV EAX,FS:[18] 00403000-648B0518000000 ;MOV EAX,[EAX+30] 00403007-8B8030000000 ;MOV [403050],EAX 0040300D-890550304000 ;RETN 00403013-C3●参考<IsDebuggerPresent関数無効化>
PEBのアドレス取得に伴い、IsDebuggerPresent関数がTEB経由で参照する、PEB構造体に含まれるプロセスの被デバッグ情報を指すメンバ変数BeingDebuggedの書き替え(1→0)により、同関数によるデバッグ検出の無効化が可能になります。
;MOV EAX,FS:[30] 00403000-648B0530000000 ;MOV BYTE PTR [EAX+2],0 00403007-C6800200000000 ;RETN 0040300E-C3●参考<OSバージョン偽装>
PEBのアドレス取得に伴い、解析対象アプリケーションがGetVersionEx関数で取得するWindowsのバージョンを偽装することも可能です。
;MOV EAX,FS:[18] 00403000-648B0518000000 ;MOV EAX,[EAX+30] 00403007-8B8030000000 ;Windows XP ;MOV DWORD PTR [EAX+A4],5 ;OSMajorVersion 0040300D-C780A400000005000000 ;MOV DWORD PTR [EAX+A8],1 ;OSMinorVersion 00403017-C780A800000001000000 ;MOV DWORD PTR [EAX+B0],2 ;OSPlatformId->定数VER_PLATFORM_WIN32_NT 00403021-C780B000000002000000 ;RETN 0040302B-C3●参考<PEB構造体>
注意:以下は公式の情報ではない上、Windows Vista以降でメンバが変更される可能性があります。あくまで参考程度として下さい。
typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN Spare; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PVOID FastPebLock; PPEBLOCKROUTINE FastPebLockRoutine; PPEBLOCKROUTINE FastPebUnlockRoutine; ULONG EnvironmentUpdateCount; PPVOID KernelCallbackTable; PVOID EventLogSection; PVOID EventLog; PPEB_FREE_BLOCK FreeList; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[0x2]; PVOID ReadOnlySharedMemoryBase; PVOID ReadOnlySharedMemoryHeap; PPVOID ReadOnlyStaticServerData; PVOID AnsiCodePageData; PVOID OemCodePageData; PVOID UnicodeCaseTableData; ULONG NumberOfProcessors; ULONG NtGlobalFlag; BYTE Spare2[0x4]; LARGE_INTEGER CriticalSectionTimeout; ULONG HeapSegmentReserve; ULONG HeapSegmentCommit; ULONG HeapDeCommitTotalFreeThreshold; ULONG HeapDeCommitFreeBlockThreshold; ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; PPVOID *ProcessHeaps; PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; PVOID GdiDCAttributeList; PVOID LoaderLock; ULONG OSMajorVersion; //+A4h ULONG OSMinorVersion; //+A8h ULONG OSBuildNumber; //Word*2 ULONG OSPlatformId; //+B0h ULONG ImageSubSystem; ULONG ImageSubSystemMajorVersion; ULONG ImageSubSystemMinorVersion; ULONG GdiHandleBuffer[0x22]; ULONG PostProcessInitRoutine; ULONG TlsExpansionBitmap; BYTE TlsExpansionBitmapBits[0x80]; ULONG SessionId; } PEB, *PPEB;関連して、解析ツールがGetThreadContext関数を用いて解析対象プロセスに属するスレッドのセグメントレジスタを取得済みである場合は、GetThreadSelectorEntry関数でTEBのアドレスを取得して、そこからプロセスとスレッドの各種情報を参照することもできます。また、WindowsXPのSP1以前やWindows2000であれば、メインスレッドのTEBのアドレスに+1000hすることでPEBのアドレスになります。
LDT_ENTRY ldt = { 0 }; GetThreadSelectorEntry(hThread, ct.SegFs, &ldt); DWORD dwTebPtr = (ldt.HighWord.Bytes.BaseHi << 24) | (ldt.HighWord.Bytes.BaseMid << 16) | (ldt.BaseLow) + 0x18; DWORD dwReadBytes = 0, dwTEBAddr = 0; ReadProcessMemory(hProcess, (LPBYTE)dwTebPtr, &dwTEBAddr, sizeof dwTEBAddr, &dwReadBytes);
Windows XP Service Pack 2で実装されるデータ実行防止機能で注意すべき点があるそうですが…
このデータ実行防止(DEP:Data Execution Prevention)機能により、コードが実行されるメモリエリアは、必ずアクセス属性として実行属性が必要になります。例えば、パラサイトルーチンを埋め込むメモリエリアとして、コードセクション以外のセクションの空き領域を使用する場合、そのセクションを含むメモリエリアにはアクセス属性として実行属性が必須となります。また、パッカーによりパックされたアプリケーションが、起動時に実行する解凍/復号化ルーチンを格納したセクションも同様です。十分な検証は行っていませんが、 このようなケースでは、PEヘッダで指定された当該セクションのCharacteristicsとして実行属性IMAGE_SCN_MEM_EXECUTE(0x20000000)を論理和で追加設定することで対処可能と考えられます。なお、「スペシャルねこまんま57号」のPEファイル再構築機能を使えば、実行及び読み書き属性を持つ任意の名前とサイズのセクションを実行ファイルに追加することができます。モジュールエリア上の実行属性を持つセクション以外のメモリエリアでは、コードを実行するためにはVirtualAlloc関数によるメモリ割り当て時にPAGE_EXECUTE等の実行属性を付加する必要があります。現在Microsoft社が公開しているDEP関連資料では、VirtualAllocEx関数による他プロセス上でのメモリ割り当てや、すでに割り当てられたメモリエリアのアクセス属性変更については触れられていません。なお、既定プロセスヒープやスタックエリア上でのコード実行は、原則不可となります。
データ実行防止機能が動作していれば、実行属性の無いメモリエリアでコードが実行された場合、例外としては従来のメモリアクセス違反同様にSTATUS_ACCESS_VIOLATION (0xC0000005)が発生します。しかし、従来のメモリアクセス違反と異なり、例外ハンドラのサーチ無しにプロセスが終了されることに注意が必要です。つまり、デバッガを用いてもこのデータ実行防止機能により発生した例外を動的に無効化することはできません。
このOSのデータ実行防止機能は使用プロセッサのアーキテクチャに依存するため、現状ではごく一部のプロセッサ上でのみ動作しますが、将来的には標準的な機能となると考えられます。
なお、このデータ実行防止機能にはアプリケーションを指定して無効化するオプションが実装されています。データ実行防止機能によりプロセスが終了される際の警告ダイアログから、あるいはコントロールパネル→「システム」→「詳細設定」タブ→「パフォーマンス」欄の「設定」ボタン→「データ実行防止」タブでデータ実行防止機能の設定が可能になります。
『OllyDbg』のセキュリティホールについて教えてください
OllyDbgの1.10及び過去のいくつかのバージョンでは、デバッギーがOutputDebugString関数の第一引数に「%s」や「%08X」等の書式指定文字列を含んだ文字列を指定した場合に、その出力文字列をそのまま表示せず、wsprintf関数を実行するように、スタックの内容を元にその書式指定文字列を変換して表示するというバグがあります。このため、悪意のあるデバッギーが、書式指定文字列を大量に含む文字列をOutputDebugString関数で出力した場合、OllyDbgはクラッシュすることがあります。さらに、OllyDbgに任意のコードを実行させることが可能になる危険性は否定できません。
応急的な対処策としては、デバッギーのOutputDebugString関数呼び出しを無効化することが挙げられます。あるいは、同関数の呼び出しにブレークポイントを設定しておいて、第一引数の内容を確認するようにしておくことも有効な対処策と考えられます。
OllyDbg実行ファイル書き換えによる対処の方法は、下記質問を参照して下さい。ただし、完璧な対処ではありません。
なお、「うさみみハリケーン」のデバッガには、上記のセキュリティーホールはありませんので、「うさみみハリケーン」で予めデバッグ文字列の取得を試してみることも予防策として有効です。
『OllyDbg』の改造版とはどのようなものですか?
OllyDbg1.10は複数の『改造版』がネット上で配布されています。これは大抵『OllyDbg Forums』などで有志が公開したパッチ情報を組み込んだり、ネット上で公開されているOllyDbgプラグインなどを同梱したものです。また、リソースを改変しているケースもありますが、OllyDbgの機能や利便性にはさほど影響のないものが殆どです。さらに、アンチデバッグ対策等、改造版がウリにしている追加機能も、実際はそのような機能を持つOllyDbgプラグインを同梱したり、実行ファイル名を変えただけということが多いようです。パッチについては概ね以下のものが組み込まれています。以下のパッチコードによる『OLLYDBG.EXE』の書き換えは、『スペシャルねこまんま57号』の「簡易バイナリファイル書き換え」機能で簡単に行うことが可能です。「シンボルサーバー使用可能化」については、下記質問を参照して下さい。
*OllyDbg1.10パッチ例 FILENAME OLLYDBG.EXE *シンボルサーバー使用可能化 00090709: 10 37 0009070A: 12 02 0009070B: 00 03 0009070C: 00 80 000907EC: 74 EB *OutputDebugString関数関連バグ修正 *注意:強引な手法のため一部文字列が崩れます *2バイト文字は考慮されていません 0003094C: 83 E9 0003094D: C4 F3 0003094E: 10 E2 0003094F: 3B 07 00030950: C3 00 000AEC44: 00 51 000AEC45: 00 50 000AEC46: 00 57 000AEC47: 00 8B 000AEC48: 00 7C 000AEC49: 00 24 000AEC4A: 00 0C 000AEC4B: 00 8B 000AEC4C: 00 4C 000AEC4D: 00 24 000AEC4E: 00 14 000AEC4F: 00 B8 000AEC50: 00 25 000AEC54: 00 F2 000AEC55: 00 AE 000AEC56: 00 83 000AEC57: 00 F9 000AEC59: 00 74 000AEC5A: 00 06 000AEC5B: 00 C6 000AEC5C: 00 47 000AEC5D: 00 FF 000AEC5E: 00 20 000AEC5F: 00 EB 000AEC60: 00 F3 000AEC61: 00 5F 000AEC62: 00 58 000AEC63: 00 59 000AEC64: 00 83 000AEC65: 00 C4 000AEC66: 00 10 000AEC67: 00 3B 000AEC68: 00 C3 000AEC69: 00 E9 000AEC6A: 00 E3 000AEC6B: 00 1C 000AEC6C: 00 F8 000AEC6D: 00 FF *ウィンドウクラス名変更 *実行ファイル名変更との併用が効果的 000B6018: 4F 4E 000B6019: 4C 6F 000B601A: 4C 74 000B601B: 59 65 000B601C: 44 70 000B601D: 42 61 000B601E: 47 64なお、シンボルサーバー使用可能化など、環境によっては上記パッチによりOllyDbgの一部の機能が使用不能になることもあります。パッチコード実行の際にはバックアップの作成をお勧めします。
逆アセンブルが正常に行えず、しかもエントリーポイントがNOP命令というソフトウェアがあるのですが。
単純な偽装済UPXでした(実物で確認)。エントリーポイントを1バイト前にずらしてNOP命令から開始するのは、『PEiD』等パッカーの種類を特定する特殊なソフトウェアにより、エントリーポイントを基点とするバイナリパターンを元に、UPX圧縮と判定されるのを避けるためです。UPXで自作のソフトウェアの実行ファイルを圧縮し、デバッガで起動してエントリーポイントから展開ルーチンを抜けて、本来のエントリーポイントに至るまでの処理を考察されることをお奨めします。 また、オリジナルとUPX圧縮後の実行ファイルでPEヘッダの内容を比較してみて下さい。
デバッガなどのプログラム解析関連ツールを自作してみたい
デバッガを含むプログラム解析関連ツールを自作して無償で公開するのは、プログラム解析の振興だけにとどまらず、自己のプログラミングや解析のスキルアップという面からみても素晴らしいことです。しかし、『OllyDbg』のような優秀なデバッガがある以上、重複する機能を持つデバッガの自作よりは、『OllyDbg』に無い機能を実装した『OllyDbg』プラグインやプログラム解析関連ツールなどを自作する方が、そのプラグインやツールのユーザーにとって、より解析における利便性を向上させたりアプローチの可能性を広げるという点で有益と考えられます。 『うさみみハリケーン』のデバッガが、スレッド毎のブレークポイント設定機能など『OllyDbg』に無い機能を重視して実装しているのは上記の考えによるものです。『OllyDbg』プラグインの作成については、優れたプラグイン製作者であるDokoDon氏が『クラッキングバイブル』 でその手法を解説されていますので、参照されることをお勧めします。
プログラム解析関連ツールの自作については、プラットフォームSDKやネット上の検索に加え、参考図書、プログラミング系サイトの資料も役立ちます。プログラム解析関連ツールの自作で参考になるサイトは、当サイトのメインページでリンクしていますが、それ以外では以下などが挙げられます。また、デバッガ関連ツールならば、ロシア語やフランス語のサイトにかなり有用な解説がみつかることがありますので、Googleなどで検索対象を日本語・英語のサイトに限定しないことをお勧めします。
CodeGuru
http://www.codeguru.com/
The Code Project
http://www.codeproject.com/
なお、プラグインやツールの製作にあたっては、まずそれらが必要となる局面や需要を十分に検討した上でプログラミングを進めて下さい。最初にコンセプトを明確にしておかないと、実装する機能などで迷いが生じ、プログラミングが迷走することになりかねません。また、ソフトウェア製作一般にいえることですが、『ユーザーに目を向けて』開発とサポートを行って下さい。ユーザーが喜ぶものを、ユーザーの役に立つものを作るという「もの作りの基本」を忘れると、ユーザーから目を向けられなくなります。
プロセスメモリ上のモジュールエリアを実行ファイルとしてダンプ(ファイルに保存)しても、正常に動作しないのは何故ですか?
プロセスメモリ上にロードされた実行ファイルと、元のディスクイメージ上の実行ファイルは構造が異なるためです。当サイトのチュートリアル「オフセット−アドレス変換の原理とCDチェック解除への応用」で述べているように、PEヘッダ内のセクション情報にはメモリイメージ上での設定とディスクイメージ上での設定両方が含まれており、単純にプロセスメモリ上からダンプしたものでは、セクション情報に齟齬が生じるため修正が必要です。また、UPX等による実行ファイルの圧縮やその他の各種パッカーによる実行ファイルの暗号化等で実行ファイルに操作が加えられている場合は、他にも修正しなければならない箇所があります。なお、プログラムとしては自分がオリジナルかダンプされたものか判別するのは容易であり、ダンプした実行ファイルを起動すると警告を表示するソフトウェアも実在します。例:UPXで圧縮された実行ファイルをプロセスメモリ上からダンプした場合は、以下の操作で正常に動作する実行ファイルが得られます。このような解凍あるいは復号化操作は、リバースエンジニアの間ではどちらも「アンパック」と呼ばれています。
1.エントリーポイントを本来のもの(OEP:オリジナルエントリーポイント)に修正
2.セクション情報(Raw Offset等)の修正
3.IAT(インポートアドレステーブル)の再構築
4.リソースディレクトリの再構成(必須ではない)
5.「Base of Code」の修正(動作上必須ではない)
なお、他人が製作したソフトウェアの実行ファイルのアンパックは、日本語化目的等の特殊なケース以外は、著作権法や不正競争防止法に抵触するといわれていますので注意して下さい。
解析超初心者がデバッガで解析対象を起動する前に行うべきことは?
以下は複数のリバースエンジニアの方々から頂いた意見をまとめたものです。質問で「起動前」となっているので、解析対象の実際の動作を見ないとアプローチを想定できないケースは考慮していません。1.ウイルスチェック
2.ヘルプ等の添付文書に目を通して、解析に役立ちそうな情報はないか確認
3.解析対象を『eXeScope』で開いてセクション名、インポートDLL、使用API関数、エントリーポイント等からコンパイラや圧縮等の有無を推測
4.PCゲームの場合はリソースも確認してデバッグモード等の有無を推測
5.以上と既存のチュートリアル等を元にどのようなアプローチを行うか想定
6.想定したアプローチにおいて、API関数等の必要な知識が不足している場合は予め資料で確認しておく
デバッガで解析対象を起動後に場当たり的なアプローチを行うのではなく、予め解析対象について考察することが解析スキルを向上させる上で重要です。また、既存のチュートリアルで解説されている手法を漫然となぞるのではなく、解析中も常にアプローチのポイントとなる点を自分なりに考えることにより、必要最小限の手数で効果的な解析を行うための解析センスを磨くことが可能です。
△操作関連
『OllyDbg』の操作方法等を解説した参考書はありますか?
プログラム解析の解説書で、OllyDbgについての解説があるものとしては以下が挙げられます。いずれもOllyDbgを使いこなす上で必要となるアセンブリ言語やAPI関数および基本的なアプローチ方法等、プログラム解析の基本事項も解説しており、有用な解説書と言えます。・デバッガによるx86プログラム解析入門
・解析魔法少女美咲ちゃん マジカル・オープン!
・クラッカー・プログラム大全
・クラッキングバイブル
『OllyDbg』から解析対象を簡単に起動する方法を知りたい。
メニューから「Options」 (オプション) → 「Add to Explorer」 (エクスプローラメニューに追加) を使用してください。また、『eXeScope』等についても同様のレジストリ操作を行うことをお奨めします(『PeRdr』の同梱ファイルに詳細説明あり)。
操作上で特に覚えておくと役立つポイントはありますか?
『OllyDbg』では、マウスで各ウィンドウ上の数値や範囲等を選択すると、そこから右クリックでポップアップメニューを呼び出し、選択内容に応じた色々な機能を使うことができます。操作に慣れないうちは色々な箇所で右クリックして、どのような機能があるか確認しておくことをお奨めします。少し分かり辛いのですが、レジスタウィンドウ(右上ウィンドウ)では汎用レジスタだけでなくEFLAGSレジスタやゼロフラグ等の各ステータスフラグも変更可能です。また、ウィンドウによってはドラッグまたは「Shift」キーを押しながら範囲を選択することで複数列等範囲選択が可能で、レジスタウィンドウ(右上ウィンドウ)ではドラッグで表示内容の移動を行うこともできます。
<参考>主なショートカットキー(特に左上ウィンドウ用に使用するもの)
Enter:選択されているコマンドを表示リストに追加(選択されているのがJUMP命令やCALL命令の場合は対象アドレス該当箇所を表示)
-/+:表示リスト中の前/次のアドレス該当箇所を表示
F2:ブレークポイント設定/解除
F7:詳細ステップ実行(ステップイン:サブルーチンに入る)
F8:ステップ実行(ステップオーバー:サブルーチンは実行して乗り越える)
F9:デバッギー実行
F12:デバッギー実行一時停止
Ctrl+F9:リターンまで実行
Alt+F9:ユーザーコードまで実行(システムDLL内から抜ける際等に使用)
Space:アセンブル
Ctrl+E:バイナリデータの編集
Ctrl+G:アドレスを指定して移動(表示位置変更)
ESC:詳細自動ステップ実行の停止
『OllyDbg』プラグインの入手先は?
以下のページを参照して下さい。他のプラグインについては当方へ質問願います。現在公開されているOllyDbgプラグインの中では、特に「OllyDump」の導入をお奨めします。なお、「Command line」プラグインでは式入力による計算も可能です。http://www.woodmann.com/ollystuph/index.php
http://www.openrce.org/downloads/browse/OllyDbg_Plugins
API関数呼び出しへのブレークポイント設定の仕方を知りたい。
特定のコードにブレークポイントを設定する場合は、メインウィンドウの左上ウィンドウ上で逆アセンブルコードリストから目的のコードを選択してF2キーですが、API関数へのブレークポイントの場合は以下の手法を用います。まず、メインウィンドウの左上ウィンドウ上で右クリック→「Search for」(検索)→「Name (label) in current module」(ラベル名(現在のモジュール内))で、デバッギーが使用する関数一覧を表示させます(「Ctrl」キー+「N」キーでも可)。ここで、関数一覧表示ウィンドウの各カラムをクリック或いは右クリック→「Sort by」(整列)メニューを使うと目的の関数を探し易くなります。また、標準の整列順序を標準メニューの「Options」(オプション)→「Debugging options」(解析詳細設定)→「Addresses」(アドレス)タブで設定できます。
次に、目的の関数を選択し右クリックから「Set breakpoint on every reference」(全ての参照にブレークポイントをセット)で、目的の関数を呼び出すコードに一括してブレークポイント設定を行います。ただし、この方法ではインポートセクションへのジャンプ命令(例:「JMP DWORD PTR DS:[<USER32.DialogBoxParamA>]」)も参照箇所に含まれますので、必要に応じてこのジャンプ命令へのブレークポイントを解除して下さい。
個々のAPI関数呼び出し箇所にブレークポイントを設定(解除)する場合は、目的の関数を選択して右クリック→「Find references to import」(インポート関数の一覧表示)で関数呼び出し箇所のコード一覧を表示させ、各コードをマウスや上下カーソルキーで選択してF2キーでブレークポイント設定(解除)を行います。
条件付きブレークポイントを設定してみたい。
レジスタや参照アドレスの値等が条件の場合は、メインウィンドウの左上ウィンドウ上で、目的のコードを選択してから「Shift」キー+F2キーでブレークする条件を設定します。条件式のフォーマットはヘルプを参照して下さい。例としては下記。
EAX==64
DWORD PTR [432100]<=(EAX+270F)
また、「Shift」キー+F4キーでログ付き条件ブレークポイントを設定できます。ここでの式の例としては上記条件式の左辺(レジスタや特定アドレスの格納値)等です。これにより、条件判定時の式の値がログウィンドウに記録されます。ログウィンドウは「Alt」キー+「L」キーで呼び出せます。
ウィンドウメッセージにブレークポイントを設定してみたい。
基本的なウィンドウメッセージへのブレークポイント設定は、標準メニューの「View」(表示)→「Windows」(ウィンドウ)でウィンドウのリストを表示してから、目的のウィンドウを選択して右クリック→「Message breakpoint on WinProc」(メッセージブレークポイントをセット->WinProc)で詳細設定ダイアログを呼び出してブレークポイントを設定します。NT系OSでは、標準メニューの「Options」(オプション)→「Debugging options」(解析詳細設定)→「Security」(セキュリティ)タブの「Allow code injection to get address of WinProc」(WinProcのアドレス取得の為コードの注入を許可する)で正確なウィンドウプロシージャを認識することが可能です。ただし、この手法は安全性と確実性に問題がありますので注意して下さい。このオプションを使用した場合は、デバッギーをF12キー等で停止させた状態でのみウィンドウプロシージャにメッセージブレークポイントを設定して下さい。
上記の手法で上手くメッセージブレークポイントを設定できない場合は手動で行います。この場合は、ウィンドウメッセージを認識するウィンドウプロシージャの、先頭コードか呼び出し元コードに対し条件付きブレークポイントを設定します。同プロシージャのアドレスはRegisterClass関数やダイアログ生成関数の引数で確認可能です。また、このアドレスは、ウィンドウプロシージャで処理しないウィンドウメッセージをシステム側で処理させるための、DefWindowProc関数の呼び出し箇所から探し出すことも可能です。DefWindowProc関数は通常ウィンドウプロシージャの最後に近い部分で呼び出されます。なお、ダイアログベースのアプリケーションでは通常プログラムの実行処理としてDefWindowProc関数を呼び出すことはありません。
WM_LBUTTONDBLCLKの場合の条件式は下記。
MSG==0203
目的のプロシージャ内でウィンドウメッセージの値が格納されるアドレスとメッセージ名を指定することもできます。
[EBP+C]==WM_LBUTTONDBLCLK
なお、目的のウィンドウに送られるウィンドウメッセージの監視を行いたい場合は、メッセージループのTranslateMessage関数呼び出し箇所にログ付き条件ブレークポイント(式:MSG)を設定してブレーク結果をログウィンドウに表示させることで、メッセージモニターの様に処理させることが可能です。ただし、ダイアログベースのソフトウェアは、動作上コード中にメッセージループが必須ではありません。
<参考>C言語でのメッセージループ例。ウィンドウメッセージやこれらのAPI関数とMSG構造体について、プラットフォームSDK等の資料に目を通すことをお奨めします。また、当サイトで公開している『改造初心者向け練習用プログラム』のソースコードも参考になると思われます。
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
API関数呼び出し箇所ではない任意の複数コードに一括してブレークポイントを設定したい。
予めその様なブレークポイントを設定したいコードにユーザー定義ラベルを設定しておいて下さい。それからメインウィンドウの左上ウィンドウ上で右クリック→「Search for」(検索)→「User-defined label」(ユーザ定義ラベル)でラベル一覧ウィンドウを表示し、そのウィンドウ上で右クリック→「Set breakpoint on every command」(全てのコマンドにブレークポイントをセット)です。これにより一括ブレークポイント設定が可能になります。リアルタイムアセンブラで注意すべきことは?
PCゲームでのパラサイトルーチン埋め込み等、リアルタイムアセンブラによりコードを動的に変更/追加する場合は、汎用レジスタの値が変更/追加箇所の前後で保持されないと不具合を招くケースがあります(特にESI及びEDIレジスタ)。この場合はPUSHAD命令とPOPAD命令を変更/追加箇所の先頭と終端に用いれば最小限のコードで対処可能です。また、EFLAGSレジスタの内容を保持したい場合はPUSHFD命令とPOPFD命令を用います。また、Windows XP Service Pack 2で実装されるデータ実行防止機能により、コードが実行されるメモリエリアは、必ずアクセス属性として実行属性が必要になります(参照)。
リアルタイムアセンブラでAPI関数呼び出しをコーディングする場合は、「call kernel32.ExitProcess」というように関数名を入力します。関数名は大文字/小文字も正確に入力して下さい。
コードの動的変更中に『OllyDbg』が誤った逆アセンブルコードを表示した場合は、「Ctrl」キー+「A」キーでコードを再解析させると対処できます。
メモリ内容の内容変動箇所を調べるには?
まず標準メニューから「View」(表示)→「Memory」(メモリ)でメモリーマップを表示します。次に内容変動を調べたいエリアを選択・右クリックから「Dump」(Dump)で同エリアのメモリ内容をダンプし、右クリックで表示されるポップアップメニューから「Backup」(BKUP)→「Create backup」(BKUP 作成)を選択します。これによりそのエリアのメモリ内容に変動があれば、その変動箇所がハイライト表示になります。変動箇所の検索は同様にポップアップメニューから「Search for」(検索)→「Modified data」(修正済データ)で検索可能です。
なお、上の機能では値増減等の変動内容詳細を区別できず、また、絞込み検索機能も実装されていません。そのため、PCゲーム解析時等のプロセスメモリ上の変動検索には、『うさみみハリケーン』か『スペシャルねこまんま57号』を使用されることをお奨めします。
プログラムが参照している文字列の一覧表示機能を使いたい。
メインウィンドウの左上ウィンドウ上で右クリックし、ポップアップメニューから「Search for」(検索)→「All referenced text strings」(全ての参照文字列)を選択して下さい。事前に参照文字列の検索を日本語文字列対応可能にして下さい(当サイト配布の日本語化パッチ添付文書を参照願います)。特定文字列の参照コード一覧表示機能を使いたい。
デフォルトでデータセクションの内容が表示されるメインウィンドウの左下ウィンドウで、目的の文字列を探し出し(ポップアップメニューから検索可能)、その文字列の先頭アドレスに当たるバイナリデータを選択・右クリックします。これにより表示されたポップアップメニューから「Find references」(参照を検索)(「Ctrl」キー+「R」キーでも可)を選択して下さい。特定のコードにワンタッチで戻る方法が知りたい。
目的のコードを右クリックから「New origin here」(移動元にセット)を設定しておくと、移動後に右クリックから「Go to」(移動)→「Origin」(移動元)で元のコードへワンタッチで戻ってくることができます。なお、ここでいう移動はコードを実行させている訳ではなく、あくまで逆アセンブルコードリスト上での表示箇所を変更しているだけです。また、目的のコードを「Enter」キーで表示リストに登録しておいて「+」や「-」キー等で移動する方法や、Bookmarkプラグインを利用するという選択肢もあります。コール命令からサブルーチン及び、ジャンプ命令からジャンプ先にワンタッチで移動する方法はありませんか?
コールまたはジャンプ命令のコードを選択して、「Enter」キーです。F7キーでサブルーチンにステップインした後に、簡単にサブルーチンを抜ける方法はありませんか?
「Ctrl」キー+F9キーでリターンまで実行させるか、予めCALL命令によるプロシージャ呼び出し部の次のコードにブレークポイントを設定しておきます。システムDLL等、デバッギー実行ファイル以外のコードを表示した場合の対処法は?
上記の手法で表示位置を変更或いは、「Alt」キー+F9キーでユーザーコード(デバッギーのコード)まで実行させます。ユーザーコードまで実行させる場合は、一旦デバッギーに制御を戻して何らかの処理を行わせることが必要なケースもあります。また、左上ウィンドウ上で右クリックから「View」(表示)で任意のモジュールにワンタッチで表示を変更することも可能です。 他には、メインメニューの「View」(表示)→「Executable modules」(実行モジュール)で実行モジュール一覧を表示させ、デバッギーのモジュールを選択・右クリックで表示されるポップアップメニューから「View code in CPU」(CPUコード表示)でも対処できます。逆アセンブルコードウィンドウ(左上ウィンドウ)でコード変更後の実行ファイルの保存が上手くいきません。
右クリックで「Copy to executable 」(実行ファイルへコピー)からコード変更箇所のうち実行ファイルに反映させる箇所を全てか一部か特定して、更にダンプウィンドウ上で右クリックして「Save file」(ファイル保存)で保存します。なお、この保存先ファイルは必ずオリジナルとは別名のものにして、オリジナルの実行ファイルには一切変更を加えないで下さい。「Just-in-time debugging」(ジャスト・イン・タイム デバッグ)とは何ですか?
簡単に言えば、アプリケーションが何らかの問題によりクラッシュした場合に、デバッガでそのアプリケーションにアタッチして問題箇所の発見・解決を促す機能です。例えば『VC』(マイクロソフト社のVisual C++)等コンパイラをインストールすると、そのコンパイラの付属デバッガが「Just-in-time debugging」用デバッガとしてレジストリに登録されることもあります。プログラミングをされる方は勿論、プログラミングをされない方も『OllyDbg』を「Just-in-time debugging」用デバッガとして登録しておくことをお奨めします。アプリケーションの不具合を作者に知らせる際に役立つことがあります。
OllyDbgでのソースレベルデバッグが上手くいきません(開発環境:Visual C++ 6.0)
OllyDbgでソースレベルデバッグを行う場合は、事前にコンパイラでリンク時の設定を変更して、「デバッグ情報の生成」でpdb形式のデバッグ情報ファイルを出力させるようにしてからコンパイルしておきます。これでデバッギーを起動し、「View」(表示)→「Source files」(ソースファイル)で表示されるソースファイルリスト上で目的のソースファイルを選択・ダブルクリックすると、そのソースファイル内のソースコードが別ウィンドウで表示されます。表示されたソースコードの行番号の左に「>」がある行では、行選択→右クリックで表示されるポップアップメニューから各種ブレークポイントを設定可能です。さらに、同ポップアップメニューから「View in Disassembler」(逆アセンブラ画面表示)で、ソースコード上の選択行に対応する逆アセンブルコードをCPUウィンドウ内の左上ウィンドウの逆アセンブルコードリスト上で選択・ハイライト表示にします。逆に、逆アセンブルコードリスト上でブレークポイントを設定すると、表示されたソースコードの該当位置の行番号がハイライト表示になります。ソースレベルデバッグ中は、ブレークポイントでのブレーク時のログにソースコードの情報(関数名と関数開始アドレスからのオフセット)が併せて表示されます。なお、このソースレベルデバッグは、コンパイラにVCを用いる場合では、OllyDbgがPDBファイルのデバッグ情報を元にデバッグシンボルを利用するために、コンパイルをデバッグビルドとして行う必要があります。
OllyDbgはソースレベルデバッグをデバッグヘルプ系API関数によって実現していますが、このAPI関数はバージョン差異を含む全てのコンパイラが生成するデバッグシンボルに対応している訳ではないため、ソースレベルデバッグができないケースもあります。その場合はOllyDbg同梱のDBGHELP.DLLを、使用しているコンパイラが出力するデバッグシンボルに対応するバージョンのものを入手して置き換えることで対処可能と考えられます(DBGHELP.DLLは再配布可能コンポーネント)。ケースによってはMicrosoftが無償配布している『Debugging Tools for Windows』から同DLLを抽出する必要があります。なお、DBGHELP.DLLはMicrosoft製以外のコンパイラで出力されたデバッグシンボルにも対応しているとは限りませんので注意して下さい。
また、ユーザーモードで動作するアプリケーションのデバッグにおいては、大抵システムデバッグシンボルは不要ですが、OllyDbgでシステムデバッグシンボルを利用したい場合は、以下の操作を行ってシンボルサーバーを使えるようにしておいて下さい。ただし、以下の操作結果に伴うOllyDbgの動作は保証致しかねます。
1. 『Debugging Tools for Windows』をインストールし、『dbghelp.dll』、『symsrv.dll』および『srcsrv.dll』をOllyDbgのフォルダにコピーします。
2. 実行ファイル「OLLYDBG.EXE」に以下の書き換えを行います。このパッチコードによる書き換えは『スペシャルねこまんま57号』でメニューの「ファイル」から「簡易バイナリファイル書き換え」で簡単に行うことが可能です。
*OllyDbg1.10用 FILENAME OLLYDBG.EXE 00090709: 10 37 0009070A: 12 02 0009070B: 00 03 0009070C: 00 80 000907EC: 74 EB3. 環境変数「_NT_SYMBOL_PATH」でシステムデバッグシンボルのフォルダが設定されているか確認します。
(例)_NT_SYMBOL_PATH=symsrv*symsrv.dll*c:\symbols*http://msdl.microsoft.com /download/symbols
△ゲーム解析関連
『OllyDbg』を使ったPCゲーム解析のチュートリアルはありませんか?
『ExGAME』Vol6にBlueAshさんが詳細なチュートリアルを寄稿されていますのでご覧下さい。API関数へのブレークポイント、ステップ実行、全セクションからの文字列検索や注意事項他、基本的な操作方法だけでなく、アプローチに対する考え方が詳しく解説されており、必見だと思います。ちなみに、このチュートリアルが日本人向けに公開された最初の『OllyDbg』を用いた解析チュートリアルです。また、当サイトや解説書『デバッガによるx86プログラム解析入門』および、当サイトリンク先の「DANAの部屋」や「猫缶Index」にもチュートリアルがありますので、ご覧になることをお奨めします。
『OllyDbg』でのメモリ書き換え操作を記録・再現するには?
『うさみみハリケーン』あるいは『スペシャルねこまんま57号』のAPIトレース機能を使用して下さい。この機能で『OllyDbg』のWriteProcessMemory関数呼び出しをトレースすることにより、『OllyDbg』のメモリ書き換え操作を『うさみみハリケーン』や『スペシャルねこまんま57号』用の改造コードとして出力することができます。なお、APIトレース機能は『OllyDbg』以外のWriteProcessMemory関数を用いるソフトウェアにも使用可能です。海外のゲーム専門リバースエンジニアの『OllyDbg』に対する評価はどうでしょうか?
Ver1.08の頃からある程度の評価は得ています。しかし、海外で人気があるRPGゲーム等は、『OllyDbg』が対応できないDirectX排他モードで動作するものが多いため、PCゲーム解析用デバッガとしては『SoftICE』に人気があります。ただし、すでにDirectX初期化処理のフック等によりウィンドウモード化を実現するソフトウェアも公開されており、『OllyDbg』がメインの解析用デバッガになりつつあるといえます。なお、DirectX排他モードのゲームでも、ウィンドウモード化させた状態や、エントリーポイントからウィンドウ作成の間でコードの実行を停止させた状態ならば、『OllyDbg』を用いた各種解析が可能です。