コレクトAPIコールログへの簡単な方法

それが問題をデバッグするときに、1つまたは複数の機能への呼び出しについての情報を記録することで、共通の課題に直面する可能性があります。あなたがプログラムをお知りになりたい場合は、あなたのソースコードの機能を持っている、あなたには、いくつかのデバッグプリントと再建プログラムを追加することができ、時にはそれが実用的ではありません。たとえば、あなたは常に問題を再現することができないかもしれないので、それは実現可能ではないかもしれない、あなたの再生を吹き飛ばす可能性があるので、それは、デバッグビルドを再起動する必要があります。それとも、もっと重要なのは、ソースコードのない関数の呼び出しを(またはビルドへのプログラムの一環として、修正しないか、したくない)を記録する必要があるかもしれません。
たとえば、あなたがあることの問題のトラブルシューティングに関する情報を得るために、Windows API呼び出しのさまざまなを記録したい場合があります。さて、あなたがやっている仕事によると、あなたは、デバッグと仕事を得るために、各特定のAPI呼び出しの前に印刷した後に追加することができます。あなたはどのような場合には、あなたはこのルートを行くことができない、直前の呼び出し元の機能をログインしていない場合は、これは、非常に簡単で、通常ではありません。
あり、多くのAPIのスパイ/ APIロギングソフトウェアパッケージ(Windowsのは、それは非常に壊れやすい傾向にあるものの、でもリリースは、ロガーと呼ばれるソフトウェア・パッケージに付属のツールをデバッグする-私にとって個人的に私は、多くの場合、それがクラッシュに遭遇、というよりも、仕事中に遭遇した現実的な問題)。多くの制約が、あなたがそれらのいずれかを使用しますが、ロギングツール「シュリンクラップ」することができ、彼らは適切に記録する機能のカスタム関数の呼び出し、またはログ記録ツールは知らないのか分からないということです。彼らは通常、ユーザーを可能にする(つまり、あなたが)ログそれらを行うためには、引数と呼び出し規約を記述しているスクリプト言語やプログラミング言語のいくつかの並べ替えを提供しますので、ある程度、より良いロギング機能は、ユーザーにスケーラブルです。
しかし、それはこれらのツールの機能の多くの種類を記述するためにしばしば困難(不可能ではない)である、例えば、関数ポインタは、他の構造体を指すポインタ、または他の構造などの重要でないを含む構造体が含まれています。そのため、多くの場合、私は、関数呼び出しの場合は、いわゆる「シュリンクラップ」APIロギングツールの使用を記録する必要性をお勧めしない傾向があります。

それは、ソースコードでソリューションをデバッグ実行可能な印刷なので、表面でない場合は、それは、レコードのコールに対する解決策を利用できるようにしません。実際にはない場合-それはあなたが、多くの場合、これを提供するために(この記事の残りの部分が言及されているようにWinDbgを/ NTSD / CDB / KDなど、)デバッガを使用することができ、いわゆる「条件付きブレークポイント」を慎重に使用することにより、判明コールログの種類。デバッガを使用すると、多くの利点があり、たとえば、あなたは、このようなロギングAPIの実装を「飛ぶ」ことができ、そしてプロセスは付属ケースデバッガの後に開始することができ、あなたもそれを記録を開始するために特別なプログラムを必要としません。しかし、いっそ、データの広範囲のためのデバッガサポートは、意味のある形でユーザに表示されます。
あなたはそれについて考える場合は、ユーザデータに表示されているが、実際にデバッガの主な機能の一つです。その複雑なデータ構造が表示され、有意義な方法で解釈することができますので、これは、主な原因は、高度にスケーラブル持つデバッガによって拡張されます。APIロギングがデバッガを使用することによって実行され、コールログとして機能するように、デバッガ(およびその拡張、そしてあなた自身を記述することも、任意のカスタム拡張機能)に焼かれた表示データに豊富な機能を利用することができます。
デバッガがシンボルファイルに基づいて行うことができるので、読んで、有意義な方法でデータの多くの種類を表示するには(あなたがプライベートなコンパイラなどの記号、または提供されている場合)、およびさらに良いことに、これらのデータ型は、それらを表示するために使用されていません特定デバッガ(例えば!ハンドル!エラー(エラーコード)!devobjとsoforth)の膨張は、一般的に容量デバッガフォーマットされた情報シンボルデータのタイプに基づいて使用することができます。これは通常、DTコマンドによって行われ、通常、ほとんどのカスタムデータ型は、ログという複雑な「トレーニング」画像処理プログラムを実行することなく、実行可能なように表示されます。そのような木やリストなど(もっとインテリジェンス特定のデータ構造は、()のデータ構造のすべての部分でDTよりも、表示に必要な場合があります。一般的に真実である「コンテナ」データ型については、あなたのことも、これらのタイプのためのが、 dtは、まだ多くの場合、コンテナの実際のメンバーを表示するために有意義な方法で使用することができます。)シンボルファイル(デバッガによってAPIログに含まれる情報)を使用しても、ロギングプログラムは、構造および他のすべてのタイプを定義していないことを確認してくださいデバッガは自動的に記号正しい定義(サーバーで使用されるシンボルは、インデックス記号の独自の内部バージョンが含まれている場合は、シンボリックデバッガにも自分自身を見つけることができます)に基づいて受信するので、あなたのプログラムとの同期は、デバッグされています。

この方法のもう一つの利点は、あなたがデバッガでかなり精通している場合、あなたはAPIロガーで説明したように新しい言語を学ぶように持っていないかもしれないということです。すでにデバッガからデバッガに精通しているかもしれないとコマンドの多くは、提供される表示の毎日のデータを使用しているためです。(デバッガに精通していないが、豊富なドキュメントが付属していますデフォルトのデバッガで様々なデバッガを通じてどのようにフォーマットして表示コマンドデータに説明しています。また、多くの例は、インターネット上のほとんどを使用する方法について説明があった場合でも、重要または有用なデバッガコマンド。)
さて、あなたはコールログを十分に実行するために、デバッガを使用することを検討したい理由について。次回、およびこれを行う方法を迅速ウォーク(先に述べたように、それは非常に簡単です)、同様に注意が必要になることがあり、いくつかの検討事項や問題。

デバッガのコールログを使用して実行することはそれほど難しいことではありません。関連する基本的な考え方は、あなたが興味を持っている関数の先頭に(bpのコマンドによって、例えば)「条件付き」にブレークポイントを設定することです。そこから、あなたはブレークポイントコマンド入力パラメータを表示することができます。デバッグされるプログラムの特性に応じて、これは問題であり得るものの、いくつかの場合(例えば、ディスプレイの戻り値、出力パラメータ値、等)で、あなたが賢くなることができ、それは問題ではないかもしれません。
簡単な例として、私が意味する、「はCreateFileによって開かれたすべてのファイルを表示します。」古典あり このため、この方法は、KERNEL32!Cre​​ateFileWにブレークポイントを設定することです。あなたは通常は同時に両方のAPIを取得するために「W」バージョンにブレークポイントを設定することができるように(「A」のほとんどはWin32APIのは、APIの「W」にジャンプしますことを心に留めておいてください。もちろん、これは必ずしも正しくありません(などのWinInetなどの一部の奇怪なAPIは、「」ゴツンと「W」から実際にある)が、一般的な経験則として、これはよくあるケースです。)kernel32のブレークポイントは、どのように日常的に議論している必要があります最初のパラメータの呼び出し規約ディスプレイ知識。CreateFileはSTDCALLであるので、それがあるべきである[ESP + 4](x86用)および(x64の場合)RCX。

次のように最も基本的なケースでは、ブレークポイントのコマンドは次のようになります。

0001 > BP kernel32のCreateFileW!" デュポイ(@ ESP + 4); GC "

(注、GCコマンドgはあなたがGC制御とブレークポイントの実行をトレースした場合、それはユーザ制御プログラムの実行が再開されます。条件付きブレークポイントやデザインで使用するために設計されていますが、無条件されていないことを除いて、似ています通常の使用グラムブレークポイントと使用ブレークポイントを復元することは、GCの違いは、GCトラックが破損する場合、その次の命令にトラックであり、追跡グラムブレークポイントならば、制御がフルスピードで再開され、あなたが失うことになります位置。)

このブレークポイントデバッガ出力(撮影)リストkernel32のに渡された名前!CreateFileW、これと同様の(私はこのCMD.EXEにブレークポイントを設定し、「Cを:\ README.TXT」を入力した場合、この場合のデバッガ出力を発生することがあります)。

00657ff0 "C:\\ README.TXT"

(「文字列が関数に転送されたときにプログラムが相対パスを使用している場合、ブレークポイントは、表示された通知は、それは相対パスになります。」)

プロの翻訳者、企業、ウェブサイト翻訳とライブラリの無料鑑賞から学びます。本事例では、これは良いアイデア、ハンドルで、返された最後のエラーコードが表示される場合があります。この関数は、ポイントは、追加情報が表示され、反転ブレークポイントの後の最初のパラメータで完了するように移動することができる返します。これを行うために、我々は次のブレークポイントを使用することができます。

0:001> BP kernel32のCreateFileW "デュポイ(@ ESP + 4);グラム@ $ RA;ハンドル@eaxのF; GLE;グラム!!の"!

このブレークポイントは、関数が返すディスプレイ(そして最後のエラー状態)へのハンドルの後に返されます。これは、リターンアドレスは、継続するためにデバッガを指示することでヒットするまでではなかった戻り値(!ハンドル@eax f)は、最後のエラー状態(!GLE)。(@ $ RAのシンボルは、擬似レジスタがあり、それが邪魔にプラットフォームを、現在の独立した機能の参照アドレスを返します。実際には、G @ $ RAリターンアドレスは、これまでのヒットになるまでコマンドは、プログラムを実行します。)

次のようにこのブレークの出力は次のようになります。
0016f0f0   " coffbase.txt " 
ハンドル60
  タイプファイル
  属性        0 
  GrantedAccess     0x120089は
         ReadControl、Synchの
         読み取り / リスト、ReadEA、ReadAttr
  HandleCount       2 
  PointerCount      3
  利用可能なオブジェクト固有の情報がありません
LastErrorValue:(Win32の)00) - 操作
  正常に完了しました。
LastStatusValue:(NTSTATUS)0 - STATUS_WAIT_0

私たちは、ファイルを開くことができない場合でも、結果は理想的ではありません。

00657ff0   " C:\\のreadme.txt " 
ハンドル4
  タイプディレクトリ
[...]すべてのハンドルの列挙は、以下の[...]
21のハンドル
タイプカウント
イベント               3 
ファイル                4 
ディレクトリ           3 
変異              1 
のwindowstation       1 
セマフォ           2 
キー                 6 
スレッド              1 
LastErrorValue:(Win32の)を0x22) - システム
  することはできません見つけるファイル指定を。
LastStatusValue:(NTSTATUS)0xc0000034 - 
  オブジェクト名が見つかりません。

何が起こったのか?まあ、基本的にその!ハンドルコマンド拡張機能「!ハンドル-1 F」、のCreateFileは、無効なハンドル値を返したため(-1)。このモデル!ハンドル延長ハンドルすべての列挙プロセスは、これは我々が望むものではありません。しかし、ほんの少し賢く、私たちはこれを改善することができます。次のように第二のブレークポイントは次のようになります。

0001!> BP KERNEL32 CreateFileW " (!@eax = -1)デュPOI(@ ESP + 4); G @ $ RA .IF {.printf \"オープンハンドル%のP \\ N \」、@eax ;ハンドル@eaxのF} {.ELSE .echoが開いているファイルに失敗しました、エラー:!;!のGLE};グラム

このコマンドは、ちょっと怖いし始めているが、それは実際には非常に簡単ですが。このブレークポイントの以前のバージョンと同様に、本質は渡さ実現することを示すことであるkernel32の!CreateFileWは、その後のCreateFile戻るまで継続します。次に、関数返すINVALID_HANDLE_VALUE(-1)、ハンドルが表示されているか否かに基づいて、または最後のエラー状態。ブレークポイントは、出力が(例えば、ファイルが正常に開かれますが、ファイルを開くために失敗した)ように見えるかもしれません改善しました:

成功:

0016f0f0   " coffbase.txt " 
オープンハンドル00000060 
ハンドル60
  タイプファイル
  属性        0 
  GrantedAccess     0x120089は
         ReadControl、Synchの
         読み取り / リスト、ReadEA、ReadAttr
  HandleCount       2 
  PointerCount      3
  利用可能なオブジェクト固有の情報がありません

失敗:

00657ff0   " C:\\ README.TXT " 
オープンに失敗したファイル、エラー:
LastErrorValue:(Win32の)を0x22) - システム
  することはできません見つけるファイル指定を。
LastStatusValue:(NTSTATUS)0xc0000034 - 
  オブジェクト名が見つかりません。

はるかに良いです。全体インテリジェンスにおけるブレークポイントは、私たちが悪い行動の障害が発生した場合に、ダンププロセスハンドルテーブルをスキップすることができ、さらには最後の表示に成功した場合のエラーコードをスキップ。
1アカウントに条件付きブレークポイントを提供するための柔軟性を取ったら、想像の通り、ここでは他の可能性の範囲です。しかし、この方法にはいくつかの欠点を持っている、それが考慮されなければなりません。その他のいくつかの「問題のロギング(だけでなく、いくつかの制限および欠点の代わりに専用のプログラムのデバッガを使用して詳しく見ための他のより高度な条件付きブレークポイントの一部、およびこの方法の使用は、今後の記事で遭遇することができる上「)。

 
その後も、私は休憩前に説明したように、本当に便利(入力パラメータと戻り値のつまり表示機能)を記録していますが、これらのシーンにおけるいくつかのシーンが出ていることが、私のスタイル提案ブレークポイントなどは提供しません問題のために必要な追跡コンテンツ。
最も顕著な例は、関数呼び出しを実行した後、あなたは充填関数によって呼び出されたパラメータをチェックアウトする必要があります。スタックアクセスの関数呼び出しの戻り値関数のパラメータの後に、通常は信頼性がないため、問題は、(呼び出し規則に基づいてこのポーズは、スタックとレジスタは、Windows上で使用すると呼ばれる機能が自由に必要な場所などのパラメータを変更することができますあなたが最適化を有効にすると、これは)非常に一般的です。私たちは本当に関数が戻った後、関数のパラメータの一部にアクセスするための関数呼び出しを越えて、いくつかの状態を保存できるようにする必要がありますので。

幸いなことに、これはかなり遠回りものの、デバッガで可能です。ここで重要なのは(表情評価値の点でそれほど疑似レジスタと呼ばれ、従来のアクセス・レジスタと同じである)の概念で、プラットフォームに依存しない追加の記憶場所である、いわゆるユーザ定義のダミーレジスタを使用することです。本質的に唯一の変数従来のプログラミングが利用可能に擬似レジスタの限られた数(現在のバージョン20)にもかかわらず、これらの擬似レジスタの意味。そのため、彼らはいくつかの制限を達成することができますが、ほとんどの場合、20で十分です。あなたは強くC言語デバッガ拡張機能を使用して、代わりのデバッガスクリプト言語を使用することを検討すべきであるより多くの状態を追跡する必要があることが判明した場合。
(私はWinDbgのセッションに直面して座っていた覚えている、ドライバーのDevConの上の数年前、ところで、発表者は、スクリプト言語デバッガで書かれた(その後、比較的新しい大規模なプログラムを訪問している)の拡張機能、そして、、支持条件やエラー処理の追加。私は助けることはできませんが、スクリプトデバッガが公正であることをが、(一緒にデバッガの式評価をPerlのスタイルcmd.exeのバッチスクリプトの醜い部分を組み合わせることであると見ること値は、バッチスクリプトのいくつかよりも強力であり、それは最初に、より強力な一部の単純な式よりも)使用されることを意図されていませんでした。正直に言うと、私はまだ強く、可能な場合には、非常に複雑なスクリプトデバッガを書くことではないお勧めします;それらの一つであるこのケースで維持する悪夢、書き込みデバッガ拡張(または完全なドライブデバッガプログラム)がより良い選択である、しかし、私は脱線; ...通話記録の話題に戻って)
デバッガ疑似レジスタのユーザ定義関数の関数呼び出しを保存するために使用され得る(可能であれば、多少不器用な場合)の状態を記憶する効率的な方法を、提供 パラメータ値。例えば、我々は、データのダンプファイル読み取り中に必要とするので、ファイルを読むためにすべてのコールを記録することがあります。このタスクを達成するために、我々は(カウント、別のアウトパラメータを転送されたバイトを使用して)、出力バッファの内容をダンプする必要があります。これは、(本実施例では、簡単のため、私は、同期I / OモードのReadFileに使用されるプログラムを想定)のように行うことができます。

0000!> BP KERNEL32のReadFile R @ $ T0 = POI(@ ESP + 8)であり; R @ $ T1 = POI(@ ESP + 10); G @ $ RA; .IF(@eax!= 0){ .printf \ "読み取り%のLUがバイト:\\ N \"!の、DWO(@ $ T1); DB @ $ T0 ldwo(@ $ T1)} .ELSE {読み取りが失敗.echo; GLE}; G "

このコマンドの出力は次のようになります。

22のバイトを読みます: 
0016ec3c 54 68 69 73 20 69 73 20から61 20 74 65 78 74 20 66
              これは、テキストFであります
0016ec4c 69 6cは65 2E 0D 0A
              ...

昨日の例は、このコマンドの性質を論理的に拡張され、我々は、通話状態でいくつかの共有を追加しました。具体的には、@ $ T0 @ $ t1とユーザ定義のダミーレジスタ実行機能lpBuffer([ESP + 08H])とlpNumberOfBytesRead([ESP + 10H])コールでのReadFile引数中。@ T0 @ t1と参照が参照する値の除去によるリターンアドレスの停止実行は、ダンプ・ファイル・データの内容が読み込まれたばかりのとき。

実行全体で保全のこの状態は便利ですが、欠点はありますが。 まず第一に、ブレークポイントのこのタイプは、複数のスレッド(複数のスレッドの場合は、少なくとも同時に問題のブレークポイントにヒットする)と基本的に互換性がありません。いわば、複数のスレッドが足ブレークポイントにヒットする上で新たな一歩することができます-デバッガは「局所的発現」または状態「ローカルスレッド」を提供していないためです。(「ステートフル」のブレークポイントは、より深刻かもしれにもかかわらず、この問題は、ブレークポイントのいずれかのタイプで発生する可能性があり、ブレークポイントは、「G <アドレス>」暗黙のブレークを作成するには、コマンドの前に続行するには、中に含まれています。)
デバッガこの制限は、頻繁に行うことは困難であるが、限定的に解決するためにスレッド指定子によってスレッド固有のブレークポイントにGコマンドであってもよいです。多くのコールロガーユニットは、マルチスレッド考慮し、マルチスレッドの関数呼び出しを満たすために、特別な作業を必要としません。(この問題は、通常はそれほど深刻な音ではないことに注意してください- 、多くの場合、でも、マルチスレッド・プログラムであなたがに興味を持っている機能には通常一つだけの関数呼び出しを、または競合の可能性をスレッドは、それがなければならないことを十分に小さいです問題は、多くの場合、複数のスレッドからの関数呼び出し、およびデータをチェックする必要がある場合に最も時間の作業を行うことができます。しかし、いくつかのケースでは、これらのスタイルのブレークポイントは、関数が戻った後、うまく動作しません。)
使用別の重要な制限デバッガのコールのロギングは(専用プログラムではなく)、デバッガは通常、非常に遅いのロギングプログラムの遅れ実装と比較されていることです。ここではその理由は、各ブレークポイントのイベントのために、実際には、プログラム内のすべてのスレッドが凍結されていることがあり、状態情報の多様性は、デバッガへのプログラムからコピーされ、その後、デバッガ側のブレークポイントの式を計算しています。さらに、特別なプログラムは、その結果は、後の表示およびフォーマットするためのバイナリログバッファに格納されているのではなく(例えば)、リアルタイム・ログ・ブレークポイントに表示され、異なっています。各UIデバッガのブレークポイントで更新する必要があるため、それはより多くのオーバーヘッドが生成されます、この手段。あなたが頻繁にヒットされている機能に条件付きブレークポイントを設定した場合はそのため、あなたはプログラムでも使用されていない限り、おそらく、大幅に鈍化していることに気づくことがあります。特別ロガーは、これらの制限デバッガ、これらの制限は、主にデバッガのAPIモニターの代わりに、高速の製品という事実のために設計されているデバッガを回避するために様々な技術を使用することができます。
変換の速度は変換がKDモードで、実際にシステムが使用できなくなることはほぼ十分であることを、非常に遅い秒あたりにも数回あるので、これは、カーネルデバッガ、デバッガ場合に、より顕著になります。そのため、注意するカーネルデバッガ位置ニーズにログブレークポイントの条件を設定の選択(機能固有のコードパスの途中でそれらを置いてもよいが、すべてのコールを捕捉するためには、むしろ初めに比べて、面白いです)。
これらの制約を考えると、デバッガまたはプライベートログプログラムが最良の選択であるかどうかを判断するために、いくつかの分析を疑問視する必要があります。デバッガは非常に柔軟な(そして通常は非常に便利)ですが、それは、可能な各シーンのために最良の選択ではないかもしれないが、どちらの方法でも、長所と短所を持っています。言い換えれば、最高のツールで仕事をします。しかし、いくつかのケースでは、唯一のオプションは、私はあなたが正常に常に専用使用している場合でも、あなたは、ロギングタスクを完了するためにデバッガを使用する方法の少なくとも基本を把握することをお勧めしますので、そのようなカーネルモードのコールログなど、デバッガを使用することですログ記録プログラム。(ただし、カーネルモードデバッグの場合、同じで、デバッガの変換は、ゆっくりと「低流量」位置がブレークポイントとして非常に重要である選択。作る)
それにもかかわらず、効果的なデバッグの重要な部分を、問題を解決し、あなたを理解することですそれらを使用するためのオプションと(と)。デバッガを使用して、これらのオプションの多くで、コールログ1なければならないだけで「デバッグキット」を実行します。

おすすめ

転載: www.cnblogs.com/yilang/p/12157228.html