From 0c2c47899096a1ce7921552759eaa07038d9ef01 Mon Sep 17 00:00:00 2001 From: ineedbots Date: Mon, 28 Jun 2021 12:13:34 -0600 Subject: [PATCH] Init iw4x base --- .gitattributes | 4 + .gitignore | 153 ++++++++++++++++++ generate.bat | 4 + premake5.lua | 317 ++++++++++++++++++++++++++++++++++++++ src/Game/MP.cpp | 9 ++ src/Game/MP.hpp | 6 + src/Game/MP/Functions.hpp | 0 src/Game/MP/Structs.hpp | 0 src/Game/SP.cpp | 9 ++ src/Game/SP.hpp | 6 + src/Game/SP/Functions.hpp | 0 src/Game/SP/Structs.hpp | 0 src/Main.cpp | 89 +++++++++++ src/Resource.rc | 97 ++++++++++++ src/SDLLP.cpp | 90 +++++++++++ src/STDInclude.cpp | 115 ++++++++++++++ src/STDInclude.hpp | 120 +++++++++++++++ src/Utils/Hooking.cpp | 256 ++++++++++++++++++++++++++++++ src/Utils/Hooking.hpp | 185 ++++++++++++++++++++++ tools/premake5.exe | Bin 0 -> 1362432 bytes tools/protoc.exe | Bin 0 -> 2639360 bytes 21 files changed, 1460 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 generate.bat create mode 100644 premake5.lua create mode 100644 src/Game/MP.cpp create mode 100644 src/Game/MP.hpp create mode 100644 src/Game/MP/Functions.hpp create mode 100644 src/Game/MP/Structs.hpp create mode 100644 src/Game/SP.cpp create mode 100644 src/Game/SP.hpp create mode 100644 src/Game/SP/Functions.hpp create mode 100644 src/Game/SP/Structs.hpp create mode 100644 src/Main.cpp create mode 100644 src/Resource.rc create mode 100644 src/SDLLP.cpp create mode 100644 src/STDInclude.cpp create mode 100644 src/STDInclude.hpp create mode 100644 src/Utils/Hooking.cpp create mode 100644 src/Utils/Hooking.hpp create mode 100644 tools/premake5.exe create mode 100644 tools/protoc.exe diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3843510 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.hpp eol=crlf +*.cpp eol=crlf +*.lua eol=crlf +*.proto eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5278875 --- /dev/null +++ b/.gitignore @@ -0,0 +1,153 @@ +### Windows + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Shortcuts +*.lnk + +### OSX + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Visual Studio + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +build + +# Visual Studio 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +### IDA +*.id0 +*.id1 +*.id2 +*.nam +*.til +*.idb +*.i64 +ida/* + +### Custom user files +# User scripts +user*.bat + +# Premake binary +#premake5.exe diff --git a/generate.bat b/generate.bat new file mode 100644 index 0000000..65a3b20 --- /dev/null +++ b/generate.bat @@ -0,0 +1,4 @@ +@echo off +echo Updating submodules... +call git submodule update --init --recursive +call tools\premake5 %* vs2019 diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 0000000..24de5ac --- /dev/null +++ b/premake5.lua @@ -0,0 +1,317 @@ +gitVersioningCommand = "git describe --tags --dirty --always" +gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD" + +-- Quote the given string input as a C string +function cstrquote(value) + result = value:gsub("\\", "\\\\") + result = result:gsub("\"", "\\\"") + result = result:gsub("\n", "\\n") + result = result:gsub("\t", "\\t") + result = result:gsub("\r", "\\r") + result = result:gsub("\a", "\\a") + result = result:gsub("\b", "\\b") + result = "\"" .. result .. "\"" + return result +end + +-- Converts tags in "vX.X.X" format to an array of numbers {X,X,X}. +-- In the case where the format does not work fall back to old {4,2,REVISION}. +function vertonumarr(value, vernumber) + vernum = {} + for num in string.gmatch(value, "%d+") do + table.insert(vernum, tonumber(num)) + end + if #vernum < 3 then + return {4,2,tonumber(vernumber)} + end + return vernum +end + +-- Option to allow copying the DLL file to a custom folder after build +newoption { + trigger = "copy-to", + description = "Optional, copy the DLL to a custom folder after build, define the path here if wanted.", + value = "PATH" +} + +newoption { + trigger = "no-new-structure", + description = "Do not use new virtual path structure (separating headers and source files)." +} + +newoption { + trigger = "copy-pdb", + description = "Copy debug information for binaries as well to the path given via --copy-to." +} + +newaction { + trigger = "version", + description = "Returns the version string for the current commit of the source code.", + onWorkspace = function(wks) + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + local version = gitDescribeOutput + + proc = assert(io.popen(gitCurrentBranchCommand, "r")) + local gitCurrentBranchOutput = assert(proc:read('*a')):gsub("%s+", "") + local gitCurrentBranchSuccess = proc:close() + if gitCurrentBranchSuccess then + -- We got a branch name, check if it is a feature branch + if gitCurrentBranchOutput ~= "develop" and gitCurrentBranchOutput ~= "master" then + version = version .. "-" .. gitCurrentBranchOutput + end + end + + print(version) + os.exit(0) + end +} + +newaction { + trigger = "generate-buildinfo", + description = "Sets up build information file like version.h.", + onWorkspace = function(wks) + -- get revision number via git + local proc = assert(io.popen("git rev-list --count HEAD", "r")) + local revNumber = assert(proc:read('*a')):gsub("%s+", "") + + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + + -- get whether this is a clean revision (no uncommitted changes) + proc = assert(io.popen("git status --porcelain", "r")) + local revDirty = (assert(proc:read('*a')) ~= "") + if revDirty then revDirty = 1 else revDirty = 0 end + proc:close() + + -- get current tag name + proc = assert(io.popen("git describe --tags --abbrev=0")) + local tagName = assert(proc:read('*l')) + + -- get old version number from version.hpp if any + local oldVersion = "(none)" + local oldVersionHeader = io.open(wks.location .. "/src/version.h", "r") + if oldVersionHeader ~= nil then + local oldVersionHeaderContent = assert(oldVersionHeader:read('*l')) + while oldVersionHeaderContent do + m = string.match(oldVersionHeaderContent, "#define GIT_DESCRIBE (.+)%s*$") + if m ~= nil then + oldVersion = m + end + + oldVersionHeaderContent = oldVersionHeader:read('*l') + end + end + + -- generate version.hpp with a revision number if not equal + gitDescribeOutputQuoted = cstrquote(gitDescribeOutput) + if oldVersion ~= gitDescribeOutputQuoted then + print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted) + local versionHeader = assert(io.open(wks.location .. "/src/version.h", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch, you fucking moron!\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n") + versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n") + versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n") + versionHeader:write("\n") + versionHeader:write("// Legacy definitions (needed for update check)\n") + versionHeader:write("#define REVISION " .. revNumber .. "\n") + versionHeader:write("\n") + versionHeader:write("// Version transformed for RC files\n") + versionHeader:write("#define VERSION_RC " .. table.concat(vertonumarr(tagName, revNumber), ",") .. "\n") + versionHeader:write("\n") + versionHeader:write("// Alias definitions\n") + versionHeader:write("#define VERSION GIT_DESCRIBE\n") + versionHeader:write("#define SHORTVERSION " .. cstrquote(table.concat(vertonumarr(tagName, revNumber), ".")) .. "\n") + versionHeader:close() + local versionHeader = assert(io.open(wks.location .. "/src/version.hpp", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch, you fucking moron!\n") + versionHeader:write(" *\n") + versionHeader:write(" * This file exists for reasons of complying with our coding standards.\n") + versionHeader:write(" *\n") + versionHeader:write(" * The Resource Compiler will ignore any content from C++ header files if they're not from STDInclude.hpp.\n") + versionHeader:write(" * That's the reason why we now place all version info in version.h instead.\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#include \".\\version.h\"\n") + versionHeader:close() + end + end +} + +workspace "cod2m" + startproject "cod2m" + location "./build" + objdir "%{wks.location}/obj" + targetdir "%{wks.location}/bin/%{cfg.buildcfg}" + buildlog "%{wks.location}/obj/%{cfg.architecture}/%{cfg.buildcfg}/%{prj.name}/%{prj.name}.log" + configurations { "Debug", "Release" } + architecture "x86" + platforms "x86" + --exceptionhandling ("SEH") + + staticruntime "On" + + configuration "windows" + defines { "_WINDOWS", "WIN32" } + + configuration "Release*" + defines { "NDEBUG" } + flags { "MultiProcessorCompile", "LinkTimeOptimization", "No64BitChecks" } + optimize "On" + rtti ("Off") + + configuration "Debug*" + defines { "DEBUG", "_DEBUG" } + flags { "MultiProcessorCompile", "No64BitChecks" } + optimize "Debug" + if symbols ~= nil then + symbols "On" + else + flags { "Symbols" } + end + + project "d3d9" + kind "SharedLib" + language "C++" + files { + "./src/**.rc", + "./src/**.hpp", + "./src/**.cpp", + --"./src/**.proto", + } + includedirs { + "%{prj.location}/src", + "./src", + "./lib/include", + } + syslibdirs { + "./lib/bin", + } + resincludedirs { + "$(ProjectDir)src" -- fix for VS IDE + } + + + -- Pre-compiled header + pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives + pchsource "src/STDInclude.cpp" -- real path + buildoptions { "/Zm200" } + + -- fix vpaths for protobuf sources + vpaths + { + ["*"] = { "./src/**" }, + --["Proto/Generated"] = { "**.pb.*" }, -- meh. + } + + -- Virtual paths + if not _OPTIONS["no-new-structure"] then + vpaths + { + ["Headers/*"] = { "./src/**.hpp" }, + ["Sources/*"] = { "./src/**.cpp" }, + ["Resource/*"] = { "./src/**.rc" }, + --["Proto/Definitions/*"] = { "./src/Proto/**.proto" }, + --["Proto/Generated/*"] = { "**.pb.*" }, -- meh. + } + end + + vpaths + { + ["Docs/*"] = { "**.txt","**.md" }, + } + + -- Pre-build + prebuildcommands + { + "pushd %{_MAIN_SCRIPT_DIR}", + "tools\\premake5 generate-buildinfo", + "popd", + } + + -- Post-build + if _OPTIONS["copy-to"] then + saneCopyToPath = string.gsub(_OPTIONS["copy-to"] .. "\\", "\\\\", "\\") + postbuildcommands { + "if not exist \"" .. saneCopyToPath .. "\" mkdir \"" .. saneCopyToPath .. "\"", + } + + if _OPTIONS["copy-pdb"] then + postbuildcommands { + "copy /y \"$(TargetDir)*.pdb\" \"" .. saneCopyToPath .. "\"", + } + end + + -- This has to be the last one, as otherwise VisualStudio will succeed building even if copying fails + postbuildcommands { + "copy /y \"$(TargetDir)*.dll\" \"" .. saneCopyToPath .. "\"", + } + end + + -- Specific configurations + flags { "UndefinedIdentifiers" } + warnings "Extra" + + if symbols ~= nil then + symbols "On" + else + flags { "Symbols" } + end + + configuration "Release*" + flags { + "FatalCompileWarnings", + "FatalLinkWarnings", + } + configuration {} + + --[[ + -- Generate source code from protobuf definitions + rules { "ProtobufCompiler" } + + -- Workaround: Consume protobuf generated source files + matches = os.matchfiles(path.join("src/Proto/**.proto")) + for i, srcPath in ipairs(matches) do + basename = path.getbasename(srcPath) + files + { + string.format("%%{prj.location}/src/proto/%s.pb.h", basename), + string.format("%%{prj.location}/src/proto/%s.pb.cc", basename), + } + end + includedirs + { + "%{prj.location}/src/proto", + } + filter "files:**.pb.*" + flags { + "NoPCH", + } + buildoptions { + "/wd4100", -- "Unused formal parameter" + "/wd4389", -- "Signed/Unsigned mismatch" + "/wd6011", -- "Dereferencing NULL pointer" + "/wd4125", -- "Decimal digit terminates octal escape sequence" + } + defines { + "_SCL_SECURE_NO_WARNINGS", + } + filter {} + ]] + +workspace "*" + buildoptions { + "/std:c++latest" + } + systemversion "latest" + defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" } diff --git a/src/Game/MP.cpp b/src/Game/MP.cpp new file mode 100644 index 0000000..e4ee6ef --- /dev/null +++ b/src/Game/MP.cpp @@ -0,0 +1,9 @@ +#include "STDInclude.hpp" + +namespace MP +{ + void PatchT4() + { + MessageBoxA(nullptr, "MP", "DEBUG", 0); + } +} diff --git a/src/Game/MP.hpp b/src/Game/MP.hpp new file mode 100644 index 0000000..2f3ddc8 --- /dev/null +++ b/src/Game/MP.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace MP +{ + void PatchT4(); +} diff --git a/src/Game/MP/Functions.hpp b/src/Game/MP/Functions.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Game/MP/Structs.hpp b/src/Game/MP/Structs.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Game/SP.cpp b/src/Game/SP.cpp new file mode 100644 index 0000000..fe5bfc1 --- /dev/null +++ b/src/Game/SP.cpp @@ -0,0 +1,9 @@ +#include "STDInclude.hpp" + +namespace SP +{ + void PatchT4() + { + MessageBoxA(nullptr, "SP", "DEBUG", 0); + } +} diff --git a/src/Game/SP.hpp b/src/Game/SP.hpp new file mode 100644 index 0000000..2befd43 --- /dev/null +++ b/src/Game/SP.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace SP +{ + void PatchT4(); +} diff --git a/src/Game/SP/Functions.hpp b/src/Game/SP/Functions.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Game/SP/Structs.hpp b/src/Game/SP/Structs.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..2d6e8e5 --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,89 @@ +#include "STDInclude.hpp" + +namespace Main +{ + static BYTE originalCode[5]; + static PBYTE originalEP = 0; + + void UnprotectModule(HMODULE hModule) + { + PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew); + + // unprotect the entire PE image + SIZE_T size = ntHeader->OptionalHeader.SizeOfImage; + DWORD oldProtect; + VirtualProtect((LPVOID)hModule, size, PAGE_EXECUTE_READWRITE, &oldProtect); + } + + void DoInit() + { + // return to the original EP + memcpy(originalEP, &originalCode, sizeof(originalCode)); + + // unprotect our entire PE image + HMODULE hModule; + if (SUCCEEDED(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)DoInit, &hModule))) + { + UnprotectModule(hModule); + } + + // detect which executable's patches to apply + DWORD dataStrData = Utils::Hook::Get(0x881CAC); + if (dataStrData == 0x62616E55) + MP::PatchT4(); + else + SP::PatchT4(); + + hModule = GetModuleHandle(NULL); + PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew); + + // back up original code + originalEP = (PBYTE)((DWORD)hModule + ntHeader->OptionalHeader.AddressOfEntryPoint); + + __asm + { + jmp originalEP + } + } + + void SetSafeInit() + { + // find the entry point for the executable process, set page access, and replace the EP + HMODULE hModule = GetModuleHandle(NULL); // passing NULL should be safe even with the loader lock being held (according to ReactOS ldr.c) + + if (hModule) + { + PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew); + + UnprotectModule(hModule); + + // back up original code + PBYTE ep = (PBYTE)((DWORD)hModule + ntHeader->OptionalHeader.AddressOfEntryPoint); + memcpy(originalCode, ep, sizeof(originalCode)); + + // patch to call our EP + int newEP = (int)DoInit - ((int)ep + 5); + ep[0] = 0xE9; // for some reason this doesn't work properly when run under the debugger + memcpy(&ep[1], &newEP, 4); + + originalEP = ep; + } + } +} + + +bool APIENTRY DllMain(HMODULE, DWORD dwReason, LPVOID) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + DWORD textSegData = Utils::Hook::Get(0x401000); + + // detect for all executables to hook into + //if (textSegData == 0x9EF490B8 || textSegData == 0x83EC8B55) + Main::SetSafeInit(); + } + return true; +} diff --git a/src/Resource.rc b/src/Resource.rc new file mode 100644 index 0000000..e2c3bdb --- /dev/null +++ b/src/Resource.rc @@ -0,0 +1,97 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "STDInclude.hpp" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\n" + "\0" +END + +2 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "CoD2m" +#ifdef _DEBUG + VALUE "FileDescription", "CoD2 client/server modification (DEBUG)" +#else + VALUE "FileDescription", "CoD2 client/server modification" +#endif + VALUE "FileVersion", SHORTVERSION + VALUE "InternalName", "cod2m" + VALUE "LegalCopyright", "All rights reserved." + VALUE "OriginalFilename", "d3d9.dll" + VALUE "ProductName", "CoD2m" + VALUE "ProductVersion", SHORTVERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/SDLLP.cpp b/src/SDLLP.cpp new file mode 100644 index 0000000..d3c134b --- /dev/null +++ b/src/SDLLP.cpp @@ -0,0 +1,90 @@ +// --------------------------------------+ +// System Dynamic Link Library Proxy +// by momo5502 +// --------------------------------------+ + +#include "STDInclude.hpp" + +// Macro to declare an export +// --------------------------------------+ +#define EXPORT(_export) extern "C" __declspec(naked) __declspec(dllexport) void _export() { static FARPROC function = 0; if(!function) function = SDLLP::GetExport(__FUNCTION__, LIBRARY); __asm { jmp function } } + +// Static class +// --------------------------------------+ +class SDLLP +{ +private: + static std::map mLibraries; + + static void Log(const char* message, ...); + static void LoadLibrary(const char* library); + static bool IsLibraryLoaded(const char* library); + +public: + static FARPROC GetExport(const char* function, const char* library); +}; + +// Class variable declarations +// --------------------------------------+ +std::map SDLLP::mLibraries; + +// Load necessary library +// --------------------------------------+ +void SDLLP::LoadLibrary(const char* library) +{ + Log("[SDLLP] Loading library '%s'.", library); + + CHAR mPath[MAX_PATH]; + + GetSystemDirectoryA(mPath, MAX_PATH); + strcat_s(mPath, "\\"); + strcat_s(mPath, library); + + mLibraries[library] = ::LoadLibraryA(mPath); + + if (!IsLibraryLoaded(library)) Log("[SDLLP] Unable to load library '%s'.", library); +} + +// Check if export already loaded +// --------------------------------------+ +bool SDLLP::IsLibraryLoaded(const char* library) +{ + return (mLibraries.find(library) != mLibraries.end() && mLibraries[library]); +} + +// Get export address +// --------------------------------------+ +FARPROC SDLLP::GetExport(const char* function, const char* library) +{ + Log("[SDLLP] Export '%s' requested from %s.", function, library); + + if (!IsLibraryLoaded(library)) LoadLibrary(library); + + FARPROC address = GetProcAddress(mLibraries[library], function); + + if (!address) Log("[SDLLP] Unable to load export '%s' from library '%s'.", function, library); + return address; +} + +// Write debug string +// --------------------------------------+ +void SDLLP::Log(const char* message, ...) +{ + CHAR buffer[1024]; + va_list ap; + + va_start(ap, message); + vsprintf(buffer, message, ap); + va_end(ap); + + OutputDebugStringA(buffer); +} + +// --------------------------------------+ +// Adapt export functions and library +// --------------------------------------+ + +#define LIBRARY "d3d9.dll" +EXPORT(D3DPERF_BeginEvent) +EXPORT(D3DPERF_EndEvent) +EXPORT(Direct3DCreate9) diff --git a/src/STDInclude.cpp b/src/STDInclude.cpp new file mode 100644 index 0000000..534f016 --- /dev/null +++ b/src/STDInclude.cpp @@ -0,0 +1,115 @@ +#include "STDInclude.hpp" + +// Rename sections +#ifndef DEBUG +#pragma comment(linker, "/merge:.text=.UPX0") +#pragma comment(linker, "/merge:.data=.UPX1") +#pragma comment(linker, "/merge:.rdata=.UPX2") +#pragma comment(linker, "/merge:.tls=.UPX3") +#pragma comment(linker, "/merge:.gfids=.UPX4") +#endif + +#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +// Do necessary assertions here +// Some compilers treat them differently which causes a size mismatch + +// WinAPI types +AssertSize(DWORD, 4); +AssertSize(WORD, 2); +AssertSize(BYTE, 1); + +// 128 bit integers (only x64) +//AssertSize(__int128, 16); +//AssertSize(unsigned __int128, 16); + +// 64 bit integers +AssertSize(__int64, 8); +AssertSize(unsigned __int64, 8); +AssertSize(long long, 8); +AssertSize(unsigned long long, 8); +AssertSize(int64_t, 8); +AssertSize(uint64_t, 8); +AssertSize(std::int64_t, 8); +AssertSize(std::uint64_t, 8); + +// 64 bit double precision floating point numbers +AssertSize(double, 8); + +// 32 bit integers +AssertSize(__int32, 4); +AssertSize(unsigned __int32, 4); +AssertSize(int, 4); +AssertSize(unsigned int, 4); +AssertSize(long, 4); +AssertSize(unsigned long, 4); +AssertSize(int32_t, 4); +AssertSize(uint32_t, 4); +AssertSize(std::int32_t, 4); +AssertSize(std::uint32_t, 4); + +// 32 bit single precision floating point numbers +AssertSize(float, 4); + +// 16 bit integers +AssertSize(__int16, 2); +AssertSize(unsigned __int16, 2); +AssertSize(short, 2); +AssertSize(unsigned short, 2); +AssertSize(int16_t, 2); +AssertSize(uint16_t, 2); +AssertSize(std::int16_t, 2); +AssertSize(std::uint16_t, 2); + +// 8 bit integers +AssertSize(bool, 1); +AssertSize(__int8, 1); +AssertSize(unsigned __int8, 1); +AssertSize(char, 1); +AssertSize(unsigned char, 1); +AssertSize(int8_t, 1); +AssertSize(uint8_t, 1); +AssertSize(std::int8_t, 1); +AssertSize(std::uint8_t, 1); + +// Ensure pointers are 4 bytes in size (32-bit) +// ReSharper disable CppRedundantBooleanExpressionArgument +static_assert(sizeof(intptr_t) == 4 && sizeof(void*) == 4 && sizeof(size_t) == 4, "This doesn't seem to be a 32-bit environment!"); +// ReSharper restore CppRedundantBooleanExpressionArgument + +#if !defined(_M_IX86) +#error "Invalid processor achritecture!" +#endif + +extern "C" +{ + // Disable telemetry data logging + void __cdecl __vcrt_initialize_telemetry_provider() {} + void __cdecl __telemetry_main_invoke_trigger() {} + void __cdecl __telemetry_main_return_trigger() {} + void __cdecl __vcrt_uninitialize_telemetry_provider() {} + + // Enable 'High Performance Graphics' + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + + // Tommath fixes + int s_read_arc4random(void*, size_t) + { + return -1; + } + + int s_read_getrandom(void*, size_t) + { + return -1; + } + + int s_read_urandom(void*, size_t) + { + return -1; + } + + int s_read_ltm_rng(void*, size_t) + { + return -1; + } +}; diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp new file mode 100644 index 0000000..c1e271b --- /dev/null +++ b/src/STDInclude.hpp @@ -0,0 +1,120 @@ +#pragma once + +// Version number +#include "version.h" + +#ifndef RC_INVOKED + +#define _HAS_CXX17 1 +#define _HAS_CXX20 1 +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +// Requires Visual Leak Detector plugin: http://vld.codeplex.com/ +#define VLD_FORCE_ENABLE +//#include +#pragma warning(disable: 4740) + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable: 4091) +#pragma warning(disable: 4244) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Experimental C++17 features +#include +#include + +#pragma warning(pop) + +// Usefull for debugging +template class Sizer { }; +#define BindNum(x, y) Sizer y; +#define Size_Of(x, y) BindNum(sizeof(x), y) +#define Offset_Of(x, y, z) BindNum(offsetof(x, y), z) + +// Submodules +// Ignore the warnings, it's not our code! +#pragma warning(push) +#pragma warning(disable: 4005) +#pragma warning(disable: 4091) +#pragma warning(disable: 4100) +#pragma warning(disable: 4244) +#pragma warning(disable: 4389) +#pragma warning(disable: 4702) +#pragma warning(disable: 4800) +#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS +#pragma warning(disable: 5054) +#pragma warning(disable: 6001) +#pragma warning(disable: 6011) +#pragma warning(disable: 6031) +#pragma warning(disable: 6255) +#pragma warning(disable: 6258) +#pragma warning(disable: 6386) +#pragma warning(disable: 6387) + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#pragma warning(pop) + +#include "Utils/Hooking.hpp" + +#include "Game/MP.hpp" +#include "Game/SP.hpp" + +// Libraries +#pragma comment(lib, "Winmm.lib") +#pragma comment(lib, "Crypt32.lib") +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "Wininet.lib") +#pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "Urlmon.lib") +#pragma comment(lib, "Advapi32.lib") +#pragma comment(lib, "rpcrt4.lib") +#pragma comment(lib, "dbghelp.lib") + +// Enable additional literals +using namespace std::literals; + +#endif + +#define STRINGIZE_(x) #x +#define STRINGIZE(x) STRINGIZE_(x) + +#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.") +#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.") + +// Resource stuff +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +// Defines below make accessing the resources from the code easier. +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/Utils/Hooking.cpp b/src/Utils/Hooking.cpp new file mode 100644 index 0000000..bf529da --- /dev/null +++ b/src/Utils/Hooking.cpp @@ -0,0 +1,256 @@ +#include "STDInclude.hpp" + +namespace Utils +{ + std::map Hook::Interceptor::IReturn; + std::map Hook::Interceptor::ICallbacks; + + void Hook::Signature::process() + { + if (this->signatures.empty()) return; + + char* _start = reinterpret_cast(this->start); + + unsigned int sigCount = this->signatures.size(); + Hook::Signature::Container* containers = this->signatures.data(); + + for (size_t i = 0; i < this->length; ++i) + { + char* address = _start + i; + + for (unsigned int k = 0; k < sigCount; ++k) + { + Hook::Signature::Container* container = &containers[k]; + + unsigned int j; + for (j = 0; j < strlen(container->mask); ++j) + { + if (container->mask[j] != '?' &&container->signature[j] != address[j]) + { + break; + } + } + + if (j == strlen(container->mask)) + { + container->callback(address); + } + } + } + } + + void Hook::Signature::add(Hook::Signature::Container& container) + { + Hook::Signature::signatures.push_back(container); + } + + void Hook::Interceptor::Install(void* place, void* stub) + { + return Hook::Interceptor::Install(place, static_cast(stub)); + } + + void Hook::Interceptor::Install(void* place, void(*stub)()) + { + return Hook::Interceptor::Install(reinterpret_cast(place), stub); + } + + void Hook::Interceptor::Install(void** place, void(*stub)()) + { + Hook::Interceptor::IReturn[place] = *place; + Hook::Interceptor::ICallbacks[place] = stub; + *place = Hook::Interceptor::InterceptionStub; + } + + __declspec(naked) void Hook::Interceptor::InterceptionStub() + { + __asm + { + sub esp, 4h // Reserve space on the stack for the return address + pushad // Store registers + + lea eax, [esp + 20h] // Load initial stack pointer + push eax // Push it onto the stack + + call Hook::Interceptor::RunCallback // Run the callback based on the given stack pointer + call Hook::Interceptor::PopReturn // Get the initial return address according to the stack pointer + + add esp, 4h // Clear the stack + + mov [esp + 20h], eax // Store the return address at the reserved space + popad // Restore the registers + + retn // Return (jump to our return address) + } + } + + void Hook::Interceptor::RunCallback(void* place) + { + auto iCallback = Hook::Interceptor::ICallbacks.find(place); + if (iCallback != Hook::Interceptor::ICallbacks.end()) + { + iCallback->second(); + Hook::Interceptor::ICallbacks.erase(iCallback); + } + } + + void* Hook::Interceptor::PopReturn(void* _place) + { + void* retVal = nullptr; + + auto iReturn = Hook::Interceptor::IReturn.find(_place); + if (iReturn != Hook::Interceptor::IReturn.end()) + { + retVal = iReturn->second; + Hook::Interceptor::IReturn.erase(iReturn); + } + + return retVal; + } + + Hook::~Hook() + { + if (this->initialized) + { + this->uninstall(); + } + } + + Hook* Hook::initialize(DWORD _place, void(*_stub)(), bool _useJump) + { + return this->initialize(_place, reinterpret_cast(_stub), _useJump); + } + + Hook* Hook::initialize(DWORD _place, void* _stub, bool _useJump) + { + return this->initialize(reinterpret_cast(_place), _stub, _useJump); + } + + Hook* Hook::initialize(void* _place, void* _stub, bool _useJump) + { + if (this->initialized) return this; + this->initialized = true; + + this->useJump = _useJump; + this->place = _place; + this->stub = _stub; + + this->original = static_cast(this->place) + 5 + *reinterpret_cast((static_cast(this->place) + 1)); + + return this; + } + + Hook* Hook::install(bool unprotect, bool keepUnprotected) + { + std::lock_guard _(this->stateMutex); + + if (!this->initialized || this->installed) + { + return this; + } + + this->installed = true; + + if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); + std::memcpy(this->buffer, this->place, sizeof(this->buffer)); + + char* code = static_cast(this->place); + + *code = static_cast(this->useJump ? 0xE9 : 0xE8); + + *reinterpret_cast(code + 1) = reinterpret_cast(this->stub) - (reinterpret_cast(this->place) + 5); + + if (unprotect && !keepUnprotected) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); + + FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); + + return this; + } + + void Hook::quick() + { + if (Hook::installed) + { + Hook::installed = false; + } + } + + Hook* Hook::uninstall(bool unprotect) + { + std::lock_guard _(this->stateMutex); + + if (!this->initialized || !this->installed) + { + return this; + } + + this->installed = false; + + if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); + + std::memcpy(this->place, this->buffer, sizeof(this->buffer)); + + if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); + + FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); + + return this; + } + + void* Hook::getAddress() + { + return this->place; + } + + void Hook::Nop(void* place, size_t length) + { + DWORD oldProtect; + VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &oldProtect); + + memset(place, 0x90, length); + + VirtualProtect(place, length, oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, length); + } + + void Hook::Nop(DWORD place, size_t length) + { + Nop(reinterpret_cast(place), length); + } + + void Hook::SetString(void* place, const char* string, size_t length) + { + DWORD oldProtect; + VirtualProtect(place, length + 1, PAGE_EXECUTE_READWRITE, &oldProtect); + + strncpy_s(static_cast(place), length, string, length); + + VirtualProtect(place, length + 1, oldProtect, &oldProtect); + } + + void Hook::SetString(DWORD place, const char* string, size_t length) + { + Hook::SetString(reinterpret_cast(place), string, length); + } + + void Hook::SetString(void* place, const char* string) + { + Hook::SetString(place, string, strlen(static_cast(place))); + } + + void Hook::SetString(DWORD place, const char* string) + { + Hook::SetString(reinterpret_cast(place), string); + } + + void Hook::RedirectJump(void* place, void* stub) + { + char* operandPtr = static_cast(place) + 2; + int newOperand = reinterpret_cast(stub) - (reinterpret_cast(place) + 6); + Utils::Hook::Set(operandPtr, newOperand); + } + + void Hook::RedirectJump(DWORD place, void* stub) + { + Hook::RedirectJump(reinterpret_cast(place), stub); + } +} diff --git a/src/Utils/Hooking.hpp b/src/Utils/Hooking.hpp new file mode 100644 index 0000000..d40e25f --- /dev/null +++ b/src/Utils/Hooking.hpp @@ -0,0 +1,185 @@ +#pragma once + +#define HOOK_JUMP true +#define HOOK_CALL false + +namespace Utils +{ + class Hook + { + public: + class Signature + { + public: + struct Container + { + const char* signature; + const char* mask; + std::function callback; + }; + + Signature(void* _start, size_t _length) : start(_start), length(_length) {} + Signature(DWORD _start, size_t _length) : Signature(reinterpret_cast(_start), _length) {} + Signature() : Signature(0x400000, 0x800000) {} + + void process(); + void add(Container& container); + + private: + void* start; + size_t length; + std::vector signatures; + }; + + class Interceptor + { + public: + static void Install(void* place, void* stub); + static void Install(void* place, void(*stub)()); + static void Install(void** place, void(*stub)()); + + private: + static std::map IReturn; + static std::map ICallbacks; + + static void InterceptionStub(); + static void RunCallback(void* place); + static void* PopReturn(void* place); + }; + + Hook() : initialized(false), installed(false), place(nullptr), stub(nullptr), original(nullptr), useJump(false), protection(0) { ZeroMemory(this->buffer, sizeof(this->buffer)); } + + Hook(void* place, void* stub, bool useJump = true) : Hook() { this->initialize(place, stub, useJump); } + Hook(void* place, void(*stub)(), bool useJump = true) : Hook(place, reinterpret_cast(stub), useJump) {} + + Hook(DWORD place, void* stub, bool useJump = true) : Hook(reinterpret_cast(place), stub, useJump) {} + Hook(DWORD place, DWORD stub, bool useJump = true) : Hook(reinterpret_cast(place), reinterpret_cast(stub), useJump) {} + Hook(DWORD place, void(*stub)(), bool useJump = true) : Hook(reinterpret_cast(place), reinterpret_cast(stub), useJump) {} + + ~Hook(); + + Hook* initialize(void* place, void* stub, bool useJump = true); + Hook* initialize(DWORD place, void* stub, bool useJump = true); + Hook* initialize(DWORD place, void(*stub)(), bool useJump = true); // For lambdas + Hook* install(bool unprotect = true, bool keepUnprotected = false); + Hook* uninstall(bool unprotect = true); + + void* getAddress(); + void quick(); + + template static std::function Call(DWORD function) + { + return std::function(reinterpret_cast(function)); + } + + template static std::function Call(FARPROC function) + { + return Call(reinterpret_cast(function)); + } + + template static std::function Call(void* function) + { + return Call(reinterpret_cast(function)); + } + + static void SetString(void* place, const char* string, size_t length); + static void SetString(DWORD place, const char* string, size_t length); + + static void SetString(void* place, const char* string); + static void SetString(DWORD place, const char* string); + + static void Nop(void* place, size_t length); + static void Nop(DWORD place, size_t length); + + static void RedirectJump(void* place, void* stub); + static void RedirectJump(DWORD place, void* stub); + + template static void Set(void* place, T value) + { + DWORD oldProtect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); + + *static_cast(place) = value; + + VirtualProtect(place, sizeof(T), oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + } + + template static void Set(DWORD place, T value) + { + return Set(reinterpret_cast(place), value); + } + + template static void Xor(void* place, T value) + { + DWORD oldProtect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); + + *static_cast(place) ^= value; + + VirtualProtect(place, sizeof(T), oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + } + + template static void Xor(DWORD place, T value) + { + return Xor(reinterpret_cast(place), value); + } + + template static void Or(void* place, T value) + { + DWORD oldProtect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); + + *static_cast(place) |= value; + + VirtualProtect(place, sizeof(T), oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + } + + template static void Or(DWORD place, T value) + { + return Or(reinterpret_cast(place), value); + } + + template static void And(void* place, T value) + { + DWORD oldProtect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); + + *static_cast(place) &= value; + + VirtualProtect(place, sizeof(T), oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + } + + template static void And(DWORD place, T value) + { + return And(reinterpret_cast(place), value); + } + + template static T Get(void* place) + { + return *static_cast(place); + } + + template static T Get(DWORD place) + { + return Get(reinterpret_cast(place)); + } + + private: + bool initialized; + bool installed; + + void* place; + void* stub; + void* original; + char buffer[5]; + bool useJump; + + DWORD protection; + + std::mutex stateMutex; + }; +} diff --git a/tools/premake5.exe b/tools/premake5.exe new file mode 100644 index 0000000000000000000000000000000000000000..9048d51e09da5bde02e6337dcfaa603a2cebf292 GIT binary patch literal 1362432 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