From eab0ec298b81138c4c602259ebf930f583113b95 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 15:26:04 -0400
Subject: refactored preloadSsl() into preloadDll() removed old HGID check
moved formatSystemMessage() to uibase added Environment class, lists loaded
modules, logged at startup
---
src/shared/util.cpp | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 327 insertions(+)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index ed7c434e..16b373db 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -20,6 +20,7 @@ along with Mod Organizer. If not, see .
#include "util.h"
#include "windows_error.h"
#include "error_report.h"
+#include
#include
#include
@@ -29,6 +30,8 @@ along with Mod Organizer. If not, see .
#include
#include
+using MOBase::formatSystemMessage;
+
namespace MOShared {
@@ -253,4 +256,328 @@ MOBase::VersionInfo createVersionInfo()
}
+struct HandleCloser
+{
+ using pointer = HANDLE;
+ void operator()(HANDLE h)
+ {
+ if (h != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(h);
+ }
+ }
+};
+
+
+Environment::Environment()
+{
+ getLoadedModules();
+}
+
+const std::vector& Environment::loadedModules()
+{
+ return m_modules;
+}
+
+void Environment::getLoadedModules()
+{
+ std::unique_ptr snapshot(CreateToolhelp32Snapshot(
+ TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
+
+ if (snapshot.get() == INVALID_HANDLE_VALUE)
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace()
+ << "CreateToolhelp32Snapshot() failed, "
+ << formatSystemMessage(e);
+
+ return;
+ }
+
+ // Set the size of the structure before using it.
+ MODULEENTRY32 me = {};
+ me.dwSize = sizeof(me);
+
+ // Retrieve information about the first module,
+ // and exit if unsuccessful
+ if (!Module32First(snapshot.get(), &me))
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace()
+ << "Module32First() failed, " << formatSystemMessage(e);
+
+ return;
+ }
+
+ // Now walk the module list of the process,
+ // and display information about each module
+ qInfo() << "modules loaded in process:";
+
+ for (;;)
+ {
+ const auto path = QString::fromWCharArray(me.szExePath);
+
+ m_modules.push_back(Module(path, me.modBaseSize));
+
+ if (!Module32Next(snapshot.get(), &me)) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_NO_MORE_FILES) {
+ qCritical() << "Module32Next() failed, " << formatSystemMessage(e);
+ }
+
+ break;
+ }
+ }
+}
+
+
+Environment::Module::Module(QString path, std::size_t fileSize)
+ : m_path(std::move(path)), m_fileSize(fileSize)
+{
+ const auto fi = getFileInfo();
+
+ m_version = getVersion(fi.ffi);
+ m_timestamp = getTimestamp(fi.ffi);
+ m_versionString = fi.fileDescription;
+}
+
+const QString& Environment::Module::path() const
+{
+ return m_path;
+}
+
+std::size_t Environment::Module::fileSize() const
+{
+ return m_fileSize;
+}
+
+const QString& Environment::Module::version() const
+{
+ return m_version;
+}
+
+const QString& Environment::Module::versionString() const
+{
+ return m_versionString;
+}
+
+QString Environment::Module::timestampString() const
+{
+ if (!m_timestamp.isValid()) {
+ return "(no timestamp)";
+ }
+
+ return m_timestamp.toString(Qt::DateFormat::ISODate);
+}
+
+QString Environment::Module::toString() const
+{
+ QStringList sl;
+
+ sl.push_back(m_path);
+ sl.push_back(QString("%1 B").arg(m_fileSize));
+
+ if (m_version.isEmpty() && m_versionString.isEmpty()) {
+ sl.push_back("(no version)");
+ } else {
+ if (!m_version.isEmpty()) {
+ sl.push_back(m_version);
+ }
+
+ if (m_versionString != m_version) {
+ sl.push_back(versionString());
+ }
+ }
+
+ if (m_timestamp.isValid()) {
+ sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate));
+ } else {
+ sl.push_back("(no timestamp)");
+ }
+
+ return sl.join(", ");
+}
+
+Environment::Module::FileInfo Environment::Module::getFileInfo() const
+{
+ const auto wspath = m_path.toStdWString();
+
+ DWORD dummy = 0;
+ const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy);
+
+ if (size == 0) {
+ const auto e = GetLastError();
+
+ if (e == ERROR_RESOURCE_TYPE_NOT_FOUND) {
+ // not an error, no version information built into that module
+ return {};
+ }
+
+ qCritical().nospace().noquote()
+ << "GetFileVersionInfoSizeW() failed on '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+ auto buffer = std::make_unique(size);
+
+ if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) {
+ const auto e = GetLastError();
+
+ qCritical().nospace().noquote()
+ << "GetFileVersionInfoW() failed on '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+
+ FileInfo fi;
+ fi.ffi = getFixedFileInfo(buffer.get());
+ fi.fileDescription = getFileDescription(buffer.get());
+
+ return fi;
+}
+
+VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
+{
+ void* valuePointer = nullptr;
+ unsigned int valueSize = 0;
+
+ const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ // not an error, no fixed file info
+ return {};
+ }
+
+ const auto* fi = reinterpret_cast(valuePointer);
+
+ if (fi->dwSignature != 0xfeef04bd) {
+ qCritical().nospace().noquote()
+ << "bad file info signature 0x" << hex << fi->dwSignature << " for "
+ << "'" << m_path << "'";
+
+ return {};
+ }
+
+ return *fi;
+}
+
+QString Environment::Module::getFileDescription(std::byte* buffer) const
+{
+ struct LANGANDCODEPAGE
+ {
+ WORD wLanguage;
+ WORD wCodePage;
+ };
+
+ void* valuePointer = nullptr;
+ unsigned int valueSize = 0;
+
+ auto ret = VerQueryValueW(
+ buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ qCritical().nospace().noquote()
+ << "VerQueryValueW() for translations failed on '" << m_path << "'";
+
+ return {};
+ }
+
+ const auto count = valueSize / sizeof(LANGANDCODEPAGE);
+ if (count == 0) {
+ return {};
+ }
+
+ const auto* lcp = reinterpret_cast(valuePointer);
+
+ const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
+ .arg(lcp->wLanguage, 4, 16, QChar('0'))
+ .arg(lcp->wCodePage, 4, 16, QChar('0'));
+
+ ret = VerQueryValueW(
+ buffer, subBlock.toStdWString().c_str(), &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ // not an error, no file version
+ return {};
+ }
+
+ // valueSize includes the null terminator
+ return QString::fromWCharArray(
+ reinterpret_cast(valuePointer), valueSize - 1);
+}
+
+QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
+{
+ if (fi.dwSignature == 0) {
+ return {};
+ }
+
+ const DWORD major = (fi.dwFileVersionMS >> 16 ) & 0xffff;
+ const DWORD minor = (fi.dwFileVersionMS >> 0 ) & 0xffff;
+ const DWORD maintenance = (fi.dwFileVersionLS >> 16 ) & 0xffff;
+ const DWORD build = (fi.dwFileVersionLS >> 0 ) & 0xffff;
+
+ if (major == 0 && minor == 0 && maintenance == 0 && build == 0) {
+ return {};
+ }
+
+ return QString("%1.%2.%3.%4")
+ .arg(major).arg(minor).arg(maintenance).arg(build);
+}
+
+QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
+{
+ FILETIME ft = {};
+
+ if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) {
+ std::unique_ptr h(CreateFileW(
+ m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+
+ if (h.get() == INVALID_HANDLE_VALUE) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "can't open file '" << m_path << "' for timestamp, "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+ if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
+ const auto e = GetLastError();
+ qCritical()
+ << "can't get file time for '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+ } else {
+ ft.dwHighDateTime = fi.dwFileDateMS;
+ ft.dwLowDateTime = fi.dwFileDateLS;
+ }
+
+
+ SYSTEMTIME utc = {};
+ if (!FileTimeToSystemTime(&ft, &utc)) {
+ qCritical()
+ << "FileTimeToSystemTime() failed on timestamp "
+ << "high=0x" << hex << ft.dwHighDateTime << " "
+ << "low=0x" << hex << ft.dwLowDateTime << " for "
+ << "'" << m_path << "'";
+
+ return {};
+ }
+
+ return QDateTime(
+ QDate(utc.wYear, utc.wMonth, utc.wDay),
+ QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
+}
+
} // namespace MOShared
--
cgit v1.3.1
From 7152b8e97b060e34e5a5bfa8f36c94722007d6aa Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 16:13:55 -0400
Subject: lower case paths, sorted list, md5 for some files
---
src/shared/util.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++-------
src/shared/util.h | 4 ++++
2 files changed, 54 insertions(+), 7 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 16b373db..772d3ca5 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -287,7 +287,7 @@ void Environment::getLoadedModules()
{
const auto e = GetLastError();
- qCritical().nospace()
+ qCritical().nospace().noquote()
<< "CreateToolhelp32Snapshot() failed, "
<< formatSystemMessage(e);
@@ -304,7 +304,7 @@ void Environment::getLoadedModules()
{
const auto e = GetLastError();
- qCritical().nospace()
+ qCritical().nospace().noquote()
<< "Module32First() failed, " << formatSystemMessage(e);
return;
@@ -324,12 +324,17 @@ void Environment::getLoadedModules()
const auto e = GetLastError();
if (e != ERROR_NO_MORE_FILES) {
- qCritical() << "Module32Next() failed, " << formatSystemMessage(e);
+ qCritical().nospace().noquote()
+ << "Module32Next() failed, " << formatSystemMessage(e);
}
break;
}
}
+
+ std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
+ return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
+ });
}
@@ -341,6 +346,7 @@ Environment::Module::Module(QString path, std::size_t fileSize)
m_version = getVersion(fi.ffi);
m_timestamp = getTimestamp(fi.ffi);
m_versionString = fi.fileDescription;
+ m_md5 = getMD5();
}
const QString& Environment::Module::path() const
@@ -348,6 +354,11 @@ const QString& Environment::Module::path() const
return m_path;
}
+QString Environment::Module::displayPath() const
+{
+ return QDir::fromNativeSeparators(m_path.toLower());
+}
+
std::size_t Environment::Module::fileSize() const
{
return m_fileSize;
@@ -376,7 +387,7 @@ QString Environment::Module::toString() const
{
QStringList sl;
- sl.push_back(m_path);
+ sl.push_back(displayPath());
sl.push_back(QString("%1 B").arg(m_fileSize));
if (m_version.isEmpty() && m_versionString.isEmpty()) {
@@ -397,6 +408,10 @@ QString Environment::Module::toString() const
sl.push_back("(no timestamp)");
}
+ if (!m_md5.isEmpty()) {
+ sl.push_back(m_md5);
+ }
+
return sl.join(", ");
}
@@ -543,7 +558,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (h.get() == INVALID_HANDLE_VALUE) {
const auto e = GetLastError();
- qCritical()
+ qCritical().nospace().noquote()
<< "can't open file '" << m_path << "' for timestamp, "
<< formatSystemMessage(e);
@@ -552,7 +567,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
const auto e = GetLastError();
- qCritical()
+ qCritical().nospace().noquote()
<< "can't get file time for '" << m_path << "', "
<< formatSystemMessage(e);
@@ -566,7 +581,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
SYSTEMTIME utc = {};
if (!FileTimeToSystemTime(&ft, &utc)) {
- qCritical()
+ qCritical().nospace().noquote()
<< "FileTimeToSystemTime() failed on timestamp "
<< "high=0x" << hex << ft.dwHighDateTime << " "
<< "low=0x" << hex << ft.dwLowDateTime << " for "
@@ -580,4 +595,32 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
}
+QString Environment::Module::getMD5() const
+{
+ if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) {
+ // don't calculate md5 for system files, it's not really relevant and
+ // it takes a while
+ return {};
+ }
+
+ QFile f(m_path);
+
+ if (!f.open(QFile::ReadOnly)) {
+ qCritical().nospace().noquote()
+ << "failed to open file '" << m_path << "' for md5";
+
+ return {};
+ }
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (!hash.addData(&f)) {
+ qCritical().nospace().noquote()
+ << "failed to calculate md5 for '" << m_path << "'";
+
+ return {};
+ }
+
+ return hash.result().toHex();
+}
+
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 5d88481c..46bdea78 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -55,6 +55,8 @@ public:
explicit Module(QString path, std::size_t fileSize);
const QString& path() const;
+ QString displayPath() const;
+
std::size_t fileSize() const;
const QString& version() const;
const QString& versionString() const;
@@ -74,11 +76,13 @@ public:
QString m_version;
QDateTime m_timestamp;
QString m_versionString;
+ QString m_md5;
FileInfo getFileInfo() const;
QString getVersion(const VS_FIXEDFILEINFO& fi) const;
QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+ QString getMD5() const;
VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
QString getFileDescription(std::byte* buffer) const;
--
cgit v1.3.1
From fb6512b72ebf86d5273744774388deb14c8a21ae Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 18:16:00 -0400
Subject: log windows version
---
src/main.cpp | 6 +-
src/shared/util.cpp | 211 ++++++++++++++++++++++++++++++++++++++++++++++++----
src/shared/util.h | 94 +++++++++++++++--------
3 files changed, 264 insertions(+), 47 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index e1a6b853..75148c4b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -467,8 +467,12 @@ int runApplication(MOApplication &application, SingleInstance &instance,
#endif
{
- Environment env;
+ env::Environment env;
+ qInfo().nospace().noquote()
+ << "windows: " << env.windowsVersion().toString();
+
+ qInfo() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qInfo().nospace().noquote() << " . " << m.toString();
}
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 772d3ca5..9337ebf6 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -256,9 +256,13 @@ MOBase::VersionInfo createVersionInfo()
}
+namespace env
+{
+
struct HandleCloser
{
using pointer = HANDLE;
+
void operator()(HANDLE h)
{
if (h != INVALID_HANDLE_VALUE) {
@@ -267,17 +271,34 @@ struct HandleCloser
}
};
+struct LibraryFreer
+{
+ using pointer = HINSTANCE;
+
+ void operator()(HINSTANCE h)
+ {
+ if (h != 0) {
+ ::FreeLibrary(h);
+ }
+ }
+};
+
Environment::Environment()
{
getLoadedModules();
}
-const std::vector& Environment::loadedModules()
+const std::vector& Environment::loadedModules()
{
return m_modules;
}
+const WindowsVersion& Environment::windowsVersion() const
+{
+ return m_windows;
+}
+
void Environment::getLoadedModules()
{
std::unique_ptr snapshot(CreateToolhelp32Snapshot(
@@ -312,7 +333,6 @@ void Environment::getLoadedModules()
// Now walk the module list of the process,
// and display information about each module
- qInfo() << "modules loaded in process:";
for (;;)
{
@@ -338,7 +358,7 @@ void Environment::getLoadedModules()
}
-Environment::Module::Module(QString path, std::size_t fileSize)
+Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
{
const auto fi = getFileInfo();
@@ -349,32 +369,32 @@ Environment::Module::Module(QString path, std::size_t fileSize)
m_md5 = getMD5();
}
-const QString& Environment::Module::path() const
+const QString& Module::path() const
{
return m_path;
}
-QString Environment::Module::displayPath() const
+QString Module::displayPath() const
{
return QDir::fromNativeSeparators(m_path.toLower());
}
-std::size_t Environment::Module::fileSize() const
+std::size_t Module::fileSize() const
{
return m_fileSize;
}
-const QString& Environment::Module::version() const
+const QString& Module::version() const
{
return m_version;
}
-const QString& Environment::Module::versionString() const
+const QString& Module::versionString() const
{
return m_versionString;
}
-QString Environment::Module::timestampString() const
+QString Module::timestampString() const
{
if (!m_timestamp.isValid()) {
return "(no timestamp)";
@@ -383,7 +403,7 @@ QString Environment::Module::timestampString() const
return m_timestamp.toString(Qt::DateFormat::ISODate);
}
-QString Environment::Module::toString() const
+QString Module::toString() const
{
QStringList sl;
@@ -415,7 +435,7 @@ QString Environment::Module::toString() const
return sl.join(", ");
}
-Environment::Module::FileInfo Environment::Module::getFileInfo() const
+Module::FileInfo Module::getFileInfo() const
{
const auto wspath = m_path.toStdWString();
@@ -457,7 +477,7 @@ Environment::Module::FileInfo Environment::Module::getFileInfo() const
return fi;
}
-VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
+VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
{
void* valuePointer = nullptr;
unsigned int valueSize = 0;
@@ -482,7 +502,7 @@ VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
return *fi;
}
-QString Environment::Module::getFileDescription(std::byte* buffer) const
+QString Module::getFileDescription(std::byte* buffer) const
{
struct LANGANDCODEPAGE
{
@@ -527,7 +547,7 @@ QString Environment::Module::getFileDescription(std::byte* buffer) const
reinterpret_cast(valuePointer), valueSize - 1);
}
-QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
+QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const
{
if (fi.dwSignature == 0) {
return {};
@@ -546,7 +566,7 @@ QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
.arg(major).arg(minor).arg(maintenance).arg(build);
}
-QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
+QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
{
FILETIME ft = {};
@@ -595,7 +615,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
}
-QString Environment::Module::getMD5() const
+QString Module::getMD5() const
{
if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) {
// don't calculate md5 for system files, it's not really relevant and
@@ -623,4 +643,163 @@ QString Environment::Module::getMD5() const
return hash.result().toHex();
}
+
+WindowsVersion::WindowsVersion() :
+ m_realMajor(0), m_realMinor(0), m_realBuild(0),
+ m_major(0), m_minor(0), m_build(0), m_UBR(0)
+{
+ getVersion();
+ getRelease();
+ getElevated();
+}
+
+QString WindowsVersion::toString() const
+{
+ QStringList sl;
+
+ const QString version = QString("%1.%2.%3")
+ .arg(m_major).arg(m_minor).arg(m_build);
+
+ const QString realVersion = QString("%1.%2.%3")
+ .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild);
+
+ sl.push_back("version: " + version);
+
+ if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) {
+ sl.push_back("real version: " + realVersion);
+ }
+
+ if (!m_buildLab.isEmpty()) {
+ sl.push_back(m_buildLab);
+ }
+
+ if (!m_productName.isEmpty()) {
+ sl.push_back(m_productName);
+ }
+
+ if (!m_releaseID.isEmpty()) {
+ sl.push_back("build " + m_releaseID);
+ }
+
+ if (m_UBR != 0) {
+ sl.push_back(QString("%1").arg(m_UBR));
+ }
+
+ QString elevated = "?";
+ if (m_elevated.has_value()) {
+ elevated = (*m_elevated ? "yes" : "no");
+ }
+
+ sl.push_back("elevated: " + elevated);
+
+ return sl.join(", ");
+}
+
+void WindowsVersion::getVersion()
+{
+ std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
+
+ if (!ntdll) {
+ qCritical() << "failed to load ntdll.dll while getting version";
+ return;
+ }
+
+ getRealVersion(ntdll.get());
+ getReportedVersion(ntdll.get());
+}
+
+void WindowsVersion::getRealVersion(HINSTANCE ntdll)
+{
+ using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
+
+ auto* RtlGetNtVersionNumbers = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+
+ if (RtlGetNtVersionNumbers) {
+ DWORD build = 0;
+ RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build);
+
+ m_realBuild = 0x0fffffff & build;
+ }
+}
+
+void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
+{
+ using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW);
+
+ auto* RtlGetVersion = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetVersion"));
+
+ if (!RtlGetVersion) {
+ qCritical() << "RtlGetVersion() not found in ntdll.dll";
+ return;
+ }
+
+ OSVERSIONINFOEX vi = {};
+ vi.dwOSVersionInfoSize = sizeof(vi);
+
+ RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
+
+ m_major = vi.dwMajorVersion;
+ m_minor = vi.dwMinorVersion;
+ m_build = vi.dwBuildNumber;
+}
+
+void WindowsVersion::getRelease()
+{
+ QSettings settings(
+ R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
+ QSettings::NativeFormat);
+
+ m_buildLab = settings.value("BuildLabEx", "").toString();
+ if (m_buildLab.isEmpty()) {
+ m_buildLab = settings.value("BuildLab", "").toString();
+ if (m_buildLab.isEmpty()) {
+ m_buildLab = settings.value("BuildBranch", "").toString();
+ }
+ }
+
+ m_productName = settings.value("ProductName", "").toString();
+ m_releaseID = settings.value("ReleaseId", "").toString();
+ m_UBR = settings.value("UBR", 0).toUInt();
+}
+
+void WindowsVersion::getElevated()
+{
+ std::unique_ptr token;
+
+ {
+ HANDLE rawToken = 0;
+
+ if (!OpenProcessToken(GetCurrentProcess( ), TOKEN_QUERY, &rawToken)) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "while trying to check if process is elevated, "
+ << "OpenProcessToken() failed: " << formatSystemMessage(e);
+
+ return;
+ }
+
+ token.reset(rawToken);
+ }
+
+ TOKEN_ELEVATION e = {};
+ DWORD size = sizeof(TOKEN_ELEVATION);
+
+ if (!GetTokenInformation(token.get(), TokenElevation, &e, sizeof(e), &size)) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "while trying to check if process is elevated, "
+ << "GetTokenInformation() failed: " << formatSystemMessage(e);
+
+ return;
+ }
+
+ m_elevated = (e.TokenIsElevated != 0);
+}
+
+} // namespace env
+
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 46bdea78..deaf6fcc 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -22,6 +22,8 @@ along with Mod Organizer. If not, see .
#include
+#include
+
#define WIN32_LEAN_AND_MEAN
#include
@@ -46,58 +48,90 @@ std::wstring ToLower(const std::wstring &text);
bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
-class Environment
+
+namespace env
+{
+
+class Module
{
public:
- class Module
+ explicit Module(QString path, std::size_t fileSize);
+
+ const QString& path() const;
+ QString displayPath() const;
+
+ std::size_t fileSize() const;
+ const QString& version() const;
+ const QString& versionString() const;
+ QString timestampString() const;
+
+ QString toString() const;
+
+private:
+ struct FileInfo
{
- public:
- explicit Module(QString path, std::size_t fileSize);
+ VS_FIXEDFILEINFO ffi;
+ QString fileDescription;
+ };
- const QString& path() const;
- QString displayPath() const;
+ QString m_path;
+ std::size_t m_fileSize;
+ QString m_version;
+ QDateTime m_timestamp;
+ QString m_versionString;
+ QString m_md5;
- std::size_t fileSize() const;
- const QString& version() const;
- const QString& versionString() const;
- QString timestampString() const;
+ FileInfo getFileInfo() const;
- QString toString() const;
+ QString getVersion(const VS_FIXEDFILEINFO& fi) const;
+ QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+ QString getMD5() const;
- private:
- struct FileInfo
- {
- VS_FIXEDFILEINFO ffi;
- QString fileDescription;
- };
+ VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
+ QString getFileDescription(std::byte* buffer) const;
+};
- QString m_path;
- std::size_t m_fileSize;
- QString m_version;
- QDateTime m_timestamp;
- QString m_versionString;
- QString m_md5;
- FileInfo getFileInfo() const;
+class WindowsVersion
+{
+public:
+ WindowsVersion();
- QString getVersion(const VS_FIXEDFILEINFO& fi) const;
- QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
- QString getMD5() const;
+ QString toString() const;
+
+private:
+ DWORD m_realMajor, m_realMinor, m_realBuild;
+ DWORD m_major, m_minor, m_build;
+ QString m_buildLab, m_productName, m_releaseID;
+ DWORD m_UBR;
+ std::optional m_elevated;
+
+ void getVersion();
+ void getRealVersion(HINSTANCE ntdll);
+ void getReportedVersion(HINSTANCE ntdll);
+ void getRelease();
+ void getElevated();
+};
- VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
- QString getFileDescription(std::byte* buffer) const;
- };
+class Environment
+{
+public:
Environment();
const std::vector& loadedModules();
+ const WindowsVersion& windowsVersion() const;
private:
std::vector m_modules;
+ WindowsVersion m_windows;
void getLoadedModules();
};
+} // namespace env
+
+
MOBase::VersionInfo createVersionInfo();
} // namespace MOShared
--
cgit v1.3.1
From 813ab32a2d16106f2799fba4e85c768a5b1c4850 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 18:58:15 -0400
Subject: added a warning when running in compatibility mode
---
src/main.cpp | 6 ++++--
src/shared/util.cpp | 14 +++++++++++++-
src/shared/util.h | 1 +
3 files changed, 18 insertions(+), 3 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index 75148c4b..e43ec6d0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -472,12 +472,14 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qInfo().nospace().noquote()
<< "windows: " << env.windowsVersion().toString();
+ if (env.windowsVersion().compatibilityMode()) {
+ qWarning() << "MO seems to be running in compatibility mode";
+ }
+
qInfo() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qInfo().nospace().noquote() << " . " << m.toString();
}
-
- return 0;
}
QString dataPath = application.property("dataPath").toString();
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 9337ebf6..33094eff 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -653,6 +653,18 @@ WindowsVersion::WindowsVersion() :
getElevated();
}
+bool WindowsVersion::compatibilityMode() const
+{
+ if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) {
+ return false;
+ }
+
+ return
+ m_realMajor != m_major ||
+ m_realMinor != m_minor ||
+ m_realBuild != m_build;
+}
+
QString WindowsVersion::toString() const
{
QStringList sl;
@@ -665,7 +677,7 @@ QString WindowsVersion::toString() const
sl.push_back("version: " + version);
- if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) {
+ if (compatibilityMode()) {
sl.push_back("real version: " + realVersion);
}
diff --git a/src/shared/util.h b/src/shared/util.h
index deaf6fcc..232a97bb 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -97,6 +97,7 @@ class WindowsVersion
public:
WindowsVersion();
+ bool compatibilityMode() const;
QString toString() const;
private:
--
cgit v1.3.1
From 1aa174f1438d7b1c1a9dab47d69e912726578a06 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 20:02:10 -0400
Subject: comments, some refactoring switched to qDebug()
---
src/main.cpp | 10 +-
src/shared/util.cpp | 259 +++++++++++++++++++++++++++++++++++-----------------
src/shared/util.h | 159 +++++++++++++++++++++++++++++---
3 files changed, 326 insertions(+), 102 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index e43ec6d0..6c3e40be 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -469,16 +469,16 @@ int runApplication(MOApplication &application, SingleInstance &instance,
{
env::Environment env;
- qInfo().nospace().noquote()
- << "windows: " << env.windowsVersion().toString();
+ qDebug().nospace().noquote()
+ << "windows: " << env.windowsInfo().toString();
- if (env.windowsVersion().compatibilityMode()) {
+ if (env.windowsInfo().compatibilityMode()) {
qWarning() << "MO seems to be running in compatibility mode";
}
- qInfo() << "modules loaded in process:";
+ qDebug() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
- qInfo().nospace().noquote() << " . " << m.toString();
+ qDebug().nospace().noquote() << " . " << m.toString();
}
}
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 33094eff..70adb791 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -294,7 +294,7 @@ const std::vector& Environment::loadedModules()
return m_modules;
}
-const WindowsVersion& Environment::windowsVersion() const
+const WindowsInfo& Environment::windowsInfo() const
{
return m_windows;
}
@@ -315,12 +315,10 @@ void Environment::getLoadedModules()
return;
}
- // Set the size of the structure before using it.
MODULEENTRY32 me = {};
me.dwSize = sizeof(me);
- // Retrieve information about the first module,
- // and exit if unsuccessful
+ // first module, this shouldn't fail because there's at least the executable
if (!Module32First(snapshot.get(), &me))
{
const auto e = GetLastError();
@@ -331,27 +329,29 @@ void Environment::getLoadedModules()
return;
}
- // Now walk the module list of the process,
- // and display information about each module
-
for (;;)
{
const auto path = QString::fromWCharArray(me.szExePath);
m_modules.push_back(Module(path, me.modBaseSize));
+ // next module
if (!Module32Next(snapshot.get(), &me)) {
const auto e = GetLastError();
- if (e != ERROR_NO_MORE_FILES) {
- qCritical().nospace().noquote()
- << "Module32Next() failed, " << formatSystemMessage(e);
+ if (e == ERROR_NO_MORE_FILES) {
+ // not an error
+ break;
}
+ qCritical().nospace().noquote()
+ << "Module32Next() failed, " << formatSystemMessage(e);
+
break;
}
}
+ // sorting by display name
std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
});
@@ -394,6 +394,16 @@ const QString& Module::versionString() const
return m_versionString;
}
+const QDateTime& Module::timestamp() const
+{
+ return m_timestamp;
+}
+
+const QString& Module::md5() const
+{
+ return m_md5;
+}
+
QString Module::timestampString() const
{
if (!m_timestamp.isValid()) {
@@ -407,9 +417,11 @@ QString Module::toString() const
{
QStringList sl;
+ // file size
sl.push_back(displayPath());
sl.push_back(QString("%1 B").arg(m_fileSize));
+ // version
if (m_version.isEmpty() && m_versionString.isEmpty()) {
sl.push_back("(no version)");
} else {
@@ -417,17 +429,19 @@ QString Module::toString() const
sl.push_back(m_version);
}
- if (m_versionString != m_version) {
+ if (!m_versionString.isEmpty() && m_versionString != m_version) {
sl.push_back(versionString());
}
}
+ // timestamp
if (m_timestamp.isValid()) {
sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate));
} else {
sl.push_back("(no timestamp)");
}
+ // md5
if (!m_md5.isEmpty()) {
sl.push_back(m_md5);
}
@@ -439,6 +453,7 @@ Module::FileInfo Module::getFileInfo() const
{
const auto wspath = m_path.toStdWString();
+ // getting version info size
DWORD dummy = 0;
const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy);
@@ -457,6 +472,7 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
+ // getting version info
auto buffer = std::make_unique(size);
if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) {
@@ -469,6 +485,8 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
+ // the version info has two major parts: a fixed version and a localizable
+ // set of strings
FileInfo fi;
fi.ffi = getFixedFileInfo(buffer.get());
@@ -482,6 +500,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
void* valuePointer = nullptr;
unsigned int valueSize = 0;
+ // the fixed version info is in the root
const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize);
if (!ret || !valuePointer || valueSize == 0) {
@@ -491,6 +510,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
const auto* fi = reinterpret_cast(valuePointer);
+ // signature is always 0xfeef04bd
if (fi->dwSignature != 0xfeef04bd) {
qCritical().nospace().noquote()
<< "bad file info signature 0x" << hex << fi->dwSignature << " for "
@@ -513,6 +533,7 @@ QString Module::getFileDescription(std::byte* buffer) const
void* valuePointer = nullptr;
unsigned int valueSize = 0;
+ // getting list of available languages
auto ret = VerQueryValueW(
buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize);
@@ -523,11 +544,13 @@ QString Module::getFileDescription(std::byte* buffer) const
return {};
}
+ // number of languages
const auto count = valueSize / sizeof(LANGANDCODEPAGE);
if (count == 0) {
return {};
}
+ // using the first language in the list to get FileVersion
const auto* lcp = reinterpret_cast(valuePointer);
const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
@@ -571,6 +594,10 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
FILETIME ft = {};
if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) {
+ // if the file info is invalid or doesn't have a date, use the creation
+ // time on the file
+
+ // opening the file
std::unique_ptr h(CreateFileW(
m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
@@ -585,6 +612,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
return {};
}
+ // getting the file time
if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
const auto e = GetLastError();
qCritical().nospace().noquote()
@@ -594,12 +622,15 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
return {};
}
} else {
+ // use the time from the file info
ft.dwHighDateTime = fi.dwFileDateMS;
ft.dwLowDateTime = fi.dwFileDateLS;
}
+ // converting to SYSTEMTIME
SYSTEMTIME utc = {};
+
if (!FileTimeToSystemTime(&ft, &utc)) {
qCritical().nospace().noquote()
<< "FileTimeToSystemTime() failed on timestamp "
@@ -623,6 +654,7 @@ QString Module::getMD5() const
return {};
}
+ // opening the file
QFile f(m_path);
if (!f.open(QFile::ReadOnly)) {
@@ -632,6 +664,7 @@ QString Module::getMD5() const
return {};
}
+ // hashing
QCryptographicHash hash(QCryptographicHash::Md5);
if (!hash.addData(&f)) {
qCritical().nospace().noquote()
@@ -644,59 +677,97 @@ QString Module::getMD5() const
}
-WindowsVersion::WindowsVersion() :
- m_realMajor(0), m_realMinor(0), m_realBuild(0),
- m_major(0), m_minor(0), m_build(0), m_UBR(0)
+WindowsInfo::WindowsInfo()
{
- getVersion();
- getRelease();
- getElevated();
+ // loading ntdll.dll, the functions will be found with GetProcAddress()
+ std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
+
+ if (!ntdll) {
+ qCritical() << "failed to load ntdll.dll while getting version";
+ return;
+ } else {
+ m_reported = getReportedVersion(ntdll.get());
+ m_real = getRealVersion(ntdll.get());
+ }
+
+ m_release = getRelease();
+ m_elevated = getElevated();
}
-bool WindowsVersion::compatibilityMode() const
+bool WindowsInfo::compatibilityMode() const
{
- if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) {
+ if (m_real == Version()) {
+ // don't know the real version, can't guess compatibility mode
return false;
}
- return
- m_realMajor != m_major ||
- m_realMinor != m_minor ||
- m_realBuild != m_build;
+ return (m_real != m_reported);
}
-QString WindowsVersion::toString() const
+const WindowsInfo::Version& WindowsInfo::reportedVersion() const
{
- QStringList sl;
+ return m_reported;
+}
+
+const WindowsInfo::Version& WindowsInfo::realVersion() const
+{
+ return m_real;
+}
+
+const WindowsInfo::Release& WindowsInfo::release() const
+{
+ return m_release;
+}
+
+std::optional WindowsInfo::isElevated() const
+{
+ return m_elevated;
+}
- const QString version = QString("%1.%2.%3")
- .arg(m_major).arg(m_minor).arg(m_build);
+QString WindowsInfo::toString() const
+{
+ QStringList sl;
- const QString realVersion = QString("%1.%2.%3")
- .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild);
+ const QString reported = m_reported.toString();
+ const QString real = m_real.toString();
- sl.push_back("version: " + version);
+ // version
+ sl.push_back("version: " + reported);
+ // real version if different
if (compatibilityMode()) {
- sl.push_back("real version: " + realVersion);
+ sl.push_back("real version: " + real);
}
- if (!m_buildLab.isEmpty()) {
- sl.push_back(m_buildLab);
+ // build.UBR, such as 17763.557
+ if (m_release.UBR != 0) {
+ DWORD build = 0;
+
+ if (compatibilityMode()) {
+ build = m_real.build;
+ } else {
+ build = m_reported.build;
+ }
+
+ sl.push_back(QString("%1.%2").arg(build).arg(m_release.UBR));
}
- if (!m_productName.isEmpty()) {
- sl.push_back(m_productName);
+ // release ID
+ if (!m_release.ID.isEmpty()) {
+ sl.push_back("release " + m_release.ID);
}
- if (!m_releaseID.isEmpty()) {
- sl.push_back("build " + m_releaseID);
+ // buildlab string
+ if (!m_release.buildLab.isEmpty()) {
+ sl.push_back(m_release.buildLab);
}
- if (m_UBR != 0) {
- sl.push_back(QString("%1").arg(m_UBR));
+ // product name
+ if (!m_release.productName.isEmpty()) {
+ sl.push_back(m_release.productName);
}
+ // elevated
QString elevated = "?";
if (m_elevated.has_value()) {
elevated = (*m_elevated ? "yes" : "no");
@@ -707,36 +778,14 @@ QString WindowsVersion::toString() const
return sl.join(", ");
}
-void WindowsVersion::getVersion()
-{
- std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
-
- if (!ntdll) {
- qCritical() << "failed to load ntdll.dll while getting version";
- return;
- }
-
- getRealVersion(ntdll.get());
- getReportedVersion(ntdll.get());
-}
-
-void WindowsVersion::getRealVersion(HINSTANCE ntdll)
+WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const
{
- using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
-
- auto* RtlGetNtVersionNumbers = reinterpret_cast(
- GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+ // windows has been deprecating pretty much all the functions having to do
+ // with getting version information because apparently, people keep misusing
+ // them for feature detection
+ //
+ // there's still RtlGetVersion() though
- if (RtlGetNtVersionNumbers) {
- DWORD build = 0;
- RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build);
-
- m_realBuild = 0x0fffffff & build;
- }
-}
-
-void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
-{
using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW);
auto* RtlGetVersion = reinterpret_cast(
@@ -744,39 +793,81 @@ void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
if (!RtlGetVersion) {
qCritical() << "RtlGetVersion() not found in ntdll.dll";
- return;
+ return {};
}
OSVERSIONINFOEX vi = {};
vi.dwOSVersionInfoSize = sizeof(vi);
+ // this apparently never fails
RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
- m_major = vi.dwMajorVersion;
- m_minor = vi.dwMinorVersion;
- m_build = vi.dwBuildNumber;
+ return {vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber};
+}
+
+WindowsInfo::Version WindowsInfo::getRealVersion(HINSTANCE ntdll) const
+{
+ // getting the actual windows version is more difficult because all the
+ // functions are lying when running in compatibility mode
+ //
+ // RtlGetNtVersionNumbers() is an undocumented function that seems to work
+ // fine, but it might not in the future
+
+ using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
+
+ auto* RtlGetNtVersionNumbers = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+
+ if (!RtlGetNtVersionNumbers) {
+ qCritical() << "RtlGetNtVersionNumbers not found in ntdll.dll";
+ return {};
+ }
+
+ DWORD major=0, minor=0, build=0;
+ RtlGetNtVersionNumbers(&major, &minor, &build);
+
+ // for whatever reason, the build number has 0xf0000000 set
+ build = 0x0fffffff & build;
+
+ return {major, minor, build};
}
-void WindowsVersion::getRelease()
+WindowsInfo::Release WindowsInfo::getRelease() const
{
+ // there are several interesting items in the registry, but most of them
+ // are undocumented, not always available, and localizable
+ //
+ // most of them are used to provide as much information as possible in case
+ // any of the other versions fail to work
+
QSettings settings(
R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
QSettings::NativeFormat);
- m_buildLab = settings.value("BuildLabEx", "").toString();
- if (m_buildLab.isEmpty()) {
- m_buildLab = settings.value("BuildLab", "").toString();
- if (m_buildLab.isEmpty()) {
- m_buildLab = settings.value("BuildBranch", "").toString();
+ Release r;
+
+ // buildlab seems to be an internal name from the build system
+ r.buildLab = settings.value("BuildLabEx", "").toString();
+ if (r.buildLab.isEmpty()) {
+ r.buildLab = settings.value("BuildLab", "").toString();
+ if (r.buildLab.isEmpty()) {
+ r.buildLab = settings.value("BuildBranch", "").toString();
}
}
- m_productName = settings.value("ProductName", "").toString();
- m_releaseID = settings.value("ReleaseId", "").toString();
- m_UBR = settings.value("UBR", 0).toUInt();
+ // localized name of windows, such as "Windows 10 Pro"
+ r.productName = settings.value("ProductName", "").toString();
+
+ // release ID, such as 1803
+ r.ID = settings.value("ReleaseId", "").toString();
+
+ // some other build number, shown in winver.exe
+ r.UBR = settings.value("UBR", 0).toUInt();
+
+ return r;
}
-void WindowsVersion::getElevated()
+std::optional WindowsInfo::getElevated() const
{
std::unique_ptr token;
@@ -790,7 +881,7 @@ void WindowsVersion::getElevated()
<< "while trying to check if process is elevated, "
<< "OpenProcessToken() failed: " << formatSystemMessage(e);
- return;
+ return {};
}
token.reset(rawToken);
@@ -806,10 +897,10 @@ void WindowsVersion::getElevated()
<< "while trying to check if process is elevated, "
<< "GetTokenInformation() failed: " << formatSystemMessage(e);
- return;
+ return {};
}
- m_elevated = (e.TokenIsElevated != 0);
+ return (e.TokenIsElevated != 0);
}
} // namespace env
diff --git a/src/shared/util.h b/src/shared/util.h
index 232a97bb..3ec677f4 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -52,22 +52,55 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
namespace env
{
+// represents one module
+//
class Module
{
public:
explicit Module(QString path, std::size_t fileSize);
+ // returns the module's path
+ //
const QString& path() const;
+
+ // returns the module's path in lowercase and using forward slashes
+ //
QString displayPath() const;
+ // returns the size in bytes, may be 0
+ //
std::size_t fileSize() const;
+
+ // returns the x.x.x.x version embedded from the version info, may be empty
+ //
const QString& version() const;
+
+ // returns the FileVersion entry from the resource file, returns
+ // "(no version)" if not available
+ //
const QString& versionString() const;
+
+ // returns the build date from the version info, or the creation time of the
+ // file on the filesystem, may be empty
+ //
+ const QDateTime& timestamp() const;
+
+ // returns the md5 of the file, may be empty for system files
+ //
+ const QString& md5() const;
+
+ // converts timestamp() to a string for display, returns "(no timestamp)" if
+ // not available
+ //
QString timestampString() const;
+ // returns a string with all the above information on one line
+ //
QString toString() const;
private:
+ // contains the information from the version resource
+ //
struct FileInfo
{
VS_FIXEDFILEINFO ffi;
@@ -81,51 +114,151 @@ private:
QString m_versionString;
QString m_md5;
+ // returns information from the version resource
+ //
FileInfo getFileInfo() const;
+ // uses VS_FIXEDFILEINFO to build the version string
+ //
QString getVersion(const VS_FIXEDFILEINFO& fi) const;
+
+ // uses the file date from VS_FIXEDFILEINFO if available, or gets the
+ // creation date on the file
+ //
QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+
+ // returns the md5 hash unless the path contains "\windows\"
+ //
QString getMD5() const;
+ // gets VS_FIXEDFILEINFO from the file version info buffer
+ //
VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
+
+ // gets FileVersion from the file version info buffer
+ //
QString getFileDescription(std::byte* buffer) const;
};
-class WindowsVersion
+// a variety of information on windows
+//
+class WindowsInfo
{
public:
- WindowsVersion();
+ struct Version
+ {
+ DWORD major=0, minor=0, build=0;
+
+ QString toString() const
+ {
+ return QString("%1.%2.%3").arg(major).arg(minor).arg(build);
+ }
+
+ friend bool operator==(const Version& a, const Version& b)
+ {
+ return
+ a.major == b.major &&
+ a.minor == b.minor &&
+ a.build == b.build;
+ }
+
+ friend bool operator!=(const Version& a, const Version& b)
+ {
+ return !(a == b);
+ }
+ };
+
+ struct Release
+ {
+ // the BuildLab entry from the registry, may be empty
+ QString buildLab;
+
+ // product name such as "Windows 10 Pro", may not be in English, may be
+ // empty
+ QString productName;
+
+ // release ID such as 1809, may be mepty
+ QString ID;
+ // some sub-build number, undocumented, may be empty
+ DWORD UBR;
+
+ Release()
+ : UBR(0)
+ {
+ }
+ };
+
+
+ WindowsInfo();
+
+ // tries to guess whether this process is running in compatibility mode
+ //
bool compatibilityMode() const;
+
+ // returns the Windows version, may not correspond to the actual version
+ // if the process is running in compatibility mode
+ //
+ const Version& reportedVersion() const;
+
+ // tries to guess the real Windows version that's running, can be empty
+ //
+ const Version& realVersion() const;
+
+ // various information about the current release
+ //
+ const Release& release() const;
+
+ // whether this process is running as administrator, may be empty if the
+ // information is not available
+ std::optional isElevated() const;
+
+ // returns a string with all the above information on one line
+ //
QString toString() const;
private:
- DWORD m_realMajor, m_realMinor, m_realBuild;
- DWORD m_major, m_minor, m_build;
- QString m_buildLab, m_productName, m_releaseID;
- DWORD m_UBR;
+ Version m_reported, m_real;
+ Release m_release;
std::optional m_elevated;
- void getVersion();
- void getRealVersion(HINSTANCE ntdll);
- void getReportedVersion(HINSTANCE ntdll);
- void getRelease();
- void getElevated();
+ // uses RtlGetVersion() to get the version number as reported by Windows
+ //
+ Version getReportedVersion(HINSTANCE ntdll) const;
+
+ // uses RtlGetNtVersionNumbers() to get the real version number
+ //
+ Version getRealVersion(HINSTANCE ntdll) const;
+
+ // gets various information from the registry
+ //
+ Release getRelease() const;
+
+ // gets whether the process is elevated
+ //
+ std::optional getElevated() const;
};
+// represents the process's environment
+//
class Environment
{
public:
Environment();
+ // list of loaded modules in the current process
+ //
const std::vector& loadedModules();
- const WindowsVersion& windowsVersion() const;
+
+ // information about the operating system
+ //
+ const WindowsInfo& windowsInfo() const;
private:
std::vector m_modules;
- WindowsVersion m_windows;
+ WindowsInfo m_windows;
void getLoadedModules();
};
--
cgit v1.3.1
From 5a74b02442302fb484b1db68cf0ce1736af4911c Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 03:41:32 -0400
Subject: security products
---
src/main.cpp | 7 ++
src/shared/util.cpp | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++-
src/shared/util.h | 21 ++++
3 files changed, 308 insertions(+), 3 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index 6c3e40be..518d31a0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -476,10 +476,17 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qWarning() << "MO seems to be running in compatibility mode";
}
+ qDebug().nospace().noquote() << "security features:";
+ for (const auto& sf : env.securityFeatures()) {
+ qDebug().nospace().noquote() << " . " << sf.toString();
+ }
+
qDebug() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qDebug().nospace().noquote() << " . " << m.toString();
}
+
+ return 0;
}
QString dataPath = application.property("dataPath").toString();
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 70adb791..aa2e8d0f 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -30,6 +30,11 @@ along with Mod Organizer. If not, see .
#include
#include
+#include
+#include
+#include
+#pragma comment(lib, "Wbemuuid.lib")
+
using MOBase::formatSystemMessage;
namespace MOShared {
@@ -283,10 +288,153 @@ struct LibraryFreer
}
};
+struct COMReleaser
+{
+ void operator()(IUnknown* p)
+ {
+ if (p) {
+ p->Release();
+ }
+ }
+};
+
+
+class WMI
+{
+public:
+ class failed {};
+
+ WMI(const std::string& ns)
+ {
+ try
+ {
+ createLocator();
+ createService(ns);
+ setSecurity();
+ }
+ catch(failed&)
+ {
+ }
+ }
+
+ template
+ void query(const std::string& q, F&& f)
+ {
+ if (!m_locator || !m_service) {
+ return;
+ }
+
+ auto enumerator = getEnumerator(q);
+
+ for (;;)
+ {
+ std::unique_ptr object;
+
+ {
+ IWbemClassObject* rawObject = nullptr;
+ ULONG count = 0;
+ auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
+
+ if (count == 0) {
+ break;
+ }
+
+ object.reset(rawObject);
+ }
+
+ f(object.get());
+ }
+ }
+
+ std::unique_ptr getEnumerator(
+ const std::string& query)
+ {
+ IEnumWbemClassObject* rawEnumerator = NULL;
+
+ auto ret = m_service->ExecQuery(
+ bstr_t("WQL"),
+ bstr_t(query.c_str()),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL,
+ &rawEnumerator);
+
+ if (FAILED(ret))
+ {
+ qCritical()
+ << "query '" << QString::fromStdString(query) << "' failed, "
+ << formatSystemMessage(ret);
+
+ return {};
+ }
+
+ return std::unique_ptr(rawEnumerator);
+ }
+
+private:
+ std::unique_ptr m_locator;
+ std::unique_ptr m_service;
+
+ void createLocator()
+ {
+ void* rawLocator = nullptr;
+
+ const auto ret = CoCreateInstance(
+ CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator, &rawLocator);
+
+ if (FAILED(ret)) {
+ qCritical()
+ << "CoCreateInstance for WbemLocator failed, "
+ << formatSystemMessage(ret);
+
+ throw failed();
+ }
+
+ m_locator.reset(static_cast(rawLocator));
+ }
+
+ void createService(const std::string& ns)
+ {
+ IWbemServices* rawService = nullptr;
+
+ const auto res = m_locator->ConnectServer(
+ _bstr_t(ns.c_str()),
+ nullptr, nullptr, nullptr, 0, nullptr, nullptr,
+ &rawService);
+
+ if (FAILED(res)) {
+ qCritical()
+ << "locator->ConnectServer() failed for namespace "
+ << "'" << QString::fromStdString(ns) << "', "
+ << formatSystemMessage(res);
+
+ throw failed();
+ }
+
+ m_service.reset(rawService);
+ }
+
+ void setSecurity()
+ {
+ auto ret = CoSetProxyBlanket(
+ m_service.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
+ RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE);
+
+ if (FAILED(ret))
+ {
+ qCritical()
+ << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret);
+
+ throw failed();
+ }
+ }
+};
+
Environment::Environment()
{
getLoadedModules();
+ getSecurityFeatures();
}
const std::vector& Environment::loadedModules()
@@ -299,6 +447,11 @@ const WindowsInfo& Environment::windowsInfo() const
return m_windows;
}
+const std::vector& Environment::securityFeatures() const
+{
+ return m_security;
+}
+
void Environment::getLoadedModules()
{
std::unique_ptr snapshot(CreateToolhelp32Snapshot(
@@ -357,6 +510,59 @@ void Environment::getLoadedModules()
});
}
+void Environment::getSecurityFeatures()
+{
+ WMI wmi("root\\SecurityCenter2");
+ std::map map;
+
+ auto handleProduct = [&](auto* o) {
+ VARIANT prop;
+
+ auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get displayName, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const std::wstring name = prop.bstrVal;
+ VariantClear(&prop);
+
+ ret = o->Get(L"productState", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get productState, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const DWORD state = prop.ulVal;
+ VariantClear(&prop);
+
+ ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const QUuid guid(QString::fromWCharArray(prop.bstrVal));
+ VariantClear(&prop);
+
+
+ auto itor = map.find(guid);
+
+ if (itor == map.end()) {
+ map.insert({
+ guid, SecurityFeature(QString::fromStdWString(name), state)});
+ }
+ };
+
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+
+ for (auto&& p : map) {
+ m_security.push_back(p.second);
+ }
+}
+
Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
@@ -508,7 +714,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
return {};
}
- const auto* fi = reinterpret_cast(valuePointer);
+ const auto* fi = static_cast(valuePointer);
// signature is always 0xfeef04bd
if (fi->dwSignature != 0xfeef04bd) {
@@ -551,7 +757,7 @@ QString Module::getFileDescription(std::byte* buffer) const
}
// using the first language in the list to get FileVersion
- const auto* lcp = reinterpret_cast(valuePointer);
+ const auto* lcp = static_cast(valuePointer);
const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
.arg(lcp->wLanguage, 4, 16, QChar('0'))
@@ -567,7 +773,7 @@ QString Module::getFileDescription(std::byte* buffer) const
// valueSize includes the null terminator
return QString::fromWCharArray(
- reinterpret_cast(valuePointer), valueSize - 1);
+ static_cast(valuePointer), valueSize - 1);
}
QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const
@@ -903,6 +1109,77 @@ std::optional WindowsInfo::getElevated() const
return (e.TokenIsElevated != 0);
}
+
+SecurityFeature::SecurityFeature(QString name, DWORD state)
+ : m_name(std::move(name)), m_state(state)
+{
+}
+
+const QString& SecurityFeature::name() const
+{
+ return m_name;
+}
+
+QString SecurityFeature::toString() const
+{
+ QString s;
+
+ s += m_name + " ";
+
+ const auto provider = (m_state >> 16) & 0xff;
+ const auto scanner = (m_state >> 8) & 0xff;
+ const auto definitions = m_state & 0xff;
+
+ QStringList ps;
+ if (provider & WSC_SECURITY_PROVIDER_FIREWALL) {
+ ps.push_back("firewall");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
+ ps.push_back("autoupdate");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
+ ps.push_back("antivirus");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
+ ps.push_back("antispyware");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
+ ps.push_back("settings");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
+ ps.push_back("uac");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_SERVICE) {
+ ps.push_back("service");
+ }
+
+ if (ps.empty()) {
+ s += "(doesn't provide anything)";
+ } else {
+ s += "(" + ps.join("|") + ")";
+ }
+
+ if (scanner & 0x10) {
+ s += ", active";
+ } else {
+ s += ", inactive";
+ }
+
+ if (definitions == 0) {
+ s += ", definitions up to date";
+ } else {
+ s += ", definitions outdated";
+ }
+
+ return s;
+}
+
} // namespace env
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 3ec677f4..6b842f8c 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -241,6 +241,21 @@ private:
};
+class SecurityFeature
+{
+public:
+ SecurityFeature(QString name, DWORD state);
+
+ const QString& name() const;
+
+ QString toString() const;
+
+private:
+ QString m_name;
+ DWORD m_state;
+};
+
+
// represents the process's environment
//
class Environment
@@ -256,11 +271,17 @@ public:
//
const WindowsInfo& windowsInfo() const;
+ // information about the installed antivirus
+ //
+ const std::vector& securityFeatures() const;
+
private:
std::vector m_modules;
WindowsInfo m_windows;
+ std::vector m_security;
void getLoadedModules();
+ void getSecurityFeatures();
};
} // namespace env
--
cgit v1.3.1
From dfe8093c1ad16e1611c12e88d52d1ac38371d3f6 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 08:42:58 -0400
Subject: added --crashdump to generate dumps of a running MO process added
dump_running_process.bat to start another instance of MO with that flag
---
CMakeLists.txt | 2 +
dump_running_process.bat | 2 +
src/main.cpp | 45 +++++-
src/settings.cpp | 10 +-
src/shared/util.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++++---
src/shared/util.h | 17 ++
6 files changed, 443 insertions(+), 25 deletions(-)
create mode 100644 dump_running_process.bat
(limited to 'src/shared/util.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ced097e1..94c76373 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,3 +29,5 @@ if(NOT EXISTS ${vcxproj_user_file})
"\n"
"\n")
endif()
+
+INSTALL(FILES dump_running_process.bat DESTINATION bin)
diff --git a/dump_running_process.bat b/dump_running_process.bat
new file mode 100644
index 00000000..4697fe5e
--- /dev/null
+++ b/dump_running_process.bat
@@ -0,0 +1,2 @@
+pushd "%~dp0"
+start ModOrganizer.exe --crashdump
diff --git a/src/main.cpp b/src/main.cpp
index 518d31a0..f74a9cf4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -485,8 +485,6 @@ int runApplication(MOApplication &application, SingleInstance &instance,
for (const auto& m : env.loadedModules()) {
qDebug().nospace().noquote() << " . " << m.toString();
}
-
- return 0;
}
QString dataPath = application.property("dataPath").toString();
@@ -682,9 +680,52 @@ int runApplication(MOApplication &application, SingleInstance &instance,
}
}
+int doCoreDump(env::CoreDumpTypes type)
+{
+ // open a console
+ AllocConsole();
+
+ // redirect stdin, stdout and stderr to it
+ FILE* in=nullptr;
+ FILE* out=nullptr;
+ FILE* err=nullptr;
+ freopen_s(&in, "CONIN$", "r", stdin);
+ freopen_s(&out, "CONOUT$", "w", stdout);
+ freopen_s(&err, "CONOUT$", "w", stderr);
+
+ // dump
+ const auto b = env::coredumpOther(type);
+ if (!b) {
+ std::wcerr << L"\n>>>> a minidump file was not written\n\n";
+ }
+
+ std::wcerr << L"Press enter to continue...";
+ std::wcin.get();
+
+ // close redirected handles
+ std::fclose(err);
+ std::fclose(out);
+ std::fclose(in);
+
+ // close console
+ FreeConsole();
+
+ return (b ? 0 : 1);
+}
int main(int argc, char *argv[])
{
+ // handle --crashdump first
+ for (int i=1; i.
#include
#include
#include
-#include
#include
+#include
+
+#include
#include
#include
@@ -36,6 +38,8 @@ along with Mod Organizer. If not, see .
#pragma comment(lib, "Wbemuuid.lib")
using MOBase::formatSystemMessage;
+using MOBase::formatSystemMessageQ;
+namespace fs = std::filesystem;
namespace MOShared {
@@ -276,6 +280,9 @@ struct HandleCloser
}
};
+using HandlePtr = std::unique_ptr;
+
+
struct LibraryFreer
{
using pointer = HINSTANCE;
@@ -362,7 +369,7 @@ public:
{
qCritical()
<< "query '" << QString::fromStdString(query) << "' failed, "
- << formatSystemMessage(ret);
+ << formatSystemMessageQ(ret);
return {};
}
@@ -385,7 +392,7 @@ private:
if (FAILED(ret)) {
qCritical()
<< "CoCreateInstance for WbemLocator failed, "
- << formatSystemMessage(ret);
+ << formatSystemMessageQ(ret);
throw failed();
}
@@ -406,7 +413,7 @@ private:
qCritical()
<< "locator->ConnectServer() failed for namespace "
<< "'" << QString::fromStdString(ns) << "', "
- << formatSystemMessage(res);
+ << formatSystemMessageQ(res);
throw failed();
}
@@ -423,7 +430,7 @@ private:
if (FAILED(ret))
{
qCritical()
- << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret);
+ << "CoSetProxyBlanket() failed, " << formatSystemMessageQ(ret);
throw failed();
}
@@ -454,7 +461,7 @@ const std::vector& Environment::securityFeatures() const
void Environment::getLoadedModules()
{
- std::unique_ptr snapshot(CreateToolhelp32Snapshot(
+ HandlePtr snapshot(CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
if (snapshot.get() == INVALID_HANDLE_VALUE)
@@ -463,7 +470,7 @@ void Environment::getLoadedModules()
qCritical().nospace().noquote()
<< "CreateToolhelp32Snapshot() failed, "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return;
}
@@ -477,7 +484,7 @@ void Environment::getLoadedModules()
const auto e = GetLastError();
qCritical().nospace().noquote()
- << "Module32First() failed, " << formatSystemMessage(e);
+ << "Module32First() failed, " << formatSystemMessageQ(e);
return;
}
@@ -498,7 +505,7 @@ void Environment::getLoadedModules()
}
qCritical().nospace().noquote()
- << "Module32Next() failed, " << formatSystemMessage(e);
+ << "Module32Next() failed, " << formatSystemMessageQ(e);
break;
}
@@ -520,7 +527,7 @@ void Environment::getSecurityFeatures()
auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get displayName, " << formatSystemMessage(ret);
+ qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret);
return;
}
@@ -529,7 +536,10 @@ void Environment::getSecurityFeatures()
ret = o->Get(L"productState", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get productState, " << formatSystemMessage(ret);
+ qCritical()
+ << "failed to get productState, "
+ << formatSystemMessageQ(ret);
+
return;
}
@@ -538,7 +548,10 @@ void Environment::getSecurityFeatures()
ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret);
+ qCritical()
+ << "failed to get instanceGuid, "
+ << formatSystemMessageQ(ret);
+
return;
}
@@ -673,7 +686,7 @@ Module::FileInfo Module::getFileInfo() const
qCritical().nospace().noquote()
<< "GetFileVersionInfoSizeW() failed on '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -686,7 +699,7 @@ Module::FileInfo Module::getFileInfo() const
qCritical().nospace().noquote()
<< "GetFileVersionInfoW() failed on '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -804,7 +817,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
// time on the file
// opening the file
- std::unique_ptr h(CreateFileW(
+ HandlePtr h(CreateFileW(
m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
@@ -813,7 +826,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
qCritical().nospace().noquote()
<< "can't open file '" << m_path << "' for timestamp, "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -823,7 +836,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
const auto e = GetLastError();
qCritical().nospace().noquote()
<< "can't get file time for '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -1075,7 +1088,7 @@ WindowsInfo::Release WindowsInfo::getRelease() const
std::optional WindowsInfo::getElevated() const
{
- std::unique_ptr token;
+ HandlePtr token;
{
HANDLE rawToken = 0;
@@ -1085,7 +1098,7 @@ std::optional WindowsInfo::getElevated() const
qCritical()
<< "while trying to check if process is elevated, "
- << "OpenProcessToken() failed: " << formatSystemMessage(e);
+ << "OpenProcessToken() failed: " << formatSystemMessageQ(e);
return {};
}
@@ -1101,7 +1114,7 @@ std::optional WindowsInfo::getElevated() const
qCritical()
<< "while trying to check if process is elevated, "
- << "GetTokenInformation() failed: " << formatSystemMessage(e);
+ << "GetTokenInformation() failed: " << formatSystemMessageQ(e);
return {};
}
@@ -1180,6 +1193,345 @@ QString SecurityFeature::toString() const
return s;
}
+
+struct Process
+{
+ std::wstring filename;
+ DWORD pid;
+
+ Process(std::wstring f, DWORD id)
+ : filename(std::move(f)), pid(id)
+ {
+ }
+};
+
+std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
+{
+ DWORD bufferSize = MAX_PATH;
+
+ for (int tries=0; tries<10; ++tries)
+ {
+ auto buffer = std::make_unique(bufferSize + 1);
+ std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0);
+
+ DWORD writtenSize = 0;
+
+ if (process == INVALID_HANDLE_VALUE) {
+ // query this process
+ writtenSize = GetModuleFileNameW(0, buffer.get(), bufferSize);
+ } else {
+ // query another process
+ writtenSize = GetModuleBaseNameW(process, 0, buffer.get(), bufferSize);
+ }
+
+ if (writtenSize == 0) {
+ const auto e = GetLastError();
+ std::wcerr << formatSystemMessage(e) << L"\n";
+ break;
+ } else if (writtenSize >= bufferSize) {
+ // buffer is too small, try again
+ bufferSize *= 2;
+ } else {
+ // if GetModuleFileName() works, `writtenSize` does not include the null
+ // terminator
+ const std::wstring s(buffer.get(), writtenSize);
+ const fs::path path(s);
+
+ return path.filename().native();
+ }
+ }
+
+
+ std::wstring what;
+ if (process == INVALID_HANDLE_VALUE) {
+ what = L"the current process";
+ } else {
+ what = L"pid " + std::to_wstring(reinterpret_cast(process));
+ }
+
+ std::wcerr << L"failed to get filename for " << what << L"\n";
+ return {};
+}
+
+std::vector runningProcessesIds()
+{
+ // initial size of 300 processes, unlikely to be more than that
+ std::size_t size = 300;
+
+ for (int tries=0; tries<10; ++tries) {
+ auto ids = std::make_unique(size);
+ std::fill(ids.get(), ids.get() + size, 0);
+
+ DWORD bytesGiven = static_cast(size * sizeof(ids[0]));
+ DWORD bytesWritten = 0;
+
+ if (!EnumProcesses(ids.get(), bytesGiven, &bytesWritten))
+ {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to enumerate processes, "
+ << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ if (bytesWritten == bytesGiven) {
+ size *= 2;
+ continue;
+ }
+
+ const auto count = bytesWritten / sizeof(ids[0]);
+ return std::vector(ids.get(), ids.get() + count);
+ }
+
+ std::cerr << L"too many processes to enumerate";
+ return {};
+}
+
+std::vector runningProcesses()
+{
+ const auto pids = runningProcessesIds();
+ std::vector v;
+
+ for (const auto& pid : pids) {
+ if (pid == 0) {
+ // the idle process seems to be picked up by EnumProcesses()
+ continue;
+ }
+
+ HandlePtr h(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_ACCESS_DENIED) {
+ // don't log access denied, will happen a lot when not elevated
+ std::wcerr
+ << L"failed to open process " << pid << L", "
+ << formatSystemMessage(e) << L"\n";
+ }
+
+ continue;
+ }
+
+ auto filename = processFilename(h.get());
+ if (!filename.empty()) {
+ v.emplace_back(std::move(filename), pid);
+ }
+ }
+
+ return v;
+}
+
+DWORD findOtherPid()
+{
+ const std::wstring defaultName = L"ModOrganizer.exe";
+
+ std::wclog << L"looking for the other process...\n";
+
+ const auto thisPid = GetCurrentProcessId();
+ std::wclog << L"this process id is " << thisPid << L"\n";
+
+ auto filename = processFilename();
+ if (filename.empty()) {
+ std::wcerr
+ << L"can't get current process filename, defaulting to "
+ << defaultName << L"\n";
+
+ filename = defaultName;
+ } else {
+ std::wclog << L"this process filename is " << filename << L"\n";
+ }
+
+ const auto processes = runningProcesses();
+ std::wclog << L"there are " << processes.size() << L" processes running\n";
+
+ for (const auto& p : processes) {
+ if (p.filename == filename) {
+ if (p.pid != thisPid) {
+ return p.pid;
+ }
+ }
+ }
+
+ std::wclog
+ << L"no process with this filename\n"
+ << L"MO may not be running, or it may be running as administrator\n"
+ << L"you can try running this again as administrator\n";
+
+ return 0;
+}
+
+
+std::wstring tempDir()
+{
+ const DWORD bufferSize = MAX_PATH + 1;
+ wchar_t buffer[bufferSize + 1] = {};
+
+ const auto written = GetTempPathW(bufferSize, buffer);
+ if (written == 0) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to get temp path, " << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ // `written` does not include the null terminator
+ return std::wstring(buffer, buffer + written);
+}
+
+HandlePtr tempFile(const std::wstring dir)
+{
+ const auto now = std::time(0);
+ const auto tm = std::gmtime(&now);
+
+ std::wostringstream oss;
+ oss
+ << L"ModOrganizer-"
+ << std::setw(4) << (1900 + tm->tm_year)
+ << std::setw(2) << std::setfill(L'0') << (tm->tm_mon + 1)
+ << std::setw(2) << std::setfill(L'0') << tm->tm_mday << "T"
+ << std::setw(2) << std::setfill(L'0') << tm->tm_hour
+ << std::setw(2) << std::setfill(L'0') << tm->tm_min
+ << std::setw(2) << std::setfill(L'0') << tm->tm_sec;
+
+ const std::wstring prefix = oss.str();
+ const std::wstring ext = L".dmp";
+
+ std::wstring path = dir + L"\\" + prefix + ext;
+ for (int i=0; i<100; ++i) {
+ std::wclog << L"trying file '" << path << L"'\n";
+
+ HandlePtr h (CreateFileW(
+ path.c_str(), GENERIC_WRITE, 0, nullptr,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr));
+
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+
+ const auto e = GetLastError();
+ if (e != ERROR_FILE_EXISTS) {
+ // probably no write access
+ std::wcerr
+ << L"failed to create dump file, " << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ path = dir + L"\\" + prefix + L"-" + std::to_wstring(i + 1) + ext;
+ }
+
+ std::wcerr << L"can't create dump file, ran out of filenames\n";
+ return {};
+}
+
+HandlePtr dumpFile()
+{
+ // try the current directory
+ HandlePtr h = tempFile(L".");
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+
+ std::wclog << L"cannot write dump file in current directory\n";
+
+ // try the temp directory
+ const auto dir = tempDir();
+
+ if (dir.empty()) {
+ std::wclog << L"can't get the temp directory\n";
+ } else {
+ h = tempFile(dir.c_str());
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+ }
+
+ std::wcerr << L"nowhere to write the dump file\n";
+ return {};
+}
+
+bool createMiniDump(HANDLE process, CoreDumpTypes type)
+{
+ const DWORD pid = GetProcessId(process);
+
+ const HandlePtr file = dumpFile();
+ if (!file) {
+ return false;
+ }
+
+ auto flags = _MINIDUMP_TYPE(
+ MiniDumpNormal |
+ MiniDumpWithHandleData |
+ MiniDumpWithUnloadedModules |
+ MiniDumpWithProcessThreadData);
+
+ if (type == CoreDumpTypes::Data) {
+ std::wclog << L"writing minidump with data\n";
+ flags = _MINIDUMP_TYPE(flags | MiniDumpWithDataSegs);
+ } else if (type == CoreDumpTypes::Full) {
+ std::wclog << L"writing full minidump\n";
+ flags = _MINIDUMP_TYPE(flags | MiniDumpWithFullMemory);
+ } else {
+ std::wclog << L"writing mini minidump\n";
+ }
+
+ const auto ret = MiniDumpWriteDump(
+ process, pid, file.get(), flags, nullptr, nullptr, nullptr);
+
+ if (!ret) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to write mini dump, " << formatSystemMessage(e) << L"\n";
+
+ return false;
+ }
+
+ std::wclog << L"minidump written correctly\n";
+ return true;
+}
+
+
+bool coredump(CoreDumpTypes type)
+{
+ std::wclog << L"creating minidump for the current process\n";
+ return createMiniDump(GetCurrentProcess(), type);
+}
+
+bool coredumpOther(CoreDumpTypes type)
+{
+ std::wclog << L"creating minidump for an running process\n";
+
+ const auto pid = findOtherPid();
+ if (pid == 0) {
+ std::wcerr << L"no other process found\n";
+ return false;
+ }
+
+ std::wclog << L"found other process with pid " << pid << L"\n";
+
+ HandlePtr handle(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+
+ if (!handle) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to open process " << pid << L", "
+ << formatSystemMessage(e) << L"\n";
+
+ return false;
+ }
+
+ return createMiniDump(handle.get(), type);
+}
+
} // namespace env
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 6b842f8c..2296651c 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -284,6 +284,23 @@ private:
void getSecurityFeatures();
};
+
+enum class CoreDumpTypes
+{
+ Mini = 1,
+ Data,
+ Full
+};
+
+// creates a minidump file for the given process
+//
+bool coredump(CoreDumpTypes type);
+
+// finds another process with the same name as this one and creates a minidump
+// file for it
+//
+bool coredumpOther(CoreDumpTypes type);
+
} // namespace env
--
cgit v1.3.1
From 5e27a96a27a351701182d493c64de698aaac4ae8 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 08:55:24 -0400
Subject: a few comments
---
src/shared/util.cpp | 47 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 37 insertions(+), 10 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index b14a02b1..36800d5b 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1205,11 +1205,16 @@ struct Process
}
};
+// returns the filename of the given process or the current one
+//
std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
{
+ // double the buffer size 10 times
+ const int MaxTries = 10;
+
DWORD bufferSize = MAX_PATH;
- for (int tries=0; tries<10; ++tries)
+ for (int tries=0; tries(bufferSize + 1);
std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0);
@@ -1225,6 +1230,7 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
}
if (writtenSize == 0) {
+ // hard failure
const auto e = GetLastError();
std::wcerr << formatSystemMessage(e) << L"\n";
break;
@@ -1241,6 +1247,7 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
}
}
+ // something failed or the path is way too long to make sense
std::wstring what;
if (process == INVALID_HANDLE_VALUE) {
@@ -1255,10 +1262,13 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
std::vector runningProcessesIds()
{
+ // double the buffer size 10 times
+ const int MaxTries = 10;
+
// initial size of 300 processes, unlikely to be more than that
std::size_t size = 300;
- for (int tries=0; tries<10; ++tries) {
+ for (int tries=0; tries(size);
std::fill(ids.get(), ids.get() + size, 0);
@@ -1277,6 +1287,8 @@ std::vector runningProcessesIds()
}
if (bytesWritten == bytesGiven) {
+ // no way to distinguish between an exact fit and not enough space,
+ // just try again
size *= 2;
continue;
}
@@ -1296,7 +1308,7 @@ std::vector runningProcesses()
for (const auto& pid : pids) {
if (pid == 0) {
- // the idle process seems to be picked up by EnumProcesses()
+ // the idle process has pid 0 and seems to be picked up by EnumProcesses()
continue;
}
@@ -1307,7 +1319,8 @@ std::vector runningProcesses()
const auto e = GetLastError();
if (e != ERROR_ACCESS_DENIED) {
- // don't log access denied, will happen a lot when not elevated
+ // don't log access denied, will happen a lot for system processes, even
+ // when elevated
std::wcerr
<< L"failed to open process " << pid << L", "
<< formatSystemMessage(e) << L"\n";
@@ -1331,9 +1344,12 @@ DWORD findOtherPid()
std::wclog << L"looking for the other process...\n";
+ // used to skip the current process below
const auto thisPid = GetCurrentProcessId();
std::wclog << L"this process id is " << thisPid << L"\n";
+ // getting the filename for this process, assumes the other process has the
+ // smae one
auto filename = processFilename();
if (filename.empty()) {
std::wcerr
@@ -1345,9 +1361,12 @@ DWORD findOtherPid()
std::wclog << L"this process filename is " << filename << L"\n";
}
+ // getting all running processes
const auto processes = runningProcesses();
std::wclog << L"there are " << processes.size() << L" processes running\n";
+ // going through processes, trying to find one with the same name and a
+ // different pid than this process has
for (const auto& p : processes) {
if (p.filename == filename) {
if (p.pid != thisPid) {
@@ -1364,7 +1383,6 @@ DWORD findOtherPid()
return 0;
}
-
std::wstring tempDir()
{
const DWORD bufferSize = MAX_PATH + 1;
@@ -1386,9 +1404,15 @@ std::wstring tempDir()
HandlePtr tempFile(const std::wstring dir)
{
+ // maximum tries of incrementing the counter
+ const int MaxTries = 100;
+
+ // UTC time and date will be in the filename
const auto now = std::time(0);
const auto tm = std::gmtime(&now);
+ // "ModOrganizer-YYYYMMDDThhmmss.dmp", with a possible "-i" appended, where
+ // i can go until MaxTries
std::wostringstream oss;
oss
<< L"ModOrganizer-"
@@ -1402,8 +1426,10 @@ HandlePtr tempFile(const std::wstring dir)
const std::wstring prefix = oss.str();
const std::wstring ext = L".dmp";
+ // first path to try, without counter in it
std::wstring path = dir + L"\\" + prefix + ext;
- for (int i=0; i<100; ++i) {
+
+ for (int i=0; i
Date: Mon, 1 Jul 2019 12:38:35 -0400
Subject: now handles windows firewall, which apparently doens't report itself
to wmi some more error checking
---
src/main.cpp | 6 +-
src/shared/util.cpp | 294 ++++++++++++++++++++++++++++++++++++++--------------
src/shared/util.h | 26 +++--
3 files changed, 239 insertions(+), 87 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index f08e5ad3..c6c87a64 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -493,9 +493,9 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qWarning() << "MO seems to be running in compatibility mode";
}
- qDebug().nospace().noquote() << "security features:";
- for (const auto& sf : env.securityFeatures()) {
- qDebug().nospace().noquote() << " . " << sf.toString();
+ qDebug().nospace().noquote() << "security products:";
+ for (const auto& sp : env.securityProducts()) {
+ qDebug().nospace().noquote() << " . " << sp.toString();
}
qDebug() << "modules loaded in process:";
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 36800d5b..072cee2d 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -35,6 +35,8 @@ along with Mod Organizer. If not, see .
#include
#include
#include
+#include
+
#pragma comment(lib, "Wbemuuid.lib")
using MOBase::formatSystemMessage;
@@ -305,6 +307,9 @@ struct COMReleaser
}
};
+template
+using COMPtr = std::unique_ptr;
+
class WMI
{
@@ -332,17 +337,26 @@ public:
}
auto enumerator = getEnumerator(q);
+ if (!enumerator) {
+ return;
+ }
for (;;)
{
- std::unique_ptr object;
+ COMPtr object;
{
IWbemClassObject* rawObject = nullptr;
ULONG count = 0;
auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
- if (count == 0) {
+ if (count == 0 || !rawObject) {
+ break;
+ }
+
+ if (FAILED(ret)) {
+ qCritical()
+ << "enumerator->next() failed, " << formatSystemMessageQ(ret);
break;
}
@@ -353,33 +367,9 @@ public:
}
}
- std::unique_ptr getEnumerator(
- const std::string& query)
- {
- IEnumWbemClassObject* rawEnumerator = NULL;
-
- auto ret = m_service->ExecQuery(
- bstr_t("WQL"),
- bstr_t(query.c_str()),
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
- NULL,
- &rawEnumerator);
-
- if (FAILED(ret))
- {
- qCritical()
- << "query '" << QString::fromStdString(query) << "' failed, "
- << formatSystemMessageQ(ret);
-
- return {};
- }
-
- return std::unique_ptr(rawEnumerator);
- }
-
private:
- std::unique_ptr m_locator;
- std::unique_ptr m_service;
+ COMPtr m_locator;
+ COMPtr m_service;
void createLocator()
{
@@ -389,7 +379,7 @@ private:
CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
IID_IWbemLocator, &rawLocator);
- if (FAILED(ret)) {
+ if (FAILED(ret) || !rawLocator) {
qCritical()
<< "CoCreateInstance for WbemLocator failed, "
<< formatSystemMessageQ(ret);
@@ -409,7 +399,7 @@ private:
nullptr, nullptr, nullptr, 0, nullptr, nullptr,
&rawService);
- if (FAILED(res)) {
+ if (FAILED(res) || !rawService) {
qCritical()
<< "locator->ConnectServer() failed for namespace "
<< "'" << QString::fromStdString(ns) << "', "
@@ -435,13 +425,37 @@ private:
throw failed();
}
}
+
+ COMPtr getEnumerator(
+ const std::string& query)
+ {
+ IEnumWbemClassObject* rawEnumerator = NULL;
+
+ auto ret = m_service->ExecQuery(
+ bstr_t("WQL"),
+ bstr_t(query.c_str()),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL,
+ &rawEnumerator);
+
+ if (FAILED(ret) || !rawEnumerator)
+ {
+ qCritical()
+ << "query '" << QString::fromStdString(query) << "' failed, "
+ << formatSystemMessageQ(ret);
+
+ return {};
+ }
+
+ return COMPtr(rawEnumerator);
+ }
};
Environment::Environment()
{
- getLoadedModules();
- getSecurityFeatures();
+ m_modules = getLoadedModules();
+ m_security = getSecurityProducts();
}
const std::vector& Environment::loadedModules()
@@ -454,12 +468,12 @@ const WindowsInfo& Environment::windowsInfo() const
return m_windows;
}
-const std::vector& Environment::securityFeatures() const
+const std::vector& Environment::securityProducts() const
{
return m_security;
}
-void Environment::getLoadedModules()
+std::vector Environment::getLoadedModules() const
{
HandlePtr snapshot(CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
@@ -472,7 +486,7 @@ void Environment::getLoadedModules()
<< "CreateToolhelp32Snapshot() failed, "
<< formatSystemMessageQ(e);
- return;
+ return {};
}
MODULEENTRY32 me = {};
@@ -486,54 +500,88 @@ void Environment::getLoadedModules()
qCritical().nospace().noquote()
<< "Module32First() failed, " << formatSystemMessageQ(e);
- return;
+ return {};
}
+ std::vector v;
+
for (;;)
{
const auto path = QString::fromWCharArray(me.szExePath);
-
- m_modules.push_back(Module(path, me.modBaseSize));
+ if (!path.isEmpty()) {
+ v.push_back(Module(path, me.modBaseSize));
+ }
// next module
if (!Module32Next(snapshot.get(), &me)) {
const auto e = GetLastError();
- if (e == ERROR_NO_MORE_FILES) {
- // not an error
- break;
- }
-
+ // no more modules is not an error
+ if (e != ERROR_NO_MORE_FILES) {
qCritical().nospace().noquote()
<< "Module32Next() failed, " << formatSystemMessageQ(e);
+ }
break;
}
}
// sorting by display name
- std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
+ std::sort(v.begin(), v.end(), [](auto&& a, auto&& b) {
return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
});
+
+ return v;
+}
+
+std::vector Environment::getSecurityProducts() const
+{
+ std::vector v;
+
+ {
+ auto fromWMI = getSecurityProductsFromWMI();
+ v.insert(
+ v.end(),
+ std::make_move_iterator(fromWMI.begin()),
+ std::make_move_iterator(fromWMI.end()));
+ }
+
+ if (auto p=getWindowsFirewall()) {
+ v.push_back(std::move(*p));
+ }
+
+ return v;
}
-void Environment::getSecurityFeatures()
+std::vector Environment::getSecurityProductsFromWMI() const
{
- WMI wmi("root\\SecurityCenter2");
- std::map map;
+ // some products may be present in multiple queries, such as a product marked
+ // as both antivirus and antispyware, but they'll have the same GUID, so use
+ // that to avoid duplicating entries
+ std::map map;
auto handleProduct = [&](auto* o) {
VARIANT prop;
+ // display name
auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret);
+ qCritical()
+ << "failed to get displayName, "
+ << formatSystemMessageQ(ret);
+
+ return;
+ }
+
+ if (prop.vt != VT_BSTR) {
+ qCritical() << "displayName is a " << prop.vt << ", not a bstr";
return;
}
const std::wstring name = prop.bstrVal;
VariantClear(&prop);
+ // product state
ret = o->Get(L"productState", 0, &prop, 0, 0);
if (FAILED(ret)) {
qCritical()
@@ -543,9 +591,21 @@ void Environment::getSecurityFeatures()
return;
}
- const DWORD state = prop.ulVal;
+ if (prop.vt != VT_UI4 && prop.vt != VT_I4) {
+ qCritical() << "productState is a " << prop.vt << ", is not a VT_UI4";
+ return;
+ }
+
+ DWORD state = 0;
+ if (prop.vt == VT_I4) {
+ state = prop.lVal;
+ } else {
+ state = prop.ulVal;
+ }
+
VariantClear(&prop);
+ // guid
ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
if (FAILED(ret)) {
qCritical()
@@ -555,25 +615,94 @@ void Environment::getSecurityFeatures()
return;
}
+ if (prop.vt != VT_BSTR) {
+ qCritical() << "instanceGuid is a " << prop.vt << ", is not a bstr";
+ return;
+ }
+
const QUuid guid(QString::fromWCharArray(prop.bstrVal));
VariantClear(&prop);
+ const auto provider = static_cast((state >> 16) & 0xff);
+ const auto scanner = (state >> 8) & 0xff;
+ const auto definitions = state & 0xff;
- auto itor = map.find(guid);
+ const bool active = ((scanner & 0x10) != 0);
+ const bool upToDate = (definitions == 0);
- if (itor == map.end()) {
- map.insert({
- guid, SecurityFeature(QString::fromStdWString(name), state)});
- }
+ map.insert({
+ guid,
+ {QString::fromStdWString(name), provider, active, upToDate}});
};
- wmi.query("select * from AntivirusProduct", handleProduct);
- wmi.query("select * from FirewallProduct", handleProduct);
- wmi.query("select * from AntiSpywareProduct", handleProduct);
+ {
+ WMI wmi("root\\SecurityCenter2");
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+ }
+
+ {
+ WMI wmi("root\\SecurityCenter");
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+ }
+
+ std::vector v;
for (auto&& p : map) {
- m_security.push_back(p.second);
+ v.push_back(p.second);
}
+
+ return v;
+}
+
+std::optional Environment::getWindowsFirewall() const
+{
+ HRESULT hr = 0;
+
+ COMPtr policy;
+
+ {
+ void* rawPolicy = nullptr;
+
+ hr = CoCreateInstance(
+ __uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwPolicy2), &rawPolicy);
+
+ if (FAILED(hr) || !rawPolicy) {
+ qCritical()
+ << "CoCreateInstance for NetFwPolicy2 failed, "
+ << formatSystemMessage(hr);
+
+ return {};
+ }
+
+ policy.reset(static_cast(rawPolicy));
+ }
+
+ VARIANT_BOOL enabledVariant;
+
+ if (policy) {
+ hr = policy->get_FirewallEnabled(NET_FW_PROFILE2_PUBLIC, &enabledVariant);
+ if (FAILED(hr))
+ {
+ qCritical()
+ << "get_FirewallEnabled failed, "
+ << formatSystemMessage(hr);
+
+ return {};
+ }
+ }
+
+ const auto enabled = (enabledVariant != VARIANT_FALSE);
+ if (!enabled) {
+ return {};
+ }
+
+ return SecurityProduct(
+ "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
}
@@ -1123,52 +1252,67 @@ std::optional WindowsInfo::getElevated() const
}
-SecurityFeature::SecurityFeature(QString name, DWORD state)
- : m_name(std::move(name)), m_state(state)
+SecurityProduct::SecurityProduct(
+ QString name, int provider,
+ bool active, bool upToDate) :
+ m_name(std::move(name)), m_provider(provider),
+ m_active(active), m_upToDate(upToDate)
{
}
-const QString& SecurityFeature::name() const
+const QString& SecurityProduct::name() const
{
return m_name;
}
-QString SecurityFeature::toString() const
+int SecurityProduct::provider() const
+{
+ return m_provider;
+}
+
+bool SecurityProduct::active() const
+{
+ return m_active;
+}
+
+bool SecurityProduct::upToDate() const
+{
+ return m_upToDate;
+}
+
+QString SecurityProduct::toString() const
{
QString s;
s += m_name + " ";
- const auto provider = (m_state >> 16) & 0xff;
- const auto scanner = (m_state >> 8) & 0xff;
- const auto definitions = m_state & 0xff;
QStringList ps;
- if (provider & WSC_SECURITY_PROVIDER_FIREWALL) {
+ if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) {
ps.push_back("firewall");
}
- if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
ps.push_back("autoupdate");
}
- if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
ps.push_back("antivirus");
}
- if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
ps.push_back("antispyware");
}
- if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
ps.push_back("settings");
}
- if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
+ if (m_provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
ps.push_back("uac");
}
- if (provider & WSC_SECURITY_PROVIDER_SERVICE) {
+ if (m_provider & WSC_SECURITY_PROVIDER_SERVICE) {
ps.push_back("service");
}
@@ -1178,15 +1322,13 @@ QString SecurityFeature::toString() const
s += "(" + ps.join("|") + ")";
}
- if (scanner & 0x10) {
+ if (m_active) {
s += ", active";
} else {
s += ", inactive";
}
- if (definitions == 0) {
- s += ", definitions up to date";
- } else {
+ if (!m_upToDate) {
s += ", definitions outdated";
}
diff --git a/src/shared/util.h b/src/shared/util.h
index 2296651c..e0da8a99 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -241,18 +241,25 @@ private:
};
-class SecurityFeature
+class SecurityProduct
{
public:
- SecurityFeature(QString name, DWORD state);
+ SecurityProduct(
+ QString name, int provider,
+ bool active, bool upToDate);
const QString& name() const;
+ int provider() const;
+ bool active() const;
+ bool upToDate() const;
QString toString() const;
private:
QString m_name;
- DWORD m_state;
+ int m_provider;
+ bool m_active;
+ bool m_upToDate;
};
@@ -271,17 +278,20 @@ public:
//
const WindowsInfo& windowsInfo() const;
- // information about the installed antivirus
+ // information about the installed security products
//
- const std::vector& securityFeatures() const;
+ const std::vector& securityProducts() const;
private:
std::vector m_modules;
WindowsInfo m_windows;
- std::vector m_security;
+ std::vector m_security;
- void getLoadedModules();
- void getSecurityFeatures();
+ std::vector getLoadedModules() const;
+ std::vector getSecurityProducts() const;
+
+ std::vector getSecurityProductsFromWMI() const;
+ std::optional getWindowsFirewall() const;
};
--
cgit v1.3.1