potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++11 IMAGE_COR20_HEADERを取得する

概要

32ビット形式のPEファイルを生データとして解析してIMAGE_COR20_HEADER構造体を取得するサンプルコードです。実際にはIMAGE_COR20_HEADER構造体のメインであろうメタデータは今後の拡張に備えてフォーマット非公開なのでCLSID_CorMetaDataDispenserで公開されるクラスを使うべきだと思いますが、PEファイルを自力で解析するコードのメモとして。

C++20版はこちらです。

コード

#define STRICT
#include <Windows.h>
#include <CorHdr.h>

// PEファイルのRVAを含むセクションのIMAGE_SECTION_HEADERを返します。
const PIMAGE_SECTION_HEADER ImageSectionHeaderContainingRVA(LPCVOID Base, const IMAGE_NT_HEADERS32* NTHeaders, DWORD RVA)
{
    // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTORを含むセクションの探索
    const PIMAGE_SECTION_HEADER SectionHeaders = IMAGE_FIRST_SECTION(NTHeaders);
    for (UINT i = 0; i < NTHeaders->FileHeader.NumberOfSections; i++)
    {
        if (SectionHeaders[i].VirtualAddress <= RVA &&
            RVA <= (SectionHeaders[i].VirtualAddress + SectionHeaders[i].SizeOfRawData))
        {
            return SectionHeaders + i;
        }
    }
    return nullptr;
}

// PEファイルのRVAをVAに変換します。
LPCVOID ImageRVAToVA32(LPCVOID Base, const IMAGE_NT_HEADERS32* NTHeaders, DWORD RVA)
{
    const PIMAGE_SECTION_HEADER SectionHeader = ImageSectionHeaderContainingRVA(
        Base, NTHeaders, RVA);
    if (SectionHeader == nullptr)
        return nullptr;
    return ((LPCBYTE)Base) + SectionHeader->PointerToRawData
        + (RVA - SectionHeader->VirtualAddress);
}

// ファイルが32ビットPE形式であればIMAGE_NT_HEADERS32のポインタを返します。
const PIMAGE_NT_HEADERS32 ImageCheckPE32(LPCVOID Base)
{
    const PIMAGE_DOS_HEADER DOSHeader = (const PIMAGE_DOS_HEADER)Base;
    if (DOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return nullptr;
    }

    const PDWORD Signature = (const PDWORD)((LPCBYTE)Base + DOSHeader->e_lfanew);
    if (*Signature != IMAGE_NT_SIGNATURE)
    {
        return nullptr;
    }
    const PIMAGE_FILE_HEADER FileHeader = (const PIMAGE_FILE_HEADER)(Signature + 1);
    if (FileHeader->SizeOfOptionalHeader != sizeof IMAGE_OPTIONAL_HEADER32)
    {
        return nullptr;
    }
    const PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (const PIMAGE_OPTIONAL_HEADER32)(FileHeader + 1);
    if (OptionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
    {
        return nullptr;
    }

    return (const PIMAGE_NT_HEADERS32)Signature;
}

#include <iostream>
#include <iomanip>

// 32ビットPEファイルのCLIヘッダー情報を表示します。
void main()
{
    std::wcout.imbue(std::locale("ja"));

    // CLIヘッダーを確認する実行ファイルの場所
    LPCWSTR path = <ファイル名を指定して下さい>;

    HANDLE FileHandle = CreateFileW(path, GENERIC_READ,
        FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
    if (FileHandle == INVALID_HANDLE_VALUE)
    {
        std::wcout << L"ファイルハンドルが開けませんでした。" << std::endl;
        return;
    }
    HANDLE FileMapHandle = CreateFileMappingW(FileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
    if (FileMapHandle == nullptr)
    {
        std::wcout << L"ファイルマッピングオブジェクトが作成できませんでした。" << std::endl;
        return;
    }
    LPCBYTE Base = (LPCBYTE)MapViewOfFile(FileMapHandle, FILE_MAP_READ, 0, 0, 0);
    if (Base == nullptr)
    {
        std::wcout << L"ファイルマッピングオブジェクトのビューが作成できませんでした。" << std::endl;
        return;
    }

    const PIMAGE_NT_HEADERS32 NTHeaders = ImageCheckPE32(Base);
    if (ImageCheckPE32 == nullptr)
    {
        std::wcout << L"ファイルは32ビットPE形式ではありません。" << std::endl;
        return;
    }

    // IMAGE_COR2_HEADERはIMAGE_DIRECTORY_ENTRY_COM_DESCRIPTORに配置されます
    const PIMAGE_DATA_DIRECTORY ComDescDir = &NTHeaders->OptionalHeader.DataDirectory[
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
    const PIMAGE_COR20_HEADER CorHeader = (const PIMAGE_COR20_HEADER)(
        ImageRVAToVA32(Base, NTHeaders, ComDescDir->VirtualAddress));
    if (CorHeader == nullptr)
    {
        std::wcout << L"ファイルはCLIヘッダーを含みません。" << std::endl;
        return;
    }

    // CLIヘッダー情報の出力
    std::wcout << L"IMAGE_COR20_HEADER" << std::endl;
    std::wcout << L"ランタイムのバージョン: "
        << CorHeader->MajorRuntimeVersion
        << L"."
        << CorHeader->MinorRuntimeVersion
        << std::endl;
    std::wcout << L"メタデータのサイズ: "
        << CorHeader->MetaData.Size
        << std::endl;
    std::wcout << L"フラグ: 0x"
        << std::hex << std::setfill(L'0') << std::setw(8) << CorHeader->Flags
        << std::endl;
    std::wcout << L"エントリーポイント: 0x"
        << std::hex << std::setfill(L'0') << std::setw(8) << CorHeader->EntryPointRVA
        << L" ("
        << (CorHeader->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT ? L"Native" : L"Managed")
        << L")"
        << std::endl;

    UnmapViewOfFile(Base);
    CloseHandle(FileMapHandle);
    CloseHandle(FileHandle);
}

出力例

IMAGE_COR20_HEADER
ランタイムのバージョン: 2.5
メタデータのサイズ: 4,772
フラグ: 0x00000001
エントリーポイント: 0x00000000 (Managed)