6#include <QtCore/private/qsystemerror_p.h>
11# include <QtCore/qt_windows.h>
12# include <QtCore/private/qsystemerror_p.h>
19using namespace Qt::StringLiterals;
21PeHeaderInfo::PeHeaderInfo(
const QString fileName)
23 mFileHandle = CreateFile(
reinterpret_cast<
const WCHAR*>(fileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
24 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
25 if (mFileHandle == INVALID_HANDLE_VALUE || mFileHandle == NULL) {
26 mErrorMessage = QString::fromLatin1(
"Cannot open '%1': %2")
27 .arg(fileName, QSystemError::windowsString());
31 mFileMapHandle = CreateFileMapping(mFileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
32 if (mFileMapHandle == NULL) {
33 mErrorMessage = QString::fromLatin1(
"Cannot create file mapping of '%1': %2")
34 .arg(fileName, QSystemError::windowsString());
38 mFileMemory = MapViewOfFile(mFileMapHandle, FILE_MAP_READ, 0, 0, 0);
40 mErrorMessage = QString::fromLatin1(
"Cannot map '%1': %2")
41 .arg(fileName, QSystemError::windowsString());
45 mNtHeaders = getNtHeader();
52PeHeaderInfo::~PeHeaderInfo()
55 UnmapViewOfFile(mFileMemory);
57 if (mFileMapHandle != NULL)
58 CloseHandle(mFileMapHandle);
60 if (mFileHandle != NULL && mFileHandle != INVALID_HANDLE_VALUE)
61 CloseHandle(mFileHandle);
64bool PeHeaderInfo::isValid()
69QString PeHeaderInfo::errorMessage()
74unsigned int PeHeaderInfo::wordSize()
79 return ntHeaderWordSize(mNtHeaders);
82unsigned int PeHeaderInfo::machineArch()
87 return mNtHeaders->FileHeader.Machine;
90QStringList PeHeaderInfo::dependentLibs()
95 if (!mDependentLibs.isEmpty())
96 return mDependentLibs;
98 if (wordSize() == 32) {
100 = determineDependentLibs(
reinterpret_cast<
const IMAGE_NT_HEADERS32 *>(mNtHeaders));
103 = determineDependentLibs(
reinterpret_cast<
const IMAGE_NT_HEADERS64 *>(mNtHeaders));
105 return mDependentLibs;
108bool PeHeaderInfo::isDebug()
110 if (!mIsDebug.has_value()) {
111 QStringList dependents = dependentLibs();
112 if (wordSize() == 32) {
113 mIsDebug = determineDebug(
reinterpret_cast<
const IMAGE_NT_HEADERS32 *>(mNtHeaders));
115 mIsDebug = determineDebug(
reinterpret_cast<
const IMAGE_NT_HEADERS64 *>(mNtHeaders));
119 return mIsDebug.value();
122IMAGE_NT_HEADERS *PeHeaderInfo::getNtHeader()
124 IMAGE_DOS_HEADER *dosHeader =
static_cast<PIMAGE_DOS_HEADER>(mFileMemory);
126 if (IsBadReadPtr(dosHeader,
sizeof(IMAGE_DOS_HEADER))
127 || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
128 mErrorMessage = QString::fromLatin1(
"DOS header check failed.");
132 char *ntHeaderC =
static_cast<
char *>(mFileMemory) + dosHeader->e_lfanew;
133 IMAGE_NT_HEADERS *ntHeaders =
reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
135 if (IsBadReadPtr(ntHeaders,
sizeof(ntHeaders->Signature))
136 || ntHeaders->Signature != IMAGE_NT_SIGNATURE
137 || IsBadReadPtr(&ntHeaders->FileHeader,
sizeof(IMAGE_FILE_HEADER))) {
138 mErrorMessage = QString::fromLatin1(
"NT header check failed.");
142 if (!ntHeaderWordSize(ntHeaders)) {
143 mErrorMessage = QString::fromLatin1(
"NT header check failed; magic %1 is invalid.").arg(ntHeaders->OptionalHeader.Magic);
147 IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
148 if (IsBadReadPtr(sectionHeaders,
149 ntHeaders->FileHeader.NumberOfSections *
sizeof(IMAGE_SECTION_HEADER))) {
150 mErrorMessage = QString::fromLatin1(
"NT header section header check failed.");
156QString PeHeaderInfo::stringFromRvaPtr(
const void *rvaPtr)
158 return QString::fromLocal8Bit(
static_cast<
const char *>(rvaPtr));
161PeHeaderInfo::MsvcDebugRuntimeResult PeHeaderInfo::checkMsvcDebugRuntime()
163 for (
const QString &lib : mDependentLibs) {
165 if (lib.startsWith(
"MSVCR"_L1, Qt::CaseInsensitive)
166 || lib.startsWith(
"MSVCP"_L1, Qt::CaseInsensitive)
167 || lib.startsWith(
"VCRUNTIME"_L1, Qt::CaseInsensitive)
168 || lib.startsWith(
"VCCORLIB"_L1, Qt::CaseInsensitive)
169 || lib.startsWith(
"CONCRT"_L1, Qt::CaseInsensitive)
170 || lib.startsWith(
"UCRTBASE"_L1, Qt::CaseInsensitive)) {
171 qsizetype lastDotPos = lib.lastIndexOf(u'.');
172 pos = -1 == lastDotPos ? 0 : lastDotPos - 1;
176 const auto removeExtraSuffix = [&lib, &pos](
const QString &suffix) ->
void {
177 if (lib.contains(suffix, Qt::CaseInsensitive))
178 pos -= suffix.size();
180 removeExtraSuffix(
"_app"_L1);
181 removeExtraSuffix(
"_atomic_wait"_L1);
182 removeExtraSuffix(
"_codecvt_ids"_L1);
186 return lib.at(pos).toLower() == u'd' ? MsvcDebugRuntime : MsvcReleaseRuntime;
188 return NoMsvcRuntime;
191template <
class ImageNtHeader>
192QStringList PeHeaderInfo::readImportSections(
const ImageNtHeader *ntHeaders)
195 const DWORD importsStartRVA = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
196 if (!importsStartRVA) {
197 mErrorMessage = QString::fromLatin1(
"Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
198 return QStringList();
200 const IMAGE_IMPORT_DESCRIPTOR *importDesc =
static_cast<
const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, mFileMemory));
202 mErrorMessage = QString::fromLatin1(
"Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
203 return QStringList();
206 for ( ; importDesc->Name; ++importDesc)
207 result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, mFileMemory)));
211 if (
const DWORD delayedImportsStartRVA = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
212 const ImgDelayDescr *delayedImportDesc =
static_cast<
const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, mFileMemory));
213 for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
214 result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, mFileMemory)));
220template <
class ImageNtHeader>
221QStringList PeHeaderInfo::determineDependentLibs(
const ImageNtHeader *nth)
223 return readImportSections(nth);
226template <
class ImageNtHeader>
227bool PeHeaderInfo::determineDebug(
const ImageNtHeader *nth)
229 if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
232 if (mDependentLibs.isEmpty())
233 mDependentLibs = determineDependentLibs(nth);
235 const bool hasDebugEntry = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
238 const MsvcDebugRuntimeResult msvcrt = checkMsvcDebugRuntime();
239 if (msvcrt == NoMsvcRuntime)
240 return hasDebugEntry;
242 return hasDebugEntry && msvcrt == MsvcDebugRuntime;
245template <
class ImageNtHeader>
246const IMAGE_SECTION_HEADER *PeHeaderInfo::findSectionHeader(DWORD rva,
const ImageNtHeader *nTHeader)
248 const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
249 const IMAGE_SECTION_HEADER *sectionEnd = section + mNtHeaders->FileHeader.NumberOfSections;
250 for ( ; section < sectionEnd; ++section)
251 if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
256template <
class ImageNtHeader>
257const void *PeHeaderInfo::rvaToPtr(DWORD rva,
const ImageNtHeader *nTHeader,
const void *imageBase)
259 const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
262 const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
263 return static_cast<
const char *>(imageBase) + rva - delta;
266template <
class ImageNtHeader>
270 enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
271 if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
273 if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
283 if (peCache->contains(fileName))
284 return peCache->value(fileName);
286 auto *peHeaderInfo =
new PeHeaderInfo(fileName);
287 peCache->insert(fileName, peHeaderInfo);
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)