Skip to content

Commit 01149af

Browse files
committed
Add module signature and RWX checks
1 parent 54e1ace commit 01149af

1 file changed

Lines changed: 105 additions & 2 deletions

File tree

auth.cpp

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@
3535
#pragma comment(lib, "rpcrt4.lib")
3636
#pragma comment(lib, "httpapi.lib")
3737
#pragma comment(lib, "psapi.lib")
38+
#pragma comment(lib, "wintrust.lib")
3839

3940
#include <cstdio>
4041
#include <iostream>
4142
#include <memory>
4243
#include <algorithm>
4344
#include <psapi.h>
45+
#include <wintrust.h>
46+
#include <softpub.h>
47+
#include <cwctype>
4448
#include <stdexcept>
4549
#include <string>
4650
#include <array>
@@ -83,6 +87,9 @@ std::string extract_host(const std::string& url);
8387
bool hosts_override_present(const std::string& host);
8488
bool module_paths_ok();
8589
bool duplicate_system_modules_present();
90+
bool user_writable_module_present();
91+
bool module_has_rwx_section(HMODULE mod);
92+
bool core_modules_signed();
8693
std::string seed;
8794
void cleanUpSeedData(const std::string& seed);
8895
std::string signature;
@@ -1776,6 +1783,72 @@ static std::wstring to_lower_ws(std::wstring value)
17761783
return value;
17771784
}
17781785

1786+
static bool path_has_any(const std::wstring& p, const std::initializer_list<std::wstring>& needles)
1787+
{
1788+
for (const auto& n : needles) {
1789+
if (p.find(n) != std::wstring::npos)
1790+
return true;
1791+
}
1792+
return false;
1793+
}
1794+
1795+
bool module_has_rwx_section(HMODULE mod)
1796+
{
1797+
if (!mod)
1798+
return false;
1799+
auto base = reinterpret_cast<std::uintptr_t>(mod);
1800+
auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
1801+
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
1802+
return false;
1803+
auto nt = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
1804+
if (nt->Signature != IMAGE_NT_SIGNATURE)
1805+
return false;
1806+
auto section = IMAGE_FIRST_SECTION(nt);
1807+
for (unsigned i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) {
1808+
const auto ch = section->Characteristics;
1809+
if ((ch & IMAGE_SCN_MEM_EXECUTE) && (ch & IMAGE_SCN_MEM_WRITE))
1810+
return true;
1811+
}
1812+
return false;
1813+
}
1814+
1815+
static bool verify_signature(const std::wstring& path)
1816+
{
1817+
WINTRUST_FILE_INFO file_info{};
1818+
file_info.cbStruct = sizeof(file_info);
1819+
file_info.pcwszFilePath = path.c_str();
1820+
1821+
WINTRUST_DATA trust_data{};
1822+
trust_data.cbStruct = sizeof(trust_data);
1823+
trust_data.dwUIChoice = WTD_UI_NONE;
1824+
trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
1825+
trust_data.dwUnionChoice = WTD_CHOICE_FILE;
1826+
trust_data.pFile = &file_info;
1827+
trust_data.dwStateAction = WTD_STATEACTION_IGNORE;
1828+
1829+
GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
1830+
LONG status = WinVerifyTrust(nullptr, &policy, &trust_data);
1831+
return status == ERROR_SUCCESS;
1832+
}
1833+
1834+
bool core_modules_signed()
1835+
{
1836+
const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" };
1837+
for (const auto* name : kModules) {
1838+
HMODULE mod = GetModuleHandleW(name);
1839+
if (!mod)
1840+
return false;
1841+
wchar_t path[MAX_PATH] = {};
1842+
if (!GetModuleFileNameW(mod, path, MAX_PATH))
1843+
return false;
1844+
if (!verify_signature(path))
1845+
return false;
1846+
if (module_has_rwx_section(mod))
1847+
return false;
1848+
}
1849+
return true;
1850+
}
1851+
17791852
bool module_paths_ok()
17801853
{
17811854
const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" };
@@ -1834,6 +1907,36 @@ bool duplicate_system_modules_present()
18341907
return false;
18351908
}
18361909

1910+
bool user_writable_module_present()
1911+
{
1912+
HMODULE mods[1024] = {};
1913+
DWORD needed = 0;
1914+
if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed))
1915+
return false;
1916+
1917+
wchar_t exe_path[MAX_PATH] = {};
1918+
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
1919+
std::wstring exe_dir = exe_path;
1920+
const auto last_slash = exe_dir.find_last_of(L"\\/");
1921+
if (last_slash != std::wstring::npos)
1922+
exe_dir = exe_dir.substr(0, last_slash + 1);
1923+
exe_dir = to_lower_ws(exe_dir);
1924+
1925+
const size_t count = needed / sizeof(HMODULE);
1926+
for (size_t i = 0; i < count; ++i) {
1927+
wchar_t path[MAX_PATH] = {};
1928+
if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH))
1929+
continue;
1930+
std::wstring p = to_lower_ws(path);
1931+
if (p.rfind(exe_dir, 0) == 0)
1932+
continue;
1933+
1934+
if (path_has_any(p, { L"\\temp\\", L"\\appdata\\local\\temp\\", L"\\downloads\\" }))
1935+
return true;
1936+
}
1937+
return false;
1938+
}
1939+
18371940
void KeyAuth::api::setDebug(bool value) {
18381941
KeyAuth::api::debug = value;
18391942
}
@@ -2220,7 +2323,7 @@ void checkInit() {
22202323
const auto last_mod = last_module_check.load();
22212324
if (now - last_mod > 60) {
22222325
last_module_check.store(now);
2223-
if (!module_paths_ok() || duplicate_system_modules_present()) {
2326+
if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) {
22242327
error(XorStr("module path check failed, possible side-load detected."));
22252328
}
22262329
}
@@ -2252,7 +2355,7 @@ void integrity_watchdog() {
22522355
const auto last_mod = last_module_check.load();
22532356
if (now - last_mod > 120) {
22542357
last_module_check.store(now);
2255-
if (!module_paths_ok() || duplicate_system_modules_present()) {
2358+
if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) {
22562359
error(XorStr("module path check failed, possible side-load detected."));
22572360
}
22582361
}

0 commit comments

Comments
 (0)