image_interpreter.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #include "image_interpreter.hpp"
  2. #include <fmt/format.h>
  3. #include "exceptions/index_exception.hpp"
  4. #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\image_interpreter.cpp"
  5. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  6. namespace nkg {
  7. image_interpreter::image_interpreter() :
  8. m_dos_header(nullptr),
  9. m_nt_headers(nullptr),
  10. m_section_header_table(nullptr),
  11. m_vs_fixed_file_info(nullptr) {}
  12. [[nodiscard]]
  13. image_interpreter image_interpreter::parse(void* image_base, bool parse_relocation) {
  14. image_interpreter new_image;
  15. new_image.m_dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(image_base);
  16. if (new_image.m_dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
  17. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: DOS signature check failure")
  18. .push_hint(u8"Are you sure you DO provide a valid WinPE file?");
  19. }
  20. new_image.m_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<uint8_t*>(image_base) + new_image.m_dos_header->e_lfanew);
  21. if (new_image.m_nt_headers->Signature != IMAGE_NT_SIGNATURE) {
  22. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: NT signature check failure")
  23. .push_hint(u8"Are you sure you DO provide a valid WinPE file?");
  24. }
  25. #if defined(_M_AMD64)
  26. if (new_image.m_nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  27. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: optional header magic check failure")
  28. .push_hint(u8"Are you sure you DO provide a valid 64-bits WinPE file?");
  29. }
  30. if (new_image.m_nt_headers->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
  31. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid image: machine check failure")
  32. .push_hint(u8"Are you sure you DO provide a valid 64-bits WinPE file?");
  33. }
  34. #elif defined(_M_IX86)
  35. if (new_image.m_nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  36. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid Image. (Optional header magic check failure)")
  37. .push_hint(u8"Are you sure you DO provide a valid 32-bits WinPE file?");
  38. }
  39. if (new_image.m_nt_headers->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
  40. throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid Image. (Machine check failure)")
  41. .push_hint(u8"Are you sure you DO provide a valid 32-bits WinPE file?");
  42. }
  43. #else
  44. #error "image_interpreter.cpp: unsupported architecture."
  45. #endif
  46. new_image.m_section_header_table =
  47. reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<char*>(&new_image.m_nt_headers->OptionalHeader) + new_image.m_nt_headers->FileHeader.SizeOfOptionalHeader);
  48. for (WORD i = 0; i < new_image.m_nt_headers->FileHeader.NumberOfSections; ++i) {
  49. auto section_name = make_section_name(new_image.m_section_header_table[i].Name);
  50. if (new_image.m_section_header_name_lookup_table.find(section_name) == new_image.m_section_header_name_lookup_table.end()) {
  51. new_image.m_section_header_name_lookup_table[section_name] = &new_image.m_section_header_table[i];
  52. }
  53. new_image.m_section_header_rva_lookup_table[new_image.m_section_header_table[i].VirtualAddress] = &new_image.m_section_header_table[i];
  54. new_image.m_section_header_fo_lookup_table[new_image.m_section_header_table[i].PointerToRawData] = &new_image.m_section_header_table[i];
  55. }
  56. if (parse_relocation && new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) {
  57. auto relocation_table =
  58. new_image.convert_rva_to_ptr<PIMAGE_BASE_RELOCATION>(new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
  59. while (relocation_table->VirtualAddress != 0) {
  60. rva_t rva = relocation_table->VirtualAddress;
  61. auto reloc_items = reinterpret_cast<WORD*>(relocation_table + 1);
  62. auto reloc_items_count = (relocation_table->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
  63. for (DWORD i = 0; i < reloc_items_count; ++i) {
  64. auto reloc_type = reloc_items[i] >> 12;
  65. switch (reloc_type) {
  66. case IMAGE_REL_BASED_ABSOLUTE:
  67. break;
  68. case IMAGE_REL_BASED_HIGH:
  69. case IMAGE_REL_BASED_LOW:
  70. case IMAGE_REL_BASED_HIGHADJ:
  71. new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 2;
  72. break;
  73. case IMAGE_REL_BASED_HIGHLOW:
  74. new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 4;
  75. break;
  76. #if defined(IMAGE_REL_BASED_DIR64)
  77. case IMAGE_REL_BASED_DIR64:
  78. new_image.m_relocation_rva_lookup_table[rva + (reloc_items[i] & 0x0fff)] = 8;
  79. break;
  80. #endif
  81. default:
  82. break;
  83. }
  84. }
  85. relocation_table = reinterpret_cast<PIMAGE_BASE_RELOCATION>(&reloc_items[reloc_items_count]);
  86. }
  87. }
  88. if (new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) {
  89. rva_t import_rva = new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  90. auto import_descriptors = new_image.convert_rva_to_ptr<PIMAGE_IMPORT_DESCRIPTOR>(import_rva);
  91. for (size_t i = 0; import_descriptors[i].OriginalFirstThunk != 0; ++i) {
  92. auto import_lookup_table = new_image.convert_rva_to_ptr<PIMAGE_THUNK_DATA>(import_descriptors[i].OriginalFirstThunk);
  93. rva_t import_address_table_rva = import_descriptors[i].FirstThunk;
  94. for (size_t j = 0; import_lookup_table[j].u1.Ordinal != 0; ++j) {
  95. 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]);
  96. }
  97. }
  98. }
  99. if (new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) {
  100. rva_t resource_rva = new_image.m_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  101. auto res_type_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva);
  102. auto res_type_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_type_directory + 1);
  103. auto res_type_id_entries = res_type_name_entries + res_type_directory->NumberOfNamedEntries;
  104. for (WORD i = 0; i < res_type_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++i) {
  105. if (res_type_id_entries[i].Id == reinterpret_cast<uintptr_t>(RT_VERSION) && res_type_id_entries[i].DataIsDirectory) {
  106. auto res_name_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva + res_type_id_entries[i].OffsetToDirectory);
  107. auto res_name_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_name_directory + 1);
  108. auto res_name_id_entries = res_name_name_entries + res_name_directory->NumberOfNamedEntries;
  109. for (WORD j = 0; j < res_name_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++j) {
  110. if (res_name_id_entries[j].Id == VS_VERSION_INFO && res_name_id_entries[j].DataIsDirectory) {
  111. auto res_lang_directory = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DIRECTORY>(resource_rva + res_name_id_entries[j].OffsetToDirectory);
  112. auto res_lang_name_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(res_lang_directory + 1);
  113. auto res_lang_id_entries = res_lang_name_entries + res_lang_directory->NumberOfNamedEntries;
  114. for (WORD k = 0; k < res_lang_directory->NumberOfIdEntries && new_image.m_vs_fixed_file_info == nullptr; ++k) {
  115. constexpr WORD neutral_lang_id = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  116. constexpr WORD english_lang_id = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
  117. 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) {
  118. auto res_data_entry = new_image.convert_rva_to_ptr<PIMAGE_RESOURCE_DATA_ENTRY>(resource_rva + res_lang_id_entries[k].OffsetToData);
  119. auto vs_version_info = new_image.convert_rva_to_ptr<PBYTE>(res_data_entry->OffsetToData);
  120. auto vs_version_info_key = reinterpret_cast<PWSTR>(vs_version_info + 6); // vs_version_info->szKey
  121. if (_wcsicmp(vs_version_info_key, L"VS_VERSION_INFO") == 0) {
  122. auto p = reinterpret_cast<PBYTE>(vs_version_info_key + _countof(L"VS_VERSION_INFO"));
  123. while (new_image.convert_ptr_to_rva(p) % sizeof(DWORD)) {
  124. ++p;
  125. }
  126. auto vs_fixed_file_info = reinterpret_cast<VS_FIXEDFILEINFO*>(p);
  127. if (vs_fixed_file_info->dwSignature == VS_FFI_SIGNATURE) {
  128. new_image.m_vs_fixed_file_info = vs_fixed_file_info;
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. return new_image;
  139. }
  140. [[nodiscard]]
  141. PIMAGE_DOS_HEADER image_interpreter::image_dos_header() const noexcept {
  142. return m_dos_header;
  143. }
  144. [[nodiscard]]
  145. PIMAGE_NT_HEADERS image_interpreter::image_nt_headers() const noexcept {
  146. return m_nt_headers;
  147. }
  148. [[nodiscard]]
  149. PIMAGE_SECTION_HEADER image_interpreter::image_section_header_table() const noexcept {
  150. return m_section_header_table;
  151. }
  152. [[nodiscard]]
  153. PIMAGE_SECTION_HEADER image_interpreter::image_section_header(size_t n) const {
  154. if (n < m_nt_headers->FileHeader.NumberOfSections) {
  155. return m_section_header_table + n;
  156. } else {
  157. throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Section index is out of range.");
  158. }
  159. }
  160. [[nodiscard]]
  161. PIMAGE_SECTION_HEADER image_interpreter::image_section_header(std::string_view section_name) const {
  162. if (section_name.length() <= 8) {
  163. std::array<BYTE, 8> name{};
  164. std::copy(section_name.begin(), section_name.end(), name.begin());
  165. auto it = m_section_header_name_lookup_table.find(name);
  166. if (it != m_section_header_name_lookup_table.end()) {
  167. return it->second;
  168. } else {
  169. throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format(u8"Target section header is not found: section_name = {}", section_name));
  170. }
  171. } else {
  172. throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found: section_name is too long.");
  173. }
  174. }
  175. [[nodiscard]]
  176. PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_rva(rva_t rva) const {
  177. auto it = m_section_header_rva_lookup_table.upper_bound(rva);
  178. if (it != m_section_header_rva_lookup_table.begin()) {
  179. --it;
  180. }
  181. rva_t section_rva_begin = it->second->VirtualAddress;
  182. rva_t section_rva_end = section_rva_begin + it->second->Misc.VirtualSize;
  183. if (section_rva_begin <= rva && rva < section_rva_end) {
  184. return it->second;
  185. } else {
  186. throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found.")
  187. .push_hint(fmt::format("rva = 0x{:x}", rva));
  188. }
  189. }
  190. [[nodiscard]]
  191. PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_va(va_t va) const {
  192. return image_section_header_from_rva(static_cast<rva_t>(va - m_nt_headers->OptionalHeader.ImageBase));
  193. }
  194. [[nodiscard]]
  195. PIMAGE_SECTION_HEADER image_interpreter::image_section_header_from_fo(fo_t file_offset) const {
  196. auto it = m_section_header_fo_lookup_table.upper_bound(file_offset);
  197. if (it != m_section_header_fo_lookup_table.begin()) {
  198. --it;
  199. }
  200. uintptr_t section_fo_begin = it->second->PointerToRawData;
  201. uintptr_t section_fo_end = section_fo_begin + it->second->SizeOfRawData;
  202. if (section_fo_begin <= file_offset && file_offset < section_fo_end) {
  203. return it->second;
  204. } else {
  205. throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target section header is not found.")
  206. .push_hint(fmt::format(u8"file_offset = 0x{:x}", file_offset));
  207. }
  208. }
  209. [[nodiscard]]
  210. image_interpreter::va_t image_interpreter::convert_rva_to_va(rva_t rva) const noexcept {
  211. return rva + m_nt_headers->OptionalHeader.ImageBase;
  212. }
  213. [[nodiscard]]
  214. image_interpreter::fo_t image_interpreter::convert_rva_to_fo(rva_t rva) const {
  215. auto section_header = image_section_header_from_rva(rva);
  216. return section_header->PointerToRawData + (rva - static_cast<uintptr_t>(section_header->VirtualAddress));
  217. }
  218. [[nodiscard]]
  219. image_interpreter::rva_t image_interpreter::convert_fo_to_rva(fo_t file_offset) const {
  220. auto section_header = image_section_header_from_fo(file_offset);
  221. return section_header->VirtualAddress + (file_offset - section_header->PointerToRawData);
  222. }
  223. [[nodiscard]]
  224. image_interpreter::va_t image_interpreter::convert_fo_to_va(fo_t file_offset) const {
  225. return convert_fo_to_rva(file_offset) + m_nt_headers->OptionalHeader.ImageBase;
  226. }
  227. [[nodiscard]]
  228. image_interpreter::rva_t image_interpreter::convert_va_to_rva(va_t va) const noexcept {
  229. return va - m_nt_headers->OptionalHeader.ImageBase;
  230. }
  231. [[nodiscard]]
  232. image_interpreter::fo_t image_interpreter::convert_va_to_fo(va_t va) const {
  233. return image_section_header_from_va(va)->PointerToRawData;
  234. }
  235. [[nodiscard]]
  236. size_t image_interpreter::number_of_sections() const noexcept {
  237. return m_nt_headers->FileHeader.NumberOfSections;
  238. }
  239. PIMAGE_IMPORT_DESCRIPTOR image_interpreter::import_descriptor_from_rva(rva_t rva) {
  240. auto it = m_iat_rva_lookup_table.find(rva);
  241. return it != m_iat_rva_lookup_table.end() ? it->second.first : nullptr;
  242. }
  243. PIMAGE_THUNK_DATA image_interpreter::import_lookup_entry_from_rva(rva_t rva) {
  244. auto it = m_iat_rva_lookup_table.find(rva);
  245. return it != m_iat_rva_lookup_table.end() ? it->second.second : nullptr;
  246. }
  247. }
  248. #undef NKG_CURRENT_SOURCE_LINE
  249. #undef NKG_CURRENT_SOURCE_FILE