diff --git a/Tadah.DLL/Discord.cpp b/Tadah.DLL/Discord.cpp index 4af7fa6..4965f15 100644 --- a/Tadah.DLL/Discord.cpp +++ b/Tadah.DLL/Discord.cpp @@ -10,10 +10,7 @@ int placeId; void InitializeDiscord() { // Check if Discord should be enabled by checking if the binary is the client as well as if the binary's containing folder contains a ".nodiscord" file - char buffer[MAX_PATH]; - GetModuleFileNameA(NULL, buffer, MAX_PATH); - - std::string path = std::string(buffer); + std::string path = Helpers::getModulePath(); if (fs::path(path).stem() != "TadahPlayer") { @@ -26,32 +23,14 @@ void InitializeDiscord() } // Get the username and placeId - CURL* curl = curl_easy_init(); - CURLcode result; - long response = 0; - std::string data; - - if (!curl) - { - return; - } - - curl_easy_setopt(curl, CURLOPT_URL, std::string(BASE_URL + std::string("/api/places/information?ticket=") + ticket)); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Helpers::write); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - result = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - curl_easy_cleanup(curl); - - if (result != CURLE_OK || response != 200) + std::pair response = Helpers::httpGet(BASE_URL + std::string("/api/places/information?ticket=") + ticket); + if (!response.first) { return; } rapidjson::Document document; - document.Parse(data.c_str()); + document.Parse(response.second.c_str()); if (document.HasParseError() || !document.HasMember("username") || !document.HasMember("placeId")) { @@ -79,32 +58,14 @@ void UpdatePresence() int max = 0; // Get title, size, and max - CURL* curl = curl_easy_init(); - CURLcode result; - long response = 0; - std::string data; - - if (!curl) - { - return; - } - - curl_easy_setopt(curl, CURLOPT_URL, std::string(BASE_URL + std::string("/api/places/information?id=") + std::to_string(placeId))); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Helpers::write); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - result = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - curl_easy_cleanup(curl); - - if (result != CURLE_OK || response != 200) + std::pair response = Helpers::httpGet(BASE_URL + std::string("/api/places/information?id=") + std::to_string(placeId)); + if (!response.first) { return; } rapidjson::Document document; - document.Parse(data.c_str()); + document.Parse(response.second.c_str()); if (document.HasParseError() || !document.HasMember("title") || !document.HasMember("size") || !document.HasMember("max")) { diff --git a/Tadah.DLL/Helpers.cpp b/Tadah.DLL/Helpers.cpp index deb9861..6f5fb13 100644 --- a/Tadah.DLL/Helpers.cpp +++ b/Tadah.DLL/Helpers.cpp @@ -121,4 +121,91 @@ size_t Helpers::write(char* contents, size_t size, size_t memory, void* pointer) { ((std::string*)pointer)->append((char*)contents, size * memory); return size * memory; -} \ No newline at end of file +} + +std::string Helpers::getModulePath() +{ + char buffer[MAX_PATH]; + GetModuleFileNameA(NULL, buffer, MAX_PATH); + + return std::string(buffer); +} + +std::string Helpers::getISOTimestamp() +{ + time_t now = time(0); + struct tm now_gmt {}; + + gmtime_s(&now_gmt, &now); + + char buffer[20]; + strftime(buffer, sizeof(buffer), "%FT%TZ", &now_gmt); + + return std::string(buffer); +} + +std::pair> Helpers::parseURL(std::string url) +{ + CURLU* curl = curl_url(); + CURLUcode result = curl_url_set(curl, CURLUPART_URL, url.c_str(), 0); + + std::map map; + bool success = false; + + if (result == CURLE_OK) + { + success = true; + + char* path; + char* host; + char* query; + + curl_url_get(curl, CURLUPART_PATH, &path, 0); + curl_url_get(curl, CURLUPART_HOST, &host, 0); + curl_url_get(curl, CURLUPART_QUERY, &query, 0); + curl_url_cleanup(curl); + + map = { + { "path", std::string(path) }, + { "host", std::string(host) }, + { "query", std::string(query) } + }; + + curl_free(path); + curl_free(host); + curl_free(query); + } + + return std::make_pair(success, map); +} + +std::pair Helpers::httpGet(std::string url) +{ + bool success = false; + std::string data; + + CURL* curl = curl_easy_init(); + CURLcode result; + long response = 0; + + if (!curl) + { + return std::make_pair(false, ""); + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Helpers::write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + result = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + + curl_easy_cleanup(curl); + + if (result != CURLE_OK || response != 200) + { + return std::make_pair(false, ""); + } + + return std::make_pair(true, data); +} diff --git a/Tadah.DLL/Hooks/Http.cpp b/Tadah.DLL/Hooks/Http.cpp index 765cc85..20d5d83 100644 --- a/Tadah.DLL/Hooks/Http.cpp +++ b/Tadah.DLL/Hooks/Http.cpp @@ -14,63 +14,34 @@ Http__trustCheck_t Http__trustCheck = (Http__trustCheck_t)ADDRESS_HTTP__TRUSTCHE void __fastcall Http__httpGetPostWinInet_hook(Http* _this, void*, bool isPost, int a3, bool compressData, LPCSTR additionalHeaders, int a6) { - CURLU* curl = curl_url(); - CURLUcode result = curl_url_set(curl, CURLUPART_URL, _this->url.c_str(), 0); - + std::pair> parsed = Helpers::parseURL(_this->url); Http _changed = *_this; - if (result == CURLE_OK) + if (parsed.first) { - char* path; - char* host; - char* query; + std::map url = parsed.second; - curl_url_get(curl, CURLUPART_PATH, &path, 0); - curl_url_get(curl, CURLUPART_HOST, &host, 0); - curl_url_get(curl, CURLUPART_QUERY, &query, 0); - curl_url_cleanup(curl); - - if (path != NULL && host != NULL && query != NULL) + if (!url["path"].empty() && !url["host"].empty() && !url["query"].empty()) { - std::string _path = Helpers::toLower(std::string(path)); - std::string _host = std::string(host); - std::string _query = std::string(query); + url["path"] = Helpers::toLower(url["path"]); - if (_host == "roblox.com" || _host == "www.roblox.com") + if (url["host"] == "roblox.com" || url["host"] == "www.roblox.com") { - if (_path == "/asset" || _path == "/asset/" || _path == "/asset/default.ashx") + if (url["path"] == "/asset" || url["path"] == "/asset/" || url["path"] == "/asset/default.ashx") { - _changed.url = "https://assetdelivery.roblox.com/v1/asset/?" + _query; + _changed.url = "https://assetdelivery.roblox.com/v1/asset/?" + url["query"]; _this = &_changed; } - else if (_path == "/thumbs/asset.ashx" || _path == "/thumbs/avatar.ashx") + else if (url["path"] == "/thumbs/asset.ashx" || url["path"] == "/thumbs/avatar.ashx") { - /* - Both Roblox endpoints require thumbnailFormatId to be set. We will make the default value for it as 0. + std::string api = "https://www.roblox.com/" + std::string(url["path"] == "/thumbs/asset.ashx" ? "asset" : "avatar") + "/request-thumbnail-fix"; - Asset.ashx -> requires overrideModeration (default false) -> /asset/request-thumbnail-fix - Avatar.ashx -> requires dummy (default false) -> /avatar/request-thumbnail-fix - - 1. Parse query - 2. Construct a brand new blank query with thumbnailFormatId as 0 and dummy/overrideModeration as false (if Avatar.ashx or Asset.ashx) - 3. Merge the old query with priority over the old query so that if they declared any of the special variables, ours gets overwritten - 4. Rename id (if found) to assetId or userId (specific to the endpoint) - 5. Append to the Roblox url (specific to the endpoint) - 6. Fetch Roblox API - 7. Parse JSON - 8. Set the URL as the given url - */ - - std::string api = "https://www.roblox.com/" + std::string(_path == "/thumbs/asset.ashx" ? "asset" : "avatar") + "/request-thumbnail-fix"; - - std::map source = Helpers::parseQueryString(query); + std::map source = Helpers::parseQueryString(url["query"]); std::map fixed = { - { _path == "/thumbs/asset.ashx" ? "overrideModeration" : "dummy", "false" }, + { url["path"] == "/thumbs/asset.ashx" ? "overrideModeration" : "dummy", "false" }, { "thumbnailFormatId", "0" } }; - - // the modern Roblox API doesn't care about parameter capitalization because of asp.net quirks - // thus, we are able to do this :-) + for (auto& pair : source) { fixed[Helpers::toLower(pair.first)] = pair.second; @@ -79,42 +50,21 @@ void __fastcall Http__httpGetPostWinInet_hook(Http* _this, void*, bool isPost, i if (fixed.find("id") != fixed.end()) { auto handler = fixed.extract("id"); - handler.key() = _path == "/thumbs/asset.ashx" ? "assetId" : "userId"; + handler.key() = url["path"] == "/thumbs/asset.ashx" ? "assetId" : "userId"; fixed.insert(std::move(handler)); } api += Helpers::joinQueryString(fixed); - // get the api response - CURL* curl = curl_easy_init(); - CURLcode result; - long response = 0; - std::string data; - - if (!curl) - { - throw std::runtime_error("Failed to initialize cURL"); - } - - curl_easy_setopt(curl, CURLOPT_URL, api); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Helpers::write); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - result = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - curl_easy_cleanup(curl); - - if (result != CURLE_OK) + // Get the API response + std::pair response = Helpers::httpGet(api); + if (!response.first) { throw std::runtime_error("Unexpected error occurred when fetching Roblox API: 0x0"); } - if (response != 200) - { - throw std::runtime_error("Unexpected error occurred when fetching Roblox API: Response code was " + std::to_string(response)); - } + std::string data = response.second; rapidjson::Document document; document.Parse(data.c_str()); @@ -139,6 +89,12 @@ void __fastcall Http__httpGetPostWinInet_hook(Http* _this, void*, bool isPost, i } } +#ifdef SERVER + httpLog.open(httpLogPath, std::ios::out); + httpLog << "[" << Helpers::getISOTimestamp << "] [" << (isPost ? "POST" : "GET") << "] '" << _this->url << "'" << std::endl; + httpLog.close(); +#endif + Http__httpGetPostWinInet(_this, isPost, a3, compressData, additionalHeaders, a6); } @@ -146,19 +102,11 @@ bool __fastcall Http__trustCheck_hook(const char* url) { if (strlen(url) == 7 && !Helpers::isASCII(url)) { - // so the client does this really fucking stupid thing where if it opens an ie window, - // it passes a char**, and not a char* - // no idea if thats a detours quirk (i doubt it) or if thats how its just actually handled - // practically no url is ever going to be seven characters long so it doesn't really matter - url = ((char**)url)[0]; } std::string _url = std::string(url); - // checking for embedded schemes must come BEFORE checking if it's valid - // cURL does not treat embedded resources as URLs - if (_url == "about:blank") { return true; @@ -172,35 +120,28 @@ bool __fastcall Http__trustCheck_hook(const char* url) } } - CURLU* curl = curl_url(); - CURLUcode result = curl_url_set(curl, CURLUPART_URL, url, 0); - - if (result != CURLE_OK) + std::pair> result = Helpers::parseURL(url); + if (!result.first) { return false; } - char* scheme; - char* host; + std::map parsed = result.second; - curl_url_get(curl, CURLUPART_SCHEME, &scheme, 0); - curl_url_get(curl, CURLUPART_HOST, &host, 0); - curl_url_cleanup(curl); - - if (std::find(Helpers::allowedSchemes.begin(), Helpers::allowedSchemes.end(), std::string(scheme)) != Helpers::allowedSchemes.end()) + if (!parsed["scheme"].empty() && !parsed["host"].empty()) { - std::string _host = std::string(host); - - // this is crude and inefficient but I don't care - for (std::string wildcard : Helpers::allowedWildcardDomains) + if (std::find(Helpers::allowedSchemes.begin(), Helpers::allowedSchemes.end(), std::string(parsed["scheme"])) != Helpers::allowedSchemes.end()) { - if (_host.size() >= wildcard.size() && 0 == wildcard.compare(_host.size() - wildcard.size(), wildcard.size(), wildcard)) + for (std::string wildcard : Helpers::allowedWildcardDomains) { - return true; + if (parsed["host"].size() >= wildcard.size() && 0 == wildcard.compare(parsed["host"].size() - wildcard.size(), wildcard.size(), wildcard)) + { + return true; + } } - } - return std::find(Helpers::allowedDomains.begin(), Helpers::allowedDomains.end(), _host) != Helpers::allowedDomains.end(); + return std::find(Helpers::allowedDomains.begin(), Helpers::allowedDomains.end(), parsed["host"]) != Helpers::allowedDomains.end(); + } } return false; diff --git a/Tadah.DLL/Hooks/StandardOut.cpp b/Tadah.DLL/Hooks/StandardOut.cpp index c14f5d8..9fcfddc 100644 --- a/Tadah.DLL/Hooks/StandardOut.cpp +++ b/Tadah.DLL/Hooks/StandardOut.cpp @@ -1,10 +1,17 @@ #include "pch.h" -#include "Patches.h" #include "Hooks/StandardOut.h" +#ifdef SERVER + HANDLE outputHandle; +std::string httpLogPath; +std::string stdoutLogPath; + +std::ofstream httpLog; +std::ofstream stdoutLog; + void InitializeOutput() { AllocConsole(); @@ -16,8 +23,25 @@ void InitializeOutput() #ifdef _DEBUG SetConsoleTextAttribute(outputHandle, FOREGROUND_RED | FOREGROUND_GREEN); - printf("Compiled as Debug\n\n"); // + printf("Compiled as Debug\n\n"); #endif + + // Initialize file logging + fs::create_directory(fs::path(Helpers::getModulePath()).parent_path() / "logs"); + + std::string time = Helpers::getISOTimestamp(); + + stdoutLogPath = (fs::path(Helpers::getModulePath()).parent_path() / "logs" / (time + "-StandardOut.log")).string(); + httpLogPath = (fs::path(Helpers::getModulePath()).parent_path() / "logs" / (time + "-Http.log")).string(); + + stdoutLog.open(stdoutLogPath, std::ios::out); + httpLog.open(stdoutLogPath, std::ios::out); + + stdoutLog << "Tadah.DLL v1.0.0 - StandardOut" << std::endl << std::endl; + httpLog << "Tadah.DLL v1.0.0 - Http" << std::endl << std::endl; + + stdoutLog.close(); + httpLog.close(); } StandardOut__print_t StandardOut__print = (StandardOut__print_t)ADDRESS_STANDARDOUT__PRINT; @@ -31,22 +55,35 @@ void __fastcall StandardOut__print_hook(int _this, void*, int type, std::string* message = reinterpret_cast((int)message + 4); #endif + std::string stringifiedType; + std::string time = Helpers::getISOTimestamp(); + switch (type) { case RBX__MESSAGE_OUTPUT: SetConsoleTextAttribute(outputHandle, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + stringifiedType = "out"; break; case RBX__MESSAGE_INFO: SetConsoleTextAttribute(outputHandle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + stringifiedType = "info"; break; case RBX__MESSAGE_WARNING: SetConsoleTextAttribute(outputHandle, FOREGROUND_RED | FOREGROUND_GREEN); + stringifiedType = "warn"; break; case RBX__MESSAGE_ERROR: + stringifiedType = "err"; SetConsoleTextAttribute(outputHandle, FOREGROUND_RED | FOREGROUND_INTENSITY); break; } - + printf("%s\n", message->c_str()); SetConsoleTextAttribute(outputHandle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); -} \ No newline at end of file + + stdoutLog.open(stdoutLogPath, std::ios::out); + stdoutLog << "[" << time << "] [" << stringifiedType << "] " << message << std::endl; + stdoutLog.close(); +} + +#endif \ No newline at end of file diff --git a/Tadah.DLL/Include/Helpers.h b/Tadah.DLL/Include/Helpers.h index c4a52fa..5150baa 100644 --- a/Tadah.DLL/Include/Helpers.h +++ b/Tadah.DLL/Include/Helpers.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "Configuration.h" class Helpers @@ -17,4 +19,8 @@ public: static std::string joinQueryString(std::map query); static std::string ws2s(std::wstring widestring); static size_t write(char* contents, size_t size, size_t memory, void* pointer); + static std::string getModulePath(); + static std::string getISOTimestamp(); + static std::pair> parseURL(std::string url); + static std::pair httpGet(std::string url); }; \ No newline at end of file diff --git a/Tadah.DLL/Include/Hooks/Http.h b/Tadah.DLL/Include/Hooks/Http.h index b751161..a8909ee 100644 --- a/Tadah.DLL/Include/Hooks/Http.h +++ b/Tadah.DLL/Include/Hooks/Http.h @@ -6,6 +6,10 @@ #include "Configuration.h" #include "Helpers.h" +#ifdef SERVER +#include "Hooks/StandardOut.h" +#endif + struct Http { #if PADDING_STRUCT != 0 diff --git a/Tadah.DLL/Include/Hooks/StandardOut.h b/Tadah.DLL/Include/Hooks/StandardOut.h index 37ad63c..8990b17 100644 --- a/Tadah.DLL/Include/Hooks/StandardOut.h +++ b/Tadah.DLL/Include/Hooks/StandardOut.h @@ -1,6 +1,10 @@ #pragma once #include "Configuration.h" +#include "Patches.h" +#include "Helpers.h" + +#ifdef SERVER void InitializeOutput(); @@ -8,7 +12,15 @@ typedef void(__thiscall* StandardOut__print_t)(int _this, int type, std::string* void __fastcall StandardOut__print_hook(int _this, void*, int type, std::string* message); extern StandardOut__print_t StandardOut__print; +extern std::string httpLogPath; +extern std::string stdoutLogPath; + +extern std::ofstream httpLog; +extern std::ofstream stdoutLog; + #define RBX__MESSAGE_INFO 0 #define RBX__MESSAGE_OUTPUT 1 #define RBX__MESSAGE_WARNING 2 -#define RBX__MESSAGE_ERROR 3 \ No newline at end of file +#define RBX__MESSAGE_ERROR 3 + +#endif \ No newline at end of file diff --git a/Tadah.DLL/Include/pch.h b/Tadah.DLL/Include/pch.h index d288abd..1678d56 100644 --- a/Tadah.DLL/Include/pch.h +++ b/Tadah.DLL/Include/pch.h @@ -12,5 +12,7 @@ #include #include #include +#include +#include namespace fs = std::filesystem; \ No newline at end of file