mirror of
https://github.com/diamante0018/MW3ServerFreezer.git
synced 2025-04-19 19:52:53 +00:00
Refactor
This commit is contained in:
parent
1dd227baa5
commit
8a2df0a179
12
.clang-format
Normal file
12
.clang-format
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
SortIncludes: false
|
||||
|
||||
# Regroup causes unnecessary noise due to clang-format bug.
|
||||
IncludeBlocks: Preserve
|
||||
|
||||
---
|
55
.github/workflows/build.yml
vendored
Normal file
55
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
build:
|
||||
name: Build binaries
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
matrix:
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
steps:
|
||||
- name: Wait for previous workflows
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
|
||||
uses: softprops/turnstyle@v1
|
||||
with:
|
||||
poll-interval-seconds: 10
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check out files
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
||||
lfs: false
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
|
||||
- name: Generate project files
|
||||
run: tools/premake5 vs2022
|
||||
|
||||
- name: Set up problem matching
|
||||
uses: ammaraskar/msvc-problem-matcher@master
|
||||
|
||||
- name: Build ${{matrix.configuration}} binaries
|
||||
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:Platform=Win32 build/mw3-server-freezer.sln
|
||||
|
||||
- name: Upload ${{matrix.configuration}} binaries
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{matrix.configuration}} binaries
|
||||
path: |
|
||||
build/bin/Win32/${{matrix.configuration}}/mw3-server-freezer.dll
|
||||
build/bin/Win32/${{matrix.configuration}}/mw3-server-freezer.pdb
|
20
.github/workflows/clang-format-check.yml
vendored
Normal file
20
.github/workflows/clang-format-check.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: clang-format Check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
formatting-check:
|
||||
name: Formatting Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run clang-format style check for C/C++/Protobuf programs.
|
||||
uses: jidicula/clang-format-action@v4.6.2
|
||||
with:
|
||||
clang-format-version: '14'
|
||||
check-path: 'src'
|
@ -14,8 +14,7 @@ If you think your server is vulnerable you should seek help in the appropriate d
|
||||
## Compile from source
|
||||
|
||||
- Clone the Git repo. Do NOT download it as ZIP, that won't work.
|
||||
- Update the submodules and run `premake5 vs2019` or simply use the delivered `generate.bat`.
|
||||
- Visual Studio Properties -> Linker -> Input -> Module Definition File -> add module.def located in src folder
|
||||
- Update the submodules and run `premake5 vs2022` or simply use the delivered `generate.bat`.
|
||||
- Build via solution file in `build\mw3-server-freezer.sln`.
|
||||
|
||||
## Commands
|
||||
|
@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
echo Updating submodules...
|
||||
call git submodule update --init --recursive
|
||||
call tools\premake5 %* vs2019
|
||||
call tools\premake5 %* vs2022
|
||||
|
31
premake5.lua
31
premake5.lua
@ -36,36 +36,39 @@ workspace "mw3-server-freezer"
|
||||
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||
targetname "%{prj.name}"
|
||||
|
||||
language "C++"
|
||||
|
||||
architecture "x86"
|
||||
platforms "x86"
|
||||
|
||||
buildoptions "/std:c++latest"
|
||||
systemversion "latest"
|
||||
|
||||
flags { "NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks" }
|
||||
|
||||
configurations {"Debug", "Release"}
|
||||
|
||||
language "C++"
|
||||
cppdialect "C++20"
|
||||
|
||||
architecture "x86"
|
||||
platforms "Win32"
|
||||
|
||||
systemversion "latest"
|
||||
symbols "On"
|
||||
staticruntime "On"
|
||||
editandcontinue "Off"
|
||||
warnings "Extra"
|
||||
characterset "ASCII"
|
||||
|
||||
configuration "Release"
|
||||
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
|
||||
|
||||
filter "platforms:Win*"
|
||||
defines {"_WINDOWS", "WIN32"}
|
||||
filter {}
|
||||
|
||||
filter "configurations:Release"
|
||||
optimize "Size"
|
||||
defines {"NDEBUG"}
|
||||
flags {"FatalCompileWarnings"}
|
||||
buildoptions {"/GL"}
|
||||
linkoptions { "/IGNORE:4702", "/LTCG" }
|
||||
configuration {}
|
||||
filter {}
|
||||
|
||||
configuration "Debug"
|
||||
filter "configurations:Debug"
|
||||
optimize "Debug"
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
configuration {}
|
||||
filter {}
|
||||
|
||||
project "mw3-server-freezer"
|
||||
kind "SharedLib"
|
||||
|
@ -13,8 +13,7 @@ namespace cheats
|
||||
|
||||
__declspec(naked) void draw_red_box_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
__asm {
|
||||
push eax
|
||||
mov eax, cl_EnableCheats
|
||||
cmp byte ptr [eax + 12], 1
|
||||
@ -35,8 +34,7 @@ namespace cheats
|
||||
|
||||
__declspec(naked) void blind_eye_check_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
__asm {
|
||||
push eax
|
||||
mov eax, cl_EnableCheats
|
||||
cmp byte ptr [eax + 12], 1
|
||||
@ -45,11 +43,11 @@ namespace cheats
|
||||
je draw
|
||||
|
||||
test byte ptr [esi], 0x20
|
||||
jnz skipBecauseBlindeye
|
||||
jnz skip_because_blindeye
|
||||
|
||||
jmp draw
|
||||
|
||||
skipBecauseBlindeye:
|
||||
skip_because_blindeye:
|
||||
push 0x5AA5A2
|
||||
retn
|
||||
|
||||
@ -64,7 +62,8 @@ namespace cheats
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
cl_EnableCheats = game::Dvar_RegisterBool("cl_EnableCheats", false, game::DVAR_FLAG_NONE, "Enable FoF wallhack");
|
||||
cl_EnableCheats = game::Dvar_RegisterBool(
|
||||
"cl_EnableCheats", false, game::DVAR_NONE, "Enable FoF wallhack");
|
||||
|
||||
utils::hook::jump(0x430561, draw_red_box_stub);
|
||||
utils::hook::nop(0x430566, 2);
|
||||
@ -77,27 +76,38 @@ namespace cheats
|
||||
private:
|
||||
static void add_cheat_commands()
|
||||
{
|
||||
key_catcher::on_key_press("Z", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"Z",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
game::Dvar_SetBool(cl_EnableCheats, true);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press("X", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"X",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
game::Dvar_SetBool(cl_EnableCheats, false);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press("Y", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"Y",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
command::execute(utils::string::va("cmd mr %i 2 allies", *game::serverId), true);
|
||||
command::execute(
|
||||
utils::string::va("cmd mr %i 2 allies", *game::serverId), true);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press("8", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"8",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
command::execute(utils::string::va("cmd mr %i -1 endround", *game::serverId), true);
|
||||
command::execute(
|
||||
utils::string::va("cmd mr %i -1 endround", *game::serverId),
|
||||
true);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace cheats
|
||||
|
||||
REGISTER_COMPONENT(cheats::component)
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
|
||||
namespace command
|
||||
{
|
||||
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||
@ -15,7 +17,7 @@ namespace command
|
||||
params params = {};
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
if (handlers.find(command) != handlers.end())
|
||||
if (!handlers.contains(command))
|
||||
{
|
||||
handlers[command](params);
|
||||
}
|
||||
@ -24,6 +26,7 @@ namespace command
|
||||
params::params()
|
||||
: nesting_(game::cmd_args->nesting)
|
||||
{
|
||||
assert(game::cmd_args->nesting < CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
int params::size() const
|
||||
@ -50,19 +53,23 @@ namespace command
|
||||
if (i > index) result.append(" ");
|
||||
result.append(this->get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_raw(const char* name, void (*callback)())
|
||||
{
|
||||
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_t>());
|
||||
game::Cmd_AddCommandInternal(
|
||||
name,
|
||||
callback,
|
||||
utils::memory::get_allocator()->allocate<game::cmd_function_t>());
|
||||
}
|
||||
|
||||
void add(const char* name, const std::function<void(const params&)>& callback)
|
||||
{
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (handlers.find(command) == handlers.end())
|
||||
if (!handlers.contains(command))
|
||||
{
|
||||
add_raw(name, main_handler);
|
||||
}
|
||||
@ -70,26 +77,13 @@ namespace command
|
||||
handlers[command] = callback;
|
||||
}
|
||||
|
||||
std::vector<std::string> script_commands;
|
||||
utils::memory::allocator allocator;
|
||||
|
||||
void add_script_command(const std::string& name, const std::function<void(const params&)>& callback)
|
||||
void add(const char* name, const std::function<void()>& callback)
|
||||
{
|
||||
script_commands.push_back(name);
|
||||
const auto _name = allocator.duplicate_string(name);
|
||||
add(_name, callback);
|
||||
}
|
||||
|
||||
void clear_script_commands()
|
||||
add(name,
|
||||
[callback](const params&)
|
||||
{
|
||||
for (const auto& name : script_commands)
|
||||
{
|
||||
handlers.erase(name);
|
||||
game::Cmd_RemoveCommand(name.data());
|
||||
}
|
||||
|
||||
allocator.clear();
|
||||
script_commands.clear();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
void execute(std::string command, const bool sync)
|
||||
@ -98,11 +92,13 @@ namespace command
|
||||
|
||||
if (sync)
|
||||
{
|
||||
game::Cmd_ExecuteSingleCommand(game::LocalClientNum_t::LOCAL_CLIENT_0, 0, command.data());
|
||||
game::Cmd_ExecuteSingleCommand(
|
||||
game::LocalClientNum_t::LOCAL_CLIENT_0, 0, command.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
game::Cbuf_AddText(game::LocalClientNum_t::LOCAL_CLIENT_0, command.data());
|
||||
game::Cbuf_AddText(game::LocalClientNum_t::LOCAL_CLIENT_0,
|
||||
command.data());
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,26 +110,15 @@ namespace command
|
||||
add_commands_generic();
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
{
|
||||
clear_script_commands();
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_commands_generic()
|
||||
{
|
||||
add("quit_meme", [](const params&)
|
||||
{
|
||||
// Will cause blue screen
|
||||
utils::nt::raise_hard_exception();
|
||||
});
|
||||
add("quit_meme", utils::nt::raise_hard_exception);
|
||||
|
||||
add("dia_quit", [](const params&)
|
||||
{
|
||||
game::Com_Quit_f();
|
||||
});
|
||||
add("quit", game::Com_Quit_f);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace command
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
||||
|
@ -7,9 +7,9 @@ namespace command
|
||||
public:
|
||||
params();
|
||||
|
||||
int size() const;
|
||||
const char* get(int index) const;
|
||||
std::string join(int index) const;
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const
|
||||
{
|
||||
@ -21,10 +21,9 @@ namespace command
|
||||
};
|
||||
|
||||
void add_raw(const char* name, void (*callback)());
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
|
||||
void add_script_command(const std::string& name, const std::function<void(const params&)>& callback);
|
||||
void clear_script_commands();
|
||||
void add(const char* name,
|
||||
const std::function<void(const params&)>& callback);
|
||||
void add(const char* name, const std::function<void()>& callback);
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
}
|
||||
} // namespace command
|
||||
|
@ -17,11 +17,13 @@ namespace console
|
||||
|
||||
void console_unlock()
|
||||
{
|
||||
const auto callBack = SetWindowLongA(*game::g_wv_hWnd,
|
||||
GWL_WNDPROC, reinterpret_cast<LONG>(sys_start_console));
|
||||
const auto callback =
|
||||
SetWindowLongA(*game::g_wv_hWnd,
|
||||
GWL_WNDPROC,
|
||||
reinterpret_cast<LONG>(sys_start_console));
|
||||
|
||||
SendMessage(*game::g_wv_hWnd, WM_QUIT, 0, 0);
|
||||
SetWindowLongA(*game::g_wv_hWnd, GWL_WNDPROC, callBack);
|
||||
SendMessageA(*game::g_wv_hWnd, WM_QUIT, 0, 0);
|
||||
SetWindowLongA(*game::g_wv_hWnd, GWL_WNDPROC, callback);
|
||||
}
|
||||
|
||||
void show_console()
|
||||
@ -31,14 +33,15 @@ namespace console
|
||||
ShowWindow(*game::s_wcd_hWnd, SW_SHOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
thread = std::thread([]()
|
||||
thread = std::thread(
|
||||
[]()
|
||||
{
|
||||
console_unlock();
|
||||
show_console();
|
||||
@ -47,6 +50,6 @@ namespace console
|
||||
async_thread_id = thread.get_id();
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace console
|
||||
|
||||
REGISTER_COMPONENT(console::component)
|
||||
|
@ -1,14 +1,11 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.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*)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void dvar_set_from_string_by_name_stub(const char*, const char*) {}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
@ -18,6 +15,6 @@ namespace dvar_patches
|
||||
utils::hook::call(0x59C0EF, dvar_set_from_string_by_name_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace dvar_patches
|
||||
|
||||
REGISTER_COMPONENT(dvar_patches::component)
|
@ -12,42 +12,40 @@ namespace exploit
|
||||
|
||||
/*
|
||||
* void CL_Netchan_Transmit(netchan_t* chan, unsigned char* data, int a3)
|
||||
* A brief description of data: the first few bytes contain information from clientConnection_t structure
|
||||
* Offset 0: ServerID Size : 1
|
||||
* Offset 1: serverMessageSequence Size: 4
|
||||
* Offset 5: serverCommandSequence Size: 4
|
||||
* One clean way of sending invalid data to the server is to hook the functions that write the info to the packet
|
||||
* Credit: https://stackoverflow.com/questions/58981714/how-do-i-change-the-value-of-a-single-byte-in-a-uint32-t-variable
|
||||
* A brief description of data: the first few bytes contain information from
|
||||
* clientConnection_t structure Offset 0: ServerID Size : 1 Offset 1:
|
||||
* serverMessageSequence Size: 4 Offset 5: serverCommandSequence Size: 4 One
|
||||
* clean way of sending invalid data to the server is to hook the functions
|
||||
* that write the info to the packet Credit:
|
||||
* https://stackoverflow.com/questions/58981714/how-do-i-change-the-value-of-a-single-byte-in-a-uint32-t-variable
|
||||
*/
|
||||
|
||||
void write_message_sequence(game::msg_t* msg, int data)
|
||||
{
|
||||
if (msg->maxsize - msg->cursize < sizeof(int))
|
||||
if (msg->maxsize - static_cast<unsigned int>(msg->cursize) < sizeof(int))
|
||||
{
|
||||
msg->overflowed = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl_exploit->current.enabled)
|
||||
data = (data & 0xFFFFFF00) | 0xAAu;
|
||||
if (cl_exploit->current.enabled) data = (data & 0xFFFFFF00) | 0xAAu;
|
||||
|
||||
auto dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
auto* dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
*dest = data;
|
||||
msg->cursize += sizeof(int);
|
||||
}
|
||||
|
||||
void write_command_sequence(game::msg_t* msg, int data)
|
||||
{
|
||||
if (msg->maxsize - msg->cursize < sizeof(int))
|
||||
if (msg->maxsize - static_cast<unsigned int>(msg->cursize) < sizeof(int))
|
||||
{
|
||||
msg->overflowed = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl_exploit->current.enabled)
|
||||
data = (data & 0x00FFFFFF) | (0x80u << 24);
|
||||
if (cl_exploit->current.enabled) data = (data & 0x00FFFFFF) | (0x80u << 24);
|
||||
|
||||
auto dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
auto* dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
*dest = data;
|
||||
msg->cursize += sizeof(int);
|
||||
}
|
||||
@ -57,33 +55,36 @@ namespace exploit
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
cl_exploit = game::Dvar_RegisterBool("cl_exploit", false, game::DVAR_FLAG_NONE, "Enable server freezer");
|
||||
cl_exploit = game::Dvar_RegisterBool(
|
||||
"cl_exploit", false, game::DVAR_NONE, "Enable server freezer");
|
||||
|
||||
add_exploit_commands();
|
||||
add_key_hooks();
|
||||
|
||||
utils::hook::call(0x420B76, write_message_sequence);
|
||||
utils::hook::call(0x420B86, write_command_sequence);
|
||||
|
||||
// Increase size;
|
||||
utils::hook::set<DWORD>(0x4639C7, 0x1FFFCu);
|
||||
utils::hook::set<DWORD>(0x496237, 0x1FFFCu);
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_key_hooks()
|
||||
{
|
||||
key_catcher::on_key_press("O", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"O",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
command::execute("exploit");
|
||||
});
|
||||
|
||||
key_catcher::on_key_press("L", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"L",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
command::execute("undo_exploit");
|
||||
});
|
||||
|
||||
key_catcher::on_key_press("K", [](const game::LocalClientNum_t&)
|
||||
key_catcher::on_key_press(
|
||||
"K",
|
||||
[]([[maybe_unused]] const game::LocalClientNum_t& local_client)
|
||||
{
|
||||
command::execute("disconnect");
|
||||
});
|
||||
@ -91,17 +92,21 @@ namespace exploit
|
||||
|
||||
static void add_exploit_commands()
|
||||
{
|
||||
command::add("exploit", [](const command::params&)
|
||||
command::add("exploit",
|
||||
[]([[maybe_unused]] const command::params& params)
|
||||
{
|
||||
game::Dvar_SetBool(cl_exploit, true);
|
||||
});
|
||||
|
||||
command::add("undo_exploit", [](const command::params&)
|
||||
command::add("undo_exploit",
|
||||
[]([[maybe_unused]] const command::params& params)
|
||||
{
|
||||
game::Dvar_SetBool(cl_exploit, false);
|
||||
});
|
||||
|
||||
command::add("send_command", [](const command::params& params)
|
||||
command::add(
|
||||
"send_command",
|
||||
[]([[maybe_unused]] const command::params& params)
|
||||
{
|
||||
if (params.size() < 2) return;
|
||||
|
||||
@ -109,10 +114,12 @@ namespace exploit
|
||||
return;
|
||||
|
||||
const auto cmd = std::format("queryserverinfo ;{}", params.join(1));
|
||||
game::NET_OutOfBandPrint(game::NS_SERVER, game::localClientConnection->serverAddress, cmd.data());
|
||||
game::NET_OutOfBandPrint(game::NS_SERVER,
|
||||
game::localClientConnection->serverAddress,
|
||||
cmd.data());
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace exploit
|
||||
|
||||
REGISTER_COMPONENT(exploit::component)
|
||||
|
@ -11,40 +11,42 @@ namespace key_catcher
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<std::string, callback>& get_key_callbacks()
|
||||
std::unordered_map<std::string, key_catcher::callback>& get_key_callbacks()
|
||||
{
|
||||
static std::unordered_map<std::string, callback> key_callbacks{};
|
||||
static std::unordered_map<std::string, key_catcher::callback>
|
||||
key_callbacks{};
|
||||
return key_callbacks;
|
||||
}
|
||||
|
||||
void handle_key_event(game::LocalClientNum_t localClient, int keyID)
|
||||
void handle_key_event(game::LocalClientNum_t local_client, int key_id)
|
||||
{
|
||||
auto result = VkKeyScanA(static_cast<CHAR>(keyID));
|
||||
auto VkKey = LOBYTE(result);
|
||||
auto& callbacks = get_key_callbacks();
|
||||
const auto result = VkKeyScanA(static_cast<CHAR>(key_id));
|
||||
const auto vk_key = LOBYTE(result);
|
||||
const auto& callbacks = get_key_callbacks();
|
||||
|
||||
for (auto const& i : callbacks)
|
||||
for (auto const& [key, value] : callbacks)
|
||||
{
|
||||
auto gameVkKey = game::Key_StringToKeynum(i.first.data());
|
||||
if (static_cast<BYTE>(gameVkKey) == VkKey)
|
||||
const auto game_vk_key = game::Key_StringToKeynum(key.data());
|
||||
if (static_cast<BYTE>(game_vk_key) == vk_key)
|
||||
{
|
||||
i.second(localClient);
|
||||
value(local_client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void on_key_press(const std::string& command, const callback& callback)
|
||||
{
|
||||
get_key_callbacks()[command] = callback;
|
||||
}
|
||||
|
||||
void cl_key_event_stub(game::LocalClientNum_t localClient, int keyID, int a3)
|
||||
void cl_key_event_stub(game::LocalClientNum_t local_client, int key_id,
|
||||
int a3)
|
||||
{
|
||||
handle_key_event(localClient, keyID);
|
||||
handle_key_event(local_client, key_id);
|
||||
|
||||
cl_key_event_hook.invoke<void>(localClient, keyID, a3);
|
||||
cl_key_event_hook.invoke<void>(local_client, key_id, a3);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -60,6 +62,6 @@ namespace key_catcher
|
||||
cl_key_event_hook.clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace key_catcher
|
||||
|
||||
REGISTER_COMPONENT(key_catcher::component)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace key_catcher
|
||||
{
|
||||
using callback = std::function<void(game::LocalClientNum_t& localClient)>;
|
||||
using callback = std::function<void(game::LocalClientNum_t& local_client)>;
|
||||
|
||||
void on_key_press(const std::string& command, const callback& callback);
|
||||
}
|
||||
} // namespace key_catcher
|
||||
|
@ -11,36 +11,43 @@ namespace network
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<std::string, callback>& get_callbacks()
|
||||
std::unordered_map<std::string, network::callback>& get_callbacks()
|
||||
{
|
||||
static std::unordered_map<std::string, callback> network_callbacks{};
|
||||
static std::unordered_map<std::string, network::callback>
|
||||
network_callbacks{};
|
||||
return network_callbacks;
|
||||
}
|
||||
|
||||
bool handle_command(game::netadr_s* address, const char* command, game::msg_t* message)
|
||||
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;
|
||||
|
||||
if (handler == callbacks.end())
|
||||
if (static_cast<unsigned int>(msg->cursize) < offset ||
|
||||
handler == callbacks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto offset = cmd_string.size() + 5;
|
||||
const std::string_view data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
|
||||
const std::string_view data(reinterpret_cast<char*>(msg->data) + offset,
|
||||
msg->cursize - offset);
|
||||
|
||||
handler->second(*address, data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int packet_interception_handler(game::netadr_s* from, const char* command, game::msg_t* message)
|
||||
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 reinterpret_cast<int (*)(
|
||||
game::netadr_s*, const char*, game::msg_t*)>(0x525730)(
|
||||
from, command, message);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@ -64,12 +71,13 @@ namespace network
|
||||
private:
|
||||
static void add_network_commands()
|
||||
{
|
||||
on_packet("naughty_reply", [](const game::netadr_s&, const std::string_view&)
|
||||
on_packet("naughty_reply",
|
||||
[](const game::netadr_s&, const std::string_view&)
|
||||
{
|
||||
command::execute("quit_meme");
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace network
|
||||
|
||||
REGISTER_COMPONENT(network::component)
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
namespace network
|
||||
{
|
||||
using callback = std::function<void(const game::netadr_s&, const std::string_view&)>;
|
||||
using callback =
|
||||
std::function<void(const game::netadr_s&, const std::string_view&)>;
|
||||
|
||||
void on_packet(const std::string& command, const callback& callback);
|
||||
}
|
||||
} // namespace network
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include <loader/component_loader.hpp>
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace remove_hooks
|
||||
@ -29,6 +29,6 @@ namespace remove_hooks
|
||||
utils::hook::set<BYTE>(0x6EA964, 0xEC);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace remove_hooks
|
||||
|
||||
REGISTER_COMPONENT(remove_hooks::component)
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include <loader/component_loader.hpp>
|
||||
|
||||
#include "utils/concurrency.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
@ -26,7 +26,8 @@ namespace scheduler
|
||||
public:
|
||||
void add(task&& task)
|
||||
{
|
||||
new_callbacks_.access([&task, this](task_list& tasks)
|
||||
new_callbacks_.access(
|
||||
[&task, this](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
@ -34,7 +35,8 @@ namespace scheduler
|
||||
|
||||
void clear()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
callbacks_.access(
|
||||
[&](task_list& tasks)
|
||||
{
|
||||
this->merge_callbacks();
|
||||
tasks.clear();
|
||||
@ -43,7 +45,8 @@ namespace scheduler
|
||||
|
||||
void execute()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
callbacks_.access(
|
||||
[&](task_list& tasks)
|
||||
{
|
||||
this->merge_callbacks();
|
||||
|
||||
@ -79,11 +82,17 @@ namespace scheduler
|
||||
|
||||
void merge_callbacks()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
callbacks_.access(
|
||||
[&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
new_callbacks_.access(
|
||||
[&](task_list& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
tasks.insert(tasks.end(),
|
||||
std::move_iterator<task_list::iterator>(
|
||||
new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(
|
||||
new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
@ -104,7 +113,7 @@ namespace scheduler
|
||||
reinterpret_cast<void (*)(game::LocalClientNum_t)>(0x41C9B0)(local);
|
||||
execute(pipeline::client);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void clear_tasks(const pipeline type)
|
||||
{
|
||||
@ -127,21 +136,27 @@ namespace scheduler
|
||||
void loop(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
schedule(
|
||||
[callback]()
|
||||
{
|
||||
callback();
|
||||
return cond_continue;
|
||||
}, type, delay);
|
||||
},
|
||||
type,
|
||||
delay);
|
||||
}
|
||||
|
||||
void once(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
schedule(
|
||||
[callback]()
|
||||
{
|
||||
callback();
|
||||
return cond_end;
|
||||
}, type, delay);
|
||||
},
|
||||
type,
|
||||
delay);
|
||||
}
|
||||
|
||||
unsigned int thread_id;
|
||||
@ -151,7 +166,8 @@ namespace scheduler
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
thread = std::thread([]()
|
||||
thread = std::thread(
|
||||
[]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@ -165,6 +181,6 @@ namespace scheduler
|
||||
utils::hook::call(0x4E4A0D, cl_frame_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace scheduler
|
||||
|
||||
REGISTER_COMPONENT(scheduler::component)
|
||||
|
@ -16,10 +16,13 @@ namespace scheduler
|
||||
|
||||
void clear_tasks(const pipeline type);
|
||||
|
||||
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::client,
|
||||
void schedule(const std::function<bool()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void loop(const std::function<void()>& callback, pipeline type = pipeline::client,
|
||||
void loop(const std::function<void()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void once(const std::function<void()>& callback, pipeline type = pipeline::client,
|
||||
void once(const std::function<void()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
}
|
||||
} // namespace scheduler
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include <loader/component_loader.hpp>
|
||||
#include "utils/hook.hpp"
|
||||
#include "utils/info_string.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/info_string.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
@ -12,35 +13,35 @@ namespace user_info
|
||||
namespace
|
||||
{
|
||||
int a1 = 0;
|
||||
void cl_check_user_info(int _a1, int force)
|
||||
void cl_check_user_info(int _a1, const int force)
|
||||
{
|
||||
a1 = _a1;
|
||||
|
||||
if (*game::connectionState <= game::connstate_t::CA_CHALLENGING)
|
||||
return;
|
||||
if (*game::connectionState <= game::connstate_t::CA_CHALLENGING) return;
|
||||
|
||||
if (game::cl_paused->current.enabled && !force)
|
||||
return;
|
||||
if (game::cl_paused->current.enabled && !force) return;
|
||||
|
||||
const std::string infoString = game::Dvar_InfoString(_a1, 0x200);
|
||||
utils::info_string info(infoString);
|
||||
const std::string info_string = game::Dvar_InfoString(_a1, 0x200);
|
||||
utils::info_string info(info_string);
|
||||
|
||||
const auto colorCode = rand() % 10;
|
||||
const auto color_code = std::rand() % 10;
|
||||
|
||||
char name[16];
|
||||
|
||||
const auto& numbers = std::to_string(rand() % 10000);
|
||||
_snprintf_s(name, sizeof(name), _TRUNCATE, "^%d%s", colorCode, numbers.data());
|
||||
const auto numbers = std::to_string(std::rand() % 10000);
|
||||
_snprintf_s(
|
||||
name, sizeof(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", colorCode));
|
||||
info.set("ec_TagText", utils::string::va("^%dGG", color_code));
|
||||
|
||||
const auto& bigTitle = std::to_string(rand() % 512);
|
||||
info.set("ec_TitleBg", bigTitle);
|
||||
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()));
|
||||
game::CL_AddReliableCommand(
|
||||
_a1, utils::string::va("userinfo \"%s\"", info.build().data()));
|
||||
}
|
||||
|
||||
__declspec(naked) void cl_check_user_info_stub()
|
||||
@ -58,7 +59,7 @@ namespace user_info
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
@ -67,12 +68,15 @@ namespace user_info
|
||||
{
|
||||
utils::hook::call(0x41CA53, cl_check_user_info_stub);
|
||||
|
||||
scheduler::loop([]
|
||||
scheduler::loop(
|
||||
[]
|
||||
{
|
||||
cl_check_user_info(a1, TRUE);
|
||||
}, scheduler::pipeline::client, 4s);
|
||||
},
|
||||
scheduler::pipeline::client,
|
||||
4s);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace user_info
|
||||
|
||||
REGISTER_COMPONENT(user_info::component)
|
||||
|
@ -1,19 +1,15 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/,
|
||||
DWORD ul_reason_for_call,
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call,
|
||||
LPVOID /*lpReserved*/
|
||||
)
|
||||
{
|
||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
srand(uint32_t(time(nullptr)));
|
||||
) {
|
||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
|
||||
std::srand(uint32_t(time(nullptr)));
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
else if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
||||
{
|
||||
else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
|
||||
component_loader::pre_destroy();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
namespace game
|
||||
{
|
||||
}
|
||||
namespace game {}
|
||||
|
@ -1,34 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace game
|
||||
{
|
||||
template <typename T>
|
||||
class symbol
|
||||
{
|
||||
public:
|
||||
symbol(const size_t dedi)
|
||||
: dedi_(reinterpret_cast<T*>(dedi))
|
||||
{
|
||||
}
|
||||
namespace game {
|
||||
template <typename T> class symbol {
|
||||
public:
|
||||
symbol(const size_t dedi) : dedi_(reinterpret_cast<T*>(dedi)) {}
|
||||
|
||||
T* get() const
|
||||
{
|
||||
return dedi_;
|
||||
}
|
||||
T* get() const { return dedi_; }
|
||||
|
||||
operator T* () const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
operator T*() const { return this->get(); }
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
T* operator->() const { return this->get(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
T* dedi_;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace game
|
||||
|
||||
#include "symbols.hpp"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,66 +2,78 @@
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game
|
||||
{
|
||||
// Functions
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x515CD0};
|
||||
WEAK symbol<void(HINSTANCE__*)> Sys_CreateConsole{0x51B770};
|
||||
WEAK symbol<void(const char* fmt, ...)> Sys_Error{0x434000};
|
||||
WEAK symbol<void(const char* text)> Conbuf_AppendText{0x4F7300};
|
||||
WEAK symbol<void(errorParm_t, const char* fmt, ...)> Com_Error{0x4A6660};
|
||||
WEAK symbol<const char*(int index)> ConcatArgs{0x539060};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x4C1030};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x429920};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* cmd)> Cmd_AddCommandInternal{0x537E70};
|
||||
WEAK symbol<void(LocalClientNum_t, int, const char* text)> Cmd_ExecuteSingleCommand{0x4EB8F0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x4EAF30};
|
||||
WEAK symbol<const char*(int index)> Cmd_Argv{0x5580E0};
|
||||
namespace game {
|
||||
// Functions
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x515CD0};
|
||||
WEAK symbol<void(HINSTANCE__*)> Sys_CreateConsole{0x51B770};
|
||||
WEAK symbol<void(const char* fmt, ...)> Sys_Error{0x434000};
|
||||
WEAK symbol<void(const char* text)> Conbuf_AppendText{0x4F7300};
|
||||
WEAK symbol<void(errorParm_t, const char* fmt, ...)> Com_Error{0x4A6660};
|
||||
WEAK symbol<const char*(int index)> ConcatArgs{0x539060};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x4C1030};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x429920};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* cmd)>
|
||||
Cmd_AddCommandInternal{0x537E70};
|
||||
WEAK symbol<void(LocalClientNum_t, int, const char* text)>
|
||||
Cmd_ExecuteSingleCommand{0x4EB8F0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x4EAF30};
|
||||
WEAK symbol<const char*(int index)> Cmd_Argv{0x5580E0};
|
||||
|
||||
WEAK symbol<dvar_t*(const char*)> Dvar_FindVar{0x4EBB50};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned __int16 flags, const char* desc)>
|
||||
WEAK symbol<dvar_t*(const char*)> Dvar_FindVar{0x4EBB50};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max,
|
||||
unsigned __int16 flags, const char* desc)>
|
||||
Dvar_RegisterInt{0x50C760};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned __int16 flags, const char* description)>
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned __int16 flags,
|
||||
const char* description)>
|
||||
Dvar_RegisterBool{0x4A3300};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned __int16 flags, const char* description)>
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterString{0x4157E0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description)>
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterFloat{0x4A5CF0};
|
||||
WEAK symbol<void(dvar_t* var, bool value)> Dvar_SetBool{0x46DD70};
|
||||
WEAK symbol<void(const char* dvarName, bool value)> Dvar_SetBoolByName{0x48C7D0};
|
||||
WEAK symbol<const char*(int, int)> Dvar_InfoString{0x4028C0};
|
||||
WEAK symbol<void(dvar_t* var, bool value)> Dvar_SetBool{0x46DD70};
|
||||
WEAK symbol<void(const char* dvarName, bool value)> Dvar_SetBoolByName{
|
||||
0x48C7D0};
|
||||
WEAK symbol<const char*(int, int)> Dvar_InfoString{0x4028C0};
|
||||
|
||||
WEAK symbol<int(const char* cmd)> Key_GetBindingForCmd{0x47D300};
|
||||
WEAK symbol<int(const char* keyAsText)> Key_StringToKeynum{0x50A710}; // Virtual-Key Code
|
||||
WEAK symbol<void(LocalClientNum_t, int, int)> Key_SetBinding{0x50B770};
|
||||
WEAK symbol<int(const char* cmd)> Key_GetBindingForCmd{0x47D300};
|
||||
WEAK symbol<int(const char* keyAsText)> Key_StringToKeynum{
|
||||
0x50A710}; // Virtual-Key Code
|
||||
WEAK symbol<void(LocalClientNum_t, int, int)> Key_SetBinding{0x50B770};
|
||||
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x4F6B00};
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{
|
||||
0x4F6B00};
|
||||
|
||||
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};
|
||||
WEAK symbol<SOCKET> query_socket{0x5A861EC};
|
||||
WEAK symbol<void()> Com_Quit_f{0x556060};
|
||||
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};
|
||||
WEAK symbol<SOCKET> query_socket{0x5A861EC};
|
||||
WEAK symbol<void()> Com_Quit_f{0x556060};
|
||||
|
||||
WEAK symbol<void(const msg_t*, unsigned char*, int)> MSG_Init{0x40E030};
|
||||
WEAK symbol<void(const msg_t*, const char*)> MSG_WriteString{0x42A560};
|
||||
WEAK symbol<void(const msg_t*, unsigned __int64)> MSG_WriteInt64{0x4906B0};
|
||||
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<void(const msg_t*, unsigned char*, int)> MSG_Init{0x40E030};
|
||||
WEAK symbol<void(const msg_t*, const char*)> MSG_WriteString{0x42A560};
|
||||
WEAK symbol<void(const msg_t*, unsigned __int64)> MSG_WriteInt64{0x4906B0};
|
||||
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<unsigned __int64()> LiveSteam_GetUid{0x4A4050};
|
||||
WEAK symbol<int(unsigned __int64, const void*, unsigned int)> LiveSteam_Client_ConnectToSteamServer{0x4D6980};
|
||||
WEAK symbol<unsigned __int64()> LiveSteam_GetUid{0x4A4050};
|
||||
WEAK symbol<int(unsigned __int64, const void*, unsigned int)>
|
||||
LiveSteam_Client_ConnectToSteamServer{0x4D6980};
|
||||
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};
|
||||
WEAK symbol<clientConnection_t> localClientConnection{0xB3D360};
|
||||
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};
|
||||
}
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};
|
||||
WEAK symbol<clientConnection_t> localClientConnection{0xB3D360};
|
||||
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
|
||||
|
@ -1,35 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
class component_interface
|
||||
{
|
||||
class component_interface {
|
||||
public:
|
||||
virtual ~component_interface()
|
||||
{
|
||||
}
|
||||
virtual ~component_interface() {}
|
||||
|
||||
virtual void post_start()
|
||||
{
|
||||
}
|
||||
virtual void post_start() {}
|
||||
|
||||
virtual void post_load()
|
||||
{
|
||||
}
|
||||
virtual void post_load() {}
|
||||
|
||||
virtual void pre_destroy()
|
||||
{
|
||||
}
|
||||
virtual void pre_destroy() {}
|
||||
|
||||
virtual void post_unpack()
|
||||
{
|
||||
}
|
||||
virtual void post_unpack() {}
|
||||
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function)
|
||||
{
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library,
|
||||
[[maybe_unused]] const std::string& function) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool is_supported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool is_supported() { return true; }
|
||||
};
|
||||
|
@ -1,105 +1,89 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(std::unique_ptr<component_interface>&& component_)
|
||||
{
|
||||
void component_loader::register_component(
|
||||
std::unique_ptr<component_interface>&& component_) {
|
||||
get_components().push_back(std::move(component_));
|
||||
}
|
||||
|
||||
bool component_loader::post_start()
|
||||
{
|
||||
bool component_loader::post_start() {
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_start();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool component_loader::post_load()
|
||||
{
|
||||
bool component_loader::post_load() {
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
clean();
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_load();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void component_loader::post_unpack()
|
||||
{
|
||||
void component_loader::post_unpack() {
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
if (handled)
|
||||
return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_unpack();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::pre_destroy()
|
||||
{
|
||||
void component_loader::pre_destroy() {
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
if (handled)
|
||||
return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->pre_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::clean()
|
||||
{
|
||||
void component_loader::clean() {
|
||||
auto& components = get_components();
|
||||
for (auto i = components.begin(); i != components.end();)
|
||||
{
|
||||
if (!(*i)->is_supported())
|
||||
{
|
||||
for (auto i = components.begin(); i != components.end();) {
|
||||
if (!(*i)->is_supported()) {
|
||||
(*i)->pre_destroy();
|
||||
i = components.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* component_loader::load_import(const std::string& library, const std::string& function)
|
||||
{
|
||||
void* component_loader::load_import(const std::string& library,
|
||||
const std::string& function) {
|
||||
void* function_ptr = nullptr;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
auto* const component_function_ptr = component_->load_import(library, function);
|
||||
if (component_function_ptr)
|
||||
{
|
||||
for (const auto& component_ : get_components()) {
|
||||
auto* const component_function_ptr =
|
||||
component_->load_import(library, function);
|
||||
if (component_function_ptr) {
|
||||
function_ptr = component_function_ptr;
|
||||
}
|
||||
}
|
||||
@ -107,18 +91,18 @@ void* component_loader::load_import(const std::string& library, const std::strin
|
||||
return function_ptr;
|
||||
}
|
||||
|
||||
void component_loader::trigger_premature_shutdown()
|
||||
{
|
||||
void component_loader::trigger_premature_shutdown() {
|
||||
throw premature_shutdown_trigger();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component_interface>>& component_loader::get_components()
|
||||
{
|
||||
std::vector<std::unique_ptr<component_interface>>&
|
||||
component_loader::get_components() {
|
||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
using component_vector_container =
|
||||
std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
|
||||
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
||||
{
|
||||
static component_vector_container components(
|
||||
new component_vector, [](component_vector* component_vector) {
|
||||
pre_destroy();
|
||||
delete component_vector;
|
||||
});
|
||||
|
@ -1,36 +1,25 @@
|
||||
#pragma once
|
||||
#include "component_interface.hpp"
|
||||
|
||||
class component_loader final
|
||||
{
|
||||
class component_loader final {
|
||||
public:
|
||||
class premature_shutdown_trigger final : public std::exception
|
||||
{
|
||||
[[nodiscard]] const char* what() const noexcept override
|
||||
{
|
||||
class premature_shutdown_trigger final : public std::exception {
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return "Premature shutdown requested";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class installer final
|
||||
{
|
||||
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
||||
template <typename T> class installer final {
|
||||
static_assert(std::is_base_of<component_interface, T>::value,
|
||||
"component has invalid base class");
|
||||
|
||||
public:
|
||||
installer()
|
||||
{
|
||||
register_component(std::make_unique<T>());
|
||||
}
|
||||
installer() { register_component(std::make_unique<T>()); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static T* get()
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
if (typeid(*component_.get()) == typeid(T))
|
||||
{
|
||||
template <typename T> static T* get() {
|
||||
for (const auto& component_ : get_components()) {
|
||||
if (typeid(*component_.get()) == typeid(T)) {
|
||||
return reinterpret_cast<T*>(component_.get());
|
||||
}
|
||||
}
|
||||
@ -38,7 +27,8 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void register_component(std::unique_ptr<component_interface>&& component);
|
||||
static void
|
||||
register_component(std::unique_ptr<component_interface>&& component);
|
||||
|
||||
static bool post_start();
|
||||
static bool post_load();
|
||||
@ -46,7 +36,8 @@ public:
|
||||
static void pre_destroy();
|
||||
static void clean();
|
||||
|
||||
static void* load_import(const std::string& library, const std::string& function);
|
||||
static void* load_import(const std::string& library,
|
||||
const std::string& function);
|
||||
|
||||
static void trigger_premature_shutdown();
|
||||
|
||||
@ -55,7 +46,6 @@ private:
|
||||
};
|
||||
|
||||
#define REGISTER_COMPONENT(name) \
|
||||
namespace \
|
||||
{ \
|
||||
namespace { \
|
||||
static component_loader::installer<name> __component; \
|
||||
}
|
||||
}
|
||||
|
@ -4,23 +4,21 @@
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
// clang-format off
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
// clang-format on
|
||||
|
@ -2,36 +2,26 @@
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace utils::concurrency
|
||||
{
|
||||
template <typename T, typename MutexType = std::mutex>
|
||||
class container
|
||||
{
|
||||
public:
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor) const
|
||||
{
|
||||
namespace utils::concurrency {
|
||||
template <typename T, typename MutexType = std::mutex> class container {
|
||||
public:
|
||||
template <typename R = void, typename F> R access(F&& accessor) const {
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F> R access(F&& accessor) {
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor)
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor) const
|
||||
{
|
||||
R access_with_lock(F&& accessor) const {
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor)
|
||||
{
|
||||
template <typename R = void, typename F> R access_with_lock(F&& accessor) {
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
@ -39,8 +29,8 @@ namespace utils::concurrency
|
||||
T& get_raw() { return object_; }
|
||||
const T& get_raw() const { return object_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace utils::concurrency
|
||||
|
@ -5,88 +5,59 @@
|
||||
|
||||
#include <MinHook.h>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
namespace
|
||||
{
|
||||
[[maybe_unused]] class _
|
||||
{
|
||||
public:
|
||||
_()
|
||||
{
|
||||
if (MH_Initialize() != MH_OK)
|
||||
{
|
||||
namespace utils::hook {
|
||||
namespace {
|
||||
[[maybe_unused]] class _ {
|
||||
public:
|
||||
_() {
|
||||
if (MH_Initialize() != MH_OK) {
|
||||
throw std::runtime_error("Failed to initialize MinHook");
|
||||
}
|
||||
}
|
||||
|
||||
~_()
|
||||
{
|
||||
MH_Uninitialize();
|
||||
}
|
||||
} __;
|
||||
}
|
||||
~_() { MH_Uninitialize(); }
|
||||
} __;
|
||||
} // namespace
|
||||
|
||||
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target)
|
||||
{
|
||||
}
|
||||
detour::detour(const size_t place, void* target)
|
||||
: detour(reinterpret_cast<void*>(place), target) {}
|
||||
|
||||
detour::detour(void* place, void* target)
|
||||
{
|
||||
this->create(place, target);
|
||||
}
|
||||
detour::detour(void* place, void* target) { this->create(place, target); }
|
||||
|
||||
detour::~detour()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
detour::~detour() { this->clear(); }
|
||||
|
||||
void detour::enable() const
|
||||
{
|
||||
MH_EnableHook(this->place_);
|
||||
}
|
||||
void detour::enable() const { MH_EnableHook(this->place_); }
|
||||
|
||||
void detour::disable() const
|
||||
{
|
||||
MH_DisableHook(this->place_);
|
||||
}
|
||||
void detour::disable() const { MH_DisableHook(this->place_); }
|
||||
|
||||
void detour::create(void* place, void* target)
|
||||
{
|
||||
void detour::create(void* place, void* target) {
|
||||
this->clear();
|
||||
this->place_ = place;
|
||||
|
||||
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
|
||||
{
|
||||
throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_));
|
||||
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) {
|
||||
throw std::runtime_error(
|
||||
string::va("Unable to create hook at location: %p", this->place_));
|
||||
}
|
||||
|
||||
this->enable();
|
||||
}
|
||||
}
|
||||
|
||||
void detour::create(const size_t place, void* target)
|
||||
{
|
||||
void detour::create(const size_t place, void* target) {
|
||||
this->create(reinterpret_cast<void*>(place), target);
|
||||
}
|
||||
}
|
||||
|
||||
void detour::clear()
|
||||
{
|
||||
if (this->place_)
|
||||
{
|
||||
void detour::clear() {
|
||||
if (this->place_) {
|
||||
MH_RemoveHook(this->place_);
|
||||
}
|
||||
|
||||
this->place_ = nullptr;
|
||||
this->original_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void* detour::get_original() const
|
||||
{
|
||||
return this->original_;
|
||||
}
|
||||
void* detour::get_original() const { return this->original_; }
|
||||
|
||||
void nop(void* place, const size_t length)
|
||||
{
|
||||
void nop(void* place, const size_t length) {
|
||||
DWORD old_protect{};
|
||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
@ -94,15 +65,13 @@ namespace utils::hook
|
||||
|
||||
VirtualProtect(place, length, old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, length);
|
||||
}
|
||||
}
|
||||
|
||||
void nop(const size_t place, const size_t length)
|
||||
{
|
||||
void nop(const size_t place, const size_t length) {
|
||||
nop(reinterpret_cast<void*>(place), length);
|
||||
}
|
||||
}
|
||||
|
||||
void copy(void* place, const void* data, const size_t length)
|
||||
{
|
||||
void copy(void* place, const void* data, const size_t length) {
|
||||
DWORD old_protect{};
|
||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
@ -110,44 +79,39 @@ namespace utils::hook
|
||||
|
||||
VirtualProtect(place, length, old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, length);
|
||||
}
|
||||
}
|
||||
|
||||
void copy(const size_t place, const void* data, const size_t length)
|
||||
{
|
||||
void copy(const size_t place, const void* data, const size_t length) {
|
||||
copy(reinterpret_cast<void*>(place), data, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_relatively_far(const void* pointer, const void* data, const int offset)
|
||||
{
|
||||
bool is_relatively_far(const void* pointer, const void* data,
|
||||
const int offset) {
|
||||
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
||||
const auto small_diff = int32_t(diff);
|
||||
return diff != int64_t(small_diff);
|
||||
}
|
||||
}
|
||||
|
||||
void call(void* pointer, void* data)
|
||||
{
|
||||
if (is_relatively_far(pointer, data))
|
||||
{
|
||||
void call(void* pointer, void* data) {
|
||||
if (is_relatively_far(pointer, data)) {
|
||||
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||
}
|
||||
|
||||
auto* patch_pointer = PBYTE(pointer);
|
||||
set<uint8_t>(patch_pointer, 0xE8);
|
||||
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
||||
}
|
||||
set<int32_t>(patch_pointer + 1,
|
||||
int32_t(size_t(data) - (size_t(pointer) + 5)));
|
||||
}
|
||||
|
||||
void call(const size_t pointer, void* data)
|
||||
{
|
||||
void call(const size_t pointer, void* data) {
|
||||
return call(reinterpret_cast<void*>(pointer), data);
|
||||
}
|
||||
}
|
||||
|
||||
void call(const size_t pointer, const size_t data)
|
||||
{
|
||||
void call(const size_t pointer, const size_t data) {
|
||||
return call(pointer, reinterpret_cast<void*>(data));
|
||||
}
|
||||
}
|
||||
|
||||
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes)
|
||||
{
|
||||
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes) {
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
auto* place = reinterpret_cast<void*>(address);
|
||||
@ -155,10 +119,9 @@ namespace utils::hook
|
||||
memcpy(place, bytes.data(), bytes.size());
|
||||
VirtualProtect(place, bytes.size(), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, bytes.size());
|
||||
}
|
||||
}
|
||||
|
||||
void set(std::uintptr_t address, void* buffer, size_t size)
|
||||
{
|
||||
void set(std::uintptr_t address, void* buffer, size_t size) {
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
auto* place = reinterpret_cast<void*>(address);
|
||||
@ -166,30 +129,30 @@ namespace utils::hook
|
||||
memcpy(place, buffer, size);
|
||||
VirtualProtect(place, size, oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, size);
|
||||
}
|
||||
}
|
||||
|
||||
void jump(std::uintptr_t address, void* destination)
|
||||
{
|
||||
if (!address) return;
|
||||
void jump(std::uintptr_t address, void* destination) {
|
||||
if (!address)
|
||||
return;
|
||||
|
||||
std::uint8_t* bytes = new std::uint8_t[5];
|
||||
*bytes = 0xE9;
|
||||
*reinterpret_cast<std::uint32_t*>(bytes + 1) = CalculateRelativeJMPAddress(address, destination);
|
||||
*reinterpret_cast<std::uint32_t*>(bytes + 1) =
|
||||
CalculateRelativeJMPAddress(address, destination);
|
||||
|
||||
set(address, bytes, 5);
|
||||
|
||||
delete[] bytes;
|
||||
}
|
||||
|
||||
void redirect_jump(void* pointer, void* data)
|
||||
{
|
||||
char* operand_ptr = static_cast<char*>(pointer) + 2;
|
||||
int new_operand = reinterpret_cast<int>(data) - (reinterpret_cast<int>(pointer) + 6);
|
||||
set<int>(operand_ptr, new_operand);
|
||||
}
|
||||
|
||||
void redirect_jump(size_t pointer, void* data)
|
||||
{
|
||||
redirect_jump(reinterpret_cast<void*>(pointer), data);
|
||||
}
|
||||
}
|
||||
|
||||
void redirect_jump(void* pointer, void* data) {
|
||||
char* operand_ptr = static_cast<char*>(pointer) + 2;
|
||||
int new_operand =
|
||||
reinterpret_cast<int>(data) - (reinterpret_cast<int>(pointer) + 6);
|
||||
set<int>(operand_ptr, new_operand);
|
||||
}
|
||||
|
||||
void redirect_jump(size_t pointer, void* data) {
|
||||
redirect_jump(reinterpret_cast<void*>(pointer), data);
|
||||
}
|
||||
} // namespace utils::hook
|
||||
|
@ -1,27 +1,21 @@
|
||||
#pragma once
|
||||
#include "signature.hpp"
|
||||
|
||||
#define CalculateRelativeJMPAddress(X, Y) (((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||
#define CalculateRelativeJMPAddress(X, Y) \
|
||||
(((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
class detour
|
||||
{
|
||||
public:
|
||||
namespace utils::hook {
|
||||
class detour {
|
||||
public:
|
||||
detour() = default;
|
||||
detour(void* place, void* target);
|
||||
detour(size_t place, void* target);
|
||||
~detour();
|
||||
|
||||
detour(detour&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
detour(detour&& other) noexcept { this->operator=(std::move(other)); }
|
||||
|
||||
detour& operator= (detour&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
detour& operator=(detour&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~detour();
|
||||
|
||||
this->place_ = other.place_;
|
||||
@ -35,7 +29,7 @@ namespace utils::hook
|
||||
}
|
||||
|
||||
detour(const detour&) = delete;
|
||||
detour& operator= (const detour&) = delete;
|
||||
detour& operator=(const detour&) = delete;
|
||||
|
||||
void enable() const;
|
||||
void disable() const;
|
||||
@ -44,53 +38,45 @@ namespace utils::hook
|
||||
void create(size_t place, void* target);
|
||||
void clear();
|
||||
|
||||
template <typename T>
|
||||
T* get() const
|
||||
{
|
||||
template <typename T> T* get() const {
|
||||
return static_cast<T*>(this->get_original());
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(Args... args)
|
||||
{
|
||||
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||
template <typename T, typename... Args> T invoke(Args... args) {
|
||||
return static_cast<T (*)(Args...)>(this->get_original())(args...);
|
||||
}
|
||||
|
||||
[[nodiscard]] void* get_original() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
void* place_{};
|
||||
void* original_{};
|
||||
};
|
||||
};
|
||||
|
||||
void nop(void* place, size_t length);
|
||||
void nop(size_t place, size_t length);
|
||||
void nop(void* place, size_t length);
|
||||
void nop(size_t place, size_t length);
|
||||
|
||||
void copy(void* place, const void* data, size_t length);
|
||||
void copy(size_t place, const void* data, size_t length);
|
||||
void copy(void* place, const void* data, size_t length);
|
||||
void copy(size_t place, const void* data, size_t length);
|
||||
|
||||
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||
|
||||
void call(void* pointer, void* data);
|
||||
void call(size_t pointer, void* data);
|
||||
void call(size_t pointer, size_t data);
|
||||
void call(void* pointer, void* data);
|
||||
void call(size_t pointer, void* data);
|
||||
void call(size_t pointer, size_t data);
|
||||
|
||||
void jump(std::uintptr_t address, void* destination);
|
||||
void jump(std::uintptr_t address, void* destination);
|
||||
|
||||
void redirect_jump(void* pointer, void* data);
|
||||
void redirect_jump(size_t pointer, void* data);
|
||||
void redirect_jump(void* pointer, void* data);
|
||||
void redirect_jump(size_t pointer, void* data);
|
||||
|
||||
template <typename T>
|
||||
T extract(void* address)
|
||||
{
|
||||
template <typename T> T extract(void* address) {
|
||||
const auto data = static_cast<uint8_t*>(address);
|
||||
const auto offset = *reinterpret_cast<int32_t*>(data);
|
||||
return reinterpret_cast<T>(data + offset + 4);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(void* place, T value)
|
||||
{
|
||||
template <typename T> static void set(void* place, T value) {
|
||||
DWORD old_protect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
@ -98,23 +84,19 @@ namespace utils::hook
|
||||
|
||||
VirtualProtect(place, sizeof(T), old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(const size_t place, T value)
|
||||
{
|
||||
return set<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(size_t func, Args ... args)
|
||||
{
|
||||
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(void* func, Args ... args)
|
||||
{
|
||||
return static_cast<T(*)(Args ...)>(func)(args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> static void set(const size_t place, T value) {
|
||||
return set<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(size_t func, Args... args) {
|
||||
return reinterpret_cast<T (*)(Args...)>(func)(args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(void* func, Args... args) {
|
||||
return static_cast<T (*)(Args...)>(func)(args...);
|
||||
}
|
||||
} // namespace utils::hook
|
||||
|
@ -3,58 +3,44 @@
|
||||
#include "info_string.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
info_string::info_string(const std::string& buffer)
|
||||
{
|
||||
this->parse(buffer);
|
||||
}
|
||||
namespace utils {
|
||||
info_string::info_string(const std::string& buffer) { this->parse(buffer); }
|
||||
|
||||
info_string::info_string(const std::string_view& buffer)
|
||||
: info_string(std::string{buffer})
|
||||
{
|
||||
}
|
||||
info_string::info_string(const std::string_view& buffer)
|
||||
: info_string(std::string{buffer}) {}
|
||||
|
||||
void info_string::set(const std::string& key, const std::string& value)
|
||||
{
|
||||
void info_string::set(const std::string& key, const std::string& value) {
|
||||
this->key_value_pairs_[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
std::string info_string::get(const std::string& key) const
|
||||
{
|
||||
std::string info_string::get(const std::string& key) const {
|
||||
const auto value = this->key_value_pairs_.find(key);
|
||||
if (value != this->key_value_pairs_.end())
|
||||
{
|
||||
if (value != this->key_value_pairs_.end()) {
|
||||
return value->second;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void info_string::parse(std::string buffer)
|
||||
{
|
||||
if (buffer[0] == '\\')
|
||||
{
|
||||
void info_string::parse(std::string buffer) {
|
||||
if (buffer[0] == '\\') {
|
||||
buffer = buffer.substr(1);
|
||||
}
|
||||
|
||||
auto key_values = string::split(buffer, '\\');
|
||||
for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1); i += 2)
|
||||
{
|
||||
const auto key_values = string::split(buffer, '\\');
|
||||
for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1);
|
||||
i += 2) {
|
||||
const auto& key = key_values[i];
|
||||
const auto& value = key_values[i + 1];
|
||||
this->key_value_pairs_[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string info_string::build() const
|
||||
{
|
||||
//auto first = true;
|
||||
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)
|
||||
{
|
||||
//if (first) first = false;
|
||||
/*else*/ info_string.append("\\");
|
||||
for (auto i = this->key_value_pairs_.begin();
|
||||
i != this->key_value_pairs_.end(); ++i) {
|
||||
info_string.append("\\");
|
||||
|
||||
info_string.append(i->first); // Key
|
||||
info_string.append("\\");
|
||||
@ -62,5 +48,5 @@ namespace utils
|
||||
}
|
||||
|
||||
return info_string;
|
||||
}
|
||||
}
|
||||
} // namespace utils
|
||||
|
@ -3,22 +3,20 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class info_string
|
||||
{
|
||||
public:
|
||||
namespace utils {
|
||||
class info_string {
|
||||
public:
|
||||
info_string() = default;
|
||||
info_string(const std::string& buffer);
|
||||
info_string(const std::string_view& buffer);
|
||||
explicit info_string(const std::string& buffer);
|
||||
explicit info_string(const std::string_view& buffer);
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
std::string get(const std::string& key) const;
|
||||
std::string build() const;
|
||||
[[nodiscard]] std::string get(const std::string& key) const;
|
||||
[[nodiscard]] std::string build() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> key_value_pairs_{};
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> key_value_pairs_;
|
||||
|
||||
void parse(std::string buffer);
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace utils
|
||||
|
@ -3,154 +3,126 @@
|
||||
#include "memory.hpp"
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
memory::allocator memory::mem_allocator_;
|
||||
namespace utils {
|
||||
memory::allocator memory::mem_allocator_;
|
||||
|
||||
memory::allocator::~allocator()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
memory::allocator::~allocator() { this->clear(); }
|
||||
|
||||
void memory::allocator::clear()
|
||||
{
|
||||
void memory::allocator::clear() {
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
for (auto& data : this->pool_)
|
||||
{
|
||||
for (const auto& data : this->pool_) {
|
||||
memory::free(data);
|
||||
}
|
||||
|
||||
this->pool_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void memory::allocator::free(void* data)
|
||||
{
|
||||
void memory::allocator::free(void* data) {
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||
if (j != this->pool_.end())
|
||||
{
|
||||
if (j != this->pool_.end()) {
|
||||
memory::free(data);
|
||||
this->pool_.erase(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void memory::allocator::free(const void* data)
|
||||
{
|
||||
void memory::allocator::free(const void* data) {
|
||||
this->free(const_cast<void*>(data));
|
||||
}
|
||||
}
|
||||
|
||||
void* memory::allocator::allocate(const size_t length)
|
||||
{
|
||||
void* memory::allocator::allocate(const size_t length) {
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::allocate(length);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
bool memory::allocator::empty() const
|
||||
{
|
||||
return this->pool_.empty();
|
||||
}
|
||||
bool memory::allocator::empty() const { return this->pool_.empty(); }
|
||||
|
||||
char* memory::allocator::duplicate_string(const std::string& string)
|
||||
{
|
||||
char* memory::allocator::duplicate_string(const std::string& string) {
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::duplicate_string(string);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
void* memory::allocate(const size_t length)
|
||||
{
|
||||
return calloc(length, 1);
|
||||
}
|
||||
void* memory::allocate(const size_t length) { return calloc(length, 1); }
|
||||
|
||||
char* memory::duplicate_string(const std::string& string)
|
||||
{
|
||||
char* memory::duplicate_string(const std::string& string) {
|
||||
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||
std::memcpy(new_string, string.data(), string.size());
|
||||
return new_string;
|
||||
}
|
||||
}
|
||||
|
||||
void memory::free(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
void memory::free(void* data) {
|
||||
if (data) {
|
||||
::free(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void memory::free(const void* data)
|
||||
{
|
||||
free(const_cast<void*>(data));
|
||||
}
|
||||
void memory::free(const void* data) { free(const_cast<void*>(data)); }
|
||||
|
||||
bool memory::is_set(const void* mem, const char chr, const size_t length)
|
||||
{
|
||||
bool memory::is_set(const void* mem, const char chr, const size_t length) {
|
||||
const auto mem_arr = static_cast<const char*>(mem);
|
||||
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (mem_arr[i] != chr)
|
||||
{
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
if (mem_arr[i] != chr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool memory::is_bad_read_ptr(const void* ptr)
|
||||
{
|
||||
bool memory::is_bad_read_ptr(const void* ptr) {
|
||||
MEMORY_BASIC_INFORMATION mbi = {};
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||
{
|
||||
const DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ |
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi))) {
|
||||
const DWORD mask =
|
||||
(PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ |
|
||||
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
auto b = !(mbi.Protect & mask);
|
||||
// check the page is not a guard page
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
|
||||
b = true;
|
||||
|
||||
return b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool memory::is_bad_code_ptr(const void* ptr)
|
||||
{
|
||||
bool memory::is_bad_code_ptr(const void* ptr) {
|
||||
MEMORY_BASIC_INFORMATION mbi = {};
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||
{
|
||||
const DWORD mask = (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi))) {
|
||||
const DWORD mask =
|
||||
(PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
auto b = !(mbi.Protect & mask);
|
||||
// check the page is not a guard page
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
|
||||
b = true;
|
||||
|
||||
return b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool memory::is_rdata_ptr(void* pointer)
|
||||
{
|
||||
bool memory::is_rdata_ptr(void* ptr) {
|
||||
const std::string rdata = ".rdata";
|
||||
const auto pointer_lib = utils::nt::library::get_by_address(pointer);
|
||||
const auto pointer_lib = utils::nt::library::get_by_address(ptr);
|
||||
|
||||
for (const auto& section : pointer_lib.get_section_headers())
|
||||
{
|
||||
const auto size = sizeof(section->Name);
|
||||
for (const auto& section : pointer_lib.get_section_headers()) {
|
||||
constexpr auto size = sizeof(section->Name);
|
||||
char name[size + 1];
|
||||
name[size] = 0;
|
||||
std::memcpy(name, section->Name, size);
|
||||
|
||||
if (name == rdata)
|
||||
{
|
||||
const auto target = size_t(pointer);
|
||||
const size_t source_start = size_t(pointer_lib.get_ptr()) + section->PointerToRawData;
|
||||
if (name == rdata) {
|
||||
const auto target = size_t(ptr);
|
||||
const size_t source_start =
|
||||
size_t(pointer_lib.get_ptr()) + section->PointerToRawData;
|
||||
const size_t source_end = source_start + section->SizeOfRawData;
|
||||
|
||||
return target >= source_start && target <= source_end;
|
||||
@ -158,10 +130,7 @@ namespace utils
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
memory::allocator* memory::get_allocator()
|
||||
{
|
||||
return &memory::mem_allocator_;
|
||||
}
|
||||
}
|
||||
|
||||
memory::allocator* memory::get_allocator() { return &memory::mem_allocator_; }
|
||||
} // namespace utils
|
||||
|
@ -3,13 +3,10 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class memory final
|
||||
{
|
||||
public:
|
||||
class allocator final
|
||||
{
|
||||
namespace utils {
|
||||
class memory final {
|
||||
public:
|
||||
class allocator final {
|
||||
public:
|
||||
~allocator();
|
||||
|
||||
@ -21,15 +18,9 @@ namespace utils
|
||||
|
||||
void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate()
|
||||
{
|
||||
return this->allocate_array<T>(1);
|
||||
}
|
||||
template <typename T> T* allocate() { return this->allocate_array<T>(1); }
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
template <typename T> T* allocate_array(const size_t count = 1) {
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
@ -44,15 +35,9 @@ namespace utils
|
||||
|
||||
static void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate()
|
||||
{
|
||||
return allocate_array<T>(1);
|
||||
}
|
||||
template <typename T> static T* allocate() { return allocate_array<T>(1); }
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
template <typename T> static T* allocate_array(const size_t count = 1) {
|
||||
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
@ -69,7 +54,7 @@ namespace utils
|
||||
|
||||
static allocator* get_allocator();
|
||||
|
||||
private:
|
||||
private:
|
||||
static allocator mem_allocator_;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace utils
|
||||
|
270
src/utils/nt.cpp
270
src/utils/nt.cpp
@ -2,196 +2,181 @@
|
||||
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils::nt
|
||||
{
|
||||
library library::load(const std::string& name)
|
||||
{
|
||||
namespace utils::nt {
|
||||
library library::load(const std::string& name) {
|
||||
return library(LoadLibraryA(name.data()));
|
||||
}
|
||||
}
|
||||
|
||||
library library::load(const std::filesystem::path& path)
|
||||
{
|
||||
library library::load(const std::filesystem::path& path) {
|
||||
return library::load(path.generic_string());
|
||||
}
|
||||
}
|
||||
|
||||
library library::get_by_address(void* address)
|
||||
{
|
||||
library library::get_by_address(void* address) {
|
||||
HMODULE handle = nullptr;
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(address), &handle);
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
static_cast<LPCSTR>(address), &handle);
|
||||
return library(handle);
|
||||
}
|
||||
}
|
||||
|
||||
library::library()
|
||||
{
|
||||
this->module_ = GetModuleHandleA(nullptr);
|
||||
}
|
||||
library::library() { this->module_ = GetModuleHandleA(nullptr); }
|
||||
|
||||
library::library(const std::string& name)
|
||||
{
|
||||
library::library(const std::string& name) {
|
||||
this->module_ = GetModuleHandleA(name.data());
|
||||
}
|
||||
}
|
||||
|
||||
library::library(const HMODULE handle)
|
||||
{
|
||||
this->module_ = handle;
|
||||
}
|
||||
library::library(const HMODULE handle) { this->module_ = handle; }
|
||||
|
||||
bool library::operator==(const library& obj) const
|
||||
{
|
||||
bool library::operator==(const library& obj) const {
|
||||
return this->module_ == obj.module_;
|
||||
}
|
||||
}
|
||||
|
||||
library::operator bool() const
|
||||
{
|
||||
return this->is_valid();
|
||||
}
|
||||
library::operator bool() const { return this->is_valid(); }
|
||||
|
||||
library::operator HMODULE() const
|
||||
{
|
||||
return this->get_handle();
|
||||
}
|
||||
library::operator HMODULE() const { return this->get_handle(); }
|
||||
|
||||
PIMAGE_NT_HEADERS library::get_nt_headers() const
|
||||
{
|
||||
if (!this->is_valid()) return nullptr;
|
||||
return reinterpret_cast<PIMAGE_NT_HEADERS>(this->get_ptr() + this->get_dos_header()->e_lfanew);
|
||||
}
|
||||
PIMAGE_NT_HEADERS library::get_nt_headers() const {
|
||||
if (!this->is_valid())
|
||||
return nullptr;
|
||||
return reinterpret_cast<PIMAGE_NT_HEADERS>(this->get_ptr() +
|
||||
this->get_dos_header()->e_lfanew);
|
||||
}
|
||||
|
||||
PIMAGE_DOS_HEADER library::get_dos_header() const
|
||||
{
|
||||
PIMAGE_DOS_HEADER library::get_dos_header() const {
|
||||
return reinterpret_cast<PIMAGE_DOS_HEADER>(this->get_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
PIMAGE_OPTIONAL_HEADER library::get_optional_header() const
|
||||
{
|
||||
if (!this->is_valid()) return nullptr;
|
||||
PIMAGE_OPTIONAL_HEADER library::get_optional_header() const {
|
||||
if (!this->is_valid())
|
||||
return nullptr;
|
||||
return &this->get_nt_headers()->OptionalHeader;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PIMAGE_SECTION_HEADER> library::get_section_headers() const
|
||||
{
|
||||
std::vector<PIMAGE_SECTION_HEADER> library::get_section_headers() const {
|
||||
std::vector<PIMAGE_SECTION_HEADER> headers;
|
||||
|
||||
auto nt_headers = this->get_nt_headers();
|
||||
auto section = IMAGE_FIRST_SECTION(nt_headers);
|
||||
|
||||
for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section)
|
||||
{
|
||||
if (section) headers.push_back(section);
|
||||
else OutputDebugStringA("There was an invalid section :O");
|
||||
for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections;
|
||||
++i, ++section) {
|
||||
if (section)
|
||||
headers.push_back(section);
|
||||
else
|
||||
OutputDebugStringA("There was an invalid section :O");
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
|
||||
std::uint8_t* library::get_ptr() const
|
||||
{
|
||||
std::uint8_t* library::get_ptr() const {
|
||||
return reinterpret_cast<std::uint8_t*>(this->module_);
|
||||
}
|
||||
}
|
||||
|
||||
void library::unprotect() const
|
||||
{
|
||||
if (!this->is_valid()) return;
|
||||
void library::unprotect() const {
|
||||
if (!this->is_valid())
|
||||
return;
|
||||
|
||||
DWORD protection;
|
||||
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE,
|
||||
&protection);
|
||||
}
|
||||
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage,
|
||||
PAGE_EXECUTE_READWRITE, &protection);
|
||||
}
|
||||
|
||||
size_t library::get_relative_entry_point() const
|
||||
{
|
||||
if (!this->is_valid()) return 0;
|
||||
size_t library::get_relative_entry_point() const {
|
||||
if (!this->is_valid())
|
||||
return 0;
|
||||
return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint;
|
||||
}
|
||||
}
|
||||
|
||||
void* library::get_entry_point() const
|
||||
{
|
||||
if (!this->is_valid()) return nullptr;
|
||||
void* library::get_entry_point() const {
|
||||
if (!this->is_valid())
|
||||
return nullptr;
|
||||
return this->get_ptr() + this->get_relative_entry_point();
|
||||
}
|
||||
}
|
||||
|
||||
bool library::is_valid() const
|
||||
{
|
||||
return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE;
|
||||
}
|
||||
bool library::is_valid() const {
|
||||
return this->module_ != nullptr &&
|
||||
this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE;
|
||||
}
|
||||
|
||||
std::string library::get_name() const
|
||||
{
|
||||
if (!this->is_valid()) return "";
|
||||
std::string library::get_name() const {
|
||||
if (!this->is_valid())
|
||||
return "";
|
||||
|
||||
auto path = this->get_path();
|
||||
const auto pos = path.find_last_of("/\\");
|
||||
if (pos == std::string::npos) return path;
|
||||
if (pos == std::string::npos)
|
||||
return path;
|
||||
|
||||
return path.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string library::get_path() const
|
||||
{
|
||||
if (!this->is_valid()) return "";
|
||||
std::string library::get_path() const {
|
||||
if (!this->is_valid())
|
||||
return "";
|
||||
|
||||
char name[MAX_PATH] = {0};
|
||||
GetModuleFileNameA(this->module_, name, sizeof name);
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string library::get_folder() const
|
||||
{
|
||||
if (!this->is_valid()) return "";
|
||||
std::string library::get_folder() const {
|
||||
if (!this->is_valid())
|
||||
return "";
|
||||
|
||||
const auto path = std::filesystem::path(this->get_path());
|
||||
return path.parent_path().generic_string();
|
||||
}
|
||||
}
|
||||
|
||||
void library::free()
|
||||
{
|
||||
if (this->is_valid())
|
||||
{
|
||||
void library::free() {
|
||||
if (this->is_valid()) {
|
||||
FreeLibrary(this->module_);
|
||||
this->module_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HMODULE library::get_handle() const
|
||||
{
|
||||
return this->module_;
|
||||
}
|
||||
HMODULE library::get_handle() const { return this->module_; }
|
||||
|
||||
void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const
|
||||
{
|
||||
if (!this->is_valid()) return nullptr;
|
||||
void** library::get_iat_entry(const std::string& module_name,
|
||||
const std::string& proc_name) const {
|
||||
if (!this->is_valid())
|
||||
return nullptr;
|
||||
|
||||
const library other_module(module_name);
|
||||
if (!other_module.is_valid()) return nullptr;
|
||||
if (!other_module.is_valid())
|
||||
return nullptr;
|
||||
|
||||
auto* const target_function = other_module.get_proc<void*>(proc_name);
|
||||
if (!target_function) return nullptr;
|
||||
if (!target_function)
|
||||
return nullptr;
|
||||
|
||||
auto* header = this->get_optional_header();
|
||||
if (!header) return nullptr;
|
||||
if (!header)
|
||||
return nullptr;
|
||||
|
||||
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory
|
||||
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
|
||||
this->get_ptr() +
|
||||
header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||
|
||||
while (import_descriptor->Name)
|
||||
{
|
||||
if (!_stricmp(reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name), module_name.data()))
|
||||
{
|
||||
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->
|
||||
OriginalFirstThunk + this->get_ptr());
|
||||
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->
|
||||
get_ptr());
|
||||
while (import_descriptor->Name) {
|
||||
if (!_stricmp(
|
||||
reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name),
|
||||
module_name.data())) {
|
||||
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(
|
||||
import_descriptor->OriginalFirstThunk + this->get_ptr());
|
||||
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(
|
||||
import_descriptor->FirstThunk + this->get_ptr());
|
||||
|
||||
while (original_thunk_data->u1.AddressOfData)
|
||||
{
|
||||
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
||||
while (original_thunk_data->u1.AddressOfData) {
|
||||
const size_t ordinal_number =
|
||||
original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
||||
|
||||
if (ordinal_number > 0xFFFF) continue;
|
||||
if (ordinal_number > 0xFFFF)
|
||||
continue;
|
||||
|
||||
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) ==
|
||||
target_function)
|
||||
{
|
||||
if (GetProcAddress(other_module.module_,
|
||||
reinterpret_cast<char*>(ordinal_number)) ==
|
||||
target_function) {
|
||||
return reinterpret_cast<void**>(&thunk_data->u1.Function);
|
||||
}
|
||||
|
||||
@ -199,36 +184,36 @@ namespace utils::nt
|
||||
++thunk_data;
|
||||
}
|
||||
|
||||
//break;
|
||||
// break;
|
||||
}
|
||||
|
||||
++import_descriptor;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void raise_hard_exception()
|
||||
{
|
||||
void raise_hard_exception() {
|
||||
int data = false;
|
||||
const library ntdll("ntdll.dll");
|
||||
ntdll.invoke_pascal<void>("RtlAdjustPrivilege", 19, true, false, &data);
|
||||
ntdll.invoke_pascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data);
|
||||
}
|
||||
ntdll.invoke_pascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr,
|
||||
6, &data);
|
||||
}
|
||||
|
||||
std::string load_resource(const int id)
|
||||
{
|
||||
std::string load_resource(const int id) {
|
||||
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
|
||||
if (!res) return {};
|
||||
if (!res)
|
||||
return {};
|
||||
|
||||
auto* const handle = LoadResource(nullptr, res);
|
||||
if (!handle) return {};
|
||||
if (!handle)
|
||||
return {};
|
||||
|
||||
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
|
||||
}
|
||||
}
|
||||
|
||||
void relaunch_self()
|
||||
{
|
||||
void relaunch_self() {
|
||||
const utils::nt::library self;
|
||||
|
||||
STARTUPINFOA startup_info;
|
||||
@ -242,15 +227,16 @@ namespace utils::nt
|
||||
GetCurrentDirectoryA(sizeof(current_dir), current_dir);
|
||||
auto* const command_line = GetCommandLineA();
|
||||
|
||||
CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir,
|
||||
&startup_info, &process_info);
|
||||
CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false,
|
||||
NULL, nullptr, current_dir, &startup_info, &process_info);
|
||||
|
||||
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread);
|
||||
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess);
|
||||
}
|
||||
|
||||
void terminate(const uint32_t code)
|
||||
{
|
||||
TerminateProcess(GetCurrentProcess(), code);
|
||||
}
|
||||
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(process_info.hThread);
|
||||
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(process_info.hProcess);
|
||||
}
|
||||
|
||||
void terminate(const uint32_t code) {
|
||||
TerminateProcess(GetCurrentProcess(), code);
|
||||
}
|
||||
} // namespace utils::nt
|
||||
|
@ -12,15 +12,13 @@
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace utils::nt
|
||||
{
|
||||
class library final
|
||||
{
|
||||
public:
|
||||
namespace utils::nt {
|
||||
class library final {
|
||||
public:
|
||||
static library load(const std::string& name);
|
||||
static library load(const std::filesystem::path& path);
|
||||
static library get_by_address(void* address);
|
||||
@ -29,9 +27,7 @@ namespace utils::nt
|
||||
explicit library(const std::string& name);
|
||||
explicit library(HMODULE handle);
|
||||
|
||||
library(const library& a) : module_(a.module_)
|
||||
{
|
||||
}
|
||||
library(const library& a) : module_(a.module_) {}
|
||||
|
||||
bool operator!=(const library& obj) const { return !(*this == obj); };
|
||||
bool operator==(const library& obj) const;
|
||||
@ -52,41 +48,40 @@ namespace utils::nt
|
||||
|
||||
HMODULE get_handle() const;
|
||||
|
||||
template <typename T>
|
||||
T get_proc(const std::string& process) const
|
||||
{
|
||||
if (!this->is_valid()) T{};
|
||||
template <typename T> T get_proc(const std::string& process) const {
|
||||
if (!this->is_valid())
|
||||
T{};
|
||||
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::function<T> get(const std::string& process) const
|
||||
{
|
||||
if (!this->is_valid()) return std::function<T>();
|
||||
template <typename T> std::function<T> get(const std::string& process) const {
|
||||
if (!this->is_valid())
|
||||
return std::function<T>();
|
||||
return static_cast<T*>(this->get_proc<void*>(process));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(const std::string& process, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__cdecl)(Args ...)>(process);
|
||||
if (method) return method(args...);
|
||||
T invoke(const std::string& process, Args... args) const {
|
||||
auto method = this->get<T(__cdecl)(Args...)>(process);
|
||||
if (method)
|
||||
return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke_pascal(const std::string& process, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__stdcall)(Args ...)>(process);
|
||||
if (method) return method(args...);
|
||||
T invoke_pascal(const std::string& process, Args... args) const {
|
||||
auto method = this->get<T(__stdcall)(Args...)>(process);
|
||||
if (method)
|
||||
return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke_this(const std::string& process, void* this_ptr, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__thiscall)(void*, Args ...)>(this_ptr, process);
|
||||
if (method) return method(args...);
|
||||
T invoke_this(const std::string& process, void* this_ptr,
|
||||
Args... args) const {
|
||||
auto method = this->get<T(__thiscall)(void*, Args...)>(this_ptr, process);
|
||||
if (method)
|
||||
return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
@ -96,15 +91,16 @@ namespace utils::nt
|
||||
PIMAGE_DOS_HEADER get_dos_header() const;
|
||||
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
||||
|
||||
void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const;
|
||||
void** get_iat_entry(const std::string& module_name,
|
||||
const std::string& proc_name) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
HMODULE module_;
|
||||
};
|
||||
};
|
||||
|
||||
__declspec(noreturn) void raise_hard_exception();
|
||||
std::string load_resource(int id);
|
||||
__declspec(noreturn) void raise_hard_exception();
|
||||
std::string load_resource(int id);
|
||||
|
||||
void relaunch_self();
|
||||
__declspec(noreturn) void terminate(uint32_t code = 0);
|
||||
}
|
||||
void relaunch_self();
|
||||
__declspec(noreturn) void terminate(uint32_t code = 0);
|
||||
} // namespace utils::nt
|
||||
|
@ -5,41 +5,34 @@
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
void signature::load_pattern(const std::string& pattern)
|
||||
{
|
||||
namespace utils::hook {
|
||||
void signature::load_pattern(const std::string& pattern) {
|
||||
this->mask_.clear();
|
||||
this->pattern_.clear();
|
||||
|
||||
uint8_t nibble = 0;
|
||||
auto has_nibble = false;
|
||||
|
||||
for (auto val : pattern)
|
||||
{
|
||||
if (val == ' ') continue;
|
||||
if (val == '?')
|
||||
{
|
||||
for (auto val : pattern) {
|
||||
if (val == ' ')
|
||||
continue;
|
||||
if (val == '?') {
|
||||
this->mask_.push_back(val);
|
||||
this->pattern_.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((val < '0' || val > '9') && (val < 'A' || val > 'F') && (val < 'a' || val > 'f'))
|
||||
{
|
||||
} else {
|
||||
if ((val < '0' || val > '9') && (val < 'A' || val > 'F') &&
|
||||
(val < 'a' || val > 'f')) {
|
||||
throw std::runtime_error("Invalid pattern");
|
||||
}
|
||||
|
||||
char str[] = {val, 0};
|
||||
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
|
||||
const auto current_nibble =
|
||||
static_cast<uint8_t>(strtol(str, nullptr, 16));
|
||||
|
||||
if (!has_nibble)
|
||||
{
|
||||
if (!has_nibble) {
|
||||
has_nibble = true;
|
||||
nibble = current_nibble;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
has_nibble = false;
|
||||
const uint8_t byte = current_nibble | (nibble << 4);
|
||||
|
||||
@ -49,107 +42,99 @@ namespace utils::hook
|
||||
}
|
||||
}
|
||||
|
||||
while (!this->mask_.empty() && this->mask_.back() == '?')
|
||||
{
|
||||
while (!this->mask_.empty() && this->mask_.back() == '?') {
|
||||
this->mask_.pop_back();
|
||||
this->pattern_.pop_back();
|
||||
}
|
||||
|
||||
if (this->has_sse_support())
|
||||
{
|
||||
while (this->pattern_.size() < 16)
|
||||
{
|
||||
if (this->has_sse_support()) {
|
||||
while (this->pattern_.size() < 16) {
|
||||
this->pattern_.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_nibble)
|
||||
{
|
||||
if (has_nibble) {
|
||||
throw std::runtime_error("Invalid pattern");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> signature::process_range(uint8_t* start, const size_t length) const
|
||||
{
|
||||
if (this->has_sse_support()) return this->process_range_vectorized(start, length);
|
||||
std::vector<size_t> signature::process_range(uint8_t* start,
|
||||
const size_t length) const {
|
||||
if (this->has_sse_support())
|
||||
return this->process_range_vectorized(start, length);
|
||||
return this->process_range_linear(start, length);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> signature::process_range_linear(uint8_t* start, const size_t length) const
|
||||
{
|
||||
std::vector<size_t> signature::process_range_linear(uint8_t* start,
|
||||
const size_t length) const {
|
||||
std::vector<size_t> result;
|
||||
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const auto address = start + i;
|
||||
|
||||
size_t j = 0;
|
||||
for (; j < this->mask_.size(); ++j)
|
||||
{
|
||||
if (this->mask_[j] != '?' && this->pattern_[j] != address[j])
|
||||
{
|
||||
for (; j < this->mask_.size(); ++j) {
|
||||
if (this->mask_[j] != '?' && this->pattern_[j] != address[j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == this->mask_.size())
|
||||
{
|
||||
if (j == this->mask_.size()) {
|
||||
result.push_back(size_t(address));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> signature::process_range_vectorized(uint8_t* start, const size_t length) const
|
||||
{
|
||||
std::vector<size_t>
|
||||
signature::process_range_vectorized(uint8_t* start, const size_t length) const {
|
||||
std::vector<size_t> result;
|
||||
__declspec(align(16)) char desired_mask[16] = {0};
|
||||
|
||||
for (size_t i = 0; i < this->mask_.size(); i++)
|
||||
{
|
||||
for (size_t i = 0; i < this->mask_.size(); i++) {
|
||||
desired_mask[i / 8] |= (this->mask_[i] == '?' ? 0 : 1) << i % 8;
|
||||
}
|
||||
|
||||
const auto mask = _mm_load_si128(reinterpret_cast<const __m128i*>(desired_mask));
|
||||
const auto comparand = _mm_loadu_si128(reinterpret_cast<const __m128i*>(this->pattern_.data()));
|
||||
const auto mask =
|
||||
_mm_load_si128(reinterpret_cast<const __m128i*>(desired_mask));
|
||||
const auto comparand =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(this->pattern_.data()));
|
||||
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const auto address = start + i;
|
||||
const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
|
||||
const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
|
||||
const auto value =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
|
||||
const auto comparison =
|
||||
_mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
|
||||
_SIDD_CMP_EQUAL_EACH);
|
||||
|
||||
const auto matches = _mm_and_si128(mask, comparison);
|
||||
const auto equivalence = _mm_xor_si128(mask, matches);
|
||||
|
||||
if (_mm_test_all_zeros(equivalence, equivalence))
|
||||
{
|
||||
if (_mm_test_all_zeros(equivalence, equivalence)) {
|
||||
result.push_back(size_t(address));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
signature::signature_result signature::process() const
|
||||
{
|
||||
signature::signature_result signature::process() const {
|
||||
const auto range = this->length_ - this->mask_.size();
|
||||
const auto cores = std::max(1u, std::thread::hardware_concurrency());
|
||||
|
||||
if (range <= cores * 10ull) return this->process_serial();
|
||||
if (range <= cores * 10ull)
|
||||
return this->process_serial();
|
||||
return this->process_parallel();
|
||||
}
|
||||
}
|
||||
|
||||
signature::signature_result signature::process_serial() const
|
||||
{
|
||||
signature::signature_result signature::process_serial() const {
|
||||
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
|
||||
return {this->process_range(this->start_, this->length_ - sub)};
|
||||
}
|
||||
}
|
||||
|
||||
signature::signature_result signature::process_parallel() const
|
||||
{
|
||||
signature::signature_result signature::process_parallel() const {
|
||||
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
|
||||
const auto range = this->length_ - sub;
|
||||
const auto cores = std::max(1u, std::thread::hardware_concurrency() / 2);
|
||||
@ -160,54 +145,48 @@ namespace utils::hook
|
||||
std::vector<size_t> result;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for (auto i = 0u; i < cores; ++i)
|
||||
{
|
||||
for (auto i = 0u; i < cores; ++i) {
|
||||
const auto start = this->start_ + (grid * i);
|
||||
const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
|
||||
threads.emplace_back([&, start, length]()
|
||||
{
|
||||
const auto length =
|
||||
(i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
|
||||
threads.emplace_back([&, start, length]() {
|
||||
auto local_result = this->process_range(start, length);
|
||||
if (local_result.empty()) return;
|
||||
if (local_result.empty())
|
||||
return;
|
||||
|
||||
std::lock_guard _(mutex);
|
||||
for (const auto& address : local_result)
|
||||
{
|
||||
for (const auto& address : local_result) {
|
||||
result.push_back(address);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : threads)
|
||||
{
|
||||
if (t.joinable())
|
||||
{
|
||||
for (auto& t : threads) {
|
||||
if (t.joinable()) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(result.begin(), result.end());
|
||||
return {std::move(result)};
|
||||
}
|
||||
}
|
||||
|
||||
bool signature::has_sse_support() const
|
||||
{
|
||||
if (this->mask_.size() <= 16)
|
||||
{
|
||||
bool signature::has_sse_support() const {
|
||||
if (this->mask_.size() <= 16) {
|
||||
int cpu_id[4];
|
||||
__cpuid(cpu_id, 0);
|
||||
|
||||
if (cpu_id[0] >= 1)
|
||||
{
|
||||
if (cpu_id[0] >= 1) {
|
||||
__cpuidex(cpu_id, 1, 0);
|
||||
return (cpu_id[2] & (1 << 20)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace utils::hook
|
||||
|
||||
utils::hook::signature::signature_result operator"" _sig(const char* str, const size_t len)
|
||||
{
|
||||
utils::hook::signature::signature_result operator"" _sig(const char* str,
|
||||
const size_t len) {
|
||||
return utils::hook::signature(std::string(str, len)).process();
|
||||
}
|
||||
|
@ -2,56 +2,43 @@
|
||||
#include "nt.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
class signature final
|
||||
{
|
||||
namespace utils::hook {
|
||||
class signature final {
|
||||
public:
|
||||
class signature_result {
|
||||
public:
|
||||
class signature_result
|
||||
{
|
||||
public:
|
||||
signature_result(std::vector<size_t>&& matches) : matches_(std::move(matches))
|
||||
{
|
||||
}
|
||||
signature_result(std::vector<size_t>&& matches)
|
||||
: matches_(std::move(matches)) {}
|
||||
|
||||
[[nodiscard]] uint8_t* get(const size_t index) const
|
||||
{
|
||||
if (index >= this->count())
|
||||
{
|
||||
[[nodiscard]] uint8_t* get(const size_t index) const {
|
||||
if (index >= this->count()) {
|
||||
throw std::runtime_error("Invalid index");
|
||||
}
|
||||
|
||||
return reinterpret_cast<uint8_t*>(this->matches_[index]);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t count() const
|
||||
{
|
||||
return this->matches_.size();
|
||||
}
|
||||
[[nodiscard]] size_t count() const { return this->matches_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<size_t> matches_;
|
||||
};
|
||||
|
||||
explicit signature(const std::string& pattern, const nt::library library = {})
|
||||
: signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
|
||||
{
|
||||
}
|
||||
: signature(pattern, library.get_ptr(),
|
||||
library.get_optional_header()->SizeOfImage) {}
|
||||
|
||||
signature(const std::string& pattern, void* start, void* end)
|
||||
: signature(pattern, start, size_t(end) - size_t(start))
|
||||
{
|
||||
}
|
||||
: signature(pattern, start, size_t(end) - size_t(start)) {}
|
||||
|
||||
signature(const std::string& pattern, void* start, const size_t length)
|
||||
: start_(static_cast<uint8_t*>(start)), length_(length)
|
||||
{
|
||||
: start_(static_cast<uint8_t*>(start)), length_(length) {
|
||||
this->load_pattern(pattern);
|
||||
}
|
||||
|
||||
signature_result process() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
std::string mask_;
|
||||
std::basic_string<uint8_t> pattern_;
|
||||
|
||||
@ -64,10 +51,12 @@ namespace utils::hook
|
||||
signature_result process_serial() const;
|
||||
std::vector<size_t> process_range(uint8_t* start, size_t length) const;
|
||||
std::vector<size_t> process_range_linear(uint8_t* start, size_t length) const;
|
||||
std::vector<size_t> process_range_vectorized(uint8_t* start, size_t length) const;
|
||||
std::vector<size_t> process_range_vectorized(uint8_t* start,
|
||||
size_t length) const;
|
||||
|
||||
bool has_sse_support() const;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace utils::hook
|
||||
|
||||
utils::hook::signature::signature_result operator"" _sig(const char* str, size_t len);
|
||||
utils::hook::signature::signature_result operator"" _sig(const char* str,
|
||||
size_t len);
|
||||
|
@ -1,16 +1,14 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "string.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
const char* va(const char* fmt, ...)
|
||||
{
|
||||
namespace utils::string {
|
||||
const char* va(const char* fmt, ...) {
|
||||
static thread_local va_provider<8, 256> provider;
|
||||
|
||||
va_list ap;
|
||||
@ -20,61 +18,52 @@ namespace utils::string
|
||||
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const char delim)
|
||||
{
|
||||
std::vector<std::string> split(const std::string& s, const char delim) {
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
std::vector<std::string> elems;
|
||||
|
||||
while (std::getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
|
||||
while (std::getline(ss, item, delim)) {
|
||||
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11
|
||||
// (based on comment from @mchiasson)
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_lower(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||
{
|
||||
std::string to_lower(std::string text) {
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input) {
|
||||
return static_cast<char>(tolower(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_upper(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||
{
|
||||
std::string to_upper(std::string text) {
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input) {
|
||||
return static_cast<char>(toupper(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
bool starts_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
bool starts_with(const std::string& text, const std::string& substring) {
|
||||
return text.find(substring) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ends_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
if (substring.size() > text.size()) return false;
|
||||
bool ends_with(const std::string& text, const std::string& substring) {
|
||||
if (substring.size() > text.size())
|
||||
return false;
|
||||
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
|
||||
}
|
||||
}
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||
{
|
||||
std::string dump_hex(const std::string& data, const std::string& separator) {
|
||||
std::string result;
|
||||
|
||||
for (unsigned int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
for (unsigned int i = 0; i < data.size(); ++i) {
|
||||
if (i > 0) {
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
@ -82,20 +71,16 @@ namespace utils::string
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_clipboard_data()
|
||||
{
|
||||
if (OpenClipboard(nullptr))
|
||||
{
|
||||
std::string get_clipboard_data() {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
std::string data;
|
||||
|
||||
auto* const clipboard_data = GetClipboardData(1u);
|
||||
if (clipboard_data)
|
||||
{
|
||||
if (clipboard_data) {
|
||||
auto* const cliptext = static_cast<char*>(GlobalLock(clipboard_data));
|
||||
if (cliptext)
|
||||
{
|
||||
if (cliptext) {
|
||||
data.append(cliptext);
|
||||
GlobalUnlock(clipboard_data);
|
||||
}
|
||||
@ -105,82 +90,42 @@ namespace utils::string
|
||||
return data;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void strip(const char* in, char* out, int max)
|
||||
{
|
||||
if (!in || !out) return;
|
||||
|
||||
max--;
|
||||
auto current = 0;
|
||||
while (*in != 0 && current < max)
|
||||
{
|
||||
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
|
||||
|
||||
if (*in == '^' && (color_index != 7 || *(in + 1) == '7'))
|
||||
{
|
||||
++in;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out = *in;
|
||||
++out;
|
||||
++current;
|
||||
}
|
||||
|
||||
++in;
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
std::string convert(const std::wstring& wstr)
|
||||
{
|
||||
std::string convert(const std::wstring& wstr) {
|
||||
std::string result;
|
||||
result.reserve(wstr.size());
|
||||
|
||||
for (const auto& chr : wstr)
|
||||
{
|
||||
for (const auto& chr : wstr) {
|
||||
result.push_back(static_cast<char>(chr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring convert(const std::string& str)
|
||||
{
|
||||
std::wstring convert(const std::string& str) {
|
||||
std::wstring result;
|
||||
result.reserve(str.size());
|
||||
|
||||
for (const auto& chr : str)
|
||||
{
|
||||
for (const auto& chr : str) {
|
||||
result.push_back(static_cast<wchar_t>(chr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty())
|
||||
{
|
||||
std::string replace(std::string str, const std::string& from,
|
||||
const std::string& to) {
|
||||
if (from.empty()) {
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
||||
{
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool contains(const std::string& str1, const std::string& str2)
|
||||
{
|
||||
return str1.find(str2) != std::string::npos;
|
||||
}
|
||||
}
|
||||
} // namespace utils::string
|
||||
|
@ -1,39 +1,29 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#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:
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0,
|
||||
"Buffers and MinBufferSize mustn't be 0");
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
template <size_t Buffers, size_t MinBufferSize>
|
||||
class va_provider final
|
||||
{
|
||||
public:
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||
va_provider() : current_buffer_(0) {}
|
||||
|
||||
va_provider() : current_buffer_(0)
|
||||
{
|
||||
}
|
||||
|
||||
char* get(const char* format, const va_list ap)
|
||||
{
|
||||
char* get(const char* format, const va_list ap) {
|
||||
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
|
||||
auto entry = &this->string_pool_[this->current_buffer_];
|
||||
|
||||
if (!entry->size || !entry->buffer)
|
||||
{
|
||||
if (!entry->size || !entry->buffer) {
|
||||
throw std::runtime_error("String pool not initialized");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return nullptr; // Error
|
||||
while (true) {
|
||||
const int res =
|
||||
vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0)
|
||||
break; // Success
|
||||
if (res == 0)
|
||||
return nullptr; // Error
|
||||
|
||||
entry->double_size();
|
||||
}
|
||||
@ -41,31 +31,31 @@ namespace utils::string
|
||||
return entry->buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
class entry final
|
||||
{
|
||||
private:
|
||||
class entry final {
|
||||
public:
|
||||
explicit entry(const size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||
{
|
||||
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||
explicit entry(const size_t _size = MinBufferSize)
|
||||
: size(_size), buffer(nullptr) {
|
||||
if (this->size < MinBufferSize)
|
||||
this->size = MinBufferSize;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
~entry()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
~entry() {
|
||||
if (this->buffer)
|
||||
memory::get_allocator()->free(this->buffer);
|
||||
this->size = 0;
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1);
|
||||
void allocate() {
|
||||
if (this->buffer)
|
||||
memory::get_allocator()->free(this->buffer);
|
||||
this->buffer =
|
||||
memory::get_allocator()->allocate_array<char>(this->size + 1);
|
||||
}
|
||||
|
||||
void double_size()
|
||||
{
|
||||
void double_size() {
|
||||
this->size *= 2;
|
||||
this->allocate();
|
||||
}
|
||||
@ -76,27 +66,25 @@ namespace utils::string
|
||||
|
||||
size_t current_buffer_;
|
||||
entry string_pool_[Buffers];
|
||||
};
|
||||
};
|
||||
|
||||
const char* va(const char* fmt, ...);
|
||||
const char* va(const char* fmt, ...);
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delim);
|
||||
std::vector<std::string> split(const std::string& s, char delim);
|
||||
|
||||
std::string to_lower(std::string text);
|
||||
std::string to_upper(std::string text);
|
||||
bool starts_with(const std::string& text, const std::string& substring);
|
||||
bool ends_with(const std::string& text, const std::string& substring);
|
||||
std::string to_lower(std::string text);
|
||||
std::string to_upper(std::string text);
|
||||
bool starts_with(const std::string& text, const std::string& substring);
|
||||
bool ends_with(const std::string& text, const std::string& substring);
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||
std::string dump_hex(const std::string& data,
|
||||
const std::string& separator = " ");
|
||||
|
||||
std::string get_clipboard_data();
|
||||
std::string get_clipboard_data();
|
||||
|
||||
void strip(const char* in, char* out, int max);
|
||||
std::string convert(const std::wstring& wstr);
|
||||
std::wstring convert(const std::string& str);
|
||||
|
||||
std::string convert(const std::wstring& wstr);
|
||||
std::wstring convert(const std::string& str);
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to);
|
||||
|
||||
bool contains(const std::string& str1, const std::string& str2);
|
||||
}
|
||||
std::string replace(std::string str, const std::string& from,
|
||||
const std::string& to);
|
||||
} // namespace utils::string
|
||||
|
Loading…
x
Reference in New Issue
Block a user