[MS技術]
DLLのフック
PE/COFF
- 概要は 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;
- ヘッダに関する構造体の情報は以下が詳しい
- PE(Portable Executable)ファイルフォーマットの概要
- PIMAGE_DOS_HEADER: MS-DOSヘッダー
- PIMAGE_NT_HEADERS: NT ヘッダ
// 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); } }
pe.png