ImageInterpreter.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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._SectionRvaTable[NewImage._SectionHeaderTable[i].VirtualAddress] = i;
  57. NewImage._SectionFileOffsetTable[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._RelocationRvaTable[Rva + (RelocItems[i] & 0x0fff)] = 2;
  75. break;
  76. case IMAGE_REL_BASED_HIGHLOW:
  77. NewImage._RelocationRvaTable[Rva + (RelocItems[i] & 0x0fff)] = 4;
  78. break;
  79. #if defined(IMAGE_REL_BASED_DIR64)
  80. case IMAGE_REL_BASED_DIR64:
  81. NewImage._RelocationRvaTable[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(size_t Idx) const {
  155. if (Idx < _NtHeaders->FileHeader.NumberOfSections) {
  156. return _SectionHeaderTable + Idx;
  157. } else {
  158. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Idx is out of range."));
  159. }
  160. }
  161. [[nodiscard]]
  162. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByName(PCSTR lpszSectionName) const {
  163. uint64_t NameValue = 0;
  164. for (int i = 0; i < sizeof(NameValue) && lpszSectionName[i]; ++i)
  165. reinterpret_cast<PSTR>(&NameValue)[i] = lpszSectionName[i];
  166. auto it = _SectionNameTable.find(NameValue);
  167. if (it == _SectionNameTable.end()) {
  168. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  169. .AddHint(std::xstring::format(TEXT("lpszSectionName = %s"), lpszSectionName));
  170. }
  171. return &_SectionHeaderTable[it->second];
  172. }
  173. [[nodiscard]]
  174. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByRva(uintptr_t Rva) const {
  175. auto it = _SectionRvaTable.upper_bound(Rva);
  176. if (it != _SectionRvaTable.begin()) {
  177. --it;
  178. }
  179. auto SectionHeader = &_SectionHeaderTable[it->second];
  180. uintptr_t SectionRvaBegin = SectionHeader->VirtualAddress;
  181. uintptr_t SectionRvaEnd = SectionRvaBegin + SectionHeader->Misc.VirtualSize;
  182. if (SectionRvaBegin <= Rva && Rva < SectionRvaEnd) {
  183. return SectionHeader;
  184. } else {
  185. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  186. .AddHint(std::xstring::format(TEXT("Rva = 0x%zx"), Rva));
  187. }
  188. }
  189. [[nodiscard]]
  190. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByVa(uintptr_t Va) const {
  191. return ImageSectionHeaderByRva(Va - _NtHeaders->OptionalHeader.ImageBase);
  192. }
  193. [[nodiscard]]
  194. PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByFileOffset(uintptr_t FileOffset) const {
  195. auto it = _SectionFileOffsetTable.upper_bound(FileOffset);
  196. if (it != _SectionFileOffsetTable.begin()) {
  197. --it;
  198. }
  199. auto SectionHeader = &_SectionHeaderTable[it->second];
  200. uintptr_t SectionFileOffsetBegin = SectionHeader->PointerToRawData;
  201. uintptr_t SectionFileOffsetEnd = SectionFileOffsetBegin + SectionHeader->SizeOfRawData;
  202. if (SectionFileOffsetBegin <= FileOffset && FileOffset < SectionFileOffsetEnd) {
  203. return SectionHeader;
  204. } else {
  205. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
  206. .AddHint(std::xstring::format(TEXT("FileOffset = 0x%zx"), FileOffset));
  207. }
  208. }
  209. [[nodiscard]]
  210. uintptr_t ImageInterpreter::RvaToVa(uintptr_t Rva) const noexcept {
  211. return Rva + _NtHeaders->OptionalHeader.ImageBase;
  212. }
  213. [[nodiscard]]
  214. uintptr_t ImageInterpreter::RvaToFileOffset(uintptr_t Rva) const {
  215. auto SectionHeader = ImageSectionHeaderByRva(Rva);
  216. return SectionHeader->PointerToRawData + (Rva - static_cast<uintptr_t>(SectionHeader->VirtualAddress));
  217. }
  218. [[nodiscard]]
  219. uintptr_t ImageInterpreter::FileOffsetToRva(uintptr_t FileOffset) const {
  220. auto SectionHeader = ImageSectionHeaderByFileOffset(FileOffset);
  221. return SectionHeader->VirtualAddress + (FileOffset - SectionHeader->PointerToRawData);
  222. }
  223. [[nodiscard]]
  224. uintptr_t ImageInterpreter::FileOffsetToVa(uintptr_t FileOffset) const {
  225. return FileOffsetToRva(FileOffset) + _NtHeaders->OptionalHeader.ImageBase;
  226. }
  227. [[nodiscard]]
  228. uintptr_t ImageInterpreter::VaToRva(uintptr_t Va) const noexcept {
  229. return Va - _NtHeaders->OptionalHeader.ImageBase;
  230. }
  231. [[nodiscard]]
  232. uintptr_t ImageInterpreter::VaToFileOffset(uintptr_t Va) const {
  233. return ImageSectionHeaderByVa(Va)->PointerToRawData;
  234. }
  235. [[nodiscard]]
  236. bool ImageInterpreter::IsRvaRangeInRelocTable(uintptr_t Rva, size_t Size) const {
  237. auto it = _RelocationRvaTable.upper_bound(Rva);
  238. if (it != _RelocationRvaTable.begin()) {
  239. --it;
  240. }
  241. return it->first <= Rva && Rva < it->first + it->second;
  242. }
  243. [[nodiscard]]
  244. bool ImageInterpreter::IsVaRangeInRelocTable(uintptr_t Va, size_t Size) const {
  245. return IsRvaRangeInRelocTable(VaToRva(Va), Size);
  246. }
  247. [[nodiscard]]
  248. bool ImageInterpreter::IsFileOffsetRangeInRelocTable(uintptr_t FileOffset, size_t Size) const {
  249. return IsRvaRangeInRelocTable(FileOffsetToRva(FileOffset), Size);
  250. }
  251. [[nodiscard]]
  252. DWORD ImageInterpreter::ImageFileMajorVersion() const {
  253. if (_VsFixedFileInfo) {
  254. return _VsFixedFileInfo->dwFileVersionMS;
  255. } else {
  256. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  257. }
  258. }
  259. [[nodiscard]]
  260. DWORD ImageInterpreter::ImageFileMinorVersion() const {
  261. if (_VsFixedFileInfo) {
  262. return _VsFixedFileInfo->dwFileVersionLS;
  263. } else {
  264. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  265. }
  266. }
  267. [[nodiscard]]
  268. DWORD ImageInterpreter::ImageProductMajorVersion() const {
  269. if (_VsFixedFileInfo) {
  270. return _VsFixedFileInfo->dwProductVersionMS;
  271. } else {
  272. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  273. }
  274. }
  275. [[nodiscard]]
  276. DWORD ImageInterpreter::ImageProductMinorVersion() const {
  277. if (_VsFixedFileInfo) {
  278. return _VsFixedFileInfo->dwProductVersionLS;
  279. } else {
  280. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
  281. }
  282. }
  283. [[nodiscard]]
  284. size_t ImageInterpreter::NumberOfSections() const noexcept {
  285. return _NtHeaders->FileHeader.NumberOfSections;
  286. }
  287. }