« 2009年4月 | トップページ | 2009年7月 »

2009年5月

2009年5月11日 (月)

Win32デバッグ(5)・・・動的コールグラフ

前回までで、Win32のデバッグAPIそしてTD32デバッグ情報について述べ、これで、色々できるようになったので、なんか、実用的というより現実的?なソフトウェアを作ってみるhappy02

お題はタイトルにもあるように動的コールグラフ生成ツール。動的コールグラフとは、実行時の各ルーチンの呼び出し関係を表すグラフの事であるが、これを生成するツールを作ってみる。

ということで開始するのであるが、今回はそのための基本的な事柄・設計や処理のおおまかな流れについて。

まず、動的コールグラフを作成するには各ルーチンの呼び出しを検出する必要があるが、この方法について。色々なやり方があるのかもしれないが、ここでは、x86系プロセッサに備わっているソフトウェアブレークポイントと呼ばれるINT 3命令(ブレークポイント命令)とシングルステップモードを使用する方法を採用する。

INT 3命令とは、x86プロセッサの命令の1つで、実行されるとブレークポイント(BP)例外というトラップクラスの例外が発生する。例外が発生するとCPUは指定された例外ハンドラと呼ばれるルーチンに制御を移すが、通常、その例外ハンドラはOSによって提供され、Windowsの場合、INT 3命令を実行したスレッドのプロセスがデバッグされていると、例外コードEXCEPTION_BREAKPOINTのDEBUG_EXCEPTIONイベントとして、通知されるのでこれをルーチンの開始と終了の検出に利用する。

具体的にはプロセスのアドレス空間内のルーチンのコードの開始と終了位置の命令をINT 3命令に置き換え、ブレークポイントを設定する。置き換えられた元の命令はルーチンの開始または終了の検出後に実行する必要があるので退避する。また、ルーチンのコードの開始と終了位置はロードされたモジュールのベースアドレスとデバッグ情報から求まる。

DEBUG_EVENTイベント発生時(ブレークポイント例外)には、ルーチンの開始または終了の検出に応じてコールグラフを成長させる。また、ContinueForDebugEvent関数による実行の再開において、INT 3命令で置き換えられた元の命令を実行する必要があるので、ブレークポイントを解除し、元の命令に戻すのであるが、この時、再び同じルーチンの呼び出しを検出するため、シングルステップモードで実行を再開する。シングルステップモードにするとCPUは1命令を実行した直後にフォルトクラスのデバッグ(DB)例外を発生させる。これはWindowsでは例外コードEXCEPTION_SINGLE_STEPのDEBUG_EXCEPTIOINイベントとして通知される。

DEBUG_EVENTイベント例外発生時(シングルステップ例外)には、上記の通り、該当のブレークポイントを再設定する。

これらをデバッグイベント別にまとめると次のようになる。

  • CREATE_PROCESS_EVENTイベント発生時
    • ロードされたモジュールにデバッグ情報があれば、必要に応じて各ルーチンのコードの開始・終了位置にブレークポイントを設定
  • LOAD_DLL_EVENTイベント発生時
    • ロードされたモジュールにデバッグ情報があれば、必要に応じて各ルーチンのコードの開始・終了位置にブレークポイントを設定
  • UNLOAD_DLL_EVENTイベント発生時
    • アンロードされたモジュールで設定されている全てのブレークポイントを解除
  • EXIT_PROCESS_EVENTイベント発生時
    • アンロードされたモジュールで設定されている全てのブレークポイントを解除
  • EXCEPTION_EVENTイベント発生時
    • ブレークポイント例外(例外コードEXCEPTION_BREAKPOINT)
      • ブレークポイント例外が発生したアドレスから自身が設定したブレークポイントによる例外かを判定。もし、そうならブレークポイントを解除し、その位置からシングルステップモードで実行再開
    • シングルステップ例外(例外コードEXCEPTION_SINGLE_STEP)
      • 該当のブレークポイントを再設定。

こんな感じだろうか???(LOAD_DLL_EVENT、EXIT_PROCESS_EVENTイベント発生時にブレークポイントをわざわざ解除する必要がないかもしれない・・・とかそういう細かい事は気にしないbearing)。

今回はここまでで、次回実際にクラス設計を行ってみる。

2009年5月10日 (日)

Win32デバッグ(4)・・・デバッグ情報(Borland/CodeGear編)

今回は具体的にデバッグ情報の中身を実際に覗いてみるgood。覗いてみると宣言するように、デバッグ情報の構造やファイルフォーマットなどの詳細まで書くと確実に倒れるbearingので、あくまでイメージを掴むために覗くだけにする。ははは・・。というより、基本的にファイルフォーマットはコンパイラなどの開発環境のメーカーによってその製品のバージョンアップ時に改訂されることがあるので、デバッグ情報にアクセスするためのインターフェースだけを公開し、フォーマットの詳細は公開しないのが現在の流れであるようなので、フォーマットの詳細は知りませんbearing

また、Microsoftの開発ツールによって生成されるデバッグ情報はMicrosoftのよって公開されているDbgHelpライブラリもしくはDIA(Debug Interface Access) SDKを使ってアクセスできるので、詳細はMSDNを参照ということで。DbgHelpライブラリはOSインストール時にインストールされるが、DIA SDKはデフォルトでインストールされていないので、使用する場合には、Visual Studioの統合開発環境をインストールするか、もしくは、再配布可能パッケージvcredist_x86.exeをインストールし、COMサーバーなのでmsdiaXX.dll(XXは80とか90)をregsvr32.exeなどでシステムに登録する必要がある。

ここではタイトルにもあるように自分が使っているBorland/CodeGearの開発ツールiよって生成されるデバッグ情報の中身を覗いてみるのであるが、DelphiのIDEに統合されている統合デバッガが利用しているデバッグ情報にアクセスするためのAPIが公開されていないのである。ガーンcoldsweats02

ということ終了・・って事になるなら、普通は初めからこんなタイトルの記事を書かない事からも分かるように、ここでは、TD32デバッグ情報と呼ばれるデバッグ情報を生成でき、また、アクセスするためのAPIも公開されているのでその中身を見てみる。歴史は詳しくわからないが、TD32デバッグ情報はTurbo Debuggerと呼ばれる社名もまだBorlandの頃の古いデバッガで利用していたデバッグ情報の事だと思う・・・

まず、デフォルトではTD32デバッグ情報は生成されないので、TD32デバッグ情報の生成の仕方から。

適当なプロジェクトを作成して、「プロジェクト」メニューから「オプション」メニューを選択、そして、「リンカ」設定を選択、

Win32debug_4_1_3

上の画像のように「EXEとDLLのオプション」の「TD32デバッグ情報を含める」にチェック。

Win32debug_4_2_2

以上であるが、「コンパイラ」設定の「デバッグ情報」「ローカルシンボル」あたりのオプションも生成されるTD32デバッグ情報の詳細レベルの影響するので・・

で、プロジェクトをコンパイルする。これによってTD32デバッグ情報を含んだ実行可能ファイルが生成される。

TD32デバッグ情報にアクセスするためのAPI(BorDebug.DLLとそのCヘッダ)はCodeGearの以下のサイトからダウンロードできる。

また、BorDebug.DLLの説明とDelphi用のヘッダファイルは以下のサイトからダウンロードできる。

英語の文書を読むのが苦にならない人は上記のサイトからダウンロードできるPDFファイルとCヘッダのコメント読んだ方が手っ取り早いので・・bearing

でいよいよ、実際にTD32デバッグ情報を覗いてみるのだが、そのためのツールTD32Scannerを作ってみた(製品に付属のTDUMP.EXEを使えば、中身をダンプできるのであるが、後でBorDebug.DLLを使ってプログラムからアクセスしたいので、その使い方を覚えるために作ってみたbearing)。実行画面は次の通り。

Win32debug_4_3_2

左側の「SubSection Headers」ウィンドウにはTD32デバッグ情報に含まれるサブセクションのヘッダ一覧が表示されている。最初にデバッグ情報の構造やファイルフォーマットまで詳細に書かないと述べたが、話を進めるために最低限の事は書こうと思う。、まずは、サブセクションから。サブセクションとはTD32デバッグ情報に含まれる論理的な情報のグループのことで、サブセクションに含まれる情報によって、次のタイプに分けられる。

  • Module
  • SrcModule
  • AlignSym
  • GlobalSym
  • GlobalPub
  • Names
  • GlobalTypes
  • Browse

順にこれらのうちのいくつかを見ていくが、その前に今回は、次の簡単なコンソールプログラムのTD32デバッグ情報を例にとり話を進めるので・・

1×1から9×9まで計算するプログラムで、Multiplyは2つの値を積を返す関数である。

まずは、Moduleサブセクション。Moduleサブセクションはモジュールに関する情報が格納されているサブセクションである。Delphiでいうとモジュールは.DCU(Delphi Compiled Unit)ファイルに相当する。上の画像より今回の例ではModuleIndexが1から7までの7つのModuleサブセクションがあることがわかる。「SubSection Headers」ウィンドウのグリッドからModuleIndexが7のModuleサブセクションをダブルクリックしてその詳細を見てみると、次のようになる。

Win32debug_4_5_2

上の画像のNameIndexより、モジュールTD32Target.DCUに関するModuleサブセクションであることが分かる。

また、Segmentsグリッドにはそのモジュールに含まれるコードが最終的な実行可能ファイルのどこに含まれているかの対応関係を表す情報が表示されている。

Windowsにおける実行可能ファイルのフォーマットであるPE(Portable Executale)フォーマットの場合、コンパイラによって生成される機械語命令は最終的にはセクションと呼ばれるブロックに格納されるが、先ほどのSegmentsグリッドのSegment列はそのセクションのインデックスを表す。

この事から、先ほどの画像より、例えば、TD32Target.DCUに含まれるコードがセクションインデックス1の(コード)セクションのオフセット0x00007070からオフセット0x00007213までの420バイトの領域に含まれている事が分かる(ややこしいが、TD32Scannerにおいて、Segmentと表記されているものは、PEファイルにおけるセクションのインデックスを表すので注意・・・・bearing)。

次に、ソースに関する情報が格納されているSrcModuleサブセクション。ここでは、TD32Target.DCUの元になるソースに関する情報を見るとして、「SubSection Headers」ウィンドウのModuleIndexが7のSrcModuleサブセクションをダブルクリックする。

Win32debug_4_6

SourceFilesグリッドより、TD32Target.DCUは1つのソースファイルTD32Target.dprから構成されていることが分かる。

次に、ローカルシンボルに関する情報が格納されているAlignSymサブセクション。SrcModuleの時と同じように、TD32Target.DCUに含まれるシンボルを見るとして、ModuleIndexが7のAlignSymサブセクションをダブルクリックする。

Win32debug_4_8

Symbolsグリッドには含まれるすべてのシンボルが列挙されている。SymbolKindはシンボルの種類を表すがその一覧はここでは省略するとして、ここではローカルプロシージャを表すシンボルであるLPROC32シンボルの詳細を見てみる。例えば、上の画像において、上から2つめのLPROCシンボルを選択してみる。

Win32debug_4_9_2

NameIndexより自分で定義したMultiply関数のシンボルである事が分かる。また、Segment、Offset、Lengthより、この関数の機械語命令のコードがセクションインデックス1のセクションのオフセット0x00007070から0x0000708Bまでの28バイトの領域に含まれていることが分かる。

とまぁ、残りは省略するが、デバッグ情報の内容とはこんな感じなのである。

作ったTD32ScannerはSkyDriveにアップロードしておいた。現在、GlobalTypesサブセクションにはしっかり対応していないのでsad。型に関する情報多すぎbearing

ふぅ。

2009年5月 5日 (火)

Win32デバッグ(3)・・・デバッグ情報

前回までで、Win32のデバッグAPIなどを見てきて、GetThreadContextやWriteProcessMemory関数を使えば、スレッドのコンテキストやプロセスのアドレス空間のメモリを書き換えることができることなどと書いたが、デバッガやプロファイラなどの実用的なソフトウェアを作成するにはこれだけではまだ不十分なのである。

ということで、今回はそのような実用的なソフトウェアを作成するために必要となるであろうデバッグ情報について。

デバッグ情報とは、コンパイラやリンカによって生成される実行可能モジュールなどをデバッグするために必要な情報(そのまんまじゃん・・wobbly)、例えば、ソースファイル内で定義した関数なら、実行可能モジュールを実行すると、ローダーと呼ばれるOSのコンポーネントによって、実行可能モジュールのイメージがプロセスのアドレス空間にロードされ、実行が開始されるが、ロード後のプロセスのアドレス空間における関数のコードの開始と終了位置のアドレスを求めるために必要な情報などそのような類似の情報の総称の事である。

生成されるデバッグ情報の詳細レベルは使用しているコンパイラやリンカの種類やまたそれらのオプションスイッチによって変わるが、まぁ、ソースファイル内の行からロード後のアドレスを求めるための行レベルの情報など程度の差こそあれ基本的に似たような情報が格納されているのでここでは問題にしない。

また、デバッグ情報はその情報がシンボルとして格納されているのが一般的なのでシンボリックデバッグ情報とも呼ばれる。シンボルには、宣言した変数や関数などのソースファイル内に現われる具体的なシンボルの他にもソースコードをコンパイル・リンクしたコンパイラやリンカの情報を表すシンボルなどもあり、これも使用しているコンパイラやリンカによって異なるでここでは問題にしない。

Microsoftの統合開発環境Visual Studioを使ってる場合、基本的にそのような情報は拡張子PDBのプログラムデータベースファイル(Program Database File)と呼ばれるファイルに格納され、自分が使ってるBorland/CodeGearの統合開発環境だとorz・・・・

と今回はこれぐらいにするが、ちょっと抽象的な説明ばかりなので、次回以降に具体的にデバッグ情報というものの中身を簡単に覗いてみようかなと・・

最後に、前2回、えらく、長くなってしまったが、ココログにホームページを作成する機能があるっぽさそうなので、ホームページとして作成すれば良かったのかもdespair。まず、このブログの幅leftrightが狭いので広げたいのであるが・・・とりあえず、このネタはブログの記事として終わらせるとして、今度使ってみようgood

2009年5月 4日 (月)

Win32デバッグ(2)・・・クラス設計

前回はWin32のデバッグAPIの基本的なことを書いたが、今回はより実践的な事をやろうと思う。

前回、待機関数WaitForDebugEventでDEBUG_EVENT構造体に発生したイベントに関する情報が返されると書いたが、最初にこの構造体を内容を簡単に見てみる。まずは、DEBUG_EVENT構造体の定義から(定義だけはC言語を使うので、あしからず・・)

前半の3つメンバと後半の共用体の部分から構成されている。まずは、前半の3つのメンバから。これらは前回も触れたが、dwDebugCodeEventには、デバッグイベントの種類、dwProcessId、dwThreadIdにはデバッグイベントが発生したプロセスとスレッドの識別子がそれざれ返される。残りの共用体の部分には、デバッグイベントの種類に応じた追加の情報が返されるのであるが、例えば、CREATE_PROCESS_DEBUG_EVENTイベント発生時には、共用体の部分にCREATE_PROCESS_DEBUG_INFO構造体として返される。

順にCREAET_PROCESS_DEBUG_INFO構造体の定義は

となっている。例えば、上記構造体において、hProcess、hThreadには作成されたプロセス、スレッドのハンドルが返されるのであるが、シングルスレッドアプリケーションをデバッグするのならまだしも、マルチスレッドアプリケーションさらにはCreateProcess関数でDEBUG_PROCESSオプションを指定して、子孫のプロセスまでデバッグする場合、これらの情報を適切に管理しておくと、デバッガAPIを使った何かしらの実践的なアプリケーション(例えば、デバッガ自体やプロファイラなど)を作成する時に楽なので、それらをさっさと設計してしまおうというのが今回の趣旨なのである。

と、前置き長すぎbearing

ということで、ここでは、クラスとして設計するのであるが、まずは、プロセスから。デバッグ対象のプロセスの作成、終了は述べたとおり、CREATE_PROCESS_DEBUG_EVENT、EXIT_PROCESS_DEBUG_EVENTイベントで通知され、また、プロセスはプロセス識別子で一意に識別できるので、例えば、プロセスを表すクラスTProcessは次のようになる。

ProcessIdプロパティはプロセスのプロセス識別子、Handleプロパティはプロセスのプロセスハンドルを表す、また、プロセスは1つ以上のスレッドから構成されるので、そのリストであるTThreadListクラスのThreadsプロパティ、同様に、プロセスは、1つ以上のモジュールから構成されるので、そのリストであるTModuleListクラスのModulesプロパティからなる。また、プロセスを強制終了するTerminiateメソッド、プロセスのアドレス空間のメモリを読み書きできるようヘルパーとしてReadXXX、WriteXXXメソッドも追加してある。ReadXXX、WriteXXXメソッドの実装は前回に出てきたAPIを使って、こんな感じ。

次に、複数のプロセスをデバッグできるようにTProcessクラスのリストを保持するTProcessListクラスを定義する。

Addメソッドはリストに指定のプロセス識別子のプロセスを追加する。戻り値は上記のTProcessクラスのインスタンスで、重複したプロセス識別子を指定すると例外が発生する。Removeメソッドは、指定のプロセス識別子のプロセスをリストから削除する。Itemsプロパティは指定したプロセス識別子のプロセスを表すTProcessクラスのインスタンスを返す(存在しなければnil)。

同様に、スレッドを表すTThreadクラス、モジュールを表すTModuleクラスとそれらのリストクラスであるTThreadList、TModuleListクラスを定義する。プロセス、スレッドの場合はそれぞれプロセス識別子、スレッド識別子で一意に識別できるが、モジュールの場合は、モジュールがロードされたアドレスを使って一意に識別する(実行イメージやDLLはローダーによって衝突しないようプロセスのアドレス空間に配置されるのでロードされたアドレスはプロセス内で一意である、また、CREATE_PROCESS_DEBUG_EVENT、EXIT_PROCESS_DEBUG_EVENT、LOAD_DLL_DEBUG_EVENT、UNLOAD_DLL_DEBUG_EVENTイベント発生時にDEBUG_INFO構造体にそのアドレスが返される)。

TProcessとそのリストTProcessListと同様の設計になっているが、TThreadクラスには、スレッドのコンテキストを取得・設定するGetContextメソッド、SetContextメソッドを、また、スレッドを再開・停止・強制終了するResume、Suspend、Terminateメソッド、TModuleクラスには、モジュールのPEファイルのセクションヘッダを読み込むReadSectionHeadersメソッドをヘルパとして追加してある。

と、以上のクラスを定義すれば、各デバッグイベントのイベントハンドラは例えば次のように書ける。

最後に今回、作成したプログラムのダウンロードはSkyDriveのここから。これだけじゃ、ドライバが無いのでコンパイルもできないがとりあえず、アップロードしといた。Debug.pasは、今回作成したTProcessなどのクラスを定義したユニット、Debugger.pasでは、デバッグループやイベントハンドラを含むTDebuggerクラスを定義してある。複数のプロセスをデバッグできるようスレッドとして動作するようにしてある。

以上。ふぅhappy02

2009年5月 3日 (日)

Win32デバッグ

プログラミングを行ってる人はソフトウェアを作成する過程で特定のソースコード位置などで実行を停止して、その時の変数の値を確認したり、また、1行ずつコードを実行して、処理の実際の流れを確認したりと、デバッガと呼ばれるソフトウェアもしくは機能を利用する事になると思うが、今回はそのようなデバッガやパフォーマンスプロファイラなどを作成するためにWindowsで提供されているデバッグAPIについて。

Windowsで提供されているデバッグAPIはイベント駆動型のモデルを採用していて、デバッグ対象のプロセス内でデバッグイベントと呼ばれるイベントが発生するのを待機し、発生したら発生したイベントに応じて処理を行うという、Windows上でGUIアプリケーションを作成する時に見かけられるWindowsメッセージを処理するためのメッセージループに似たループを行うことになる。発生するデバッグイベントには次のようなものがある。

デバッグイベント
種類 概要
CREATE_PROCESS_DEBUG_EVENT 新しいデバッグ対象のプロセスが作成される、または、既存のプロセスにデバッグのためアタッチされた時に発生
EXIT_PROCESS_DEBUG_EVENT デバッグ対象のプロセスが終了される時に発生
CREATE_THREAD_DEBUG_EVENT デバッグ対象のプロセス内で新しいスレッドが作成される時に発生
EXIT_THREAD_DEBUG_EVENT デバッグ対象のプロセス内のスレッドが終了する時に発生
LOAD_DLL_DEBUG_EVENT デバッグ対象のプロセス内でDLLがロードされる時に発生
UNLOAD_DLL_DEBUG_EVENT デバッグ対象のプロセス内でDLLがアンロードされる時に発生
EXCEPTION_DEBUG_EVENT デバッグ対象のプロセス内で例外が生成された時に発生
OUTPUT_DEBUG_STRING_EVENT デバッグ対象のプロセス内でOutputDebugString関数が呼び出された時に発生
RIP_DEBUG_EVENT

上記イベントの厳密な発生条件などはMSDNのここを参照。

新規にプロセスを作成してデバッグを開始するには、CreateProcess関数で第6引数dwCreationFlagsにDEBUG_PROCESSオプションを指定する。DEBUG_PROCESSを指定するとCreateProcess関数で作成されたプロセスによって作成される子孫のプロセスもデバッグ対象になるが、CreateProcess関数で作成されるプロセスだけに限定する場合は、DEBUG_PROCESSオプションの代わりにDEBUG_ONLY_THIS_PROCESSオプションを指定する(紛らわしいがDEBUG_PROCESSまたはDEBUG_ONLY_THIS_PROCESSのどちららかを指定する。論理和とって両方指定するのではない.ので注意)。

また、既存の実行中のプロセスのデバッグを開始するには、DebugActiveProcess関数で引数に既存の実行中のプロセスのプロセス識別子を指定し、プロセスにアタッチする。

CreateProcessもしくはDebugActiveProcessでデバッグを開始した後は、先ほど述べたループに突入するのであるが、Delphiで書くと全体の流れは次のような感じなる。

WaitForDebugEventはデバッグイベントが発生するのを待機する待機関数で、第2引数にタイムアウトを指定する。第2引数にINFINITEを指定するとこの関数を呼び出した側へWaitForDebugEvent関数から制御が返らない。デバッグベントが発生すると第1引数で指定したDEBUG_EVENT構造体に発生したイベントの種類、イベントが発生させたプロセスとスレッドの識別子、イベントの種類に応じた追加の情報が返される。

デバッグ対象のプロセス内でデバッグイベントが発生するとデバッグ対象のプロセス内のすべてのスレッドの実行が停止されるが、ContinueDebugEventで実行を再開する。

ContinueDebugEventの第1、2引数にはデバッグイベントが発生して、デバッグ対象のプロセスが停止した原因となったプロセス、スレッドの識別子を指定するが、これらの値はDEBUG_EVENT構造体のdwProcessId、dwThreadIdにWaitForDebugEvent関数から返されているのでこれらの値をそのまま指定する(もちろん、デバッグ対象がシングルスレッドアプリケーションで、CreateProcess関数を使ってデバッグを開始するのならCreateProcess関数で返されるPROCESS_INFORMATION構造体の値を指定してもいいのだが、DEBUG_EVENT構造体の値を使った方が汎用的である)。

ContinueDebugEventの第3引数には以下の続行オプションのいずれかを指定するのであるが、これは、ややこしいbearing

  • DBG_CONTINUE
  • DBG_EXCEPTION_NOT_HANDLED

完全に説明すると、Windowsの構造化例外処理(Structured Exception Handling)の話から書かないといけないので、EXCEPTION_DEBUG_EVENTイベント発生時以外は、MSDNのドキュメントに書いてあるように上記2つのオプションに違いはないので、上記サンプルではDBG_CONTINUEオプションを、EXCEPTION_DEBUG_EVENTイベント発生時は、発生した例外の種類がシングルステップ例外(EXCEPTION_SINGLE_STEP)、ブレークポイント例外(EXCEPTION_BREAKPOINT)以外の時は、通常の例外処理を行わせるためにDBG_EXCEPTION_NOT_HANDLEDを指定、シングルステップ例外、ブレークポイント例外の場合は、DBG_CONTINUEを指定した。

ということで。ははは。

とMSDNのドキュメントを反復してるだけでbearing、MSDNのドキュメントを読んだ方が正確だし手っ取り早いのであるが、ここでは、実際にデバッグAPIを使った時の注意点をいくつか。

まず、CREATE_PROCESS_DEBUG_EVENT、LOAD_DLL_DEBUG_EVENTイベント発生時に返されるファイルハンドル(hFileメンバ)について。これらイベントの発生時、DEBUG_EVENT構造体を通して、ロードされたプロセスの実行イメージのファイルハンドル(CREAET_PROCESS_DEBUG_EVENTの場合)、もしくは、ロードされたDLLのファイルハンドル(LOAD_DLL_DEBUG_EVENT)が返されるが、MSDNのドキュメントにもあるように、使い終わったらこのハンドルは明示的にCloseHandle関数でクローズしないとハンドルがオープンされたままになるので注意。上記のプログラム例では、88行目あたり。

また、

特に、EXIT_PROCESS_DEBUG_EVENTイベント発生時に、発生直後すぐにブレイクしてデバッグループを抜ける例をよく見かけるが、これだと、CREATE_PROCESS_DEBUG_EVENTイベントなどで返されるプロセスハンドルやスレッドハンドルがオープンされたままになるので、通常は1回、ContinueDebugEvent関数を呼んだ後にループからブレイクする方が無難である(ContinueDebugEvent関数で自動的にハンドルがクローズされるので)。上記のプログラム例では、66行目あたり。

最後は、WaitForDebugEvent関数はDEBUG_PROCESSまたはDEBUG_ONLY_THIS_PROCESSオプションを指定したCreateProcess関数またはDebugActiveProcess関数を呼んでデバッグを開始したスレッドでしか呼ぶことができないとの事。

とここまでくれば、後はデバッグAPIの他の関数である

  • ReadProcessMemory
  • WriteProcessMemory

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

  • GetThreadContext
  • SetThreadContext

を使えば、スレッドのコンテキスト(x86プラットフォームのWindowsの場合は、EAXやEBXなど各レジスタなど)を読み書きできる。これらのAPIは何もDEBUG_PROCESSまたはDEBUG_ONLY_THIS_PROCESSオプションを指定したCreateProcess関数によって作成されたプロセスやDebugActiveProcessでアタッチしたプロセスでなくてもよいのだが、使用する時は基本的に対象のプロセス・スレッドをSuspendThread関数などで停止していないとおかしくなると思う(WaitForDebugEvent関数でデバッグイベントを受信した時はデバッグ対象プロセス内のすべてのスレッドが停止しているのでSuspendThread関数で停止する必要はないが・・・)。

以上長々と書いたが次回以降は具体的に使ってみようと。

というより、TrueType/OpenTypeフォントの時の成果物であるT2FAnalyzerと同じようにブログ書きながら最終的に実用的?なソフトウェアが出来上がればいいのであるが・・・ははは。

2009年5月 1日 (金)

もう既に今年も5月か

時が経つのは速いな。もう、5月に突入。今年はまだブログ全然書いてないなsad。ネタがないというより、2月、3月当たりはプログラミングすらもやる気起きなかったけど、先月は新しい面白いネタが見つかったので、それに向かってばく進してましたbearing。まだ、途中なのですが既に燃え尽きそう。頑張って、今月いっぱいまでモチベーションを維持しなければhappy02。それまでにはある程度、成果物が完成するだろうし、そしたら、それをネタに色々ブログが書ければ幸い。モチベーション持たないと、再びやる気起きるまで半年ぐらいはかかるだろうし、やばいsad

まぁ、流れ的には去年と似てるな。去年は4月ぐらいにTrueType/OpenTypeフォントの仕組みがムショウに知りたくなって調べ始め、T2FAnalyzerとして公開して、その後、ODBCについて気になりだし・・、去年の締めくくりのブログにも書いたが、更にODBCやら10進浮動小数、更には、字句解析から始まり構文解析そして意味解析を勉強しながら、自作スクリプト言語の作成、Unicode絡みでUniscribeも色々いじくった。けど、成果物として公開できたのはT2FAnalyzerだけdespair。気合入れて色々プログラム書いたんだけどなんかもったいないなぁ。まぁ、中途半端なのは訳があって実装方法なりクラス設計で行き詰まるってるからなんだけど。ははは。まじで笑えんsad。それらに対するブレークスルーというか解決策が全く想像できん。ぐぉーーーーーーーーーwobbly。解決できれば、色々公開して、それをネタにブログ書けるんだけど(まぁ、こういうものは完全に自己満足の世界のお話であって、他人に役に立つかは別問題だけど)。

という事で今やってる事が、行き詰まらずにある程度完成して無事に日の目が見られる事を祈ろう。南無南無。ははは。

と、ブログでストレスを発散するのであった。スッキリgood

あー。そうそう、定期的に自分のブログのアクセス解析を見てるけど、フォント関連が一番多くて、次にDB2当たりかな?何気にDB2ユーザーいるのかな?というより、DB2絡みの情報がインターネットに少ないからかな?

ふむ。

あー。そういえば、Windows7でDirectWriteが導入されるとの事でそれも調べったっけ。インターフェース見たぐらいだけど。MicrosoftのUnicodeテキストを扱うルーチンの最終形のような気がする。単純なファンクションベースのUniscribeから始まって、オブジェクト(クラス)ベースの.NETのWPF(Windows Presentation Foundation)で、オブジェクトベースというよりインターフェースベース(確か、IDispatchインターフェースは継承してなかったと思うので・・)?のCOM(Component Object Model)のDirectWriteて流れかな。

« 2009年4月 | トップページ | 2009年7月 »

自作ソフトウェア

無料ブログはココログ

メモ