diff options
Diffstat (limited to 'primedev/wsockproxy')
-rw-r--r-- | primedev/wsockproxy/dllmain.cpp | 182 | ||||
-rw-r--r-- | primedev/wsockproxy/loader.cpp | 141 | ||||
-rw-r--r-- | primedev/wsockproxy/loader.h | 9 | ||||
-rw-r--r-- | primedev/wsockproxy/pch.h | 16 | ||||
-rw-r--r-- | primedev/wsockproxy/wsock32.asm | 7 | ||||
-rw-r--r-- | primedev/wsockproxy/wsock32.def | 78 |
6 files changed, 433 insertions, 0 deletions
diff --git a/primedev/wsockproxy/dllmain.cpp b/primedev/wsockproxy/dllmain.cpp new file mode 100644 index 00000000..4cc4f26e --- /dev/null +++ b/primedev/wsockproxy/dllmain.cpp @@ -0,0 +1,182 @@ +#include "loader.h" + +#include <shlwapi.h> +#include <filesystem> + +HINSTANCE hLThis = 0; +FARPROC p[857]; +HINSTANCE hL = 0; + +bool GetExePathWide(wchar_t* dest, DWORD destSize) +{ + if (!dest) + return NULL; + if (destSize < MAX_PATH) + return NULL; + + DWORD length = GetModuleFileNameW(NULL, dest, destSize); + return length && PathRemoveFileSpecW(dest); +} + +wchar_t exePath[4096]; +wchar_t buffer1[8192]; +wchar_t buffer2[12288]; + +BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) + { + hLThis = hInst; + + if (!GetExePathWide(exePath, 4096)) + { + MessageBoxA( + GetForegroundWindow(), + "Failed getting game directory.\nThe game cannot continue and has to exit.", + "Northstar Wsock32 Proxy Error", + 0); + return true; + } + + SetCurrentDirectoryW(exePath); + + if (!ProvisionNorthstar()) // does not call InitialiseNorthstar yet, will do it on LauncherMain hook + return true; + + // copy the original library for system to our local directory, with changed name so that we can load it + swprintf_s(buffer1, L"%s\\bin\\x64_retail\\wsock32.org.dll", exePath); + GetSystemDirectoryW(buffer2, 4096); + swprintf_s(buffer2, L"%s\\wsock32.dll", buffer2); + try + { + std::filesystem::copy_file(buffer2, buffer1); + } + catch (const std::exception& e1) + { + if (!std::filesystem::exists(buffer1)) + { + // fallback by copying to temp dir... + // because apparently games installed by EA Desktop app don't have write permissions in their directories + auto temp_dir = std::filesystem::temp_directory_path() / L"wsock32.org.dll"; + try + { + std::filesystem::copy_file(buffer2, temp_dir); + } + catch (const std::exception& e2) + { + if (!std::filesystem::exists(temp_dir)) + { + swprintf_s( + buffer2, + L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S\n\nFurthermore, we failed copying wsock32.dll into " + L"temporary directory at \"%s\"\n\n%S", + buffer1, + e1.what(), + temp_dir.c_str(), + e2.what()); + MessageBoxW(GetForegroundWindow(), buffer2, L"Northstar Wsock32 Proxy Error", 0); + return false; + } + } + swprintf_s(buffer1, L"%s", temp_dir.c_str()); + } + } + hL = LoadLibraryExW(buffer1, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hL) + { + LibraryLoadError(GetLastError(), L"wsock32.org.dll", buffer1); + return false; + } + + // load the functions to proxy + // it's only some of them, because in case of wsock32 most of the functions can actually be natively redirected + // (see wsock32.def and https://source.winehq.org/WineAPI/wsock32.html) + p[1] = GetProcAddress(hL, "EnumProtocolsA"); + p[2] = GetProcAddress(hL, "EnumProtocolsW"); + p[4] = GetProcAddress(hL, "GetAddressByNameA"); + p[5] = GetProcAddress(hL, "GetAddressByNameW"); + p[17] = GetProcAddress(hL, "WEP"); + p[30] = GetProcAddress(hL, "WSARecvEx"); + p[36] = GetProcAddress(hL, "__WSAFDIsSet"); + p[45] = GetProcAddress(hL, "getnetbyname"); + p[52] = GetProcAddress(hL, "getsockopt"); + p[56] = GetProcAddress(hL, "inet_network"); + p[67] = GetProcAddress(hL, "s_perror"); + p[72] = GetProcAddress(hL, "setsockopt"); + } + + if (reason == DLL_PROCESS_DETACH) + { + FreeLibrary(hL); + return true; + } + + return true; +} + +extern "C" +{ + FARPROC PA = NULL; + int RunASM(); + + void PROXY_EnumProtocolsA() + { + PA = p[1]; + RunASM(); + } + void PROXY_EnumProtocolsW() + { + PA = p[2]; + RunASM(); + } + void PROXY_GetAddressByNameA() + { + PA = p[4]; + RunASM(); + } + void PROXY_GetAddressByNameW() + { + PA = p[5]; + RunASM(); + } + void PROXY_WEP() + { + PA = p[17]; + RunASM(); + } + void PROXY_WSARecvEx() + { + PA = p[30]; + RunASM(); + } + void PROXY___WSAFDIsSet() + { + PA = p[36]; + RunASM(); + } + void PROXY_getnetbyname() + { + PA = p[45]; + RunASM(); + } + void PROXY_getsockopt() + { + PA = p[52]; + RunASM(); + } + void PROXY_inet_network() + { + PA = p[56]; + RunASM(); + } + void PROXY_s_perror() + { + PA = p[67]; + RunASM(); + } + void PROXY_setsockopt() + { + PA = p[72]; + RunASM(); + } +} diff --git a/primedev/wsockproxy/loader.cpp b/primedev/wsockproxy/loader.cpp new file mode 100644 index 00000000..3e46c1a6 --- /dev/null +++ b/primedev/wsockproxy/loader.cpp @@ -0,0 +1,141 @@ +#include "loader.h" +#include <string> +#include <system_error> +#include <sstream> +#include <fstream> +#include <filesystem> +#include <iostream> + +namespace fs = std::filesystem; + +void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) +{ + char text[4096]; + std::string message = std::system_category().message(dwMessageId); + sprintf_s(text, "Failed to load the %ls at \"%ls\" (%lu):\n\n%hs", libName, location, dwMessageId, message.c_str()); + if (dwMessageId == 126 && std::filesystem::exists(location)) + { + sprintf_s( + text, + "%s\n\nThe file at the specified location DOES exist, so this error indicates that one of its *dependencies* failed to be " + "found.", + text); + } + MessageBoxA(GetForegroundWindow(), text, "Northstar Wsock32 Proxy Error", 0); +} + +bool ShouldLoadNorthstar() +{ + bool loadNorthstar = strstr(GetCommandLineA(), "-northstar"); + + if (loadNorthstar) + return loadNorthstar; + + auto runNorthstarFile = std::ifstream("run_northstar.txt"); + if (runNorthstarFile) + { + std::stringstream runNorthstarFileBuffer; + runNorthstarFileBuffer << runNorthstarFile.rdbuf(); + runNorthstarFile.close(); + if (!runNorthstarFileBuffer.str().starts_with("0")) + loadNorthstar = true; + } + return loadNorthstar; +} + +bool LoadNorthstar() +{ + FARPROC Hook_Init = nullptr; + { + std::string strProfile = "R2Northstar"; + char* clachar = strstr(GetCommandLineA(), "-profile="); + if (clachar) + { + std::string cla = std::string(clachar); + if (strncmp(cla.substr(9, 1).c_str(), "\"", 1)) + { + int space = cla.find(" "); + std::string dirname = cla.substr(9, space - 9); + std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; + strProfile = dirname.c_str(); + } + else + { + std::string quote = "\""; + int quote1 = cla.find(quote); + int quote2 = (cla.substr(quote1 + 1)).find(quote); + std::string dirname = cla.substr(quote1 + 1, quote2); + std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; + strProfile = dirname; + } + } + else + { + std::cout << "[*] Profile was not found in command line arguments. Using default: R2Northstar" << std::endl; + strProfile = "R2Northstar"; + } + + wchar_t buffer[8192]; + + // Check if "Northstar.dll" exists in profile directory, if it doesnt fall back to root + swprintf_s(buffer, L"%s\\%s\\Northstar.dll", exePath, std::wstring(strProfile.begin(), strProfile.end()).c_str()); + + if (!fs::exists(fs::path(buffer))) + swprintf_s(buffer, L"%s\\Northstar.dll", exePath); + + std::wcout << L"[*] Using: " << buffer << std::endl; + + HMODULE hHookModule = LoadLibraryExW(buffer, 0, 8u); + if (hHookModule) + Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (!hHookModule || Hook_Init == nullptr) + { + LibraryLoadError(GetLastError(), L"Northstar.dll", buffer); + return false; + } + } + ((bool (*)())Hook_Init)(); + + return true; +} + +typedef int (*LauncherMainType)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); +LauncherMainType LauncherMainOriginal; + +int LauncherMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + if (ShouldLoadNorthstar()) + LoadNorthstar(); + return LauncherMainOriginal(hInstance, hPrevInstance, lpCmdLine, nCmdShow); +} + +bool ProvisionNorthstar() +{ + if (!ShouldLoadNorthstar()) + return true; + + if (MH_Initialize() != MH_OK) + { + MessageBoxA( + GetForegroundWindow(), "MH_Initialize failed\nThe game cannot continue and has to exit.", "Northstar Wsock32 Proxy Error", 0); + return false; + } + + auto launcherHandle = GetModuleHandleA("launcher.dll"); + if (!launcherHandle) + { + MessageBoxA( + GetForegroundWindow(), + "Launcher isn't loaded yet.\nThe game cannot continue and has to exit.", + "Northstar Wsock32 Proxy Error", + 0); + return false; + } + + LPVOID pTarget = (LPVOID)GetProcAddress(launcherHandle, "LauncherMain"); + if (MH_CreateHook(pTarget, (LPVOID)&LauncherMainHook, reinterpret_cast<LPVOID*>(&LauncherMainOriginal)) != MH_OK || + MH_EnableHook(pTarget) != MH_OK) + MessageBoxA(GetForegroundWindow(), "Hook creation failed for function LauncherMain.", "Northstar Wsock32 Proxy Error", 0); + + return true; +} diff --git a/primedev/wsockproxy/loader.h b/primedev/wsockproxy/loader.h new file mode 100644 index 00000000..0c6fb053 --- /dev/null +++ b/primedev/wsockproxy/loader.h @@ -0,0 +1,9 @@ +#pragma once + +extern wchar_t exePath[4096]; +extern wchar_t buffer1[8192]; +extern wchar_t buffer2[12288]; + +void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location); +bool ShouldLoadNorthstar(); +bool ProvisionNorthstar(); diff --git a/primedev/wsockproxy/pch.h b/primedev/wsockproxy/pch.h new file mode 100644 index 00000000..ebc29547 --- /dev/null +++ b/primedev/wsockproxy/pch.h @@ -0,0 +1,16 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include <windows.h> + +#include "MinHook.h" + +#endif // PCH_H diff --git a/primedev/wsockproxy/wsock32.asm b/primedev/wsockproxy/wsock32.asm new file mode 100644 index 00000000..22a9c384 --- /dev/null +++ b/primedev/wsockproxy/wsock32.asm @@ -0,0 +1,7 @@ +.data +extern PA : qword +.code +RunASM proc +jmp qword ptr [PA] +RunASM endp +end diff --git a/primedev/wsockproxy/wsock32.def b/primedev/wsockproxy/wsock32.def new file mode 100644 index 00000000..448440b4 --- /dev/null +++ b/primedev/wsockproxy/wsock32.def @@ -0,0 +1,78 @@ +LIBRARY wsock32 +EXPORTS + AcceptEx=mswsock.AcceptEx + EnumProtocolsA=PROXY_EnumProtocolsA + EnumProtocolsW=PROXY_EnumProtocolsW + GetAcceptExSockaddrs=mswsock.GetAcceptExSockaddrs + GetAddressByNameA=PROXY_GetAddressByNameA + GetAddressByNameW=PROXY_GetAddressByNameW + GetNameByTypeA=ws2_32.GetNameByTypeA + GetNameByTypeW=ws2_32.GetNameByTypeW + GetServiceA=ws2_32.GetServiceA + GetServiceW=ws2_32.GetServiceW + GetTypeByNameA=ws2_32.GetTypeByNameA + GetTypeByNameW=ws2_32.GetTypeByNameW + MigrateWinsockConfiguration=ws2_32.MigrateWinsockConfiguration + NPLoadNameSpaces=ws2_32.NPLoadNameSpaces + SetServiceA=ws2_32.SetServiceA + SetServiceW=ws2_32.SetServiceW + TransmitFile=mswsock.TransmitFile + WEP=PROXY_WEP + WSAAsyncGetHostByAddr=ws2_32.WSAAsyncGetHostByAddr + WSAAsyncGetHostByName=ws2_32.WSAAsyncGetHostByName + WSAAsyncGetProtoByName=ws2_32.WSAAsyncGetProtoByName + WSAAsyncGetProtoByNumber=ws2_32.WSAAsyncGetProtoByNumber + WSAAsyncGetServByName=ws2_32.WSAAsyncGetServByName + WSAAsyncGetServByPort=ws2_32.WSAAsyncGetServByPort + WSAAsyncSelect=ws2_32.WSAAsyncSelect + WSACancelAsyncRequest=ws2_32.WSACancelAsyncRequest + WSACancelBlockingCall=ws2_32.WSACancelBlockingCall + WSACleanup=ws2_32.WSACleanup @116 + WSAGetLastError=ws2_32.WSAGetLastError @111 + WSAIsBlocking=ws2_32.WSAIsBlocking + WSARecvEx=PROXY_WSARecvEx + WSASetBlockingHook=ws2_32.WSASetBlockingHook + WSASetLastError=ws2_32.WSASetLastError @112 + WSAStartup=ws2_32.WSAStartup @115 + WSAUnhookBlockingHook=ws2_32.WSAUnhookBlockingHook + WSApSetPostRoutine=ws2_32.WSApSetPostRoutine + __WSAFDIsSet=PROXY___WSAFDIsSet @151 + accept=ws2_32.accept @1 + bind=ws2_32.bind @2 + closesocket=ws2_32.closesocket @3 + connect=ws2_32.connect @4 + dn_expand=ws2_32.dn_expand @1106 + gethostbyaddr=ws2_32.gethostbyaddr + gethostbyname=ws2_32.gethostbyname @52 + gethostname=ws2_32.gethostname @57 + getnetbyname=PROXY_getnetbyname @ 1101 + getpeername=ws2_32.getpeername @5 + getprotobyname=ws2_32.getprotobyname + getprotobynumber=ws2_32.getprotobynumber + getservbyname=ws2_32.getservbyname + getservbyport=ws2_32.getservbyport + getsockname=ws2_32.getsockname @6 + getsockopt=PROXY_getsockopt @7 + htonl=ws2_32.htonl + htons=ws2_32.htons @9 + inet_addr=ws2_32.inet_addr + inet_network=PROXY_inet_network + inet_ntoa=ws2_32.inet_ntoa + ioctlsocket=ws2_32.ioctlsocket @12 + listen=ws2_32.listen @13 + ntohl=ws2_32.ntohl + ntohs=ws2_32.ntohs @15 + rcmd=ws2_32.rcmd + recv=ws2_32.recv @16 + recvfrom=ws2_32.recvfrom @17 + rexec=ws2_32.rexec + rresvport=ws2_32.rresvport + s_perror=PROXY_s_perror + select=ws2_32.select @18 + select=ws2_32.select @18 + send=ws2_32.send @19 + sendto=ws2_32.sendto @20 + sethostname=ws2_32.sethostname + setsockopt=PROXY_setsockopt @21 + shutdown=ws2_32.shutdown @22 + socket=ws2_32.socket @23 |