エクスポート関数とは
エクスポート関数とは、そのモジュールが外部に公開している(別のモジュールから呼び出すことが可能な)関数の一覧になります。
DEFファイルだと以下の様な感じで定義している奴ですね。
EXPORTS
FUNC1 @1 NONAME
FUNC2 @100
エクスポート関数に関しては、
- 関数名
- 序数
のどちらでもアクセス可能となっています。
また序数は基本必須で、明示的に指定してない場合でも自動で割り振られていますが、関数名に関しては無い(あえて名前では公開していない)場合がありますので少し注意が必要です。
IMAGE_SECTION_HEADERアドレスの取得
公開関数の定義へアクセスする為には、まずそのアドレスが定義されている「IMAGE_SECTION_HEADER」へのアドレスを取得するところから始めます。
IMAGE_SECTION_HEADERへのアドレスに関しては、IMAGE_NT_HEADERSのOptionalHeaderメンバに定義されていますので、最初はそちらを参照します。
IMAGE_DATA_DIRECTORY& tData = tNT.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
DWORD nOffset = tData.VirtualAddress;
IMAGE_NT_HEADERSに関しては前述で述べたとおり、解析しているモジュール側のアーキテクチャに合せて32bitと64bitで対応する構造体を使用します。
IMAGE_DIRECTORY_ENTRY_EXPORTに関しては定義されている定数で、通常は0が割り当てられています。
このVirtualAddressが目的とするIMAGE_SECTION_HEADERへのアドレスとなります。
IMAGE_SECTION_HEADERの検索
IMAGE_SECTION_HEADERの数に関しては、IMAGE_NT_HEADERSのFileHeader.NumberOfSectionsに保存されていて、構造体の中には、
- アドレス
- ファイルポインタ
- セクションサイズ
が格納されています。
メンバ | |
---|---|
VirtualAddress | 先頭アドレス |
Misc.VirtualSize | メモリ上に読み込まれた時に確保されるデータサイズ。 |
SizeOfRawData | そのセクションがファイル上で確保されているサイズ。 |
また、構造体の配列の先頭アドレスは、以下の様にマクロを使用して取得します。
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(&tNT);
実際に目的とするセクションのアドレスに関しては、VirtualAddressからVirtualSizeまでの範囲に取得したアドレスが含まれているかどうか、で検索します。
PIMAGE_SECTION_HEADER sec = NULL;
for ( int i=0; i < tNT.FileHeader.NumberOfSections; i++, section++ )
{
DWORD size = section->Misc.VirtualSize;
if ( size == 0 ){size = section->SizeOfRawData;}
if ( (rva >= section->VirtualAddress) && (rva < (section->VirtualAddress + size)))
{
sec = section;
break;
}
}
if( sec == NULL ){ return false;}
セクションにはアドレスとファイルポインタの両方の情報が含まれていますので、これを使用すると相互に変換を行うことができます。
UINT delta = sec->VirtualAddress - sec->PointerToRawData;
この値を利用してIMAGE_SECTION_HEADERのアドレスをファイルポインタへと変換すれば、ようやく目的とするエクスポート関数が定義されている場所へと到達できます。
IMAGE_EXPORT_DIRECTORY& tExport = (*(IMAGE_EXPORT_DIRECTORY*)&(pFile[nOffset - delta]));
次は実際に外部公開関数の列挙を行います。