mirror of
https://github.com/ApfelTeeSaft/Embeddium.git
synced 2026-08-26 19:23:26 +00:00
246 lines
7.1 KiB
C++
246 lines
7.1 KiB
C++
#include "CurlHook.h"
|
|
#include "HookManager.h"
|
|
|
|
#include "../vendor/cURL/curl.h"
|
|
#include "../Network/UriParser.h"
|
|
#include "../Core/Config.h"
|
|
#include "../Core/Log.h"
|
|
#include "../Utilities/StringUtils.h"
|
|
|
|
#include <Windows.h>
|
|
#include <intrin.h>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <cstdint>
|
|
|
|
namespace Embeddium::CurlHook {
|
|
|
|
using FnCurlSetOpt = CURLcode(*)(struct Curl_easy*, CURLoption, va_list);
|
|
using FnCurlEasySetOpt = CURLcode(*)(struct Curl_easy*, CURLoption, ...);
|
|
|
|
static FnCurlSetOpt gCurlSetOpt = nullptr;
|
|
static FnCurlEasySetOpt gCurlEasySetOpt = nullptr;
|
|
|
|
static CURLcode ForwardOpt(struct Curl_easy* data, CURLoption tag, ...) {
|
|
va_list arg;
|
|
va_start(arg, tag);
|
|
CURLcode r;
|
|
if (gCurlSetOpt) {
|
|
r = gCurlSetOpt(data, tag, arg);
|
|
} else {
|
|
uintptr_t val = va_arg(arg, uintptr_t);
|
|
r = gCurlEasySetOpt(data, tag, val);
|
|
}
|
|
va_end(arg);
|
|
return r;
|
|
}
|
|
|
|
static uintptr_t FindPattern(const char* pattern, const char* mask) {
|
|
HMODULE hMod = GetModuleHandleA(nullptr);
|
|
if (!hMod) return 0;
|
|
|
|
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(hMod);
|
|
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(
|
|
reinterpret_cast<uint8_t*>(hMod) + dos->e_lfanew);
|
|
|
|
uintptr_t base = reinterpret_cast<uintptr_t>(hMod);
|
|
uintptr_t size = nt->OptionalHeader.SizeOfImage;
|
|
size_t patLen = strlen(mask);
|
|
|
|
for (uintptr_t i = 0; i < size - patLen; ++i) {
|
|
bool found = true;
|
|
for (size_t j = 0; j < patLen && found; ++j) {
|
|
if (mask[j] == 'x')
|
|
found = reinterpret_cast<uint8_t*>(base + i)[j] == static_cast<uint8_t>(pattern[j]);
|
|
}
|
|
if (found) return base + i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uintptr_t SigScan(const char* sig) {
|
|
std::string bytes, mask;
|
|
const char* p = sig;
|
|
while (*p) {
|
|
while (*p == ' ') ++p;
|
|
if (!*p) break;
|
|
if (*p == '?') {
|
|
bytes.push_back(0);
|
|
mask.push_back('?');
|
|
++p;
|
|
if (*p == '?') ++p;
|
|
} else {
|
|
bytes.push_back(static_cast<char>(static_cast<uint8_t>(strtoul(p, nullptr, 16))));
|
|
mask.push_back('x');
|
|
p += 2;
|
|
}
|
|
}
|
|
return FindPattern(bytes.data(), mask.c_str());
|
|
}
|
|
|
|
static bool ShouldRedirect(const Uri& uri) {
|
|
return StringUtils::EndsWith(uri.Host, "ol.epicgames.com")
|
|
|| StringUtils::EndsWith(uri.Host, "epicgames.dev")
|
|
|| StringUtils::EndsWith(uri.Host, "ol.epicgames.net")
|
|
|| StringUtils::EndsWith(uri.Host, ".akamaized.net")
|
|
|| StringUtils::EndsWith(uri.Host, "on.epicgames.com")
|
|
|| StringUtils::EndsWith(uri.Host, "game-social.epicgames.com")
|
|
|| StringUtils::Contains(uri.Host, "superawesome.com")
|
|
|| StringUtils::Contains(uri.Host, "ak.epicgames.com");
|
|
}
|
|
|
|
static CURLcode CurlEasySetOptDetour_Impl(struct Curl_easy* data, CURLoption tag, va_list arg)
|
|
{
|
|
CURLcode result = CURLE_OK;
|
|
|
|
if (!data)
|
|
return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
if (tag == CURLOPT_SSL_VERIFYPEER)
|
|
{
|
|
Log::Debug("CurlHook: CURLOPT_SSL_VERIFYPEER — disabling");
|
|
result = ForwardOpt(data, tag, 0L);
|
|
Log::Debug("CurlHook: CURLOPT_SSL_VERIFYPEER — ok");
|
|
}
|
|
else if (tag == CURLOPT_URL)
|
|
{
|
|
const char* rawUrl = va_arg(arg, const char*);
|
|
std::string url = rawUrl ? rawUrl : "";
|
|
|
|
Uri uri = Uri::Parse(url);
|
|
|
|
if (ShouldRedirect(uri))
|
|
{
|
|
std::string redirected = Uri::CreateUri(
|
|
Config::kRedirectProtocol,
|
|
Config::kRedirectHost,
|
|
Config::kRedirectPort,
|
|
uri.Path,
|
|
uri.QueryString
|
|
);
|
|
|
|
Log::Debug("CurlHook: " + url + " -> " + redirected);
|
|
|
|
result = ForwardOpt(data, tag, redirected.c_str());
|
|
|
|
Log::Debug("CurlHook: CURLOPT_URL forward — ok");
|
|
}
|
|
else
|
|
{
|
|
Log::Debug("CurlHook: CURLOPT_URL passthrough — " + url);
|
|
|
|
result = ForwardOpt(data, tag, url.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char dbg[64];
|
|
wsprintfA(dbg, "CurlHook: tag=%d forwarding", (int)tag);
|
|
Log::Debug(dbg);
|
|
|
|
if (gCurlSetOpt)
|
|
{
|
|
result = gCurlSetOpt(data, tag, arg);
|
|
}
|
|
else
|
|
{
|
|
uintptr_t val = va_arg(arg, uintptr_t);
|
|
result = gCurlEasySetOpt(data, tag, val);
|
|
}
|
|
|
|
wsprintfA(dbg, "CurlHook: tag=%d ok", (int)tag);
|
|
Log::Debug(dbg);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static CURLcode CurlEasySetOptDetour(struct Curl_easy* data, CURLoption tag, ...)
|
|
{
|
|
va_list arg;
|
|
va_start(arg, tag);
|
|
|
|
CURLcode result = CURLE_FAILED_INIT;
|
|
|
|
__try
|
|
{
|
|
result = CurlEasySetOptDetour_Impl(data, tag, arg);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DWORD code = GetExceptionCode();
|
|
|
|
char msg[128];
|
|
wsprintfA(msg, "CurlHook: CRASH in detour tag=%d code=0x%08X",
|
|
(int)tag, (unsigned)code);
|
|
|
|
Log::Error(msg);
|
|
OutputDebugStringA(msg);
|
|
}
|
|
|
|
va_end(arg);
|
|
return result;
|
|
}
|
|
|
|
bool Install() {
|
|
Log::Debug("CurlHook: scanning for curl_easy_setopt...");
|
|
|
|
uintptr_t addrEasySetOpt = SigScan(
|
|
"89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 "
|
|
"48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
|
|
|
|
for (int attempt = 0; attempt < 10 && !addrEasySetOpt; ++attempt) {
|
|
Sleep(300);
|
|
addrEasySetOpt = SigScan(
|
|
"89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 "
|
|
"48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
|
|
}
|
|
|
|
if (!addrEasySetOpt) {
|
|
Log::Error("CurlHook: curl_easy_setopt not found — redirects disabled");
|
|
return false;
|
|
}
|
|
|
|
char hexBuf[24];
|
|
wsprintfA(hexBuf, "0x%llX", (unsigned long long)addrEasySetOpt);
|
|
Log::Info(std::string("CurlHook: curl_easy_setopt at ") + hexBuf);
|
|
|
|
uintptr_t addrSetOpt = SigScan(
|
|
"48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 30 "
|
|
"33 ED 49 8B F0 48 8B D9");
|
|
if (!addrSetOpt)
|
|
addrSetOpt = SigScan(
|
|
"48 89 5C 24 08 48 89 6C 24 10 56 57 41 56 48 83 EC 50 "
|
|
"33 ED 49 8B F0 8B DA 48 8B F9");
|
|
if (!addrSetOpt)
|
|
addrSetOpt = SigScan(
|
|
"48 89 5C 24 ? 55 56 57 41 56 41 57 48 83 EC 50 33 DB "
|
|
"49 8B F0 48 8B F9 8B EB 81 FA");
|
|
|
|
if (addrSetOpt) {
|
|
wsprintfA(hexBuf, "0x%llX", (unsigned long long)addrSetOpt);
|
|
Log::Info(std::string("CurlHook: curl_setopt (va_list) at ") + hexBuf);
|
|
} else {
|
|
Log::Warn("CurlHook: curl_setopt (va_list) not found — will use trampoline fallback");
|
|
}
|
|
|
|
gCurlSetOpt = reinterpret_cast<FnCurlSetOpt>(addrSetOpt);
|
|
gCurlEasySetOpt = reinterpret_cast<FnCurlEasySetOpt>(addrEasySetOpt);
|
|
|
|
bool ok = HookManager::Install(
|
|
reinterpret_cast<void*>(addrEasySetOpt),
|
|
reinterpret_cast<void*>(&CurlEasySetOptDetour),
|
|
reinterpret_cast<void**>(&gCurlEasySetOpt));
|
|
|
|
if (ok)
|
|
Log::Info("CurlHook: hook installed");
|
|
else
|
|
Log::Error("CurlHook: MinHook installation failed");
|
|
|
|
return ok;
|
|
}
|
|
|
|
} // namespace Embeddium::CurlHook
|