Windbg にブレークポイントを設定して、ソフトウェア リモート デバッグを有効にするモジュールを追跡する

目次

1. Windbg の動的デバッグ

2. Windbg でブレークポイントを設定する

2.1. 関数エントリにブレークポイントを設定する

2.2. 関数内の特定の行にブレークポイントを設定する

3. ブレークポイントを設定して、リモート デバッグ スイッチ インターフェイスへの呼び出しを追跡します。

3.1. デモコードを書く

3.2. 追跡のために Windbg の SetRemoteDebugOn インターフェイスを呼び出すブレークポイントを設定する

4. 最後に


VC++ の共通機能開発の概要 (コラム記事のリスト、購読歓迎、継続的な更新...) icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585 C++ ソフトウェア異常トラブルシューティング チュートリアル シリーズ入門から習熟まで(コラム記事一覧)、ぜひ購読して更新を続けてください...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931入門から習熟までのC++ソフトウェア解析ツール事例集(コラム)記事は更新中...) icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795 C/C++ の基礎と応用 (コラム記事、継続的に更新中...) icon-default.png?t=N7T8https://blog.csdn.net /chenlycly/category_11931267.html       最近テストしたところ、ソフトウェアの起動後にリモート デバッグ スイッチが自動的にオンになることがわかりました。ただし、セキュリティ上の考慮事項により、リモート デバッグ スイッチをオンにするには、ユーザーが設定で手動で操作する必要があります。自動的にオンにすることはできません。ソフトウェア モジュールには数百の DLL ライブラリがあり、複数の開発グループが関与しているため、どのモジュールが自動的に開かれるかを判断することはできません。その後、Windbg 動的デバッグを使用してブレークポイントを設定し、問題をすばやく特定できるのではないかと考えました。この記事では、この問題のトラブルシューティング プロセスについて詳しく説明します。

1. Windbg の動的デバッグ

       一般に、C++ ソフトウェア例外のトラブルシューティングを行う場合、例外コンテキストを含むダンプ ファイルがある場合は、まず Windbg を使用してダンプ ファイルを静的分析のために開きます。例外の発生時にダンプ ファイルが生成されない場合は、動的デバッグのために Windbg をターゲット プロセスにアタッチする (または Windbg を使用してターゲット プログラムを起動する) 必要があります。

       Windbg は主に C++ ソフトウェア例外の分析に使用されますが、動的デバッグ中に問題を特定するのにも役立ちます。動的デバッグ Windbg でブレークポイントを設定 (コード セグメントのアドレスを設定) して、プログラムの実行軌跡を追跡できます。この場合、Windbg にブレークポイントを設定することで問題が特定されました。

        Windbg を動的デバッグに使用するには、次の 2 つの方法があります。

1) Windbg をすでに実行中のターゲット プロセスに直接接続しますメニューバーで「ファイル」→「プロセスにアタッチ...」をクリックします。
2) Windbg を直接使用してターゲット プログラムを起動できますメニューバーで「ファイル」→「実行ファイルを開く...」をクリックします。問題がプログラムの起動プロセスにある場合は、この方法を使用します。

       このプロジェクトの問題では、プログラムの起動時にリモート デバッグ スイッチを自動的にオンにする操作を実行する必要があるため、Windbg を使用してプログラムを起動する必要があります。

2. Windbg でブレークポイントを設定する

       bp コマンドを使用すると、関数の入り口または関数の内部にブレークポイントを設定できます。関数内の特定の行にブレークポイントを設定します。ブレークポイントに到達すると、中断時の関数内のローカル変数の値を表示できます。変数の値は、問題をトラブルシューティングするための重要な手がかりとなる可能性があります。

2.1. 関数エントリにブレークポイントを設定する

        関数エントリにブレークポイントを設定し、関数名を使用するだけです。関数名は、コード セグメント内の関数の最初のアドレスです。netdll.dll ライブラリの SetRemoteDebugOn 関数を例にとると、ブレークポイントを設定するコマンドは次のとおりです。

bp netdll!SetRemoteDebugOn

このうち、netdll は dll ライブラリの名前 (.dll 接尾辞なし)、SetRemoteDebugOn は関数名です。

ここの SetRemoteDebugOn 関数は、netdll.dll ライブラリのエクスポートされた関数です。関数のシンボルはパブリックであり、pdb シンボル ライブラリ ファイルを必要としません。設定する関数が DLL ライブラリ内の関数および非 DLL ライブラリのエクスポート関数である場合、内部関数には pdb ファイル内の関数シンボルが必要であるため、ライブラリの pdb ファイルを Windbg に設定する必要があります。それ以外の場合は Windbgそれを認識することができません。

2.2. 関数内の特定の行にブレークポイントを設定する

       ここでいう関数内の行は、C++ ソース コード内の行ではなく、バイナリ ファイル内のアセンブリ コード (バイナリ マシン コード) 内の行です。プログラムは最終的にバイナリ ファイルを実行し、バイナリ ファイル内のバイナリ コードを実行するため (アセンブリ コードに相当し、アセンブリ コードはバイナリ コードのニーモニックです)。

       実際には、関数に基づいてオフセット offset 値を追加します (関数はコード セグメント内の関数の最初のアドレスです)。ただし、このオフセット値は任意に書き込まれているわけではないため、IDA を使用してバイナリ ファイルを開いてアセンブリ コードを表示して確認する必要があります。アセンブリ命令ごとに長さが異なるため、設定できるのはアセンブリ命令のアドレスのみであり、2 つのアセンブリ命令のアドレスの間にアドレス値を設定することはできません。設定しないと無効になり、ブレークポイントにヒットしません。

       オープン ソース ライブラリ libcurl.dll を例として、IDA Pro を使用してライブラリ ファイルを開いて逆アセンブルされたアセンブリ コードを表示し、ライブラリの内部関数 easy_perform を見つけて、関数の行 10007D2D にブレークポイントを設定します。図の中で:

IDA でライブラリの内部関数のシンボルを表示するには、pdb ファイルを取得して libcurl.dll と同じディレクトリに置く必要があります。IDA はそれを自動的にロードします。IDA 逆アセンブル ツールの使用方法については、以前に書いた記事を参照してください。

IDA 逆アセンブリ ツールの使用方法の詳細な説明 https://blog.csdn.net/chenlycly/article/details/120635120       指定した 10007D2D 行にブレークポイントを設定するには、アセンブリ命令のこの行のオフセットを計算する必要があります。関数:

0x10007D2D - 0x10007D10 = 0x1D

したがって、ブレークポイントを設定する WINdbg コマンドは次のようになります。

bp libcurl!easy_perform+0x1D

       また、以下の図から、アセンブリ命令が異なると、占有するコード セグメント メモリの長さが異なることもわかります。

なお、ここでいうアドレスは全てコードセグメントのアドレスであり、バイナリファイルのバイナリコード(アセンブリコード)のアドレスである。コード セグメントのアドレスはデータ セグメントのアドレスと区別する必要があり、変数によって占有されるメモリがデータ セグメント メモリです。   

       C++ プログラムのメモリ パーティションについては、前の記事を参照してください:
C++ プログラムの 5 つの主要なメモリ パーティションの詳細な例 https://blog.csdn.net/chenlycly/article/details/120958761        さらに、アセンブリ コードを理解することの重要性について話します。ここからいくつかのヒントも得られます。アセンブリ コードに精通していると、C++ プログラムの問題のトラブルシューティングに役立つだけでなく、高級言語では理解できないプログラミングの詳細やコード実行の詳細を理解することもできます。C++ ソフトウェアの問題のトラブルシューティングに必要な基本的なアセンブリ知識について知りたい場合は、以前に書いた記事を参照してください:
C++ ソフトウェア例外の分析に必要なアセンブリ知識の概要 https://blog.csdn.net/chenlycly/article/詳細/124758670

3. ブレークポイントを設定して、リモート デバッグ スイッチ インターフェイスへの呼び出しを追跡します。

       どのモジュールがリモート デバッグ スイッチを自動的にオンにするかを追跡するには、基礎となるライブラリを使用して、リモート デバッグをオンにする関数の名前とそれが配置されているモジュールの名前を使用するだけでよく、ブレークポイントを設定できます。動的デバッグ Windbg で追跡します。

ここでプロジェクト内の関連モジュールやインターフェイスを表示するのは不便なので、今後のビデオ コースの説明を容易にするために、問題追跡プロセス全体を説明するために特別にいくつかのテスト コードを作成しました。

3.1. デモコードを書く

       Visual Studio を使用して、MFC メイン プログラム TestDlg.exe と、リモート デバッグ インターフェイス SetRemoteDebugOn を含む netdll.dll ダイナミック ライブラリを作成します。TestDlg.exe メイン プログラムの [テスト] ボタンの応答関数で、netdll.dll ライブラリの API インターフェイス SetRemoteDebugOn を呼び出して、リモート デバッグを有効にします。

       ダイナミック ライブラリ netdll.dll の API インターフェイス SetRemoteDebugOn は次のように定義されています。

メイン プログラム TestDlg.exe の [テスト] ボタンの応答関数は SetRemoteDebugOn を呼び出します。コードは次のとおりです。

3.2. 追跡のために Windbg の SetRemoteDebugOn インターフェイスを呼び出すブレークポイントを設定する

       Windbg を使用して TestDlg.exe を起動し、動的デバッグに Windbg を使用します。netdll.dll モジュールの SetRemoteDebugOn インターフェイスは、リモート デバッグ スイッチをオンにするために最終的に呼び出されるため、次に示すように、SetRemoteDebugOn インターフェイスの入り口、つまり bp netdll !SetRemoteDebugOnにブレークポイントを設定するだけで済みます。

上記のように、bl コマンドを使用して、現在設定されているブレークポイントのリストを表示します。

       次に、TestDlg.exe プログラム ウィンドウの [テスト] ボタンをクリックします。

ボタンの応答関数で SetRemoteDebugOn インターフェイスを呼び出し、設定したブレークポイントに到達します。Windbg が中断されます。この時点で関数呼び出しスタックを表示するには、kn コマンドを使用します。リモート デバッグをオフにするためにどのモジュールが SetRemoteDebugOn インターフェイスを呼び出したかがわかります。 。

       次のように、ブレークポイントにヒットしたときの関数呼び出しスタックを表示します。

上の図からわかるように、TestDlg.exe モジュールはリモート デバッグ スイッチをオンにするためのインターフェイスを呼び出します。また、TestDlg.exe モジュールの CTestDlgDlg::OnBnClickedTest 関数によって呼び出されることもわかります。pdb をお持ちの場合は、関数内のコードのどの行が呼び出されているかも確認できます。

4. 最後に

       この例では、Windbg にブレークポイントを設定することで、リモート デバッグ スイッチをオンにするモジュールと関数の情報をすばやく見つけることができます。Windbg の動的デバッグ機能は非常に便利ですので、この記事が少しでも参考になれば幸いです。

おすすめ

転載: blog.csdn.net/chenlycly/article/details/133375507