navicat-keygen/navicat-patcher/ImageInterpreter.cpp
2019-09-28 00:20:43 +08:00

343 lines
16 KiB
C++

#include "ImageInterpreter.hpp"
#undef NKG_CURRENT_SOURCE_FILE
#undef NKG_CURRENT_SOURCE_LINE
#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\ImageInterpreter.cpp")
#define NKG_CURRENT_SOURCE_LINE() __LINE__
namespace nkg {
ImageInterpreter::ImageInterpreter() :
_DosHeader(nullptr),
_NtHeaders(nullptr),
_SectionHeaderTable(nullptr),
_VsFixedFileInfo(nullptr) {}
[[nodiscard]]
ImageInterpreter ImageInterpreter::ParseImage(PVOID ImageBase, bool DisableRelocationParsing) {
ImageInterpreter NewImage;
NewImage._DosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(ImageBase);
if (NewImage._DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (DOS signature check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
}
NewImage._NtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<uint8_t*>(ImageBase) + NewImage._DosHeader->e_lfanew
);
if (NewImage._NtHeaders->Signature != IMAGE_NT_SIGNATURE) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (NT signature check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
}
#if defined(_M_AMD64)
if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
}
if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
}
#elif defined(_M_IX86)
if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
}
if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
.AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
}
#else
#error "Unsupported architecture."
#endif
NewImage._SectionHeaderTable = reinterpret_cast<PIMAGE_SECTION_HEADER>(
reinterpret_cast<char*>(&NewImage._NtHeaders->OptionalHeader) + NewImage._NtHeaders->FileHeader.SizeOfOptionalHeader
);
for (WORD i = 0; i < NewImage._NtHeaders->FileHeader.NumberOfSections; ++i) {
uint64_t SectionName = *reinterpret_cast<uint64_t*>(NewImage._SectionHeaderTable[i].Name);
if (NewImage._SectionNameTable.find(SectionName) == NewImage._SectionNameTable.end()) {
NewImage._SectionNameTable[SectionName] = i;
}
NewImage._SectionRvaTable[NewImage._SectionHeaderTable[i].VirtualAddress] = i;
NewImage._SectionFileOffsetTable[NewImage._SectionHeaderTable[i].PointerToRawData] = i;
}
if (!DisableRelocationParsing && NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) {
auto RelocTableRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
auto RelocTable = NewImage.RvaToPointer<PIMAGE_BASE_RELOCATION>(RelocTableRva);
while (RelocTable->VirtualAddress != 0) {
uintptr_t Rva = RelocTable->VirtualAddress;
PWORD RelocItems = reinterpret_cast<PWORD>(RelocTable + 1);
DWORD RelocItemsCount = (RelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
for (DWORD i = 0; i < RelocItemsCount; ++i) {
auto RelocType = RelocItems[i] >> 12;
switch (RelocType) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
case IMAGE_REL_BASED_LOW:
case IMAGE_REL_BASED_HIGHADJ:
NewImage._RelocationRvaTable[Rva + (RelocItems[i] & 0x0fff)] = 2;
break;
case IMAGE_REL_BASED_HIGHLOW:
NewImage._RelocationRvaTable[Rva + (RelocItems[i] & 0x0fff)] = 4;
break;
#if defined(IMAGE_REL_BASED_DIR64)
case IMAGE_REL_BASED_DIR64:
NewImage._RelocationRvaTable[Rva + (RelocItems[i] & 0x0fff)] = 8;
break;
#endif
default:
break;
}
}
RelocTable = reinterpret_cast<PIMAGE_BASE_RELOCATION>(&RelocItems[RelocItemsCount]);
}
}
if (NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) {
uintptr_t ResourceRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
auto ResourceTypeTable =
NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva);
auto ResourceTypeNameEntries =
reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceTypeTable + 1);
auto ResourceTypeIdEntries =
ResourceTypeNameEntries + ResourceTypeTable->NumberOfNamedEntries;
bool VS_FII_Ok = false;
for (WORD i = 0; i < ResourceTypeTable->NumberOfIdEntries && !VS_FII_Ok; ++i) {
if (ResourceTypeIdEntries[i].Id == reinterpret_cast<uintptr_t>(RT_VERSION) && ResourceTypeIdEntries[i].DataIsDirectory) {
auto ResourceNameTable =
NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva + ResourceTypeIdEntries[i].OffsetToDirectory);
auto ResourceNameNameEntries =
reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceNameTable + 1);
auto ResourceNameIdEntries =
ResourceNameNameEntries + ResourceNameTable->NumberOfNamedEntries;
for (WORD j = 0; j < ResourceNameTable->NumberOfIdEntries && !VS_FII_Ok; ++j) {
if (ResourceNameIdEntries[j].Id == VS_VERSION_INFO && ResourceNameIdEntries[j].DataIsDirectory) {
auto ResourceLangTable =
NewImage.RvaToPointer<PIMAGE_RESOURCE_DIRECTORY>(ResourceRva + ResourceNameIdEntries[j].OffsetToDirectory);
auto ResourceLangNameEntries =
reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(ResourceLangTable + 1);
auto ResourceLangIdEntries =
ResourceLangNameEntries + ResourceLangTable->NumberOfNamedEntries;
for (WORD k = 0; k < ResourceLangTable->NumberOfIdEntries && !VS_FII_Ok; ++k) {
if (ResourceLangIdEntries[k].Id == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) && !ResourceLangIdEntries[k].DataIsDirectory) {
auto ResourceDataEntry =
NewImage.RvaToPointer<PIMAGE_RESOURCE_DATA_ENTRY>(ResourceRva + ResourceLangIdEntries[k].OffsetToData);
auto VsVersionInfo = NewImage.RvaToPointer<PBYTE>(ResourceDataEntry->OffsetToData);
auto VsVersionInfoszKey = reinterpret_cast<PWSTR>(VsVersionInfo + 6);
if (_wcsicmp(VsVersionInfoszKey, L"VS_VERSION_INFO") == 0) {
auto p = reinterpret_cast<PBYTE>(VsVersionInfoszKey + _countof(L"VS_VERSION_INFO"));
while (NewImage.PointerToRva(p) % sizeof(DWORD)) {
++p;
}
if (reinterpret_cast<VS_FIXEDFILEINFO*>(p)->dwSignature == VS_FFI_SIGNATURE) {
NewImage._VsFixedFileInfo = reinterpret_cast<VS_FIXEDFILEINFO*>(p);
VS_FII_Ok = true;
}
}
}
}
}
}
}
}
}
return NewImage;
}
[[nodiscard]]
PIMAGE_DOS_HEADER ImageInterpreter::ImageDosHeader() const noexcept {
return _DosHeader;
}
[[nodiscard]]
PIMAGE_NT_HEADERS ImageInterpreter::ImageNtHeaders() const noexcept {
return _NtHeaders;
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionTable() const noexcept {
return _SectionHeaderTable;
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeader(size_t Idx) const {
if (Idx < _NtHeaders->FileHeader.NumberOfSections) {
return _SectionHeaderTable + Idx;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Idx is out of range."));
}
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByName(PCSTR lpszSectionName) const {
uint64_t NameValue = 0;
for (int i = 0; i < sizeof(NameValue) && lpszSectionName[i]; ++i)
reinterpret_cast<PSTR>(&NameValue)[i] = lpszSectionName[i];
auto it = _SectionNameTable.find(NameValue);
if (it == _SectionNameTable.end()) {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
.AddHint(std::xstring::format(TEXT("lpszSectionName = %s"), lpszSectionName));
}
return &_SectionHeaderTable[it->second];
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByRva(uintptr_t Rva) const {
auto it = _SectionRvaTable.upper_bound(Rva);
if (it != _SectionRvaTable.begin()) {
--it;
}
auto SectionHeader = &_SectionHeaderTable[it->second];
uintptr_t SectionRvaBegin = SectionHeader->VirtualAddress;
uintptr_t SectionRvaEnd = SectionRvaBegin + SectionHeader->Misc.VirtualSize;
if (SectionRvaBegin <= Rva && Rva < SectionRvaEnd) {
return SectionHeader;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
.AddHint(std::xstring::format(TEXT("Rva = 0x%zx"), Rva));
}
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByVa(uintptr_t Va) const {
return ImageSectionHeaderByRva(Va - _NtHeaders->OptionalHeader.ImageBase);
}
[[nodiscard]]
PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeaderByFileOffset(uintptr_t FileOffset) const {
auto it = _SectionFileOffsetTable.upper_bound(FileOffset);
if (it != _SectionFileOffsetTable.begin()) {
--it;
}
auto SectionHeader = &_SectionHeaderTable[it->second];
uintptr_t SectionFileOffsetBegin = SectionHeader->PointerToRawData;
uintptr_t SectionFileOffsetEnd = SectionFileOffsetBegin + SectionHeader->SizeOfRawData;
if (SectionFileOffsetBegin <= FileOffset && FileOffset < SectionFileOffsetEnd) {
return SectionHeader;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
.AddHint(std::xstring::format(TEXT("FileOffset = 0x%zx"), FileOffset));
}
}
[[nodiscard]]
uintptr_t ImageInterpreter::RvaToVa(uintptr_t Rva) const noexcept {
return Rva + _NtHeaders->OptionalHeader.ImageBase;
}
[[nodiscard]]
uintptr_t ImageInterpreter::RvaToFileOffset(uintptr_t Rva) const {
auto SectionHeader = ImageSectionHeaderByRva(Rva);
return SectionHeader->PointerToRawData + (Rva - static_cast<uintptr_t>(SectionHeader->VirtualAddress));
}
[[nodiscard]]
uintptr_t ImageInterpreter::FileOffsetToRva(uintptr_t FileOffset) const {
auto SectionHeader = ImageSectionHeaderByFileOffset(FileOffset);
return SectionHeader->VirtualAddress + (FileOffset - SectionHeader->PointerToRawData);
}
[[nodiscard]]
uintptr_t ImageInterpreter::FileOffsetToVa(uintptr_t FileOffset) const {
return FileOffsetToRva(FileOffset) + _NtHeaders->OptionalHeader.ImageBase;
}
[[nodiscard]]
uintptr_t ImageInterpreter::VaToRva(uintptr_t Va) const noexcept {
return Va - _NtHeaders->OptionalHeader.ImageBase;
}
[[nodiscard]]
uintptr_t ImageInterpreter::VaToFileOffset(uintptr_t Va) const {
return ImageSectionHeaderByVa(Va)->PointerToRawData;
}
[[nodiscard]]
bool ImageInterpreter::IsRvaRangeInRelocTable(uintptr_t Rva, size_t Size) const {
auto it = _RelocationRvaTable.upper_bound(Rva);
if (it != _RelocationRvaTable.begin()) {
--it;
}
return it->first <= Rva && Rva < it->first + it->second;
}
[[nodiscard]]
bool ImageInterpreter::IsVaRangeInRelocTable(uintptr_t Va, size_t Size) const {
return IsRvaRangeInRelocTable(VaToRva(Va), Size);
}
[[nodiscard]]
bool ImageInterpreter::IsFileOffsetRangeInRelocTable(uintptr_t FileOffset, size_t Size) const {
return IsRvaRangeInRelocTable(FileOffsetToRva(FileOffset), Size);
}
[[nodiscard]]
DWORD ImageInterpreter::ImageFileMajorVersion() const {
if (_VsFixedFileInfo) {
return _VsFixedFileInfo->dwFileVersionMS;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
}
}
[[nodiscard]]
DWORD ImageInterpreter::ImageFileMinorVersion() const {
if (_VsFixedFileInfo) {
return _VsFixedFileInfo->dwFileVersionLS;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
}
}
[[nodiscard]]
DWORD ImageInterpreter::ImageProductMajorVersion() const {
if (_VsFixedFileInfo) {
return _VsFixedFileInfo->dwProductVersionMS;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
}
}
[[nodiscard]]
DWORD ImageInterpreter::ImageProductMinorVersion() const {
if (_VsFixedFileInfo) {
return _VsFixedFileInfo->dwProductVersionLS;
} else {
throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
}
}
[[nodiscard]]
size_t ImageInterpreter::NumberOfSections() const noexcept {
return _NtHeaders->FileHeader.NumberOfSections;
}
}