potisanのプログラミングメモ

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

C++17&WinAPI 64ビットPEファイルのデータディレクトリの物理アドレスを取得する

ベタ書きですが、C++で64ビットPEファイルの各種ヘッダーとデータディレクトリの物理アドレスを取得するサンプルです。

IMAGE_OPTIONAL_HEADERのDataDirectory[]に含まれるIMAGE_DATA_DIRECTORYの実体はIMAGE_SECTION_HEADERの指す各セクションに含まれており、その物理アドレスはそれぞれの仮想アドレスの差と所属するIMAGE_SECTION_HEADERのPointerToRawDataの和で取得できます。詳しくはコードで。

// 64ビットPE形式ファイルに含まれるセクションとセクションに含まれるデータディレクトリを列挙します。

#include <iostream>
#include <iomanip>
#include <fstream>
#include <memory>

#define STRICT
#include <Windows.h>

const wchar_t* const ImageDataDirectoryNames[] = {
    L"Export",
    L"Import",
    L"Resource",
    L"Exception",
    L"Security",
    L"Base Relocation",
    L"Debug",
    L"Architecture",
    L"Global Pointer",
    L"TLS",
    L"Load Config",
    L"Bound Import",
    L"IAT",
    L"Delay Import",
    L"COM DEscriptor" };

inline std::wstring convert_ansi_string_to_wstring(
    const char* const s,
    size_t len)
    noexcept(false);

bool parse_pefile64(
    const std::byte* base,
    const IMAGE_DOS_HEADER** imgDosHdr,
    const IMAGE_NT_HEADERS64** imgNtHdrs)
    noexcept(false);

bool ImageSectionHeaderContainsImageDataDirectory(
    const IMAGE_SECTION_HEADER& sectionHeader,
    const IMAGE_DATA_DIRECTORY& dataDirectory)
    noexcept;

using unique_HANDLE = std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>;
template <typename T>
using unique_MapViewOfFile_HANDLE = std::unique_ptr<T, decltype(&UnmapViewOfFile)>;

inline auto to_unique_HANDLE(HANDLE handle) {
    return unique_HANDLE(handle, &CloseHandle);
}

template <typename T>
inline auto to_unique_MapViewOfFile_HANDLE(void* p) {
    return unique_MapViewOfFile_HANDLE<std::byte>(
        reinterpret_cast<std::byte*>(p, &UnmapViewOfFile);
}

int wmain()
{
    unique_HANDLE fileHandle = to_unique_HANDLE(
        CreateFileW(L"C:\\Windows\\System32\\User32.dll",
            GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr));
    unique_HANDLE fileMapHandle = to_unique_HANDLE(
        CreateFileMappingW(fileHandle.get(), nullptr, PAGE_READONLY, 0, 0, nullptr));
    auto data = to_unique_MapViewOfFile_HANDLE<std::byte>(
        MapViewOfFile(fileMapHandle.get(), FILE_MAP_READ, 0, 0, 0));

    const IMAGE_DOS_HEADER* dosHeader;
    const IMAGE_NT_HEADERS64* ntHeaders;
    if (!parse_pefile64(data.get(), &dosHeader, &ntHeaders))
    {
        std::wcout << L"ファイルは64ビットのPE形式ではありません。" << std::endl;
        return -1;
    }

    auto firstSectionHeader = reinterpret_cast<const PIMAGE_SECTION_HEADER>IMAGE_FIRST_SECTION(ntHeaders);
    for (unsigned i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
    {
        auto psectionHeader = firstSectionHeader + i;
        auto sectionName = convert_ansi_string_to_wstring(
            reinterpret_cast<const char*>(psectionHeader->Name), IMAGE_SIZEOF_SHORT_NAME);
        std::wcout << sectionName.c_str() << std::endl;
        std::wcout << L"Raw Address: 0x"
            << std::hex << std::setfill(L'0') << std::setw(8) << psectionHeader->PointerToRawData << std::endl;
        std::wcout << L"Raw Size: " << std::dec << psectionHeader->SizeOfRawData << L" byte" << std::endl;
        // セクションに含まれるデータディレクトリを列挙する
        for (unsigned int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
        {
            const auto& dataDir = ntHeaders->OptionalHeader.DataDirectory[i];
            if (ImageSectionHeaderContainsImageDataDirectory(*psectionHeader, dataDir))
            {
                // ディレクトリはセクションの内部に含まれる。
                auto offset = static_cast<ptrdiff_t>(dataDir.VirtualAddress) - psectionHeader->VirtualAddress;
                std::wcout
                    << L" 0x" << std::hex << std::setw(8) << std::setfill(L'0') << offset
                    << L" " << ImageDataDirectoryNames[i] << std::endl;
            }
        }
        std::wcout << std::endl;
    }

    return 0;
}

inline std::wstring convert_ansi_string_to_wstring(
    const char* const s,
    size_t len)
    noexcept(false)
{
    if (s == nullptr)
        throw new std::invalid_argument("");
    return std::wstring(s, s + len);
}

bool parse_pefile64(
    const std::byte* base,
    const IMAGE_DOS_HEADER** imgDosHdr,
    const IMAGE_NT_HEADERS64** imgNtHdrs)
    noexcept(false)
{
    if (base == nullptr || imgDosHdr == nullptr || imgNtHdrs == nullptr)
        throw new std::invalid_argument("");

    auto dosHeader = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
    if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        return false;

    auto signature = reinterpret_cast<const DWORD*>(base + dosHeader->e_lfanew);
    if (*signature != IMAGE_NT_SIGNATURE)
        return false;

    auto fileHeader = reinterpret_cast<const IMAGE_FILE_HEADER*>(signature + 1);
    if (fileHeader->SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64))
        return false;

    auto optionalHeader = reinterpret_cast<const IMAGE_OPTIONAL_HEADER64*>(fileHeader + 1);
    if (optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
        return false;

    *imgDosHdr = dosHeader;
    *imgNtHdrs = reinterpret_cast<const IMAGE_NT_HEADERS64*>(signature);
    return true;
}

bool ImageSectionHeaderContainsImageDataDirectory(
    const IMAGE_SECTION_HEADER& sectionHeader,
    const IMAGE_DATA_DIRECTORY& dataDirectory)
    noexcept
{
    auto sec_lo = sectionHeader.VirtualAddress;
    auto sec_hi = sectionHeader.VirtualAddress + sectionHeader.SizeOfRawData;
    auto dir_lo = dataDirectory.VirtualAddress;
    auto dir_hi = dataDirectory.VirtualAddress + dataDirectory.Size;
    return sec_lo <= dir_lo && dir_hi <= sec_hi;
}