cleanup + stdout/http to file

This commit is contained in:
lightbulblighter 2022-08-06 01:16:12 -07:00
parent 6465e9e2b5
commit 7b1ba0e8c0
No known key found for this signature in database
GPG Key ID: 58D6EDC2C38C9B3B
8 changed files with 197 additions and 147 deletions

View File

@ -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<bool, std::string> 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<bool, std::string> 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"))
{

View File

@ -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;
}
}
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<bool, std::map<std::string, std::string>> Helpers::parseURL(std::string url)
{
CURLU* curl = curl_url();
CURLUcode result = curl_url_set(curl, CURLUPART_URL, url.c_str(), 0);
std::map<std::string, std::string> 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<bool, std::string> 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);
}

View File

@ -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<bool, std::map<std::string, std::string>> parsed = Helpers::parseURL(_this->url);
Http _changed = *_this;
if (result == CURLE_OK)
if (parsed.first)
{
char* path;
char* host;
char* query;
std::map<std::string, std::string> 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<std::string, std::string> source = Helpers::parseQueryString(query);
std::map<std::string, std::string> source = Helpers::parseQueryString(url["query"]);
std::map<std::string, std::string> 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<bool, std::string> 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<bool, std::map<std::string, std::string>> result = Helpers::parseURL(url);
if (!result.first)
{
return false;
}
char* scheme;
char* host;
std::map<std::string, std::string> 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;

View File

@ -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<std::string*>((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);
}
stdoutLog.open(stdoutLogPath, std::ios::out);
stdoutLog << "[" << time << "] [" << stringifiedType << "] " << message << std::endl;
stdoutLog.close();
}
#endif

View File

@ -1,5 +1,7 @@
#pragma once
#include <curl/curl.h>
#include "Configuration.h"
class Helpers
@ -17,4 +19,8 @@ public:
static std::string joinQueryString(std::map<std::string, std::string> 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<bool, std::map<std::string, std::string>> parseURL(std::string url);
static std::pair<bool, std::string> httpGet(std::string url);
};

View File

@ -6,6 +6,10 @@
#include "Configuration.h"
#include "Helpers.h"
#ifdef SERVER
#include "Hooks/StandardOut.h"
#endif
struct Http
{
#if PADDING_STRUCT != 0

View File

@ -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
#define RBX__MESSAGE_ERROR 3
#endif

View File

@ -12,5 +12,7 @@
#include <algorithm>
#include <stdexcept>
#include <thread>
#include <ctime>
#include <ios>
namespace fs = std::filesystem;