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();
51 mWordSize = ntHeaderWordSize(mNtHeaders);
52 mMachineArch = mNtHeaders->FileHeader.Machine;
53 if (mWordSize == 32) {
55 determineDependentLibs(
reinterpret_cast<
const IMAGE_NT_HEADERS32 *>(mNtHeaders));
56 mIsDebug = determineDebug(
reinterpret_cast<
const IMAGE_NT_HEADERS32 *>(mNtHeaders));
59 determineDependentLibs(
reinterpret_cast<
const IMAGE_NT_HEADERS64 *>(mNtHeaders));
60 mIsDebug = determineDebug(
reinterpret_cast<
const IMAGE_NT_HEADERS64 *>(mNtHeaders));
65 releaseFileResources();
68PeHeaderInfo::~PeHeaderInfo()
70 releaseFileResources();
73void PeHeaderInfo::releaseFileResources()
76 UnmapViewOfFile(mFileMemory);
77 mFileMemory =
nullptr;
80 if (mFileMapHandle != NULL) {
81 CloseHandle(mFileMapHandle);
82 mFileMapHandle = NULL;
85 if (mFileHandle != NULL && mFileHandle != INVALID_HANDLE_VALUE) {
86 CloseHandle(mFileHandle);
93bool PeHeaderInfo::isValid()
98QString PeHeaderInfo::errorMessage()
100 return mErrorMessage;
103unsigned int PeHeaderInfo::wordSize()
108unsigned int PeHeaderInfo::machineArch()
113QStringList PeHeaderInfo::dependentLibs()
115 return mDependentLibs;
118bool PeHeaderInfo::isDebug()
120 return mIsDebug.value_or(
false);
123IMAGE_NT_HEADERS *PeHeaderInfo::getNtHeader()
125 IMAGE_DOS_HEADER *dosHeader =
static_cast<PIMAGE_DOS_HEADER>(mFileMemory);
127 if (IsBadReadPtr(dosHeader,
sizeof(IMAGE_DOS_HEADER))
128 || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
129 mErrorMessage = QString::fromLatin1(
"DOS header check failed.");
133 char *ntHeaderC =
static_cast<
char *>(mFileMemory) + dosHeader->e_lfanew;
134 IMAGE_NT_HEADERS *ntHeaders =
reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
136 if (IsBadReadPtr(ntHeaders,
sizeof(ntHeaders->Signature))
137 || ntHeaders->Signature != IMAGE_NT_SIGNATURE
138 || IsBadReadPtr(&ntHeaders->FileHeader,
sizeof(IMAGE_FILE_HEADER))) {
139 mErrorMessage = QString::fromLatin1(
"NT header check failed.");
143 if (!ntHeaderWordSize(ntHeaders)) {
144 mErrorMessage = QString::fromLatin1(
"NT header check failed; magic %1 is invalid.").arg(ntHeaders->OptionalHeader.Magic);
148 IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
149 if (IsBadReadPtr(sectionHeaders,
150 ntHeaders->FileHeader.NumberOfSections *
sizeof(IMAGE_SECTION_HEADER))) {
151 mErrorMessage = QString::fromLatin1(
"NT header section header check failed.");
157QString PeHeaderInfo::stringFromRvaPtr(
const void *rvaPtr)
159 return QString::fromLocal8Bit(
static_cast<
const char *>(rvaPtr));
162PeHeaderInfo::MsvcDebugRuntimeResult PeHeaderInfo::checkMsvcDebugRuntime()
164 for (
const QString &lib : mDependentLibs) {
166 if (lib.startsWith(
"MSVCR"_L1, Qt::CaseInsensitive)
167 || lib.startsWith(
"MSVCP"_L1, Qt::CaseInsensitive)
168 || lib.startsWith(
"VCRUNTIME"_L1, Qt::CaseInsensitive)
169 || lib.startsWith(
"VCCORLIB"_L1, Qt::CaseInsensitive)
170 || lib.startsWith(
"CONCRT"_L1, Qt::CaseInsensitive)
171 || lib.startsWith(
"UCRTBASE"_L1, Qt::CaseInsensitive)) {
172 qsizetype lastDotPos = lib.lastIndexOf(u'.');
173 pos = -1 == lastDotPos ? 0 : lastDotPos - 1;
177 const auto removeExtraSuffix = [&lib, &pos](
const QString &suffix) ->
void {
178 if (lib.contains(suffix, Qt::CaseInsensitive))
179 pos -= suffix.size();
181 removeExtraSuffix(
"_app"_L1);
182 removeExtraSuffix(
"_atomic_wait"_L1);
183 removeExtraSuffix(
"_codecvt_ids"_L1);
187 return lib.at(pos).toLower() == u'd' ? MsvcDebugRuntime : MsvcReleaseRuntime;
189 return NoMsvcRuntime;
192template <
class ImageNtHeader>
193QStringList PeHeaderInfo::readImportSections(
const ImageNtHeader *ntHeaders)
196 const DWORD importsStartRVA = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
197 if (!importsStartRVA) {
198 mErrorMessage = QString::fromLatin1(
"Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
199 return QStringList();
201 const IMAGE_IMPORT_DESCRIPTOR *importDesc =
static_cast<
const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, mFileMemory));
203 mErrorMessage = QString::fromLatin1(
"Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
204 return QStringList();
207 for ( ; importDesc->Name; ++importDesc)
208 result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, mFileMemory)));
212 if (
const DWORD delayedImportsStartRVA = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
213 const ImgDelayDescr *delayedImportDesc =
static_cast<
const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, mFileMemory));
214 for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
215 result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, mFileMemory)));
221template <
class ImageNtHeader>
222QStringList PeHeaderInfo::determineDependentLibs(
const ImageNtHeader *nth)
224 return readImportSections(nth);
227template <
class ImageNtHeader>
228bool PeHeaderInfo::determineDebug(
const ImageNtHeader *nth)
230 if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
233 if (mDependentLibs.isEmpty())
234 mDependentLibs = determineDependentLibs(nth);
236 const bool hasDebugEntry = mNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
239 const MsvcDebugRuntimeResult msvcrt = checkMsvcDebugRuntime();
240 if (msvcrt == NoMsvcRuntime)
241 return hasDebugEntry;
243 return hasDebugEntry && msvcrt == MsvcDebugRuntime;
246template <
class ImageNtHeader>
247const IMAGE_SECTION_HEADER *PeHeaderInfo::findSectionHeader(DWORD rva,
const ImageNtHeader *nTHeader)
249 const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
250 const IMAGE_SECTION_HEADER *sectionEnd = section + mNtHeaders->FileHeader.NumberOfSections;
251 for ( ; section < sectionEnd; ++section)
252 if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
257template <
class ImageNtHeader>
258const void *PeHeaderInfo::rvaToPtr(DWORD rva,
const ImageNtHeader *nTHeader,
const void *imageBase)
260 const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
263 const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
264 return static_cast<
const char *>(imageBase) + rva - delta;
267template <
class ImageNtHeader>
271 enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
272 if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
274 if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
284 if (peCache->contains(fileName))
285 return peCache->value(fileName);
287 auto *peHeaderInfo =
new PeHeaderInfo(fileName);
288 peCache->insert(fileName, peHeaderInfo);
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)