Windows:PEヘッダからたどって、DLLのエクスポート関数を列挙する

Windows7の32bitアプリ、Visual Studio2008で動作確認。

#include <stdio.h>
#include <windows.h>

#define RVAtoVA(type, base, offset)		((type)(PVOID)(((PCHAR)(base))+(offset)))

int main()
{
	DWORD						dwModuleBaseAddr;
	PIMAGE_DOS_HEADER			pDosHeader;
	PIMAGE_NT_HEADERS32			pPEHeader;
	PIMAGE_OPTIONAL_HEADER32	pOptionalHeader;
	PIMAGE_DATA_DIRECTORY		pDataDirectoryHeader;
	PIMAGE_EXPORT_DIRECTORY		pExportDirectoryHeader;
	CHAR cDLLName[] = "kernel32.dll";
	unsigned int i;

	// DLLファイルはDOS_HEADERから始まる
	dwModuleBaseAddr = (DWORD)GetModuleHandle(cDLLName);	// HMODULEはDLLのベース(先頭)アドレス
	pDosHeader = (PIMAGE_DOS_HEADER)dwModuleBaseAddr;
	printf("pDosHeader = dwModuleBaseAddr(%s) = 0x%x\n", cDLLName, dwModuleBaseAddr);

	// pDosHeader->e_lfanew:PEヘッダ(IMAGE_NT_HEADERS32)へのRVA(モジュールベースアドレスからのオフセット)
	pPEHeader = (PIMAGE_NT_HEADERS32)(dwModuleBaseAddr + pDosHeader->e_lfanew);
	printf("pDosHeader->e_lfanew = 0x%x\n", pDosHeader->e_lfanew);
	printf("pPEHeader = 0x%x\n", pPEHeader);

	// pPEHeader->OptionalHeader : IMAGE_OPTIONAL_HEADER32
	pOptionalHeader = &pPEHeader->OptionalHeader;
	printf("pOptionalHeader = pPEHeader->OptionalHeader = 0x%x\n", pOptionalHeader);

	// pOptionalHeader->DataDirectory[0] : エクスポート関数に関わる情報が格納されている
	pDataDirectoryHeader = &pOptionalHeader->DataDirectory[0];
	printf("pDataDirectoryHeader = &pOptionalHeader->DataDirectory[0] = 0x%x\n", pDataDirectoryHeader);

	// pDataDirectoryHeader->VirtualAddress : IMAGE_EXPORT_DIRECTORY構造体へのRVA(モジュールベースアドレスからのオフセット)
	pExportDirectoryHeader = (PIMAGE_EXPORT_DIRECTORY)(dwModuleBaseAddr + pDataDirectoryHeader->VirtualAddress);
	printf("pDataDirectoryHeader->VirtualAddress = 0x%x\n", pDataDirectoryHeader->VirtualAddress);
	//printf("&pDataDirectoryHeader->VirtualAddress = 0x%x\n", &pDataDirectoryHeader->VirtualAddress);
	printf("pExportDirectoryHeader = 0x%x\n", pExportDirectoryHeader);

	// エクスポートされている関数を列挙する
	for(i=0; i<pExportDirectoryHeader->NumberOfNames; i++)
	{
		PCHAR pProcName;
		DWORD pProc;
		WORD wOrdinalNum;
		
		pProcName = RVAtoVA(PCHAR, dwModuleBaseAddr, RVAtoVA(PDWORD, dwModuleBaseAddr, pExportDirectoryHeader->AddressOfNames)[i]);
		wOrdinalNum = RVAtoVA(PWORD, dwModuleBaseAddr, pExportDirectoryHeader->AddressOfNameOrdinals)[i];
		pProc = RVAtoVA(DWORD, dwModuleBaseAddr, RVAtoVA(PDWORD, dwModuleBaseAddr, pExportDirectoryHeader->AddressOfFunctions)[wOrdinalNum]);
		printf("0x%x : %s\n", pProc, pProcName);
	}

	return 0;
}

[参考]
PEフォーマットを解釈せよ!(1-3) − @IT
http://www.atmarkit.co.jp/fsecurity/rensai/re05/re01.html