diff options
49 files changed, 1715 insertions, 407 deletions
diff --git a/LauncherInjector/LauncherInjector.vcxproj b/LauncherInjector/LauncherInjector.vcxproj index e205207d..8870c732 100644 --- a/LauncherInjector/LauncherInjector.vcxproj +++ b/LauncherInjector/LauncherInjector.vcxproj @@ -1,14 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> @@ -27,19 +19,6 @@ <ProjectName>NorthstarLauncher</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> @@ -58,12 +37,6 @@ </ImportGroup> <ImportGroup Label="Shared"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> @@ -71,50 +44,12 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -122,10 +57,13 @@ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <LanguageStandard>stdcpp17</LanguageStandard> + <AdditionalOptions>/F8000000 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <SubSystem>Console</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <StackReserveSize>8000000</StackReserveSize> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -137,12 +75,15 @@ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <LanguageStandard>stdcpp17</LanguageStandard> + <AdditionalOptions>/F8000000 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <SubSystem>Console</SubSystem> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <StackReserveSize>8000000</StackReserveSize> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/LauncherInjector/main.cpp b/LauncherInjector/main.cpp index 5828e9e2..7697e80d 100644 --- a/LauncherInjector/main.cpp +++ b/LauncherInjector/main.cpp @@ -1,12 +1,25 @@ +#define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <TlHelp32.h> #include <filesystem> #include <sstream> -#include <iostream> #include <fstream> +#include <Shlwapi.h> namespace fs = std::filesystem; +extern "C" { + __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +} + +HMODULE hLauncherModule; +HMODULE hHookModule; +HMODULE hTier0Module; + +wchar_t exePath[4096]; +wchar_t buffer[8192]; + DWORD GetProcessByName(std::wstring processName) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); @@ -33,132 +46,195 @@ DWORD GetProcessByName(std::wstring processName) return 0; } -#define PROCESS_NAME L"Titanfall2-unpacked.exe" -#define DLL_NAME L"Northstar.dll" +bool GetExePathWide(wchar_t* dest, DWORD destSize) +{ + if (!dest) return NULL; + if (destSize < MAX_PATH) return NULL; -int main(int argc, char* argv[]) { - if (!fs::exists(PROCESS_NAME)) + DWORD length = GetModuleFileNameW(NULL, dest, destSize); + return length && PathRemoveFileSpecW(dest); +} + +FARPROC GetLauncherMain() +{ + static FARPROC Launcher_LauncherMain; + if (!Launcher_LauncherMain) + Launcher_LauncherMain = GetProcAddress(hLauncherModule, "LauncherMain"); + return Launcher_LauncherMain; +} + +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\n\nMake sure you followed the Northstar installation instructions carefully.", libName, location, dwMessageId, message.c_str()); + + if (!fs::exists("Titanfall2.exe") && fs::exists("..\\Titanfall2.exe")) { - MessageBoxA(0, "Titanfall2-unpacked.exe not found! Please launch from your titanfall 2 directory and ensure you have Northstar installed correctly!", "", MB_OK); - return 1; + auto curDir = std::filesystem::current_path().filename().string(); + auto aboveDir = std::filesystem::current_path().parent_path().filename().string(); + sprintf_s(text, "%s\n\nWe detected that in your case you have extracted the files into a *subdirectory* of your Titanfall 2 installation.\nPlease move all the files and folders from current folder (\"%s\") into the Titanfall 2 installation directory just above (\"%s\").\n\nPlease try out the above steps by yourself before reaching out to the community for support.", text, curDir.c_str(), aboveDir.c_str()); } - if (!fs::exists(DLL_NAME)) + MessageBoxA(GetForegroundWindow(), text, "Northstar Launcher Error", 0); +} + +void EnsureOriginStarted() +{ + if (GetProcessByName(L"Origin.exe") || GetProcessByName(L"EADesktop.exe")) + return; // already started + + // unpacked exe will crash if origin isn't open on launch, so launch it + // get origin path from registry, code here is reversed from OriginSDK.dll + HKEY key; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\WOW6432Node\\Origin", 0, KEY_READ, &key) != ERROR_SUCCESS) { - MessageBoxA(0, "Northstar.dll not found! Please launch from your titanfall 2 directory and ensure you have Northstar installed correctly!", "", MB_OK); - return 1; + MessageBoxA(0, "Error: failed reading Origin path!", "Northstar Launcher Error", MB_OK); + return; } - bool isdedi = false; - for (int i = 0; i < argc; i++) - if (!strcmp(argv[i], "-dedicated")) - isdedi = true; - - if (!isdedi && !GetProcessByName(L"Origin.exe") && !GetProcessByName(L"EADesktop.exe")) + char originPath[520]; + DWORD originPathLength = 520; + if (RegQueryValueExA(key, "ClientPath", 0, 0, (LPBYTE)&originPath, &originPathLength) != ERROR_SUCCESS) { - // unpacked exe will crash if origin isn't open on launch, so launch it - // get origin path from registry, code here is reversed from OriginSDK.dll - HKEY key; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\WOW6432Node\\Origin", 0, KEY_READ, &key) != ERROR_SUCCESS) - { - MessageBoxA(0, "Error: failed reading origin path!", "", MB_OK); - return 1; - } + MessageBoxA(0, "Error: failed reading Origin path!", "Northstar Launcher Error", MB_OK); + return; + } - char originPath[520]; - DWORD originPathLength = 520; - if (RegQueryValueExA(key, "ClientPath", 0, 0, (LPBYTE)&originPath, &originPathLength) != ERROR_SUCCESS) - { - MessageBoxA(0, "Error: failed reading origin path!", "", MB_OK); - return 1; - } + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + STARTUPINFO si; + memset(&si, 0, sizeof(si)); + CreateProcessA(originPath, (char*)"", NULL, NULL, false, CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP, NULL, NULL, (LPSTARTUPINFOA)&si, &pi); - PROCESS_INFORMATION pi; - memset(&pi, 0, sizeof(pi)); - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - CreateProcessA(originPath, (LPSTR)"", NULL, NULL, false, CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP, NULL, NULL, (LPSTARTUPINFOA)&si, &pi); + printf("[*] Waiting for Origin...\n"); - // wait for origin to be ready, this process is created when origin is ready enough to launch game without any errors - while (!GetProcessByName(L"OriginClientService.exe") && !GetProcessByName(L"EADesktop.exe")) - Sleep(200); - } + // wait for origin to be ready, this process is created when origin is ready enough to launch game without any errors + while (!GetProcessByName(L"OriginClientService.exe") && !GetProcessByName(L"EADesktop.exe")) + Sleep(200); - // get cmdline args from file - std::wstring args; - std::ifstream cmdlineArgFile; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); +} - args.append(L" "); - for (int i = 0; i < argc; i++) +void PrependPath() +{ + wchar_t* pPath; + size_t len; + errno_t err = _wdupenv_s(&pPath, &len, L"PATH"); + if (!err) { - std::string str = argv[i]; - - args.append(std::wstring(str.begin(), str.end())); - args.append(L" "); + swprintf_s(buffer, L"PATH=%s\\bin\\x64_retail\\;%s", exePath, pPath); + auto result = _wputenv(buffer); + if (result == -1) + { + MessageBoxW(GetForegroundWindow(), L"Warning: could not prepend the current directory to app's PATH environment variable. Something may break because of that.", L"Northstar Launcher Warning", 0); + } + free(pPath); } - - if (!isdedi) - cmdlineArgFile = std::ifstream("ns_startup_args.txt"); else - cmdlineArgFile = std::ifstream("ns_startup_args_dedi.txt"); - - if (cmdlineArgFile) { - std::stringstream argBuffer; - argBuffer << cmdlineArgFile.rdbuf(); - cmdlineArgFile.close(); - - std::string str = argBuffer.str(); - args.append(std::wstring(str.begin(), str.end())); + MessageBoxW(GetForegroundWindow(), L"Warning: could not get current PATH environment variable in order to prepend the current directory to it. Something may break because of that.", L"Northstar Launcher Warning", 0); } +} - //if (isdedi) - // // copy -dedicated into args if we have it in commandline args - // args.append(L" -dedicated"); - - STARTUPINFO startupInfo; - PROCESS_INFORMATION processInfo; +bool ShouldLoadNorthstar(int argc, char* argv[]) +{ + bool loadNorthstar = true; + for (int i = 0; i < argc; i++) + if (!strcmp(argv[i], "-vanilla")) + loadNorthstar = false; - memset(&startupInfo, 0, sizeof(startupInfo)); - memset(&processInfo, 0, sizeof(processInfo)); + if (!loadNorthstar) + return loadNorthstar; - CreateProcessW(PROCESS_NAME, (LPWSTR)args.c_str(), NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo); + auto runNorthstarFile = std::ifstream("run_northstar.txt"); + if (runNorthstarFile) + { + std::stringstream runNorthstarFileBuffer; + runNorthstarFileBuffer << runNorthstarFile.rdbuf(); + runNorthstarFile.close(); + if (runNorthstarFileBuffer.str()._Starts_with("0")) + loadNorthstar = false; + } + return loadNorthstar; +} - HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll"); - LPTHREAD_START_ROUTINE pLoadLibraryW = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW"); +bool LoadNorthstar() +{ + FARPROC Hook_Init = nullptr; + { + swprintf_s(buffer, L"%s\\Northstar.dll", exePath); + hHookModule = LoadLibraryExW(buffer, 0i64, 8u); + if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (!hHookModule || Hook_Init == nullptr) + { + LibraryLoadError(GetLastError(), L"Northstar.dll", buffer); + return false; + } + } - SIZE_T dwLength = (wcslen(DLL_NAME) + 1) * 2; - LPVOID lpLibName = VirtualAllocEx(processInfo.hProcess, NULL, dwLength, MEM_COMMIT, PAGE_READWRITE); + ((bool (*)()) Hook_Init)(); + return true; +} - SIZE_T written = 0; - WriteProcessMemory(processInfo.hProcess, lpLibName, DLL_NAME, dwLength, &written); +int main(int argc, char* argv[]) { - HANDLE hThread = CreateRemoteThread(processInfo.hProcess, NULL, NULL, pLoadLibraryW, lpLibName, NULL, NULL); + // checked to avoid starting origin, Northstar.dll will check for -dedicated as well on its own + bool noOriginStartup = false; + for (int i = 0; i < argc; i++) + if (!strcmp(argv[i], "-noOriginStartup") || !strcmp(argv[i], "-dedicated")) + noOriginStartup = true; - if (hThread == NULL) + if (!noOriginStartup) { - // injection failed - - std::string errorMessage = "Injection failed! CreateRemoteThread returned "; - errorMessage += std::to_string(GetLastError()).c_str(); - errorMessage += ", make sure bob hasn't accidentally shipped a debug build"; - - MessageBoxA(0, errorMessage.c_str(), "", MB_OK); - return 0; + EnsureOriginStarted(); } - WaitForSingleObject(hThread, INFINITE); - - //MessageBoxA(0, std::to_string(GetLastError()).c_str(), "", MB_OK); + { + if (!GetExePathWide(exePath, sizeof(exePath))) + { + MessageBoxA(GetForegroundWindow(), "Failed getting game directory.\nThe game cannot continue and has to exit.", "Northstar Launcher Error", 0); + return 1; + } - CloseHandle(hThread); + PrependPath(); - ResumeThread(processInfo.hThread); + printf("[*] Loading tier0.dll\n"); + swprintf_s(buffer, L"%s\\bin\\x64_retail\\tier0.dll", exePath); + hTier0Module = LoadLibraryExW(buffer, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hTier0Module) + { + LibraryLoadError(GetLastError(), L"tier0.dll", buffer); + return 1; + } - VirtualFreeEx(processInfo.hProcess, lpLibName, dwLength, MEM_RELEASE); + bool loadNorthstar = ShouldLoadNorthstar(argc, argv); + if (loadNorthstar) + { + printf("[*] Loading Northstar\n"); + if (!LoadNorthstar()) + return 1; + } + else + printf("[*] Going to load the vanilla game\n"); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); + printf("[*] Loading launcher.dll\n"); + swprintf_s(buffer, L"%s\\bin\\x64_retail\\launcher.dll", exePath); + hLauncherModule = LoadLibraryExW(buffer, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hLauncherModule) + { + LibraryLoadError(GetLastError(), L"launcher.dll", buffer); + return 1; + } + } - return 0; + printf("[*] Launching the game...\n"); + auto LauncherMain = GetLauncherMain(); + if (!LauncherMain) + MessageBoxA(GetForegroundWindow(), "Failed loading launcher.dll.\nThe game cannot continue and has to exit.", "Northstar Launcher Error", 0); + //auto result = ((__int64(__fastcall*)())LauncherMain)(); + //auto result = ((signed __int64(__fastcall*)(__int64))LauncherMain)(0i64); + return ((int(/*__fastcall*/*)(HINSTANCE, HINSTANCE, LPSTR, int))LauncherMain)(NULL, NULL, NULL, 0); // the parameters aren't really used anyways }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index bd0d9eb6..23b2cccf 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -1,14 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> @@ -27,19 +19,6 @@ <ProjectName>Northstar</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> @@ -58,12 +37,6 @@ </ImportGroup> <ImportGroup Label="Shared"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> @@ -71,56 +44,12 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;_DEBUG;NORTHSTARDEDICATEDTEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <PrecompiledHeader>Use</PrecompiledHeader> - <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableUAC>false</EnableUAC> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;NDEBUG;NORTHSTARDEDICATEDTEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <PrecompiledHeader>Use</PrecompiledHeader> - <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableUAC>false</EnableUAC> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -136,7 +65,7 @@ <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcrypto_static.lib;$(ProjectDir)include\libssl_static.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -158,6 +87,7 @@ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -165,7 +95,7 @@ <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcrypto_static.lib;$(ProjectDir)include\libssl_static.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -586,6 +516,7 @@ <ClInclude Include="include\spdlog\tweakme.h" /> <ClInclude Include="include\spdlog\version.h" /> <ClInclude Include="keyvalues.h" /> + <ClInclude Include="languagehooks.h" /> <ClInclude Include="logging.h" /> <ClInclude Include="main.h" /> <ClInclude Include="masterserver.h" /> @@ -627,6 +558,7 @@ <ClCompile Include="hooks.cpp" /> <ClCompile Include="hookutils.cpp" /> <ClCompile Include="keyvalues.cpp" /> + <ClCompile Include="languagehooks.cpp" /> <ClCompile Include="memalloc.cpp" /> <ClCompile Include="miscclientfixes.cpp" /> <ClCompile Include="misccommands.cpp" /> @@ -636,9 +568,7 @@ <ClCompile Include="masterserver.cpp" /> <ClCompile Include="modmanager.cpp" /> <ClCompile Include="pch.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> </ClCompile> <ClCompile Include="pdef.cpp" /> diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index 8d6bc246..be15618c 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1428,6 +1428,9 @@ <ClInclude Include="bansystem.h"> <Filter>Header Files\Server\Authentication</Filter> </ClInclude> + <ClInclude Include="languagehooks.h"> + <Filter>Header Files\Client</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -1544,6 +1547,9 @@ <ClCompile Include="bansystem.cpp"> <Filter>Source Files\Server\Authentication</Filter> </ClCompile> + <ClCompile Include="languagehooks.cpp"> + <Filter>Source Files\Client</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="include\spdlog\fmt\bundled\LICENSE.rst"> diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp index ed7e8dac..de460662 100644 --- a/NorthstarDedicatedTest/convar.cpp +++ b/NorthstarDedicatedTest/convar.cpp @@ -20,7 +20,7 @@ ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, co ConVar* newVar = new ConVar; conVarConstructor(newVar, name, defaultValue, flags, helpString); - g_CustomConvars.insert(std::make_pair(name, newVar)); + g_CustomConvars.emplace(name, newVar); return newVar; } diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 83a9950a..8dedcdd9 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -6,13 +6,14 @@ bool IsDedicated() { - return CommandLine()->CheckParm("-dedicated"); + //return CommandLine()->CheckParm("-dedicated"); + return strstr(GetCommandLineA(), "-dedicated"); } // CDedidcatedExports defs struct CDedicatedExports; // forward declare -typedef void (*DedicatedSys_PrintfType)(CDedicatedExports* dedicated, char* msg); +typedef void (*DedicatedSys_PrintfType)(CDedicatedExports* dedicated, const char* msg); typedef void (*DedicatedRunServerType)(CDedicatedExports* dedicated); // would've liked to just do this as a class but have not been able to get it to work @@ -26,7 +27,7 @@ struct CDedicatedExports DedicatedRunServerType RunServer; }; -void Sys_Printf(CDedicatedExports* dedicated, char* msg) +void Sys_Printf(CDedicatedExports* dedicated, const char* msg) { spdlog::info("[DEDICATED PRINT] {}", msg); } @@ -35,7 +36,7 @@ typedef void(*CHostState__InitType)(CHostState* self); void RunServer(CDedicatedExports* dedicated) { - Sys_Printf(dedicated, (char*)"CDedicatedExports::RunServer(): starting"); + Sys_Printf(dedicated, "CDedicatedExports::RunServer(): starting"); // init hoststate, if we don't do this, we get a crash later on CHostState__InitType CHostState__Init = (CHostState__InitType)((char*)GetModuleHandleA("engine.dll") + 0x16E110); @@ -209,7 +210,7 @@ void InitialiseDedicated(HMODULE engineAddress) TempReadWrite rw(ptr); // remove call to Shader_Connect - *ptr = 0x90; + *ptr = (char)0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; *(ptr + 3) = (char)0x90; @@ -246,7 +247,7 @@ void InitialiseDedicated(HMODULE engineAddress) TempReadWrite rw(ptr); // remove call to ui loading stuff - *ptr = 0x90; + *ptr = (char)0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; *(ptr + 3) = (char)0x90; @@ -382,23 +383,18 @@ void InitialiseDedicated(HMODULE engineAddress) CommandLine()->AppendParm("+exec", "autoexec_ns_server"); } -typedef void(*Tier0_InitOriginType)(); -Tier0_InitOriginType Tier0_InitOrigin; -void Tier0_InitOriginHook() +void InitialiseDedicatedOrigin(HMODULE baseAddress) { // disable origin on dedicated // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without an origin id as a client // for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - Tier0_InitOrigin(); -} -void InitialiseDedicatedOrigin(HMODULE baseAddress) -{ if (!IsDedicated()) return; - HookEnabler hook; - ENABLER_CREATEHOOK(hook, GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), &Tier0_InitOriginHook, reinterpret_cast<LPVOID*>(&Tier0_InitOrigin)); + char* ptr = (char*)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"); + TempReadWrite rw(ptr); + *ptr = (char)0xC3; // ret } typedef void(*PrintFatalSquirrelErrorType)(void* sqvm); diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index 7097e11a..1d0e13b6 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -59,7 +59,7 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) TempReadWrite rw(ptr); // make the game always use the error material - *ptr = 0xE9; + *ptr = (char)0xE9; *(ptr + 1) = (char)0x34; *(ptr + 2) = (char)0x03; *(ptr + 3) = (char)0x00; diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index 6ae24292..cc1b0b7d 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -28,6 +28,7 @@ #include "rpakfilesystem.h" #include "bansystem.h" #include "memalloc.h" +#include "languagehooks.h" bool initialised = false; @@ -45,52 +46,49 @@ BOOL APIENTRY DllMain( HMODULE hModule, break; } - if (!initialised) - InitialiseNorthstar(); - initialised = true; - return TRUE; } void WaitForDebugger(HMODULE baseAddress) { // earlier waitfordebugger call than is in vanilla, just so we can debug stuff a little easier - if (CommandLine()->CheckParm("-waitfordebugger")) + //if (CommandLine()->CheckParm("-waitfordebugger")) + if (strstr(GetCommandLineA(), "-waitfordebugger")) { spdlog::info("waiting for debugger..."); - spdlog::info("{} bytes have been statically allocated", g_iStaticAllocated); while (!IsDebuggerPresent()) Sleep(100); } } -// in the future this will be called from launcher instead of dllmain -void InitialiseNorthstar() +bool InitialiseNorthstar() { if (initialised) { - spdlog::error("Called InitialiseNorthstar more than once!"); - return; + //spdlog::warn("Called InitialiseNorthstar more than once!"); // it's actually 100% fine for that to happen + return false; } + initialised = true; + curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); + InitialiseLogging(); // apply initial hooks InstallInitialHooks(); InitialiseInterfaceCreationHooks(); - // adding a callback to tier0 won't work for some reason - AddDllLoadCallback("launcher.dll", InitialiseTier0GameUtilFunctions); + AddDllLoadCallback("tier0.dll", InitialiseTier0GameUtilFunctions); AddDllLoadCallback("engine.dll", WaitForDebugger); AddDllLoadCallback("engine.dll", InitialiseEngineGameUtilFunctions); AddDllLoadCallback("server.dll", InitialiseServerGameUtilFunctions); // dedi patches { + AddDllLoadCallback("tier0.dll", InitialiseDedicatedOrigin); AddDllLoadCallback("engine.dll", InitialiseDedicated); - AddDllLoadCallback("launcher.dll", InitialiseDedicatedOrigin); AddDllLoadCallback("server.dll", InitialiseDedicatedServerGameDLL); AddDllLoadCallback("materialsystem_dx11.dll", InitialiseDedicatedMaterialSystem); // this fucking sucks, but seemingly we somehow load after rtech_game???? unsure how, but because of this we have to apply patches here, not on rtech_game load @@ -102,6 +100,7 @@ void InitialiseNorthstar() // client-exclusive patches { + AddDllLoadCallback("tier0.dll", InitialiseTier0LanguageHooks); AddDllLoadCallback("engine.dll", InitialiseClientEngineSecurityPatches); AddDllLoadCallback("client.dll", InitialiseClientSquirrel); AddDllLoadCallback("client.dll", InitialiseSourceConsole); @@ -131,4 +130,9 @@ void InitialiseNorthstar() // mod manager after everything else AddDllLoadCallback("engine.dll", InitialiseModManager); + + // run callbacks for any libraries that are already loaded by now + CallAllPendingDLLLoadCallbacks(); + + return true; }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp index 89eb9423..1fe3ec9d 100644 --- a/NorthstarDedicatedTest/filesystem.cpp +++ b/NorthstarDedicatedTest/filesystem.cpp @@ -57,7 +57,7 @@ std::string ReadVPKFile(const char* path) char data[4096]; do { - bytesRead = (*g_Filesystem)->m_vtable2->Read(&(*g_Filesystem)->m_vtable2, data, std::size(data), fileHandle); + bytesRead = (*g_Filesystem)->m_vtable2->Read(&(*g_Filesystem)->m_vtable2, data, (int)std::size(data), fileHandle); fileStream.write(data, bytesRead); } while (bytesRead == std::size(data)); diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index 642c44e6..1cbd8648 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -78,12 +78,32 @@ void InitialiseServerGameUtilFunctions(HMODULE baseAddress) void InitialiseTier0GameUtilFunctions(HMODULE baseAddress) { - baseAddress = GetModuleHandleA("tier0.dll"); - - CreateGlobalMemAlloc = (CreateGlobalMemAllocType)GetProcAddress(baseAddress, "CreateGlobalMemAlloc"); - g_pMemAllocSingleton = CreateGlobalMemAlloc(); - - Error = (ErrorType)GetProcAddress(baseAddress, "Error"); - CommandLine = (CommandLineType)GetProcAddress(baseAddress, "CommandLine"); - Plat_FloatTime = (Plat_FloatTimeType)GetProcAddress(baseAddress, "Plat_FloatTime"); + if (!baseAddress) + { + spdlog::critical("tier0 base address is null, but it should be already loaded"); + throw "tier0 base address is null, but it should be already loaded"; + } + if (g_pMemAllocSingleton) + return; // seems this function was already called + CreateGlobalMemAlloc = reinterpret_cast<CreateGlobalMemAllocType>(GetProcAddress(baseAddress, "CreateGlobalMemAlloc")); + IMemAlloc** ppMemAllocSingleton = reinterpret_cast<IMemAlloc**>(GetProcAddress(baseAddress, "g_pMemAllocSingleton")); + if (!ppMemAllocSingleton) + { + spdlog::critical("Address of g_pMemAllocSingleton is a null pointer, this should never happen"); + throw "Address of g_pMemAllocSingleton is a null pointer, this should never happen"; + } + if (!*ppMemAllocSingleton) + { + g_pMemAllocSingleton = CreateGlobalMemAlloc(); + *ppMemAllocSingleton = g_pMemAllocSingleton; + spdlog::info("Created new g_pMemAllocSingleton"); + } + else + { + g_pMemAllocSingleton = *ppMemAllocSingleton; + } + + Error = reinterpret_cast<ErrorType>(GetProcAddress(baseAddress, "Error")); + CommandLine = reinterpret_cast<CommandLineType>(GetProcAddress(baseAddress, "CommandLine")); + Plat_FloatTime = reinterpret_cast<Plat_FloatTimeType>(GetProcAddress(baseAddress, "Plat_FloatTime")); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index 8def57eb..43f387d1 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -7,10 +7,19 @@ class IMemAlloc public: struct VTable { - void* unknown[1]; - void* (*Alloc)(IMemAlloc* memAlloc, size_t nSize); - void* unknown2[3]; - void (*Free)(IMemAlloc* memAlloc, void* pMem); + void* unknown[1]; // alloc debug + void* (*Alloc) (IMemAlloc* memAlloc, size_t nSize); + void* unknown2[1]; // realloc debug + void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize); + void* unknown3[1]; // free #1 + void (*Free) (IMemAlloc* memAlloc, void* pMem); + void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag + size_t(*GetSize) (IMemAlloc* memAlloc, void* pMem); + void* unknown5[9]; // they all do literally nothing + void (*DumpStats) (IMemAlloc* memAlloc); + void (*DumpStatsFileBase) (IMemAlloc* memAlloc, const char* pchFileBase); + void* unknown6[4]; + int (*heapchk) (IMemAlloc* memAlloc); }; VTable* m_vtable; diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index 3de8d483..0e653d4e 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -5,15 +5,31 @@ #include <wchar.h> #include <iostream> #include <vector> +#include <fstream> +#include <sstream> +#include <filesystem> +#include <Psapi.h> + +typedef LPSTR(*GetCommandLineAType)(); +LPSTR GetCommandLineAHook(); typedef HMODULE(*LoadLibraryExAType)(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +typedef HMODULE(*LoadLibraryAType)(LPCSTR lpLibFileName); +HMODULE LoadLibraryAHook(LPCSTR lpLibFileName); + typedef HMODULE(*LoadLibraryExWType)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +typedef HMODULE(*LoadLibraryWType)(LPCWSTR lpLibFileName); +HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName); + +GetCommandLineAType GetCommandLineAOriginal; LoadLibraryExAType LoadLibraryExAOriginal; +LoadLibraryAType LoadLibraryAOriginal; LoadLibraryExWType LoadLibraryExWOriginal; +LoadLibraryWType LoadLibraryWOriginal; void InstallInitialHooks() { @@ -21,8 +37,57 @@ void InstallInitialHooks() spdlog::error("MH_Initialize failed"); HookEnabler hook; + ENABLER_CREATEHOOK(hook, &GetCommandLineA, &GetCommandLineAHook, reinterpret_cast<LPVOID*>(&GetCommandLineAOriginal)); ENABLER_CREATEHOOK(hook, &LoadLibraryExA, &LoadLibraryExAHook, reinterpret_cast<LPVOID*>(&LoadLibraryExAOriginal)); + ENABLER_CREATEHOOK(hook, &LoadLibraryA, &LoadLibraryAHook, reinterpret_cast<LPVOID*>(&LoadLibraryAOriginal)); ENABLER_CREATEHOOK(hook, &LoadLibraryExW, &LoadLibraryExWHook, reinterpret_cast<LPVOID*>(&LoadLibraryExWOriginal)); + ENABLER_CREATEHOOK(hook, &LoadLibraryW, &LoadLibraryWHook, reinterpret_cast<LPVOID*>(&LoadLibraryWOriginal)); +} + +LPSTR GetCommandLineAHook() +{ + static char* cmdlineModified; + static char* cmdlineOrg; + + if (cmdlineOrg == nullptr || cmdlineModified == nullptr) + { + cmdlineOrg = GetCommandLineAOriginal(); + bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument + + std::string args; + std::ifstream cmdlineArgFile; + + // it looks like CommandLine() prioritizes parameters apprearing first, so we want the real commandline to take priority + // not to mention that cmdlineOrg starts with the EXE path + args.append(cmdlineOrg); + args.append(" "); + + // append those from the file + + cmdlineArgFile = std::ifstream(!isDedi ? "ns_startup_args.txt" : "ns_startup_args_dedi.txt"); + + if (cmdlineArgFile) + { + std::stringstream argBuffer; + argBuffer << cmdlineArgFile.rdbuf(); + cmdlineArgFile.close(); + + args.append(argBuffer.str()); + } + + auto len = args.length(); + cmdlineModified = new char[len + 1]; + if (!cmdlineModified) + { + spdlog::error("malloc failed for command line"); + return cmdlineOrg; + } + memcpy(cmdlineModified, args.c_str(), len + 1); + + spdlog::info("Command line: {}", cmdlineModified); + } + + return cmdlineModified; } // dll load callback stuff @@ -46,21 +111,75 @@ void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback) dllLoadCallbacks.push_back(callbackStruct); } -HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) { - HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); - - if (moduleAddress) + for (auto& callbackStruct : dllLoadCallbacks) + { + if (!callbackStruct->called && strstr(lpLibFileName + (strlen(lpLibFileName) - callbackStruct->dll.length()), callbackStruct->dll.c_str()) != nullptr) + { + callbackStruct->callback(moduleAddress); + callbackStruct->called = true; + } + } +} + +void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress) +{ + for (auto& callbackStruct : dllLoadCallbacks) + { + std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); + const wchar_t* callbackDll = wcharStrDll.c_str(); + if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcharStrDll.length()), callbackDll) != nullptr) + { + callbackStruct->callback(moduleAddress); + callbackStruct->called = true; + } + } +} + +void CallAllPendingDLLLoadCallbacks() +{ + HMODULE hMods[1024]; + HANDLE hProcess = GetCurrentProcess(); + DWORD cbNeeded; + unsigned int i; + + // Get a list of all the modules in this process. + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { - for (auto& callbackStruct : dllLoadCallbacks) + for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { - if (!callbackStruct->called && strstr(lpLibFileName + (strlen(lpLibFileName) - strlen(callbackStruct->dll.c_str())), callbackStruct->dll.c_str()) != nullptr) + wchar_t szModName[MAX_PATH]; + + // Get the full path to the module's file. + if (GetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; + CallLoadLibraryWCallbacks(szModName, hMods[i]); } } } +} + +HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); + + if (moduleAddress) + { + CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); + } + + return moduleAddress; +} + +HMODULE LoadLibraryAHook(LPCSTR lpLibFileName) +{ + HMODULE moduleAddress = LoadLibraryAOriginal(lpLibFileName); + + if (moduleAddress) + { + CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); + } return moduleAddress; } @@ -71,16 +190,19 @@ HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) if (moduleAddress) { - for (auto& callbackStruct : dllLoadCallbacks) - { - std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); - const wchar_t* callbackDll = wcharStrDll.c_str(); - if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcslen(callbackDll)), callbackDll) != nullptr) - { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; - } - } + CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); + } + + return moduleAddress; +} + +HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName) +{ + HMODULE moduleAddress = LoadLibraryWOriginal(lpLibFileName); + + if (moduleAddress) + { + CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); } return moduleAddress; diff --git a/NorthstarDedicatedTest/hooks.h b/NorthstarDedicatedTest/hooks.h index 972b38a6..10e4d4ba 100644 --- a/NorthstarDedicatedTest/hooks.h +++ b/NorthstarDedicatedTest/hooks.h @@ -4,4 +4,6 @@ void InstallInitialHooks(); typedef void(*DllLoadCallbackFuncType)(HMODULE moduleAddress); -void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback);
\ No newline at end of file +void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback); + +void CallAllPendingDLLLoadCallbacks();
\ No newline at end of file diff --git a/NorthstarDedicatedTest/hookutils.cpp b/NorthstarDedicatedTest/hookutils.cpp index e86c671c..8ab24a3b 100644 --- a/NorthstarDedicatedTest/hookutils.cpp +++ b/NorthstarDedicatedTest/hookutils.cpp @@ -38,7 +38,7 @@ void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOrigina else { if (targetName != nullptr) - spdlog::error("MH_CreateHook failed for function %s", targetName); + spdlog::error("MH_CreateHook failed for function {}", targetName); else spdlog::error("MH_CreateHook failed for unknown function"); } @@ -51,7 +51,7 @@ HookEnabler::~HookEnabler() if (MH_EnableHook(hook->targetAddress) != MH_OK) { if (hook->targetName != nullptr) - spdlog::error("MH_EnableHook failed for function %s", hook->targetName); + spdlog::error("MH_EnableHook failed for function {}", hook->targetName); else spdlog::error("MH_EnableHook failed for unknown function"); } diff --git a/NorthstarDedicatedTest/include/MinHook.x64.dll b/NorthstarDedicatedTest/include/MinHook.x64.dll Binary files differdeleted file mode 100644 index b4f51c2e..00000000 --- a/NorthstarDedicatedTest/include/MinHook.x64.dll +++ /dev/null diff --git a/NorthstarDedicatedTest/include/MinHook.x64.lib b/NorthstarDedicatedTest/include/MinHook.x64.lib Binary files differindex 909fe682..213df08f 100644 --- a/NorthstarDedicatedTest/include/MinHook.x64.lib +++ b/NorthstarDedicatedTest/include/MinHook.x64.lib diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp index 6de93822..b14ecf42 100644 --- a/NorthstarDedicatedTest/keyvalues.cpp +++ b/NorthstarDedicatedTest/keyvalues.cpp @@ -50,7 +50,7 @@ void ModManager::TryBuildKeyValues(const char* filename) // copy over patch kv files, and add #bases to new file, last mods' patches should be applied first // note: #include should be identical but it's actually just broken, thanks respawn - for (int i = m_loadedMods.size() - 1; i > -1; i--) + for (int64_t i = m_loadedMods.size() - 1; i > -1; i--) { if (!m_loadedMods[i].Enabled) continue; @@ -92,6 +92,7 @@ void ModManager::TryBuildKeyValues(const char* filename) char rootName[64]; memset(rootName, 0, sizeof(rootName)); + rootName[63] = '\0'; // iterate until we hit an ascii char that isn't in a # command or comment to get root obj name int i = 0; diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp new file mode 100644 index 00000000..08bacaf9 --- /dev/null +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -0,0 +1,109 @@ +#include "pch.h" +#include "languagehooks.h" +#include "gameutils.h" +#include <filesystem> +#include <regex> + +namespace fs = std::filesystem; + +typedef char* (*GetGameLanguageType)(); +char* GetGameLanguage(); + +typedef LANGID(*Tier0_DetectDefaultLanguageType)(); + +GetGameLanguageType GetGameLanguageOriginal; + +bool CheckLangAudioExists(char* lang) +{ + std::string path{ "r2\\sound\\general_" }; + path += lang; + path += ".mstr"; + return fs::exists(path); +} + +std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern) +{ + std::vector<std::string> result; + + using iterator = fs::directory_iterator; + + const iterator end; + for (iterator iter{ dir }; iter != end; ++iter) + { + const std::string filename = iter->path().filename().string(); + std::smatch matches; + if (fs::is_regular_file(*iter) && std::regex_match(filename, matches, ext_pattern)) + { + result.push_back(std::move(matches.str(1))); + } + } + + return result; +} + +std::string GetAnyInstalledAudioLanguage() +{ + for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr"))) + if (lang != "general" || lang != "") + return lang; + return "NO LANGUAGE DETECTED"; +} + +char* GetGameLanguageHook() +{ + auto tier0Handle = GetModuleHandleA("tier0.dll"); + auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage"); + char* ingameLang1 = (char*)tier0Handle + 0xA9B60; // one of the globals we need to override if overriding lang (size: 256) + bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90); + + const char* forcedLanguage; + if (CommandLine()->CheckParm("-language", &forcedLanguage)) + { + if (!CheckLangAudioExists((char*)forcedLanguage)) + { + spdlog::info("User tried to force the language (-language) to \"{}\", but audio for this language doesn't exist and the game is bound to error, falling back to next option...", forcedLanguage); + } + else + { + spdlog::info("User forcing the language (-language) to: {}", forcedLanguage); + strncpy(ingameLang1, forcedLanguage, 256); + return ingameLang1; + } + } + + canOriginDictateLang = true; // let it try + { + auto lang = GetGameLanguageOriginal(); + if (!CheckLangAudioExists(lang)) + { + spdlog::info("Origin detected language \"{}\", but we do not have audio for it installed, falling back to the next option", lang); + + } + else + { + spdlog::info("Origin detected language: {}", lang); + return lang; + } + } + + Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than defaulting to Russian + canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language + auto lang = GetGameLanguageOriginal(); + spdlog::info("Detected system language: {}", lang); + if (!CheckLangAudioExists(lang)) + { + spdlog::warn("Caution, audio for this language does NOT exist. You might want to override your game language with -language command line option."); + auto lang = GetAnyInstalledAudioLanguage(); + spdlog::warn("Falling back to first installed audio language: {}", lang.c_str()); + strncpy(ingameLang1, lang.c_str(), 256); + return ingameLang1; + } + + return lang; +} + +void InitialiseTier0LanguageHooks(HMODULE baseAddress) +{ + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xF560, &GetGameLanguageHook, reinterpret_cast<LPVOID*>(&GetGameLanguageOriginal)); +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/languagehooks.h b/NorthstarDedicatedTest/languagehooks.h new file mode 100644 index 00000000..55b591e0 --- /dev/null +++ b/NorthstarDedicatedTest/languagehooks.h @@ -0,0 +1,3 @@ +#pragma once + +void InitialiseTier0LanguageHooks(HMODULE baseAddress); diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index e13a9150..7d5f7ae5 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -113,7 +113,7 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; - DWORD crashedModuleOffset = ((DWORD)exceptionAddress) - ((DWORD)crashedModuleInfo.lpBaseOfDll); + DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); CONTEXT* exceptionContext = exceptionInfo->ContextRecord; spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); @@ -186,6 +186,7 @@ void InitialiseLogging() AllocConsole(); freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); spdlog::flush_on(spdlog::level::info); diff --git a/NorthstarDedicatedTest/main.h b/NorthstarDedicatedTest/main.h index ef5d86dc..90e88912 100644 --- a/NorthstarDedicatedTest/main.h +++ b/NorthstarDedicatedTest/main.h @@ -1,3 +1,3 @@ #pragma once -extern "C" __declspec(dllexport) void InitialiseNorthstar();
\ No newline at end of file +extern "C" __declspec(dllexport) bool InitialiseNorthstar();
\ No newline at end of file diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index 0913be11..01f4bc8e 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -3,7 +3,6 @@ #include "concommand.h" #include "gameutils.h" #include "hookutils.h" -#include "libcurl/include/curl/curl.h" #include "serverauthentication.h" #include "gameutils.h" #include "rapidjson/document.h" @@ -38,6 +37,39 @@ CHostState__State_ChangeLevelSPType CHostState__State_ChangeLevelSP; typedef void(*CHostState__State_GameShutdownType)(CHostState* hostState); CHostState__State_GameShutdownType CHostState__State_GameShutdown; +const char* HttplibErrorToString(httplib::Error error) +{ + switch (error) + { + case httplib::Error::Success: + return "httplib::Error::Success"; + case httplib::Error::Unknown: + return "httplib::Error::Unknown"; + case httplib::Error::Connection: + return "httplib::Error::Connection"; + case httplib::Error::BindIPAddress: + return "httplib::Error::BindIPAddress"; + case httplib::Error::Read: + return "httplib::Error::Read"; + case httplib::Error::Write: + return "httplib::Error::Write"; + case httplib::Error::ExceedRedirectCount: + return "httplib::Error::ExceedRedirectCount"; + case httplib::Error::Canceled: + return "httplib::Error::Canceled"; + case httplib::Error::SSLConnection: + return "httplib::Error::SSLConnection"; + case httplib::Error::SSLLoadingCerts: + return "httplib::Error::SSLLoadingCerts"; + case httplib::Error::SSLServerVerification: + return "httplib::Error::SSLServerVerification"; + case httplib::Error::UnsupportedMultipartBoundaryChars: + return "httplib::Error::UnsupportedMultipartBoundaryChars"; + } + + return ""; +} + RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, bool newRequiresPassword) { // passworded servers don't have public ips @@ -59,6 +91,18 @@ RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const maxPlayers = newMaxPlayers; } +void MasterServerManager::SetCommonHttpClientOptions(CURL* curl) +{ + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + //curl_easy_setopt(curl, CURLOPT_STDERR, stdout); + if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work + { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + } +} + void MasterServerManager::ClearServerList() { // this doesn't really do anything lol, probably isn't threadsafe @@ -90,12 +134,11 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or spdlog::info("Trying to authenticate with northstar masterserver for user {}", uidStr); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/origin_auth?id={}&token={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -105,12 +148,12 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or { m_successfullyConnected = true; - rapidjson::Document originAuthInfo; + rapidjson_document originAuthInfo; originAuthInfo.Parse(readBuffer.c_str()); if (originAuthInfo.HasParseError()) { - spdlog::error("Failed reading origin auth info response: encountered parse error \{}\"", rapidjson::GetParseError_En(originAuthInfo.GetParseError())); + spdlog::error("Failed reading origin auth info response: encountered parse error \"{}\"", rapidjson::GetParseError_En(originAuthInfo.GetParseError())); goto REQUEST_END_CLEANUP; } @@ -163,12 +206,11 @@ void MasterServerManager::RequestServerList() spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->m_pszString); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/servers", Cvar_ns_masterserver_hostname->m_pszString).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -178,7 +220,7 @@ void MasterServerManager::RequestServerList() { m_successfullyConnected = true; - rapidjson::Document serverInfoJson; + rapidjson_document serverInfoJson; serverInfoJson.Parse(readBuffer.c_str()); if (serverInfoJson.HasParseError()) @@ -200,7 +242,7 @@ void MasterServerManager::RequestServerList() goto REQUEST_END_CLEANUP; } - rapidjson::GenericArray<false, rapidjson::Value> serverArray = serverInfoJson.GetArray(); + rapidjson::GenericArray<false, rapidjson_document::GenericValue> serverArray = serverInfoJson.GetArray(); spdlog::info("Got {} servers", serverArray.Size()); @@ -273,7 +315,7 @@ void MasterServerManager::RequestServerList() std::sort(m_remoteServers.begin(), m_remoteServers.end(), [](RemoteServerInfo& a, RemoteServerInfo& b) { return a.playerCount > b.playerCount; - }); + }); } else { @@ -301,12 +343,11 @@ void MasterServerManager::RequestMainMenuPromos() Sleep(500); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/mainmenupromos", Cvar_ns_masterserver_hostname->m_pszString).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -316,7 +357,7 @@ void MasterServerManager::RequestMainMenuPromos() { m_successfullyConnected = true; - rapidjson::Document mainMenuPromoJson; + rapidjson_document mainMenuPromoJson; mainMenuPromoJson.Parse(readBuffer.c_str()); if (mainMenuPromoJson.HasParseError()) @@ -412,12 +453,11 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken std::thread requestThread([this, uidStr, tokenStr]() { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -427,7 +467,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken { m_successfullyConnected = true; - rapidjson::Document authInfoJson; + rapidjson_document authInfoJson; authInfoJson.Parse(readBuffer.c_str()); if (authInfoJson.HasParseError()) @@ -480,7 +520,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken goto REQUEST_END_CLEANUP; } - newAuthData.pdata[i++] = (char)byte.GetUint(); + newAuthData.pdata[i++] = static_cast<char>(byte.GetUint()); } std::lock_guard<std::mutex> guard(g_ServerAuthenticationManager->m_authDataMutex); @@ -538,11 +578,10 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c spdlog::info("Attempting authentication with server of id \"{}\"", serverIdStr); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -560,7 +599,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c { m_successfullyConnected = true; - rapidjson::Document connectionInfoJson; + rapidjson_document connectionInfoJson; connectionInfoJson.Parse(readBuffer.c_str()); if (connectionInfoJson.HasParseError()) @@ -595,7 +634,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c } m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString()); - m_pendingConnectionInfo.port = connectionInfoJson["port"].GetInt(); + m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint(); strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31); m_pendingConnectionInfo.authToken[31] = 0; @@ -645,37 +684,36 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name m_ownServerAuthToken[0] = 0; // build modinfo obj - rapidjson::Document modinfoDoc; + rapidjson_document modinfoDoc; modinfoDoc.SetObject(); - modinfoDoc.AddMember("Mods", rapidjson::Value(rapidjson::kArrayType), modinfoDoc.GetAllocator()); + modinfoDoc.AddMember("Mods", rapidjson_document::GenericValue(rapidjson::kArrayType), modinfoDoc.GetAllocator()); - int currentModIndex = 0; - for (Mod& mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) - continue; + int currentModIndex = 0; + for (Mod& mod : g_ModManager->m_loadedMods) + { + if (!mod.Enabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) + continue; - modinfoDoc["Mods"].PushBack(rapidjson::Value(rapidjson::kObjectType), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Pdiff", rapidjson::StringRef(&mod.Pdiff[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"].PushBack(rapidjson_document::GenericValue(rapidjson::kObjectType), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Pdiff", rapidjson::StringRef(&mod.Pdiff[0]), modinfoDoc.GetAllocator()); - currentModIndex++; - } + currentModIndex++; + } - rapidjson::StringBuffer buffer; - buffer.Clear(); - rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); - modinfoDoc.Accept(writer); - const char* modInfoString = buffer.GetString(); + rapidjson::StringBuffer buffer; + buffer.Clear(); + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + modinfoDoc.Accept(writer); + const char* modInfoString = buffer.GetString(); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -712,7 +750,7 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name { m_successfullyConnected = true; - rapidjson::Document serverAddedJson; + rapidjson_document serverAddedJson; serverAddedJson.Parse(readBuffer.c_str()); if (serverAddedJson.HasParseError()) @@ -746,8 +784,8 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name goto REQUEST_END_CLEANUP; } - strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); - m_ownServerId[sizeof(m_ownServerId) - 1] = 0; + strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); + m_ownServerId[sizeof(m_ownServerId) - 1] = 0; strncpy(m_ownServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_ownServerAuthToken)); m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0; @@ -760,13 +798,14 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name do { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/heartbeat?id={}&playerCount={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, g_ServerAuthenticationManager->m_additionalPlayerData.size()).c_str()); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); CURLcode result = curl_easy_perform(curl); if (result != CURLcode::CURLE_OK) @@ -805,11 +844,10 @@ void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, std::thread requestThread([this, strMap, strPlaylist, maxPlayers] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -846,11 +884,10 @@ void MasterServerManager::UpdateServerPlayerCount(int playerCount) std::thread requestThread([this, playerCount] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, playerCount).c_str()); @@ -884,12 +921,11 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, std::thread requestThread([this, strPlayerId, strPdata, pdataSize] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->m_pszString, strPlayerId, m_ownServerId).c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -912,7 +948,7 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, curl_easy_cleanup(curl); - m_savingPersistentData = false; + m_savingPersistentData = false; }); requestThread.detach(); @@ -926,11 +962,10 @@ void MasterServerManager::RemoveSelfFromServerList() std::thread requestThread([this] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId).c_str()); @@ -1004,9 +1039,9 @@ void CHostState__State_GameShutdownHook(CHostState* hostState) CHostState__State_GameShutdown(hostState); } -MasterServerManager::MasterServerManager() +MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{ "" }, m_ownClientAuthToken{ "" } { - curl_global_init(CURL_GLOBAL_DEFAULT); + } void InitialiseSharedMasterServer(HMODULE baseAddress) diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h index b4d0d476..d3c83052 100644 --- a/NorthstarDedicatedTest/masterserver.h +++ b/NorthstarDedicatedTest/masterserver.h @@ -37,7 +37,7 @@ public: char authToken[32]; in_addr ip; - int port; + unsigned short port; }; struct MainMenuPromoData @@ -93,6 +93,9 @@ public: bool m_bHasMainMenuPromoData = false; MainMenuPromoData m_MainMenuPromoData; +private: + void SetCommonHttpClientOptions(CURL* curl); + public: MasterServerManager(); void ClearServerList(); diff --git a/NorthstarDedicatedTest/memalloc.cpp b/NorthstarDedicatedTest/memalloc.cpp index 113f56b9..1b9eaae8 100644 --- a/NorthstarDedicatedTest/memalloc.cpp +++ b/NorthstarDedicatedTest/memalloc.cpp @@ -2,39 +2,76 @@ #include "memalloc.h" #include "gameutils.h" -// so for anyone reading this code, you may be curious why the fuck i'm overriding new to alloc into a static 100k buffer -// pretty much, the issue here is that we need to use the game's memory allocator (g_pMemAllocSingleton) or risk heap corruptions, but this allocator is defined in tier0 -// as such, it doesn't exist when we inject -// initially i wanted to just call malloc and free until g_pMemAllocSingleton was initialised, but the issue then becomes that we might try to -// call g_pMemAllocSingleton->Free on memory that was allocated with malloc, which will cause game to crash -// so, the best idea i had for this was to just alloc 100k of memory, have all pre-tier0 allocations use that -// (from what i can tell we hit about 12k before tier0 is loaded atm in debug builds, so it's more than enough) -// then just use the game's allocator after that -// yes, this means we leak 100k of memory, idk how else to do this without breaking stuff +// TODO: rename to malloc and free after removing statically compiled .libs -const int STATIC_ALLOC_SIZE = 100000; // alot more than we need, could reduce to 50k or even 25k later potentially - -size_t g_iStaticAllocated = 0; -char pStaticAllocBuf[STATIC_ALLOC_SIZE]; - -void* operator new(size_t n) +extern "C" void* _malloc_base(size_t n) { // allocate into static buffer if g_pMemAllocSingleton isn't initialised - if (g_pMemAllocSingleton) - return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n); - else + if (!g_pMemAllocSingleton) { - void* ret = pStaticAllocBuf + g_iStaticAllocated; - g_iStaticAllocated += n; - return ret; - } + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } + return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n); } -void operator delete(void* p) +/*extern "C" void* malloc(size_t n) { - // if it was allocated into the static buffer, just do nothing, safest way to deal with it - if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE) - return; + return _malloc_base(n); +}*/ +extern "C" void _free_base(void* p) +{ + if (!g_pMemAllocSingleton) + { + spdlog::warn("Trying to free something before g_pMemAllocSingleton was ready, this should never happen"); + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } g_pMemAllocSingleton->m_vtable->Free(g_pMemAllocSingleton, p); -}
\ No newline at end of file +} + + +extern "C" void* _realloc_base(void* oldPtr, size_t size) +{ + if (!g_pMemAllocSingleton) + { + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } + return g_pMemAllocSingleton->m_vtable->Realloc(g_pMemAllocSingleton, oldPtr, size); +} + +extern "C" void* _calloc_base(size_t n, size_t size) +{ + size_t bytes = n * size; + void* memory = _malloc_base(bytes); + if (memory) + { + memset(memory, 0, bytes); + } + return memory; +} + +extern "C" char* _strdup_base(const char* src) +{ + char* str; + char* p; + int len = 0; + + while (src[len]) + len++; + str = reinterpret_cast<char*>(_malloc_base(len + 1)); + p = str; + while (*src) + *p++ = *src++; + *p = '\0'; + return str; +} + +void* operator new(size_t n) +{ + return _malloc_base(n); +} + +void operator delete(void* p) +{ + _free_base(p); +}// /FORCE:MULTIPLE
\ No newline at end of file diff --git a/NorthstarDedicatedTest/memalloc.h b/NorthstarDedicatedTest/memalloc.h index fe3c5255..92ab9672 100644 --- a/NorthstarDedicatedTest/memalloc.h +++ b/NorthstarDedicatedTest/memalloc.h @@ -1,6 +1,42 @@ #pragma once -extern size_t g_iStaticAllocated; +#include "include/rapidjson/document.h" +//#include "include/rapidjson/allocators.h" + +extern "C" void* _malloc_base(size_t size); +extern "C" void* _calloc_base(size_t const count, size_t const size); +extern "C" void* _realloc_base(void* block, size_t size); +extern "C" void* _recalloc_base(void* const block, size_t const count, size_t const size); +extern "C" void _free_base(void* const block); +extern "C" char* _strdup_base(const char* src); void* operator new(size_t n); -void operator delete(void* p);
\ No newline at end of file +void operator delete(void* p); + +//void* malloc(size_t n); + +class SourceAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return _malloc_base(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + _free_base(originalPtr); + return NULL; + } + return _realloc_base(originalPtr, newSize); + } + static void Free(void* ptr) { _free_base(ptr); } +}; + +typedef rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<SourceAllocator>, SourceAllocator> rapidjson_document; +//typedef rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator> rapidjson_document; +//typedef rapidjson::Document rapidjson_document; +//using MyDocument = rapidjson::GenericDocument<rapidjson::UTF8<>, MemoryAllocator>; +//using rapidjson_document = rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator>; diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 4f4a2da0..23dd2d6e 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -22,7 +22,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) ModDirectory = modDir; - rapidjson::Document modJson; + rapidjson_document modJson; modJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(jsonBuf); // fail if parse error @@ -309,7 +309,7 @@ void ModManager::LoadMods() if (fs::is_regular_file(file)) { std::string kvStr = file.path().lexically_relative(mod.ModDirectory / "keyvalues").lexically_normal().string(); - mod.KeyValues.insert(std::make_pair(std::hash<std::string>{}(kvStr), kvStr)); + mod.KeyValues.emplace(std::hash<std::string>{}(kvStr), kvStr); } } } @@ -333,7 +333,7 @@ void ModManager::LoadMods() } // in a seperate loop because we register mod files in reverse order, since mods loaded later should have their files prioritised - for (int i = m_loadedMods.size() - 1; i > -1; i--) + for (int64_t i = m_loadedMods.size() - 1; i > -1; i--) { if (!m_loadedMods[i].Enabled) continue; @@ -379,7 +379,7 @@ void ModManager::UnloadMods() // should we be doing this here or should scripts be doing this manually? // main issue with doing this here is when we reload mods for connecting to a server, we write enabled mods, which isn't necessarily what we wanna do if (!m_enabledModsCfg.HasMember(mod.Name.c_str())) - m_enabledModsCfg.AddMember(rapidjson::StringRef(mod.Name.c_str()), rapidjson::Value(false), m_enabledModsCfg.GetAllocator()); + m_enabledModsCfg.AddMember(rapidjson_document::StringRefType(mod.Name.c_str()), rapidjson_document::GenericValue(false), m_enabledModsCfg.GetAllocator()); m_enabledModsCfg[mod.Name.c_str()].SetBool(mod.Enabled); } diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h index 5f2f6441..20cb0a42 100644 --- a/NorthstarDedicatedTest/modmanager.h +++ b/NorthstarDedicatedTest/modmanager.h @@ -4,6 +4,7 @@ #include <vector> #include <filesystem> #include "rapidjson/document.h" +#include "memalloc.h" namespace fs = std::filesystem; @@ -100,7 +101,7 @@ class ModManager private: bool m_hasLoadedMods = false; bool m_hasEnabledModsCfg; - rapidjson::Document m_enabledModsCfg; + rapidjson_document m_enabledModsCfg; // precalculated hashes size_t m_hScriptsRsonHash; diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h index 9ac5b8a9..a07d1401 100644 --- a/NorthstarDedicatedTest/pch.h +++ b/NorthstarDedicatedTest/pch.h @@ -11,10 +11,12 @@ // httplib ssl // add headers that you want to pre-compile here +#include "memalloc.h" #include <Windows.h> #include "logging.h" #include "include/MinHook.h" #include "spdlog/spdlog.h" +#include "libcurl/include/curl/curl.h" #include "hookutils.h" #endif
\ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index 59a13a26..9cbd0c05 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -95,7 +95,7 @@ void InitialisePlaylistHooks(HMODULE baseAddress) { void* ptr = (char*)baseAddress + 0x18ED8D; TempReadWrite rw(ptr); - *((char*)ptr) = 0xC3; // jmp => ret + *((char*)ptr) = (char)0xC3; // jmp => ret } if (IsDedicated()) diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index e0ac5d57..33a57c6a 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -162,7 +162,7 @@ bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid, // get file length pdataStream.seekg(0, pdataStream.end); - int length = pdataStream.tellg(); + auto length = pdataStream.tellg(); pdataStream.seekg(0, pdataStream.beg); // copy pdata into buffer diff --git a/R2Northstar.sln b/R2Northstar.sln index 1b664dd9..3dfdb218 100644 --- a/R2Northstar.sln +++ b/R2Northstar.sln @@ -1,35 +1,38 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30621.155 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Northstar", "NorthstarDedicatedTest\NorthstarDedicatedTest.vcxproj", "{CFAD2623-064F-453C-8196-79EE10292E32}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NorthstarLauncher", "LauncherInjector\LauncherInjector.vcxproj", "{0EA82CB0-53FE-4D4C-96DF-47FA970513D0}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loader_launcher_proxy", "loader_launcher_proxy\loader_launcher_proxy.vcxproj", "{F65C322D-66DF-4AF1-B650-70221DE334C0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loader_wsock32_proxy", "loader_wsock32_proxy\loader_wsock32_proxy.vcxproj", "{CF55F3B5-F348-450A-9CCB-C269F21D629D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CFAD2623-064F-453C-8196-79EE10292E32}.Debug|x64.ActiveCfg = Debug|x64 {CFAD2623-064F-453C-8196-79EE10292E32}.Debug|x64.Build.0 = Debug|x64 - {CFAD2623-064F-453C-8196-79EE10292E32}.Debug|x86.ActiveCfg = Debug|Win32 {CFAD2623-064F-453C-8196-79EE10292E32}.Release|x64.ActiveCfg = Release|x64 {CFAD2623-064F-453C-8196-79EE10292E32}.Release|x64.Build.0 = Release|x64 - {CFAD2623-064F-453C-8196-79EE10292E32}.Release|x86.ActiveCfg = Release|Win32 - {CFAD2623-064F-453C-8196-79EE10292E32}.Release|x86.Build.0 = Release|Win32 {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Debug|x64.ActiveCfg = Debug|x64 {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Debug|x64.Build.0 = Debug|x64 - {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Debug|x86.ActiveCfg = Debug|Win32 - {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Debug|x86.Build.0 = Debug|Win32 {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Release|x64.ActiveCfg = Release|x64 {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Release|x64.Build.0 = Release|x64 - {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Release|x86.ActiveCfg = Release|Win32 - {0EA82CB0-53FE-4D4C-96DF-47FA970513D0}.Release|x86.Build.0 = Release|Win32 + {F65C322D-66DF-4AF1-B650-70221DE334C0}.Debug|x64.ActiveCfg = Debug|x64 + {F65C322D-66DF-4AF1-B650-70221DE334C0}.Debug|x64.Build.0 = Debug|x64 + {F65C322D-66DF-4AF1-B650-70221DE334C0}.Release|x64.ActiveCfg = Release|x64 + {F65C322D-66DF-4AF1-B650-70221DE334C0}.Release|x64.Build.0 = Release|x64 + {CF55F3B5-F348-450A-9CCB-C269F21D629D}.Debug|x64.ActiveCfg = Debug|x64 + {CF55F3B5-F348-450A-9CCB-C269F21D629D}.Debug|x64.Build.0 = Debug|x64 + {CF55F3B5-F348-450A-9CCB-C269F21D629D}.Release|x64.ActiveCfg = Release|x64 + {CF55F3B5-F348-450A-9CCB-C269F21D629D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/loader_launcher_proxy/Memory.cpp b/loader_launcher_proxy/Memory.cpp new file mode 100644 index 00000000..f00c4d96 --- /dev/null +++ b/loader_launcher_proxy/Memory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" + +extern HMODULE hTier0Module; +IMemAlloc** g_ppMemAllocSingleton; + +void LoadTier0Handle() +{ + if (!hTier0Module) hTier0Module = GetModuleHandleA("tier0.dll"); + if (!hTier0Module) return; + + g_ppMemAllocSingleton = (IMemAlloc**)GetProcAddress(hTier0Module, "g_pMemAllocSingleton"); +} + +const int STATIC_ALLOC_SIZE = 4096; + +size_t g_iStaticAllocated = 0; +void* g_pLastAllocated = nullptr; +char pStaticAllocBuf[STATIC_ALLOC_SIZE]; + +// they should never be used here, except in LibraryLoadError? + +void* malloc(size_t n) +{ + // allocate into static buffer + if (g_iStaticAllocated + n <= STATIC_ALLOC_SIZE) + { + void* ret = pStaticAllocBuf + g_iStaticAllocated; + g_iStaticAllocated += n; + return ret; + } + else + { + // try to fallback to g_pMemAllocSingleton + if (!hTier0Module || !g_ppMemAllocSingleton) LoadTier0Handle(); + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + return (*g_ppMemAllocSingleton)->m_vtable->Alloc(*g_ppMemAllocSingleton, n); + else + throw "Cannot allocate"; + } +} + +void free(void* p) +{ + // if it was allocated into the static buffer, just do nothing, safest way to deal with it + if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE) + return; + + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + (*g_ppMemAllocSingleton)->m_vtable->Free(*g_ppMemAllocSingleton, p); +} + +void* realloc(void* old_ptr, size_t size) { + // it was allocated into the static buffer + if (old_ptr >= pStaticAllocBuf && old_ptr <= pStaticAllocBuf + STATIC_ALLOC_SIZE) + { + if (g_pLastAllocated == old_ptr) + { + // nothing was allocated after this + size_t old_size = g_iStaticAllocated - ((size_t)g_pLastAllocated - (size_t)pStaticAllocBuf); + size_t diff = size - old_size; + if (diff > 0) + g_iStaticAllocated += diff; + return old_ptr; + } + else + { + return malloc(size); + } + } + + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + return (*g_ppMemAllocSingleton)->m_vtable->Realloc(*g_ppMemAllocSingleton, old_ptr, size); + return nullptr; +} + +void* operator new(size_t n) +{ + return malloc(n); +} + +void operator delete(void* p) +{ + return free(p); +} diff --git a/loader_launcher_proxy/Memory.h b/loader_launcher_proxy/Memory.h new file mode 100644 index 00000000..c983966c --- /dev/null +++ b/loader_launcher_proxy/Memory.h @@ -0,0 +1,24 @@ +#pragma once + +class IMemAlloc +{ +public: + struct VTable + { + void* unknown[1]; // alloc debug + void* (*Alloc) (IMemAlloc* memAlloc, size_t nSize); + void* unknown2[1]; // realloc debug + void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize); + void* unknown3[1]; // free #1 + void (*Free) (IMemAlloc* memAlloc, void* pMem); + void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag + size_t(*GetSize) (IMemAlloc* memAlloc, void* pMem); + void* unknown5[9]; // they all do literally nothing + void (*DumpStats) (IMemAlloc* memAlloc); + void (*DumpStatsFileBase) (IMemAlloc* memAlloc, const char* pchFileBase); + void* unknown6[4]; + int (*heapchk) (IMemAlloc* memAlloc); + }; + + VTable* m_vtable; +}; diff --git a/loader_launcher_proxy/dllmain.cpp b/loader_launcher_proxy/dllmain.cpp new file mode 100644 index 00000000..cf69d63e --- /dev/null +++ b/loader_launcher_proxy/dllmain.cpp @@ -0,0 +1,156 @@ +#include "pch.h" +#include <stdio.h> +#include <string> +#include <system_error> +#include <Shlwapi.h> +#include <sstream> +#include <fstream> + +HMODULE hLauncherModule; +HMODULE hHookModule; +HMODULE hTier0Module; + +using CreateInterfaceFn = void* (*)(const char* pName, int* pReturnCode); + +// does not seem to ever be used +extern "C" _declspec(dllexport) void* __fastcall CreateInterface(const char* pName, int* pReturnCode) +{ + //AppSystemCreateInterfaceFn(pName, pReturnCode); + printf("external CreateInterface: name: %s\n", pName); + + static CreateInterfaceFn launcher_CreateInterface = (CreateInterfaceFn)GetProcAddress(hLauncherModule, "CreateInterface"); + auto res = launcher_CreateInterface(pName, pReturnCode); + + printf("external CreateInterface: return code: %p\n", res); + return res; +} + +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); +} + +FARPROC GetLauncherMain() +{ + static FARPROC Launcher_LauncherMain; + if (!Launcher_LauncherMain) + Launcher_LauncherMain = GetProcAddress(hLauncherModule, "LauncherMain"); + return Launcher_LauncherMain; +} + +void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) +{ + char text[2048]; + 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()); + MessageBoxA(GetForegroundWindow(), text, "Northstar Launcher Proxy Error", 0); +} + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +wchar_t exePath[4096]; +wchar_t dllPath[4096]; + +bool ShouldLoadNorthstar() +{ + bool loadNorthstar = !strstr(GetCommandLineA(), "-vanilla"); + + 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 = false; + } + return loadNorthstar; +} + +bool LoadNorthstar() +{ + FARPROC Hook_Init = nullptr; + { + swprintf_s(dllPath, L"%s\\Northstar.dll", exePath); + hHookModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (!hHookModule || Hook_Init == nullptr) + { + LibraryLoadError(GetLastError(), L"Northstar.dll", dllPath); + return false; + } + } + + ((bool (*)()) Hook_Init)(); + return true; +} + +extern "C" __declspec(dllexport) int LauncherMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + { + if (!GetExePathWide(exePath, 4096)) + { + MessageBoxA(GetForegroundWindow(), "Failed getting game directory.\nThe game cannot continue and has to exit.", "Northstar Launcher Proxy Error", 0); + return 1; + } + + bool loadNorthstar = ShouldLoadNorthstar(); + + if (loadNorthstar) + { + swprintf_s(dllPath, L"%s\\bin\\x64_retail\\tier0.dll", exePath); + hTier0Module = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hTier0Module) + { + LibraryLoadError(GetLastError(), L"tier0.dll", dllPath); + return 1; + } + + if (!LoadNorthstar()) + return 1; + } + //else printf("\n\n WILL !!!NOT!!! LOAD NORTHSTAR\n\n"); + + swprintf_s(dllPath, L"%s\\bin\\x64_retail\\launcher.org.dll", exePath); + hLauncherModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hLauncherModule) + { + LibraryLoadError(GetLastError(), L"launcher.org.dll", dllPath); + return 1; + } + } + + auto LauncherMain = GetLauncherMain(); + if (!LauncherMain) + MessageBoxA(GetForegroundWindow(), "Failed loading launcher.org.dll.\nThe game cannot continue and has to exit.", "Northstar Launcher Proxy Error", 0); + //auto result = ((__int64(__fastcall*)())LauncherMain)(); + //auto result = ((signed __int64(__fastcall*)(__int64))LauncherMain)(0i64); + return ((int(__fastcall*)(HINSTANCE, HINSTANCE, LPSTR, int))LauncherMain)(hInstance, hPrevInstance, lpCmdLine, nCmdShow); +} + +// doubt that will help us here (in launcher.dll) though +extern "C" { + __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +} diff --git a/loader_launcher_proxy/framework.h b/loader_launcher_proxy/framework.h new file mode 100644 index 00000000..d1b49600 --- /dev/null +++ b/loader_launcher_proxy/framework.h @@ -0,0 +1,7 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define WIN32_EXTRA_LEAN +#define VC_EXTRALEAN +// Windows Header Files +#include <Windows.h> diff --git a/loader_launcher_proxy/loader_launcher_proxy.vcxproj b/loader_launcher_proxy/loader_launcher_proxy.vcxproj new file mode 100644 index 00000000..24cdabc0 --- /dev/null +++ b/loader_launcher_proxy/loader_launcher_proxy.vcxproj @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{f65c322d-66df-4af1-b650-70221de334c0}</ProjectGuid> + <RootNamespace>loaderlauncherproxy</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <TargetName>launcher</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>launcher</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;LOADERLAUNCHERPROXY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableUAC>false</EnableUAC> + <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;LOADERLAUNCHERPROXY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableUAC>false</EnableUAC> + <AdditionalOptions>/HIGHENTROPYVA:NO %(AdditionalOptions)</AdditionalOptions> + <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="framework.h" /> + <ClInclude Include="pch.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + </ClCompile> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters b/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters new file mode 100644 index 00000000..1e57c7b1 --- /dev/null +++ b/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="framework.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/loader_launcher_proxy/pch.cpp b/loader_launcher_proxy/pch.cpp new file mode 100644 index 00000000..64b7eef6 --- /dev/null +++ b/loader_launcher_proxy/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/loader_launcher_proxy/pch.h b/loader_launcher_proxy/pch.h new file mode 100644 index 00000000..885d5d62 --- /dev/null +++ b/loader_launcher_proxy/pch.h @@ -0,0 +1,13 @@ +// 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 + +// add headers that you want to pre-compile here +#include "framework.h" + +#endif //PCH_H diff --git a/loader_wsock32_proxy/dllmain.cpp b/loader_wsock32_proxy/dllmain.cpp new file mode 100644 index 00000000..8e3dcd72 --- /dev/null +++ b/loader_wsock32_proxy/dllmain.cpp @@ -0,0 +1,133 @@ +#include "pch.h" +#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 dllPath[8192]; +wchar_t dllPath2[4096]; + +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 1; + } + + if (!ProvisionNorthstar()) // does not call InitialiseNorthstar yet, will do it on LauncherMain hook + return 1; + + swprintf_s(dllPath, L"%s\\bin\\x64_retail\\wsock32.org.dll", exePath); + GetSystemDirectoryW(dllPath2, 4096); + swprintf_s(dllPath2, L"%s\\wsock32.dll", dllPath2); + try + { + std::filesystem::copy_file(dllPath2, dllPath); + } + catch (const std::exception& e) + { + if (!std::filesystem::exists(dllPath)) + { + swprintf_s(dllPath, L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S", dllPath, e.what()); + MessageBoxW(GetForegroundWindow(), dllPath, L"Northstar Wsock32 Proxy Error", 0); + } + } + hL = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hL) return false; + + 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 1; + } + + return 1; +} + +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(); + } +}
\ No newline at end of file diff --git a/loader_wsock32_proxy/hookutils.cpp b/loader_wsock32_proxy/hookutils.cpp new file mode 100644 index 00000000..8603cb35 --- /dev/null +++ b/loader_wsock32_proxy/hookutils.cpp @@ -0,0 +1,71 @@ +#include "pch.h" +#include "../NorthstarDedicatedTest/hookutils.h" + +#define ERROR(...) { char err[2048]; sprintf_s(err, __VA_ARGS__); MessageBoxA(GetForegroundWindow(), err, "Northstar Wsock32 Proxy Error", 0); } + +TempReadWrite::TempReadWrite(void* ptr) +{ + m_ptr = ptr; + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(m_ptr, &mbi, sizeof(mbi)); + VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect); + m_origProtection = mbi.Protect; +} + +TempReadWrite::~TempReadWrite() +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(m_ptr, &mbi, sizeof(mbi)); + VirtualProtect(mbi.BaseAddress, mbi.RegionSize, m_origProtection, &mbi.Protect); +} + + +void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName) +{ + // the macro for this uses ppTarget's name as targetName, and this typically starts with & + // targetname is used for debug stuff and debug output is nicer if we don't have this + if (*targetName == '&') + targetName++; + + if (MH_CreateHook(ppTarget, ppDetour, ppOriginal) == MH_OK) + { + HookTarget* target = new HookTarget; + target->targetAddress = ppTarget; + target->targetName = (char*)targetName; + + m_hookTargets.push_back(target); + } + else + { + if (targetName != nullptr) + { + ERROR("MH_CreateHook failed for function %s", targetName); + } + else + { + ERROR("MH_CreateHook failed for unknown function"); + } + } +} + +HookEnabler::~HookEnabler() +{ + for (auto& hook : m_hookTargets) + { + if (MH_EnableHook(hook->targetAddress) != MH_OK) + { + if (hook->targetName != nullptr) + { + ERROR("MH_EnableHook failed for function %s", hook->targetName); + } + else + { + ERROR("MH_EnableHook failed for unknown function"); + } + } + else + { + //ERROR("Enabling hook %s", hook->targetName); + } + } +}
\ No newline at end of file diff --git a/loader_wsock32_proxy/loader.cpp b/loader_wsock32_proxy/loader.cpp new file mode 100644 index 00000000..19a448b2 --- /dev/null +++ b/loader_wsock32_proxy/loader.cpp @@ -0,0 +1,61 @@ +#include "pch.h" +#include "loader.h" +#include "../NorthstarDedicatedTest/hookutils.h" +#include <string> +#include <system_error> + +void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) +{ + char text[2048]; + 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()); + MessageBoxA(GetForegroundWindow(), text, "Northstar Wsock32 Proxy Error", 0); +} + +bool LoadNorthstar() +{ + FARPROC Hook_Init = nullptr; + { + swprintf_s(dllPath, L"%s\\Northstar.dll", exePath); + auto hHookModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (!hHookModule || Hook_Init == nullptr) + { + LibraryLoadError(GetLastError(), L"Northstar.dll", dllPath); + 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) +{ + LoadNorthstar(); + return LauncherMainOriginal(hInstance, hPrevInstance, lpCmdLine, nCmdShow); +} + +bool ProvisionNorthstar() +{ + 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; + } + + HookEnabler hook; + ENABLER_CREATEHOOK(hook, GetProcAddress(launcherHandle, "LauncherMain"), &LauncherMainHook, reinterpret_cast<LPVOID*>(&LauncherMainOriginal)); + + return true; +}
\ No newline at end of file diff --git a/loader_wsock32_proxy/loader.h b/loader_wsock32_proxy/loader.h new file mode 100644 index 00000000..02ccb97d --- /dev/null +++ b/loader_wsock32_proxy/loader.h @@ -0,0 +1,7 @@ +#pragma once + +extern wchar_t exePath[4096]; +extern wchar_t dllPath[8192]; +extern wchar_t dllPath2[4096]; + +bool ProvisionNorthstar(); diff --git a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj new file mode 100644 index 00000000..993a5250 --- /dev/null +++ b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{cf55f3b5-f348-450a-9ccb-c269f21d629d}</ProjectGuid> + <RootNamespace>loaderwsock32proxy</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" /> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>wsock32</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;LOADERADVAPI32PROXY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableUAC>false</EnableUAC> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;LOADERADVAPI32PROXY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <AdditionalIncludeDirectories>..\NorthstarDedicatedTest\</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableUAC>false</EnableUAC> + <ModuleDefinitionFile>wsock32.def</ModuleDefinitionFile> + <AdditionalDependencies>..\NorthstarDedicatedTest\include\MinHook.x64.lib;mswsock.lib;ws2_32.lib;Shlwapi.lib;imagehlp.lib;dbghelp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;wsock32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="loader.h" /> + <ClInclude Include="pch.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="hookutils.cpp" /> + <ClCompile Include="loader.cpp" /> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + </ClCompile> + </ItemGroup> + <ItemGroup> + <MASM Include="wsock32.asm" /> + </ItemGroup> + <ItemGroup> + <None Include="wsock32.def" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" /> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters new file mode 100644 index 00000000..6d131e5b --- /dev/null +++ b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="loader.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="loader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="hookutils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <MASM Include="wsock32.asm"> + <Filter>Source Files</Filter> + </MASM> + </ItemGroup> + <ItemGroup> + <None Include="wsock32.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/loader_wsock32_proxy/pch.cpp b/loader_wsock32_proxy/pch.cpp new file mode 100644 index 00000000..64b7eef6 --- /dev/null +++ b/loader_wsock32_proxy/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/loader_wsock32_proxy/pch.h b/loader_wsock32_proxy/pch.h new file mode 100644 index 00000000..0103ff59 --- /dev/null +++ b/loader_wsock32_proxy/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 "include/MinHook.h" + +#endif //PCH_H diff --git a/loader_wsock32_proxy/wsock32.asm b/loader_wsock32_proxy/wsock32.asm new file mode 100644 index 00000000..22a9c384 --- /dev/null +++ b/loader_wsock32_proxy/wsock32.asm @@ -0,0 +1,7 @@ +.data +extern PA : qword +.code +RunASM proc +jmp qword ptr [PA] +RunASM endp +end diff --git a/loader_wsock32_proxy/wsock32.def b/loader_wsock32_proxy/wsock32.def new file mode 100644 index 00000000..448440b4 --- /dev/null +++ b/loader_wsock32_proxy/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 |