mirror of
https://github.com/ineedbots/cod2m.git
synced 2025-04-19 08:02:52 +00:00
Init iw4x base
This commit is contained in:
commit
0c2c478990
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.hpp eol=crlf
|
||||
*.cpp eol=crlf
|
||||
*.lua eol=crlf
|
||||
*.proto eol=crlf
|
153
.gitignore
vendored
Normal file
153
.gitignore
vendored
Normal file
@ -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
|
4
generate.bat
Normal file
4
generate.bat
Normal file
@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
echo Updating submodules...
|
||||
call git submodule update --init --recursive
|
||||
call tools\premake5 %* vs2019
|
317
premake5.lua
Normal file
317
premake5.lua
Normal file
@ -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" }
|
9
src/Game/MP.cpp
Normal file
9
src/Game/MP.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace MP
|
||||
{
|
||||
void PatchT4()
|
||||
{
|
||||
MessageBoxA(nullptr, "MP", "DEBUG", 0);
|
||||
}
|
||||
}
|
6
src/Game/MP.hpp
Normal file
6
src/Game/MP.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace MP
|
||||
{
|
||||
void PatchT4();
|
||||
}
|
0
src/Game/MP/Functions.hpp
Normal file
0
src/Game/MP/Functions.hpp
Normal file
0
src/Game/MP/Structs.hpp
Normal file
0
src/Game/MP/Structs.hpp
Normal file
9
src/Game/SP.cpp
Normal file
9
src/Game/SP.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace SP
|
||||
{
|
||||
void PatchT4()
|
||||
{
|
||||
MessageBoxA(nullptr, "SP", "DEBUG", 0);
|
||||
}
|
||||
}
|
6
src/Game/SP.hpp
Normal file
6
src/Game/SP.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace SP
|
||||
{
|
||||
void PatchT4();
|
||||
}
|
0
src/Game/SP/Functions.hpp
Normal file
0
src/Game/SP/Functions.hpp
Normal file
0
src/Game/SP/Structs.hpp
Normal file
0
src/Game/SP/Structs.hpp
Normal file
89
src/Main.cpp
Normal file
89
src/Main.cpp
Normal file
@ -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<DWORD>(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<DWORD>(0x401000);
|
||||
|
||||
// detect for all executables to hook into
|
||||
//if (textSegData == 0x9EF490B8 || textSegData == 0x83EC8B55)
|
||||
Main::SetSafeInit();
|
||||
}
|
||||
return true;
|
||||
}
|
97
src/Resource.rc
Normal file
97
src/Resource.rc
Normal file
@ -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
|
||||
|
90
src/SDLLP.cpp
Normal file
90
src/SDLLP.cpp
Normal file
@ -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<std::string, HINSTANCE> 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<std::string, HINSTANCE> 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)
|
115
src/STDInclude.cpp
Normal file
115
src/STDInclude.cpp
Normal file
@ -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;
|
||||
}
|
||||
};
|
120
src/STDInclude.hpp
Normal file
120
src/STDInclude.hpp
Normal file
@ -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 <vld.h>
|
||||
#pragma warning(disable: 4740)
|
||||
|
||||
#include <windows.h>
|
||||
#include <timeapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <Wininet.h>
|
||||
#include <Aclapi.h>
|
||||
#include <Psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091)
|
||||
#pragma warning(disable: 4244)
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cctype>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
// Experimental C++17 features
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
// Usefull for debugging
|
||||
template <size_t S> class Sizer { };
|
||||
#define BindNum(x, y) Sizer<x> 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
|
256
src/Utils/Hooking.cpp
Normal file
256
src/Utils/Hooking.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
std::map<void*, void*> Hook::Interceptor::IReturn;
|
||||
std::map<void*, void(*)()> Hook::Interceptor::ICallbacks;
|
||||
|
||||
void Hook::Signature::process()
|
||||
{
|
||||
if (this->signatures.empty()) return;
|
||||
|
||||
char* _start = reinterpret_cast<char*>(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<void(*)()>(stub));
|
||||
}
|
||||
|
||||
void Hook::Interceptor::Install(void* place, void(*stub)())
|
||||
{
|
||||
return Hook::Interceptor::Install(reinterpret_cast<void**>(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<void*>(_stub), _useJump);
|
||||
}
|
||||
|
||||
Hook* Hook::initialize(DWORD _place, void* _stub, bool _useJump)
|
||||
{
|
||||
return this->initialize(reinterpret_cast<void*>(_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<char*>(this->place) + 5 + *reinterpret_cast<DWORD*>((static_cast<char*>(this->place) + 1));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Hook* Hook::install(bool unprotect, bool keepUnprotected)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(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<char*>(this->place);
|
||||
|
||||
*code = static_cast<char>(this->useJump ? 0xE9 : 0xE8);
|
||||
|
||||
*reinterpret_cast<size_t*>(code + 1) = reinterpret_cast<size_t>(this->stub) - (reinterpret_cast<size_t>(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<std::mutex> _(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<void*>(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<char*>(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<void*>(place), string, length);
|
||||
}
|
||||
|
||||
void Hook::SetString(void* place, const char* string)
|
||||
{
|
||||
Hook::SetString(place, string, strlen(static_cast<char*>(place)));
|
||||
}
|
||||
|
||||
void Hook::SetString(DWORD place, const char* string)
|
||||
{
|
||||
Hook::SetString(reinterpret_cast<void*>(place), string);
|
||||
}
|
||||
|
||||
void Hook::RedirectJump(void* place, void* stub)
|
||||
{
|
||||
char* operandPtr = static_cast<char*>(place) + 2;
|
||||
int newOperand = reinterpret_cast<int>(stub) - (reinterpret_cast<int>(place) + 6);
|
||||
Utils::Hook::Set<int>(operandPtr, newOperand);
|
||||
}
|
||||
|
||||
void Hook::RedirectJump(DWORD place, void* stub)
|
||||
{
|
||||
Hook::RedirectJump(reinterpret_cast<void*>(place), stub);
|
||||
}
|
||||
}
|
185
src/Utils/Hooking.hpp
Normal file
185
src/Utils/Hooking.hpp
Normal file
@ -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<void(char*)> callback;
|
||||
};
|
||||
|
||||
Signature(void* _start, size_t _length) : start(_start), length(_length) {}
|
||||
Signature(DWORD _start, size_t _length) : Signature(reinterpret_cast<void*>(_start), _length) {}
|
||||
Signature() : Signature(0x400000, 0x800000) {}
|
||||
|
||||
void process();
|
||||
void add(Container& container);
|
||||
|
||||
private:
|
||||
void* start;
|
||||
size_t length;
|
||||
std::vector<Container> 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<void*, void*> IReturn;
|
||||
static std::map<void*, void(*)()> 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<void*>(stub), useJump) {}
|
||||
|
||||
Hook(DWORD place, void* stub, bool useJump = true) : Hook(reinterpret_cast<void*>(place), stub, useJump) {}
|
||||
Hook(DWORD place, DWORD stub, bool useJump = true) : Hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), useJump) {}
|
||||
Hook(DWORD place, void(*stub)(), bool useJump = true) : Hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(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 <typename T> static std::function<T> Call(DWORD function)
|
||||
{
|
||||
return std::function<T>(reinterpret_cast<T*>(function));
|
||||
}
|
||||
|
||||
template <typename T> static std::function<T> Call(FARPROC function)
|
||||
{
|
||||
return Call<T>(reinterpret_cast<DWORD>(function));
|
||||
}
|
||||
|
||||
template <typename T> static std::function<T> Call(void* function)
|
||||
{
|
||||
return Call<T>(reinterpret_cast<DWORD>(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 <typename T> static void Set(void* place, T value)
|
||||
{
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
*static_cast<T*>(place) = value;
|
||||
|
||||
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> static void Set(DWORD place, T value)
|
||||
{
|
||||
return Set<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T> static void Xor(void* place, T value)
|
||||
{
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
*static_cast<T*>(place) ^= value;
|
||||
|
||||
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> static void Xor(DWORD place, T value)
|
||||
{
|
||||
return Xor<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T> static void Or(void* place, T value)
|
||||
{
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
*static_cast<T*>(place) |= value;
|
||||
|
||||
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> static void Or(DWORD place, T value)
|
||||
{
|
||||
return Or<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T> static void And(void* place, T value)
|
||||
{
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
*static_cast<T*>(place) &= value;
|
||||
|
||||
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> static void And(DWORD place, T value)
|
||||
{
|
||||
return And<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T> static T Get(void* place)
|
||||
{
|
||||
return *static_cast<T*>(place);
|
||||
}
|
||||
|
||||
template <typename T> static T Get(DWORD place)
|
||||
{
|
||||
return Get<T>(reinterpret_cast<void*>(place));
|
||||
}
|
||||
|
||||
private:
|
||||
bool initialized;
|
||||
bool installed;
|
||||
|
||||
void* place;
|
||||
void* stub;
|
||||
void* original;
|
||||
char buffer[5];
|
||||
bool useJump;
|
||||
|
||||
DWORD protection;
|
||||
|
||||
std::mutex stateMutex;
|
||||
};
|
||||
}
|
BIN
tools/premake5.exe
Normal file
BIN
tools/premake5.exe
Normal file
Binary file not shown.
BIN
tools/protoc.exe
Normal file
BIN
tools/protoc.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user