potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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;
}