ImageInterpreter.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #include "ImageInterpreter.hpp"
  2. #undef NKG_CURRENT_SOURCE_FILE
  3. #undef NKG_CURRENT_SOURCE_LINE
  4. #define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\ImageInterpreter.cpp")
  5. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  6. namespace nkg {
  7. ImageInterpreter::ImageInterpreter() :
  8. _DosHeader(nullptr),
  9. _NtHeaders(nullptr),
  10. _SectionHeaderTable(nullptr),
  11. _VsFixedFileInfo(nullptr) {}
  12. [[nodiscard]]
  13. ImageInterpreter ImageInterpreter::ParseImage(PVOID ImageBase, bool DisableRelocationParsing) {
  14. ImageInterpreter NewImage;
  15. NewImage._DosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(ImageBase);
  16. if (NewImage._DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  17. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (DOS signature check failure)"))
  18. .AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
  19. }
  20. NewImage._NtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
  21. reinterpret_cast<uint8_t*>(ImageBase) + NewImage._DosHeader->e_lfanew
  22. );
  23. if (NewImage._NtHeaders->Signature != IMAGE_NT_SIGNATURE) {
  24. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (NT signature check failure)"))
  25. .AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
  26. }
  27. #if defined(_M_AMD64)
  28. if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  29. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
  30. .AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
  31. }
  32. if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
  33. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
  34. .AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
  35. }
  36. #elif defined(_M_IX86)
  37. if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  38. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
  39. .AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
  40. }
  41. if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
  42. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
  43. .AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
  44. }
  45. #else
  46. #error "Unsupported architecture."
  47. #endif
  48. NewImage._SectionHeaderTable = reinterpret_cast<PIMAGE_SECTION_HEADER>(
  49. reinterpret_cast<char*>(&NewImage._NtHeaders->OptionalHeader) + NewImage._NtHeaders->FileHeader.SizeOfOptionalHeader
  50. );
  51. for (WORD i = 0; i < NewImage._NtHeaders->FileHeader.NumberOfSections; ++i) {
  52. uint64_t SectionName = *reinterpret_cast<uint64_t*>(NewImage._SectionHeaderTable[i].Name);
  53. if (NewImage._SectionNameTable.find(SectionName) == NewImage._SectionNameTable.end()) {
  54. NewImage._SectionNameTable[SectionName] = i;
  55. }
  56. NewImage._SectionAddressTable[NewImage._SectionHeaderTable[i].VirtualAddress] = i;
  57. NewImage._SectionOffsetTable[NewImage._SectionHeaderTable[i].PointerToRawData] = i;
  58. }
  59. if (!DisableRelocationParsing && NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) {
  60. auto RelocTableRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
  61. auto RelocTable = NewImage.RvaToPointer<PIMAGE_BASE_RELOCATION>(RelocTableRva);
  62. while (RelocTable->VirtualAddress != 0) {
  63. uintptr_t Rva = RelocTable->VirtualAddress;
  64. PWORD RelocItems = reinterpret_cast<PWORD>(RelocTable + 1);
  65. DWORD RelocItemsCount = (RelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
  66. for (DWORD i = 0; i < RelocItemsCount; ++i) {
  67. auto RelocType = RelocItems[i] >> 12;
  68. switch (RelocType) {
  69. case IMAGE_REL_BASED_ABSOLUTE:
  70. break;
  71. case IMAGE_REL_BASED_HIGH:
  72. case IMAGE_REL_BASED_LOW:
  73. case IMAGE_REL_BASED_HIGHADJ:
  74. NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 2;
  75. break;
  76. case IMAGE_REL_BASED_HIGHLOW:
  77. NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 4;
  78. break;
  79. #if defined(IMAGE_REL_BASED_DIR64)
  80. case IMAGE_REL_BASED_DIR64:
  81. NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 8;
  82. break;
  83. #endif
  84. default:
  85. break;
  86. }
  87. }
  88. RelocTable = reinterpret_cast<PIMAGE_BASE_RELOCATION>(&RelocItems[RelocItemsCount]);
  89. }
  90. }
  91. if (NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) {
  92. uintptr_t ResourceRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  93. auto ResourceTypeTable =
  94. NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva);
  95. auto ResourceTypeNameEntries =
  96. reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceTypeTable + 1);
  97. auto ResourceTypeIdEntries =
  98. ResourceTypeNameEntries + ResourceTypeTable->NumberOfNamedEntries;
  99. bool VS_FII_Ok = false;
  100. for (WORD i = 0; i < ResourceTypeTable->NumberOfIdEntries && !VS_FII_Ok; ++i) {
  101. if (ResourceTypeIdEntries[i].Id == reinterpret_cast<uintptr_t>(RT_VERSION) && ResourceTypeIdEntries[i].DataIsDirectory) {
  102. auto ResourceNameTable =
  103. NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva + ResourceTypeIdEntries[i].OffsetToDirectory);
  104. auto ResourceNameNameEntries =
  105. reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceNameTable + 1);
  106. auto ResourceNameIdEntries =
  107. ResourceNameNameEntries + ResourceNameTable->NumberOfNamedEntries;
  108. for (WORD j = 0; j < ResourceNameTable->NumberOfIdEntries && !VS_FII_Ok; ++j) {
  109. if (ResourceNameIdEntries[j].Id == VS_VERSION_INFO && ResourceNameIdEntries[j].DataIsDirectory) {
  110. auto ResourceLangTable =
  111. NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva + ResourceNameIdEntries[j].OffsetToDirectory);
  112. auto ResourceLangNameEntries =
  113. reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceLangTable + 1);
  114. auto ResourceLangIdEntries =
  115. ResourceLangNameEntries + ResourceLangTable->NumberOfNamedEntries;
  116. for (WORD k = 0; k < ResourceLangTable->NumberOfIdEntries && !VS_FII_Ok; ++k) {
  117. if (ResourceLangIdEntries[k].Id == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) && !ResourceLangIdEntries[k].DataIsDirectory) {
  118. auto ResourceDataEntry =
  119. NewImage.RvaToPointer<PIMAGE_RESOURCE_DATA_ENTRY>(ResourceRva + ResourceLangIdEntries[k].OffsetToData);
  120. auto VsVersionInfo = NewImage.RvaToPointer<PBYTE>(ResourceDataEntry->OffsetToData);
  121. auto VsVersionInfoszKey = reinterpret_cast<PWSTR>(VsVersionInfo + 6);
  122. if (_wcsicmp(VsVersionInfoszKey, L"VS_VERSION_INFO") == 0) {
  123. auto p = reinterpret_cast<PBYTE>(VsVersionInfoszKey + _countof(L"VS_VERSION_INFO"));
  124. while (NewImage.PointerToRva(p) % sizeof(DWORD)) {
  125. ++p;
  126. }
  127. if (reinterpret_cast<VS_FIXEDFILEINFO*>(p)->dwSignature == VS_FFI_SIGNATURE) {
  128. NewImage._VsFixedFileInfo = reinterpret_cast<VS_FIXEDFILEINFO*>(p);
  129. VS_FII_Ok = true;
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. }
  139. return NewImage;
  140. }
  141. [[nodiscard]]
  142. PIMAGE_DOS_HEADER ImageInterpreter::ImageDosHeader() const noexcept {
  143. return _DosHeader;
  144. }
  145. [[nodiscard]]
  146. PIMAGE_NT_HEADERS ImageInterpreter::ImageNtHeaders() const noexcept {
  147. return _NtHeaders;
  148. }
  149. [[nodiscard]]
  150. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionTable() const noexcept {
  151. return _SectionHeaderTable;
  152. }
  153. [[nodiscard]]
  154. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeader(PCSTR lpszSectionName) const {
  155. uint64_t NameValue = 0;
  156. for (int i = 0; i < sizeof(NameValue) && lpszSectionName[i]; ++i)
  157. reinterpret_cast<PSTR>(&NameValue)[i] = lpszSectionName[i];
  158. auto it = _SectionNameTable.find(NameValue);
  159. if (it == _SectionNameTable.end()) {
  160. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  161. .AddHint(std::xstring::format(TEXT("lpszSectionName = %s"), lpszSectionName));
  162. }
  163. return &_SectionHeaderTable[it->second];
  164. }
  165. [[nodiscard]]
  166. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeader(uintptr_t Rva) const {
  167. auto it = _SectionAddressTable.upper_bound(Rva);
  168. if (it != _SectionAddressTable.begin()) {
  169. --it;
  170. }
  171. auto SectionHeader = &_SectionHeaderTable[it->second];
  172. uintptr_t SectionRvaBegin = SectionHeader->VirtualAddress;
  173. uintptr_t SectionRvaEnd = SectionRvaBegin + SectionHeader->Misc.VirtualSize;
  174. if (SectionRvaBegin <= Rva && Rva < SectionRvaEnd) {
  175. return SectionHeader;
  176. } else {
  177. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  178. .AddHint(std::xstring::format(TEXT("Rva = 0x%zx"), Rva));
  179. }
  180. }
  181. [[nodiscard]]
  182. uintptr_t ImageInterpreter::RvaToFileOffset(uintptr_t Rva) const {
  183. auto SectionHeader = ImageSectionHeader(Rva);
  184. return SectionHeader->PointerToRawData + (Rva - static_cast<uintptr_t>(SectionHeader->VirtualAddress));
  185. }
  186. [[nodiscard]]
  187. uintptr_t ImageInterpreter::FileOffsetToRva(uintptr_t FileOffset) const {
  188. auto it = _SectionOffsetTable.upper_bound(FileOffset);
  189. if (it != _SectionOffsetTable.begin()) {
  190. --it;
  191. }
  192. auto SectionHeader = &_SectionHeaderTable[it->second];
  193. uintptr_t SectionFileOffsetBegin = SectionHeader->PointerToRawData;
  194. uintptr_t SectionFileOffsetEnd = SectionFileOffsetBegin + SectionHeader->SizeOfRawData;
  195. if (SectionFileOffsetBegin <= FileOffset && FileOffset < SectionFileOffsetEnd) {
  196. return SectionHeader->VirtualAddress + (FileOffset - SectionHeader->PointerToRawData);
  197. } else {
  198. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  199. .AddHint(std::xstring::format(TEXT("FileOffset = 0x%zx"), FileOffset));
  200. }
  201. }
  202. [[nodiscard]]
  203. bool ImageInterpreter::IsRvaRangeInRelocTable(uintptr_t Rva, size_t Size) const {
  204. auto it = _RelocationAddressTable.upper_bound(Rva);
  205. if (it != _RelocationAddressTable.begin()) {
  206. --it;
  207. }
  208. return it->first <= Rva && Rva < it->first + it->second;
  209. }
  210. DWORD ImageInterpreter::ImageFileMajorVersion() const {
  211. if (_VsFixedFileInfo) {
  212. return _VsFixedFileInfo->dwFileVersionMS;
  213. } else {
  214. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  215. }
  216. }
  217. DWORD ImageInterpreter::ImageFileMinorVersion() const {
  218. if (_VsFixedFileInfo) {
  219. return _VsFixedFileInfo->dwFileVersionLS;
  220. } else {
  221. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  222. }
  223. }
  224. DWORD ImageInterpreter::ImageProductMajorVersion() const {
  225. if (_VsFixedFileInfo) {
  226. return _VsFixedFileInfo->dwProductVersionMS;
  227. } else {
  228. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  229. }
  230. }
  231. DWORD ImageInterpreter::ImageProductMinorVersion() const {
  232. if (_VsFixedFileInfo) {
  233. return _VsFixedFileInfo->dwProductVersionLS;
  234. } else {
  235. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  236. }
  237. }
  238. }