diff options
| author | Jeremy Rimpo <jrim@rimpo.org> | 2019-08-02 01:49:33 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-02 01:49:33 -0500 |
| commit | cff526415d781cb8a7761961ae2bd1fb6775c376 (patch) | |
| tree | cf5ff4f0d7bdd3767155a8a3e251201861284f89 /src/envwindows.cpp | |
| parent | bdf45aea69ab7df0b01eb87cc80a2641ea4261d0 (diff) | |
| parent | e4cf2c314d6397c5d73bcf567d4420171238bd29 (diff) | |
Merge pull request #807 from isanae/logging-rework
Logging rework
Diffstat (limited to 'src/envwindows.cpp')
| -rw-r--r-- | src/envwindows.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/envwindows.cpp b/src/envwindows.cpp new file mode 100644 index 00000000..3932a9b5 --- /dev/null +++ b/src/envwindows.cpp @@ -0,0 +1,237 @@ +#include "envwindows.h" +#include "env.h" +#include <utility.h> +#include <log.h> + +namespace env +{ + +using namespace MOBase; + +WindowsInfo::WindowsInfo() +{ + // loading ntdll.dll, the functions will be found with GetProcAddress() + LibraryPtr ntdll(LoadLibraryW(L"ntdll.dll")); + + if (!ntdll) { + log::error("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 WindowsInfo::compatibilityMode() const +{ + if (m_real == Version()) { + // don't know the real version, can't guess compatibility mode + return false; + } + + return (m_real != m_reported); +} + +const WindowsInfo::Version& WindowsInfo::reportedVersion() const +{ + return m_reported; +} + +const WindowsInfo::Version& WindowsInfo::realVersion() const +{ + return m_real; +} + +const WindowsInfo::Release& WindowsInfo::release() const +{ + return m_release; +} + +std::optional<bool> WindowsInfo::isElevated() const +{ + return m_elevated; +} + +QString WindowsInfo::toString() const +{ + QStringList sl; + + const QString reported = m_reported.toString(); + const QString real = m_real.toString(); + + // version + sl.push_back("version " + reported); + + // real version if different + if (compatibilityMode()) { + sl.push_back("real version " + real); + } + + // 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)); + } + + // release ID + if (!m_release.ID.isEmpty()) { + sl.push_back("release " + m_release.ID); + } + + // buildlab string + if (!m_release.buildLab.isEmpty()) { + sl.push_back(m_release.buildLab); + } + + // 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"); + } + + sl.push_back("elevated: " + elevated); + + return sl.join(", "); +} + +WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const +{ + // 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 + + using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW); + + auto* RtlGetVersion = reinterpret_cast<RtlGetVersionType*>( + GetProcAddress(ntdll, "RtlGetVersion")); + + if (!RtlGetVersion) { + log::error("RtlGetVersion() not found in ntdll.dll"); + return {}; + } + + OSVERSIONINFOEX vi = {}; + vi.dwOSVersionInfoSize = sizeof(vi); + + // this apparently never fails + RtlGetVersion((RTL_OSVERSIONINFOW*)&vi); + + 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<RtlGetNtVersionNumbersType*>( + GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); + + if (!RtlGetNtVersionNumbers) { + log::error("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}; +} + +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); + + 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(); + } + } + + // 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; +} + +std::optional<bool> WindowsInfo::getElevated() const +{ + HandlePtr token; + + { + HANDLE rawToken = 0; + + if (!OpenProcessToken(GetCurrentProcess( ), TOKEN_QUERY, &rawToken)) { + const auto e = GetLastError(); + + log::error( + "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(); + + log::error( + "while trying to check if process is elevated, " + "GetTokenInformation() failed: {}", formatSystemMessage(e)); + + return {}; + } + + return (e.TokenIsElevated != 0); +} + +} // namespace |
