Refactor this (tekno gods dont sue please)

This commit is contained in:
6arelyFuture 2022-04-22 15:35:14 +02:00
parent 4e1dfcfc3c
commit b48ab294b8
Signed by: Future
GPG Key ID: FA77F074E98D98A5
46 changed files with 341 additions and 201 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "deps/minhook"]
path = deps/minhook
url = https://github.com/TsudaKageyu/minhook.git
[submodule "deps/GSL"]
path = deps/GSL
url = https://github.com/microsoft/GSL.git

1
deps/GSL vendored Submodule

@ -0,0 +1 @@
Subproject commit 2bfd4950802a223dde37a08a205812b6dfdfeb61

19
deps/premake/gsl.lua vendored Normal file
View File

@ -0,0 +1,19 @@
gsl = {
source = path.join(dependencies.basePath, "GSL")
}
function gsl.import()
gsl.includes()
end
function gsl.includes()
includedirs {
path.join(gsl.source, "include")
}
end
function gsl.project()
end
table.insert(dependencies, gsl)

View File

@ -31,55 +31,74 @@ end
dependencies.load()
workspace "mw3-server-freezer"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
targetname "%{prj.name}"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
configurations {"Debug", "Release"}
configurations {"Debug", "Release"}
language "C++"
cppdialect "C++20"
language "C++"
cppdialect "C++20"
architecture "x86"
platforms "Win32"
architecture "x86"
platforms "Win32"
systemversion "latest"
symbols "On"
staticruntime "On"
editandcontinue "Off"
warnings "Extra"
characterset "ASCII"
systemversion "latest"
symbols "On"
staticruntime "On"
editandcontinue "Off"
warnings "Extra"
characterset "ASCII"
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
filter "platforms:Win*"
defines {"_WINDOWS", "WIN32"}
filter {}
filter "platforms:Win*"
defines {"_WINDOWS", "WIN32"}
filter {}
filter "configurations:Release"
optimize "Size"
defines {"NDEBUG"}
flags {"FatalCompileWarnings"}
buildoptions {"/GL"}
linkoptions { "/IGNORE:4702", "/LTCG" }
filter {}
filter "configurations:Release"
optimize "Size"
defines {"NDEBUG"}
flags {"FatalCompileWarnings"}
buildoptions {"/GL"}
linkoptions { "/IGNORE:4702", "/LTCG" }
filter {}
filter "configurations:Debug"
optimize "Debug"
defines {"DEBUG", "_DEBUG"}
filter {}
filter "configurations:Debug"
optimize "Debug"
defines {"DEBUG", "_DEBUG"}
filter {}
project "mw3-server-freezer"
kind "SharedLib"
language "C++"
project "common"
kind "StaticLib"
language "C++"
pchheader "stdinc.hpp"
pchsource "src/stdinc.cpp"
files {"./src/common/**.hpp", "./src/common/**.cpp"}
files { "./src/**.hpp", "./src/**.cpp" }
includedirs {"./src/common", "%{prj.location}/src"}
includedirs { "src" }
resincludedirs {"$(ProjectDir)src"}
dependencies.imports()
project "client"
kind "SharedLib"
language "C++"
targetname "mw3-server-freezer"
pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp"
linkoptions {"/IGNORE:4254", "/PDBCompress"}
files {"./src/client/**.hpp", "./src/client/**.cpp"}
includedirs {"./src/client", "./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
links {"common"}
dependencies.imports()

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -49,7 +49,7 @@ __declspec(naked) void blind_eye_check_stub() {
retn
draw:
push 0x05AA529
push 0x5AA529
retn
}
}

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <loader/component_loader.hpp>
#include <utils/string.hpp>
#include <utils/nt.hpp>
@ -84,8 +84,7 @@ public:
private:
static void add_commands_generic() {
// Will cause blue screen
add("quit_meme", utils::nt::raise_hard_exception);
add("quitMeme", utils::nt::raise_hard_exception);
add("quit", game::Com_Quit_f);
}
};

View File

@ -1,6 +1,5 @@
#include <stdinc.hpp>
#include <loader/component_loader.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
namespace console {
namespace {

View File

@ -1,10 +1,11 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
namespace dvar_patches {
void dvar_set_from_string_by_name_stub(const char*, const char*) {}
void dvar_set_from_string_by_name_stub(const char* /*dvarName*/,
const char* /*string*/) {}
class component final : public component_interface {
public:

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
#include "command.hpp"

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
#include "key_catcher.hpp"

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -18,6 +18,7 @@ bool handle_command(game::netadr_s* address, const char* command,
game::msg_t* msg) {
const auto cmd_string = utils::string::to_lower(command);
auto& callbacks = get_callbacks();
const auto handler = callbacks.find(cmd_string);
const auto offset = cmd_string.size() + 5;
@ -37,9 +38,7 @@ bool handle_command(game::netadr_s* address, const char* command,
int packet_interception_handler(game::netadr_s* from, const char* command,
game::msg_t* message) {
if (!handle_command(from, command, message)) {
return reinterpret_cast<int (*)(game::netadr_s*, const char*,
game::msg_t*)>(0x525730)(from, command,
message);
return utils::hook::invoke<int>(0x525730, from, command, message);
}
return TRUE;
@ -61,7 +60,7 @@ private:
static void add_network_commands() {
on_packet("naughty_reply",
[](const game::netadr_s&, const std::string_view&) {
command::execute("quit_meme");
command::execute("quitMeme");
});
}
};

View File

@ -0,0 +1,69 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
namespace remove_hooks {
namespace {
int msg_read_bits_compress_check_sv(const char* from, char* to, int size) {
static char buffer[0x8000];
if (size > 0x800) {
return 0;
}
size = game::MSG_ReadBitsCompress(from, buffer, size);
if (size > 0x800) {
return 0;
}
std::memcpy(to, buffer, size);
return size;
}
int msg_read_bits_compress_check_cl(const char* from, char* to, int size) {
static char buffer[0x100000];
if (size > 0x20000) {
return 0;
}
size = game::MSG_ReadBitsCompress(from, buffer, size);
if (size > 0x20000) {
return 0;
}
std::memcpy(to, buffer, size);
return size;
}
} // namespace
class component final : public component_interface {
public:
void post_start() override { remove_tekno_hooks(); }
void post_unpack() override {
utils::hook::call(0x4E3D42, msg_read_bits_compress_check_sv);
utils::hook::call(0x4A9F56, msg_read_bits_compress_check_cl);
}
private:
static void remove_tekno_hooks() {
utils::hook::set<BYTE>(0x4E3D42, 0xE8);
utils::hook::set<BYTE>(0x4E3D43, 0xA9);
utils::hook::set<BYTE>(0x4E3D44, 0x25);
utils::hook::set<BYTE>(0x4E3D45, 0xFE);
utils::hook::set<BYTE>(0x4E3D46, 0xFF);
utils::hook::set<BYTE>(0x6EA960, 0x55);
utils::hook::set<BYTE>(0x6EA961, 0x8B);
utils::hook::set<BYTE>(0x6EA962, 0xEC);
utils::hook::set<BYTE>(0x6EA963, 0x81);
utils::hook::set<BYTE>(0x6EA964, 0xEC);
}
};
} // namespace remove_hooks
REGISTER_COMPONENT(remove_hooks::component)

View File

@ -1,14 +1,13 @@
#include <stdinc.hpp>
#include <loader/component_loader.hpp>
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/concurrency.hpp>
#include <utils/hook.hpp>
#include <utils/thread.hpp>
#include "scheduler.hpp"
namespace scheduler {
std::thread::id async_thread_id;
namespace {
struct task {
std::function<bool()> handler{};
@ -74,6 +73,7 @@ private:
}
};
volatile bool kill = false;
std::thread thread;
task_pipeline pipelines[pipeline::count];
@ -82,10 +82,15 @@ void execute(const pipeline type) {
pipelines[type].execute();
}
void cl_frame_stub(game::LocalClientNum_t local) {
reinterpret_cast<void (*)(game::LocalClientNum_t)>(0x41C9B0)(local);
void cl_frame_stub(game::LocalClientNum_t localClientNum) {
utils::hook::invoke<void>(0x41C9B0, localClientNum);
execute(pipeline::client);
}
void main_frame_stub() {
utils::hook::invoke<void>(0x4E46A0);
execute(pipeline::main);
}
} // namespace
void clear_tasks(const pipeline type) { return pipelines[type].clear(); }
@ -127,16 +132,22 @@ unsigned int thread_id;
class component final : public component_interface {
public:
void post_unpack() override {
thread = std::thread([]() {
while (true) {
thread = utils::thread::create_named_thread("Async Scheduler", []() {
while (!kill) {
execute(pipeline::async);
std::this_thread::sleep_for(10ms);
}
});
async_thread_id = thread.get_id();
utils::hook::call(0x4E4A0D, cl_frame_stub);
utils::hook::call(0x543B0E, main_frame_stub);
}
void pre_destroy() override {
kill = true;
if (thread.joinable()) {
thread.join();
}
}
};
} // namespace scheduler

View File

@ -1,11 +1,10 @@
#pragma once
namespace scheduler {
extern std::thread::id async_thread_id;
enum pipeline {
client,
async,
main,
count,
};

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp>
#include "std_include.hpp"
#include "loader/component_loader.hpp"
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call,
@ -6,6 +6,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call,
) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
std::srand(uint32_t(time(nullptr)));
component_loader::post_start();
component_loader::post_unpack();
}

3
src/client/game/game.cpp Normal file
View File

@ -0,0 +1,3 @@
#include <std_include.hpp>
namespace game {}

View File

@ -49,7 +49,6 @@ WEAK symbol<bool(netsrc_t, netadr_s dest, const char* message)>
NET_OutOfBandPrint{0x496230};
WEAK symbol<bool(netsrc_t, netadr_s dest, unsigned char* data, int size)>
NET_OutOfBandData{0x4639C0};
WEAK symbol<int(unsigned int, void*, netadr_s)> dwSendTo{0x673B20};
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x48B460};
WEAK symbol<int(const char* serverName, netadr_s serverRemote)> NET_StringToAdr{
0x4E09A0};
@ -63,6 +62,9 @@ WEAK symbol<void(const msg_t*, int)> MSG_WriteShort{0x4ACD80};
WEAK symbol<void(const msg_t*, const void*, int)> MSG_WriteData{0x4F8C20};
WEAK symbol<void(int, const char*)> CL_AddReliableCommand{0x4EE3A0};
WEAK symbol<int(const char* from, char* to, int size)> MSG_ReadBitsCompress{
0x4C62F0};
WEAK symbol<unsigned __int64()> LiveSteam_GetUid{0x4A4050};
WEAK symbol<int(unsigned __int64, const void*, unsigned int)>
LiveSteam_Client_ConnectToSteamServer{0x4D6980};
@ -75,5 +77,4 @@ WEAK symbol<HWND> g_wv_hWnd{0x5A86AF0};
WEAK symbol<HWND> s_wcd_hWnd{0x5A86330};
WEAK symbol<int> serverId{0xFF5058};
WEAK symbol<connstate_t> connectionState{0x1060214};
WEAK symbol<dvar_t> cl_paused{0x1CE6190};
} // namespace game

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "component_loader.hpp"
void component_loader::register_component(

View File

@ -0,0 +1 @@
#include "std_include.hpp"

View File

@ -5,7 +5,7 @@
#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <windows.h>
#include <Windows.h>
#include <algorithm>
#include <cassert>

View File

@ -1,5 +1,3 @@
#include <stdinc.hpp>
#include "hook.hpp"
#include "string.hpp"

View File

@ -1,5 +1,3 @@
#include <stdinc.hpp>
#include "info_string.hpp"
#include "string.hpp"
@ -38,13 +36,12 @@ void info_string::parse(std::string buffer) {
std::string info_string::build() const {
std::string info_string;
for (auto i = this->key_value_pairs_.begin();
i != this->key_value_pairs_.end(); ++i) {
for (const auto& [key, val] : this->key_value_pairs_) {
info_string.append("\\");
info_string.append(i->first); // Key
info_string.append(key);
info_string.append("\\");
info_string.append(i->second); // Value
info_string.append(val);
}
return info_string;

View File

@ -1,5 +1,3 @@
#include <stdinc.hpp>
#include "memory.hpp"
#include "nt.hpp"
@ -50,7 +48,7 @@ char* memory::allocator::duplicate_string(const std::string& string) {
return data;
}
void* memory::allocate(const size_t length) { return calloc(length, 1); }
void* memory::allocate(const size_t length) { return std::calloc(length, 1); }
char* memory::duplicate_string(const std::string& string) {
const auto new_string = allocate_array<char>(string.size() + 1);
@ -60,7 +58,7 @@ char* memory::duplicate_string(const std::string& string) {
void memory::free(void* data) {
if (data) {
::free(data);
std::free(data);
}
}

View File

@ -1,5 +1,3 @@
#include <stdinc.hpp>
#include "nt.hpp"
namespace utils::nt {

View File

@ -1,7 +1,6 @@
#include <stdinc.hpp>
#include "signature.hpp"
#include <thread>
#include <mutex>
#include <intrin.h>

View File

@ -1,9 +1,7 @@
#include <stdinc.hpp>
#include "string.hpp"
#include <algorithm>
#include <cstdarg>
#include <sstream>
#include <cstdarg>
#include <algorithm>
#include "nt.hpp"

View File

@ -1,6 +1,10 @@
#pragma once
#include "memory.hpp"
#ifndef ARRAYSIZE
template <class Type, size_t n> size_t ARRAYSIZE(Type (&)[n]) { return n; }
#endif
namespace utils::string {
template <size_t Buffers, size_t MinBufferSize> class va_provider final {
public:
@ -19,7 +23,7 @@ public:
while (true) {
const int res =
vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
_vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
if (res > 0)
break; // Success
if (res == 0)

105
src/common/utils/thread.cpp Normal file
View File

@ -0,0 +1,105 @@
#include <thread>
#include "nt.hpp"
#include "string.hpp"
#include "thread.hpp"
#include <TlHelp32.h>
#include <gsl/gsl>
namespace utils::thread {
bool set_name(const HANDLE t, const std::string& name) {
const nt::library kernel32("kernel32.dll");
if (!kernel32) {
return false;
}
const auto set_description =
kernel32.get_proc<HRESULT(WINAPI*)(HANDLE, PCWSTR)>(
"SetThreadDescription");
if (!set_description) {
return false;
}
return SUCCEEDED(set_description(t, string::convert(name).data()));
}
bool set_name(const DWORD id, const std::string& name) {
auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
if (!t)
return false;
const auto _ = gsl::finally([t]() { CloseHandle(t); });
return set_name(t, name);
}
bool set_name(std::thread& t, const std::string& name) {
return set_name(t.native_handle(), name);
}
bool set_name(const std::string& name) {
return set_name(GetCurrentThread(), name);
}
std::vector<DWORD> get_thread_ids() {
auto* const h =
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
if (h == INVALID_HANDLE_VALUE) {
return {};
}
const auto _ = gsl::finally([h]() { CloseHandle(h); });
THREADENTRY32 entry{};
entry.dwSize = sizeof(entry);
if (!Thread32First(h, &entry)) {
return {};
}
std::vector<DWORD> ids;
do {
const auto check_size =
entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
sizeof(entry.th32OwnerProcessID);
entry.dwSize = sizeof(entry);
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId()) {
ids.emplace_back(entry.th32ThreadID);
}
} while (Thread32Next(h, &entry));
return ids;
}
void for_each_thread(const std::function<void(HANDLE)>& callback) {
const auto ids = get_thread_ids();
for (const auto& id : ids) {
auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
if (thread != nullptr) {
const auto _ = gsl::finally([thread]() { CloseHandle(thread); });
callback(thread);
}
}
}
void suspend_other_threads() {
for_each_thread([](const HANDLE thread) {
if (GetThreadId(thread) != GetCurrentThreadId()) {
SuspendThread(thread);
}
});
}
void resume_other_threads() {
for_each_thread([](const HANDLE thread) {
if (GetThreadId(thread) != GetCurrentThreadId()) {
ResumeThread(thread);
}
});
}
} // namespace utils::thread

View File

@ -0,0 +1,21 @@
#pragma once
namespace utils::thread {
bool set_name(HANDLE t, const std::string& name);
bool set_name(DWORD id, const std::string& name);
bool set_name(std::thread& t, const std::string& name);
bool set_name(const std::string& name);
template <typename... Args>
std::thread create_named_thread(const std::string& name, Args&&... args) {
auto t = std::thread(std::forward<Args>(args)...);
set_name(t, name);
return t;
}
std::vector<DWORD> get_thread_ids();
void for_each_thread(const std::function<void(HANDLE)>& callback);
void suspend_other_threads();
void resume_other_threads();
} // namespace utils::thread

View File

@ -1,28 +0,0 @@
#include <stdinc.hpp>
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
namespace remove_hooks {
class component final : public component_interface {
public:
void post_unpack() override { remove_tekno_hooks(); }
private:
static void remove_tekno_hooks() {
utils::hook::set<BYTE>(0x4E3D42, 0xE8);
utils::hook::set<BYTE>(0x4E3D43, 0xA9);
utils::hook::set<BYTE>(0x4E3D44, 0x25);
utils::hook::set<BYTE>(0x4E3D45, 0xFE);
utils::hook::set<BYTE>(0x4E3D46, 0xFF);
utils::hook::set<BYTE>(0x6EA960, 0x55);
utils::hook::set<BYTE>(0x6EA961, 0x8B);
utils::hook::set<BYTE>(0x6EA962, 0xEC);
utils::hook::set<BYTE>(0x6EA963, 0x81);
utils::hook::set<BYTE>(0x6EA964, 0xEC);
}
};
} // namespace remove_hooks
REGISTER_COMPONENT(remove_hooks::component)

View File

@ -1,71 +0,0 @@
#include <stdinc.hpp>
#include <loader/component_loader.hpp>
#include <utils/hook.hpp>
#include <utils/info_string.hpp>
#include <utils/string.hpp>
#include "scheduler.hpp"
namespace user_info {
namespace {
int a1 = 0;
void cl_check_user_info(int _a1, const int force) {
a1 = _a1;
if (*game::connectionState <= game::connstate_t::CA_CHALLENGING)
return;
if (game::cl_paused->current.enabled && !force)
return;
const std::string info_string = game::Dvar_InfoString(_a1, 0x200);
utils::info_string info(info_string);
const auto color_code = std::rand() % 10;
char name[32];
const auto numbers = std::to_string(std::rand() % 10000);
_snprintf_s(name, _TRUNCATE, "^%d%s", color_code, numbers.data());
info.set("name", name);
info.set("ec_usingTag", "1");
info.set("ec_TagText", utils::string::va("^%dGG", color_code));
const auto big_title = std::to_string(std::rand() % 512);
info.set("ec_TitleBg", big_title);
game::CL_AddReliableCommand(
_a1, utils::string::va("userinfo \"%s\"", info.build().data()));
}
__declspec(naked) void cl_check_user_info_stub() {
__asm {
pushad
push 0
push esi
call cl_check_user_info
add esp, 8
popad
ret
}
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
utils::hook::call(0x41CA53, cl_check_user_info_stub);
scheduler::loop([] { cl_check_user_info(a1, TRUE); },
scheduler::pipeline::client, 4s);
}
};
} // namespace user_info
REGISTER_COMPONENT(user_info::component)

View File

@ -1,3 +0,0 @@
#include <stdinc.hpp>
namespace game {}

View File

@ -1 +0,0 @@
#include <stdinc.hpp>