|
35 | 35 | #pragma comment(lib, "rpcrt4.lib") |
36 | 36 | #pragma comment(lib, "httpapi.lib") |
37 | 37 | #pragma comment(lib, "psapi.lib") |
| 38 | +#pragma comment(lib, "wintrust.lib") |
38 | 39 |
|
39 | 40 | #include <cstdio> |
40 | 41 | #include <iostream> |
41 | 42 | #include <memory> |
42 | 43 | #include <algorithm> |
43 | 44 | #include <psapi.h> |
| 45 | +#include <wintrust.h> |
| 46 | +#include <softpub.h> |
| 47 | +#include <cwctype> |
44 | 48 | #include <stdexcept> |
45 | 49 | #include <string> |
46 | 50 | #include <array> |
@@ -83,6 +87,9 @@ std::string extract_host(const std::string& url); |
83 | 87 | bool hosts_override_present(const std::string& host); |
84 | 88 | bool module_paths_ok(); |
85 | 89 | bool duplicate_system_modules_present(); |
| 90 | +bool user_writable_module_present(); |
| 91 | +bool module_has_rwx_section(HMODULE mod); |
| 92 | +bool core_modules_signed(); |
86 | 93 | std::string seed; |
87 | 94 | void cleanUpSeedData(const std::string& seed); |
88 | 95 | std::string signature; |
@@ -1776,6 +1783,72 @@ static std::wstring to_lower_ws(std::wstring value) |
1776 | 1783 | return value; |
1777 | 1784 | } |
1778 | 1785 |
|
| 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 | + |
1779 | 1852 | bool module_paths_ok() |
1780 | 1853 | { |
1781 | 1854 | 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() |
1834 | 1907 | return false; |
1835 | 1908 | } |
1836 | 1909 |
|
| 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 | + |
1837 | 1940 | void KeyAuth::api::setDebug(bool value) { |
1838 | 1941 | KeyAuth::api::debug = value; |
1839 | 1942 | } |
@@ -2220,7 +2323,7 @@ void checkInit() { |
2220 | 2323 | const auto last_mod = last_module_check.load(); |
2221 | 2324 | if (now - last_mod > 60) { |
2222 | 2325 | 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()) { |
2224 | 2327 | error(XorStr("module path check failed, possible side-load detected.")); |
2225 | 2328 | } |
2226 | 2329 | } |
@@ -2252,7 +2355,7 @@ void integrity_watchdog() { |
2252 | 2355 | const auto last_mod = last_module_check.load(); |
2253 | 2356 | if (now - last_mod > 120) { |
2254 | 2357 | 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()) { |
2256 | 2359 | error(XorStr("module path check failed, possible side-load detected.")); |
2257 | 2360 | } |
2258 | 2361 | } |
|
0 commit comments