123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- #include "image_interpreter.hpp"
- #include <fmt/format.h>
- #include "exceptions/index_exception.hpp"
- #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\image_interpreter.cpp"
- #define NKG_CURRENT_SOURCE_LINE() __LINE__
- namespace nkg {
- image_interpreter::image_interpreter() :
- m_dos_header(nullptr),
- m_nt_headers(nullptr),
- m_section_header_table(nullptr),
- m_vs_fixed_file_info(nullptr) {}
- [[nodiscard]]
- image_interpreter image_interpreter::parse(void* image_base, bool parse_relocation) {
- image_interpreter new_image;
- new_image.m_dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(image_base);
- if (new_image.m_dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: DOS signature check failure")
- .push_hint(u8"Are you sure you DO provide a valid WinPE file?");
- }
- new_image.m_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<uint8_t*>(image_base) + new_image.m_dos_header->e_lfanew);
- if (new_image.m_nt_headers->Signature != IMAGE_NT_SIGNATURE) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: NT signature check failure")
- .push_hint(u8"Are you sure you DO provide a valid WinPE file?");
- }
- #if defined(_M_AMD64)
- if (new_image.m_nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: optional header magic check failure")
- .push_hint(u8"Are you sure you DO provide a valid 64-bits WinPE file?");
- }
- if (new_image.m_nt_headers->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: machine check failure")
- .push_hint(u8"Are you sure you DO provide a valid 64-bits WinPE file?");
- }
- #elif defined(_M_IX86)
- if (new_image.m_nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid Image. (Optional header magic check failure)")
- .push_hint(u8"Are you sure you DO provide a valid 32-bits WinPE file?");
- }
- if (new_image.m_nt_headers->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
- throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid Image. (Machine check failure)")
- .push_hint(u8"Are you sure you DO provide a valid 32-bits WinPE file?");
- }
- #else
- #error "image_interpreter.cpp: unsupported architecture."
- #endif
- new_image.m_section_header_table =
- reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<char*>(&new_image.m_nt_headers->OptionalHeader) + new_image.m_nt_headers->FileHeader.SizeOfOptionalHeader);
- for (WORD i = 0; i < new_image.m_nt_headers->FileHeader.NumberOfSections; ++i) {
- auto section_name = make_section_name(new_image.m_section_header_table[i].Name);
- if (new_image.m_section_header_name_lookup_table.find(section_name) == new_image.m_section_header_name_lookup_table.end()) {
- new_image.m_section_header_name_lookup_table[section_name] = &new_image.m_section_header_table[i];
- }
- new_image.m_section_header_rva_lookup_table[new_image.m_section_header_table[i].VirtualAddress] = &new_image.m_section_header_table[i];
- new_image.m_section_header_fo_lookup_table[new_image.m_section_header_table[i].PointerToRawData] = &new_image.m_section_header_table[i];
- }
- if (parse_relocation && new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) {
- auto relocation_table =
- new_image.convert_rva_to_ptr<PIMAGE_BASE_RELOCATION>(new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
- while (relocation_table->VirtualAddress != 0) {
- rva_t rva = relocation_table->VirtualAddress;
- auto reloc_items = reinterpret_cast<WORD*>(relocation_table + 1);
- auto reloc_items_count = (relocation_table->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
- for (DWORD i = 0; i < reloc_items_count; ++i) {
- auto reloc_type = reloc_items[i] >> 12;
- switch (reloc_type) {
- case IMAGE_REL_BASED_ABSOLUTE:
- break;
- case IMAGE_REL_BASED_HIGH:
- case IMAGE_REL_BASED_LOW:
- case IMAGE_REL_BASED_HIGHADJ:
- new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 2;
- break;
- case IMAGE_REL_BASED_HIGHLOW:
- new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 4;
- break;
- #if defined(IMAGE_REL_BASED_DIR64)
- case IMAGE_REL_BASED_DIR64:
- new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 8;
- break;
- #endif
- default:
- break;
- }
- }
- relocation_table = reinterpret_cast<PIMAGE_BASE_RELOCATION>(&reloc_items[reloc_items_count]);
- }
- }
- if (new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) {
- rva_t import_rva = new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
- auto import_descriptors = new_image.convert_rva_to_ptr<PIMAGE_IMPORT_DESCRIPTOR>(import_rva);
- for (size_t i = 0; import_descriptors[i].OriginalFirstThunk != 0; ++i) {
- auto import_lookup_table = new_image.convert_rva_to_ptr<PIMAGE_THUNK_DATA>(import_descriptors[i].OriginalFirstThunk);
- rva_t import_address_table_rva = import_descriptors[i].FirstThunk;
- for (size_t j = 0; import_lookup_table[j].u1.Ordinal != 0; ++j) {
- new_image.m_iat_rva_lookup_table[import_address_table_rva + j * sizeof(IMAGE_THUNK_DATA)] = std::make_pair(&import_descriptors[i], &import_lookup_table[j]);
- }
- }
- }
- if (new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) {
- rva_t resource_rva = new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
- auto res_type_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva);
- auto res_type_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_type_directory + 1);
- auto res_type_id_entries = res_type_name_entries + res_type_directory->NumberOfNamedEntries;
- for (WORD i = 0; i < res_type_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++i) {
- if (res_type_id_entries[i].Id == reinterpret_cast<uintptr_t>(RT_VERSION) && res_type_id_entries[i].DataIsDirectory) {
- auto res_name_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva + res_type_id_entries[i].OffsetToDirectory);
- auto res_name_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_name_directory + 1);
- auto res_name_id_entries = res_name_name_entries + res_name_directory->NumberOfNamedEntries;
- for (WORD j = 0; j < res_name_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++j) {
- if (res_name_id_entries[j].Id == VS_VERSION_INFO && res_name_id_entries[j].DataIsDirectory) {
- auto res_lang_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva + res_name_id_entries[j].OffsetToDirectory);
- auto res_lang_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_lang_directory + 1);
- auto res_lang_id_entries = res_lang_name_entries + res_lang_directory->NumberOfNamedEntries;
- for (WORD k = 0; k < res_lang_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++k) {
- constexpr WORD neutral_lang_id = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
- constexpr WORD english_lang_id = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
- if ((res_lang_id_entries[k].Id == neutral_lang_id || res_lang_id_entries[k].Id == english_lang_id) && !res_lang_id_entries[k].DataIsDirectory) {
- auto res_data_entry = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DATA_ENTRY>(resource_rva + res_lang_id_entries[k].OffsetToData);
- auto vs_version_info = new_image.convert_rva_to_ptr<PBYTE>(res_data_entry->OffsetToData);
- auto vs_version_info_key = reinterpret_cast<PWSTR>(vs_version_info + 6);
- if (_wcsicmp(vs_version_info_key, L"VS_VERSION_INFO") == 0) {
- auto p = reinterpret_cast<PBYTE>(vs_version_info_key + _countof(L"VS_VERSION_INFO"));
- while (new_image.convert_ptr_to_rva(p) % sizeof(DWORD)) {
- ++p;
- }
- auto vs_fixed_file_info = reinterpret_cast<VS_FIXEDFILEINFO*>(p);
- if (vs_fixed_file_info->dwSignature == VS_FFI_SIGNATURE) {
- new_image.m_vs_fixed_file_info = vs_fixed_file_info;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return new_image;
- }
- [[nodiscard]]
- PIMAGE_DOS_HEADER image_interpreter::image_dos_header() const noexcept {
- return m_dos_header;
- }
- [[nodiscard]]
- PIMAGE_NT_HEADERS image_interpreter::image_nt_headers() const noexcept {
- return m_nt_headers;
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header_table() const noexcept {
- return m_section_header_table;
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header(size_t n) const {
- if (n < m_nt_headers->FileHeader.NumberOfSections) {
- return m_section_header_table + n;
- } else {
- throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Section index is out of range.");
- }
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header(std::string_view section_name) const {
- if (section_name.length() <= 8) {
- std::array<BYTE, 8> name{};
- std::copy(section_name.begin(), section_name.end(), name.begin());
- auto it = m_section_header_name_lookup_table.find(name);
- if (it != m_section_header_name_lookup_table.end()) {
- return it->second;
- } else {
- throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format(u8"Target section header is not found: section_name = {}", section_name));
- }
- } else {
- throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found: section_name is too long.");
- }
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_rva(rva_t rva) const {
- auto it = m_section_header_rva_lookup_table.upper_bound(rva);
- if (it != m_section_header_rva_lookup_table.begin()) {
- --it;
- }
- rva_t section_rva_begin = it->second->VirtualAddress;
- rva_t section_rva_end = section_rva_begin + it->second->Misc.VirtualSize;
- if (section_rva_begin <= rva && rva < section_rva_end) {
- return it->second;
- } else {
- throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found.")
- .push_hint(fmt::format("rva = 0x{:x}", rva));
- }
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_va(va_t va) const {
- return image_section_header_from_rva(static_cast<rva_t>(va - m_nt_headers->OptionalHeader.ImageBase));
- }
- [[nodiscard]]
- PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_fo(fo_t file_offset) const {
- auto it = m_section_header_fo_lookup_table.upper_bound(file_offset);
- if (it != m_section_header_fo_lookup_table.begin()) {
- --it;
- }
- uintptr_t section_fo_begin = it->second->PointerToRawData;
- uintptr_t section_fo_end = section_fo_begin + it->second->SizeOfRawData;
- if (section_fo_begin <= file_offset && file_offset < section_fo_end) {
- return it->second;
- } else {
- throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found.")
- .push_hint(fmt::format(u8"file_offset = 0x{:x}", file_offset));
- }
- }
- [[nodiscard]]
- image_interpreter::va_t image_interpreter::convert_rva_to_va(rva_t rva) const noexcept {
- return rva + m_nt_headers->OptionalHeader.ImageBase;
- }
- [[nodiscard]]
- image_interpreter::fo_t image_interpreter::convert_rva_to_fo(rva_t rva) const {
- auto section_header = image_section_header_from_rva(rva);
- return section_header->PointerToRawData + (rva - static_cast<uintptr_t>(section_header->VirtualAddress));
- }
- [[nodiscard]]
- image_interpreter::rva_t image_interpreter::convert_fo_to_rva(fo_t file_offset) const {
- auto section_header = image_section_header_from_fo(file_offset);
- return section_header->VirtualAddress + (file_offset - section_header->PointerToRawData);
- }
- [[nodiscard]]
- image_interpreter::va_t image_interpreter::convert_fo_to_va(fo_t file_offset) const {
- return convert_fo_to_rva(file_offset) + m_nt_headers->OptionalHeader.ImageBase;
- }
- [[nodiscard]]
- image_interpreter::rva_t image_interpreter::convert_va_to_rva(va_t va) const noexcept {
- return va - m_nt_headers->OptionalHeader.ImageBase;
- }
- [[nodiscard]]
- image_interpreter::fo_t image_interpreter::convert_va_to_fo(va_t va) const {
- return image_section_header_from_va(va)->PointerToRawData;
- }
- [[nodiscard]]
- size_t image_interpreter::number_of_sections() const noexcept {
- return m_nt_headers->FileHeader.NumberOfSections;
- }
- PIMAGE_IMPORT_DESCRIPTOR image_interpreter::import_descriptor_from_rva(rva_t rva) {
- auto it = m_iat_rva_lookup_table.find(rva);
- return it != m_iat_rva_lookup_table.end() ? it->second.first : nullptr;
- }
- PIMAGE_THUNK_DATA image_interpreter::import_lookup_entry_from_rva(rva_t rva) {
- auto it = m_iat_rva_lookup_table.find(rva);
- return it != m_iat_rva_lookup_table.end() ? it->second.second : nullptr;
- }
- }
- #undef NKG_CURRENT_SOURCE_LINE
- #undef NKG_CURRENT_SOURCE_FILE
|