デバッグ

2011年4月 3日 (日)

Win32デバッグ(13)・・・最終章

MSDNを眺めててひょんな事から、ある事を発見し、やる気が起きたので、とりあえず、完成させました。

ということで、久しぶりにデバッグ絡みですdash

何を発見したかと言うと、デバッグ対象がマルスレッドアプリケーションの場合のブレークポイントの設定の仕方です。前に書いたようにブレークポイント例外発生時に、自分で設定したブレークポイントなら、元の命令に戻して、シングルステップ実行で実行させた後、続く、シングルステップ例外発生時に再びブレークポイントを設定するという基本的な流れは問題ないのですが、デバッグ対象がマルチスレッドアプリケーションの場合、タイミングによって、シングルステップ例外発生時にブレークポイントを再設定する前に、他のスレッドが通過してしまう可能性があるなぁdashdash・・とずっと前から気づいてはいたんですが、解決策が見つからず見なかった事にしてましたsweat02sweat02

で、先日MSDNを眺めてたらそのやり方が書いてありました。

MSDNのResumeThreadのnoteの部分です。

Note that while reporting debug events, all threads within the reporting process are frozen. Debuggers are expected to use the SuspendThread and ResumeThread functions to limit the set of threads that can execute within a process. By suspending all threads in a process except for the one reporting a debug event, it is possible to "single step" a single thread. The other threads are not released by a continue operation if they are suspended.

単純にSuspendThreadとResumeThreadを使えばよかったんですね・・ブレークポイント例外が発生したら、例外が発生したスレッド以外のすべてのスレッドに対してSuspendThreadしシングルステップ実行、そして、続くシングルステップ例外発生時に、ResumeThreadで元に戻すup

胸のつっかえが1つ取れました(これらの関数の存在は知っていましたが、デバッグの時使っていいのかな??と思ってました・・・)。

ということで、上記を組み込んだり、ソースを公開できるようクラス設計やアルゴリズムを一部見直したりして実用性は0ですが、デバッグ情報からコールツリーを生成するプログラムを完成させました。

Delphiで作ってるので、使用するデバッグ情報はTD32というものですが・・DbgHelpやDIA SDKを使えばMSVCが生成するPDBファイルなどのデバッグ情報も利用できるはずです。コールツリーを生成するためのアルゴリズムはMSVCの生成するコードでも問題ないと思うので(ルーチンはCALL命令で呼んで、RET命令でリターンするルーチンがとりあえず前提です。ジャンプ命令とかは勘弁sweat01)。

ダウンロードはSkyDriveから。 ソースコードとかもろもろついてます。

2010年2月21日 (日)

完成させれない男。

最近というか、前々からちょくちょく思ってたのだが、自分はプログラミング向いてないのかなーとたまに思うdashdash

というのもプログラムを中々完成させれないのであるsweat02。今までに色々プログラムを書いてきたが、中途半端なプログラムだけが増えていく・・bearing

一昨年は、ここに書いたように、ODBCやら10進浮動小数やら色々やってたがいまだに完成しないのである。去年は新しいデバッグ関係以外にもそれらの続きをもちろんやっていたのであるが・・

そして、前回デバッグ関係を終わらそうと高々?に宣言happy02したのであるが・・・今、ODBCの事を再びやってたり、自分に泣けてくるcryingcrying

ちなみにデバッグの方はあの後、ソースを含めて公開できるように1週間ぐらい作業してて、見た目をちょっとおしゃれにしたり、ソースをリファクタリングしたりして、コンパイルできるよう1度完成させたのであるが、どうしても1点だけ設計上気になる部分があって、そこを変更しながら途中で放置・・・・

ちなみに、画面はこんな感じ。起動して、メニューからプロファイルする実行可能ファイルを選択すると、

Ct_1

その実行可能ファイルが実行され、TD32デバッグ情報が含まれてると、含まれるモジュールやクラス構造そして手続き・関数の一覧が出るので、プロファイルする項目にチェックを入れて、続行。

Ct_2_3

で、実行が終わった後にそのコールツリーが表示される。

Ct_3_3


本来はこれをたたき台にパフォーマンスプロファイラを作ろうと思ってたのだが、最初の予備調査に反して、逆アセンブルするはめになったり、四苦八苦して、今は、そんなモチベーションは失われてしまった。とりあえずたたき台だけを完成させて終了しようとdash

先ほども書いたように動くようになったので、設計上気になる部分を変更したら、全ソースをコンパイルできる形で公開する予定だが、いったいいつになる事やらcrying

まぁ、完成させれないのは自分の性格の問題とは思ってるが・・・

2010年1月20日 (水)

Win32デバッグ???

ということでまたDelphi絡みdashdash

以前、Win32デバッグの事について色々書いていたのだが、完結させないまま放置してたsweat02

頑張ってたなぁdash。何をやろうとしてたかというとデバッグ情報とWin32のデバッグAPIを使い動的コールグラフを求めようとしてて、最後の記事を書き上げた後、燃え尽きてしまったbearing。動いて公開できそうなプログラムは完成してたんだけど、そのまま放置プレイbearing

ということで他におもしろそうな事がないので、久しぶりこのネタの続きをやろうかなと思っていたり(といっても、無理やり完結させるつもりであるが・・・)。

で、昔?の内容を忘れかけてるプログラムを見たりしながら、このためにわざわざ作ったTD32デバッグ情報を表示するツールの最新版をTD32Scannerをアップデートした。TD32デバッグ情報って何?という人は上の4番目の記事 「Win32デバッグ(4)・・・デバッグ情報(Borland/CodeGear編)」を。ダウンロードはSkyDrive から。

Delphi2005ぐらい?からかだったか、日本語というよりUnicodeの文字を識別子に使えるようになったので、それに対応してみた。TD32デバッグ情報では、UTF-8で格納されてるようである(といってもアプリ自体はUnicodeに対応してないので・・・)。

Td32s_1_2

 


2009年10月 7日 (水)

Win32デバッグ(12)・・・SEH(Structured Exception Handling)

まぁ、どうしようか悩んだが、とりあえず、進める所まで進んでみますsad

ということで今回のお題目は例外。結論から言うとDelphiではtry-except文による例外処理にしろ、try-finally文の終了処理にしろ、WindowsのSEH(Structured Exception Handling)を使って実装されているのだが、そのSEHについて。

SEHはWindowsによって提供されている構造化例外処理のためのメカニズムのことであるが、その流れについて説明する。

まず、0による除算や不正なメモリへのアクセスなどのハードウェア例外にしろ、Win32 APIのRaiseException関数によるソフトウェア例外にしろ、スレッド内で例外が発生するとOSに制御が移る。そして、OSは例外が発生したスレッドのCPUレジスタなどのコンテキスト情報の保存など必要な処理を行った後、例外をハンドルするための例外ハンドラを検索し、呼び出す。

具体的には、まず、OSは例外が発生したスレッドのスレッド環境ブロック(TEB:Thread Environment Block)またはスレッド情報ブロック(TIB:Thread Information Block)と呼ばれる領域の先頭4バイト(at FS:[0])の値を取得する。この4バイトの値は例外ハンドラのリンクリストの先頭をポイントしているので、この値から先頭の例外ハンドラから順に呼び出していくのである(つまり、ここに例外ハンドラを登録すれば例外発生時に例外ハンドラが呼び出されるようになるのだが、通常は、Delphiにしろ、C++にしろ、言語提供の例外処理などを利用すれば、コンパイラによって登録するコードが挿入されるので、普通は自前で登録しないが・・)。

リンクリストのノードは次のようなEXCEPTION_REGISTRATION構造体になっている。

prevメンバは次のノードへのアドレス、handlerメンバーには例外ハンドラのアドレスつまりOSから呼び出されるコールバック関数のアドレスを表す。呼び出されるコールバック関数のプロトタイプは次のようになる。

第1引数はEXCEPTION_RECORD構造体のアドレスである。EXCEPTION_RECORD構造体は例外の種類を表すコードや例外発生時のアドレスなどの発生した例外に関する様々な情報が格納されているので、例外ハンドラはこれらの情報を見て例外を処理するかを決定する。Delphiのtry-except文では例外オブジェクトのクラスとexceptのon句に指定したクラス(on E: ExceptionClass doのExceptionClassの部分)を比較して判定している。

呼び出された例外ハンドラで例外を処理せず、OSに次の例外ハンドラを呼び出させる場合はExceptionSearchException(1)を返して例外ハンドラからOSにリターンする。

また、例外が発生したアドレスから例外の原因を修正(0による除算が発生した場合、除数を0以外の値に修正)するなどして、再実行する場合はExceptionContinueExecute(0)を返してOSにリターンする。Delphiではこれは行われない。

例外ハンドラで例外を処理する場合は、例外ハンドラからOSにリターンせず、通常は、Win32APIのRtlUnwindなどでアンワインド(巻き戻し)したり、スタックフレームを適切に設定し直して、処理を続行する。Delphiではexceptのon句の後に指定した処理から実行が再開される。

RtlUnwindによるアンワインドはMSDNのドキュメントみても何の事だかさっぱり理解できないと思うが、簡単に言うと、例外ハンドラのリンクリストから不必要になった例外ハンドラのノードを削除することである。また、このとき、例外ハンドラの2回目の呼び出しが行われる。この2回目の呼び出しのときに、通常は終了処理を行う。Delphiではtry-finally文のfinally句の後に指定した終了処理がこの2回目の呼び出しのときに、実行される。

とまぁ、おおざっぱに書くとこんなところであろうが、詳細は

すべて、英語で書かれているが、特に一番上の記事にはSEHについて分かりやすく、詳細に書かれているのでお勧めです。自分は頑張って読みましたdash

2009年10月 6日 (火)

Win32デバッグ(11)・・・番外編

今回は寄り道。というより、前回までに示したコードに少し問題があった。

第1回で

  • ReadProcessMemory
  • WriteProcessMemory

を使えば、プロセスハンドルからそのプロセスのアドレス空間を読み書きできるし、また、

  • GetThreadContext
  • SetThreadContext

を使えば、スレッドハンドルからそのスレッドのコンテキスト(レジスタの値)を読み書きできる。

と書いたが、大切な事が抜けていたshock

ということで、例えば、あるスレッドが次に実行する命令を読み込むプログラムを上記のAPIを使って書いてみる。x86アキーテクチャではインストラクションポインタ(EIP)レジスタが次に実行される命令をポイントするので、例えば、次のようになる(繰り返すが、対象スレッドは停止してないとまずいと思う)。

x86命令は可変長の命令で何バイト読みこめばよいか?という問題があるが、上記のプログラムではとりあえず4バイト読み込んでいる。上記のプログラムを実際に動かすと正しく動いているように見えるが、問題があるのである。

何が問題かと言うと、ReadProcessMemory/WriteProcessMemoryの第2引数には読み書きするメモリの仮想アドレスを渡すのであり、上記のプログラムでは、EIPレジスタの値を渡しているのであるが、EIPレジスタの値は仮想アドレスの値ではないのである。EIPレジスタに格納されているのは確かにアドレスであるが、CSレジスタに格納されているセグメントセレクタによってポイントされるセグメント(コードセグメント)内の相対アドレス(segment-relative address)なのである。

ということで、仮想アドレスに変換する必要があるのであるが、そのために使うAPIがGetThreadSelectorEntryである。GetThreadSelectorEntryによって引数で指定したセグメントの仮想アドレス空間内のセグメントのベースアドレスが求まるので、それを使って正しく先ほどのプログラムを書き換えると次のようになる。

と、肝心な事を忘れたdash

ちなみに、最初に示したプログラムが動いてしまうのは、どうも、NT環境ではコードセグメントのベースアドレスが常に0になるからのようである。特にESPレジスタによってポイントされるスタックトップを読み書きするような場合、スレッド毎に異なるスタックセグメント(SSレジスタ)が割り当てられるので、なお更まずい。

2009年9月30日 (水)

Win32デバッグ(10)・・・泣

このネタも10回目に突入してしまったbearing

早速、前回やり残した事をやる。前回で逆アセンブラスクラスを設計したので、今回は問題となっていたデバッグ情報からルーチンの終了位置のアドレスを求める部分を逆アセンブルして求めるよう次のように書き換える。

で、再びドライバプログラムを実行してターゲットプログラムをプロファイルしてみたところ、ターゲットプログラムが正常に動作したhappy02。プロファイルしたターゲットプログラムはコンソールアプリで次の通り。

意味のないプログラムであるが、関数Calcが10回呼び出され、その結果がコンソールに表示されるだけである。また、関数Calcからは関数SumとMulが呼び出される。このターゲットプログラムのコールツリーは次のようになった。

Win32debug_10_1_3

上の画面から確かに関数Calcが呼び出され、関数Calcから関数SumとMulが呼び出されているのが分かるだが、呼び出された回数つまりヒットカウントが10ではなく1になってるのである・・・全部合計すると10回なのであるが、ノードが分かれている・・・

がーんsad

で、デバッグして原因を調べたところ、肝心な事をすっかり忘れていたsad。それは、最近の言語でたいていはサポートされている例外処理・終了処理である。Delphiでいうところのtry-except,try-finally文である。この実現のためにコンパイラが生成してるコードに対応できていなかったのである。つまり、今までの設計では、ルーチンは必ずCALL命令で呼び出されRET命令でリータンする、また、RET命令は必ずルーチン内で1箇所だけ(現状、1つのルーチンに対して開始位置と終了位置の2つだけのブレークポイントを作成してるため)という仕様?前提?が甘かったのである・・・(JMP命令なども無視してるし・・・)

はぁ。

どうしよう??

と、本来はすべてが上手くいき今回で最終回にする予定であったのだが。どうしよう。

また、ドライバプログラムも含めて動作する形でソースをアップロードする予定であったのだが、どうせおかしなコールグラフが表示されるのでやめておく。

と言う事で次回に続くかは本気で分からない・・ははは。

2009年8月15日 (土)

Win32デバッグ(9)・・・逆アセンブラ

前回で、どうもx86命令を逆アセンブルする必要が出てきたので今回は逆アセンブルについて。

まずは、逆アセンブラを自作する方向で進めていたのであるが、ご存知のようにx86命令は可変長であったり、SSE命令などの拡張命令がどんどん追加されていったり、面倒そうなので、とりあえず、既存の逆アセンブラライブラリを利用する事にした。

ということで既存の逆アセンブラライブラリをいくつか挙げてみる。

逆アセンブラライブラリ
名前 サイト ライセンス
BeaEngine http://beatrix2004.free.fr/ LGPL
diStorm64 http://www.ragestorm.net/distorm/ BSD
Hacker Disassembler Engine(HDE) http://patkov-site.narod.ru/ Free
libdisasm http://bastard.sourceforge.net/libdisasm.html Free/Open Source
SysDasm http://rootkit.com/newsread.php?newsid=208 Free/Open Source
Udis86 http://udis86.sourceforge.net/ Free/Open Source
VirtualBox Disassembler Library http://www.woodmann.com/forum/showthread.php?t=11904 ?

各逆アセンブラライブラリの詳細については、各ライブラリのサイトを参照。ここではどのライブラリを使うかだが、ここでは上記の1つであるHacker Disassembler Engine(HDE)を使うことにした。理由は最終更新日が最近(2009年3月)であるのと、軽量である点だ。他のライブラリは結構高機能で逆アセンブルした機械語をアセンブラのニーモニックのテキスト形式に変換できたりするが、今回はx86命令のRET命令を検出できればよいので、命令長やオプコードなど最小限の情報だけを取得できるHDEにした(ちなみに、上記BeaEngineのサイトにLength Disassemblerというものもあるようだ・・)。

次に、新しいクラスを設計するが、その前にx86命令の大まかな命令フォーマットを知らないと設計できないので、x86命令の命令フォーマットを少し。x86命令の命令フォーマットは次のようになる。

X86instformat_2

先程も述べたように可変長なのであるが、各フィールドの詳細はIntelのマニュアルを参照ということで。

まずは、抽象クラスで逆アセンブルを行うTDisassemblerクラス。

各プロパティは先ほどのx86命令フォーマットの各フィールドに対応する。そのうちのいくつかのプロパティの型がVariant型なのは、対応するフィールドが必ずしも存在するとは限らないからである。DoNextメソッドは現在位置からx86命令を解析し、次に命令の先頭にポインタを進める抽象メソッドであるので、下位クラスではDoNextメソッドをオーバーライドしなければいけない、また、解析が成功したらTrueを返す。

そして、TDisassemblerクラスを継承し、HDEを使った逆アセンブラクラスTHdeDisassemblerのDoNextメソッドを次のように実装する。

ここは、使用する各逆アセンブラライブラリに依存するので詳しくは省略・・

後、一息なのであるが、今回はここまでbearing

最後に、HDEはC言語のライブラリなので、Delphiで利用するためには、ヘッダを移植し、ライブラリ自体はCコンパイラでコンパイルし、Delphiの$Lコンパイラ指令を使ってリンクすればよいはず。

2009年8月14日 (金)

Win32デバッグ(8)・・・そして

今回は、前回までで必要なクラスの設計そして実装ができたので、約束通りドライバを作成して動かしてみる。

で、ドライバとTD32デバッグ情報付きの簡単なターゲットプログラムを用意して動かしてみたのだが・・・

ターゲットプログラムの動きがおかしい・・・weep

動的コールグラフを求めるにあたって、前回までに述べた通り、ブレークポイントを設定するためターゲットプログラムのプロセスのメモリ空間のコード領域を書き換えてるのだがどうもそこらへんの書き換えがおかしいっぽい・・・

で、調べる事数時間・・・

予期せぬ原因が分かったweep。やはり、ブレークポイントを設定する位置のアドレスの計算に問題があった・・・。再掲載するが現在は

のように、ルーチンの呼び出しを検出するために、ルーチンの開始位置と終了位置のアドレスをデバッグ情報から取得できるセクションインデックスと開始位置のオフセット・長さから求め、開始位置のアドレスの計算は合っているようなのだが、終了位置のアドレスの計算が間違っているようなのである。現在、終了位置のアドレスは上の25行目のように、

  終了位置のアドレス=開始位置のアドレス+ルーチンの長さ

で求めているのであるが、この式によって求めた終了位置のアドレスがルーチンによって想定外の位置を指してたりと・・・。要するに、上の式によって求めたルーチンの終了位置のアドレスは必ずx86命令のRET命令の先頭を指していると思っていたのだが、甘かった・・

そして、対応策を色々考えたのだが、どうも、まさかここまでする必要はないと思ってこのネタをやりだしたのだが、開始位置のアドレスは正しいようなので、開始位置のアドレスから自前で逆アセンブルして、x86命令のRET命令の位置を求めることに・・・(まぁ、必ずしもすべてのコンパイラがルーチンの呼び出し・リターンにx86命令のCALL・RET命令を使ったコードを生成するとは限らない?が、まぁ、そこらへんはよしとしよう・・)。

続く・・・

2009年8月13日 (木)

Win32デバッグ(7)・・・動的コールグラフ(クラス設計の続き)

今回は残りの必要なクラスをさっさと設計してまおうbearing。また、長くなりそうだが・・・

まずは、要となるクラス。動的グラフとはつまるところルーチンの呼び出し関係を表すグラフ理論?で言うところ有向グラフなのであるが、そのルーチンの呼び出しを表すノードを表すTCallGraphNodeクラスを次のように定義する。

Callerプロパティはこのノードが表すルーチンを呼び出したルーチンのノードをあらわす。

Calleesプロパティは、次に出てくるTCallGraphNodeListクラスのプロパティであるが、これはこのルーチンから呼び出されたルーチンのノードリストを表す。あるルーチンから同じルーチンが複数回呼び出される時、呼び出し毎にノードを作成しても良いがメモリ効率が悪そうなのでここでは1つにまとめることにする。

HitCountプロパティはこのノードが表すルーチンが呼び出された回数を表す(もちろん、前述のCallerプロパティのノードが表すルーチンによって呼び出された回数である)。

Symbolプロパティは呼び出されたルーチンを表す。

次に、TCallGraphNodeのリストを保持するTCallGraphNodeListクラス。

ここでは、1点だけ、先程、あるルーチン内から同じルーチンへの直接の呼び出しは1つにまとめると書いたが、そのため、このリストは同じシンボルを参照する複数のノードを追加しようとすると例外が発生するようにしてある。シンボルの等価性の判定には、ここでは単純にTSymbolクラスのインスタンスの参照を比較してるがまぁ、これで問題ないだろう・・

ふぅ。コーヒーブレイクcafe

次に、ここで定義したクラスをどう使うかであるが、対象プロセスがマルチスレッドアプリケーションの場合、その動的コールグラフってどうなるんだ??って事の解を探る気力が全くないbearing(最近、思考するのが疲れたので息抜きでブログ書いてるのであるが・・・)ので、ここでは、スレッド毎にコールグラフを作成する事にするのでTThreadクラスにいくつか修正を加えてみた。

CallGraphプロパティは動的コールグラフを表すTCallGraphクラスのプロパティである。初出のクラスであるが、これは動的コールグラフの先頭のヘッドノードのリストを管理するクラスである。なぜ、リストを管理する必要があるかと言うと、少し考えれば分かるのだが、ルーチンの呼び出し階層に現われるルーチンにすべてに対するシンボル情報が存在もしくては提供されるとは限らないので、動的コールグラフが切れる可能性があるためである。クラスの定義はここでは省略。

CallStackプロパティはコールスタックを表すスタックである。動的コールグラフを成長させる時、内部で一時的に使用する。ルーチンの実行の開始の検出時には、そのルーチンのノードをスタックにプッシュ、ルーチンの実行の終了の検出時には、スタックからノードをポップする。このようにして、現在実行されているルーチンを追跡する(スタックトップのノードのルーチンが現在実行されているルーチンになる)。まぁ、実際、スタックなんて大袈裟なものを容易しなくても、現在実行されているルーチンのノードを表す変数1個用意すればすむのだが・・・

いよいよ、終盤であるが。以上を踏まえて、実際に動的コールグラフを成長させるプログラムは例えば次のようになる。

以外にシンプルだった。以上。

次回以降は、実際に今まで定義したクラスを使うドライバプログラムを作成して実際に動かしてみようと思う。

ここまでのプログラムはいつものようにSkyDriveから。

2009年8月12日 (水)

Win32デバッグ(6)・・・動的コールグラフ(クラス設計)

今回はWin32デバッグの続きで、新たなクラスを設計してみるgood

まずは、動的コールグラフにおいて、シンボルを表すクラス。

Sectionプロパティはセクションインデックス、StartOffset・Lengthプロパティはシンボルというよりここでは関数や手続きもしくはメソッドつまりルーチンのセクション内での開始位置のオフセット・長さを表す。このクラスはDelphiの言語上厳密に、抽象クラスではないが、抽象クラス的な使い方をする。理由は後述。

お約束?通り、わざわざ専用のリストクラスを定義する必要ないかもしれないが、TSymbolクラスのリストを保持するTSymbolListクラスを次のように定義してみる。

このクラスのリストは実行を追跡するルーチンだけを保持するために使われる。つまり、コンパイラやリンカによって生成されるデバッグ情報のすべてのルーチンの実行を追跡するのではなく、必要に応じて選択できるようにする。

また、最終的なアウトプットとして動的コールグラフを画面に表示したりする必要があるのだが、その時、上記TSymbolクラスのセクションインデックスや開始位置のオフセット・長さなどを表示してもユーザーには区別できないので、ルーチンの名前など表示したりするわけだが、そのためには、例えば、TD32デバッグ情報の場合、例えば、

として、TSymbolListクラスのリストには実際にはこのクラスのインタンスを格納する事になる。デバッグ情報に格納されている情報はそれを生成したコンパイラやリンカもしくはフォーマットによって変わるので、このような設計にする。先ほどTSymbolクラスは抽象クラス的な使い方をすると書いたがこのためである(まぁ、ルーチンの名前はどのコンパイラ・リンカでも持ってると思うが・・・)。

次に、各ルーチンの実行の追跡においてブレークポイントを設定・解除する必要があるが、ここではそれ専用のクラスを設計する。まずは、1個のブレークポントを表すTBreakpointクラスを定義する。

Addressプロパティはブレークポイントのアドレスを表す、アドレスは上述したシンボルの情報を元に計算する。OrignalCodeプロパティはブレークポイントを設定する位置の元の命令コードを表す。ブレークポイントの設定では1バイトのINT 3命令(具体的には16進数の0xCC)で置き換えるので1バイトのByte型である。Symbolプロパティはブレークポイントの元になるTSymbolクラスのシンボルであり、Startプロパティはブレークポイントがその元になったシンボルの開始位置または終了位置のどちらに対応するかを表す。と、1つのTSymbolクラスのインタンスからルーチンの2つのTBreakpointクラスのインスタンスを作成する。

また、実際にブレークポイントを設定、解除するためのSetBreakpoint、ResetBreakpointメソッドをヘルパとして追加してある。実装はこんな感じ。

同じく、ブレークポイントのリストクラスであるTBreakpointListクラスを定義する。

リスト内のすべてのブレークポイントを設定・解除するSetAllBreakpointsメソッド、ResetAllBreakpointsメソッドをヘルパとして追加してある。

ふぅ。疲れたdespair

実際にこれらをどう使うかだが、デバッグ対象のプロセスのメインの実行可能モジュール(EXEファイル)内のルーチンだけ実行を追跡するならプロセスレベルのTProcessクラスでもよいのだが、前回に書いたように、DLLファイル内のルーチンの実行の追跡もできるように、TModuleクラスにTSymbolListクラスのSymbolsプロパティ、TBreakpontListクラスのBreakpointsプロパティを追加する事にする(もちろん、DLLのデバッグ情報が見つかればの話だが・・・)。

また、実際にブレークポイントを設定するために、CREATE_PROCESS_DEBUG_EVENT、LOAD_DLL_DEBUG_EVENTでブレークポイントを設定するように修正。

と長くなるので、今回はここまでで、2回にわけようと思うbearing。作ったソースは次回ダウンロードできるようにする。というより、クラス図書いた方が速いとか・・

自作ソフトウェア

無料ブログはココログ

メモ