はじめに
本記事では、DbgHelpを利用してDLLがエクスポートしている関数を表示するプログラムを作成し、DLLやEXEなどのPE形式のファイルから情報を得るための手法を示します。
対象読者
本記事の対象読者は、WindowsでC++を利用してツールを作成したり、プログラムの実行時障害の解析を行ったりする方です。
また、Windowsでバイナリを直接操作することに興味をお持ちのバイナリアンも対象です。
必要な環境
サンプルプログラムは、Visual Studio 2005のソリューションとして構成してあります。利用したプロジェクトテンプレートはWin32コンソールアプリケーションです。ビルドするには、Platform SDKをインストールしておく必要があります。
同様にPlatform SDKをインストールした環境であれば、Visual C++ 6やVisual Studio 2003でもプロジェクトを設定することでビルドできます。
依存するヘッダとライブラリ
サンプルプログラムはDbgHelpを利用するため、ビルド時に「DbgHelp.h」と「DbgHelp.lib」を参照します。これらはPlatform SDKに含まれます。
なお、「DbgHelp.dll」は、Windows 2000以降のWindowsには標準で付属していますが、古いWindowsには別途用意する必要があります。
サンプルプログラムについて
添付アーカイブを展開すると、「showexports」ディレクトリをルートとした以下のツリーが作成されます。
showexports | +debug …デバッグビルド済みファイル | +release …リリースビルド済みファイル | +showexports …ソースファイル | +debug …ビルド用空ディレクトリ | +release …ビルド用空ディレクトリ
動作を確認するには、「showexports\debug」または「showexports\release」のいずれかのディレクトリで、「showexports dll名」を実行してください(例1)。
C:\temp\showexports>cd release C:\temp\showexports\release>showexports c:\windows\system32\atl71.dll name=ATL71.DLL 10 fname=AtlAdvise 41 fname=AtlAxAttachControl (略) 45 fname=AtlWinModuleRegisterWndClassInfoW 66 fname=AtlWinModuleTerm C:\temp\showexports\release>
これは、「dumpbin.exe」を/exportsオプション付きで実行した場合に得られる結果(例2)のサブセットです。
C:\temp\showexports\release>dumpbin /exports c:\windows\system32\atl71.dll Microsoft (R) COFF Binary File Dumper Version 6.00.8447 (略) ordinal hint RVA name 10 0 000050AA AtlAdvise (略) 66 34 000012FA AtlWinModuleTerm (略) C:\temp\showexports\release>
用語説明
PE
PE(Portable Executable)ファイルは、マイクロソフト社の実行ファイルの形式のうち、アーキテクチャ非依存フォーマットを利用したファイルです。PEファイルの内部には、プログラムのコード、インポート情報、エクスポート情報、リソースなど種々の情報が格納されています。
PEファイルには、EXEファイル、DLLファイルなどの実行可能ファイル(イメージ)と、コンパイル後の機械語を収めたオブジェクトファイル(オブジェクト)の2種類があります。
PEファイルの仕様については、参考資料を参照してください。
RVA
PEファイル上の情報は、各情報に固有のヘッダ上で、オフセットと長さのペアによって管理されます。オフセット値は、ファイル上のオフセットではなく、プロセス内に配置された場合の境界に基づいた値です。このため、イメージをプロセス空間へ配置する作業は高速に行える反面、ファイル内でポインタをたどるのを難しくしています。なお、このオフセットをRVA(Relative Virtual Address)と呼びます。仕様書で、RVAと書かれているものについては、ファイルポインタとして直接扱うことができないので注意が必要です。
SDKヘッダファイルとPEファイルの構造体
ヘッダファイルのうち、PEファイルの各種ヘッダなどの構造体が定義されているのは、「WinNT.H」です。
「WinNT.H」で特に重要なのは、IMAGE_FILE_HEADER
構造体(仕様書ではCOFF File Header)と、IMAGE_OPTIONAL_HEADER
構造体(仕様書ではOptional Header)の2つです。両者は先頭のシグネチャ("PE"という文字列)と合わせてIMAGE_NT_HEADERS
構造体のフィールドです。
IMAGE_NT_HEADERS
の直後には、実際のバイナリデータのインデックスを提供するIMAGE_SECTION_HEADER
構造体(仕様書ではSection Header)が、IMAGE_FILE_HEADER
構造体のNumberOfSections
フィールドで指定された数だけ続きます。
実際のコード、定数、リソースといったデータは、IMAGE_SECTION_HEADER
構造体の後ろに配置されます。
「WinNT.H」は、「Windows.H」がincludeする「WinDEF.H」によってincludeされています。このため、プログラムからは「Windows.H」をincludeすれば特に明示的にincludeする必要はありません。
DbgHelp
「DbgHelp.dll」は、WindowsアプリケーションがEXEやDLLなどのバイナリファイルを簡単に扱えるように提供されています。デバッグ用のシンボル情報や、スタックトレースを取得するためのAPIや、本記事で利用するイメージファイル操作APIなどが実装されています。
プログラムで「DbgHelp.dll」のAPIを利用するには、「DbgHelp.lib」をリンクします。また、APIは「DbgHelp.H」に定義されています。これらはいずれもPlatform SDKに収録されています。
DbgHelpを利用する場合の注意点は、DbgHelpがマルチスレッディングを考慮していないことです。このため、同時に複数のスレッドからAPIを呼び出すと正しい結果を得られないことがあります。