FreeStyleWiki

DLLのフック

このエントリーをはてなブックマークに追加

[MS技術]

DLLのフック

  PE/COFF

PEの構造の図示(PE(Portable Executable)ファイルフォーマットの概要から)

  ソースの解析

get_module_ranges

  • このへんが結構難しい
//
// module には GetModuleHandleA で取得したDLL自身のポインタが入る
//
void byte_pattern::get_module_ranges(memory_pointer module)
{
    static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
    {
        return reinterpret_cast<PIMAGE_SECTION_HEADER>(
            (UCHAR*)nt_headers->OptionalHeader.DataDirectory +
            nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
            section * sizeof(IMAGE_SECTION_HEADER));
    };

    _ranges.clear();
    pair<uintptr_t, uintptr_t> range;
    // DLLの先頭ポインタをMS-DOSヘッダーにキャスト
    PIMAGE_DOS_HEADER dosHeader = module.pointer<IMAGE_DOS_HEADER>();
    // MS-DOSヘッダーからNTヘッダのアドレスを取得し、NTヘッダーにキャスト
    PIMAGE_NT_HEADERS ntHeader = module.pointer<IMAGE_NT_HEADERS>(dosHeader->e_lfanew);

    // NTヘッダのセクションテーブルの数だけループする
    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        // 以下、セクションテーブルの各要素の範囲を取得している
        auto sec = getSection(ntHeader, i);
        auto secSize = sec->SizeOfRawData != 0 ? sec->SizeOfRawData : sec->Misc.VirtualSize;

        range.first = module.address() + sec->VirtualAddress;

        if (memcmp((const char *)sec->Name, ".text", 6) == 0 || memcmp((const char *)sec->Name, ".rdata", 7) == 0)
        {
            // .text / .rdata のときは範囲の最初のアドレス〜(最初のアドレス+SizeOfRawData or 最初のアドレス+VirtualSize)
            range.second = range.first + secSize;
            this->_ranges.emplace_back(range);
        }

        if ((i == ntHeader->FileHeader.NumberOfSections - 1) && _ranges.empty())
            // 上記以外は以下のアドレス取得方式
            this->_ranges.emplace_back(module.address(), module.address() + sec->PointerToRawData + secSize);
    }
}