diff --git a/gen_stubs.py b/gen_stubs.py index 9c86197..5f4fa84 100644 --- a/gen_stubs.py +++ b/gen_stubs.py @@ -120,6 +120,7 @@ def main(): "EOS_Sanctions_QueryActivePlayerSanctions", "EOS_Sanctions_GetPlayerSanctionCount", "EOS_Sanctions_CopyPlayerSanctionByIndex", + ] funcs = [] diff --git a/keyDumper/encrypt_bundles.py b/keyDumper/encrypt_bundles.py deleted file mode 100644 index 70242b4..0000000 --- a/keyDumper/encrypt_bundles.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 - -import os, sys, struct, argparse -from Crypto.Cipher import AES - -KEY_DEFAULT = b"rK7CcATuZk7LAmhqqU4iBLNmAq8QbK3s" -KEY_ALTERNATE = b"x4DZmD6D2HhkzT6qD8HpKeZdgM9HCmXP" - - -def xor_crypt(data, key): - cipher = AES.new(key, AES.MODE_ECB) - out = bytearray(len(data)) - for off in range(0, len(data), 16): - ctr = off // 16 + 1 - ks = cipher.encrypt(struct.pack(' {os.path.basename(outpath)}") - return True - encrypted = xor_crypt(data, key) - os.makedirs(os.path.dirname(outpath) or '.', exist_ok=True) - with open(outpath, 'wb') as f: - f.write(encrypted) - print(f" {os.path.basename(inpath)} -> {os.path.basename(outpath)} ({len(data)} bytes)") - return True - except Exception as e: - print(f" ERROR {os.path.basename(inpath)}: {e}", file=sys.stderr) - return False - - -def main(): - parser = argparse.ArgumentParser( - description="Re-encrypt decrypted Unity bundles for Holo's Hanafuda") - parser.add_argument('input', help='Input .unity3d file or directory of .unity3d files') - parser.add_argument('-o', '--output', help='Output file or directory') - parser.add_argument('--key-alternate', action='store_true', - help='Use alternate bundle key candidate (x4DZmD6D2HhkzT6qD8HpKeZdgM9HCmXP)') - parser.add_argument('--key-hex', metavar='HEX', - help='64-char hex AES-256 key (overrides defaults)') - parser.add_argument('--in-place', action='store_true', - help='Write output next to the original .bundle in StandaloneWindows64/') - parser.add_argument('--dry-run', action='store_true', - help='List what would be encrypted without writing') - args = parser.parse_args() - - if args.key_hex: - key = bytes.fromhex(args.key_hex) - if len(key) != 32: - print("Error: --key-hex must be exactly 64 hex chars (32 bytes)", file=sys.stderr) - sys.exit(1) - elif args.key_alternate: - key = KEY_ALTERNATE - else: - key = KEY_DEFAULT - - inpath = args.input - - if os.path.isdir(inpath): - files = sorted(f for f in os.listdir(inpath) if f.endswith('.unity3d')) - if not files: - print(f"No .unity3d files found in '{inpath}'") - return - - if args.output: - outdir = args.output - elif args.in_place: - outdir = os.path.normpath(os.path.join(inpath, '..', '..', 'StandaloneWindows64')) - else: - outdir = os.path.normpath(os.path.join(inpath, '..', 'reencrypted')) - - if not args.dry_run: - os.makedirs(outdir, exist_ok=True) - - ok = errs = 0 - for fname in files: - fpath = os.path.join(inpath, fname) - base = fname.removesuffix('.unity3d') - outpath = os.path.join(outdir, base + '.bundle') - if process_file(fpath, outpath, key, args.dry_run): - ok += 1 - else: - errs += 1 - - total = len(files) - print(f"\n{ok} OK, {errs} errors of {total}") - if not args.dry_run: - print(f"Output directory: {outdir}/") - - else: - if not os.path.exists(inpath): - print(f"Error: '{inpath}' not found", file=sys.stderr) - sys.exit(1) - - if args.output: - outpath = args.output - elif args.in_place: - base = os.path.splitext(os.path.basename(inpath))[0] - bundle_name = base + '.bundle' - candidates = [ - os.path.join(os.path.dirname(inpath), bundle_name), - os.path.normpath(os.path.join( - os.path.dirname(inpath), '..', '..', 'StandaloneWindows64', bundle_name)), - ] - outpath = None - for c in candidates: - if os.path.exists(c): - outpath = c - break - if not outpath: - print(f"Error: --in-place but no existing .bundle found for '{inpath}'", - file=sys.stderr) - sys.exit(1) - else: - basedir = os.path.dirname(inpath) or '.' - basename = os.path.splitext(os.path.basename(inpath))[0] + '.bundle' - outpath = os.path.join(basedir, basename) - - process_file(inpath, outpath, key, args.dry_run) - - -if __name__ == '__main__': - main() diff --git a/main.cpp b/main.cpp index e15d6c9..2674305 100644 --- a/main.cpp +++ b/main.cpp @@ -48,7 +48,6 @@ static void DrainCallbackQueue() { // logging system FILE *logFile = NULL; FILE *packetFile = NULL; -FILE *telemetryFile = NULL; static CRITICAL_SECTION g_packetCs; void InitLogFiles(); @@ -66,27 +65,6 @@ void Log(const char *format, ...) { va_end(args); } -void LogTelemetry(const char *format, ...) { - va_list args; - va_start(args, format); - SYSTEMTIME st; - GetLocalTime(&st); - if (logFile) { - fprintf(logFile, "[%02u:%02u:%02u.%03u] ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); - vfprintf(logFile, format, args); - fprintf(logFile, "\n"); - } - va_end(args); - va_start(args, format); - if (telemetryFile) { - fprintf(telemetryFile, "[%02u:%02u:%02u.%03u] ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); - vfprintf(telemetryFile, format, args); - fprintf(telemetryFile, "\n"); - fflush(telemetryFile); - } - va_end(args); -} - static void HexDump(const void *data, size_t len, char *out) { const unsigned char *p = (const unsigned char *)data; for (size_t i = 0; i < len; ++i) sprintf(out + i * 3, "%02X ", p[i]); @@ -264,12 +242,6 @@ static const DWORD kAutoTeardownSuppressWindowMs = 100; static LONG g_autoTeardownSuppressed = 0; static volatile DWORD g_autoTeardownSuppressTime = 0; -// Telemetry blocking config -static bool g_telemetryBlock = false; -static bool g_dumpTraffic = false; -static char g_blockedDomains[1024] = "api.epicgames.dev"; -static char g_blockedIPs[1024] = "104.18.125.108"; - void LogStateSnapshot(const char *context) { Log("[State] %s | isHost=%d peerConnected=%d lobbyHasRemoteMember=%d initialized=%d clientSawSearchResult=%d", context ? context : "???", (int)AtomicRead(&g_isHost), (int)AtomicRead(&g_peerConnected), @@ -2397,31 +2369,10 @@ void ProxySdkLogCallback(const EOS_LogMessage* Message) { if (Message) { InterlockedIncrement(&g_sdkLogCount); - bool isTelemetry = Message->Message && - (strstr(Message->Message, "api.epicgames.dev") || - strstr(Message->Message, "0.0.0.0") || - strstr(Message->Message, "Couldn't connect to server") || - strstr(Message->Message, "Connection refused") || - strstr(Message->Message, "Failed to connect to the backend") || - strstr(Message->Message, "EOS_NoConnection")); - - if (isTelemetry) { - if (telemetryFile) { - SYSTEMTIME st; - GetLocalTime(&st); - fprintf(telemetryFile, "[%02u:%02u:%02u.%03u] [SDK/%s] %s: %s\n", - st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, - Message->Category ? Message->Category : "?", - LogLevelToStr(Message->Level), - Message->Message ? Message->Message : ""); - fflush(telemetryFile); - } - } else { - Log("[SDK/%s] %s: %s", - Message->Category ? Message->Category : "?", - LogLevelToStr(Message->Level), - Message->Message ? Message->Message : ""); - } + Log("[SDK/%s] %s: %s", + Message->Category ? Message->Category : "?", + LogLevelToStr(Message->Level), + Message->Message ? Message->Message : ""); } if (g_gameLogCallback) g_gameLogCallback(Message); } @@ -2497,13 +2448,10 @@ void InitLogFiles() { else path[0] = '\0'; char logPath[MAX_PATH]; char pktPath[MAX_PATH]; - char telPath[MAX_PATH]; strcpy(logPath, path); strcat(logPath, "proxy_p2p.log"); strcpy(pktPath, path); strcat(pktPath, "proxy_packets.log"); - strcpy(telPath, path); strcat(telPath, "telemetry_logs.log"); logFile = fopen(logPath, "a"); packetFile = fopen(pktPath, "a"); - telemetryFile = fopen(telPath, "a"); if (logFile) { setvbuf(logFile, NULL, _IOFBF, 1 << 16); @@ -2520,135 +2468,14 @@ void InitLogFiles() { fprintf(packetFile, "Packet dump started at %02u:%02u:%02u\n", st.wHour, st.wMinute, st.wSecond); fflush(packetFile); } - if (telemetryFile) { - SYSTEMTIME st; - GetLocalTime(&st); - fprintf(telemetryFile, "\n========== Telemetry Log %02u:%02u:%02u ==========\n", - st.wHour, st.wMinute, st.wSecond); - fflush(telemetryFile); - } } void CloseLogFiles() { if (logFile) { fclose(logFile); logFile = NULL; } if (packetFile) { fclose(packetFile); packetFile = NULL; } - if (telemetryFile) { fclose(telemetryFile); telemetryFile = NULL; } DeleteCriticalSection(&g_packetCs); } -// === WinSock Telemetry Blocking Hooks === - -typedef int (WSAAPI *GetAddrInfoFn)(PCSTR, PCSTR, const ADDRINFOA *, PADDRINFOA *); -typedef int (WSAAPI *ConnectFn)(SOCKET, const struct sockaddr *, int); - -static GetAddrInfoFn Real_GetAddrInfo = NULL; -static ConnectFn Real_Connect = NULL; - -static bool PatchIat(HMODULE module, const char *sourceDll, const char *funcName, - WORD ordinal, void *hook, void **orig) { - if (!module) return false; - PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)module; - if (dos->e_magic != 0x5A4D) return false; - PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((char*)module + dos->e_lfanew); - if (nt->Signature != 0x00004550) return false; - PIMAGE_DATA_DIRECTORY importDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; - if (!importDir->VirtualAddress || !importDir->Size) return false; - static const uintptr_t kOrdFlag = 0x8000000000000000ULL; - PIMAGE_IMPORT_DESCRIPTOR imports = (PIMAGE_IMPORT_DESCRIPTOR)((char*)module + importDir->VirtualAddress); - for (; imports->Name; imports++) { - char *dllName = (char*)module + imports->Name; - if (_stricmp(dllName, sourceDll) != 0) continue; - PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((char*)module + imports->FirstThunk); - PIMAGE_THUNK_DATA origThunk = (PIMAGE_THUNK_DATA)((char*)module + imports->OriginalFirstThunk); - for (; thunk->u1.Function; thunk++, origThunk++) { - bool match = false; - if (funcName) { - if (origThunk->u1.Ordinal & kOrdFlag) continue; - PIMAGE_IMPORT_BY_NAME imp = (PIMAGE_IMPORT_BY_NAME)((char*)module + origThunk->u1.AddressOfData); - if (strcmp(imp->Name, funcName) == 0) match = true; - } else { - if (!(origThunk->u1.Ordinal & kOrdFlag)) continue; - if ((origThunk->u1.Ordinal & 0xFFFF) == ordinal) match = true; - } - if (!match) continue; - if (orig) *orig = (void*)(uintptr_t)thunk->u1.Function; - DWORD old; - if (VirtualProtect(&thunk->u1.Function, sizeof(void*), PAGE_READWRITE, &old)) { - thunk->u1.Function = (uintptr_t)(LONG_PTR)hook; - VirtualProtect(&thunk->u1.Function, sizeof(void*), old, &old); - return true; - } - return false; - } - } - return false; -} - -static int my_tolower(int c) { - return (c >= 'A' && c <= 'Z') ? c + 32 : c; -} - -static bool Listed(const char *value, const char *list) { - if (!value || !list || !list[0]) return false; - const char *p = list; - size_t vlen = strlen(value); - while (*p) { - while (*p == ' ' || *p == ',' || *p == ';') p++; - if (!*p) break; - const char *start = p; - while (*p && *p != ' ' && *p != ',' && *p != ';') p++; - size_t len = p - start; - if (len != vlen) continue; - bool match = true; - for (size_t i = 0; i < len; i++) { - if (my_tolower((unsigned char)value[i]) != my_tolower((unsigned char)start[i])) { - match = false; - break; - } - } - if (match) return true; - } - return false; -} - -static int WSAAPI Hook_GetAddrInfo(PCSTR name, PCSTR svc, const ADDRINFOA *hints, PADDRINFOA *res) { - int ret = Real_GetAddrInfo(name, svc, hints, res); - if (ret != 0) return ret; - if (name && Listed(name, g_blockedDomains)) { - if (g_telemetryBlock) { - LogTelemetry("[Telemetry] Redirected DNS: %s -> 0.0.0.0 (block via connect)", name); - ADDRINFOA *cur = *res; - while (cur) { - if (cur->ai_family == AF_INET && cur->ai_addr) { - ((struct sockaddr_in *)cur->ai_addr)->sin_addr.s_addr = htonl(INADDR_ANY); - } - cur = cur->ai_next; - } - } else if (g_dumpTraffic) { - LogTelemetry("[Telemetry] Traffic: DNS lookup %s (ALLOWED, dump mode)", name); - } - } - return ret; -} - -static int WSAAPI Hook_Connect(SOCKET s, const struct sockaddr *addr, int addrlen) { - if (addr && addr->sa_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)addr; - char ip[16]; - strcpy(ip, inet_ntoa(sin->sin_addr)); - bool isBlocked = g_telemetryBlock && Listed(ip, g_blockedIPs); - if (isBlocked) { - LogTelemetry("[Telemetry] Blocked connect to %s:%d", ip, ntohs(sin->sin_port)); - WSASetLastError(WSAECONNREFUSED); - return SOCKET_ERROR; - } - if (g_dumpTraffic && Listed(ip, g_blockedIPs)) { - LogTelemetry("[Telemetry] Traffic: connect to %s:%d (ALLOWED, dump mode)", ip, ntohs(sin->sin_port)); - } - } - return Real_Connect(s, addr, addrlen); -} - BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { @@ -2665,25 +2492,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv if (dirSlash) *(dirSlash + 1) = '\0'; else g_dllDir[0] = '\0'; InitLogFiles(); - - // Load telemetry config - { - char iniPath2[MAX_PATH]; - snprintf(iniPath2, sizeof(iniPath2), "%sdirectip.ini", g_dllDir); - char telemetryStr[8] = "true"; - GetPrivateProfileStringA("Network", "Telemetry", "true", telemetryStr, sizeof(telemetryStr), iniPath2); - g_telemetryBlock = (_stricmp(telemetryStr, "false") == 0 || strcmp(telemetryStr, "0") == 0); - char dumpStr[8] = "false"; - GetPrivateProfileStringA("Telemetry", "DumpTraffic", "false", dumpStr, sizeof(dumpStr), iniPath2); - g_dumpTraffic = (_stricmp(dumpStr, "true") == 0 || strcmp(dumpStr, "1") == 0); - GetPrivateProfileStringA("Telemetry", "BlockDomains", "api.epicgames.dev", g_blockedDomains, sizeof(g_blockedDomains), iniPath2); - GetPrivateProfileStringA("Telemetry", "BlockIPs", "104.18.125.108", g_blockedIPs, sizeof(g_blockedIPs), iniPath2); - // Do NOT add 0.0.0.0 to the block list — let connect fail naturally - // so the EOS SDK's HttpManagerThread doesn't crash on synthetic errors. - LogTelemetry("[Telemetry] Config: block=%d dump=%d domains=%s ips=%s", - (int)g_telemetryBlock, (int)g_dumpTraffic, g_blockedDomains, g_blockedIPs); - } - char *lastSlash = strrchr(path, '\\'); if (!lastSlash) lastSlash = strrchr(path, '/'); if (lastSlash) { @@ -2717,16 +2525,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv } else { Log("[Proxy] Failed to load original DLL! Error: %d", GetLastError()); } - - // Patch EOS SDK IAT for ws2_32 telemetry hooks - bool okGa = PatchIat(hOriginalDll, "ws2_32.dll", "getaddrinfo", 0, - (void*)Hook_GetAddrInfo, (void**)&Real_GetAddrInfo); - bool okCo = PatchIat(hOriginalDll, "ws2_32.dll", NULL, 4, - (void*)Hook_Connect, (void**)&Real_Connect); - LogTelemetry("[Telemetry] IAT hooks: getaddrinfo=%s(%p) connect=%s(%p)", - okGa ? "OK" : "FAIL", (void*)Real_GetAddrInfo, - okCo ? "OK" : "FAIL", (void*)Real_Connect); - break; } case DLL_PROCESS_DETACH: