Clang format victory royale

This commit is contained in:
6arelyFuture 2022-03-08 21:41:21 +00:00
parent b4fa5527ff
commit f3de05f065
No known key found for this signature in database
GPG Key ID: E883E2BC9657D955
36 changed files with 2481 additions and 2803 deletions

11
.clang-format Normal file
View File

@ -0,0 +1,11 @@
---
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
View 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@v2
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.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=x86 build/black-ops-plugin.sln
- name: Upload ${{matrix.configuration}} binaries
uses: actions/upload-artifact@v2
with:
name: ${{matrix.configuration}} binaries
path: |
build/bin/x86/${{matrix.configuration}}/black-ops-plugin.dll
build/bin/x86/${{matrix.configuration}}/black-ops-plugin.pdb

View File

@ -11,5 +11,5 @@ This software has been created purely for the purposes of academic research. It
## Compile from source ## Compile from source
- Clone the Git repo. Do NOT download it as ZIP, that won't work. - 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`. - Update the submodules and run `premake5 vs2022` or simply use the delivered `generate.bat`.
- Build via solution file found inside the build folder. - Build via solution file found inside the build folder.

View File

@ -1,4 +1,4 @@
@echo off @echo off
echo Updating submodules... echo Updating submodules...
call git submodule update --init --recursive call git submodule update --init --recursive
tools\premake5 %* vs2019 tools\premake5 %* vs2022

View File

@ -3,41 +3,43 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "utils/hook.hpp" #include "utils/hook.hpp"
namespace ban namespace ban {
{ namespace {
namespace const game::dvar_t* sv_discord = nullptr;
{ const game::dvar_t* sv_clanWebsite = nullptr;
const game::dvar_t* sv_discord = nullptr;
const game::dvar_t* sv_clanWebsite = nullptr;
bool out_of_band_print_hk(game::netsrc_t src, game::netadr_s to, const char* msg) bool out_of_band_print_hk(game::netsrc_t src, game::netadr_s to,
{ const char* msg) {
// Proof of concept patch. Please ignore // Proof of concept patch. Please ignore
if (msg != "error\nPATCH_BANNED_FROM_SERVER"s) if (msg != "error\nPATCH_BANNED_FROM_SERVER"s) {
{ return game::NET_OutOfBandPrint(src, to, msg);
return game::NET_OutOfBandPrint(src, to, msg); }
}
const auto errorMsg = std::format("error\nPermanently banned\nDiscord: {}\nWebsite: {}", const auto errorMsg =
sv_discord->current.string, sv_clanWebsite->current.string); std::format("error\nPermanently banned\nDiscord: {}\nWebsite: {}",
sv_discord->current.string, sv_clanWebsite->current.string);
return game::NET_OutOfBandPrint(src, to, errorMsg.data()); return game::NET_OutOfBandPrint(src, to, errorMsg.data());
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::current == game::gamemode::zombies) return;
sv_discord = game::Dvar_RegisterString("sv_discord", "https://www.discord.gg/", game::DVAR_FLAG_SAVED, "Discord invitation link");
sv_clanWebsite = game::Dvar_RegisterString("sv_clanWebsite", "https://www.google.com/", game::DVAR_FLAG_SAVED, "Website link");
utils::hook::call(0x48B7E2, out_of_band_print_hk);
}
};
} }
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
if (game::current == game::gamemode::zombies)
return;
sv_discord = game::Dvar_RegisterString(
"sv_discord", "https://www.discord.gg/", game::DVAR_ARCHIVE,
"Discord invitation link");
sv_clanWebsite =
game::Dvar_RegisterString("sv_clanWebsite", "https://www.google.com/",
game::DVAR_ARCHIVE, "Website link");
utils::hook::call(0x48B7E2, out_of_band_print_hk);
}
};
} // namespace ban
REGISTER_COMPONENT(ban::component) REGISTER_COMPONENT(ban::component)

View File

@ -5,97 +5,93 @@
#include "utils/hook.hpp" #include "utils/hook.hpp"
#include "utils/io.hpp" #include "utils/io.hpp"
namespace bots namespace bots {
{ namespace {
namespace typedef std::pair<std::string, std::string> bot_entry;
{
typedef std::pair<std::string, std::string> bot_entry;
std::vector<bot_entry> bot_names; std::vector<bot_entry> bot_names;
utils::hook::detour sv_bot_name_random_hook; utils::hook::detour sv_bot_name_random_hook;
// Json file is expected to contain a key for the bot's name. Value should be a string for the clantag // Json file is expected to contain a key for the bot's name. Value should be a
void load_bot_data() // string for the clantag
{ void load_bot_data() {
const auto path = game::Dvar_FindVar("fs_homepath")->current.string; const auto path = game::Dvar_FindVar("fs_homepath")->current.string;
std::filesystem::current_path(path); std::filesystem::current_path(path);
if (!utils::io::file_exists("bots/bots.json")) if (!utils::io::file_exists("bots/bots.json")) {
{ game::Com_Printf(game::CON_CHANNEL_SERVER, "bots.json was not found\n");
game::Com_Printf(game::CON_CHANNEL_SERVER, "bots.json was not found\n"); return;
return; }
}
rapidjson::Document obj; rapidjson::Document obj;
rapidjson::ParseResult result = obj.Parse(utils::io::read_file("bots/bots.json").data()); rapidjson::ParseResult result =
obj.Parse(utils::io::read_file("bots/bots.json").data());
if (!result || !obj.IsObject()) if (!result || !obj.IsObject()) {
{ game::Com_Printf(game::CON_CHANNEL_SERVER,
game::Com_Printf(game::CON_CHANNEL_SERVER, "Failed to parse ban file. Empty?\n"); "Failed to parse ban file. Empty?\n");
return; return;
} }
for (rapidjson::Value::ConstMemberIterator itr = obj.MemberBegin(); for (rapidjson::Value::ConstMemberIterator itr = obj.MemberBegin();
itr != obj.MemberEnd(); ++itr) itr != obj.MemberEnd(); ++itr) {
{ if (itr->value.GetType() == rapidjson::Type::kStringType) {
if (itr->value.GetType() == rapidjson::Type::kStringType) bot_names.emplace_back(
{ std::make_pair(itr->name.GetString(), itr->value.GetString()));
bot_names.emplace_back(std::make_pair(itr->name.GetString(), itr->value.GetString())); }
} }
}
}
const char* sv_bot_name_random_stub()
{
if (bot_names.empty())
{
load_bot_data();
}
static auto bot_id = 0;
if (!bot_names.empty())
{
bot_id %= bot_names.size();
const auto& entry = bot_names.at(bot_id++);
return entry.first.data();
}
return sv_bot_name_random_hook.invoke<const char*>();
}
int build_connect_string(char* buf, const char* connect_string, const char* name, const char* xuid,
int protocol, int port)
{
// Default
auto clan_tag = "3arc"s;
for (const auto& entry : bot_names)
{
if (entry.first == name)
{
clan_tag = entry.second;
break;
}
}
return _snprintf_s(buf, 0x400, _TRUNCATE, connect_string, name,
clan_tag.data(), xuid, protocol, port);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::current == game::gamemode::zombies) return;
utils::hook::set<const char*>(0x6B6294, "connect \"\\cg_predictItems\\1\\cl_punkbuster\\0\\cl_anonymous\\0\\color\\4\\head\\"
"default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%s\\bdOnlineUserID\\%s\\protocol\\%d\\qport\\%d\"");
sv_bot_name_random_hook.create(0x49ED80, &sv_bot_name_random_stub);
utils::hook::call(0x6B6299, build_connect_string);
}
};
} }
const char* sv_bot_name_random_stub() {
if (bot_names.empty()) {
load_bot_data();
}
static auto bot_id = 0;
if (!bot_names.empty()) {
bot_id %= bot_names.size();
const auto& entry = bot_names.at(bot_id++);
return entry.first.data();
}
return sv_bot_name_random_hook.invoke<const char*>();
}
int build_connect_string(char* buf, const char* connect_string,
const char* name, const char* xuid, int protocol,
int port) {
// Default
auto clan_tag = "3arc"s;
for (const auto& entry : bot_names) {
if (entry.first == name) {
clan_tag = entry.second;
break;
}
}
return _snprintf_s(buf, 0x400, _TRUNCATE, connect_string, name,
clan_tag.data(), xuid, protocol, port);
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
if (game::current == game::gamemode::zombies)
return;
utils::hook::set<const char*>(
0x6B6294,
"connect "
"\"\\cg_predictItems\\1\\cl_punkbuster\\0\\cl_"
"anonymous\\0\\color\\4\\head\\"
"default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%"
"s\\bdOnlineUserID\\%s\\protocol\\%d\\qport\\%d\"");
sv_bot_name_random_hook.create(0x49ED80, &sv_bot_name_random_stub);
utils::hook::call(0x6B6299, build_connect_string);
}
};
} // namespace bots
REGISTER_COMPONENT(bots::component) REGISTER_COMPONENT(bots::component)

View File

@ -6,142 +6,128 @@
#include "command.hpp" #include "command.hpp"
namespace chat namespace chat {
{ namespace {
namespace std::mutex chat_mutex;
{ std::unordered_set<std::uint64_t> mute_list{};
std::mutex chat_mutex;
std::unordered_set<std::uint64_t> mute_list{};
void mute_player(const game::client_s* cl) void mute_player(const game::client_s* cl) {
{ std::unique_lock<std::mutex> _(chat_mutex);
std::unique_lock<std::mutex> _(chat_mutex); if (mute_list.contains(cl->xuid)) {
if (mute_list.contains(cl->xuid)) game::SV_GameSendServerCommand(
{ -1, game::SV_CMD_CAN_IGNORE,
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s is already muted\"", 0x65, cl->name));
utils::string::va("%c \"%s is already muted\"", 0x65, cl->name)); return;
return; }
}
mute_list.insert(cl->xuid); mute_list.insert(cl->xuid);
}
void unmute_player(const game::client_s* cl)
{
std::unique_lock<std::mutex> _(chat_mutex);
mute_list.erase(cl->xuid);
game::SV_GameSendServerCommand(cl->gentity->entnum, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You were unmuted\"", 0x65));
}
void client_command(int clientNumber)
{
char buf[1024] = {0};
if (game::g_entities[clientNumber].client == nullptr)
{
// Not in game
return;
}
game::SV_Cmd_ArgvBuffer(0, buf, sizeof(buf));
std::unique_lock<std::mutex> _(chat_mutex);
if (utils::string::starts_with(buf, "say") &&
mute_list.contains(game::svs_clients[clientNumber].xuid))
{
game::SV_GameSendServerCommand(clientNumber, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You are muted\"", 0x65));
return;
}
game::ClientCommand(clientNumber);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
utils::hook::call(SELECT(0x58DA1C, 0x4FB3BD), client_command);
add_chat_commands();
}
private:
static void add_chat_commands()
{
command::add("sayAs", [](const command::params& params)
{
if (params.size() < 3)
{
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: sayAs <client number> <message>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
auto* gentity = client->gentity;
assert(gentity != nullptr);
if (gentity->client == nullptr)
return;
const auto message = params.join(2);
game::G_Say(gentity, nullptr, 0, message.data());
});
command::add("mutePlayer", [](const command::params& params)
{
if (params.size() < 2)
{
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: mutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
mute_player(client);
});
command::add("unmutePlayer", [](const command::params& params)
{
if (params.size() < 2)
{
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: unmutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
unmute_player(client);
});
}
};
} }
void unmute_player(const game::client_s* cl) {
std::unique_lock<std::mutex> _(chat_mutex);
mute_list.erase(cl->xuid);
game::SV_GameSendServerCommand(
cl->gentity->entnum, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You were unmuted\"", 0x65));
}
void client_command(int clientNumber) {
char buf[1024] = {0};
if (game::g_entities[clientNumber].client == nullptr) {
// Not in game
return;
}
game::SV_Cmd_ArgvBuffer(0, buf, sizeof(buf));
std::unique_lock<std::mutex> _(chat_mutex);
if (utils::string::starts_with(buf, "say") &&
mute_list.contains(game::svs_clients[clientNumber].xuid)) {
game::SV_GameSendServerCommand(
clientNumber, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You are muted\"", 0x65));
return;
}
game::ClientCommand(clientNumber);
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
utils::hook::call(SELECT(0x58DA1C, 0x4FB3BD), client_command);
add_chat_commands();
}
private:
static void add_chat_commands() {
command::add("sayAs", [](const command::params& params) {
if (params.size() < 3) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: sayAs <client number> <message>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
auto* gentity = client->gentity;
assert(gentity != nullptr);
if (gentity->client == nullptr)
return;
const auto message = params.join(2);
game::G_Say(gentity, nullptr, 0, message.data());
});
command::add("mutePlayer", [](const command::params& params) {
if (params.size() < 2) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: mutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
mute_player(client);
});
command::add("unmutePlayer", [](const command::params& params) {
if (params.size() < 2) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: unmutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
unmute_player(client);
});
}
};
} // namespace chat
REGISTER_COMPONENT(chat::component) REGISTER_COMPONENT(chat::component)

View File

@ -9,129 +9,100 @@
constexpr auto CMD_MAX_NESTING = 8; constexpr auto CMD_MAX_NESTING = 8;
namespace command namespace command {
{ std::unordered_map<std::string, std::function<void(params&)>> handlers;
std::unordered_map<std::string, std::function<void(params&)>> handlers;
void main_handler() void main_handler() {
{ params params = {};
params params = {};
const auto command = utils::string::to_lower(params[0]); const auto command = utils::string::to_lower(params[0]);
if (handlers.find(command) != handlers.end()) if (handlers.find(command) != handlers.end()) {
{ handlers[command](params);
handlers[command](params); }
}
}
params::params()
: nesting_(game::sv_cmd_args->nesting)
{
assert(game::sv_cmd_args->nesting < CMD_MAX_NESTING);
}
int params::size() const
{
return game::sv_cmd_args->argc[this->nesting_];
}
const char* params::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return game::sv_cmd_args->argv[this->nesting_][index];
}
std::string params::join(const int index) const
{
std::string result = {};
for (auto i = index; i < this->size(); i++)
{
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>());
}
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())
{
add_raw(name, main_handler);
}
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)
{
script_commands.push_back(name);
const auto _name = allocator.duplicate_string(name);
add(_name, callback);
}
void clear_script_commands()
{
for (const auto& name : script_commands)
{
handlers.erase(name);
game::Cmd_RemoveCommand(name.data());
}
allocator.clear();
script_commands.clear();
}
void execute(std::string command, const bool sync)
{
command += "\n";
if (sync)
{
game::Cmd_ExecuteSingleCommand(game::LOCAL_CLIENT_0, 0, command.data());
}
else
{
game::Cbuf_AddText(game::LOCAL_CLIENT_0, command.data());
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
add_commands_generic();
}
void pre_destroy() override
{
clear_script_commands();
}
private:
static void add_commands_generic()
{
add("properQuit", [](const params&)
{
utils::nt::raise_hard_exception();
});
}
};
} }
params::params() : nesting_(game::sv_cmd_args->nesting) {
assert(game::sv_cmd_args->nesting < CMD_MAX_NESTING);
}
int params::size() const { return game::sv_cmd_args->argc[this->nesting_]; }
const char* params::get(const int index) const {
if (index >= this->size()) {
return "";
}
return game::sv_cmd_args->argv[this->nesting_][index];
}
std::string params::join(const int index) const {
std::string result = {};
for (auto i = index; i < this->size(); i++) {
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>());
}
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()) {
add_raw(name, main_handler);
}
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) {
script_commands.push_back(name);
const auto _name = allocator.duplicate_string(name);
add(_name, callback);
}
void clear_script_commands() {
for (const auto& name : script_commands) {
handlers.erase(name);
game::Cmd_RemoveCommand(name.data());
}
allocator.clear();
script_commands.clear();
}
void execute(std::string command, const bool sync) {
command += "\n";
if (sync) {
game::Cmd_ExecuteSingleCommand(game::LOCAL_CLIENT_0, 0, command.data());
} else {
game::Cbuf_AddText(game::LOCAL_CLIENT_0, command.data());
}
}
class component final : public component_interface {
public:
void post_unpack() override { add_commands_generic(); }
void pre_destroy() override { clear_script_commands(); }
private:
static void add_commands_generic() {
add("properQuit", [](const params&) { utils::nt::raise_hard_exception(); });
}
};
} // namespace command
REGISTER_COMPONENT(command::component) REGISTER_COMPONENT(command::component)

View File

@ -1,30 +1,26 @@
#pragma once #pragma once
namespace command namespace command {
{ class params {
class params public:
{ params();
public:
params();
int size() const; int size() const;
const char* get(int index) const; const char* get(int index) const;
std::string join(int index) const; std::string join(int index) const;
const char* operator[](const int index) const const char* operator[](const int index) const { return this->get(index); }
{
return this->get(index);
}
private: private:
int nesting_; int nesting_;
}; };
void add_raw(const char* name, void (*callback)()); void add_raw(const char* name, void (*callback)());
void add(const char* name, const std::function<void(const params&)>& 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 add_script_command(const std::string& name,
void clear_script_commands(); const std::function<void(const params&)>& callback);
void clear_script_commands();
void execute(std::string command, bool sync = false); void execute(std::string command, bool sync = false);
} } // namespace command

View File

@ -3,32 +3,29 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "utils/hook.hpp" #include "utils/hook.hpp"
namespace gameplay namespace gameplay {
{ namespace {
namespace const game::dvar_s* player_meleeRange = nullptr;
{ utils::hook::detour fire_weapon_melee_hook;
const game::dvar_s* player_meleeRange = nullptr;
utils::hook::detour fire_weapon_melee_hook;
void fire_weapon_melee_stub(game::gentity_s* ent, int time) void fire_weapon_melee_stub(game::gentity_s* ent, int time) {
{ if (player_meleeRange->current.value == 0.0f)
if (player_meleeRange->current.value == 0.0f) return;
return;
fire_weapon_melee_hook.invoke<void>(ent, time); fire_weapon_melee_hook.invoke<void>(ent, time);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
player_meleeRange = *reinterpret_cast<game::dvar_s**>(SELECT(0xC51990, 0xBCAFE4));
fire_weapon_melee_hook.create(SELECT(0x401E00, 0x465E40), &fire_weapon_melee_stub);
}
};
} }
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
player_meleeRange =
*reinterpret_cast<game::dvar_s**>(SELECT(0xC51990, 0xBCAFE4));
fire_weapon_melee_hook.create(SELECT(0x401E00, 0x465E40),
&fire_weapon_melee_stub);
}
};
} // namespace gameplay
REGISTER_COMPONENT(gameplay::component) REGISTER_COMPONENT(gameplay::component)

View File

@ -1,21 +1,13 @@
#include <stdinc.hpp> #include <stdinc.hpp>
namespace game namespace game {
{ gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s ? gamemode::multiplayer
? gamemode::multiplayer : gamemode::zombies;
: gamemode::zombies;
namespace environment namespace environment {
{ bool t5mp() { return current == gamemode::multiplayer; }
bool t5mp()
{
return current == gamemode::multiplayer;
}
bool t5zm() bool t5zm() { return current == gamemode::zombies; }
{ } // namespace environment
return current == gamemode::zombies; } // namespace game
}
}
}

View File

@ -2,58 +2,38 @@
#define SELECT(mp, zm) (game::environment::t5mp() ? mp : zm) #define SELECT(mp, zm) (game::environment::t5mp() ? mp : zm)
namespace game namespace game {
{ enum gamemode { none, multiplayer, zombies };
enum gamemode
{
none,
multiplayer,
zombies
};
extern gamemode current; extern gamemode current;
namespace environment namespace environment {
{ bool t5mp();
bool t5mp(); bool t5zm();
bool t5zm(); } // namespace environment
}
template <typename T> template <typename T> class symbol {
class symbol public:
{ symbol(const size_t t5mp, const size_t t5zm)
public: : t5mp_(reinterpret_cast<T*>(t5mp)), t5zm_(reinterpret_cast<T*>(t5zm)) {}
symbol(const size_t t5mp, const size_t t5zm)
: t5mp_(reinterpret_cast<T*>(t5mp))
, t5zm_(reinterpret_cast<T*>(t5zm))
{
}
T* get() const T* get() const {
{ if (environment::t5mp()) {
if (environment::t5mp()) return t5mp_;
{ }
return t5mp_;
}
return t5zm_; return t5zm_;
} }
operator T* () const operator T*() const { return this->get(); }
{
return this->get();
}
T* operator->() const T* operator->() const { return this->get(); }
{
return this->get();
}
private: private:
T* t5mp_; T* t5mp_;
T* t5zm_; T* t5zm_;
}; };
} } // namespace game
#include "symbols.hpp" #include "symbols.hpp"

File diff suppressed because it is too large Load Diff

View File

@ -2,59 +2,76 @@
#define WEAK __declspec(selectany) #define WEAK __declspec(selectany)
namespace game namespace game {
{ WEAK symbol<void(errorParm_t, const char*, ...)> Com_Error{0x627380, 0x651D90};
WEAK symbol<void(errorParm_t, const char*, ...)> Com_Error{0x627380, 0x651D90}; WEAK symbol<void(conChannel_t, const char*, ...)> Com_Printf{0x4126C0,
WEAK symbol<void(conChannel_t, const char*, ...)> Com_Printf{0x4126C0, 0x43BF30}; 0x43BF30};
WEAK symbol<void(conChannel_t, const char*, ...)> Com_DPrintf{0x4E3FA0, 0x60F7F0}; WEAK symbol<void(conChannel_t, const char*, ...)> Com_DPrintf{0x4E3FA0,
WEAK symbol<void(conChannel_t, const char*, ...)> Com_PrintError{0x568B90, 0x5DFC40}; 0x60F7F0};
WEAK symbol<void(conChannel_t, const char*, ...)> Com_PrintError{0x568B90,
0x5DFC40};
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x56EF70, 0x49B930}; WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x56EF70,
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x695E10, 0x5B3EB0}; 0x49B930};
WEAK symbol<void(LocalClientNum_t, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x50B470, 0x829AD0}; WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x695E10,
0x5B3EB0};
WEAK symbol<void(LocalClientNum_t, int controllerIndex, const char* text)>
Cmd_ExecuteSingleCommand{0x50B470, 0x829AD0};
WEAK symbol<void(client_s*, svscmd_type, const char*, ...)> SV_SendServerCommand{0x588B10, 0x6106E0}; WEAK symbol<void(client_s*, svscmd_type, const char*, ...)>
WEAK symbol<void(int, svscmd_type, const char*)> SV_GameSendServerCommand{0x6B8730, 0x543CF0}; SV_SendServerCommand{0x588B10, 0x6106E0};
WEAK symbol<void(int, char*, int)> SV_Cmd_ArgvBuffer{0x462CB0, 0x4DF5A0}; WEAK symbol<void(int, svscmd_type, const char*)> SV_GameSendServerCommand{
WEAK symbol<void(client_s*, const char*)> SV_DelayDropClient{0x4A8DC0, 0x4A2EB0}; 0x6B8730, 0x543CF0};
WEAK symbol<client_s*()> SV_GetPlayerByName{0x875180, 0x87C350}; WEAK symbol<void(int, char*, int)> SV_Cmd_ArgvBuffer{0x462CB0, 0x4DF5A0};
WEAK symbol<client_s*()> SV_GetPlayerByNum{0x875260, 0x87C430}; WEAK symbol<void(client_s*, const char*)> SV_DelayDropClient{0x4A8DC0,
0x4A2EB0};
WEAK symbol<client_s*()> SV_GetPlayerByName{0x875180, 0x87C350};
WEAK symbol<client_s*()> SV_GetPlayerByNum{0x875260, 0x87C430};
WEAK symbol<bool(netsrc_t, netadr_s, const char*)> NET_OutOfBandPrint{0x560BB0, 0x472850}; WEAK symbol<bool(netsrc_t, netadr_s, const char*)> NET_OutOfBandPrint{0x560BB0,
WEAK symbol<const char*(netadr_s)> NET_AdrToString{0x49F970, 0x40D790}; 0x472850};
WEAK symbol<const char*(netadr_s)> NET_AdrToString{0x49F970, 0x40D790};
WEAK symbol<dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810}; WEAK symbol<dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810};
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0, 0x5B56F0}; WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0,
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableLatchedValue{0x4AE1A0, 0x675850}; 0x5B56F0};
WEAK symbol<const dvar_s*(const char*, const char*, WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableLatchedValue{0x4AE1A0,
unsigned __int16, const char*)> Dvar_RegisterString{0x4E3410, 0x59B3B0}; 0x675850};
WEAK symbol<const dvar_s*(const char*, float, float, WEAK symbol<const dvar_s*(const char*, const char*, unsigned __int16,
float, unsigned __int16, const char*)> Dvar_RegisterFloat{0x670020, 0x679020}; const char*)>
WEAK symbol<const dvar_s*(const char*, bool, Dvar_RegisterString{0x4E3410, 0x59B3B0};
unsigned __int16, const char*)> Dvar_RegisterBool{0x5A5350, 0x45BB20}; WEAK symbol<const dvar_s*(const char*, float, float, float, unsigned __int16,
WEAK symbol<const dvar_s*(const char*, int, int, const char*)>
int, unsigned __int16, const char*)> Dvar_RegisterInt{0x58D900, 0x651910}; Dvar_RegisterFloat{0x670020, 0x679020};
WEAK symbol<const dvar_s*(const char*, bool, unsigned __int16, const char*)>
Dvar_RegisterBool{0x5A5350, 0x45BB20};
WEAK symbol<const dvar_s*(const char*, int, int, int, unsigned __int16,
const char*)>
Dvar_RegisterInt{0x58D900, 0x651910};
WEAK symbol<void(const char*, void(), cmd_function_t*)> Cmd_AddCommandInternal{0x6AD580, 0x661400}; WEAK symbol<void(const char*, void(), cmd_function_t*)> Cmd_AddCommandInternal{
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x527EA0, 0x5F1A90}; 0x6AD580, 0x661400};
WEAK symbol<cmd_function_t*(const char*)> Cmd_FindCommand{0x445B60, 0x479DD0}; WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x527EA0, 0x5F1A90};
WEAK symbol<cmd_function_t*(const char*)> Cmd_FindCommand{0x445B60, 0x479DD0};
WEAK symbol<char*(char*)> I_CleanStr{0x4B0700, 0x0}; WEAK symbol<char*(char*)> I_CleanStr{0x4B0700, 0x0};
WEAK symbol<char*(int)> ConcatArgs{0x5D5F10, 0x4FB210}; WEAK symbol<char*(int)> ConcatArgs{0x5D5F10, 0x4FB210};
WEAK symbol<void(int)> ClientCommand{0x63DB70, 0x4AF770}; WEAK symbol<void(int)> ClientCommand{0x63DB70, 0x4AF770};
WEAK symbol<void(gentity_s*, gentity_s*, int, const char*)> G_Say{0x51BBD0, 0x49A790}; WEAK symbol<void(gentity_s*, gentity_s*, int, const char*)> G_Say{0x51BBD0,
0x49A790};
WEAK symbol<void(gentity_s*, unsigned __int16, unsigned int)> Scr_Notify{0x458D30, 0x0}; WEAK symbol<void(gentity_s*, unsigned __int16, unsigned int)> Scr_Notify{
WEAK symbol<void(int, scriptInstance_t)> Scr_AddInt{0x49F830, 0x0}; 0x458D30, 0x0};
WEAK symbol<void(const float*, scriptInstance_t)> Scr_AddVector{0x532EF0, 0x0}; WEAK symbol<void(int, scriptInstance_t)> Scr_AddInt{0x49F830, 0x0};
WEAK symbol<void(const float*, scriptInstance_t)> Scr_AddVector{0x532EF0, 0x0};
WEAK symbol<int(const playerState_s*)> PM_GetEffectiveStance{0x659590, 0x0}; WEAK symbol<int(const playerState_s*)> PM_GetEffectiveStance{0x659590, 0x0};
WEAK symbol<CmdArgs> sv_cmd_args{0x355BD88, 0x243D208}; WEAK symbol<CmdArgs> sv_cmd_args{0x355BD88, 0x243D208};
WEAK symbol<int> dvarCount{0x385BE74, 0x261CBD4}; WEAK symbol<int> dvarCount{0x385BE74, 0x261CBD4};
WEAK symbol<dvar_t*> sortedDvars{0x385BE88, 0x261CBE8}; WEAK symbol<dvar_t*> sortedDvars{0x385BE88, 0x261CBE8};
WEAK symbol<client_s> svs_clients{0x372D11C, 0x286D01C}; WEAK symbol<client_s> svs_clients{0x372D11C, 0x286D01C};
WEAK symbol<gentity_s> g_entities{0x32E5640, 0x1A796F8}; WEAK symbol<gentity_s> g_entities{0x32E5640, 0x1A796F8};
WEAK symbol<int> level_time{0x3443F4C, 0xC2078C}; WEAK symbol<int> level_time{0x3443F4C, 0xC2078C};
} } // namespace game

View File

@ -1,35 +1,21 @@
#pragma once #pragma once
class component_interface class component_interface {
{
public: 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; return nullptr;
} }
virtual bool is_supported() virtual bool is_supported() { return true; }
{
return true;
}
}; };

View File

@ -1,127 +1,111 @@
#include <stdinc.hpp> #include <stdinc.hpp>
#include "component_loader.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_)); get_components().push_back(std::move(component_));
} }
bool component_loader::post_start() bool component_loader::post_start() {
{ static auto handled = false;
static auto handled = false; if (handled)
if (handled) return true; return true;
handled = true; handled = true;
try try {
{ for (const auto& component_ : get_components()) {
for (const auto& component_ : get_components()) component_->post_start();
{ }
component_->post_start(); } catch (premature_shutdown_trigger&) {
} return false;
} }
catch (premature_shutdown_trigger&)
{
return false;
}
return true; return true;
} }
bool component_loader::post_load() bool component_loader::post_load() {
{ static auto handled = false;
static auto handled = false; if (handled)
if (handled) return true; return true;
handled = true; handled = true;
clean(); clean();
try try {
{ for (const auto& component_ : get_components()) {
for (const auto& component_ : get_components()) component_->post_load();
{ }
component_->post_load(); } catch (premature_shutdown_trigger&) {
} return false;
} }
catch (premature_shutdown_trigger&)
{
return false;
}
return true; return true;
} }
void component_loader::post_unpack() void component_loader::post_unpack() {
{ static auto handled = false;
static auto handled = false; if (handled)
if (handled) return; return;
handled = true; handled = true;
for (const auto& component_ : get_components()) for (const auto& component_ : get_components()) {
{ component_->post_unpack();
component_->post_unpack(); }
}
} }
void component_loader::pre_destroy() void component_loader::pre_destroy() {
{ static auto handled = false;
static auto handled = false; if (handled)
if (handled) return; return;
handled = true; handled = true;
for (const auto& component_ : get_components()) for (const auto& component_ : get_components()) {
{ component_->pre_destroy();
component_->pre_destroy(); }
}
} }
void component_loader::clean() void component_loader::clean() {
{ auto& components = get_components();
auto& components = get_components(); for (auto i = components.begin(); i != components.end();) {
for (auto i = components.begin(); i != components.end();) if (!(*i)->is_supported()) {
{ (*i)->pre_destroy();
if (!(*i)->is_supported()) i = components.erase(i);
{ } else {
(*i)->pre_destroy(); ++i;
i = components.erase(i); }
} }
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; void* function_ptr = nullptr;
for (const auto& component_ : get_components()) for (const auto& component_ : get_components()) {
{ auto* const component_function_ptr =
auto* const component_function_ptr = component_->load_import(library, function); component_->load_import(library, function);
if (component_function_ptr) if (component_function_ptr) {
{ function_ptr = component_function_ptr;
function_ptr = component_function_ptr; }
} }
}
return function_ptr; return function_ptr;
} }
void component_loader::trigger_premature_shutdown() void component_loader::trigger_premature_shutdown() {
{ throw premature_shutdown_trigger();
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 = 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(); pre_destroy();
delete component_vector; delete component_vector;
}); });
return *components; return *components;
} }

View File

@ -1,61 +1,51 @@
#pragma once #pragma once
#include "component_interface.hpp" #include "component_interface.hpp"
class component_loader final class component_loader final {
{
public: public:
class premature_shutdown_trigger final : public std::exception class premature_shutdown_trigger final : public std::exception {
{ [[nodiscard]] const char* what() const noexcept override {
[[nodiscard]] const char* what() const noexcept override return "Premature shutdown requested";
{ }
return "Premature shutdown requested"; };
}
};
template <typename T> template <typename T> class installer final {
class installer final static_assert(std::is_base_of<component_interface, T>::value,
{ "component has invalid base class");
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
public: public:
installer() installer() { register_component(std::make_unique<T>()); }
{ };
register_component(std::make_unique<T>());
}
};
template <typename T> template <typename T> static T* get() {
static T* get() for (const auto& component_ : get_components()) {
{ if (typeid(*component_.get()) == typeid(T)) {
for (const auto& component_ : get_components()) return reinterpret_cast<T*>(component_.get());
{ }
if (typeid(*component_.get()) == typeid(T)) }
{
return reinterpret_cast<T*>(component_.get());
}
}
return nullptr; 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_start();
static bool post_load(); static bool post_load();
static void post_unpack(); static void post_unpack();
static void pre_destroy(); static void pre_destroy();
static void clean(); 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(); static void trigger_premature_shutdown();
private: private:
static std::vector<std::unique_ptr<component_interface>>& get_components(); static std::vector<std::unique_ptr<component_interface>>& get_components();
}; };
#define REGISTER_COMPONENT(name) \ #define REGISTER_COMPONENT(name) \
namespace \ namespace { \
{ \ static component_loader::installer<name> __component; \
static component_loader::installer<name> __component; \ }
}

View File

@ -1,16 +1,13 @@
#include <stdinc.hpp> #include <stdinc.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/) BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call,
{ LPVOID /*reserved_*/) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
{ component_loader::post_unpack();
component_loader::post_unpack(); } else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
} component_loader::pre_destroy();
else if (ul_reason_for_call == DLL_PROCESS_DETACH) }
{
component_loader::pre_destroy();
}
return TRUE; return TRUE;
} }

View File

@ -9,17 +9,18 @@
#include <windows.h> #include <windows.h>
#include <vector> #include <algorithm>
#include <cassert> #include <cassert>
#include <functional>
#include <iostream>
#include <map>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <unordered_set> #include <unordered_set>
#include <map>
#include <vector> #include <vector>
#pragma warning(disable : 26812)
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h> #include <rapidjson/stringbuffer.h>
@ -28,5 +29,7 @@
using namespace std::literals; using namespace std::literals;
// clang-format off
#include "game/structs.hpp" #include "game/structs.hpp"
#include "game/game.hpp" #include "game/game.hpp"
// clang-format on

View File

@ -2,45 +2,35 @@
#include <mutex> #include <mutex>
namespace utils::concurrency namespace utils::concurrency {
{ template <typename T, typename MutexType = std::mutex> class container {
template <typename T, typename MutexType = std::mutex> public:
class container template <typename R = void, typename F> R access(F&& accessor) const {
{ std::lock_guard<MutexType> _{mutex_};
public: return accessor(object_);
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> template <typename R = void, typename F> R access(F&& accessor) {
R access(F&& accessor) std::lock_guard<MutexType> _{mutex_};
{ return accessor(object_);
std::lock_guard<MutexType> _{mutex_}; }
return accessor(object_);
}
template <typename R = void, typename F> 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_};
std::unique_lock<MutexType> lock{mutex_}; return accessor(object_, lock);
return accessor(object_, lock); }
}
template <typename R = void, typename F> template <typename R = void, typename F> R access_with_lock(F&& accessor) {
R access_with_lock(F&& accessor) std::unique_lock<MutexType> lock{mutex_};
{ return accessor(object_, lock);
std::unique_lock<MutexType> lock{mutex_}; }
return accessor(object_, lock);
}
T& get_raw() { return object_; } T& get_raw() { return object_; }
const T& get_raw() const { return object_; } const T& get_raw() const { return object_; }
private: private:
mutable MutexType mutex_{}; mutable MutexType mutex_{};
T object_{}; T object_{};
}; };
} } // namespace utils::concurrency

View File

@ -1,26 +1,22 @@
#include "string.hpp" #include "string.hpp"
#include "cryptography.hpp" #include "cryptography.hpp"
namespace jenkins_one_at_a_time namespace jenkins_one_at_a_time {
{ unsigned int jenkins_one_at_a_time::compute(const std::string& data) {
unsigned int jenkins_one_at_a_time::compute(const std::string& data) return compute(data.data(), data.size());
{
return compute(data.data(), data.size());
}
unsigned int jenkins_one_at_a_time::compute(const char* key, const size_t len)
{
unsigned int hash, i;
for (hash = i = 0; i < len; ++i)
{
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
} }
unsigned int jenkins_one_at_a_time::compute(const char* key, const size_t len) {
unsigned int hash, i;
for (hash = i = 0; i < len; ++i) {
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
} // namespace jenkins_one_at_a_time

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
namespace jenkins_one_at_a_time namespace jenkins_one_at_a_time {
{ unsigned int compute(const std::string& data);
unsigned int compute(const std::string& data); unsigned int compute(const char* key, size_t len);
unsigned int compute(const char* key, size_t len); } // namespace jenkins_one_at_a_time
}

View File

@ -3,191 +3,154 @@
#include <MinHook.h> #include <MinHook.h>
namespace utils::hook namespace utils::hook {
{ namespace {
namespace [[maybe_unused]] class _ {
{ public:
[[maybe_unused]] class _ _() {
{ if (MH_Initialize() != MH_OK) {
public: throw std::runtime_error("Failed to initialize MinHook");
_() }
{ }
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) detour::detour(void* place, void* target) { this->create(place, target); }
{
this->create(place, target);
}
detour::~detour() detour::~detour() { this->clear(); }
{
this->clear();
}
void detour::enable() const void detour::enable() const { MH_EnableHook(this->place_); }
{
MH_EnableHook(this->place_);
}
void detour::disable() const void detour::disable() const { MH_DisableHook(this->place_); }
{
MH_DisableHook(this->place_);
}
void detour::create(void* place, void* target) void detour::create(void* place, void* target) {
{ this->clear();
this->clear(); this->place_ = place;
this->place_ = place;
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) {
{ throw std::runtime_error(
throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_)); string::va("Unable to create hook at location: %p", this->place_));
} }
this->enable(); this->enable();
}
void detour::create(const size_t place, void* target)
{
this->create(reinterpret_cast<void*>(place), target);
}
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 nop(void* place, const size_t length)
{
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memset(place, 0x90, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, 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)
{
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memmove(place, data, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, 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)
{
const std::int64_t diff = size_t(data) - (size_t(pointer) + offset);
const auto small_diff = std::int32_t(diff);
return diff != std::int64_t(small_diff);
}
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<std::uint8_t>(patch_pointer, 0xE8);
set<std::int32_t>(patch_pointer + 1, std::int32_t(size_t(data) - (size_t(pointer) + 5)));
}
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)
{
return call(pointer, reinterpret_cast<void*>(data));
}
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes)
{
DWORD oldProtect = 0;
auto* place = reinterpret_cast<void*>(address);
VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
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)
{
DWORD oldProtect = 0;
auto* place = reinterpret_cast<void*>(address);
VirtualProtect(place, size, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(place, buffer, size);
VirtualProtect(place, size, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, size);
}
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);
set(address, bytes, 5);
delete[] bytes;
}
void redirect_jump(void* pointer, void* data)
{
char* operand_ptr = static_cast<char*>(pointer) + 2;
std::int32_t new_operand = reinterpret_cast<std::int32_t>(data) - (reinterpret_cast<std::int32_t>(pointer) + 6);
set<std::int32_t>(operand_ptr, new_operand);
}
void redirect_jump(size_t pointer, void* data)
{
redirect_jump(reinterpret_cast<void*>(pointer), data);
}
} }
void detour::create(const size_t place, void* target) {
this->create(reinterpret_cast<void*>(place), target);
}
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 nop(void* place, const size_t length) {
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memset(place, 0x90, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, 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) {
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memmove(place, data, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, 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) {
const std::int64_t diff = size_t(data) - (size_t(pointer) + offset);
const auto small_diff = std::int32_t(diff);
return diff != std::int64_t(small_diff);
}
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<std::uint8_t>(patch_pointer, 0xE8);
set<std::int32_t>(patch_pointer + 1,
std::int32_t(size_t(data) - (size_t(pointer) + 5)));
}
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) {
return call(pointer, reinterpret_cast<void*>(data));
}
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes) {
DWORD oldProtect = 0;
auto* place = reinterpret_cast<void*>(address);
VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
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) {
DWORD oldProtect = 0;
auto* place = reinterpret_cast<void*>(address);
VirtualProtect(place, size, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(place, buffer, size);
VirtualProtect(place, size, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, size);
}
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);
set(address, bytes, 5);
delete[] bytes;
}
void redirect_jump(void* pointer, void* data) {
char* operand_ptr = static_cast<char*>(pointer) + 2;
std::int32_t new_operand = reinterpret_cast<std::int32_t>(data) -
(reinterpret_cast<std::int32_t>(pointer) + 6);
set<std::int32_t>(operand_ptr, new_operand);
}
void redirect_jump(size_t pointer, void* data) {
redirect_jump(reinterpret_cast<void*>(pointer), data);
}
} // namespace utils::hook

View File

@ -1,120 +1,102 @@
#pragma once #pragma once
#include "signature.hpp" #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 namespace utils::hook {
{ class detour {
class detour public:
{ detour() = default;
public: detour(void* place, void* target);
detour() = default; detour(size_t place, void* target);
detour(void* place, void* target); ~detour();
detour(size_t place, void* target);
~detour();
detour(detour&& other) noexcept detour(detour&& other) noexcept { this->operator=(std::move(other)); }
{
this->operator=(std::move(other));
}
detour& operator= (detour&& other) noexcept detour& operator=(detour&& other) noexcept {
{ if (this != &other) {
if (this != &other) this->~detour();
{
this->~detour();
this->place_ = other.place_; this->place_ = other.place_;
this->original_ = other.original_; this->original_ = other.original_;
other.place_ = nullptr; other.place_ = nullptr;
other.original_ = nullptr; other.original_ = nullptr;
} }
return *this; return *this;
} }
detour(const detour&) = delete; detour(const detour&) = delete;
detour& operator= (const detour&) = delete; detour& operator=(const detour&) = delete;
void enable() const; void enable() const;
void disable() const; void disable() const;
void create(void* place, void* target); void create(void* place, void* target);
void create(size_t place, void* target); void create(size_t place, void* target);
void clear(); void clear();
template <typename T> template <typename T> T* get() const {
T* get() const return static_cast<T*>(this->get_original());
{ }
return static_cast<T*>(this->get_original());
}
template <typename T, typename... Args> template <typename T, typename... Args> T invoke(Args... args) {
T invoke(Args... args) return static_cast<T (*)(Args...)>(this->get_original())(args...);
{ }
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
}
[[nodiscard]] void* get_original() const; [[nodiscard]] void* get_original() const;
private: private:
void* place_{}; void* place_{};
void* original_{}; void* original_{};
}; };
void nop(void* place, size_t length); void nop(void* place, size_t length);
void nop(size_t place, size_t length); void nop(size_t place, size_t length);
void copy(void* 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); 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(void* pointer, void* data);
void call(size_t pointer, void* data); void call(size_t pointer, void* data);
void call(size_t pointer, size_t 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(void* pointer, void* data);
void redirect_jump(size_t pointer, void* data); void redirect_jump(size_t pointer, void* data);
template <typename T> template <typename T> T extract(void* address) {
T extract(void* address) const auto data = static_cast<uint8_t*>(address);
{ const auto offset = *reinterpret_cast<int32_t*>(data);
const auto data = static_cast<uint8_t*>(address); return reinterpret_cast<T>(data + offset + 4);
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)
{
DWORD old_protect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
*static_cast<T*>(place) = value;
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(void* place, T value) {
DWORD old_protect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
*static_cast<T*>(place) = value;
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...);
}
} // namespace utils::hook

View File

@ -1,65 +1,50 @@
#include "info_string.hpp" #include "info_string.hpp"
#include "string.hpp" #include "string.hpp"
namespace utils namespace utils {
{ info_string::info_string(const std::string& buffer) { this->parse(buffer); }
info_string::info_string(const std::string& buffer)
{
this->parse(buffer);
}
info_string::info_string(const std::string_view& buffer) info_string::info_string(const std::string_view& buffer)
: info_string(std::string{ 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;
this->key_value_pairs_[key] = value;
}
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())
{
return value->second;
}
return "";
}
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 = 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;
for (auto i = this->key_value_pairs_.begin(); i != this->key_value_pairs_.end(); ++i)
{
//if (first) first = false;
/*else*/
info_string.append("\\");
info_string.append(i->first); // Key
info_string.append("\\");
info_string.append(i->second); // Value
}
return info_string;
}
} }
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()) {
return value->second;
}
return "";
}
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 = key_values[i];
const auto& value = key_values[i + 1];
this->key_value_pairs_[key] = value;
}
}
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) {
info_string.append("\\");
info_string.append(i->first); // Key
info_string.append("\\");
info_string.append(i->second); // Value
}
return info_string;
}
} // namespace utils

View File

@ -3,22 +3,20 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace utils namespace utils {
{ class info_string {
class info_string public:
{ info_string() = default;
public: info_string(const std::string& buffer);
info_string() = default; info_string(const std::string_view& buffer);
info_string(const std::string& buffer);
info_string(const std::string_view& buffer);
void set(const std::string& key, const std::string& value); void set(const std::string& key, const std::string& value);
std::string get(const std::string& key) const; std::string get(const std::string& key) const;
std::string build() const; std::string build() const;
private: private:
std::unordered_map<std::string, std::string> key_value_pairs_{}; std::unordered_map<std::string, std::string> key_value_pairs_{};
void parse(std::string buffer); void parse(std::string buffer);
}; };
} } // namespace utils

View File

@ -2,124 +2,106 @@
#include "nt.hpp" #include "nt.hpp"
#include <fstream> #include <fstream>
namespace utils::io namespace utils::io {
{ bool remove_file(const std::string& file) {
bool remove_file(const std::string& file) return DeleteFileA(file.data()) == TRUE;
{
return DeleteFileA(file.data()) == TRUE;
}
bool move_file(const std::string& src, const std::string& target)
{
return MoveFileA(src.data(), target.data()) == TRUE;
}
bool file_exists(const std::string& file)
{
return std::ifstream(file).good();
}
bool write_file(const std::string& file, const std::string& data, const bool append)
{
const auto pos = file.find_last_of("/\\");
if (pos != std::string::npos)
{
create_directory(file.substr(0, pos));
}
std::ofstream stream(
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : 0));
if (stream.is_open())
{
stream.write(data.data(), data.size());
stream.close();
return true;
}
return false;
}
std::string read_file(const std::string& file)
{
std::string data;
read_file(file, &data);
return data;
}
bool read_file(const std::string& file, std::string* data)
{
if (!data) return false;
data->clear();
if (file_exists(file))
{
std::ifstream stream(file, std::ios::binary);
if (!stream.is_open()) return false;
stream.seekg(0, std::ios::end);
const std::streamsize size = stream.tellg();
stream.seekg(0, std::ios::beg);
if (size > -1)
{
data->resize(static_cast<uint32_t>(size));
stream.read(const_cast<char*>(data->data()), size);
stream.close();
return true;
}
}
return false;
}
size_t file_size(const std::string& file)
{
if (file_exists(file))
{
std::ifstream stream(file, std::ios::binary);
if (stream.good())
{
stream.seekg(0, std::ios::end);
return static_cast<size_t>(stream.tellg());
}
}
return 0;
}
bool create_directory(const std::string& directory)
{
return std::filesystem::create_directories(directory);
}
bool directory_exists(const std::string& directory)
{
return std::filesystem::is_directory(directory);
}
bool directory_is_empty(const std::string& directory)
{
return std::filesystem::is_empty(directory);
}
std::vector<std::string> list_files(const std::string& directory)
{
std::vector<std::string> files;
for (auto& file : std::filesystem::directory_iterator(directory))
{
files.push_back(file.path().generic_string());
}
return files;
}
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
{
std::filesystem::copy(src, target,
std::filesystem::copy_options::overwrite_existing |
std::filesystem::copy_options::recursive);
}
} }
bool move_file(const std::string& src, const std::string& target) {
return MoveFileA(src.data(), target.data()) == TRUE;
}
bool file_exists(const std::string& file) { return std::ifstream(file).good(); }
bool write_file(const std::string& file, const std::string& data,
const bool append) {
const auto pos = file.find_last_of("/\\");
if (pos != std::string::npos) {
create_directory(file.substr(0, pos));
}
std::ofstream stream(file, std::ios::binary | std::ofstream::out |
(append ? std::ofstream::app : 0));
if (stream.is_open()) {
stream.write(data.data(), data.size());
stream.close();
return true;
}
return false;
}
std::string read_file(const std::string& file) {
std::string data;
read_file(file, &data);
return data;
}
bool read_file(const std::string& file, std::string* data) {
if (!data)
return false;
data->clear();
if (file_exists(file)) {
std::ifstream stream(file, std::ios::binary);
if (!stream.is_open())
return false;
stream.seekg(0, std::ios::end);
const std::streamsize size = stream.tellg();
stream.seekg(0, std::ios::beg);
if (size > -1) {
data->resize(static_cast<uint32_t>(size));
stream.read(const_cast<char*>(data->data()), size);
stream.close();
return true;
}
}
return false;
}
size_t file_size(const std::string& file) {
if (file_exists(file)) {
std::ifstream stream(file, std::ios::binary);
if (stream.good()) {
stream.seekg(0, std::ios::end);
return static_cast<size_t>(stream.tellg());
}
}
return 0;
}
bool create_directory(const std::string& directory) {
return std::filesystem::create_directories(directory);
}
bool directory_exists(const std::string& directory) {
return std::filesystem::is_directory(directory);
}
bool directory_is_empty(const std::string& directory) {
return std::filesystem::is_empty(directory);
}
std::vector<std::string> list_files(const std::string& directory) {
std::vector<std::string> files;
for (auto& file : std::filesystem::directory_iterator(directory)) {
files.push_back(file.path().generic_string());
}
return files;
}
void copy_folder(const std::filesystem::path& src,
const std::filesystem::path& target) {
std::filesystem::copy(src, target,
std::filesystem::copy_options::overwrite_existing |
std::filesystem::copy_options::recursive);
}
} // namespace utils::io

View File

@ -1,21 +1,22 @@
#pragma once #pragma once
#include <filesystem>
#include <string> #include <string>
#include <vector> #include <vector>
#include <filesystem>
namespace utils::io namespace utils::io {
{ bool remove_file(const std::string& file);
bool remove_file(const std::string& file); bool move_file(const std::string& src, const std::string& target);
bool move_file(const std::string& src, const std::string& target); bool file_exists(const std::string& file);
bool file_exists(const std::string& file); bool write_file(const std::string& file, const std::string& data,
bool write_file(const std::string& file, const std::string& data, bool append = false); bool append = false);
bool read_file(const std::string& file, std::string* data); bool read_file(const std::string& file, std::string* data);
std::string read_file(const std::string& file); std::string read_file(const std::string& file);
size_t file_size(const std::string& file); size_t file_size(const std::string& file);
bool create_directory(const std::string& directory); bool create_directory(const std::string& directory);
bool directory_exists(const std::string& directory); bool directory_exists(const std::string& directory);
bool directory_is_empty(const std::string& directory); bool directory_is_empty(const std::string& directory);
std::vector<std::string> list_files(const std::string& directory); std::vector<std::string> list_files(const std::string& directory);
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); void copy_folder(const std::filesystem::path& src,
} const std::filesystem::path& target);
} // namespace utils::io

View File

@ -1,165 +1,134 @@
#include "memory.hpp" #include "memory.hpp"
#include "nt.hpp" #include "nt.hpp"
namespace utils namespace utils {
{ memory::allocator memory::mem_allocator_;
memory::allocator memory::mem_allocator_;
memory::allocator::~allocator() memory::allocator::~allocator() { this->clear(); }
{
this->clear();
}
void memory::allocator::clear() void memory::allocator::clear() {
{ std::lock_guard _(this->mutex_);
std::lock_guard _(this->mutex_);
for (auto& data : this->pool_) for (auto& data : this->pool_) {
{ memory::free(data);
memory::free(data); }
}
this->pool_.clear(); this->pool_.clear();
}
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())
{
memory::free(data);
this->pool_.erase(j);
}
}
void memory::allocator::free(const void* data)
{
this->free(const_cast<void*>(data));
}
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();
}
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);
}
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)
{
::free(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)
{
const auto mem_arr = static_cast<const char*>(mem);
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)
{
MEMORY_BASIC_INFORMATION mbi = {};
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;
return b;
}
return true;
}
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);
auto b = !(mbi.Protect & mask);
// check the page is not a guard page
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
return b;
}
return true;
}
bool memory::is_rdata_ptr(void* pointer)
{
const std::string rdata = ".rdata";
const auto pointer_lib = utils::nt::library::get_by_address(pointer);
for (const auto& section : pointer_lib.get_section_headers())
{
const 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;
const size_t source_end = source_start + section->SizeOfRawData;
return target >= source_start && target <= source_end;
}
}
return false;
}
memory::allocator* memory::get_allocator()
{
return &memory::mem_allocator_;
}
} }
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()) {
memory::free(data);
this->pool_.erase(j);
}
}
void memory::allocator::free(const void* data) {
this->free(const_cast<void*>(data));
}
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(); }
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); }
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) {
::free(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) {
const auto mem_arr = static_cast<const char*>(mem);
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) {
MEMORY_BASIC_INFORMATION mbi = {};
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;
return b;
}
return true;
}
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);
auto b = !(mbi.Protect & mask);
// check the page is not a guard page
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
b = true;
return b;
}
return true;
}
bool memory::is_rdata_ptr(void* pointer) {
const std::string rdata = ".rdata";
const auto pointer_lib = utils::nt::library::get_by_address(pointer);
for (const auto& section : pointer_lib.get_section_headers()) {
const 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;
const size_t source_end = source_start + section->SizeOfRawData;
return target >= source_start && target <= source_end;
}
}
return false;
}
memory::allocator* memory::get_allocator() { return &memory::mem_allocator_; }
} // namespace utils

View File

@ -3,73 +3,63 @@
#include <mutex> #include <mutex>
#include <vector> #include <vector>
namespace utils namespace utils {
{ class memory final {
class memory final public:
{ class allocator final {
public: public:
class allocator final ~allocator();
{
public:
~allocator();
void clear(); void clear();
void free(void* data); void free(void* data);
void free(const void* data); void free(const void* data);
void* allocate(size_t length); void* allocate(size_t length);
template <typename T> template <typename T> inline T* allocate() {
inline T* allocate() return this->allocate_array<T>(1);
{ }
return this->allocate_array<T>(1);
}
template <typename T> template <typename T> inline T* allocate_array(const size_t count = 1) {
inline T* allocate_array(const size_t count = 1) return static_cast<T*>(this->allocate(count * sizeof(T)));
{ }
return static_cast<T*>(this->allocate(count * sizeof(T)));
}
bool empty() const; bool empty() const;
char* duplicate_string(const std::string& string); char* duplicate_string(const std::string& string);
private: private:
std::mutex mutex_; std::mutex mutex_;
std::vector<void*> pool_; std::vector<void*> pool_;
}; };
static void* allocate(size_t length); static void* allocate(size_t length);
template <typename T> template <typename T> static inline T* allocate() {
static inline T* allocate() return allocate_array<T>(1);
{ }
return allocate_array<T>(1);
}
template <typename T> template <typename T>
static inline T* allocate_array(const size_t count = 1) static inline T* allocate_array(const size_t count = 1) {
{ return static_cast<T*>(allocate(count * sizeof(T)));
return static_cast<T*>(allocate(count * sizeof(T))); }
}
static char* duplicate_string(const std::string& string); static char* duplicate_string(const std::string& string);
static void free(void* data); static void free(void* data);
static void free(const void* data); static void free(const void* data);
static bool is_set(const void* mem, char chr, size_t length); static bool is_set(const void* mem, char chr, size_t length);
static bool is_bad_read_ptr(const void* ptr); static bool is_bad_read_ptr(const void* ptr);
static bool is_bad_code_ptr(const void* ptr); static bool is_bad_code_ptr(const void* ptr);
static bool is_rdata_ptr(void* ptr); static bool is_rdata_ptr(void* ptr);
static allocator* get_allocator(); static allocator* get_allocator();
private: private:
static allocator mem_allocator_; static allocator mem_allocator_;
}; };
} } // namespace utils

View File

@ -1,254 +1,240 @@
#include "nt.hpp" #include "nt.hpp"
namespace utils::nt namespace utils::nt {
{ library library::load(const std::string& name) {
library library::load(const std::string& name) return library(LoadLibraryA(name.data()));
{
return library(LoadLibraryA(name.data()));
}
library library::load(const std::filesystem::path& path)
{
return library::load(path.generic_string());
}
library library::get_by_address(void* address)
{
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(address), &handle);
return library(handle);
}
library::library()
{
this->module_ = GetModuleHandleA(nullptr);
}
library::library(const std::string& name)
{
this->module_ = GetModuleHandleA(name.data());
}
library::library(const HMODULE handle)
{
this->module_ = handle;
}
bool library::operator==(const library& obj) const
{
return this->module_ == obj.module_;
}
library::operator bool() const
{
return this->is_valid();
}
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_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;
return &this->get_nt_headers()->OptionalHeader;
}
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");
}
return headers;
}
std::uint8_t* library::get_ptr() const
{
return reinterpret_cast<std::uint8_t*>(this->module_);
}
void library::unprotect() const
{
if (!this->is_valid()) return;
DWORD 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;
return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint;
}
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;
}
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;
return path.substr(pos + 1);
}
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 "";
const auto path = std::filesystem::path(this->get_path());
return path.parent_path().generic_string();
}
void library::free()
{
if (this->is_valid())
{
FreeLibrary(this->module_);
this->module_ = nullptr;
}
}
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;
const library other_module(module_name);
if (!other_module.is_valid()) return nullptr;
auto* const target_function = other_module.get_proc<void*>(proc_name);
if (!target_function) return nullptr;
auto* header = this->get_optional_header();
if (!header) return nullptr;
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 (original_thunk_data->u1.AddressOfData)
{
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
if (ordinal_number > 0xFFFF) continue;
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) ==
target_function)
{
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
++original_thunk_data;
++thunk_data;
}
//break;
}
++import_descriptor;
}
return nullptr;
}
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);
}
std::string load_resource(const int id)
{
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
if (!res) return {};
auto* const handle = LoadResource(nullptr, res);
if (!handle) return {};
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
}
void relaunch_self()
{
const utils::nt::library self;
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(startup_info);
char current_dir[MAX_PATH];
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);
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);
}
} }
library library::load(const std::filesystem::path& path) {
return library::load(path.generic_string());
}
library library::get_by_address(void* address) {
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
static_cast<LPCSTR>(address), &handle);
return library(handle);
}
library::library() { this->module_ = GetModuleHandleA(nullptr); }
library::library(const std::string& name) {
this->module_ = GetModuleHandleA(name.data());
}
library::library(const HMODULE handle) { this->module_ = handle; }
bool library::operator==(const library& obj) const {
return this->module_ == obj.module_;
}
library::operator bool() const { return this->is_valid(); }
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_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;
return &this->get_nt_headers()->OptionalHeader;
}
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");
}
return headers;
}
std::uint8_t* library::get_ptr() const {
return reinterpret_cast<std::uint8_t*>(this->module_);
}
void library::unprotect() const {
if (!this->is_valid())
return;
DWORD 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;
return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint;
}
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;
}
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;
return path.substr(pos + 1);
}
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 "";
const auto path = std::filesystem::path(this->get_path());
return path.parent_path().generic_string();
}
void library::free() {
if (this->is_valid()) {
FreeLibrary(this->module_);
this->module_ = nullptr;
}
}
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;
const library other_module(module_name);
if (!other_module.is_valid())
return nullptr;
auto* const target_function = other_module.get_proc<void*>(proc_name);
if (!target_function)
return nullptr;
auto* header = this->get_optional_header();
if (!header)
return nullptr;
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 (original_thunk_data->u1.AddressOfData) {
const size_t ordinal_number =
original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
if (ordinal_number > 0xFFFF)
continue;
if (GetProcAddress(other_module.module_,
reinterpret_cast<char*>(ordinal_number)) ==
target_function) {
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
++original_thunk_data;
++thunk_data;
}
// break;
}
++import_descriptor;
}
return nullptr;
}
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);
}
std::string load_resource(const int id) {
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
if (!res)
return {};
auto* const handle = LoadResource(nullptr, res);
if (!handle)
return {};
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
}
void relaunch_self() {
const utils::nt::library self;
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(startup_info);
char current_dir[MAX_PATH];
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);
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

View File

@ -12,99 +12,95 @@
#undef min #undef min
#endif #endif
#include <string>
#include <functional>
#include <filesystem> #include <filesystem>
#include <functional>
#include <string>
namespace utils::nt namespace utils::nt {
{ class library final {
class library final public:
{ static library load(const std::string& name);
public: static library load(const std::filesystem::path& path);
static library load(const std::string& name); static library get_by_address(void* address);
static library load(const std::filesystem::path& path);
static library get_by_address(void* address);
library(); library();
explicit library(const std::string& name); explicit library(const std::string& name);
explicit library(HMODULE handle); 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 { return !(*this == obj); };
bool operator==(const library& obj) const; bool operator==(const library& obj) const;
operator bool() const; operator bool() const;
operator HMODULE() const; operator HMODULE() const;
void unprotect() const; void unprotect() const;
void* get_entry_point() const; void* get_entry_point() const;
size_t get_relative_entry_point() const; size_t get_relative_entry_point() const;
bool is_valid() const; bool is_valid() const;
std::string get_name() const; std::string get_name() const;
std::string get_path() const; std::string get_path() const;
std::string get_folder() const; std::string get_folder() const;
std::uint8_t* get_ptr() const; std::uint8_t* get_ptr() const;
void free(); void free();
HMODULE get_handle() const; HMODULE get_handle() const;
template <typename T> template <typename T> T get_proc(const std::string& process) const {
T get_proc(const std::string& process) const if (!this->is_valid())
{ T{};
if (!this->is_valid()) T{}; return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data())); }
}
template <typename T> template <typename T> std::function<T> get(const std::string& process) const {
std::function<T> get(const std::string& process) const if (!this->is_valid())
{ return std::function<T>();
if (!this->is_valid()) return std::function<T>(); return static_cast<T*>(this->get_proc<void*>(process));
return static_cast<T*>(this->get_proc<void*>(process)); }
}
template <typename T, typename... Args> template <typename T, typename... Args>
T invoke(const std::string& process, Args ... args) const T invoke(const std::string& process, Args... args) const {
{ auto method = this->get<T(__cdecl)(Args...)>(process);
auto method = this->get<T(__cdecl)(Args ...)>(process); if (method)
if (method) return method(args...); return method(args...);
return T(); return T();
} }
template <typename T, typename... Args> template <typename T, typename... Args>
T invoke_pascal(const std::string& process, Args ... args) const T invoke_pascal(const std::string& process, Args... args) const {
{ auto method = this->get<T(__stdcall)(Args...)>(process);
auto method = this->get<T(__stdcall)(Args ...)>(process); if (method)
if (method) return method(args...); return method(args...);
return T(); return T();
} }
template <typename T, typename... Args> template <typename T, typename... Args>
T invoke_this(const std::string& process, void* this_ptr, Args ... args) const T invoke_this(const std::string& process, void* this_ptr,
{ Args... args) const {
auto method = this->get<T(__thiscall)(void*, Args ...)>(this_ptr, process); auto method = this->get<T(__thiscall)(void*, Args...)>(this_ptr, process);
if (method) return method(args...); if (method)
return T(); return method(args...);
} return T();
}
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const; std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
PIMAGE_NT_HEADERS get_nt_headers() const; PIMAGE_NT_HEADERS get_nt_headers() const;
PIMAGE_DOS_HEADER get_dos_header() const; PIMAGE_DOS_HEADER get_dos_header() const;
PIMAGE_OPTIONAL_HEADER get_optional_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_; HMODULE module_;
}; };
__declspec(noreturn) void raise_hard_exception(); __declspec(noreturn) void raise_hard_exception();
std::string load_resource(int id); std::string load_resource(int id);
void relaunch_self(); void relaunch_self();
__declspec(noreturn) void terminate(uint32_t code = 0); __declspec(noreturn) void terminate(uint32_t code = 0);
} } // namespace utils::nt

View File

@ -1,212 +1,191 @@
#include "signature.hpp" #include "signature.hpp"
#include <thread>
#include <mutex> #include <mutex>
#include <thread>
#include <intrin.h> #include <intrin.h>
namespace utils::hook namespace utils::hook {
{ void signature::load_pattern(const std::string& pattern) {
void signature::load_pattern(const std::string& pattern) this->mask_.clear();
{ this->pattern_.clear();
this->mask_.clear();
this->pattern_.clear();
uint8_t nibble = 0; uint8_t nibble = 0;
auto has_nibble = false; auto has_nibble = false;
for (auto val : pattern) for (auto val : pattern) {
{ if (val == ' ')
if (val == ' ') continue; continue;
if (val == '?') if (val == '?') {
{ this->mask_.push_back(val);
this->mask_.push_back(val); this->pattern_.push_back(0);
this->pattern_.push_back(0); } else {
} if ((val < '0' || val > '9') && (val < 'A' || val > 'F') &&
else (val < 'a' || val > 'f')) {
{ throw std::runtime_error("Invalid pattern");
if ((val < '0' || val > '9') && (val < 'A' || val > 'F') && (val < 'a' || val > 'f')) }
{
throw std::runtime_error("Invalid pattern");
}
char str[] = {val, 0}; 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;
has_nibble = true; nibble = current_nibble;
nibble = current_nibble; } else {
} has_nibble = false;
else const uint8_t byte = current_nibble | (nibble << 4);
{
has_nibble = false;
const uint8_t byte = current_nibble | (nibble << 4);
this->mask_.push_back('x'); this->mask_.push_back('x');
this->pattern_.push_back(byte); this->pattern_.push_back(byte);
} }
} }
} }
while (!this->mask_.empty() && this->mask_.back() == '?') while (!this->mask_.empty() && this->mask_.back() == '?') {
{ this->mask_.pop_back();
this->mask_.pop_back(); this->pattern_.pop_back();
this->pattern_.pop_back(); }
}
if (this->has_sse_support()) if (this->has_sse_support()) {
{ while (this->pattern_.size() < 16) {
while (this->pattern_.size() < 16) this->pattern_.push_back(0);
{ }
this->pattern_.push_back(0); }
}
}
if (has_nibble) if (has_nibble) {
{ throw std::runtime_error("Invalid pattern");
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);
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> result;
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])
{
break;
}
}
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> result;
__declspec(align(16)) char desired_mask[16] = {0};
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()));
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()),
_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))
{
result.push_back(size_t(address));
}
}
return result;
}
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();
return this->process_parallel();
}
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
{
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);
// Only use half of the available cores
const auto grid = range / cores;
std::mutex mutex;
std::vector<size_t> result;
std::vector<std::thread> threads;
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]()
{
auto local_result = this->process_range(start, length);
if (local_result.empty()) return;
std::lock_guard _(mutex);
for (const auto& address : local_result)
{
result.push_back(address);
}
});
}
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)
{
int cpu_id[4];
__cpuid(cpu_id, 0);
if (cpu_id[0] >= 1)
{
__cpuidex(cpu_id, 1, 0);
return (cpu_id[2] & (1 << 20)) != 0;
}
}
return false;
}
} }
utils::hook::signature::signature_result operator"" _sig(const char* str, const size_t len) std::vector<size_t> signature::process_range(uint8_t* start,
{ const size_t length) const {
return utils::hook::signature(std::string(str, len)).process(); 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> result;
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]) {
break;
}
}
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> result;
__declspec(align(16)) char desired_mask[16] = {0};
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()));
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()),
_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)) {
result.push_back(size_t(address));
}
}
return result;
}
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();
return this->process_parallel();
}
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 {
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);
// Only use half of the available cores
const auto grid = range / cores;
std::mutex mutex;
std::vector<size_t> result;
std::vector<std::thread> threads;
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]() {
auto local_result = this->process_range(start, length);
if (local_result.empty())
return;
std::lock_guard _(mutex);
for (const auto& address : local_result) {
result.push_back(address);
}
});
}
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) {
int cpu_id[4];
__cpuid(cpu_id, 0);
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) {
return utils::hook::signature(std::string(str, len)).process();
} }

View File

@ -2,72 +2,61 @@
#include "nt.hpp" #include "nt.hpp"
#include <cstdint> #include <cstdint>
namespace utils::hook namespace utils::hook {
{ class signature final {
class signature final public:
{ class signature_result {
public: public:
class signature_result signature_result(std::vector<size_t>&& matches)
{ : matches_(std::move(matches)) {}
public:
signature_result(std::vector<size_t>&& matches) : matches_(std::move(matches))
{
}
[[nodiscard]] uint8_t* get(const size_t index) const [[nodiscard]] uint8_t* get(const size_t index) const {
{ if (index >= this->count()) {
if (index >= this->count()) throw std::runtime_error("Invalid index");
{ }
throw std::runtime_error("Invalid index");
}
return reinterpret_cast<uint8_t*>(this->matches_[index]); return reinterpret_cast<uint8_t*>(this->matches_[index]);
} }
[[nodiscard]] size_t count() const [[nodiscard]] size_t count() const { return this->matches_.size(); }
{
return this->matches_.size();
}
private: private:
std::vector<size_t> matches_; std::vector<size_t> matches_;
}; };
explicit signature(const std::string& pattern, const nt::library library = {}) 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(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) 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);
this->load_pattern(pattern); }
}
signature_result process() const; signature_result process() const;
private: private:
std::string mask_; std::string mask_;
std::basic_string<uint8_t> pattern_; std::basic_string<uint8_t> pattern_;
uint8_t* start_; uint8_t* start_;
size_t length_; size_t length_;
void load_pattern(const std::string& pattern); void load_pattern(const std::string& pattern);
signature_result process_parallel() const; signature_result process_parallel() const;
signature_result process_serial() const; signature_result process_serial() const;
std::vector<size_t> process_range(uint8_t* start, size_t length) 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_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; 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);

View File

@ -1,179 +1,151 @@
#include "string.hpp" #include "string.hpp"
#include <sstream>
#include <cstdarg>
#include <algorithm> #include <algorithm>
#include <cstdarg>
#include <sstream>
#include "nt.hpp" #include "nt.hpp"
namespace utils::string namespace utils::string {
{ const char* va(const char* fmt, ...) {
const char* va(const char* fmt, ...) static thread_local va_provider<8, 256> provider;
{
static thread_local va_provider<8, 256> provider;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
const char* result = provider.get(fmt, ap); const char* result = provider.get(fmt, ap);
va_end(ap); va_end(ap);
return result; return result;
}
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)
}
return elems;
}
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)
{
return static_cast<char>(toupper(input));
});
return text;
}
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;
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
}
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)
{
result.append(separator);
}
result.append(va("%02X", data[i] & 0xFF));
}
return result;
}
std::string get_clipboard_data()
{
if (OpenClipboard(nullptr))
{
std::string data;
auto* const clipboard_data = GetClipboardData(1u);
if (clipboard_data)
{
auto* const cliptext = static_cast<char*>(GlobalLock(clipboard_data));
if (cliptext)
{
data.append(cliptext);
GlobalUnlock(clipboard_data);
}
}
CloseClipboard();
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 result;
result.reserve(wstr.size());
for (const auto& chr : wstr)
{
result.push_back(static_cast<char>(chr));
}
return result;
}
std::wstring convert(const std::string& str)
{
std::wstring result;
result.reserve(str.size());
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())
{
return str;
}
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
} }
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)
}
return elems;
}
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) {
return static_cast<char>(toupper(input));
});
return text;
}
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;
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
}
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) {
result.append(separator);
}
result.append(va("%02X", data[i] & 0xFF));
}
return result;
}
std::string get_clipboard_data() {
if (OpenClipboard(nullptr)) {
std::string data;
auto* const clipboard_data = GetClipboardData(1u);
if (clipboard_data) {
auto* const cliptext = static_cast<char*>(GlobalLock(clipboard_data));
if (cliptext) {
data.append(cliptext);
GlobalUnlock(clipboard_data);
}
}
CloseClipboard();
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';
}
std::string convert(const std::wstring& wstr) {
std::string result;
result.reserve(wstr.size());
for (const auto& chr : wstr) {
result.push_back(static_cast<char>(chr));
}
return result;
}
std::wstring convert(const std::string& str) {
std::wstring result;
result.reserve(str.size());
for (const auto& chr : str) {
result.push_back(static_cast<wchar_t>(chr));
}
return result;
}
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) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
} // namespace utils::string

View File

@ -3,98 +3,95 @@
#include <cstdint> #include <cstdint>
#ifndef ARRAYSIZE #ifndef ARRAYSIZE
template <class Type, size_t n> template <class Type, size_t n> size_t ARRAYSIZE(Type (&)[n]) { return n; }
size_t ARRAYSIZE(Type(&)[n]) { return n; }
#endif #endif
namespace utils::string namespace utils::string {
{ template <size_t Buffers, size_t MinBufferSize> class va_provider final {
template <size_t Buffers, size_t MinBufferSize> public:
class va_provider final static_assert(Buffers != 0 && MinBufferSize != 0,
{ "Buffers and MinBufferSize mustn't be 0");
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_);
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_); auto entry = &this->string_pool_[this->current_buffer_];
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");
throw std::runtime_error("String pool not initialized"); }
}
while (true) while (true) {
{ const int res =
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
if (res > 0) break; // Success if (res > 0)
if (res == 0) return nullptr; // Error break; // Success
if (res == 0)
return nullptr; // Error
entry->double_size(); entry->double_size();
} }
return entry->buffer; return entry->buffer;
} }
private: private:
class entry final class entry final {
{ public:
public: explicit entry(const size_t _size = MinBufferSize)
explicit entry(const size_t _size = MinBufferSize) : size(_size), buffer(nullptr) : size(_size), buffer(nullptr) {
{ if (this->size < MinBufferSize)
if (this->size < MinBufferSize) this->size = MinBufferSize; this->size = MinBufferSize;
this->allocate(); this->allocate();
} }
~entry() ~entry() {
{ if (this->buffer)
if (this->buffer) memory::get_allocator()->free(this->buffer); memory::get_allocator()->free(this->buffer);
this->size = 0; this->size = 0;
this->buffer = nullptr; this->buffer = nullptr;
} }
void allocate() void allocate() {
{ if (this->buffer)
if (this->buffer) memory::get_allocator()->free(this->buffer); memory::get_allocator()->free(this->buffer);
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1); this->buffer =
} memory::get_allocator()->allocate_array<char>(this->size + 1);
}
void double_size() void double_size() {
{ this->size *= 2;
this->size *= 2; this->allocate();
this->allocate(); }
}
size_t size; size_t size;
char* buffer; char* buffer;
}; };
size_t current_buffer_; size_t current_buffer_;
entry string_pool_[Buffers]; 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_lower(std::string text);
std::string to_upper(std::string text); std::string to_upper(std::string text);
bool starts_with(const std::string& text, const std::string& substring); bool starts_with(const std::string& text, const std::string& substring);
bool ends_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); void strip(const char* in, char* out, int max);
std::string convert(const std::wstring& wstr); std::string convert(const std::wstring& wstr);
std::wstring convert(const std::string& str); std::wstring convert(const std::string& str);
std::string replace(std::string str, const std::string& from, const std::string& to); std::string replace(std::string str, const std::string& from,
} const std::string& to);
} // namespace utils::string