From f3da3fa850f611bfad987443aea052d20c748e0e Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:34:47 -0500 Subject: [PATCH 01/83] Harden parsing and curl escaping --- auth.cpp | 39 ++++++++++++++++++++++++++++++++------- auth.hpp | 4 ++-- utils.cpp | 8 +++++--- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/auth.cpp b/auth.cpp index 90fd644..10b123a 100644 --- a/auth.cpp +++ b/auth.cpp @@ -72,6 +72,7 @@ void checkFiles(); void checkRegistry(); void error(std::string message); std::string generate_random_number(); +std::string curl_escape(CURL* curl, const std::string& input); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -100,7 +101,7 @@ void KeyAuth::api::init() XorStr("type=init") + XorStr("&ver=") + version + XorStr("&hash=") + hash + - XorStr("&name=") + curl_easy_escape(curl, name.c_str(), 0) + + XorStr("&name=") + curl_escape(curl, name) + XorStr("&ownerid=") + ownerid; // to ensure people removed secret from main.cpp (some people will forget to) @@ -1421,8 +1422,8 @@ std::string KeyAuth::api::webhook(std::string id, std::string params, std::strin auto data = XorStr("type=webhook") + XorStr("&webid=") + id + - XorStr("¶ms=") + curl_easy_escape(curl, params.c_str(), 0) + - XorStr("&body=") + curl_easy_escape(curl, body.c_str(), 0) + + XorStr("¶ms=") + curl_escape(curl, params) + + XorStr("&body=") + curl_escape(curl, body) + XorStr("&conttype=") + contenttype + XorStr("&sessionid=") + sessionid + XorStr("&name=") + name + @@ -1601,7 +1602,15 @@ void KeyAuth::api::logout() { int VerifyPayload(std::string signature, std::string timestamp, std::string body) { - long long unix_timestamp = std::stoll(timestamp); + long long unix_timestamp = 0; + try { + unix_timestamp = std::stoll(timestamp); + } + catch (...) { + std::cerr << "[ERROR] Invalid timestamp format\n"; + MessageBoxA(0, "Signature verification failed (invalid timestamp)", "KeyAuth", MB_ICONERROR); + exit(2); + } auto current_time = std::chrono::system_clock::now(); long long current_unix_time = std::chrono::duration_cast( @@ -1678,14 +1687,30 @@ std::string get_str_between_two_str(const std::string& s, const std::string& start_delim, const std::string& stop_delim) { - unsigned first_delim_pos = s.find(start_delim); - unsigned end_pos_of_first_delim = first_delim_pos + start_delim.length(); - unsigned last_delim_pos = s.find(stop_delim); + const auto first_delim_pos = s.find(start_delim); + if (first_delim_pos == std::string::npos) + return {}; + const auto end_pos_of_first_delim = first_delim_pos + start_delim.length(); + const auto last_delim_pos = s.find(stop_delim, end_pos_of_first_delim); + if (last_delim_pos == std::string::npos || last_delim_pos < end_pos_of_first_delim) + return {}; return s.substr(end_pos_of_first_delim, last_delim_pos - end_pos_of_first_delim); } +std::string curl_escape(CURL* curl, const std::string& input) +{ + if (!curl) + return input; + char* escaped = curl_easy_escape(curl, input.c_str(), 0); + if (!escaped) + return {}; + std::string out(escaped); + curl_free(escaped); + return out; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } diff --git a/auth.hpp b/auth.hpp index f229460..df3abe4 100644 --- a/auth.hpp +++ b/auth.hpp @@ -124,7 +124,7 @@ namespace KeyAuth { api::user_data.createdate = data[XorStr("createdate")]; api::user_data.lastlogin = data[XorStr("lastlogin")]; - for (int i = 0; i < data[XorStr("subscriptions")].size(); i++) { // Prompto#7895 & stars#2297 was here + for (size_t i = 0; i < data[XorStr("subscriptions")].size(); i++) { // Prompto#7895 & stars#2297 was here subscriptions_class subscriptions; subscriptions.name = data[XorStr("subscriptions")][i][XorStr("subscription")]; subscriptions.expiry = data[XorStr("subscriptions")][i][XorStr("expiry")]; @@ -153,7 +153,7 @@ namespace KeyAuth { api::response.success = data["success"]; // intentional. Possibly trick a reverse engineer into thinking this string is for login function api::response.message = data["message"]; api::response.channeldata.clear(); //If you do not delete the data before pushing it, the data will be repeated. github.com/TTakaTit - for (const auto sub : data["messages"]) { + for (const auto& sub : data["messages"]) { std::string authoroutput = sub[XorStr("author")]; std::string messageoutput = sub["message"]; diff --git a/utils.cpp b/utils.cpp index 0b09151..221e5cc 100644 --- a/utils.cpp +++ b/utils.cpp @@ -12,9 +12,11 @@ std::string utils::get_hwid() { } std::time_t utils::string_to_timet(std::string timestamp) { - auto cv = strtol(timestamp.c_str(), NULL, 10); - - return (time_t)cv; + char* end = nullptr; + auto cv = strtol(timestamp.c_str(), &end, 10); + if (end == timestamp.c_str()) + return 0; + return static_cast(cv); } std::tm utils::timet_to_tm(time_t timestamp) { From 74bf34af9dec8608794887556119b71415581025 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:40:05 -0500 Subject: [PATCH 02/83] Add periodic integrity check --- auth.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/auth.cpp b/auth.cpp index 10b123a..da2b4e3 100644 --- a/auth.cpp +++ b/auth.cpp @@ -73,6 +73,7 @@ void checkRegistry(); void error(std::string message); std::string generate_random_number(); std::string curl_escape(CURL* curl, const std::string& input); +auto check_section_integrity( const char *section_name, bool fix ) -> bool; std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -81,6 +82,7 @@ bool initialized; std::string API_PUBLIC_KEY = "5586b4bc69c7a4b487e4563a4cd96afd39140f919bd31cea7d1c6a1e8439422b"; bool KeyAuth::api::debug = false; std::atomic LoggedIn(false); +std::atomic last_integrity_check{ 0 }; void KeyAuth::api::init() { @@ -2084,6 +2086,15 @@ void checkInit() { if (!initialized) { error(XorStr("You need to run the KeyAuthApp.init(); function before any other KeyAuth functions")); } + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + const auto last = last_integrity_check.load(); + if (now - last > 30) { + last_integrity_check.store(now); + if (check_section_integrity(XorStr(".text").c_str(), false)) { + error(XorStr("check_section_integrity() failed, don't tamper with the program.")); + } + } } // code submitted in pull request from https://github.com/BINM7MD BOOL bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) From aea56e35dbe3c5a79499d6200cdbec2edf9f940c Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:44:49 -0500 Subject: [PATCH 03/83] Add redundant integrity checks --- auth.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/auth.cpp b/auth.cpp index da2b4e3..054086b 100644 --- a/auth.cpp +++ b/auth.cpp @@ -74,6 +74,8 @@ void error(std::string message); std::string generate_random_number(); std::string curl_escape(CURL* curl, const std::string& input); auto check_section_integrity( const char *section_name, bool fix ) -> bool; +void integrity_check(); +void integrity_watchdog(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -87,6 +89,7 @@ std::atomic last_integrity_check{ 0 }; void KeyAuth::api::init() { std::thread(runChecks).detach(); + std::thread(integrity_watchdog).detach(); seed = generate_random_number(); std::atexit([]() { cleanUpSeedData(seed); }); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)modify, 0, 0, 0); @@ -1604,6 +1607,7 @@ void KeyAuth::api::logout() { int VerifyPayload(std::string signature, std::string timestamp, std::string body) { + integrity_check(); long long unix_timestamp = 0; try { unix_timestamp = std::stoll(timestamp); @@ -1720,6 +1724,7 @@ void KeyAuth::api::setDebug(bool value) { std::string KeyAuth::api::req(const std::string& data, const std::string& url) { signature.clear(); signatureTimestamp.clear(); + integrity_check(); CURL* curl = curl_easy_init(); if (!curl) { @@ -2086,6 +2091,10 @@ void checkInit() { if (!initialized) { error(XorStr("You need to run the KeyAuthApp.init(); function before any other KeyAuth functions")); } + integrity_check(); +} + +void integrity_check() { const auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); const auto last = last_integrity_check.load(); @@ -2096,6 +2105,18 @@ void checkInit() { } } } + +void integrity_watchdog() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution sleep_seconds(20, 50); + while (true) { + Sleep(static_cast(sleep_seconds(gen) * 1000)); + if (check_section_integrity(XorStr(".text").c_str(), false)) { + error(XorStr("check_section_integrity() failed, don't tamper with the program.")); + } + } +} // code submitted in pull request from https://github.com/BINM7MD BOOL bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { From ca78ab7381619c282b7e5be18ff812846ad7cabd Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:45:05 -0500 Subject: [PATCH 04/83] Harden integrity watchdog --- auth.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 054086b..1a708b2 100644 --- a/auth.cpp +++ b/auth.cpp @@ -85,6 +85,7 @@ std::string API_PUBLIC_KEY = "5586b4bc69c7a4b487e4563a4cd96afd39140f919bd31cea7d bool KeyAuth::api::debug = false; std::atomic LoggedIn(false); std::atomic last_integrity_check{ 0 }; +std::atomic integrity_fail_streak{ 0 }; void KeyAuth::api::init() { @@ -2112,8 +2113,15 @@ void integrity_watchdog() { std::uniform_int_distribution sleep_seconds(20, 50); while (true) { Sleep(static_cast(sleep_seconds(gen) * 1000)); + if (!initialized || !LoggedIn.load()) + continue; if (check_section_integrity(XorStr(".text").c_str(), false)) { - error(XorStr("check_section_integrity() failed, don't tamper with the program.")); + const int streak = integrity_fail_streak.fetch_add(1) + 1; + if (streak >= 2) { + error(XorStr("check_section_integrity() failed, don't tamper with the program.")); + } + } else { + integrity_fail_streak.store(0); } } } From 0a349d27b37bb6fd4bcd1801684b3af8014f3c6e Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:48:11 -0500 Subject: [PATCH 05/83] Cleanups and safety hardening --- auth.cpp | 8 +++++++- auth.hpp | 3 +++ killEmulator.hpp | 3 ++- utils.hpp | 4 +++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index 1a708b2..e1aaf62 100644 --- a/auth.cpp +++ b/auth.cpp @@ -109,6 +109,9 @@ void KeyAuth::api::init() XorStr("&hash=") + hash + XorStr("&name=") + curl_escape(curl, name) + XorStr("&ownerid=") + ownerid; + if (curl) { + curl_easy_cleanup(curl); // avoid leak from escape helper. -nigel + } // to ensure people removed secret from main.cpp (some people will forget to) if (path.find("https") != std::string::npos) { @@ -1404,7 +1407,7 @@ std::vector KeyAuth::api::download(std::string fileid) { XorStr("&fileid=") + fileid + XorStr("&sessionid=") + sessionid + XorStr("&name=") + name + - XorStr("&ownerid=").c_str() + ownerid; + XorStr("&ownerid=") + ownerid; auto response = req(data, url); auto json = response_decoder.parse(response); @@ -1761,6 +1764,9 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { } void error(std::string message) { + for (char& c : message) { + if (c == '&' || c == '|' || c == '\"') c = ' '; // minimize cmd injection surface. -nigel + } system((XorStr("start cmd /C \"color b && title Error && echo ").c_str() + message + XorStr(" && timeout /t 5\"")).c_str()); LI_FN(__fastfail)(0); } diff --git a/auth.hpp b/auth.hpp index df3abe4..31e0920 100644 --- a/auth.hpp +++ b/auth.hpp @@ -153,6 +153,9 @@ namespace KeyAuth { api::response.success = data["success"]; // intentional. Possibly trick a reverse engineer into thinking this string is for login function api::response.message = data["message"]; api::response.channeldata.clear(); //If you do not delete the data before pushing it, the data will be repeated. github.com/TTakaTit + if (!data.contains("messages") || !data["messages"].is_array()) { + return; // avoid invalid server payload crash. -nigel + } for (const auto& sub : data["messages"]) { std::string authoroutput = sub[XorStr("author")]; diff --git a/killEmulator.hpp b/killEmulator.hpp index 50c2ddf..d1cdcd3 100644 --- a/killEmulator.hpp +++ b/killEmulator.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include @@ -20,7 +21,7 @@ namespace protection MODULEINFO info; // place holder for the information // use this function in order to get the module information. - bool result = GetModuleInformation(reinterpret_cast(-1), + bool result = GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(info)); if (result) { diff --git a/utils.hpp b/utils.hpp index a2e0343..c0cbf91 100644 --- a/utils.hpp +++ b/utils.hpp @@ -1,10 +1,12 @@ #pragma once #include +#include #include +#include namespace utils { std::string get_hwid(); std::time_t string_to_timet(std::string timestamp); std::tm timet_to_tm(time_t timestamp); -} \ No newline at end of file +} From 644ecd21a6cc04b6b872f7c5d8b7e6c1a5ed2f0e Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:52:03 -0500 Subject: [PATCH 06/83] Detect hosts file overrides --- auth.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/auth.cpp b/auth.cpp index e1aaf62..3cd7d39 100644 --- a/auth.cpp +++ b/auth.cpp @@ -76,6 +76,8 @@ std::string curl_escape(CURL* curl, const std::string& input); auto check_section_integrity( const char *section_name, bool fix ) -> bool; void integrity_check(); void integrity_watchdog(); +std::string extract_host(const std::string& url); +bool hosts_override_present(const std::string& host); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1721,6 +1723,45 @@ std::string curl_escape(CURL* curl, const std::string& input) return out; } +std::string extract_host(const std::string& url) +{ + std::string host = url; + const auto scheme_pos = host.find("://"); + if (scheme_pos != std::string::npos) + host = host.substr(scheme_pos + 3); + const auto slash_pos = host.find('/'); + if (slash_pos != std::string::npos) + host = host.substr(0, slash_pos); + const auto colon_pos = host.find(':'); + if (colon_pos != std::string::npos) + host = host.substr(0, colon_pos); + return host; +} + +bool hosts_override_present(const std::string& host) +{ + if (host.empty()) + return false; + const char* sysroot = std::getenv("SystemRoot"); + std::string hosts_path = sysroot ? std::string(sysroot) : "C:\\Windows"; + hosts_path += "\\System32\\drivers\\etc\\hosts"; + std::ifstream file(hosts_path); + if (!file.good()) + return false; + std::string line; + while (std::getline(file, line)) { + auto hash_pos = line.find('#'); + if (hash_pos != std::string::npos) + line = line.substr(0, hash_pos); + if (line.find(host) == std::string::npos) + continue; + // basic whole-word check + if (line.find(" " + host) != std::string::npos || line.find("\t" + host) != std::string::npos) + return true; + } + return false; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -1729,6 +1770,10 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { signature.clear(); signatureTimestamp.clear(); integrity_check(); + const auto host = extract_host(url); + if (hosts_override_present(host)) { + error(XorStr("Hosts file override detected for API host.")); + } CURL* curl = curl_easy_init(); if (!curl) { From 13311d9668119d8d790b7721c975ec69d80f1793 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 18:57:00 -0500 Subject: [PATCH 07/83] Harden timestamp and module checks --- auth.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/auth.cpp b/auth.cpp index 3cd7d39..3339c31 100644 --- a/auth.cpp +++ b/auth.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,7 @@ void integrity_check(); void integrity_watchdog(); std::string extract_host(const std::string& url); bool hosts_override_present(const std::string& host); +bool module_paths_ok(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -88,6 +90,7 @@ bool KeyAuth::api::debug = false; std::atomic LoggedIn(false); std::atomic last_integrity_check{ 0 }; std::atomic integrity_fail_streak{ 0 }; +std::atomic last_module_check{ 0 }; void KeyAuth::api::init() { @@ -1628,10 +1631,11 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body long long current_unix_time = std::chrono::duration_cast( current_time.time_since_epoch()).count(); - if (current_unix_time - unix_timestamp > 20) { - std::cerr << "[ERROR] Timestamp too old (diff = " - << (current_unix_time - unix_timestamp) << "s)\n"; - MessageBoxA(0, "Signature verification failed (timestamp too old)", "KeyAuth", MB_ICONERROR); + const long long diff = std::llabs(current_unix_time - unix_timestamp); + if (diff > 120) { + std::cerr << "[ERROR] Timestamp too skewed (diff = " + << diff << "s)\n"; + MessageBoxA(0, "Signature verification failed (timestamp skew)", "KeyAuth", MB_ICONERROR); exit(3); } @@ -1762,6 +1766,35 @@ bool hosts_override_present(const std::string& host) return false; } +static std::wstring to_lower_ws(std::wstring value) +{ + std::transform(value.begin(), value.end(), value.begin(), + [](wchar_t c) { return static_cast(towlower(c)); }); + return value; +} + +bool module_paths_ok() +{ + const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; + const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); + std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; + std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); + std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + + for (const auto* name : kModules) { + HMODULE mod = GetModuleHandleW(name); + if (!mod) + continue; + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameW(mod, path, MAX_PATH)) + return false; + std::wstring p = to_lower_ws(path); + if (p.rfind(sys32, 0) != 0 && p.rfind(syswow, 0) != 0) + return false; + } + return true; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2143,6 +2176,15 @@ void checkInit() { if (!initialized) { error(XorStr("You need to run the KeyAuthApp.init(); function before any other KeyAuth functions")); } + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + const auto last_mod = last_module_check.load(); + if (now - last_mod > 60) { + last_module_check.store(now); + if (!module_paths_ok()) { + error(XorStr("module path check failed, possible side-load detected.")); + } + } integrity_check(); } @@ -2166,6 +2208,15 @@ void integrity_watchdog() { Sleep(static_cast(sleep_seconds(gen) * 1000)); if (!initialized || !LoggedIn.load()) continue; + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + const auto last_mod = last_module_check.load(); + if (now - last_mod > 120) { + last_module_check.store(now); + if (!module_paths_ok()) { + error(XorStr("module path check failed, possible side-load detected.")); + } + } if (check_section_integrity(XorStr(".text").c_str(), false)) { const int streak = integrity_fail_streak.fetch_add(1) + 1; if (streak >= 2) { From 54e1acec5b89ccfc6e88e673a2ec0b3bd08cf6cc Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 21:05:04 -0500 Subject: [PATCH 08/83] Add extra module check --- auth.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/auth.cpp b/auth.cpp index 3339c31..aefd7f7 100644 --- a/auth.cpp +++ b/auth.cpp @@ -34,11 +34,13 @@ #pragma comment(lib, "rpcrt4.lib") #pragma comment(lib, "httpapi.lib") +#pragma comment(lib, "psapi.lib") #include #include #include #include +#include #include #include #include @@ -80,6 +82,7 @@ void integrity_watchdog(); std::string extract_host(const std::string& url); bool hosts_override_present(const std::string& host); bool module_paths_ok(); +bool duplicate_system_modules_present(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1795,6 +1798,42 @@ bool module_paths_ok() return true; } +bool duplicate_system_modules_present() +{ + const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; + const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); + std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; + std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); + std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + + HMODULE mods[1024] = {}; + DWORD needed = 0; + if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) + return false; + + const size_t count = needed / sizeof(HMODULE); + for (size_t i = 0; i < count; ++i) { + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) + continue; + std::wstring p = to_lower_ws(path); + const auto name_pos = p.find_last_of(L"\\/"); + const std::wstring name = (name_pos == std::wstring::npos) ? p : p.substr(name_pos + 1); + bool is_target = false; + for (const auto* modname : kModules) { + if (name == modname) { + is_target = true; + break; + } + } + if (!is_target) + continue; + if (p.rfind(sys32, 0) != 0 && p.rfind(syswow, 0) != 0) + return true; + } + return false; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2181,7 +2220,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok()) { + if (!module_paths_ok() || duplicate_system_modules_present()) { error(XorStr("module path check failed, possible side-load detected.")); } } @@ -2213,7 +2252,7 @@ void integrity_watchdog() { const auto last_mod = last_module_check.load(); if (now - last_mod > 120) { last_module_check.store(now); - if (!module_paths_ok()) { + if (!module_paths_ok() || duplicate_system_modules_present()) { error(XorStr("module path check failed, possible side-load detected.")); } } From 01149afbcf56e2f9331f92cc648703dcd7b35aa0 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 21:11:15 -0500 Subject: [PATCH 09/83] Add module signature and RWX checks --- auth.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/auth.cpp b/auth.cpp index aefd7f7..87ebbdd 100644 --- a/auth.cpp +++ b/auth.cpp @@ -35,12 +35,16 @@ #pragma comment(lib, "rpcrt4.lib") #pragma comment(lib, "httpapi.lib") #pragma comment(lib, "psapi.lib") +#pragma comment(lib, "wintrust.lib") #include #include #include #include #include +#include +#include +#include #include #include #include @@ -83,6 +87,9 @@ std::string extract_host(const std::string& url); bool hosts_override_present(const std::string& host); bool module_paths_ok(); bool duplicate_system_modules_present(); +bool user_writable_module_present(); +bool module_has_rwx_section(HMODULE mod); +bool core_modules_signed(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1776,6 +1783,72 @@ static std::wstring to_lower_ws(std::wstring value) return value; } +static bool path_has_any(const std::wstring& p, const std::initializer_list& needles) +{ + for (const auto& n : needles) { + if (p.find(n) != std::wstring::npos) + return true; + } + return false; +} + +bool module_has_rwx_section(HMODULE mod) +{ + if (!mod) + return false; + auto base = reinterpret_cast(mod); + auto dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return false; + auto nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + return false; + auto section = IMAGE_FIRST_SECTION(nt); + for (unsigned i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) { + const auto ch = section->Characteristics; + if ((ch & IMAGE_SCN_MEM_EXECUTE) && (ch & IMAGE_SCN_MEM_WRITE)) + return true; + } + return false; +} + +static bool verify_signature(const std::wstring& path) +{ + WINTRUST_FILE_INFO file_info{}; + file_info.cbStruct = sizeof(file_info); + file_info.pcwszFilePath = path.c_str(); + + WINTRUST_DATA trust_data{}; + trust_data.cbStruct = sizeof(trust_data); + trust_data.dwUIChoice = WTD_UI_NONE; + trust_data.fdwRevocationChecks = WTD_REVOKE_NONE; + trust_data.dwUnionChoice = WTD_CHOICE_FILE; + trust_data.pFile = &file_info; + trust_data.dwStateAction = WTD_STATEACTION_IGNORE; + + GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2; + LONG status = WinVerifyTrust(nullptr, &policy, &trust_data); + return status == ERROR_SUCCESS; +} + +bool core_modules_signed() +{ + const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; + for (const auto* name : kModules) { + HMODULE mod = GetModuleHandleW(name); + if (!mod) + return false; + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameW(mod, path, MAX_PATH)) + return false; + if (!verify_signature(path)) + return false; + if (module_has_rwx_section(mod)) + return false; + } + return true; +} + bool module_paths_ok() { 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() return false; } +bool user_writable_module_present() +{ + HMODULE mods[1024] = {}; + DWORD needed = 0; + if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) + return false; + + wchar_t exe_path[MAX_PATH] = {}; + GetModuleFileNameW(nullptr, exe_path, MAX_PATH); + std::wstring exe_dir = exe_path; + const auto last_slash = exe_dir.find_last_of(L"\\/"); + if (last_slash != std::wstring::npos) + exe_dir = exe_dir.substr(0, last_slash + 1); + exe_dir = to_lower_ws(exe_dir); + + const size_t count = needed / sizeof(HMODULE); + for (size_t i = 0; i < count; ++i) { + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) + continue; + std::wstring p = to_lower_ws(path); + if (p.rfind(exe_dir, 0) == 0) + continue; + + if (path_has_any(p, { L"\\temp\\", L"\\appdata\\local\\temp\\", L"\\downloads\\" })) + return true; + } + return false; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2220,7 +2323,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { error(XorStr("module path check failed, possible side-load detected.")); } } @@ -2252,7 +2355,7 @@ void integrity_watchdog() { const auto last_mod = last_module_check.load(); if (now - last_mod > 120) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { error(XorStr("module path check failed, possible side-load detected.")); } } From 6302206843da969b35a9d02569d9c7f0fe3a7290 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:04:47 -0500 Subject: [PATCH 10/83] Add hypervisor detection --- auth.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/auth.cpp b/auth.cpp index 87ebbdd..0f6feda 100644 --- a/auth.cpp +++ b/auth.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,7 @@ bool duplicate_system_modules_present(); bool user_writable_module_present(); bool module_has_rwx_section(HMODULE mod); bool core_modules_signed(); +bool hypervisor_present(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1937,6 +1939,56 @@ bool user_writable_module_present() return false; } +static bool reg_key_exists(HKEY root, const wchar_t* path) +{ + HKEY h = nullptr; + const LONG res = RegOpenKeyExW(root, path, 0, KEY_READ, &h); + if (res == ERROR_SUCCESS) { + RegCloseKey(h); + return true; + } + return false; +} + +static bool file_exists(const std::wstring& path) +{ + const DWORD attr = GetFileAttributesW(path.c_str()); + return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY); +} + +bool hypervisor_present() +{ + int cpu_info[4] = {}; + __cpuid(cpu_info, 1); + const bool hv_bit = (cpu_info[2] & (1 << 31)) != 0; + if (hv_bit) { + return true; + } + + // registry artifacts (conservative) + if (reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\VBOX__") || + reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\VMWARE") || + reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\XEN") || + reg_key_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\VMware, Inc.\\VMware Tools") || + reg_key_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Oracle\\VirtualBox Guest Additions")) { + return true; + } + + // file artifacts (drivers/tools) + if (file_exists(L"C:\\Windows\\System32\\drivers\\VBoxGuest.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\VBoxMouse.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\VBoxSF.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\VBoxVideo.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\vmhgfs.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\vmmouse.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\vm3dmp.sys") || + file_exists(L"C:\\Windows\\System32\\drivers\\xen.sys")) { + return true; + } + + return false; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2323,7 +2375,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present()) { error(XorStr("module path check failed, possible side-load detected.")); } } @@ -2355,7 +2407,7 @@ void integrity_watchdog() { const auto last_mod = last_module_check.load(); if (now - last_mod > 120) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present()) { error(XorStr("module path check failed, possible side-load detected.")); } } From f16bfae2964a19a1cfac36bc8eb221796c81f152 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:12:39 -0500 Subject: [PATCH 11/83] Add prologue integrity checks --- auth.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/auth.cpp b/auth.cpp index 0f6feda..b1c8e64 100644 --- a/auth.cpp +++ b/auth.cpp @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include #include @@ -92,6 +94,8 @@ bool user_writable_module_present(); bool module_has_rwx_section(HMODULE mod); bool core_modules_signed(); bool hypervisor_present(); +void snapshot_prologues(); +bool prologues_ok(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -103,11 +107,16 @@ std::atomic LoggedIn(false); std::atomic last_integrity_check{ 0 }; std::atomic integrity_fail_streak{ 0 }; std::atomic last_module_check{ 0 }; +std::atomic prologues_ready{ false }; +std::array pro_req{}; +std::array pro_verify{}; +std::array pro_checkinit{}; void KeyAuth::api::init() { std::thread(runChecks).detach(); std::thread(integrity_watchdog).detach(); + snapshot_prologues(); seed = generate_random_number(); std::atexit([]() { cleanUpSeedData(seed); }); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)modify, 0, 0, 0); @@ -1989,6 +1998,31 @@ bool hypervisor_present() return false; } +void snapshot_prologues() +{ + if (prologues_ready.load()) + return; + const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); + const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); + const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); + std::memcpy(pro_req.data(), req_ptr, pro_req.size()); + std::memcpy(pro_verify.data(), verify_ptr, pro_verify.size()); + std::memcpy(pro_checkinit.data(), check_ptr, pro_checkinit.size()); + prologues_ready.store(true); +} + +bool prologues_ok() +{ + if (!prologues_ready.load()) + return true; + const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); + const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); + const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); + return std::memcmp(pro_req.data(), req_ptr, pro_req.size()) == 0 && + std::memcmp(pro_verify.data(), verify_ptr, pro_verify.size()) == 0 && + std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2379,6 +2413,9 @@ void checkInit() { error(XorStr("module path check failed, possible side-load detected.")); } } + if (!prologues_ok()) { + error(XorStr("function prologue check failed, possible inline hook detected.")); + } integrity_check(); } @@ -2411,6 +2448,9 @@ void integrity_watchdog() { error(XorStr("module path check failed, possible side-load detected.")); } } + if (!prologues_ok()) { + error(XorStr("function prologue check failed, possible inline hook detected.")); + } if (check_section_integrity(XorStr(".text").c_str(), false)) { const int streak = integrity_fail_streak.fetch_add(1) + 1; if (streak >= 2) { From e5e9dd7c2863867d3127f79b26d633fdc9ed3364 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:27:13 -0500 Subject: [PATCH 12/83] Add function region checks --- auth.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/auth.cpp b/auth.cpp index b1c8e64..f6c7d82 100644 --- a/auth.cpp +++ b/auth.cpp @@ -96,6 +96,7 @@ bool core_modules_signed(); bool hypervisor_present(); void snapshot_prologues(); bool prologues_ok(); +bool func_region_ok(const void* addr); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -2023,6 +2024,21 @@ bool prologues_ok() std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0; } +bool func_region_ok(const void* addr) +{ + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(addr, &mbi, sizeof(mbi)) == 0) + return false; + if (mbi.Type != MEM_IMAGE) + return false; + const DWORD prot = mbi.Protect; + const bool exec = (prot & PAGE_EXECUTE) || (prot & PAGE_EXECUTE_READ) || (prot & PAGE_EXECUTE_READWRITE) || (prot & PAGE_EXECUTE_WRITECOPY); + const bool write = (prot & PAGE_READWRITE) || (prot & PAGE_EXECUTE_READWRITE) || (prot & PAGE_WRITECOPY) || (prot & PAGE_EXECUTE_WRITECOPY); + if (!exec || write) + return false; + return true; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2416,6 +2432,11 @@ void checkInit() { if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } + if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || + !func_region_ok(reinterpret_cast(&VerifyPayload)) || + !func_region_ok(reinterpret_cast(&checkInit))) { + error(XorStr("function region check failed, possible hook detected.")); + } integrity_check(); } @@ -2451,6 +2472,11 @@ void integrity_watchdog() { if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } + if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || + !func_region_ok(reinterpret_cast(&VerifyPayload)) || + !func_region_ok(reinterpret_cast(&checkInit))) { + error(XorStr("function region check failed, possible hook detected.")); + } if (check_section_integrity(XorStr(".text").c_str(), false)) { const int streak = integrity_fail_streak.fetch_add(1) + 1; if (streak >= 2) { From da1ce7e382f01ed256bc4c231bd30023cc796968 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:32:31 -0500 Subject: [PATCH 13/83] Expand prologue and region checks --- auth.cpp | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index f6c7d82..78c551e 100644 --- a/auth.cpp +++ b/auth.cpp @@ -112,6 +112,10 @@ std::atomic prologues_ready{ false }; std::array pro_req{}; std::array pro_verify{}; std::array pro_checkinit{}; +std::array pro_error{}; +std::array pro_integrity{}; +std::array pro_watchdog{}; +std::array pro_section{}; void KeyAuth::api::init() { @@ -2006,9 +2010,17 @@ void snapshot_prologues() const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); + const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); + const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); + const auto watch_ptr = reinterpret_cast(reinterpret_cast(&integrity_watchdog)); + const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); std::memcpy(pro_req.data(), req_ptr, pro_req.size()); std::memcpy(pro_verify.data(), verify_ptr, pro_verify.size()); std::memcpy(pro_checkinit.data(), check_ptr, pro_checkinit.size()); + std::memcpy(pro_error.data(), error_ptr, pro_error.size()); + std::memcpy(pro_integrity.data(), integ_ptr, pro_integrity.size()); + std::memcpy(pro_watchdog.data(), watch_ptr, pro_watchdog.size()); + std::memcpy(pro_section.data(), section_ptr, pro_section.size()); prologues_ready.store(true); } @@ -2019,9 +2031,17 @@ bool prologues_ok() const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); + const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); + const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); + const auto watch_ptr = reinterpret_cast(reinterpret_cast(&integrity_watchdog)); + const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); return std::memcmp(pro_req.data(), req_ptr, pro_req.size()) == 0 && std::memcmp(pro_verify.data(), verify_ptr, pro_verify.size()) == 0 && - std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0; + std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0 && + std::memcmp(pro_error.data(), error_ptr, pro_error.size()) == 0 && + std::memcmp(pro_integrity.data(), integ_ptr, pro_integrity.size()) == 0 && + std::memcmp(pro_watchdog.data(), watch_ptr, pro_watchdog.size()) == 0 && + std::memcmp(pro_section.data(), section_ptr, pro_section.size()) == 0; } bool func_region_ok(const void* addr) @@ -2434,7 +2454,11 @@ void checkInit() { } if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || !func_region_ok(reinterpret_cast(&VerifyPayload)) || - !func_region_ok(reinterpret_cast(&checkInit))) { + !func_region_ok(reinterpret_cast(&checkInit)) || + !func_region_ok(reinterpret_cast(&error)) || + !func_region_ok(reinterpret_cast(&integrity_check)) || + !func_region_ok(reinterpret_cast(&integrity_watchdog)) || + !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } integrity_check(); @@ -2474,7 +2498,11 @@ void integrity_watchdog() { } if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || !func_region_ok(reinterpret_cast(&VerifyPayload)) || - !func_region_ok(reinterpret_cast(&checkInit))) { + !func_region_ok(reinterpret_cast(&checkInit)) || + !func_region_ok(reinterpret_cast(&error)) || + !func_region_ok(reinterpret_cast(&integrity_check)) || + !func_region_ok(reinterpret_cast(&integrity_watchdog)) || + !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } if (check_section_integrity(XorStr(".text").c_str(), false)) { From 4ca1d301c4ae80969608a33055397220729660f6 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:35:34 -0500 Subject: [PATCH 14/83] Remove integrity watchdog --- auth.cpp | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/auth.cpp b/auth.cpp index 78c551e..f16f0ec 100644 --- a/auth.cpp +++ b/auth.cpp @@ -85,7 +85,6 @@ std::string generate_random_number(); std::string curl_escape(CURL* curl, const std::string& input); auto check_section_integrity( const char *section_name, bool fix ) -> bool; void integrity_check(); -void integrity_watchdog(); std::string extract_host(const std::string& url); bool hosts_override_present(const std::string& host); bool module_paths_ok(); @@ -114,13 +113,11 @@ std::array pro_verify{}; std::array pro_checkinit{}; std::array pro_error{}; std::array pro_integrity{}; -std::array pro_watchdog{}; std::array pro_section{}; void KeyAuth::api::init() { std::thread(runChecks).detach(); - std::thread(integrity_watchdog).detach(); snapshot_prologues(); seed = generate_random_number(); std::atexit([]() { cleanUpSeedData(seed); }); @@ -2012,14 +2009,12 @@ void snapshot_prologues() const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); - const auto watch_ptr = reinterpret_cast(reinterpret_cast(&integrity_watchdog)); const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); std::memcpy(pro_req.data(), req_ptr, pro_req.size()); std::memcpy(pro_verify.data(), verify_ptr, pro_verify.size()); std::memcpy(pro_checkinit.data(), check_ptr, pro_checkinit.size()); std::memcpy(pro_error.data(), error_ptr, pro_error.size()); std::memcpy(pro_integrity.data(), integ_ptr, pro_integrity.size()); - std::memcpy(pro_watchdog.data(), watch_ptr, pro_watchdog.size()); std::memcpy(pro_section.data(), section_ptr, pro_section.size()); prologues_ready.store(true); } @@ -2033,14 +2028,12 @@ bool prologues_ok() const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); - const auto watch_ptr = reinterpret_cast(reinterpret_cast(&integrity_watchdog)); const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); return std::memcmp(pro_req.data(), req_ptr, pro_req.size()) == 0 && std::memcmp(pro_verify.data(), verify_ptr, pro_verify.size()) == 0 && std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0 && std::memcmp(pro_error.data(), error_ptr, pro_error.size()) == 0 && std::memcmp(pro_integrity.data(), integ_ptr, pro_integrity.size()) == 0 && - std::memcmp(pro_watchdog.data(), watch_ptr, pro_watchdog.size()) == 0 && std::memcmp(pro_section.data(), section_ptr, pro_section.size()) == 0; } @@ -2457,7 +2450,6 @@ void checkInit() { !func_region_ok(reinterpret_cast(&checkInit)) || !func_region_ok(reinterpret_cast(&error)) || !func_region_ok(reinterpret_cast(&integrity_check)) || - !func_region_ok(reinterpret_cast(&integrity_watchdog)) || !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } @@ -2476,45 +2468,6 @@ void integrity_check() { } } -void integrity_watchdog() { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution sleep_seconds(20, 50); - while (true) { - Sleep(static_cast(sleep_seconds(gen) * 1000)); - if (!initialized || !LoggedIn.load()) - continue; - const auto now = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - const auto last_mod = last_module_check.load(); - if (now - last_mod > 120) { - last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present()) { - error(XorStr("module path check failed, possible side-load detected.")); - } - } - if (!prologues_ok()) { - error(XorStr("function prologue check failed, possible inline hook detected.")); - } - if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || - !func_region_ok(reinterpret_cast(&VerifyPayload)) || - !func_region_ok(reinterpret_cast(&checkInit)) || - !func_region_ok(reinterpret_cast(&error)) || - !func_region_ok(reinterpret_cast(&integrity_check)) || - !func_region_ok(reinterpret_cast(&integrity_watchdog)) || - !func_region_ok(reinterpret_cast(&check_section_integrity))) { - error(XorStr("function region check failed, possible hook detected.")); - } - if (check_section_integrity(XorStr(".text").c_str(), false)) { - const int streak = integrity_fail_streak.fetch_add(1) + 1; - if (streak >= 2) { - error(XorStr("check_section_integrity() failed, don't tamper with the program.")); - } - } else { - integrity_fail_streak.store(0); - } - } -} // code submitted in pull request from https://github.com/BINM7MD BOOL bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { From ed5ab73c38f2ccf18f82e2bd512cbb9ff58c06ae Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:37:42 -0500 Subject: [PATCH 15/83] Add lightweight periodic checks --- auth.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/auth.cpp b/auth.cpp index f16f0ec..e36118a 100644 --- a/auth.cpp +++ b/auth.cpp @@ -107,6 +107,7 @@ std::atomic LoggedIn(false); std::atomic last_integrity_check{ 0 }; std::atomic integrity_fail_streak{ 0 }; std::atomic last_module_check{ 0 }; +std::atomic last_periodic_check{ 0 }; std::atomic prologues_ready{ false }; std::array pro_req{}; std::array pro_verify{}; @@ -2442,6 +2443,29 @@ void checkInit() { error(XorStr("module path check failed, possible side-load detected.")); } } + const auto last_periodic = last_periodic_check.load(); + if (now - last_periodic > 30) { + last_periodic_check.store(now); + if (!prologues_ok()) { + error(XorStr("function prologue check failed, possible inline hook detected.")); + } + if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || + !func_region_ok(reinterpret_cast(&VerifyPayload)) || + !func_region_ok(reinterpret_cast(&checkInit)) || + !func_region_ok(reinterpret_cast(&error)) || + !func_region_ok(reinterpret_cast(&integrity_check)) || + !func_region_ok(reinterpret_cast(&check_section_integrity))) { + error(XorStr("function region check failed, possible hook detected.")); + } + if (check_section_integrity(XorStr(".text").c_str(), false)) { + const int streak = integrity_fail_streak.fetch_add(1) + 1; + if (streak >= 2) { + error(XorStr("check_section_integrity() failed, don't tamper with the program.")); + } + } else { + integrity_fail_streak.store(0); + } + } if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } From 1e6b9e2092ab8b60e5707541adf56cd51a29d0b9 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:38:35 -0500 Subject: [PATCH 16/83] Add timing anomaly check --- auth.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/auth.cpp b/auth.cpp index e36118a..58986d7 100644 --- a/auth.cpp +++ b/auth.cpp @@ -96,6 +96,7 @@ bool hypervisor_present(); void snapshot_prologues(); bool prologues_ok(); bool func_region_ok(const void* addr); +bool timing_anomaly_detected(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -2053,6 +2054,23 @@ bool func_region_ok(const void* addr) return true; } +bool timing_anomaly_detected() +{ + const auto wall_now = std::chrono::system_clock::now(); + const auto steady_now = std::chrono::steady_clock::now(); + static auto wall_last = wall_now; + static auto steady_last = steady_now; + const auto wall_delta = std::chrono::duration_cast(wall_now - wall_last).count(); + const auto steady_delta = std::chrono::duration_cast(steady_now - steady_last).count(); + wall_last = wall_now; + steady_last = steady_now; + if (wall_delta < -60 || wall_delta > 300) + return true; + if (std::llabs(wall_delta - steady_delta) > 120) + return true; + return false; +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } @@ -2446,6 +2464,9 @@ void checkInit() { const auto last_periodic = last_periodic_check.load(); if (now - last_periodic > 30) { last_periodic_check.store(now); + if (timing_anomaly_detected()) { + error(XorStr("timing anomaly detected, possible time tamper.")); + } if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } From 835e0a2a6761fd6272bce76b7e77091a0a525298 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sat, 28 Feb 2026 22:40:26 -0500 Subject: [PATCH 17/83] Tighten request buffer handling --- auth.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/auth.cpp b/auth.cpp index 58986d7..e69735c 100644 --- a/auth.cpp +++ b/auth.cpp @@ -681,9 +681,9 @@ void KeyAuth::api::web_login() HTTP_SET_NULL_ID(&requestId); int bufferSize = 4096; int requestSize = sizeof(HTTP_REQUEST) + bufferSize; - BYTE* buffer = new BYTE[requestSize]; - PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer; - RtlZeroMemory(buffer, requestSize); + auto buffer = std::make_unique(requestSize); + PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer.get(); + RtlZeroMemory(buffer.get(), requestSize); ULONG bytesReturned; result = HttpReceiveHttpRequest( requestQueueHandle, @@ -746,7 +746,6 @@ void KeyAuth::api::web_login() NULL ); - delete[]buffer; continue; } @@ -858,8 +857,6 @@ void KeyAuth::api::web_login() going = false; } - delete[]buffer; - if (!success) LI_FN(exit)(0); } @@ -921,9 +918,9 @@ void KeyAuth::api::button(std::string button) HTTP_SET_NULL_ID(&requestId); int bufferSize = 4096; int requestSize = sizeof(HTTP_REQUEST) + bufferSize; - BYTE* buffer = new BYTE[requestSize]; - PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer; - RtlZeroMemory(buffer, requestSize); + auto buffer = std::make_unique(requestSize); + PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer.get(); + RtlZeroMemory(buffer.get(), requestSize); ULONG bytesReturned; result = HttpReceiveHttpRequest( requestQueueHandle, @@ -986,7 +983,6 @@ void KeyAuth::api::button(std::string button) NULL ); - delete[]buffer; } } From 763a70e0a8678d3099c04018d03c2441d3711b1e Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:05:34 -0500 Subject: [PATCH 18/83] Add session heartbeat --- auth.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/auth.cpp b/auth.cpp index e69735c..662662c 100644 --- a/auth.cpp +++ b/auth.cpp @@ -97,6 +97,8 @@ void snapshot_prologues(); bool prologues_ok(); bool func_region_ok(const void* addr); bool timing_anomaly_detected(); +void start_heartbeat(KeyAuth::api* instance); +void heartbeat_thread(KeyAuth::api* instance); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -110,6 +112,7 @@ std::atomic integrity_fail_streak{ 0 }; std::atomic last_module_check{ 0 }; std::atomic last_periodic_check{ 0 }; std::atomic prologues_ready{ false }; +std::atomic heartbeat_started{ false }; std::array pro_req{}; std::array pro_verify{}; std::array pro_checkinit{}; @@ -350,6 +353,9 @@ void KeyAuth::api::login(std::string username, std::string password, std::string LI_FN(GlobalAddAtomA)(ownerid.c_str()); LoggedIn.store(true); + start_heartbeat(this); + start_heartbeat(this); + start_heartbeat(this); } else { LI_FN(exit)(12); @@ -798,6 +804,7 @@ void KeyAuth::api::web_login() LI_FN(GlobalAddAtomA)(ownerid.c_str()); LoggedIn.store(true); + start_heartbeat(this); } else { LI_FN(exit)(12); @@ -2067,6 +2074,29 @@ bool timing_anomaly_detected() return false; } +void heartbeat_thread(KeyAuth::api* instance) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution sleep_seconds(45, 90); + while (true) { + Sleep(static_cast(sleep_seconds(gen) * 1000)); + if (!LoggedIn.load()) + continue; + instance->check(false); + if (!instance->response.success) { + error(XorStr("session check failed.")); + } + } +} + +void start_heartbeat(KeyAuth::api* instance) +{ + if (heartbeat_started.exchange(true)) + return; + std::thread(heartbeat_thread, instance).detach(); +} + void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } From 89b90572b23835d64c04e290e2bad2a42c302b9f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:10:03 -0500 Subject: [PATCH 19/83] Add pre-request guard checks --- auth.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/auth.cpp b/auth.cpp index 662662c..7e22f43 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1644,6 +1644,9 @@ void KeyAuth::api::logout() { int VerifyPayload(std::string signature, std::string timestamp, std::string body) { + if (!prologues_ok()) { + error(XorStr("function prologue check failed, possible inline hook detected.")); + } integrity_check(); long long unix_timestamp = 0; try { @@ -2105,6 +2108,17 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { signature.clear(); signatureTimestamp.clear(); integrity_check(); + if (!prologues_ok()) { + error(XorStr("function prologue check failed, possible inline hook detected.")); + } + if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || + !func_region_ok(reinterpret_cast(&VerifyPayload)) || + !func_region_ok(reinterpret_cast(&checkInit)) || + !func_region_ok(reinterpret_cast(&error)) || + !func_region_ok(reinterpret_cast(&integrity_check)) || + !func_region_ok(reinterpret_cast(&check_section_integrity))) { + error(XorStr("function region check failed, possible hook detected.")); + } const auto host = extract_host(url); if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); From d0e4c008ca726afaef714522903267b190b8f4a3 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:15:48 -0500 Subject: [PATCH 20/83] Add detour and text hash checks --- auth.cpp | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/auth.cpp b/auth.cpp index 7e22f43..d248688 100644 --- a/auth.cpp +++ b/auth.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,10 @@ bool func_region_ok(const void* addr); bool timing_anomaly_detected(); void start_heartbeat(KeyAuth::api* instance); void heartbeat_thread(KeyAuth::api* instance); +void snapshot_text_hashes(); +bool text_hashes_ok(); +bool detour_suspect(const uint8_t* p); +bool import_addresses_ok(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -119,6 +124,9 @@ std::array pro_checkinit{}; std::array pro_error{}; std::array pro_integrity{}; std::array pro_section{}; +std::atomic text_hashes_ready{ false }; +struct TextHash { size_t offset; size_t len; uint32_t hash; }; +std::vector text_hashes; void KeyAuth::api::init() { @@ -1647,6 +1655,18 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } + if (!import_addresses_ok()) { + error(XorStr("import address check failed.")); + } + if (!text_hashes_ok()) { + error(XorStr("text section hash check failed.")); + } + if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || + detour_suspect(reinterpret_cast(&VerifyPayload)) || + detour_suspect(reinterpret_cast(&checkInit)) || + detour_suspect(reinterpret_cast(&error))) { + error(XorStr("detour pattern detected.")); + } integrity_check(); long long unix_timestamp = 0; try { @@ -2025,6 +2045,7 @@ void snapshot_prologues() std::memcpy(pro_integrity.data(), integ_ptr, pro_integrity.size()); std::memcpy(pro_section.data(), section_ptr, pro_section.size()); prologues_ready.store(true); + snapshot_text_hashes(); } bool prologues_ok() @@ -2077,6 +2098,118 @@ bool timing_anomaly_detected() return false; } +static bool get_text_section_info(std::uintptr_t& base, size_t& size) +{ + const auto hmodule = GetModuleHandle(nullptr); + if (!hmodule) return false; + const auto base_0 = reinterpret_cast(hmodule); + const auto dos = reinterpret_cast(base_0); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; + const auto nt = reinterpret_cast(base_0 + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) return false; + auto section = IMAGE_FIRST_SECTION(nt); + for (auto i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) { + if (std::memcmp(section->Name, ".text", 5) == 0) { + base = base_0 + section->VirtualAddress; + size = section->Misc.VirtualSize; + return true; + } + } + return false; +} + +static uint32_t fnv1a(const uint8_t* data, size_t len) +{ + uint32_t hash = 2166136261u; + for (size_t i = 0; i < len; ++i) { + hash ^= data[i]; + hash *= 16777619u; + } + return hash; +} + +void snapshot_text_hashes() +{ + if (text_hashes_ready.load()) + return; + std::uintptr_t base = 0; + size_t size = 0; + if (!get_text_section_info(base, size) || size < 256) + return; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, size - 64); + text_hashes.clear(); + for (int i = 0; i < 8; ++i) { + const size_t offset = dist(gen); + const uint8_t* ptr = reinterpret_cast(base + offset); + text_hashes.push_back({ offset, 64, fnv1a(ptr, 64) }); + } + text_hashes_ready.store(true); +} + +bool text_hashes_ok() +{ + if (!text_hashes_ready.load()) + return true; + std::uintptr_t base = 0; + size_t size = 0; + if (!get_text_section_info(base, size)) + return true; + for (const auto& h : text_hashes) { + if (h.offset + h.len > size) + return false; + const uint8_t* ptr = reinterpret_cast(base + h.offset); + if (fnv1a(ptr, h.len) != h.hash) + return false; + } + return true; +} + +bool detour_suspect(const uint8_t* p) +{ + if (!p) + return true; + // jmp rel32 / call rel32 / jmp rel8 + if (p[0] == 0xE9 || p[0] == 0xE8 || p[0] == 0xEB) + return true; + // jmp/call [rip+imm32] + if (p[0] == 0xFF && (p[1] == 0x25 || p[1] == 0x15)) + return true; + // mov rax, imm64; jmp rax + if (p[0] == 0x48 && p[1] == 0xB8 && p[10] == 0xFF && p[11] == 0xE0) + return true; + return false; +} + +static bool addr_in_module(const void* addr, const wchar_t* module_name) +{ + HMODULE mod = module_name ? GetModuleHandleW(module_name) : GetModuleHandle(nullptr); + if (!mod) + return false; + MODULEINFO mi{}; + if (!GetModuleInformation(GetCurrentProcess(), mod, &mi, sizeof(mi))) + return false; + const auto base = reinterpret_cast(mi.lpBaseOfDll); + const auto end = base + mi.SizeOfImage; + return addr >= base && addr < end; +} + +bool import_addresses_ok() +{ + // wintrust functions should resolve inside wintrust.dll + if (!addr_in_module(reinterpret_cast(&WinVerifyTrust), L"wintrust.dll")) + return false; + // VirtualQuery should be inside kernelbase/kernel32 + if (!addr_in_module(reinterpret_cast(&VirtualQuery), L"kernelbase.dll") && + !addr_in_module(reinterpret_cast(&VirtualQuery), L"kernel32.dll")) + return false; + // curl functions should live in main module when statically linked + if (!addr_in_module(reinterpret_cast(&curl_easy_perform), nullptr)) + return false; + return true; +} + void heartbeat_thread(KeyAuth::api* instance) { std::random_device rd; @@ -2119,6 +2252,18 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } + if (!import_addresses_ok()) { + error(XorStr("import address check failed.")); + } + if (!text_hashes_ok()) { + error(XorStr("text section hash check failed.")); + } + if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || + detour_suspect(reinterpret_cast(&VerifyPayload)) || + detour_suspect(reinterpret_cast(&checkInit)) || + detour_suspect(reinterpret_cast(&error))) { + error(XorStr("detour pattern detected.")); + } const auto host = extract_host(url); if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); @@ -2507,6 +2652,18 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } + if (!text_hashes_ok()) { + error(XorStr("text section hash check failed.")); + } + if (!import_addresses_ok()) { + error(XorStr("import address check failed.")); + } + if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || + detour_suspect(reinterpret_cast(&VerifyPayload)) || + detour_suspect(reinterpret_cast(&checkInit)) || + detour_suspect(reinterpret_cast(&error))) { + error(XorStr("detour pattern detected.")); + } if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } From b5b151f05181beec7e04d2542c61d42b1cd81c7f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:23:27 -0500 Subject: [PATCH 21/83] Add text protection and PE header checks --- auth.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/auth.cpp b/auth.cpp index d248688..b97d9c8 100644 --- a/auth.cpp +++ b/auth.cpp @@ -104,6 +104,10 @@ void snapshot_text_hashes(); bool text_hashes_ok(); bool detour_suspect(const uint8_t* p); bool import_addresses_ok(); +void snapshot_text_page_protections(); +bool text_page_protections_ok(); +void snapshot_pe_header(); +bool pe_header_ok(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -127,6 +131,10 @@ std::array pro_section{}; std::atomic text_hashes_ready{ false }; struct TextHash { size_t offset; size_t len; uint32_t hash; }; std::vector text_hashes; +std::atomic text_prot_ready{ false }; +std::vector> text_protections; +std::atomic pe_header_ready{ false }; +uint32_t pe_header_hash = 0; void KeyAuth::api::init() { @@ -1661,6 +1669,12 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } + if (!text_page_protections_ok()) { + error(XorStr("text page protection check failed.")); + } + if (!pe_header_ok()) { + error(XorStr("pe header check failed.")); + } if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || detour_suspect(reinterpret_cast(&VerifyPayload)) || detour_suspect(reinterpret_cast(&checkInit)) || @@ -2046,6 +2060,8 @@ void snapshot_prologues() std::memcpy(pro_section.data(), section_ptr, pro_section.size()); prologues_ready.store(true); snapshot_text_hashes(); + snapshot_text_page_protections(); + snapshot_pe_header(); } bool prologues_ok() @@ -2166,6 +2182,85 @@ bool text_hashes_ok() return true; } +void snapshot_text_page_protections() +{ + if (text_prot_ready.load()) + return; + std::uintptr_t base = 0; + size_t size = 0; + if (!get_text_section_info(base, size)) + return; + text_protections.clear(); + const size_t page = 0x1000; + for (size_t off = 0; off < size; off += page) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(base + off), &mbi, sizeof(mbi)) == 0) + continue; + text_protections.emplace_back(reinterpret_cast(mbi.BaseAddress), mbi.Protect); + } + text_prot_ready.store(true); +} + +bool text_page_protections_ok() +{ + if (!text_prot_ready.load()) + return true; + for (const auto& entry : text_protections) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(entry.first), &mbi, sizeof(mbi)) == 0) + return false; + const DWORD prot = mbi.Protect; + if (prot != entry.second) + return false; + const bool exec = (prot & PAGE_EXECUTE) || (prot & PAGE_EXECUTE_READ) || (prot & PAGE_EXECUTE_READWRITE) || (prot & PAGE_EXECUTE_WRITECOPY); + const bool write = (prot & PAGE_READWRITE) || (prot & PAGE_EXECUTE_READWRITE) || (prot & PAGE_WRITECOPY) || (prot & PAGE_EXECUTE_WRITECOPY); + if (!exec || write) + return false; + } + return true; +} + +void snapshot_pe_header() +{ + if (pe_header_ready.load()) + return; + const auto hmodule = GetModuleHandle(nullptr); + if (!hmodule) + return; + const auto base = reinterpret_cast(hmodule); + const auto dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return; + const auto nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + return; + const size_t header_size = nt->OptionalHeader.SizeOfHeaders; + if (header_size == 0 || header_size > 0x4000) + return; + pe_header_hash = fnv1a(reinterpret_cast(base), header_size); + pe_header_ready.store(true); +} + +bool pe_header_ok() +{ + if (!pe_header_ready.load()) + return true; + const auto hmodule = GetModuleHandle(nullptr); + if (!hmodule) + return true; + const auto base = reinterpret_cast(hmodule); + const auto dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return false; + const auto nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + return false; + const size_t header_size = nt->OptionalHeader.SizeOfHeaders; + if (header_size == 0 || header_size > 0x4000) + return false; + return fnv1a(reinterpret_cast(base), header_size) == pe_header_hash; +} + bool detour_suspect(const uint8_t* p) { if (!p) @@ -2258,6 +2353,12 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } + if (!text_page_protections_ok()) { + error(XorStr("text page protection check failed.")); + } + if (!pe_header_ok()) { + error(XorStr("pe header check failed.")); + } if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || detour_suspect(reinterpret_cast(&VerifyPayload)) || detour_suspect(reinterpret_cast(&checkInit)) || @@ -2655,6 +2756,12 @@ void checkInit() { if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } + if (!text_page_protections_ok()) { + error(XorStr("text page protection check failed.")); + } + if (!pe_header_ok()) { + error(XorStr("pe header check failed.")); + } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } From e5647bf5f99576e589af70bc27ad92306e1c1bd1 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:45:39 -0500 Subject: [PATCH 22/83] Add VirtualProtect IAT and module allowlist checks --- auth.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index b97d9c8..ee3ef09 100644 --- a/auth.cpp +++ b/auth.cpp @@ -108,6 +108,8 @@ void snapshot_text_page_protections(); bool text_page_protections_ok(); void snapshot_pe_header(); bool pe_header_ok(); +bool iat_virtualprotect_ok(); +bool module_allowlist_ok(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1663,6 +1665,9 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2305,6 +2310,84 @@ bool import_addresses_ok() return true; } +static bool iat_resolves_to(HMODULE module, const char* import_name, const void* target) +{ + if (!module) + return false; + auto base = reinterpret_cast(module); + auto dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return false; + auto nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + return false; + const auto& dir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (!dir.VirtualAddress) + return false; + auto desc = reinterpret_cast(base + dir.VirtualAddress); + for (; desc->Name; ++desc) { + const char* dll = reinterpret_cast(base + desc->Name); + if (!_stricmp(dll, "KERNEL32.DLL") && !_stricmp(dll, "KERNELBASE.DLL")) + continue; + auto thunk = reinterpret_cast(base + desc->FirstThunk); + auto orig = reinterpret_cast(base + desc->OriginalFirstThunk); + for (; orig->u1.AddressOfData; ++orig, ++thunk) { + if (orig->u1.Ordinal & IMAGE_ORDINAL_FLAG) + continue; + auto import = reinterpret_cast(base + orig->u1.AddressOfData); + if (strcmp(reinterpret_cast(import->Name), import_name) == 0) { + return reinterpret_cast(thunk->u1.Function) == target; + } + } + } + return true; +} + +bool iat_virtualprotect_ok() +{ + HMODULE self = GetModuleHandle(nullptr); + FARPROC vp = GetProcAddress(GetModuleHandleW(L"kernelbase.dll"), "VirtualProtect"); + if (!vp) + vp = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "VirtualProtect"); + if (!vp) + return false; + if (!addr_in_module(reinterpret_cast(vp), L"kernelbase.dll") && + !addr_in_module(reinterpret_cast(vp), L"kernel32.dll")) + return false; + return iat_resolves_to(self, "VirtualProtect", reinterpret_cast(vp)); +} + +bool module_allowlist_ok() +{ + HMODULE mods[1024] = {}; + DWORD needed = 0; + if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) + return false; + wchar_t exe_path[MAX_PATH] = {}; + GetModuleFileNameW(nullptr, exe_path, MAX_PATH); + std::wstring exe_dir = exe_path; + const auto last_slash = exe_dir.find_last_of(L"\\/"); + if (last_slash != std::wstring::npos) + exe_dir = exe_dir.substr(0, last_slash + 1); + exe_dir = to_lower_ws(exe_dir); + const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); + std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; + std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); + std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + + const size_t count = needed / sizeof(HMODULE); + for (size_t i = 0; i < count; ++i) { + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) + continue; + std::wstring p = to_lower_ws(path); + if (p.rfind(sys32, 0) == 0 || p.rfind(syswow, 0) == 0 || p.rfind(exe_dir, 0) == 0) + continue; + return false; + } + return true; +} + void heartbeat_thread(KeyAuth::api* instance) { std::random_device rd; @@ -2347,6 +2430,9 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2743,7 +2829,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present() || !module_allowlist_ok()) { error(XorStr("module path check failed, possible side-load detected.")); } } @@ -2753,6 +2839,9 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } From 1390c8c67306d2f4951aa6376769430692e5c612 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:49:06 -0500 Subject: [PATCH 23/83] Use system directory APIs --- auth.cpp | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/auth.cpp b/auth.cpp index ee3ef09..85f76d3 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1912,10 +1912,8 @@ bool core_modules_signed() bool module_paths_ok() { const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); - std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; - std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); - std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); + std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); for (const auto* name : kModules) { HMODULE mod = GetModuleHandleW(name); @@ -1934,10 +1932,8 @@ bool module_paths_ok() bool duplicate_system_modules_present() { const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); - std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; - std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); - std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); + std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); HMODULE mods[1024] = {}; DWORD needed = 0; @@ -2014,6 +2010,22 @@ static bool file_exists(const std::wstring& path) return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY); } +static std::wstring get_system_dir() +{ + wchar_t buf[MAX_PATH] = {}; + if (GetSystemDirectoryW(buf, MAX_PATH) == 0) + return L"C:\\Windows\\System32"; + return buf; +} + +static std::wstring get_syswow_dir() +{ + wchar_t buf[MAX_PATH] = {}; + if (GetSystemWow64DirectoryW(buf, MAX_PATH) == 0) + return L"C:\\Windows\\SysWOW64"; + return buf; +} + bool hypervisor_present() { int cpu_info[4] = {}; @@ -2033,14 +2045,15 @@ bool hypervisor_present() } // file artifacts (drivers/tools) - if (file_exists(L"C:\\Windows\\System32\\drivers\\VBoxGuest.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\VBoxMouse.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\VBoxSF.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\VBoxVideo.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\vmhgfs.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\vmmouse.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\vm3dmp.sys") || - file_exists(L"C:\\Windows\\System32\\drivers\\xen.sys")) { + const auto sys32 = get_system_dir(); + if (file_exists(sys32 + L"\\drivers\\VBoxGuest.sys") || + file_exists(sys32 + L"\\drivers\\VBoxMouse.sys") || + file_exists(sys32 + L"\\drivers\\VBoxSF.sys") || + file_exists(sys32 + L"\\drivers\\VBoxVideo.sys") || + file_exists(sys32 + L"\\drivers\\vmhgfs.sys") || + file_exists(sys32 + L"\\drivers\\vmmouse.sys") || + file_exists(sys32 + L"\\drivers\\vm3dmp.sys") || + file_exists(sys32 + L"\\drivers\\xen.sys")) { return true; } @@ -2370,10 +2383,8 @@ bool module_allowlist_ok() if (last_slash != std::wstring::npos) exe_dir = exe_dir.substr(0, last_slash + 1); exe_dir = to_lower_ws(exe_dir); - const wchar_t* sysroot_env = _wgetenv(L"SystemRoot"); - std::wstring sysroot = sysroot_env ? sysroot_env : L"C:\\Windows"; - std::wstring sys32 = to_lower_ws(sysroot + L"\\System32\\"); - std::wstring syswow = to_lower_ws(sysroot + L"\\SysWOW64\\"); + std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); + std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); const size_t count = needed / sizeof(HMODULE); for (size_t i = 0; i < count; ++i) { From 35e60e3c221447b8590086d033af8631cbf4beb9 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:52:40 -0500 Subject: [PATCH 24/83] Remove hardcoded system paths --- auth.cpp | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/auth.cpp b/auth.cpp index 85f76d3..6d77bcd 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1912,8 +1912,10 @@ bool core_modules_signed() bool module_paths_ok() { const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); - std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); + std::wstring sys32 = get_system_dir(); + std::wstring syswow = get_syswow_dir(); + if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); + if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); for (const auto* name : kModules) { HMODULE mod = GetModuleHandleW(name); @@ -1932,8 +1934,10 @@ bool module_paths_ok() bool duplicate_system_modules_present() { const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); - std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); + std::wstring sys32 = get_system_dir(); + std::wstring syswow = get_syswow_dir(); + if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); + if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); HMODULE mods[1024] = {}; DWORD needed = 0; @@ -2014,16 +2018,16 @@ static std::wstring get_system_dir() { wchar_t buf[MAX_PATH] = {}; if (GetSystemDirectoryW(buf, MAX_PATH) == 0) - return L"C:\\Windows\\System32"; - return buf; + return L""; + return std::wstring(buf); } static std::wstring get_syswow_dir() { wchar_t buf[MAX_PATH] = {}; if (GetSystemWow64DirectoryW(buf, MAX_PATH) == 0) - return L"C:\\Windows\\SysWOW64"; - return buf; + return L""; + return std::wstring(buf); } bool hypervisor_present() @@ -2046,15 +2050,17 @@ bool hypervisor_present() // file artifacts (drivers/tools) const auto sys32 = get_system_dir(); - if (file_exists(sys32 + L"\\drivers\\VBoxGuest.sys") || - file_exists(sys32 + L"\\drivers\\VBoxMouse.sys") || - file_exists(sys32 + L"\\drivers\\VBoxSF.sys") || - file_exists(sys32 + L"\\drivers\\VBoxVideo.sys") || - file_exists(sys32 + L"\\drivers\\vmhgfs.sys") || - file_exists(sys32 + L"\\drivers\\vmmouse.sys") || - file_exists(sys32 + L"\\drivers\\vm3dmp.sys") || - file_exists(sys32 + L"\\drivers\\xen.sys")) { - return true; + if (!sys32.empty()) { + if (file_exists(sys32 + L"\\drivers\\VBoxGuest.sys") || + file_exists(sys32 + L"\\drivers\\VBoxMouse.sys") || + file_exists(sys32 + L"\\drivers\\VBoxSF.sys") || + file_exists(sys32 + L"\\drivers\\VBoxVideo.sys") || + file_exists(sys32 + L"\\drivers\\vmhgfs.sys") || + file_exists(sys32 + L"\\drivers\\vmmouse.sys") || + file_exists(sys32 + L"\\drivers\\vm3dmp.sys") || + file_exists(sys32 + L"\\drivers\\xen.sys")) { + return true; + } } return false; @@ -2383,8 +2389,10 @@ bool module_allowlist_ok() if (last_slash != std::wstring::npos) exe_dir = exe_dir.substr(0, last_slash + 1); exe_dir = to_lower_ws(exe_dir); - std::wstring sys32 = to_lower_ws(get_system_dir() + L"\\"); - std::wstring syswow = to_lower_ws(get_syswow_dir() + L"\\"); + std::wstring sys32 = get_system_dir(); + std::wstring syswow = get_syswow_dir(); + if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); + if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); const size_t count = needed / sizeof(HMODULE); for (size_t i = 0; i < count; ++i) { From 579a101bd56e702b00cdad4a87613bc8617a0b30 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:57:43 -0500 Subject: [PATCH 25/83] Obfuscate success key usage --- auth.cpp | 6 +++--- auth.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/auth.cpp b/auth.cpp index 6d77bcd..7cd1fd1 100644 --- a/auth.cpp +++ b/auth.cpp @@ -419,7 +419,7 @@ bool KeyAuth::api::chatsend(std::string message, std::string channel) auto response = req(data, url); auto json = response_decoder.parse(response); load_response_data(json); - return json[("success")]; + return json[XorStr("success")]; } void KeyAuth::api::changeUsername(std::string newusername) @@ -481,7 +481,7 @@ KeyAuth::api::Tfa& KeyAuth::api::enable2fa(std::string code) if (json.contains("2fa")) { - api::response.success = json["success"]; + api::response.success = json[XorStr("success")]; api::tfa.secret = json["2fa"]["secret_code"]; api::tfa.link = json["2fa"]["QRCode"]; } @@ -1333,7 +1333,7 @@ bool KeyAuth::api::checkblack() { size_t resultCode = hasher(json[(XorStr("code"))]); if (!json[(XorStr("success"))] || (json[(XorStr("success"))] && (resultCode == expectedHash))) { - return json[("success")]; + return json[XorStr("success")]; } LI_FN(exit)(9); } diff --git a/auth.hpp b/auth.hpp index 31e0920..7eaa5fd 100644 --- a/auth.hpp +++ b/auth.hpp @@ -150,7 +150,7 @@ namespace KeyAuth { } void load_channel_data(nlohmann::json data) { - api::response.success = data["success"]; // intentional. Possibly trick a reverse engineer into thinking this string is for login function + api::response.success = data[XorStr("success")]; // intentional. Possibly trick a reverse engineer into thinking this string is for login function api::response.message = data["message"]; api::response.channeldata.clear(); //If you do not delete the data before pushing it, the data will be repeated. github.com/TTakaTit if (!data.contains("messages") || !data["messages"].is_array()) { From d02a3984a30795ef5d0dcbb2315268148049254b Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 08:59:04 -0500 Subject: [PATCH 26/83] Fix private req references --- auth.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/auth.cpp b/auth.cpp index 7cd1fd1..74c937e 100644 --- a/auth.cpp +++ b/auth.cpp @@ -94,6 +94,8 @@ bool user_writable_module_present(); bool module_has_rwx_section(HMODULE mod); bool core_modules_signed(); bool hypervisor_present(); +static std::wstring get_system_dir(); +static std::wstring get_syswow_dir(); void snapshot_prologues(); bool prologues_ok(); bool func_region_ok(const void* addr); @@ -124,7 +126,6 @@ std::atomic last_module_check{ 0 }; std::atomic last_periodic_check{ 0 }; std::atomic prologues_ready{ false }; std::atomic heartbeat_started{ false }; -std::array pro_req{}; std::array pro_verify{}; std::array pro_checkinit{}; std::array pro_error{}; @@ -1680,8 +1681,7 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!pe_header_ok()) { error(XorStr("pe header check failed.")); } - if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || - detour_suspect(reinterpret_cast(&VerifyPayload)) || + if (detour_suspect(reinterpret_cast(&VerifyPayload)) || detour_suspect(reinterpret_cast(&checkInit)) || detour_suspect(reinterpret_cast(&error))) { error(XorStr("detour pattern detected.")); @@ -2070,13 +2070,11 @@ void snapshot_prologues() { if (prologues_ready.load()) return; - const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); - std::memcpy(pro_req.data(), req_ptr, pro_req.size()); std::memcpy(pro_verify.data(), verify_ptr, pro_verify.size()); std::memcpy(pro_checkinit.data(), check_ptr, pro_checkinit.size()); std::memcpy(pro_error.data(), error_ptr, pro_error.size()); @@ -2092,14 +2090,12 @@ bool prologues_ok() { if (!prologues_ready.load()) return true; - const auto req_ptr = reinterpret_cast(reinterpret_cast(&KeyAuth::api::req)); const auto verify_ptr = reinterpret_cast(reinterpret_cast(&VerifyPayload)); const auto check_ptr = reinterpret_cast(reinterpret_cast(&checkInit)); const auto error_ptr = reinterpret_cast(reinterpret_cast(&error)); const auto integ_ptr = reinterpret_cast(reinterpret_cast(&integrity_check)); const auto section_ptr = reinterpret_cast(reinterpret_cast(&check_section_integrity)); - return std::memcmp(pro_req.data(), req_ptr, pro_req.size()) == 0 && - std::memcmp(pro_verify.data(), verify_ptr, pro_verify.size()) == 0 && + return std::memcmp(pro_verify.data(), verify_ptr, pro_verify.size()) == 0 && std::memcmp(pro_checkinit.data(), check_ptr, pro_checkinit.size()) == 0 && std::memcmp(pro_error.data(), error_ptr, pro_error.size()) == 0 && std::memcmp(pro_integrity.data(), integ_ptr, pro_integrity.size()) == 0 && @@ -2441,8 +2437,7 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || - !func_region_ok(reinterpret_cast(&VerifyPayload)) || + if (!func_region_ok(reinterpret_cast(&VerifyPayload)) || !func_region_ok(reinterpret_cast(&checkInit)) || !func_region_ok(reinterpret_cast(&error)) || !func_region_ok(reinterpret_cast(&integrity_check)) || @@ -2464,8 +2459,7 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { if (!pe_header_ok()) { error(XorStr("pe header check failed.")); } - if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || - detour_suspect(reinterpret_cast(&VerifyPayload)) || + if (detour_suspect(reinterpret_cast(&VerifyPayload)) || detour_suspect(reinterpret_cast(&checkInit)) || detour_suspect(reinterpret_cast(&error))) { error(XorStr("detour pattern detected.")); @@ -2873,8 +2867,7 @@ void checkInit() { if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } - if (detour_suspect(reinterpret_cast(&KeyAuth::api::req)) || - detour_suspect(reinterpret_cast(&VerifyPayload)) || + if (detour_suspect(reinterpret_cast(&VerifyPayload)) || detour_suspect(reinterpret_cast(&checkInit)) || detour_suspect(reinterpret_cast(&error))) { error(XorStr("detour pattern detected.")); @@ -2882,8 +2875,7 @@ void checkInit() { if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || - !func_region_ok(reinterpret_cast(&VerifyPayload)) || + if (!func_region_ok(reinterpret_cast(&VerifyPayload)) || !func_region_ok(reinterpret_cast(&checkInit)) || !func_region_ok(reinterpret_cast(&error)) || !func_region_ok(reinterpret_cast(&integrity_check)) || @@ -2902,8 +2894,7 @@ void checkInit() { if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - if (!func_region_ok(reinterpret_cast(&KeyAuth::api::req)) || - !func_region_ok(reinterpret_cast(&VerifyPayload)) || + if (!func_region_ok(reinterpret_cast(&VerifyPayload)) || !func_region_ok(reinterpret_cast(&checkInit)) || !func_region_ok(reinterpret_cast(&error)) || !func_region_ok(reinterpret_cast(&integrity_check)) || From a62026483729be85b80a6d19cd72be5c5aef903c Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:04:05 -0500 Subject: [PATCH 27/83] Add usage section to README --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index eb59e5f..a9a6417 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,53 @@ x86 : 4- Lib Configuration -> Preprocessor definiton for CURL -> CURL_STATICLIB +## **Using The Library** +This section covers a minimal, working integration with the headers in this repo. + +1. Add the library headers and sources to your project (or build the `.lib` from this repo). +2. Include `auth.hpp` in your project file. +3. Initialize the API once at startup, then call login/license/upgrade as needed. +4. Keep your build settings on C++17 and link with the same libraries as this repo. + +Minimal example: +```cpp +#include "auth.hpp" + +using namespace KeyAuth; + +std::string name = "your_app_name"; +std::string ownerid = "your_owner_id"; +std::string version = "1.0"; +std::string url = "https://keyauth.win/api/1.3/"; +std::string path = ""; // optional + +api KeyAuthApp(name, ownerid, version, url, path); + +int main() { + KeyAuthApp.init(); + if (!KeyAuthApp.response.success) { + return 1; + } + KeyAuthApp.license("your_license_key"); + if (!KeyAuthApp.response.success) { + return 1; + } + return 0; +} +``` + +Notes: +1. If you are using the KeyAuth examples, keep their integrity/session checks intact. +2. Use the same `CURL_STATICLIB` define as shown above when statically linking. +3. Rebuild the library after pulling updates to keep everything in sync. + +Helpful references (copy and paste into your browser): +``` +https://github.com/KeyAuth/KeyAuth-CPP-Example +https://keyauth.cc/app/ +https://keyauth.cc/app/?page=forms +``` + ## **What is KeyAuth?** KeyAuth is a powerful cloud-based authentication system designed to protect your software from piracy and unauthorized access. With KeyAuth, you can implement secure licensing, user management, and subscription systems in minutes. Client SDKs available for [C#](https://github.com/KeyAuth/KeyAuth-CSHARP-Example), [C++](https://github.com/KeyAuth/KeyAuth-CPP-Example), [Python](https://github.com/KeyAuth/KeyAuth-Python-Example), [Java](https://github.com/KeyAuth-Archive/KeyAuth-JAVA-api), [JavaScript](https://github.com/mazkdevf/KeyAuth-JS-Example), [VB.NET](https://github.com/KeyAuth/KeyAuth-VB-Example), [PHP](https://github.com/KeyAuth/KeyAuth-PHP-Example), [Rust](https://github.com/KeyAuth/KeyAuth-Rust-Example), [Go](https://github.com/mazkdevf/KeyAuth-Go-Example), [Lua](https://github.com/mazkdevf/KeyAuth-Lua-Examples), [Ruby](https://github.com/mazkdevf/KeyAuth-Ruby-Example), and [Perl](https://github.com/mazkdevf/KeyAuth-Perl-Example). KeyAuth has several unique features such as memory streaming, webhook function where you can send requests to API without leaking the API, discord webhook notifications, ban the user securely through the application at your discretion. Feel free to join https://t.me/keyauth if you have questions or suggestions. From 7ddda6de7019b6f1ebce6bcf598760080660cb3f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:05:23 -0500 Subject: [PATCH 28/83] Document built-in security features --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index a9a6417..fc283dd 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,26 @@ Notes: 2. Use the same `CURL_STATICLIB` define as shown above when statically linking. 3. Rebuild the library after pulling updates to keep everything in sync. +## **Security Features (Built-In)** +The library ships with security checks enabled by default. You do not need to manually call anything beyond `init()` and a normal login/license call. + +What runs automatically: +1. **Integrity checks** (prologue, region, section hash, PE header, page protections). +2. **Module checks** (system module paths, signatures, RWX detection, user-writable paths, allowlist). +3. **Hosts-file checks** for API host tampering. +4. **Hypervisor detection** (conservative, low false positives). +5. **Timing anomaly checks** to detect time tamper. +6. **Session heartbeat** after successful login/license/upgrade/web login. + +How to keep security enabled: +1. Always call `KeyAuthApp.init()` once before any other API call. +2. Do not remove the built-in checks or tamper with the library internals. +3. Keep your application linked against the updated library after pulling changes. + +How to verify it is running: +1. Use the library normally — the checks are automatic. +2. If a check fails, the library will fail closed with an error message. + Helpful references (copy and paste into your browser): ``` https://github.com/KeyAuth/KeyAuth-CPP-Example From 117cc717c39300ffdc8b5ad95a17caf79dc45b50 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:06:06 -0500 Subject: [PATCH 29/83] Add security troubleshooting section --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index fc283dd..26190df 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,16 @@ How to verify it is running: 1. Use the library normally — the checks are automatic. 2. If a check fails, the library will fail closed with an error message. +## **Security Troubleshooting** +If you see security failures, common causes include: +1. **DLL injection / overlays**: third‑party overlays or injectors can trip module allowlists. +2. **Modified system DLLs**: non‑Microsoft versions or patched DLLs will be rejected. +3. **Time tampering**: manual clock changes or large time skew can trigger timing checks. +4. **VMs/Hypervisors**: running inside a VM can trigger hypervisor detection. +5. **Patched binaries**: inline hooks/NOP patches or modified `.text` will fail integrity checks. + +If you need to allow specific overlays or tools, add them to an allowlist in the code and rebuild the library. + Helpful references (copy and paste into your browser): ``` https://github.com/KeyAuth/KeyAuth-CPP-Example From fb74069a26aa420a754c3edbe5bb38bfaec10f97 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:10:03 -0500 Subject: [PATCH 30/83] Fix VirtualProtect IAT check --- auth.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index 74c937e..0cf08bb 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2340,22 +2340,26 @@ static bool iat_resolves_to(HMODULE module, const char* import_name, const void* if (!dir.VirtualAddress) return false; auto desc = reinterpret_cast(base + dir.VirtualAddress); + bool found = false; for (; desc->Name; ++desc) { const char* dll = reinterpret_cast(base + desc->Name); - if (!_stricmp(dll, "KERNEL32.DLL") && !_stricmp(dll, "KERNELBASE.DLL")) + if (_stricmp(dll, "KERNEL32.DLL") != 0 && _stricmp(dll, "KERNELBASE.DLL") != 0) continue; auto thunk = reinterpret_cast(base + desc->FirstThunk); - auto orig = reinterpret_cast(base + desc->OriginalFirstThunk); + auto orig = desc->OriginalFirstThunk + ? reinterpret_cast(base + desc->OriginalFirstThunk) + : thunk; for (; orig->u1.AddressOfData; ++orig, ++thunk) { if (orig->u1.Ordinal & IMAGE_ORDINAL_FLAG) continue; auto import = reinterpret_cast(base + orig->u1.AddressOfData); if (strcmp(reinterpret_cast(import->Name), import_name) == 0) { + found = true; return reinterpret_cast(thunk->u1.Function) == target; } } } - return true; + return !found; // if not imported (e.g., static link), do not fail. -nigel } bool iat_virtualprotect_ok() From 8ce50a274fda99e49bd1ebd68e2c8ad690435b48 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:15:45 -0500 Subject: [PATCH 31/83] Relax VirtualProtect IAT check --- auth.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/auth.cpp b/auth.cpp index 0cf08bb..3655ae4 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2325,22 +2325,21 @@ bool import_addresses_ok() return true; } -static bool iat_resolves_to(HMODULE module, const char* import_name, const void* target) +static bool iat_resolves_to(HMODULE module, const char* import_name, const void* target, bool& found) { if (!module) - return false; + return true; auto base = reinterpret_cast(module); auto dos = reinterpret_cast(base); if (dos->e_magic != IMAGE_DOS_SIGNATURE) - return false; + return true; auto nt = reinterpret_cast(base + dos->e_lfanew); if (nt->Signature != IMAGE_NT_SIGNATURE) - return false; + return true; const auto& dir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if (!dir.VirtualAddress) - return false; + return true; auto desc = reinterpret_cast(base + dir.VirtualAddress); - bool found = false; for (; desc->Name; ++desc) { const char* dll = reinterpret_cast(base + desc->Name); if (_stricmp(dll, "KERNEL32.DLL") != 0 && _stricmp(dll, "KERNELBASE.DLL") != 0) @@ -2359,7 +2358,7 @@ static bool iat_resolves_to(HMODULE module, const char* import_name, const void* } } } - return !found; // if not imported (e.g., static link), do not fail. -nigel + return true; } bool iat_virtualprotect_ok() @@ -2373,7 +2372,11 @@ bool iat_virtualprotect_ok() if (!addr_in_module(reinterpret_cast(vp), L"kernelbase.dll") && !addr_in_module(reinterpret_cast(vp), L"kernel32.dll")) return false; - return iat_resolves_to(self, "VirtualProtect", reinterpret_cast(vp)); + bool found = false; + const bool match = iat_resolves_to(self, "VirtualProtect", reinterpret_cast(vp), found); + if (!found) + return true; // allow when not imported + return match; } bool module_allowlist_ok() From 8f5d50fd405175c4dda10abf19e982ea4fcf4559 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:18:17 -0500 Subject: [PATCH 32/83] Disable VirtualProtect IAT check --- auth.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/auth.cpp b/auth.cpp index 3655ae4..4bf6848 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1666,9 +1666,9 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } + // if (!iat_virtualprotect_ok()) { + // error(XorStr("VirtualProtect IAT check failed.")); + // } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2451,9 +2451,9 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } + // if (!iat_virtualprotect_ok()) { + // error(XorStr("VirtualProtect IAT check failed.")); + // } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2859,9 +2859,9 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } + // if (!iat_virtualprotect_ok()) { + // error(XorStr("VirtualProtect IAT check failed.")); + // } if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } From 257ccb97b167ac165bfd3ad417ec56c6341ae06e Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:20:58 -0500 Subject: [PATCH 33/83] Restore and relax VirtualProtect IAT check --- auth.cpp | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/auth.cpp b/auth.cpp index 4bf6848..27ce435 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1666,9 +1666,9 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - // if (!iat_virtualprotect_ok()) { - // error(XorStr("VirtualProtect IAT check failed.")); - // } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2325,7 +2325,7 @@ bool import_addresses_ok() return true; } -static bool iat_resolves_to(HMODULE module, const char* import_name, const void* target, bool& found) +static bool iat_get_import_address(HMODULE module, const char* import_name, void*& out_addr, bool& found) { if (!module) return true; @@ -2354,7 +2354,8 @@ static bool iat_resolves_to(HMODULE module, const char* import_name, const void* auto import = reinterpret_cast(base + orig->u1.AddressOfData); if (strcmp(reinterpret_cast(import->Name), import_name) == 0) { found = true; - return reinterpret_cast(thunk->u1.Function) == target; + out_addr = reinterpret_cast(thunk->u1.Function); + return true; } } } @@ -2368,15 +2369,15 @@ bool iat_virtualprotect_ok() if (!vp) vp = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "VirtualProtect"); if (!vp) - return false; - if (!addr_in_module(reinterpret_cast(vp), L"kernelbase.dll") && - !addr_in_module(reinterpret_cast(vp), L"kernel32.dll")) - return false; + return true; bool found = false; - const bool match = iat_resolves_to(self, "VirtualProtect", reinterpret_cast(vp), found); - if (!found) + void* iat_addr = nullptr; + const bool ok = iat_get_import_address(self, "VirtualProtect", iat_addr, found); + if (!ok || !found) return true; // allow when not imported - return match; + if (addr_in_module(iat_addr, L"kernelbase.dll") || addr_in_module(iat_addr, L"kernel32.dll")) + return true; + return false; } bool module_allowlist_ok() @@ -2451,9 +2452,9 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } - // if (!iat_virtualprotect_ok()) { - // error(XorStr("VirtualProtect IAT check failed.")); - // } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!import_addresses_ok()) { error(XorStr("import address check failed.")); } @@ -2859,9 +2860,9 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } - // if (!iat_virtualprotect_ok()) { - // error(XorStr("VirtualProtect IAT check failed.")); - // } + if (!iat_virtualprotect_ok()) { + error(XorStr("VirtualProtect IAT check failed.")); + } if (!text_hashes_ok()) { error(XorStr("text section hash check failed.")); } From 5d6fbf5d2644005421d1763b67428e42f2e6f5ad Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:27:10 -0500 Subject: [PATCH 34/83] Relax import address checks --- auth.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/auth.cpp b/auth.cpp index 27ce435..ccb0d6f 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2312,15 +2312,20 @@ static bool addr_in_module(const void* addr, const wchar_t* module_name) bool import_addresses_ok() { - // wintrust functions should resolve inside wintrust.dll - if (!addr_in_module(reinterpret_cast(&WinVerifyTrust), L"wintrust.dll")) - return false; - // VirtualQuery should be inside kernelbase/kernel32 - if (!addr_in_module(reinterpret_cast(&VirtualQuery), L"kernelbase.dll") && - !addr_in_module(reinterpret_cast(&VirtualQuery), L"kernel32.dll")) - return false; - // curl functions should live in main module when statically linked - if (!addr_in_module(reinterpret_cast(&curl_easy_perform), nullptr)) + // wintrust functions should resolve inside wintrust.dll when loaded + if (GetModuleHandleW(L"wintrust.dll")) { + if (!addr_in_module(reinterpret_cast(&WinVerifyTrust), L"wintrust.dll")) + return false; + } + // VirtualQuery should be inside kernelbase/kernel32 when loaded + if (GetModuleHandleW(L"kernelbase.dll") || GetModuleHandleW(L"kernel32.dll")) { + if (!addr_in_module(reinterpret_cast(&VirtualQuery), L"kernelbase.dll") && + !addr_in_module(reinterpret_cast(&VirtualQuery), L"kernel32.dll")) + return false; + } + // curl functions can live in main module (static) or libcurl.dll (dynamic) + if (!addr_in_module(reinterpret_cast(&curl_easy_perform), nullptr) && + !addr_in_module(reinterpret_cast(&curl_easy_perform), L"libcurl.dll")) return false; return true; } From 60cab7577f50955d2d131e0ab054811aaedbb4ae Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:33:32 -0500 Subject: [PATCH 35/83] Reduce pre-login check false positives --- auth.cpp | 44 ++++---------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/auth.cpp b/auth.cpp index ccb0d6f..bfe9a8e 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1666,26 +1666,6 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } - if (!import_addresses_ok()) { - error(XorStr("import address check failed.")); - } - if (!text_hashes_ok()) { - error(XorStr("text section hash check failed.")); - } - if (!text_page_protections_ok()) { - error(XorStr("text page protection check failed.")); - } - if (!pe_header_ok()) { - error(XorStr("pe header check failed.")); - } - if (detour_suspect(reinterpret_cast(&VerifyPayload)) || - detour_suspect(reinterpret_cast(&checkInit)) || - detour_suspect(reinterpret_cast(&error))) { - error(XorStr("detour pattern detected.")); - } integrity_check(); long long unix_timestamp = 0; try { @@ -2457,26 +2437,6 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } - if (!import_addresses_ok()) { - error(XorStr("import address check failed.")); - } - if (!text_hashes_ok()) { - error(XorStr("text section hash check failed.")); - } - if (!text_page_protections_ok()) { - error(XorStr("text page protection check failed.")); - } - if (!pe_header_ok()) { - error(XorStr("pe header check failed.")); - } - if (detour_suspect(reinterpret_cast(&VerifyPayload)) || - detour_suspect(reinterpret_cast(&checkInit)) || - detour_suspect(reinterpret_cast(&error))) { - error(XorStr("detour pattern detected.")); - } const auto host = extract_host(url); if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); @@ -2865,6 +2825,9 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } + if (!LoggedIn.load()) { + goto periodic_done; + } if (!iat_virtualprotect_ok()) { error(XorStr("VirtualProtect IAT check failed.")); } @@ -2895,6 +2858,7 @@ void checkInit() { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } +periodic_done: if (check_section_integrity(XorStr(".text").c_str(), false)) { const int streak = integrity_fail_streak.fetch_add(1) + 1; if (streak >= 2) { From fed0cdf798c7172fcc2884bd631713fe7c7b7757 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:35:24 -0500 Subject: [PATCH 36/83] Run heavy checks with streak gating --- auth.cpp | 56 ++++++++++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/auth.cpp b/auth.cpp index bfe9a8e..760cbfd 100644 --- a/auth.cpp +++ b/auth.cpp @@ -138,6 +138,7 @@ std::atomic text_prot_ready{ false }; std::vector> text_protections; std::atomic pe_header_ready{ false }; uint32_t pe_header_hash = 0; +std::atomic heavy_fail_streak{ 0 }; void KeyAuth::api::init() { @@ -2825,38 +2826,29 @@ void checkInit() { if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } - if (!LoggedIn.load()) { - goto periodic_done; - } - if (!iat_virtualprotect_ok()) { - error(XorStr("VirtualProtect IAT check failed.")); - } - if (!text_hashes_ok()) { - error(XorStr("text section hash check failed.")); - } - if (!text_page_protections_ok()) { - error(XorStr("text page protection check failed.")); - } - if (!pe_header_ok()) { - error(XorStr("pe header check failed.")); - } - if (!import_addresses_ok()) { - error(XorStr("import address check failed.")); - } - if (detour_suspect(reinterpret_cast(&VerifyPayload)) || - detour_suspect(reinterpret_cast(&checkInit)) || - detour_suspect(reinterpret_cast(&error))) { - error(XorStr("detour pattern detected.")); - } - if (!prologues_ok()) { - error(XorStr("function prologue check failed, possible inline hook detected.")); - } - if (!func_region_ok(reinterpret_cast(&VerifyPayload)) || - !func_region_ok(reinterpret_cast(&checkInit)) || - !func_region_ok(reinterpret_cast(&error)) || - !func_region_ok(reinterpret_cast(&integrity_check)) || - !func_region_ok(reinterpret_cast(&check_section_integrity))) { - error(XorStr("function region check failed, possible hook detected.")); + const bool heavy_ok = + iat_virtualprotect_ok() && + text_hashes_ok() && + text_page_protections_ok() && + pe_header_ok() && + import_addresses_ok() && + !detour_suspect(reinterpret_cast(&VerifyPayload)) && + !detour_suspect(reinterpret_cast(&checkInit)) && + !detour_suspect(reinterpret_cast(&error)) && + prologues_ok() && + func_region_ok(reinterpret_cast(&VerifyPayload)) && + func_region_ok(reinterpret_cast(&checkInit)) && + func_region_ok(reinterpret_cast(&error)) && + func_region_ok(reinterpret_cast(&integrity_check)) && + func_region_ok(reinterpret_cast(&check_section_integrity)); + + if (!heavy_ok) { + const int streak = heavy_fail_streak.fetch_add(1) + 1; + if (streak >= 2) { + error(XorStr("security checks failed, possible tamper detected.")); + } + } else { + heavy_fail_streak.store(0); } periodic_done: if (check_section_integrity(XorStr(".text").c_str(), false)) { From 780d3933aaf842554d67098477a18df8104ddf29 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:38:29 -0500 Subject: [PATCH 37/83] Zeroize request data buffer --- auth.cpp | 16 ++++++++++++++-- auth.hpp | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index 760cbfd..3cb8939 100644 --- a/auth.cpp +++ b/auth.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,7 @@ void snapshot_pe_header(); bool pe_header_ok(); bool iat_virtualprotect_ok(); bool module_allowlist_ok(); +static void secure_zero(std::string& value); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -1995,6 +1997,15 @@ static bool file_exists(const std::wstring& path) return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY); } +static void secure_zero(std::string& value) +{ + if (value.empty()) + return; + SecureZeroMemory(value.data(), value.size()); + value.clear(); + value.shrink_to_fit(); +} + static std::wstring get_system_dir() { wchar_t buf[MAX_PATH] = {}; @@ -2424,7 +2435,7 @@ void KeyAuth::api::setDebug(bool value) { KeyAuth::api::debug = value; } -std::string KeyAuth::api::req(const std::string& data, const std::string& url) { +std::string KeyAuth::api::req(std::string data, const std::string& url) { signature.clear(); signatureTimestamp.clear(); integrity_check(); @@ -2472,7 +2483,8 @@ std::string KeyAuth::api::req(const std::string& data, const std::string& url) { } debugInfo(data, url, to_return, "Sig: " + signature + "\nTimestamp:" + signatureTimestamp); - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); + secure_zero(data); return to_return; } diff --git a/auth.hpp b/auth.hpp index 7eaa5fd..e54f14e 100644 --- a/auth.hpp +++ b/auth.hpp @@ -107,7 +107,7 @@ namespace KeyAuth { private: std::string sessionid, enckey; - static std::string req(const std::string& data, const std::string& url); + static std::string req(std::string data, const std::string& url); static void debugInfo(std::string data, std::string url, std::string response, std::string headers); static void setDebug(bool value); From be355718c280b849d2bed6dc03b5541ee709ddfa Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:40:43 -0500 Subject: [PATCH 38/83] Wipe sensitive parameters --- auth.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/auth.cpp b/auth.cpp index 3cb8939..110bea6 100644 --- a/auth.cpp +++ b/auth.cpp @@ -319,6 +319,9 @@ size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) void KeyAuth::api::login(std::string username, std::string password, std::string code) { checkInit(); + ScopeWipe wipe_user(username); + ScopeWipe wipe_pass(password); + ScopeWipe wipe_code(code); std::string hwid = utils::get_hwid(); auto data = @@ -395,6 +398,7 @@ void KeyAuth::api::login(std::string username, std::string password, std::string void KeyAuth::api::chatget(std::string channel) { checkInit(); + ScopeWipe wipe_channel(channel); auto data = XorStr("type=chatget") + @@ -411,6 +415,8 @@ void KeyAuth::api::chatget(std::string channel) bool KeyAuth::api::chatsend(std::string message, std::string channel) { checkInit(); + ScopeWipe wipe_message(message); + ScopeWipe wipe_channel(channel); auto data = XorStr("type=chatsend") + @@ -429,6 +435,7 @@ bool KeyAuth::api::chatsend(std::string message, std::string channel) void KeyAuth::api::changeUsername(std::string newusername) { checkInit(); + ScopeWipe wipe_user(newusername); auto data = XorStr("type=changeUsername") + @@ -902,6 +909,7 @@ void KeyAuth::api::web_login() void KeyAuth::api::button(std::string button) { checkInit(); + ScopeWipe wipe_button(button); // from https://perpetualprogrammers.wordpress.com/2016/05/22/the-http-server-api/ @@ -1017,6 +1025,10 @@ void KeyAuth::api::button(std::string button) void KeyAuth::api::regstr(std::string username, std::string password, std::string key, std::string email) { checkInit(); + ScopeWipe wipe_user(username); + ScopeWipe wipe_pass(password); + ScopeWipe wipe_key(key); + ScopeWipe wipe_email(email); std::string hwid = utils::get_hwid(); auto data = @@ -1090,6 +1102,8 @@ void KeyAuth::api::regstr(std::string username, std::string password, std::strin void KeyAuth::api::upgrade(std::string username, std::string key) { checkInit(); + ScopeWipe wipe_user(username); + ScopeWipe wipe_key(key); auto data = XorStr("type=upgrade") + @@ -1146,6 +1160,8 @@ std::string generate_random_number() { void KeyAuth::api::license(std::string key, std::string code) { checkInit(); + ScopeWipe wipe_key(key); + ScopeWipe wipe_code(code); std::string hwid = utils::get_hwid(); auto data = @@ -1215,6 +1231,8 @@ void KeyAuth::api::license(std::string key, std::string code) { void KeyAuth::api::setvar(std::string var, std::string vardata) { checkInit(); + ScopeWipe wipe_var(var); + ScopeWipe wipe_data(vardata); auto data = XorStr("type=setvar") + @@ -1230,6 +1248,7 @@ void KeyAuth::api::setvar(std::string var, std::string vardata) { std::string KeyAuth::api::getvar(std::string var) { checkInit(); + ScopeWipe wipe_var(var); auto data = XorStr("type=getvar") + @@ -1270,6 +1289,7 @@ std::string KeyAuth::api::getvar(std::string var) { void KeyAuth::api::ban(std::string reason) { checkInit(); + ScopeWipe wipe_reason(reason); auto data = XorStr("type=ban") + @@ -1432,6 +1452,7 @@ std::string KeyAuth::api::var(std::string varid) { void KeyAuth::api::log(std::string message) { checkInit(); + ScopeWipe wipe_message(message); char acUserName[100]; DWORD nUserName = sizeof(acUserName); @@ -1451,6 +1472,7 @@ void KeyAuth::api::log(std::string message) { std::vector KeyAuth::api::download(std::string fileid) { checkInit(); + ScopeWipe wipe_fileid(fileid); auto to_uc_vector = [](std::string value) { return std::vector(value.data(), value.data() + value.length() ); @@ -1481,6 +1503,10 @@ std::vector KeyAuth::api::download(std::string fileid) { std::string KeyAuth::api::webhook(std::string id, std::string params, std::string body, std::string contenttype) { checkInit(); + ScopeWipe wipe_id(id); + ScopeWipe wipe_params(params); + ScopeWipe wipe_body(body); + ScopeWipe wipe_type(contenttype); CURL *curl = curl_easy_init(); auto data = @@ -1620,6 +1646,8 @@ void KeyAuth::api::fetchstats() void KeyAuth::api::forgot(std::string username, std::string email) { checkInit(); + ScopeWipe wipe_user(username); + ScopeWipe wipe_email(email); auto data = XorStr("type=forgot") + @@ -2006,6 +2034,12 @@ static void secure_zero(std::string& value) value.shrink_to_fit(); } +struct ScopeWipe { + std::string* value; + explicit ScopeWipe(std::string& v) : value(&v) {} + ~ScopeWipe() { secure_zero(*value); } +}; + static std::wstring get_system_dir() { wchar_t buf[MAX_PATH] = {}; From 72c9c60920f41dd753949f031999e1213d8a00e1 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 09:49:20 -0500 Subject: [PATCH 39/83] Add guard page and new module checks --- auth.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/auth.cpp b/auth.cpp index 110bea6..530fa77 100644 --- a/auth.cpp +++ b/auth.cpp @@ -113,6 +113,9 @@ void snapshot_pe_header(); bool pe_header_ok(); bool iat_virtualprotect_ok(); bool module_allowlist_ok(); +void snapshot_module_baseline(); +bool new_modules_present(); +bool text_guard_pages_present(); static void secure_zero(std::string& value); std::string seed; void cleanUpSeedData(const std::string& seed); @@ -141,6 +144,8 @@ std::vector> text_protections; std::atomic pe_header_ready{ false }; uint32_t pe_header_hash = 0; std::atomic heavy_fail_streak{ 0 }; +std::atomic module_baseline_ready{ false }; +std::vector module_baseline; void KeyAuth::api::init() { @@ -2110,6 +2115,7 @@ void snapshot_prologues() snapshot_text_hashes(); snapshot_text_page_protections(); snapshot_pe_header(); + snapshot_module_baseline(); } bool prologues_ok() @@ -2266,6 +2272,62 @@ bool text_page_protections_ok() return true; } +bool text_guard_pages_present() +{ + std::uintptr_t base = 0; + size_t size = 0; + if (!get_text_section_info(base, size)) + return false; + const size_t page = 0x1000; + for (size_t off = 0; off < size; off += page) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(base + off), &mbi, sizeof(mbi)) == 0) + continue; + if (mbi.Protect & PAGE_GUARD) + return true; + } + return false; +} + +void snapshot_module_baseline() +{ + if (module_baseline_ready.load()) + return; + HMODULE mods[1024] = {}; + DWORD needed = 0; + if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) + return; + const size_t count = needed / sizeof(HMODULE); + module_baseline.clear(); + for (size_t i = 0; i < count; ++i) { + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) + continue; + module_baseline.push_back(to_lower_ws(path)); + } + module_baseline_ready.store(true); +} + +bool new_modules_present() +{ + if (!module_baseline_ready.load()) + return false; + HMODULE mods[1024] = {}; + DWORD needed = 0; + if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) + return false; + const size_t count = needed / sizeof(HMODULE); + for (size_t i = 0; i < count; ++i) { + wchar_t path[MAX_PATH] = {}; + if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) + continue; + const auto p = to_lower_ws(path); + if (std::find(module_baseline.begin(), module_baseline.end(), p) == module_baseline.end()) + return true; + } + return false; +} + void snapshot_pe_header() { if (pe_header_ready.load()) @@ -2307,6 +2369,13 @@ bool pe_header_ok() return fnv1a(reinterpret_cast(base), header_size) == pe_header_hash; } +struct EarlyChecks { + EarlyChecks() { + snapshot_prologues(); + } +}; +static EarlyChecks g_early_checks; + bool detour_suspect(const uint8_t* p) { if (!p) @@ -2878,6 +2947,8 @@ void checkInit() { text_page_protections_ok() && pe_header_ok() && import_addresses_ok() && + !text_guard_pages_present() && + !new_modules_present() && !detour_suspect(reinterpret_cast(&VerifyPayload)) && !detour_suspect(reinterpret_cast(&checkInit)) && !detour_suspect(reinterpret_cast(&error)) && From 9811d31ef4b50f1101142c57e955476c46fd776a Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:32:01 -0500 Subject: [PATCH 40/83] Require static libcurl linkage --- auth.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index 530fa77..4dc53f9 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2418,9 +2418,8 @@ bool import_addresses_ok() !addr_in_module(reinterpret_cast(&VirtualQuery), L"kernel32.dll")) return false; } - // curl functions can live in main module (static) or libcurl.dll (dynamic) - if (!addr_in_module(reinterpret_cast(&curl_easy_perform), nullptr) && - !addr_in_module(reinterpret_cast(&curl_easy_perform), L"libcurl.dll")) + // curl functions must live in main module (static) + if (!addr_in_module(reinterpret_cast(&curl_easy_perform), nullptr)) return false; return true; } From e4372aea5877a28e6c2ab88f44c387fde118f358 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:36:22 -0500 Subject: [PATCH 41/83] Reduce debug string exposure --- auth.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 4dc53f9..b23e6b4 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2584,7 +2584,9 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { error(errorMsg); } - debugInfo(data, url, to_return, "Sig: " + signature + "\nTimestamp:" + signatureTimestamp); + if (KeyAuth::api::debug) { + debugInfo("n/a", "n/a", to_return, "n/a"); + } curl_easy_cleanup(curl); secure_zero(data); return to_return; From d267d1cb5b0a936d8ae3099371fe8bec84f0b01f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:40:47 -0500 Subject: [PATCH 42/83] Harden DLL search order --- auth.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/auth.cpp b/auth.cpp index b23e6b4..de61c77 100644 --- a/auth.cpp +++ b/auth.cpp @@ -149,6 +149,19 @@ std::vector module_baseline; void KeyAuth::api::init() { + // harden dll search order to reduce current-dir hijacks + SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS); + SetDllDirectoryW(L""); + { + wchar_t exe_path[MAX_PATH] = {}; + GetModuleFileNameW(nullptr, exe_path, MAX_PATH); + std::wstring exe_dir = exe_path; + const auto last_slash = exe_dir.find_last_of(L"\\/"); + if (last_slash != std::wstring::npos) { + exe_dir = exe_dir.substr(0, last_slash); + AddDllDirectory(exe_dir.c_str()); + } + } std::thread(runChecks).detach(); snapshot_prologues(); seed = generate_random_number(); From c1dc28113bbfebca29d202139a0839b0ebe51392 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:42:58 -0500 Subject: [PATCH 43/83] Define securewipe helper --- auth.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index de61c77..764f996 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2052,10 +2052,15 @@ static void secure_zero(std::string& value) value.shrink_to_fit(); } +static void securewipe(std::string& value) +{ + secure_zero(value); +} + struct ScopeWipe { std::string* value; explicit ScopeWipe(std::string& v) : value(&v) {} - ~ScopeWipe() { secure_zero(*value); } + ~ScopeWipe() { securewipe(*value); } }; static std::wstring get_system_dir() From 26de74d1605adc16e2b8928e42637977823313bd Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:44:35 -0500 Subject: [PATCH 44/83] Fix double curl cleanup in init --- auth.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 764f996..7f39976 100644 --- a/auth.cpp +++ b/auth.cpp @@ -184,6 +184,7 @@ void KeyAuth::api::init() XorStr("&ownerid=") + ownerid; if (curl) { curl_easy_cleanup(curl); // avoid leak from escape helper. -nigel + curl = nullptr; } // to ensure people removed secret from main.cpp (some people will forget to) @@ -225,7 +226,7 @@ void KeyAuth::api::init() data += XorStr("&token=").c_str() + token; data += XorStr("&thash=").c_str() + path; } - curl_easy_cleanup(curl); + // curl was only used for escape above auto response = req(data, url); From 2dbf570ed5b05dfed49d47fe8ab5520b96b84259 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:47:22 -0500 Subject: [PATCH 45/83] Move securewipe helpers above init --- auth.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/auth.cpp b/auth.cpp index 7f39976..317300a 100644 --- a/auth.cpp +++ b/auth.cpp @@ -147,6 +147,26 @@ std::atomic heavy_fail_streak{ 0 }; std::atomic module_baseline_ready{ false }; std::vector module_baseline; +static void secure_zero(std::string& value) +{ + if (value.empty()) + return; + SecureZeroMemory(value.data(), value.size()); + value.clear(); + value.shrink_to_fit(); +} + +static void securewipe(std::string& value) +{ + secure_zero(value); +} + +struct ScopeWipe { + std::string* value; + explicit ScopeWipe(std::string& v) : value(&v) {} + ~ScopeWipe() { securewipe(*value); } +}; + void KeyAuth::api::init() { // harden dll search order to reduce current-dir hijacks @@ -2044,26 +2064,6 @@ static bool file_exists(const std::wstring& path) return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY); } -static void secure_zero(std::string& value) -{ - if (value.empty()) - return; - SecureZeroMemory(value.data(), value.size()); - value.clear(); - value.shrink_to_fit(); -} - -static void securewipe(std::string& value) -{ - secure_zero(value); -} - -struct ScopeWipe { - std::string* value; - explicit ScopeWipe(std::string& v) : value(&v) {} - ~ScopeWipe() { securewipe(*value); } -}; - static std::wstring get_system_dir() { wchar_t buf[MAX_PATH] = {}; From c6f625ec7ad7c9f863e74752f600a9723a02b6bc Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 10:53:46 -0500 Subject: [PATCH 46/83] Update overhaul changelog --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 26190df..4a50527 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,25 @@ If you see security failures, common causes include: If you need to allow specific overlays or tools, add them to an allowlist in the code and rebuild the library. +## **Changelog (Overhaul Summary)** +This list summarizes all changes made in the overhaul: +1. **Security checks**: `.text` integrity, prologue snapshots, function region validation, detour detection. +2. **Hashing & headers**: `.text` slice hashing, PE header hash validation. +3. **Memory protections**: `.text` page protection checks and guard‑page detection. +4. **Module validation**: System32/SysWOW64 path checks, duplicate module detection, allowlist, new‑module detection. +5. **Module trust**: Microsoft signature verification for core DLLs, RWX section detection. +6. **Environment checks**: hypervisor detection and timing anomaly detection. +7. **Import checks**: import address validation; VirtualProtect IAT check (only when imported). +8. **Network hardening**: hosts‑file override detection for API host. +9. **Session hardening**: session heartbeat after successful login/license/upgrade/web login. +10. **DLL search order**: hardened DLL lookup and removed current‑dir hijacking. +11. **String exposure**: request data zeroized after use; sensitive parameters wiped via `ScopeWipe`. +12. **Debug logging**: minimized request/URL logging to reduce in‑memory exposure. +13. **Parsing hardening**: safer JSON parsing and substring handling to avoid crashes. +14. **Curl safety**: fixed cleanup issues; enforced static libcurl linkage. +15. **Module path APIs**: removed hardcoded System32 paths (uses `GetSystemDirectoryW`). +16. **Example/docs**: added usage section, security feature docs, and troubleshooting guidance. + Helpful references (copy and paste into your browser): ``` https://github.com/KeyAuth/KeyAuth-CPP-Example From 221877fc9e5e3982cba927291cf2b2f9327fff1f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:05:25 -0500 Subject: [PATCH 47/83] Stream POST data to reduce copies --- auth.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 317300a..3280fea 100644 --- a/auth.cpp +++ b/auth.cpp @@ -327,6 +327,24 @@ size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { return size * nmemb; } +struct PostData { + const char* data; + size_t len; + size_t pos; +}; + +size_t read_callback(char* buffer, size_t size, size_t nmemb, void* userp) { + auto* pd = static_cast(userp); + const size_t cap = size * nmemb; + const size_t remaining = (pd->pos < pd->len) ? (pd->len - pd->pos) : 0; + const size_t to_copy = remaining < cap ? remaining : cap; + if (to_copy > 0) { + std::memcpy(buffer, pd->data + pd->pos, to_copy); + pd->pos += to_copy; + } + return to_copy; +} + // Callback function to handle headers size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) { size_t totalSize = size * nitems; @@ -2583,13 +2601,18 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { std::string to_return; std::string headers; + PostData post{ data.c_str(), data.size(), 0 }; + // Set CURL options curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READDATA, &post); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(post.len)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); From a940b9ecbec8ea2ee554cd3eb4b50c793879d995 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:09:10 -0500 Subject: [PATCH 48/83] Fix libcurl read callback usage --- auth.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 3280fea..3514ce1 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2610,9 +2610,10 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, nullptr); curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); curl_easy_setopt(curl, CURLOPT_READDATA, &post); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(post.len)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(post.len)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); From da6caadb74315a55d0eeb1bc51863ff8c6928169 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:11:46 -0500 Subject: [PATCH 49/83] Add content-type header for read callback --- auth.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/auth.cpp b/auth.cpp index 3514ce1..3fd7d03 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2600,6 +2600,8 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { std::string to_return; std::string headers; + struct curl_slist* req_headers = nullptr; + req_headers = curl_slist_append(req_headers, "Content-Type: application/x-www-form-urlencoded"); PostData post{ data.c_str(), data.size(), 0 }; @@ -2618,11 +2620,13 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); // Perform the request CURLcode code = curl_easy_perform(curl); if (code != CURLE_OK) { std::string errorMsg = "CURL Error: " + std::string(curl_easy_strerror(code)); + if (req_headers) curl_slist_free_all(req_headers); curl_easy_cleanup(curl); error(errorMsg); } @@ -2630,6 +2634,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (KeyAuth::api::debug) { debugInfo("n/a", "n/a", to_return, "n/a"); } + if (req_headers) curl_slist_free_all(req_headers); curl_easy_cleanup(curl); secure_zero(data); return to_return; From bbad2346e813a937ea2c31316e909db6d739be0a Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:12:57 -0500 Subject: [PATCH 50/83] Improve libcurl POST headers --- auth.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/auth.cpp b/auth.cpp index 3fd7d03..bec4e55 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2602,6 +2602,8 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { std::string headers; struct curl_slist* req_headers = nullptr; req_headers = curl_slist_append(req_headers, "Content-Type: application/x-www-form-urlencoded"); + req_headers = curl_slist_append(req_headers, "Accept: */*"); + req_headers = curl_slist_append(req_headers, "Expect:"); PostData post{ data.c_str(), data.size(), 0 }; @@ -2616,11 +2618,13 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); curl_easy_setopt(curl, CURLOPT_READDATA, &post); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(post.len)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(post.len)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "KeyAuth"); // Perform the request CURLcode code = curl_easy_perform(curl); From c9fad4f15e5343f833cfd67fa7c546bb8cd831f5 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:15:49 -0500 Subject: [PATCH 51/83] Fallback to POSTFIELDS if headers missing --- auth.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index bec4e55..08545c5 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2631,10 +2631,40 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (code != CURLE_OK) { std::string errorMsg = "CURL Error: " + std::string(curl_easy_strerror(code)); if (req_headers) curl_slist_free_all(req_headers); - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); error(errorMsg); } + // Fallback: if signature headers are missing, retry using POSTFIELDS + if (signature.empty() || signatureTimestamp.empty()) { + curl_easy_cleanup(curl); + curl = curl_easy_init(); + if (!curl) { + if (req_headers) curl_slist_free_all(req_headers); + error(XorStr("CURL Initialization Failed!")); + } + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(data.size())); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "KeyAuth"); + code = curl_easy_perform(curl); + if (code != CURLE_OK) { + std::string errorMsg = "CURL Error: " + std::string(curl_easy_strerror(code)); + if (req_headers) curl_slist_free_all(req_headers); + curl_easy_cleanup(curl); + error(errorMsg); + } + } + if (KeyAuth::api::debug) { debugInfo("n/a", "n/a", to_return, "n/a"); } From 2cf67f84d031f8a1e1b90b08ef098e39710e41a3 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:25:52 -0500 Subject: [PATCH 52/83] Format secure wipe helpers --- auth.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/auth.cpp b/auth.cpp index 08545c5..6b7a793 100644 --- a/auth.cpp +++ b/auth.cpp @@ -147,24 +147,24 @@ std::atomic heavy_fail_streak{ 0 }; std::atomic module_baseline_ready{ false }; std::vector module_baseline; -static void secure_zero(std::string& value) +static inline void secure_zero(std::string& value) noexcept { - if (value.empty()) - return; - SecureZeroMemory(value.data(), value.size()); - value.clear(); - value.shrink_to_fit(); + if (!value.empty()) { + SecureZeroMemory(value.data(), value.size()); + value.clear(); + value.shrink_to_fit(); + } } -static void securewipe(std::string& value) +static inline void securewipe(std::string& value) noexcept { secure_zero(value); } -struct ScopeWipe { +struct ScopeWipe final { std::string* value; - explicit ScopeWipe(std::string& v) : value(&v) {} - ~ScopeWipe() { securewipe(*value); } + explicit ScopeWipe(std::string& v) noexcept : value(&v) {} + ~ScopeWipe() noexcept { securewipe(*value); } }; void KeyAuth::api::init() From df69c2bfd19bca21a2ccad7b289c9fcad10abced Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:29:02 -0500 Subject: [PATCH 53/83] Revert to POSTFIELDS request body --- auth.cpp | 58 ++------------------------------------------------------ 1 file changed, 2 insertions(+), 56 deletions(-) diff --git a/auth.cpp b/auth.cpp index 6b7a793..2ae2e46 100644 --- a/auth.cpp +++ b/auth.cpp @@ -327,24 +327,6 @@ size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { return size * nmemb; } -struct PostData { - const char* data; - size_t len; - size_t pos; -}; - -size_t read_callback(char* buffer, size_t size, size_t nmemb, void* userp) { - auto* pd = static_cast(userp); - const size_t cap = size * nmemb; - const size_t remaining = (pd->pos < pd->len) ? (pd->len - pd->pos) : 0; - const size_t to_copy = remaining < cap ? remaining : cap; - if (to_copy > 0) { - std::memcpy(buffer, pd->data + pd->pos, to_copy); - pd->pos += to_copy; - } - return to_copy; -} - // Callback function to handle headers size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) { size_t totalSize = size * nitems; @@ -2605,20 +2587,14 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { req_headers = curl_slist_append(req_headers, "Accept: */*"); req_headers = curl_slist_append(req_headers, "Expect:"); - PostData post{ data.c_str(), data.size(), 0 }; - // Set CURL options curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, nullptr); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); - curl_easy_setopt(curl, CURLOPT_READDATA, &post); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(post.len)); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(post.len)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(data.size())); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); @@ -2635,36 +2611,6 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { error(errorMsg); } - // Fallback: if signature headers are missing, retry using POSTFIELDS - if (signature.empty() || signatureTimestamp.empty()) { - curl_easy_cleanup(curl); - curl = curl_easy_init(); - if (!curl) { - if (req_headers) curl_slist_free_all(req_headers); - error(XorStr("CURL Initialization Failed!")); - } - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); - curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(data.size())); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "KeyAuth"); - code = curl_easy_perform(curl); - if (code != CURLE_OK) { - std::string errorMsg = "CURL Error: " + std::string(curl_easy_strerror(code)); - if (req_headers) curl_slist_free_all(req_headers); - curl_easy_cleanup(curl); - error(errorMsg); - } - } - if (KeyAuth::api::debug) { debugInfo("n/a", "n/a", to_return, "n/a"); } From cdfb15f0b66616806195ff33e710d9cddb68e2b9 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:31:03 -0500 Subject: [PATCH 54/83] Remove secure_zero redeclaration --- auth.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 2ae2e46..7f2cfd6 100644 --- a/auth.cpp +++ b/auth.cpp @@ -116,7 +116,6 @@ bool module_allowlist_ok(); void snapshot_module_baseline(); bool new_modules_present(); bool text_guard_pages_present(); -static void secure_zero(std::string& value); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; From bff03e6904ec67cf33952d84ba9100d541033a27 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:32:10 -0500 Subject: [PATCH 55/83] Add inline forward decls for wipe helpers --- auth.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/auth.cpp b/auth.cpp index 7f2cfd6..e8a52fb 100644 --- a/auth.cpp +++ b/auth.cpp @@ -116,6 +116,8 @@ bool module_allowlist_ok(); void snapshot_module_baseline(); bool new_modules_present(); bool text_guard_pages_present(); +inline void secure_zero(std::string& value) noexcept; +inline void securewipe(std::string& value) noexcept; std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; From ee73713eeae3c03098a4b156a5cab5e97a6e7b9c Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:34:19 -0500 Subject: [PATCH 56/83] Harden header parsing for signature --- auth.cpp | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/auth.cpp b/auth.cpp index e8a52fb..ef4cbc7 100644 --- a/auth.cpp +++ b/auth.cpp @@ -333,23 +333,27 @@ size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) size_t totalSize = size * nitems; std::string header(buffer, totalSize); - - // Convert to lowercase for comparison - std::string lowercase = header; - std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), ::tolower); - - // Signature - if (lowercase.find("x-signature-ed25519: ") == 0) { - signature = header.substr(header.find(": ") + 2); - signature.erase(signature.find_last_not_of("\r\n") + 1); - //std::cout << "[DEBUG] Captured signature header: " << signature << std::endl; - } - - // Timestamp - if (lowercase.find("x-signature-timestamp: ") == 0) { - signatureTimestamp = header.substr(header.find(": ") + 2); - signatureTimestamp.erase(signatureTimestamp.find_last_not_of("\r\n") + 1); - //std::cout << "[DEBUG] Captured timestamp header: " << signatureTimestamp << std::endl; + if (header.empty()) + return totalSize; + // trim CRLF + while (!header.empty() && (header.back() == '\r' || header.back() == '\n')) { + header.pop_back(); + } + const auto colon = header.find(':'); + if (colon == std::string::npos) + return totalSize; + std::string key = header.substr(0, colon); + std::string value = header.substr(colon + 1); + while (!value.empty() && (value.front() == ' ' || value.front() == '\t')) { + value.erase(value.begin()); + } + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + + if (key == "x-signature-ed25519") { + signature = value; + } + if (key == "x-signature-timestamp") { + signatureTimestamp = value; } return totalSize; From cf5165f2945d810c8fce76b63b839f523e76080d Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:40:55 -0500 Subject: [PATCH 57/83] Let libcurl infer POST length --- auth.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index ef4cbc7..ef95792 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2599,7 +2599,6 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(data.size())); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &to_return); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); From ec9729abdc3e5c7c45b74031e7ac29a280bb8504 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:52:41 -0500 Subject: [PATCH 58/83] Remove VM/IAT/PE/new-module checks from enforcement --- auth.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/auth.cpp b/auth.cpp index ef95792..0b756c5 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2964,7 +2964,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || hypervisor_present() || !module_allowlist_ok()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || !module_allowlist_ok()) { error(XorStr("module path check failed, possible side-load detected.")); } } @@ -2975,13 +2975,10 @@ void checkInit() { error(XorStr("timing anomaly detected, possible time tamper.")); } const bool heavy_ok = - iat_virtualprotect_ok() && text_hashes_ok() && text_page_protections_ok() && - pe_header_ok() && import_addresses_ok() && !text_guard_pages_present() && - !new_modules_present() && !detour_suspect(reinterpret_cast(&VerifyPayload)) && !detour_suspect(reinterpret_cast(&checkInit)) && !detour_suspect(reinterpret_cast(&error)) && From 8a7c3bd11697d4c5e0c83e98a04b8acf8593c264 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 11:58:31 -0500 Subject: [PATCH 59/83] Remove legacy VM/PE/IAT/module baseline globals --- auth.cpp | 171 +------------------------------------------------------ 1 file changed, 1 insertion(+), 170 deletions(-) diff --git a/auth.cpp b/auth.cpp index 0b756c5..55b27ab 100644 --- a/auth.cpp +++ b/auth.cpp @@ -94,7 +94,6 @@ bool duplicate_system_modules_present(); bool user_writable_module_present(); bool module_has_rwx_section(HMODULE mod); bool core_modules_signed(); -bool hypervisor_present(); static std::wstring get_system_dir(); static std::wstring get_syswow_dir(); void snapshot_prologues(); @@ -109,13 +108,8 @@ bool detour_suspect(const uint8_t* p); bool import_addresses_ok(); void snapshot_text_page_protections(); bool text_page_protections_ok(); -void snapshot_pe_header(); -bool pe_header_ok(); -bool iat_virtualprotect_ok(); + bool module_allowlist_ok(); -void snapshot_module_baseline(); -bool new_modules_present(); -bool text_guard_pages_present(); inline void secure_zero(std::string& value) noexcept; inline void securewipe(std::string& value) noexcept; std::string seed; @@ -142,11 +136,7 @@ struct TextHash { size_t offset; size_t len; uint32_t hash; }; std::vector text_hashes; std::atomic text_prot_ready{ false }; std::vector> text_protections; -std::atomic pe_header_ready{ false }; -uint32_t pe_header_hash = 0; std::atomic heavy_fail_streak{ 0 }; -std::atomic module_baseline_ready{ false }; -std::vector module_baseline; static inline void secure_zero(std::string& value) noexcept { @@ -2085,41 +2075,6 @@ static std::wstring get_syswow_dir() return std::wstring(buf); } -bool hypervisor_present() -{ - int cpu_info[4] = {}; - __cpuid(cpu_info, 1); - const bool hv_bit = (cpu_info[2] & (1 << 31)) != 0; - if (hv_bit) { - return true; - } - - // registry artifacts (conservative) - if (reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\VBOX__") || - reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\VMWARE") || - reg_key_exists(HKEY_LOCAL_MACHINE, L"HARDWARE\\ACPI\\DSDT\\XEN") || - reg_key_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\VMware, Inc.\\VMware Tools") || - reg_key_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Oracle\\VirtualBox Guest Additions")) { - return true; - } - - // file artifacts (drivers/tools) - const auto sys32 = get_system_dir(); - if (!sys32.empty()) { - if (file_exists(sys32 + L"\\drivers\\VBoxGuest.sys") || - file_exists(sys32 + L"\\drivers\\VBoxMouse.sys") || - file_exists(sys32 + L"\\drivers\\VBoxSF.sys") || - file_exists(sys32 + L"\\drivers\\VBoxVideo.sys") || - file_exists(sys32 + L"\\drivers\\vmhgfs.sys") || - file_exists(sys32 + L"\\drivers\\vmmouse.sys") || - file_exists(sys32 + L"\\drivers\\vm3dmp.sys") || - file_exists(sys32 + L"\\drivers\\xen.sys")) { - return true; - } - } - - return false; -} void snapshot_prologues() { @@ -2138,8 +2093,6 @@ void snapshot_prologues() prologues_ready.store(true); snapshot_text_hashes(); snapshot_text_page_protections(); - snapshot_pe_header(); - snapshot_module_baseline(); } bool prologues_ok() @@ -2296,109 +2249,6 @@ bool text_page_protections_ok() return true; } -bool text_guard_pages_present() -{ - std::uintptr_t base = 0; - size_t size = 0; - if (!get_text_section_info(base, size)) - return false; - const size_t page = 0x1000; - for (size_t off = 0; off < size; off += page) { - MEMORY_BASIC_INFORMATION mbi{}; - if (VirtualQuery(reinterpret_cast(base + off), &mbi, sizeof(mbi)) == 0) - continue; - if (mbi.Protect & PAGE_GUARD) - return true; - } - return false; -} - -void snapshot_module_baseline() -{ - if (module_baseline_ready.load()) - return; - HMODULE mods[1024] = {}; - DWORD needed = 0; - if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) - return; - const size_t count = needed / sizeof(HMODULE); - module_baseline.clear(); - for (size_t i = 0; i < count; ++i) { - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) - continue; - module_baseline.push_back(to_lower_ws(path)); - } - module_baseline_ready.store(true); -} - -bool new_modules_present() -{ - if (!module_baseline_ready.load()) - return false; - HMODULE mods[1024] = {}; - DWORD needed = 0; - if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) - return false; - const size_t count = needed / sizeof(HMODULE); - for (size_t i = 0; i < count; ++i) { - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) - continue; - const auto p = to_lower_ws(path); - if (std::find(module_baseline.begin(), module_baseline.end(), p) == module_baseline.end()) - return true; - } - return false; -} - -void snapshot_pe_header() -{ - if (pe_header_ready.load()) - return; - const auto hmodule = GetModuleHandle(nullptr); - if (!hmodule) - return; - const auto base = reinterpret_cast(hmodule); - const auto dos = reinterpret_cast(base); - if (dos->e_magic != IMAGE_DOS_SIGNATURE) - return; - const auto nt = reinterpret_cast(base + dos->e_lfanew); - if (nt->Signature != IMAGE_NT_SIGNATURE) - return; - const size_t header_size = nt->OptionalHeader.SizeOfHeaders; - if (header_size == 0 || header_size > 0x4000) - return; - pe_header_hash = fnv1a(reinterpret_cast(base), header_size); - pe_header_ready.store(true); -} - -bool pe_header_ok() -{ - if (!pe_header_ready.load()) - return true; - const auto hmodule = GetModuleHandle(nullptr); - if (!hmodule) - return true; - const auto base = reinterpret_cast(hmodule); - const auto dos = reinterpret_cast(base); - if (dos->e_magic != IMAGE_DOS_SIGNATURE) - return false; - const auto nt = reinterpret_cast(base + dos->e_lfanew); - if (nt->Signature != IMAGE_NT_SIGNATURE) - return false; - const size_t header_size = nt->OptionalHeader.SizeOfHeaders; - if (header_size == 0 || header_size > 0x4000) - return false; - return fnv1a(reinterpret_cast(base), header_size) == pe_header_hash; -} - -struct EarlyChecks { - EarlyChecks() { - snapshot_prologues(); - } -}; -static EarlyChecks g_early_checks; bool detour_suspect(const uint8_t* p) { @@ -2485,24 +2335,6 @@ static bool iat_get_import_address(HMODULE module, const char* import_name, void return true; } -bool iat_virtualprotect_ok() -{ - HMODULE self = GetModuleHandle(nullptr); - FARPROC vp = GetProcAddress(GetModuleHandleW(L"kernelbase.dll"), "VirtualProtect"); - if (!vp) - vp = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "VirtualProtect"); - if (!vp) - return true; - bool found = false; - void* iat_addr = nullptr; - const bool ok = iat_get_import_address(self, "VirtualProtect", iat_addr, found); - if (!ok || !found) - return true; // allow when not imported - if (addr_in_module(iat_addr, L"kernelbase.dll") || addr_in_module(iat_addr, L"kernel32.dll")) - return true; - return false; -} - bool module_allowlist_ok() { HMODULE mods[1024] = {}; @@ -2978,7 +2810,6 @@ void checkInit() { text_hashes_ok() && text_page_protections_ok() && import_addresses_ok() && - !text_guard_pages_present() && !detour_suspect(reinterpret_cast(&VerifyPayload)) && !detour_suspect(reinterpret_cast(&checkInit)) && !detour_suspect(reinterpret_cast(&error)) && From f04ac1bd63ae0d317f50cbc068986a6c9ea038ca Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:07:06 -0500 Subject: [PATCH 60/83] Remove module allowlist check --- auth.cpp | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/auth.cpp b/auth.cpp index 55b27ab..f823ab5 100644 --- a/auth.cpp +++ b/auth.cpp @@ -109,7 +109,6 @@ bool import_addresses_ok(); void snapshot_text_page_protections(); bool text_page_protections_ok(); -bool module_allowlist_ok(); inline void secure_zero(std::string& value) noexcept; inline void securewipe(std::string& value) noexcept; std::string seed; @@ -2335,37 +2334,6 @@ static bool iat_get_import_address(HMODULE module, const char* import_name, void return true; } -bool module_allowlist_ok() -{ - HMODULE mods[1024] = {}; - DWORD needed = 0; - if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) - return false; - wchar_t exe_path[MAX_PATH] = {}; - GetModuleFileNameW(nullptr, exe_path, MAX_PATH); - std::wstring exe_dir = exe_path; - const auto last_slash = exe_dir.find_last_of(L"\\/"); - if (last_slash != std::wstring::npos) - exe_dir = exe_dir.substr(0, last_slash + 1); - exe_dir = to_lower_ws(exe_dir); - std::wstring sys32 = get_system_dir(); - std::wstring syswow = get_syswow_dir(); - if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); - if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); - - const size_t count = needed / sizeof(HMODULE); - for (size_t i = 0; i < count; ++i) { - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) - continue; - std::wstring p = to_lower_ws(path); - if (p.rfind(sys32, 0) == 0 || p.rfind(syswow, 0) == 0 || p.rfind(exe_dir, 0) == 0) - continue; - return false; - } - return true; -} - void heartbeat_thread(KeyAuth::api* instance) { std::random_device rd; @@ -2796,7 +2764,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed() || !module_allowlist_ok()) { + if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { error(XorStr("module path check failed, possible side-load detected.")); } } From 88192975dda974f5e47260b0008f31e9c92c0597 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:09:42 -0500 Subject: [PATCH 61/83] Limit module checks to core signatures --- auth.cpp | 93 +------------------------------------------------------- 1 file changed, 1 insertion(+), 92 deletions(-) diff --git a/auth.cpp b/auth.cpp index f823ab5..ef81995 100644 --- a/auth.cpp +++ b/auth.cpp @@ -89,9 +89,6 @@ auto check_section_integrity( const char *section_name, bool fix ) -> bool; void integrity_check(); std::string extract_host(const std::string& url); bool hosts_override_present(const std::string& host); -bool module_paths_ok(); -bool duplicate_system_modules_present(); -bool user_writable_module_present(); bool module_has_rwx_section(HMODULE mod); bool core_modules_signed(); static std::wstring get_system_dir(); @@ -1953,94 +1950,6 @@ bool core_modules_signed() return true; } -bool module_paths_ok() -{ - const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - std::wstring sys32 = get_system_dir(); - std::wstring syswow = get_syswow_dir(); - if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); - if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); - - for (const auto* name : kModules) { - HMODULE mod = GetModuleHandleW(name); - if (!mod) - continue; - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameW(mod, path, MAX_PATH)) - return false; - std::wstring p = to_lower_ws(path); - if (p.rfind(sys32, 0) != 0 && p.rfind(syswow, 0) != 0) - return false; - } - return true; -} - -bool duplicate_system_modules_present() -{ - const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; - std::wstring sys32 = get_system_dir(); - std::wstring syswow = get_syswow_dir(); - if (!sys32.empty()) sys32 = to_lower_ws(sys32 + L"\\"); - if (!syswow.empty()) syswow = to_lower_ws(syswow + L"\\"); - - HMODULE mods[1024] = {}; - DWORD needed = 0; - if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) - return false; - - const size_t count = needed / sizeof(HMODULE); - for (size_t i = 0; i < count; ++i) { - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) - continue; - std::wstring p = to_lower_ws(path); - const auto name_pos = p.find_last_of(L"\\/"); - const std::wstring name = (name_pos == std::wstring::npos) ? p : p.substr(name_pos + 1); - bool is_target = false; - for (const auto* modname : kModules) { - if (name == modname) { - is_target = true; - break; - } - } - if (!is_target) - continue; - if (p.rfind(sys32, 0) != 0 && p.rfind(syswow, 0) != 0) - return true; - } - return false; -} - -bool user_writable_module_present() -{ - HMODULE mods[1024] = {}; - DWORD needed = 0; - if (!EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &needed)) - return false; - - wchar_t exe_path[MAX_PATH] = {}; - GetModuleFileNameW(nullptr, exe_path, MAX_PATH); - std::wstring exe_dir = exe_path; - const auto last_slash = exe_dir.find_last_of(L"\\/"); - if (last_slash != std::wstring::npos) - exe_dir = exe_dir.substr(0, last_slash + 1); - exe_dir = to_lower_ws(exe_dir); - - const size_t count = needed / sizeof(HMODULE); - for (size_t i = 0; i < count; ++i) { - wchar_t path[MAX_PATH] = {}; - if (!GetModuleFileNameExW(GetCurrentProcess(), mods[i], path, MAX_PATH)) - continue; - std::wstring p = to_lower_ws(path); - if (p.rfind(exe_dir, 0) == 0) - continue; - - if (path_has_any(p, { L"\\temp\\", L"\\appdata\\local\\temp\\", L"\\downloads\\" })) - return true; - } - return false; -} - static bool reg_key_exists(HKEY root, const wchar_t* path) { HKEY h = nullptr; @@ -2764,7 +2673,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); - if (!module_paths_ok() || duplicate_system_modules_present() || user_writable_module_present() || !core_modules_signed()) { + if (!core_modules_signed()) { error(XorStr("module path check failed, possible side-load detected.")); } } From f057ff392b2f055db1e7023b3d45b69d0c3dd983 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:15:31 -0500 Subject: [PATCH 62/83] Remove duplicate heartbeat starts --- auth.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/auth.cpp b/auth.cpp index ef81995..ce259f1 100644 --- a/auth.cpp +++ b/auth.cpp @@ -409,8 +409,6 @@ void KeyAuth::api::login(std::string username, std::string password, std::string LI_FN(GlobalAddAtomA)(ownerid.c_str()); LoggedIn.store(true); start_heartbeat(this); - start_heartbeat(this); - start_heartbeat(this); } else { LI_FN(exit)(12); From 4abd0a9876ee18c187342ece03b5630dee232414 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:17:14 -0500 Subject: [PATCH 63/83] Remove unnecessary curl headers --- auth.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/auth.cpp b/auth.cpp index ce259f1..c667d01 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2295,9 +2295,6 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { std::string to_return; std::string headers; struct curl_slist* req_headers = nullptr; - req_headers = curl_slist_append(req_headers, "Content-Type: application/x-www-form-urlencoded"); - req_headers = curl_slist_append(req_headers, "Accept: */*"); - req_headers = curl_slist_append(req_headers, "Expect:"); // Set CURL options curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); From d7f3f0b744c8da1aa3ceab6541fffb1721c6f4dd Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:22:51 -0500 Subject: [PATCH 64/83] Document security checks and optional hardening --- README.md | 57 ++++++++++++++++++++++++++++--------------------------- auth.cpp | 6 ++++++ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 4a50527..8bac192 100644 --- a/README.md +++ b/README.md @@ -66,12 +66,11 @@ Notes: The library ships with security checks enabled by default. You do not need to manually call anything beyond `init()` and a normal login/license call. What runs automatically: -1. **Integrity checks** (prologue, region, section hash, PE header, page protections). -2. **Module checks** (system module paths, signatures, RWX detection, user-writable paths, allowlist). +1. **Integrity checks** (prologue snapshots, function region validation, `.text` hashing, page protections). +2. **Module checks** (core module signature verification + RWX section detection). 3. **Hosts-file checks** for API host tampering. -4. **Hypervisor detection** (conservative, low false positives). -5. **Timing anomaly checks** to detect time tamper. -6. **Session heartbeat** after successful login/license/upgrade/web login. +4. **Timing anomaly checks** to detect time tamper. +5. **Session heartbeat** after successful login/license/upgrade/web login. How to keep security enabled: 1. Always call `KeyAuthApp.init()` once before any other API call. @@ -82,34 +81,36 @@ How to verify it is running: 1. Use the library normally — the checks are automatic. 2. If a check fails, the library will fail closed with an error message. +## **Optional Hardening Ideas (Not Enabled)** +These are intentionally **not** enabled in the library to avoid false positives, but you can add them if your app needs them. + +1. **PE header erase**: wipe PE header pages after load to make casual dumping harder. This is not a check; it simply reduces dump quality. +2. **Module allowlists**: require a strict set of loaded modules; this breaks overlays and many legitimate plugins. +3. **System module path checks**: enforce System32/SysWOW64-only paths; can fail on custom Windows installs. +4. **Hypervisor detection**: block VMs; useful for niche threat models but unfriendly to legit users. +5. **VirtualProtect IAT validation**: detect IAT hooks; can false-positive in some environments. + ## **Security Troubleshooting** If you see security failures, common causes include: -1. **DLL injection / overlays**: third‑party overlays or injectors can trip module allowlists. -2. **Modified system DLLs**: non‑Microsoft versions or patched DLLs will be rejected. -3. **Time tampering**: manual clock changes or large time skew can trigger timing checks. -4. **VMs/Hypervisors**: running inside a VM can trigger hypervisor detection. -5. **Patched binaries**: inline hooks/NOP patches or modified `.text` will fail integrity checks. - -If you need to allow specific overlays or tools, add them to an allowlist in the code and rebuild the library. +1. **Modified system DLLs**: non‑Microsoft versions or patched DLLs will be rejected. +2. **Time tampering**: manual clock changes or large time skew can trigger timing checks. +3. **Patched binaries**: inline hooks/NOP patches or modified `.text` will fail integrity checks. ## **Changelog (Overhaul Summary)** This list summarizes all changes made in the overhaul: -1. **Security checks**: `.text` integrity, prologue snapshots, function region validation, detour detection. -2. **Hashing & headers**: `.text` slice hashing, PE header hash validation. -3. **Memory protections**: `.text` page protection checks and guard‑page detection. -4. **Module validation**: System32/SysWOW64 path checks, duplicate module detection, allowlist, new‑module detection. -5. **Module trust**: Microsoft signature verification for core DLLs, RWX section detection. -6. **Environment checks**: hypervisor detection and timing anomaly detection. -7. **Import checks**: import address validation; VirtualProtect IAT check (only when imported). -8. **Network hardening**: hosts‑file override detection for API host. -9. **Session hardening**: session heartbeat after successful login/license/upgrade/web login. -10. **DLL search order**: hardened DLL lookup and removed current‑dir hijacking. -11. **String exposure**: request data zeroized after use; sensitive parameters wiped via `ScopeWipe`. -12. **Debug logging**: minimized request/URL logging to reduce in‑memory exposure. -13. **Parsing hardening**: safer JSON parsing and substring handling to avoid crashes. -14. **Curl safety**: fixed cleanup issues; enforced static libcurl linkage. -15. **Module path APIs**: removed hardcoded System32 paths (uses `GetSystemDirectoryW`). -16. **Example/docs**: added usage section, security feature docs, and troubleshooting guidance. +1. **Integrity checks**: prologue snapshots, function region validation, detour detection, `.text` slice hashing, page protections. +2. **Module trust**: Microsoft signature verification for core DLLs, RWX section detection. +3. **Timing checks**: timing anomaly detection to catch clock tamper. +4. **Import checks**: import address validation. +5. **Network hardening**: hosts‑file override detection for API host. +6. **Session hardening**: session heartbeat after successful login/license/upgrade/web login. +7. **DLL search order**: hardened DLL lookup and removed current‑dir hijacking. +8. **String exposure**: request data zeroized after use; sensitive parameters wiped via `ScopeWipe`. +9. **Debug logging**: minimized request/URL logging to reduce in‑memory exposure. +10. **Parsing hardening**: safer JSON parsing and substring handling to avoid crashes. +11. **Curl safety**: fixed cleanup issues; enforced static libcurl linkage. +12. **Module path APIs**: removed hardcoded System32 paths (uses `GetSystemDirectoryW`). +13. **Example/docs**: added usage section, security feature docs, and troubleshooting guidance. Helpful references (copy and paste into your browser): ``` diff --git a/auth.cpp b/auth.cpp index c667d01..f6f9e12 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1932,6 +1932,7 @@ static bool verify_signature(const std::wstring& path) bool core_modules_signed() { + // verify core dll signatures and reject rwx sections -nigel const wchar_t* kModules[] = { L"ntdll.dll", L"kernel32.dll", L"kernelbase.dll", L"user32.dll" }; for (const auto* name : kModules) { HMODULE mod = GetModuleHandleW(name); @@ -2271,6 +2272,7 @@ void KeyAuth::api::setDebug(bool value) { std::string KeyAuth::api::req(std::string data, const std::string& url) { signature.clear(); signatureTimestamp.clear(); + // gate requests on integrity checks to reduce bypasses -nigel integrity_check(); if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); @@ -2283,6 +2285,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { error(XorStr("function region check failed, possible hook detected.")); } const auto host = extract_host(url); + // block hosts-file redirects for api host -nigel if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); } @@ -2668,6 +2671,7 @@ void checkInit() { const auto last_mod = last_module_check.load(); if (now - last_mod > 60) { last_module_check.store(now); + // core module trust check to detect tampered system dlls -nigel if (!core_modules_signed()) { error(XorStr("module path check failed, possible side-load detected.")); } @@ -2675,9 +2679,11 @@ void checkInit() { const auto last_periodic = last_periodic_check.load(); if (now - last_periodic > 30) { last_periodic_check.store(now); + // detect basic clock tampering to block expired key reuse -nigel if (timing_anomaly_detected()) { error(XorStr("timing anomaly detected, possible time tamper.")); } + // periodic integrity sweep across code regions -nigel const bool heavy_ok = text_hashes_ok() && text_page_protections_ok() && From 3d19801c2fc289baf2486fbe30a6dc5472572d49 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:24:11 -0500 Subject: [PATCH 65/83] Add security overview documentation --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 8bac192..3903000 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,26 @@ What runs automatically: 4. **Timing anomaly checks** to detect time tamper. 5. **Session heartbeat** after successful login/license/upgrade/web login. +## **Security Overview** +This SDK includes lightweight, client-side defenses that raise the cost of common bypass techniques while keeping normal integrations simple. + +What it protects against: +1. **Inline patching/NOPs**: prologue snapshots and detour heuristics catch modified function entry points. +2. **Code tamper**: `.text` hashing and page‑protection checks detect modified code pages. +3. **API redirection**: hosts‑file checks flag local DNS overrides of the API host. +4. **Time spoofing**: timing anomaly checks reduce abuse of expired keys by system clock changes. +5. **Tampered system DLLs**: core module signature checks reject patched or unsigned system libraries. + +Benefits: +1. **Fail‑closed behavior**: when a check fails, requests are blocked before the API call. +2. **Low integration cost**: no additional calls are required beyond `init()` and a normal login/license flow. +3. **Reduced false positives**: checks are limited to core modules and conservative tamper signals. + +Design notes: +1. These are **client‑side** protections. They complement — not replace — server‑side session validation. +2. If you modify or strip checks, you reduce protection. Keep the SDK updated to inherit fixes. +3. Optional hardening ideas are listed below for advanced users who accept higher false‑positive risk. + How to keep security enabled: 1. Always call `KeyAuthApp.init()` once before any other API call. 2. Do not remove the built-in checks or tamper with the library internals. From 99dba44db5dc11d5e22cd33dbd2bf3d9e00677b4 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:28:52 -0500 Subject: [PATCH 66/83] Generalize IAT validation doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3903000..2725a60 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ These are intentionally **not** enabled in the library to avoid false positives, 2. **Module allowlists**: require a strict set of loaded modules; this breaks overlays and many legitimate plugins. 3. **System module path checks**: enforce System32/SysWOW64-only paths; can fail on custom Windows installs. 4. **Hypervisor detection**: block VMs; useful for niche threat models but unfriendly to legit users. -5. **VirtualProtect IAT validation**: detect IAT hooks; can false-positive in some environments. +5. **IAT validation**: detect import-table hooks for any imported API; can false-positive in some environments. ## **Security Troubleshooting** If you see security failures, common causes include: From d6eecb95f0f7ef91f978a0c511037470a7a107e5 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 12:36:55 -0500 Subject: [PATCH 67/83] Tidy helpful references formatting --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2725a60..2d21b46 100644 --- a/README.md +++ b/README.md @@ -132,12 +132,10 @@ This list summarizes all changes made in the overhaul: 12. **Module path APIs**: removed hardcoded System32 paths (uses `GetSystemDirectoryW`). 13. **Example/docs**: added usage section, security feature docs, and troubleshooting guidance. -Helpful references (copy and paste into your browser): -``` -https://github.com/KeyAuth/KeyAuth-CPP-Example -https://keyauth.cc/app/ -https://keyauth.cc/app/?page=forms -``` +Helpful references: +- https://github.com/KeyAuth/KeyAuth-CPP-Example +- https://keyauth.cc/app/ +- https://keyauth.cc/app/?page=forms ## **What is KeyAuth?** From 92c8bda956819015fb10915838da8263f14248b2 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 14:52:06 -0500 Subject: [PATCH 68/83] Add usage notes for security checks --- auth.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/auth.cpp b/auth.cpp index f6f9e12..23d435d 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2274,6 +2274,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { signatureTimestamp.clear(); // gate requests on integrity checks to reduce bypasses -nigel integrity_check(); + // usage: keep this in req() so every api call is protected -nigel if (!prologues_ok()) { error(XorStr("function prologue check failed, possible inline hook detected.")); } @@ -2666,6 +2667,7 @@ void checkInit() { if (!initialized) { error(XorStr("You need to run the KeyAuthApp.init(); function before any other KeyAuth functions")); } + // usage: call init() once at startup; checks run automatically after that -nigel const auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); const auto last_mod = last_module_check.load(); From 2206463abe1478903b008f7e482df7826cd2044c Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 22:21:09 -0500 Subject: [PATCH 69/83] Add GitHub Actions build workflow --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ebcbf24 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: ci + +on: + push: + pull_request: + +jobs: + build-windows: + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: setup msvc + uses: ilammy/msvc-dev-cmd@v1 + + - name: build x64 release + shell: powershell + run: | + if (Test-Path "library.sln") { + msbuild "library.sln" /m /p:Configuration=Release /p:Platform=x64 + } elseif (Test-Path "*.sln") { + $sln = Get-ChildItem -Filter *.sln | Select-Object -First 1 + msbuild $sln.FullName /m /p:Configuration=Release /p:Platform=x64 + } else { + Write-Error "no solution (.sln) found to build" + } From 722fc6aaa7fd3a4bfaefb7c455602420d641528f Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 22:25:33 -0500 Subject: [PATCH 70/83] Improve input handling and expiry helpers --- auth.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- auth.hpp | 38 +++++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/auth.cpp b/auth.cpp index 23d435d..f3a0306 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1720,6 +1720,45 @@ void KeyAuth::api::logout() { load_response_data(json); } +std::string KeyAuth::api::expiry_remaining(const std::string& expiry) +{ + if (expiry.empty()) + return "unknown"; + long long exp = 0; + try { + exp = std::stoll(expiry); + } + catch (...) { + return "unknown"; + } + const long long now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + long long diff = exp - now; + if (diff <= 0) + return "expired"; + + const long long days = diff / 86400; + const long long weeks = days / 7; + const long long months = days / 30; + const long long hours = (diff % 86400) / 3600; + const long long minutes = (diff % 3600) / 60; + + std::time_t tt = static_cast(exp); + std::tm tm{}; +#ifdef _WIN32 + localtime_s(&tm, &tt); +#else + tm = *std::localtime(&tt); +#endif + char buf[32] = {}; + std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); + + std::ostringstream out; + out << days << "d " << hours << "h " << minutes << "m remaining" + << " (expires " << buf << ", ~" << weeks << "w / " << months << "mo)"; + return out.str(); +} + int VerifyPayload(std::string signature, std::string timestamp, std::string body) { if (!prologues_ok()) { @@ -2039,14 +2078,26 @@ bool timing_anomaly_detected() const auto steady_now = std::chrono::steady_clock::now(); static auto wall_last = wall_now; static auto steady_last = steady_now; + static ULONGLONG tick_last = GetTickCount64(); + static long long wall_last_sec = std::chrono::duration_cast( + wall_now.time_since_epoch()).count(); const auto wall_delta = std::chrono::duration_cast(wall_now - wall_last).count(); const auto steady_delta = std::chrono::duration_cast(steady_now - steady_last).count(); wall_last = wall_now; steady_last = steady_now; + const ULONGLONG tick_now = GetTickCount64(); + const long long tick_delta = static_cast((tick_now - tick_last) / 1000ULL); + tick_last = tick_now; + const long long wall_now_sec = std::chrono::duration_cast( + wall_now.time_since_epoch()).count(); + const long long wall_tick_delta = wall_now_sec - wall_last_sec; + wall_last_sec = wall_now_sec; if (wall_delta < -60 || wall_delta > 300) return true; if (std::llabs(wall_delta - steady_delta) > 120) return true; + if (std::llabs(wall_tick_delta - tick_delta) > 120) + return true; return false; } @@ -2578,7 +2629,10 @@ void KeyAuth::api::debugInfo(std::string data, std::string url, std::string resp std::istream_iterator()); for (auto const& value : results) { - datas[value.substr(0, value.find('='))] = value.substr(value.find('=') + 1); + const auto pos = value.find('='); + if (pos == std::string::npos) + continue; + datas[value.substr(0, pos)] = value.substr(pos + 1); } RedactField(datas, "sessionid"); diff --git a/auth.hpp b/auth.hpp index e54f14e..1b06ae0 100644 --- a/auth.hpp +++ b/auth.hpp @@ -47,6 +47,7 @@ namespace KeyAuth { void fetchstats(); void forgot(std::string username, std::string email); void logout(); + static std::string expiry_remaining(const std::string& expiry); class subscriptions_class { public: @@ -113,22 +114,27 @@ namespace KeyAuth { void load_user_data(nlohmann::json data) { - api::user_data.username = data[XorStr("username")]; - api::user_data.ip = data[XorStr("ip")]; - if (data[XorStr("hwid")].is_null()) { + api::user_data.username = data.value(XorStr("username"), ""); + api::user_data.ip = data.value(XorStr("ip"), ""); + if (!data.contains(XorStr("hwid")) || data[XorStr("hwid")].is_null()) { api::user_data.hwid = XorStr("none"); } else { api::user_data.hwid = data[XorStr("hwid")]; } - api::user_data.createdate = data[XorStr("createdate")]; - api::user_data.lastlogin = data[XorStr("lastlogin")]; - - for (size_t i = 0; i < data[XorStr("subscriptions")].size(); i++) { // Prompto#7895 & stars#2297 was here - subscriptions_class subscriptions; - subscriptions.name = data[XorStr("subscriptions")][i][XorStr("subscription")]; - subscriptions.expiry = data[XorStr("subscriptions")][i][XorStr("expiry")]; - api::user_data.subscriptions.emplace_back(subscriptions); + api::user_data.createdate = data.value(XorStr("createdate"), ""); + api::user_data.lastlogin = data.value(XorStr("lastlogin"), ""); + + api::user_data.subscriptions.clear(); + if (data.contains(XorStr("subscriptions")) && data[XorStr("subscriptions")].is_array()) { + for (const auto& sub : data[XorStr("subscriptions")]) { + subscriptions_class subscriptions; + if (sub.contains(XorStr("subscription"))) + subscriptions.name = sub.value(XorStr("subscription"), ""); + if (sub.contains(XorStr("expiry"))) + subscriptions.expiry = sub.value(XorStr("expiry"), ""); + api::user_data.subscriptions.emplace_back(subscriptions); + } } } @@ -157,10 +163,12 @@ namespace KeyAuth { return; // avoid invalid server payload crash. -nigel } for (const auto& sub : data["messages"]) { - - std::string authoroutput = sub[XorStr("author")]; - std::string messageoutput = sub["message"]; - int timestamp = sub[XorStr("timestamp")]; std::string timestampoutput = std::to_string(timestamp); + if (!sub.is_object()) + continue; + std::string authoroutput = sub.value(XorStr("author"), ""); + std::string messageoutput = sub.value("message", ""); + const int timestamp = sub.value(XorStr("timestamp"), 0); + std::string timestampoutput = std::to_string(timestamp); authoroutput.erase(remove(authoroutput.begin(), authoroutput.end(), '"'), authoroutput.end()); messageoutput.erase(remove(messageoutput.begin(), messageoutput.end(), '"'), messageoutput.end()); timestampoutput.erase(remove(timestampoutput.begin(), timestampoutput.end(), '"'), timestampoutput.end()); From 03765c35dd56c0380280d0ea2b06983dada0f77a Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 22:28:13 -0500 Subject: [PATCH 71/83] Fix json key access for xorstr --- auth.hpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/auth.hpp b/auth.hpp index 1b06ae0..d4a8f0a 100644 --- a/auth.hpp +++ b/auth.hpp @@ -114,25 +114,33 @@ namespace KeyAuth { void load_user_data(nlohmann::json data) { - api::user_data.username = data.value(XorStr("username"), ""); - api::user_data.ip = data.value(XorStr("ip"), ""); - if (!data.contains(XorStr("hwid")) || data[XorStr("hwid")].is_null()) { + const std::string key_username = XorStr("username"); + const std::string key_ip = XorStr("ip"); + const std::string key_hwid = XorStr("hwid"); + const std::string key_created = XorStr("createdate"); + const std::string key_lastlogin = XorStr("lastlogin"); + const std::string key_subs = XorStr("subscriptions"); + const std::string key_sub_name = XorStr("subscription"); + const std::string key_sub_expiry = XorStr("expiry"); + api::user_data.username = data.value(key_username, ""); + api::user_data.ip = data.value(key_ip, ""); + if (!data.contains(key_hwid) || data[key_hwid].is_null()) { api::user_data.hwid = XorStr("none"); } else { - api::user_data.hwid = data[XorStr("hwid")]; + api::user_data.hwid = data[key_hwid]; } - api::user_data.createdate = data.value(XorStr("createdate"), ""); - api::user_data.lastlogin = data.value(XorStr("lastlogin"), ""); + api::user_data.createdate = data.value(key_created, ""); + api::user_data.lastlogin = data.value(key_lastlogin, ""); api::user_data.subscriptions.clear(); - if (data.contains(XorStr("subscriptions")) && data[XorStr("subscriptions")].is_array()) { - for (const auto& sub : data[XorStr("subscriptions")]) { + if (data.contains(key_subs) && data[key_subs].is_array()) { + for (const auto& sub : data[key_subs]) { subscriptions_class subscriptions; - if (sub.contains(XorStr("subscription"))) - subscriptions.name = sub.value(XorStr("subscription"), ""); - if (sub.contains(XorStr("expiry"))) - subscriptions.expiry = sub.value(XorStr("expiry"), ""); + if (sub.contains(key_sub_name)) + subscriptions.name = sub.value(key_sub_name, ""); + if (sub.contains(key_sub_expiry)) + subscriptions.expiry = sub.value(key_sub_expiry, ""); api::user_data.subscriptions.emplace_back(subscriptions); } } @@ -162,12 +170,14 @@ namespace KeyAuth { if (!data.contains("messages") || !data["messages"].is_array()) { return; // avoid invalid server payload crash. -nigel } + const std::string key_author = XorStr("author"); + const std::string key_timestamp = XorStr("timestamp"); for (const auto& sub : data["messages"]) { if (!sub.is_object()) continue; - std::string authoroutput = sub.value(XorStr("author"), ""); + std::string authoroutput = sub.value(key_author, ""); std::string messageoutput = sub.value("message", ""); - const int timestamp = sub.value(XorStr("timestamp"), 0); + const int timestamp = sub.value(key_timestamp, 0); std::string timestampoutput = std::to_string(timestamp); authoroutput.erase(remove(authoroutput.begin(), authoroutput.end(), '"'), authoroutput.end()); messageoutput.erase(remove(messageoutput.begin(), messageoutput.end(), '"'), messageoutput.end()); From 0860267d577ef44c0703dd4c253ebe17ed1eda99 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 22:54:40 -0500 Subject: [PATCH 72/83] Add default timing/save constants --- auth.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/auth.hpp b/auth.hpp index d4a8f0a..1587cc5 100644 --- a/auth.hpp +++ b/auth.hpp @@ -48,6 +48,10 @@ namespace KeyAuth { void forgot(std::string username, std::string email); void logout(); static std::string expiry_remaining(const std::string& expiry); + static constexpr const char* kSavePath = "test.json"; + static constexpr int kInitFailSleepMs = 1500; + static constexpr int kBadInputSleepMs = 3000; + static constexpr int kCloseSleepMs = 5000; class subscriptions_class { public: From 18e77a6b8bb860e7f79a58703a5846dd64b9003d Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 22:57:45 -0500 Subject: [PATCH 73/83] Add lockout helpers and delay utilities --- auth.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ auth.hpp | 12 ++++++++++++ 2 files changed, 60 insertions(+) diff --git a/auth.cpp b/auth.cpp index f3a0306..40932f6 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1759,6 +1759,54 @@ std::string KeyAuth::api::expiry_remaining(const std::string& expiry) return out.str(); } +void KeyAuth::api::init_fail_delay() +{ + Sleep(kInitFailSleepMs); +} + +void KeyAuth::api::bad_input_delay() +{ + Sleep(kBadInputSleepMs); +} + +void KeyAuth::api::close_delay() +{ + Sleep(kCloseSleepMs); +} + +bool KeyAuth::api::lockout_active(const lockout_state& state) +{ + return std::chrono::steady_clock::now() < state.locked_until; +} + +int KeyAuth::api::lockout_remaining_ms(const lockout_state& state) +{ + if (!lockout_active(state)) + return 0; + const auto now = std::chrono::steady_clock::now(); + const auto remaining = std::chrono::duration_cast(state.locked_until - now).count(); + return remaining > 0 ? static_cast(remaining) : 0; +} + +void KeyAuth::api::record_login_fail(lockout_state& state, int max_attempts, int lock_seconds) +{ + if (max_attempts < 1) + max_attempts = 1; + if (lock_seconds < 1) + lock_seconds = 1; + state.fails += 1; + if (state.fails >= max_attempts) { + state.fails = 0; + state.locked_until = std::chrono::steady_clock::now() + std::chrono::seconds(lock_seconds); + } +} + +void KeyAuth::api::reset_lockout(lockout_state& state) +{ + state.fails = 0; + state.locked_until = std::chrono::steady_clock::time_point{}; +} + int VerifyPayload(std::string signature, std::string timestamp, std::string body) { if (!prologues_ok()) { diff --git a/auth.hpp b/auth.hpp index 1587cc5..f9d4393 100644 --- a/auth.hpp +++ b/auth.hpp @@ -1,6 +1,7 @@ #include #include #include +#include #define CURL_STATICLIB @@ -52,6 +53,17 @@ namespace KeyAuth { static constexpr int kInitFailSleepMs = 1500; static constexpr int kBadInputSleepMs = 3000; static constexpr int kCloseSleepMs = 5000; + struct lockout_state { + int fails = 0; + std::chrono::steady_clock::time_point locked_until{}; + }; + static void init_fail_delay(); + static void bad_input_delay(); + static void close_delay(); + static bool lockout_active(const lockout_state& state); + static int lockout_remaining_ms(const lockout_state& state); + static void record_login_fail(lockout_state& state, int max_attempts = 3, int lock_seconds = 30); + static void reset_lockout(lockout_state& state); class subscriptions_class { public: From 1aeb57afdcb32ef9b8fbbd61f95e44eab7142740 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 23:37:16 -0500 Subject: [PATCH 74/83] Harden api host resolution checks --- auth.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/auth.cpp b/auth.cpp index 40932f6..e81b358 100644 --- a/auth.cpp +++ b/auth.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -1938,10 +1939,90 @@ std::string extract_host(const std::string& url) return host; } +static bool is_ip_literal(const std::string& host) +{ + sockaddr_in sa4{}; + sockaddr_in6 sa6{}; + return inet_pton(AF_INET, host.c_str(), &sa4.sin_addr) == 1 || + inet_pton(AF_INET6, host.c_str(), &sa6.sin6_addr) == 1; +} + +static bool is_private_or_loopback_ipv4(uint32_t addr_net_order) +{ + const uint32_t a = ntohl(addr_net_order); + const uint8_t b1 = static_cast(a >> 24); + const uint8_t b2 = static_cast((a >> 16) & 0xFF); + if (b1 == 10) return true; + if (b1 == 127) return true; + if (b1 == 0) return true; + if (b1 == 169 && b2 == 254) return true; + if (b1 == 192 && b2 == 168) return true; + if (b1 == 172) { + const uint8_t b3 = static_cast((a >> 8) & 0xFF); + if (b3 >= 16 && b3 <= 31) return true; + } + return false; +} + +static bool is_loopback_ipv6(const in6_addr& addr) +{ + static const in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + return std::memcmp(&addr, &loopback, sizeof(loopback)) == 0; +} + +static bool host_is_keyauth(const std::string& host_lower) +{ + if (host_lower == "keyauth.win" || host_lower == "keyauth.cc" || host_lower == "api-worker.keyauth.win") + return true; + const std::string suffix = ".keyauth.win"; + if (host_lower.size() > suffix.size() && + host_lower.compare(host_lower.size() - suffix.size(), suffix.size(), suffix) == 0) + return true; + return false; +} + +static bool host_resolves_private_only(const std::string& host, bool& has_public) +{ + has_public = false; + addrinfo hints{}; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + addrinfo* res = nullptr; + if (getaddrinfo(host.c_str(), nullptr, &hints, &res) != 0) + return false; + bool any = false; + bool all_private = true; + for (addrinfo* p = res; p; p = p->ai_next) { + if (!p->ai_addr) + continue; + any = true; + if (p->ai_family == AF_INET) { + const auto* sa = reinterpret_cast(p->ai_addr); + if (!is_private_or_loopback_ipv4(sa->sin_addr.s_addr)) { + all_private = false; + has_public = true; + } + } else if (p->ai_family == AF_INET6) { + const auto* sa = reinterpret_cast(p->ai_addr); + if (!is_loopback_ipv6(sa->sin6_addr)) { + all_private = false; + has_public = true; + } + } + } + freeaddrinfo(res); + if (!any) + return false; + return all_private; +} + bool hosts_override_present(const std::string& host) { if (host.empty()) return false; + std::string host_lower = host; + std::transform(host_lower.begin(), host_lower.end(), host_lower.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); const char* sysroot = std::getenv("SystemRoot"); std::string hosts_path = sysroot ? std::string(sysroot) : "C:\\Windows"; hosts_path += "\\System32\\drivers\\etc\\hosts"; @@ -1953,10 +2034,12 @@ bool hosts_override_present(const std::string& host) auto hash_pos = line.find('#'); if (hash_pos != std::string::npos) line = line.substr(0, hash_pos); - if (line.find(host) == std::string::npos) + std::transform(line.begin(), line.end(), line.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (line.find(host_lower) == std::string::npos) continue; // basic whole-word check - if (line.find(" " + host) != std::string::npos || line.find("\t" + host) != std::string::npos) + if (line.find(" " + host_lower) != std::string::npos || line.find("\t" + host_lower) != std::string::npos) return true; } return false; @@ -2389,6 +2472,21 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); } + // block loopback/private redirects for keyauth domains -nigel + { + std::string host_lower = host; + std::transform(host_lower.begin(), host_lower.end(), host_lower.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (host_is_keyauth(host_lower)) { + if (is_ip_literal(host_lower)) { + error(XorStr("API host must not be an IP literal.")); + } + bool has_public = false; + if (host_resolves_private_only(host_lower, has_public) && !has_public) { + error(XorStr("API host resolves to private or loopback.")); + } + } + } CURL* curl = curl_easy_init(); if (!curl) { From 0806774dc6af09543eeb0fa31ca1c113b7c67b9d Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 23:37:58 -0500 Subject: [PATCH 75/83] Wipe host strings after checks --- auth.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index e81b358..060f347 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2467,7 +2467,8 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } - const auto host = extract_host(url); + std::string host = extract_host(url); + ScopeWipe host_wipe(host); // block hosts-file redirects for api host -nigel if (hosts_override_present(host)) { error(XorStr("Hosts file override detected for API host.")); @@ -2475,6 +2476,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { // block loopback/private redirects for keyauth domains -nigel { std::string host_lower = host; + ScopeWipe host_lower_wipe(host_lower); std::transform(host_lower.begin(), host_lower.end(), host_lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (host_is_keyauth(host_lower)) { From 76849d31f03f0919c62067010b312f134723e944 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 23:41:06 -0500 Subject: [PATCH 76/83] Add https/proxy guards for api host --- auth.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/auth.cpp b/auth.cpp index 060f347..3fb7955 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1981,6 +1981,45 @@ static bool host_is_keyauth(const std::string& host_lower) return false; } +static bool is_https_url(const std::string& url) +{ + const std::string prefix = "https://"; + if (url.size() < prefix.size()) + return false; + for (size_t i = 0; i < prefix.size(); ++i) { + const char c = static_cast(std::tolower(static_cast(url[i]))); + if (c != prefix[i]) + return false; + } + return true; +} + +static bool proxy_env_set() +{ + const char* keys[] = { "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy" }; + for (const char* k : keys) { + const char* v = std::getenv(k); + if (v && *v) + return true; + } + return false; +} + +static bool winhttp_proxy_set() +{ + WINHTTP_PROXY_INFO info{}; + if (!WinHttpGetDefaultProxyConfiguration(&info)) + return false; + bool set = false; + if (info.lpszProxy && *info.lpszProxy) + set = true; + if (info.lpszProxyBypass && *info.lpszProxyBypass) + set = true; + if (info.lpszProxy) GlobalFree(info.lpszProxy); + if (info.lpszProxyBypass) GlobalFree(info.lpszProxyBypass); + return set; +} + static bool host_resolves_private_only(const std::string& host, bool& has_public) { has_public = false; @@ -2467,6 +2506,9 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { !func_region_ok(reinterpret_cast(&check_section_integrity))) { error(XorStr("function region check failed, possible hook detected.")); } + if (!is_https_url(url)) { + error(XorStr("API URL must use HTTPS.")); + } std::string host = extract_host(url); ScopeWipe host_wipe(host); // block hosts-file redirects for api host -nigel @@ -2483,6 +2525,9 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (is_ip_literal(host_lower)) { error(XorStr("API host must not be an IP literal.")); } + if (proxy_env_set() || winhttp_proxy_set()) { + error(XorStr("Proxy settings detected for API host.")); + } bool has_public = false; if (host_resolves_private_only(host_lower, has_public) && !has_public) { error(XorStr("API host resolves to private or loopback.")); From 1adec9cb50c03c10918aaca2b81b7e84cd068bdd Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Sun, 1 Mar 2026 23:43:12 -0500 Subject: [PATCH 77/83] Include winhttp for proxy checks --- auth.cpp | 1 + library.vcxproj | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 3fb7955..6c13a81 100644 --- a/auth.cpp +++ b/auth.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include diff --git a/library.vcxproj b/library.vcxproj index 884ee9e..17661dc 100644 --- a/library.vcxproj +++ b/library.vcxproj @@ -160,7 +160,7 @@ MachineX64 - libsodium.lib;libcurl.lib%(AdditionalDependencies) + libsodium.lib;libcurl.lib;winhttp.lib;%(AdditionalDependencies) .\curl;.\libsodium;%(AdditionalLibraryDirectories) /NODEFAULTLIB:libcurl.lib %(AdditionalOptions) From cf78f0c89dccd5321dbf8fd243e087e5f84e1165 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 08:16:21 -0500 Subject: [PATCH 78/83] Trigger example CI on library updates --- .github/workflows/sync-example.yml | 24 ++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 .github/workflows/sync-example.yml diff --git a/.github/workflows/sync-example.yml b/.github/workflows/sync-example.yml new file mode 100644 index 0000000..3184cde --- /dev/null +++ b/.github/workflows/sync-example.yml @@ -0,0 +1,24 @@ +name: sync-example + +on: + push: + branches: [ main ] + +jobs: + trigger-example-ci: + runs-on: ubuntu-latest + steps: + - name: dispatch example workflow + env: + EXAMPLE_REPO: ELF-Nigel/KeyAuth-CPP-Example + EXAMPLE_TOKEN: ${{ secrets.EXAMPLE_REPO_TOKEN }} + run: | + if [ -z "$EXAMPLE_TOKEN" ]; then + echo "EXAMPLE_REPO_TOKEN is not set" >&2 + exit 1 + fi + curl -s -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $EXAMPLE_TOKEN" \ + https://api.github.com/repos/$EXAMPLE_REPO/dispatches \ + -d '{"event_type":"sync-library"}' diff --git a/README.md b/README.md index 2d21b46..edb077a 100644 --- a/README.md +++ b/README.md @@ -161,3 +161,7 @@ of the licensor in the software. Any use of the licensor’s trademarks is subje to applicable law. Thank you for your compliance, we work hard on the development of KeyAuth and do not appreciate our copyright being infringed. + +## Example Sync +On push to `main`, this repo can trigger the example repo CI via a repository dispatch event. +Set `EXAMPLE_REPO_TOKEN` in this repo secrets (PAT with access to `ELF-Nigel/KeyAuth-CPP-Example`). From 8e1d6b5a8685d37f2093d783c9d3b6de912962b4 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 08:19:36 -0500 Subject: [PATCH 79/83] Remove example dispatch workflow --- .github/workflows/sync-example.yml | 24 ------------------------ README.md | 4 ---- 2 files changed, 28 deletions(-) delete mode 100644 .github/workflows/sync-example.yml diff --git a/.github/workflows/sync-example.yml b/.github/workflows/sync-example.yml deleted file mode 100644 index 3184cde..0000000 --- a/.github/workflows/sync-example.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: sync-example - -on: - push: - branches: [ main ] - -jobs: - trigger-example-ci: - runs-on: ubuntu-latest - steps: - - name: dispatch example workflow - env: - EXAMPLE_REPO: ELF-Nigel/KeyAuth-CPP-Example - EXAMPLE_TOKEN: ${{ secrets.EXAMPLE_REPO_TOKEN }} - run: | - if [ -z "$EXAMPLE_TOKEN" ]; then - echo "EXAMPLE_REPO_TOKEN is not set" >&2 - exit 1 - fi - curl -s -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $EXAMPLE_TOKEN" \ - https://api.github.com/repos/$EXAMPLE_REPO/dispatches \ - -d '{"event_type":"sync-library"}' diff --git a/README.md b/README.md index edb077a..2d21b46 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,3 @@ of the licensor in the software. Any use of the licensor’s trademarks is subje to applicable law. Thank you for your compliance, we work hard on the development of KeyAuth and do not appreciate our copyright being infringed. - -## Example Sync -On push to `main`, this repo can trigger the example repo CI via a repository dispatch event. -Set `EXAMPLE_REPO_TOKEN` in this repo secrets (PAT with access to `ELF-Nigel/KeyAuth-CPP-Example`). From 8934712bb1733cb1bd5824b743d0986406427215 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 08:25:48 -0500 Subject: [PATCH 80/83] Add safe network hardening controls --- auth.cpp | 37 +++++++++++++++++++++++++++++++++++++ auth.hpp | 13 ++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/auth.cpp b/auth.cpp index 6c13a81..48dc53a 100644 --- a/auth.cpp +++ b/auth.cpp @@ -2522,6 +2522,28 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { ScopeWipe host_lower_wipe(host_lower); std::transform(host_lower.begin(), host_lower.end(), host_lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (!allowed_hosts.empty()) { + bool allowed = false; + for (const auto& entry : allowed_hosts) { + std::string entry_lower = entry; + std::transform(entry_lower.begin(), entry_lower.end(), entry_lower.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (entry_lower.rfind("*.", 0) == 0) { + auto suffix = entry_lower.substr(1); + if (host_lower.size() >= suffix.size() && + host_lower.compare(host_lower.size() - suffix.size(), suffix.size(), suffix) == 0) { + allowed = true; + break; + } + } else if (host_lower == entry_lower) { + allowed = true; + break; + } + } + if (!allowed) { + error(XorStr("API host is not in allowed host list.")); + } + } if (host_is_keyauth(host_lower)) { if (is_ip_literal(host_lower)) { error(XorStr("API host must not be an IP literal.")); @@ -2549,6 +2571,10 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 0L); + curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS); curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(curl, CURLOPT_NOPROXY, XorStr("keyauth.win").c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); @@ -2559,6 +2585,17 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); curl_easy_setopt(curl, CURLOPT_USERAGENT, "KeyAuth"); + if (!pinned_public_keys.empty()) { +#ifdef CURLOPT_PINNEDPUBLICKEY + if (pinned_public_keys.size() > 1) { + error(XorStr("Multiple pinned public keys not supported.")); + } + curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, pinned_public_keys.at(0).c_str()); +#else + error(XorStr("Pinned public key not supported by this libcurl build.")); +#endif + } + // Perform the request CURLcode code = curl_easy_perform(curl); if (code != CURLE_OK) { diff --git a/auth.hpp b/auth.hpp index f9d4393..b683d4f 100644 --- a/auth.hpp +++ b/auth.hpp @@ -121,10 +121,21 @@ namespace KeyAuth { appdata app_data; responsedata response; Tfa tfa; + + // Optional network hardening controls (do not require backend changes). + void set_allowed_hosts(const std::vector& hosts) { allowed_hosts = hosts; } + void add_allowed_host(const std::string& host) { allowed_hosts.push_back(host); } + void clear_allowed_hosts() { allowed_hosts.clear(); } + + void set_pinned_public_keys(const std::vector& pins) { pinned_public_keys = pins; } + void add_pinned_public_key(const std::string& pin) { pinned_public_keys.push_back(pin); } + void clear_pinned_public_keys() { pinned_public_keys.clear(); } private: std::string sessionid, enckey; - static std::string req(std::string data, const std::string& url); + std::vector allowed_hosts; + std::vector pinned_public_keys; + std::string req(std::string data, const std::string& url); static void debugInfo(std::string data, std::string url, std::string response, std::string headers); static void setDebug(bool value); From 2ba18714183b807fb0e2e5237d9f13040e848607 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 08:31:18 -0500 Subject: [PATCH 81/83] Check .data/.rdata page protections --- auth.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/auth.cpp b/auth.cpp index 48dc53a..c8ff5b5 100644 --- a/auth.cpp +++ b/auth.cpp @@ -107,6 +107,8 @@ bool detour_suspect(const uint8_t* p); bool import_addresses_ok(); void snapshot_text_page_protections(); bool text_page_protections_ok(); +void snapshot_data_page_protections(); +bool data_page_protections_ok(); inline void secure_zero(std::string& value) noexcept; inline void securewipe(std::string& value) noexcept; @@ -134,6 +136,8 @@ struct TextHash { size_t offset; size_t len; uint32_t hash; }; std::vector text_hashes; std::atomic text_prot_ready{ false }; std::vector> text_protections; +std::atomic data_prot_ready{ false }; +std::vector> data_protections; std::atomic heavy_fail_streak{ 0 }; static inline void secure_zero(std::string& value) noexcept @@ -2210,6 +2214,7 @@ void snapshot_prologues() prologues_ready.store(true); snapshot_text_hashes(); snapshot_text_page_protections(); + snapshot_data_page_protections(); } bool prologues_ok() @@ -2292,6 +2297,46 @@ static bool get_text_section_info(std::uintptr_t& base, size_t& size) return false; } +static bool get_data_section_info(std::uintptr_t& base, size_t& size) +{ + const auto hmodule = GetModuleHandle(nullptr); + if (!hmodule) return false; + const auto base_0 = reinterpret_cast(hmodule); + const auto dos = reinterpret_cast(base_0); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; + const auto nt = reinterpret_cast(base_0 + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) return false; + auto section = IMAGE_FIRST_SECTION(nt); + for (auto i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) { + if (std::memcmp(section->Name, ".data", 5) == 0) { + base = base_0 + section->VirtualAddress; + size = section->Misc.VirtualSize; + return true; + } + } + return false; +} + +static bool get_rdata_section_info(std::uintptr_t& base, size_t& size) +{ + const auto hmodule = GetModuleHandle(nullptr); + if (!hmodule) return false; + const auto base_0 = reinterpret_cast(hmodule); + const auto dos = reinterpret_cast(base_0); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; + const auto nt = reinterpret_cast(base_0 + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) return false; + auto section = IMAGE_FIRST_SECTION(nt); + for (auto i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) { + if (std::memcmp(section->Name, ".rdata", 6) == 0) { + base = base_0 + section->VirtualAddress; + size = section->Misc.VirtualSize; + return true; + } + } + return false; +} + static uint32_t fnv1a(const uint8_t* data, size_t len) { uint32_t hash = 2166136261u; @@ -2359,6 +2404,38 @@ void snapshot_text_page_protections() text_prot_ready.store(true); } +void snapshot_data_page_protections() +{ + if (data_prot_ready.load()) + return; + data_protections.clear(); + const size_t page = 0x1000; + + std::uintptr_t base = 0; + size_t size = 0; + if (get_data_section_info(base, size)) { + for (size_t off = 0; off < size; off += page) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(base + off), &mbi, sizeof(mbi)) == 0) + continue; + data_protections.emplace_back(reinterpret_cast(mbi.BaseAddress), mbi.Protect); + } + } + + base = 0; + size = 0; + if (get_rdata_section_info(base, size)) { + for (size_t off = 0; off < size; off += page) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(base + off), &mbi, sizeof(mbi)) == 0) + continue; + data_protections.emplace_back(reinterpret_cast(mbi.BaseAddress), mbi.Protect); + } + } + + data_prot_ready.store(true); +} + bool text_page_protections_ok() { if (!text_prot_ready.load()) @@ -2378,6 +2455,24 @@ bool text_page_protections_ok() return true; } +bool data_page_protections_ok() +{ + if (!data_prot_ready.load()) + return true; + for (const auto& entry : data_protections) { + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery(reinterpret_cast(entry.first), &mbi, sizeof(mbi)) == 0) + return false; + const DWORD prot = mbi.Protect; + if (prot != entry.second) + return false; + const bool exec = (prot & PAGE_EXECUTE) || (prot & PAGE_EXECUTE_READ) || (prot & PAGE_EXECUTE_READWRITE) || (prot & PAGE_EXECUTE_WRITECOPY); + if (exec) + return false; + } + return true; +} + bool detour_suspect(const uint8_t* p) { @@ -2974,6 +3069,7 @@ void checkInit() { const bool heavy_ok = text_hashes_ok() && text_page_protections_ok() && + data_page_protections_ok() && import_addresses_ok() && !detour_suspect(reinterpret_cast(&VerifyPayload)) && !detour_suspect(reinterpret_cast(&checkInit)) && From 50406687f2edc3f7cefbea4e47a9fad7d02748eb Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 08:32:31 -0500 Subject: [PATCH 82/83] Add changelog for network and integrity hardening --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..23110ef --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## Unreleased +- Network hardening (client-side only): HTTPS-only transport, redirects disabled, and optional host allowlist + public key pinning to reduce the risk of traffic redirection or man-in-the-middle interception. +- Integrity checks: `.text` integrity and page-protection checks, plus non-executable page checks for `.data` and `.rdata` to help detect tampering (transparent, no stealth behavior). + +## Notes +- These protections are defensive and transparent; they do not alter the backend or API and are intended to reduce common redirection and tampering risks. From 412b9996ae12aeac3620b86a8ae614dfccb27b53 Mon Sep 17 00:00:00 2001 From: ELF-Nigel Date: Mon, 2 Mar 2026 18:03:18 -0500 Subject: [PATCH 83/83] Add threaded ban monitor helper --- README.md | 14 ++++++++++++++ auth.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ auth.hpp | 11 +++++++++++ 3 files changed, 75 insertions(+) diff --git a/README.md b/README.md index 2d21b46..aea9940 100644 --- a/README.md +++ b/README.md @@ -161,3 +161,17 @@ of the licensor in the software. Any use of the licensor’s trademarks is subje to applicable law. Thank you for your compliance, we work hard on the development of KeyAuth and do not appreciate our copyright being infringed. + +## Live ban monitor (threaded) + +Optional background check that polls every 45 seconds. Always stop it before exiting. + +```cpp +KeyAuthApp.start_ban_monitor(45, false, [] { + std::cout << "Blacklisted, exiting..." << std::endl; + exit(0); +}); + +// later, before exit +KeyAuthApp.stop_ban_monitor(); +``` diff --git a/auth.cpp b/auth.cpp index c8ff5b5..07bb1cc 100644 --- a/auth.cpp +++ b/auth.cpp @@ -1726,6 +1726,56 @@ void KeyAuth::api::logout() { load_response_data(json); } +void KeyAuth::api::start_ban_monitor(int interval_seconds, bool check_session, std::function on_ban) +{ + if (ban_monitor_running_) { + return; + } + + if (interval_seconds < 1) { + interval_seconds = 1; + } + + ban_monitor_detected_ = false; + ban_monitor_running_ = true; + ban_monitor_thread_ = std::thread([this, interval_seconds, check_session, on_ban]() { + while (ban_monitor_running_) { + if (check_session) { + this->check(false); + } + + if (this->checkblack()) { + ban_monitor_detected_ = true; + ban_monitor_running_ = false; + if (on_ban) { + on_ban(); + } + return; + } + + std::this_thread::sleep_for(std::chrono::seconds(interval_seconds)); + } + }); +} + +void KeyAuth::api::stop_ban_monitor() +{ + ban_monitor_running_ = false; + if (ban_monitor_thread_.joinable()) { + ban_monitor_thread_.join(); + } +} + +bool KeyAuth::api::ban_monitor_running() const +{ + return ban_monitor_running_.load(); +} + +bool KeyAuth::api::ban_monitor_detected() const +{ + return ban_monitor_detected_.load(); +} + std::string KeyAuth::api::expiry_remaining(const std::string& expiry) { if (expiry.empty()) diff --git a/auth.hpp b/auth.hpp index b683d4f..6e40730 100644 --- a/auth.hpp +++ b/auth.hpp @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #define CURL_STATICLIB @@ -48,6 +51,10 @@ namespace KeyAuth { void fetchstats(); void forgot(std::string username, std::string email); void logout(); + void start_ban_monitor(int interval_seconds = 45, bool check_session = false, std::function on_ban = {}); + void stop_ban_monitor(); + bool ban_monitor_running() const; + bool ban_monitor_detected() const; static std::string expiry_remaining(const std::string& expiry); static constexpr const char* kSavePath = "test.json"; static constexpr int kInitFailSleepMs = 1500; @@ -214,6 +221,10 @@ namespace KeyAuth { } } + std::atomic ban_monitor_running_{ false }; + std::atomic ban_monitor_detected_{ false }; + std::thread ban_monitor_thread_; + nlohmann::json response_decoder; };