diff --git a/auth.cpp b/auth.cpp index 90fd644..e1aaf62 100644 --- a/auth.cpp +++ b/auth.cpp @@ -72,6 +72,10 @@ void checkFiles(); 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; +void integrity_check(); +void integrity_watchdog(); std::string seed; void cleanUpSeedData(const std::string& seed); std::string signature; @@ -80,10 +84,13 @@ bool initialized; std::string API_PUBLIC_KEY = "5586b4bc69c7a4b487e4563a4cd96afd39140f919bd31cea7d1c6a1e8439422b"; 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() { 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); @@ -100,8 +107,11 @@ 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; + 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) { @@ -1397,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); @@ -1421,8 +1431,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 +1611,16 @@ void KeyAuth::api::logout() { int VerifyPayload(std::string signature, std::string timestamp, std::string body) { - long long unix_timestamp = std::stoll(timestamp); + integrity_check(); + 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 +1697,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; } @@ -1693,6 +1728,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) { @@ -1728,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); } @@ -2059,6 +2098,38 @@ 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(); + 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.")); + } + } +} + +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; + 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) diff --git a/auth.hpp b/auth.hpp index f229460..31e0920 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,10 @@ 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"]) { + 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")]; std::string messageoutput = sub["message"]; 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.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) { 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 +}