mirror of
https://github.com/diamante0018/BlackOpsPlugin.git
synced 2025-04-18 17:42:54 +00:00
init
This commit is contained in:
commit
c8b84a9b6a
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
153
.gitignore
vendored
Normal file
153
.gitignore
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
### Windows
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Shortcuts
|
||||
*.lnk
|
||||
|
||||
### OSX
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Visual Studio
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
build
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
#Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
### IDA
|
||||
*.id0
|
||||
*.id1
|
||||
*.id2
|
||||
*.nam
|
||||
*.til
|
||||
|
||||
### Custom user files
|
||||
# User scripts
|
||||
user*.bat
|
||||
|
||||
# Premake binary
|
||||
#premake5.exe
|
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/minhook"]
|
||||
path = deps/minhook
|
||||
url = https://github.com/TsudaKageyu/minhook.git
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson.git
|
29
LICENSE
Normal file
29
LICENSE
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Edoardo Sanguineti
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Black-Ops-Plugin
|
||||
I have no clue as to what this does. I use this program to test experimental features.
|
||||
|
||||
## Thanks to
|
||||
*[momo5502](https://github.com/momo5502)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software has been created purely for the purposes of academic research. It is not intended to be used to attack other systems. Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
|
||||
|
||||
## Compile from source
|
||||
|
||||
- Clone the Git repo. Do NOT download it as ZIP, that won't work.
|
||||
- Update the submodules and run `premake5 vs2019` or simply use the delivered `generate.bat`.
|
||||
- Build via solution file found inside the build folder.
|
1
deps/GSL
vendored
Submodule
1
deps/GSL
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c412deb31e73c9b824abeb6619e11511b279222f
|
1
deps/minhook
vendored
Submodule
1
deps/minhook
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18
|
19
deps/premake/gsl.lua
vendored
Normal file
19
deps/premake/gsl.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
gsl = {
|
||||
source = path.join(dependencies.basePath, "GSL")
|
||||
}
|
||||
|
||||
function gsl.import()
|
||||
gsl.includes()
|
||||
end
|
||||
|
||||
function gsl.includes()
|
||||
includedirs {
|
||||
path.join(gsl.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function gsl.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, gsl)
|
31
deps/premake/minhook.lua
vendored
Normal file
31
deps/premake/minhook.lua
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
minhook = {
|
||||
source = path.join(dependencies.basePath, "minhook")
|
||||
}
|
||||
|
||||
function minhook.import()
|
||||
links { "minhook" }
|
||||
minhook.includes()
|
||||
end
|
||||
|
||||
function minhook.includes()
|
||||
includedirs {
|
||||
path.join(minhook.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function minhook.project()
|
||||
project "minhook"
|
||||
language "C"
|
||||
|
||||
minhook.includes()
|
||||
|
||||
files {
|
||||
path.join(minhook.source, "src/**.h"),
|
||||
path.join(minhook.source, "src/**.c")
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, minhook)
|
19
deps/premake/rapidjson.lua
vendored
Normal file
19
deps/premake/rapidjson.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
rapidjson = {
|
||||
source = path.join(dependencies.basePath, "rapidjson"),
|
||||
}
|
||||
|
||||
function rapidjson.import()
|
||||
rapidjson.includes()
|
||||
end
|
||||
|
||||
function rapidjson.includes()
|
||||
includedirs {
|
||||
path.join(rapidjson.source, "include"),
|
||||
}
|
||||
end
|
||||
|
||||
function rapidjson.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, rapidjson)
|
1
deps/rapidjson
vendored
Submodule
1
deps/rapidjson
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit fd3dc29a5c2852df569e1ea81dbde2c412ac5051
|
4
generate.bat
Normal file
4
generate.bat
Normal file
@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
echo Updating submodules...
|
||||
call git submodule update --init --recursive
|
||||
tools\premake5 %* vs2019
|
94
premake5.lua
Normal file
94
premake5.lua
Normal file
@ -0,0 +1,94 @@
|
||||
dependencies = {
|
||||
basePath = "./deps"
|
||||
}
|
||||
|
||||
function dependencies.load()
|
||||
dir = path.join(dependencies.basePath, "premake/*.lua")
|
||||
deps = os.matchfiles(dir)
|
||||
|
||||
for i, dep in pairs(deps) do
|
||||
dep = dep:gsub(".lua", "")
|
||||
require(dep)
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.imports()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.import()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.projects()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.project()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newoption {
|
||||
trigger = "copy-to",
|
||||
description = "Optional, copy the EXE to a custom folder after build, define the path here if wanted.",
|
||||
value = "PATH"
|
||||
}
|
||||
|
||||
dependencies.load()
|
||||
|
||||
workspace "black-ops-plugin"
|
||||
location "./build"
|
||||
objdir "%{wks.location}/obj"
|
||||
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||
targetname "%{prj.name}"
|
||||
|
||||
configurations {"Debug", "Release"}
|
||||
|
||||
language "C++"
|
||||
|
||||
architecture "x86"
|
||||
platforms "x86"
|
||||
|
||||
systemversion "latest"
|
||||
symbols "On"
|
||||
staticruntime "On"
|
||||
editandcontinue "Off"
|
||||
warnings "Extra"
|
||||
characterset "ASCII"
|
||||
|
||||
flags { "NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks" }
|
||||
|
||||
filter "windows"
|
||||
defines {"_WINDOWS", "WIN32"}
|
||||
|
||||
filter "Release"
|
||||
optimize "Size"
|
||||
buildoptions {"/GL"}
|
||||
linkoptions { "/IGNORE:4702", "/LTCG" }
|
||||
|
||||
defines {"NDEBUG"}
|
||||
|
||||
flags {"FatalCompileWarnings"}
|
||||
|
||||
filter "Debug"
|
||||
optimize "Debug"
|
||||
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
|
||||
filter {}
|
||||
|
||||
project "black-ops-plugin"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
files {"./src/**.hpp", "./src/**.cpp"}
|
||||
|
||||
includedirs {"src"}
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
group "Dependencies"
|
||||
dependencies.projects()
|
||||
|
||||
workspace "*"
|
||||
cppdialect "C++20"
|
17
src/component/chat.cpp
Normal file
17
src/component/chat.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
namespace chat
|
||||
{
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(chat::component)
|
163
src/component/command.cpp
Normal file
163
src/component/command.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/nt.hpp"
|
||||
#include "utils/io.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
namespace command
|
||||
{
|
||||
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||
|
||||
void main_handler()
|
||||
{
|
||||
params params = {};
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
if (handlers.find(command) != handlers.end())
|
||||
{
|
||||
handlers[command](params);
|
||||
}
|
||||
}
|
||||
|
||||
params::params()
|
||||
: nesting_(game::cmd_args->nesting)
|
||||
{
|
||||
}
|
||||
|
||||
int params::size() const
|
||||
{
|
||||
return game::cmd_args->argc[this->nesting_];
|
||||
}
|
||||
|
||||
const char* params::get(const int index) const
|
||||
{
|
||||
if (index >= this->size())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::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();
|
||||
});
|
||||
|
||||
add("dvarDump", [](const params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string filename = "dump/";
|
||||
filename.append(params.get(1));
|
||||
if (!filename.ends_with(".txt"))
|
||||
{
|
||||
filename.append(".txt");
|
||||
}
|
||||
|
||||
for (auto i = 0; i < *game::dvarCount; i++)
|
||||
{
|
||||
const auto dvar = game::sortedDvars[i];
|
||||
|
||||
if (dvar)
|
||||
{
|
||||
const auto line = std::format("{} \"{}\"\r\n", dvar->name,
|
||||
game::Dvar_DisplayableValue(dvar));
|
||||
utils::io::write_file(filename, line, i != 0);
|
||||
}
|
||||
}
|
||||
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER, "%i dvars\n", *game::dvarCount);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
30
src/component/command.hpp
Normal file
30
src/component/command.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
namespace command
|
||||
{
|
||||
class params
|
||||
{
|
||||
public:
|
||||
params();
|
||||
|
||||
int size() const;
|
||||
const char* get(int index) const;
|
||||
std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const
|
||||
{
|
||||
return this->get(index);
|
||||
}
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
void add_raw(const char* name, void (*callback)());
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
|
||||
void add_script_command(const std::string& name, const std::function<void(const params&)>& callback);
|
||||
void clear_script_commands();
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
}
|
17
src/component/gameplay.cpp
Normal file
17
src/component/gameplay.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
namespace gameplay
|
||||
{
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(gameplay::component)
|
21
src/game/game.cpp
Normal file
21
src/game/game.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
namespace game
|
||||
{
|
||||
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s
|
||||
? gamemode::multiplayer
|
||||
: gamemode::zombies;
|
||||
|
||||
namespace environment
|
||||
{
|
||||
bool t6mp()
|
||||
{
|
||||
return current == gamemode::multiplayer;
|
||||
}
|
||||
|
||||
bool t6zm()
|
||||
{
|
||||
return current == gamemode::zombies;
|
||||
}
|
||||
}
|
||||
}
|
59
src/game/game.hpp
Normal file
59
src/game/game.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#define SELECT(mp, zm) (game::environment::t6mp() ? mp : zm)
|
||||
|
||||
namespace game
|
||||
{
|
||||
enum gamemode
|
||||
{
|
||||
none,
|
||||
multiplayer,
|
||||
zombies
|
||||
};
|
||||
|
||||
extern gamemode current;
|
||||
|
||||
namespace environment
|
||||
{
|
||||
bool t5mp();
|
||||
bool t5zm();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class symbol
|
||||
{
|
||||
public:
|
||||
symbol(const size_t t5mp, const size_t t5zm)
|
||||
: t5mp_(reinterpret_cast<T*>(t5mp))
|
||||
, t5zm_(reinterpret_cast<T*>(t5zm))
|
||||
{
|
||||
}
|
||||
|
||||
T* get() const
|
||||
{
|
||||
if (environment::t5mp())
|
||||
{
|
||||
return t5mp_;
|
||||
}
|
||||
|
||||
return t5zm_;
|
||||
}
|
||||
|
||||
operator T* () const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
private:
|
||||
T* t5mp_;
|
||||
T* t5zm_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "symbols.hpp"
|
527
src/game/structs.hpp
Normal file
527
src/game/structs.hpp
Normal file
@ -0,0 +1,527 @@
|
||||
#pragma once
|
||||
|
||||
namespace game
|
||||
{
|
||||
typedef float vec_t;
|
||||
typedef vec_t vec2_t[2];
|
||||
typedef vec_t vec3_t[3];
|
||||
typedef vec_t vec4_t[4];
|
||||
|
||||
enum FsListBehavior_e
|
||||
{
|
||||
FS_LIST_PURE_ONLY = 0x0,
|
||||
FS_LIST_ALL = 0x1
|
||||
};
|
||||
|
||||
enum svscmd_type
|
||||
{
|
||||
SV_CMD_CAN_IGNORE,
|
||||
SV_CMD_RELIABLE
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ERR_FATAL = 0x0,
|
||||
ERR_DROP = 0x1,
|
||||
ERR_SERVERDISCONNECT = 0x2,
|
||||
ERR_DISCONNECT = 0x3,
|
||||
ERR_SCRIPT = 0x4,
|
||||
ERR_SCRIPT_DROP = 0x5,
|
||||
ERR_LOCALIZATION = 0x6
|
||||
} errorParm_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LOCAL_CLIENT_INVALID = -1,
|
||||
LOCAL_CLIENT_0 = 0,
|
||||
LOCAL_CLIENT_1 = 1,
|
||||
LOCAL_CLIENT_2 = 2,
|
||||
LOCAL_CLIENT_3 = 3,
|
||||
LOCAL_CLIENT_LAST = 3,
|
||||
LOCAL_CLIENT_COUNT = 4
|
||||
} LocalClientNum_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CON_CHANNEL_DONT_FILTER = 0x0,
|
||||
CON_CHANNEL_ERROR = 0x1,
|
||||
CON_CHANNEL_GAMENOTIFY = 0x2,
|
||||
CON_CHANNEL_BOLDGAME = 0x3,
|
||||
CON_CHANNEL_SUBTITLE = 0x4,
|
||||
CON_CHANNEL_OBITUARY = 0x5,
|
||||
CON_CHANNEL_LOGFILEONLY = 0x6,
|
||||
CON_CHANNEL_CONSOLEONLY = 0x7,
|
||||
CON_CHANNEL_GFX = 0x8,
|
||||
CON_CHANNEL_SOUND = 0x9,
|
||||
CON_CHANNEL_FILES = 0xA,
|
||||
CON_CHANNEL_DEVGUI = 0xB,
|
||||
CON_CHANNEL_PROFILE = 0xC,
|
||||
CON_CHANNEL_UI = 0xD,
|
||||
CON_CHANNEL_CLIENT = 0xE,
|
||||
CON_CHANNEL_SERVER = 0xF,
|
||||
CON_CHANNEL_SYSTEM = 0x10,
|
||||
CON_CHANNEL_PLAYERWEAP = 0x11,
|
||||
CON_CHANNEL_AI = 0x12,
|
||||
CON_CHANNEL_ANIM = 0x13,
|
||||
CON_CHANNEL_PHYS = 0x14,
|
||||
CON_CHANNEL_FX = 0x15,
|
||||
CON_CHANNEL_LEADERBOARDS = 0x16,
|
||||
CON_CHANNEL_LIVE = 0x17,
|
||||
CON_CHANNEL_PARSERSCRIPT = 0x18,
|
||||
CON_CHANNEL_SCRIPT = 0x19,
|
||||
CON_CHANNEL_SPAWNSYSTEM = 0x1A,
|
||||
CON_CHANNEL_COOPINFO = 0x1B,
|
||||
CON_CHANNEL_SERVERDEMO = 0x1C,
|
||||
CON_CHANNEL_DDL = 0x1D,
|
||||
CON_CHANNEL_NETWORK = 0x1E,
|
||||
CON_CHANNEL_SCHEDULER = 0x1F,
|
||||
CON_FIRST_DEBUG_CHANNEL = 0x1F,
|
||||
CON_CHANNEL_TASK = 0x20,
|
||||
CON_CHANNEL_SPU = 0x21,
|
||||
CON_BUILTIN_CHANNEL_COUNT = 0x22
|
||||
} conChannel_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CON_DEST_CONSOLE = 0,
|
||||
CON_DEST_MINICON = 1,
|
||||
CON_DEST_ERROR = 2,
|
||||
CON_DEST_GAME_FIRST = 3,
|
||||
CON_DEST_GAME1 = 3,
|
||||
CON_DEST_GAME2 = 4,
|
||||
CON_DEST_GAME3 = 5,
|
||||
CON_DEST_GAME_LAST = 5,
|
||||
CON_DEST_COUNT = 6
|
||||
} print_msg_dest_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NS_CLIENT1 = 0x0,
|
||||
NS_SERVER = 0x1,
|
||||
NS_MAXCLIENTS = 0x1,
|
||||
NS_PACKET = 0x2
|
||||
} netsrc_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CS_MOTD = 11,
|
||||
CS_TIMESCALE = 14
|
||||
} ConfigString;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FS_READ = 0,
|
||||
FS_WRITE = 1,
|
||||
FS_APPEND = 2,
|
||||
FS_APPEND_SYNC = 3
|
||||
} fsMode_t;
|
||||
|
||||
struct PrintChannel
|
||||
{
|
||||
char name[32];
|
||||
bool allowScript;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PrintChannel) == 33);
|
||||
|
||||
struct PrintChannelGlob
|
||||
{
|
||||
PrintChannel openChannels[256];
|
||||
unsigned int filters[6][8];
|
||||
};
|
||||
|
||||
struct usercmd_s
|
||||
{
|
||||
int serverTime;
|
||||
int button_bits[2];
|
||||
int angles[3];
|
||||
unsigned __int16 weapon;
|
||||
unsigned __int16 offHandIndex;
|
||||
unsigned __int16 lastWeaponAltModeSwitch;
|
||||
char forwardmove;
|
||||
char rightmove;
|
||||
char upmove;
|
||||
char pitchmove;
|
||||
char yawmove;
|
||||
float meleeChargeYaw;
|
||||
unsigned char meleeChargeDist;
|
||||
float rollmove;
|
||||
char selectedLocation[2];
|
||||
unsigned char selectedYaw;
|
||||
};
|
||||
|
||||
static_assert(sizeof(usercmd_s) == 52);
|
||||
|
||||
struct cmd_function_t
|
||||
{
|
||||
cmd_function_t* next;
|
||||
const char* name;
|
||||
const char* autoCompleteDir;
|
||||
const char* autoCompleteExt;
|
||||
void(__cdecl* function)();
|
||||
bool consoleAccess;
|
||||
};
|
||||
|
||||
static_assert(sizeof(cmd_function_t) == 24);
|
||||
|
||||
struct CmdArgs
|
||||
{
|
||||
int nesting;
|
||||
int localClientNum[8];
|
||||
int controllerIndex[8];
|
||||
void* itemDef[8];
|
||||
int argshift[8];
|
||||
int argc[8];
|
||||
const char** argv[8];
|
||||
char textPool[8192];
|
||||
const char* argvPool[512];
|
||||
int usedTextPool[8];
|
||||
int totalUsedArgvPool;
|
||||
int totalUsedTextPool;
|
||||
};
|
||||
|
||||
static_assert(sizeof(CmdArgs) == 10476);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NA_BOT = 0x0,
|
||||
NA_BAD = 0x1,
|
||||
NA_LOOPBACK = 0x2,
|
||||
NA_BROADCAST = 0x3,
|
||||
NA_IP = 0x4
|
||||
} netadrtype_t;
|
||||
|
||||
struct netadr_s // Confirm nigga
|
||||
{
|
||||
netadrtype_t type;
|
||||
unsigned char ip[4];
|
||||
unsigned __int16 port;
|
||||
int addrHandleIndex;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netadr_s) == 16);
|
||||
|
||||
struct msg_t
|
||||
{
|
||||
int overflowed;
|
||||
int readOnly;
|
||||
unsigned char* data;
|
||||
unsigned char* splitData;
|
||||
int maxsize;
|
||||
int cursize;
|
||||
int splitSize;
|
||||
int readcount;
|
||||
int bit;
|
||||
int lastEntityRef;
|
||||
int flush;
|
||||
netsrc_t targetLocalNetID;
|
||||
};
|
||||
|
||||
static_assert(sizeof(msg_t) == 48);
|
||||
|
||||
enum dvar_flags : unsigned __int16
|
||||
{
|
||||
DVAR_FLAG_SAVED = 0x1,
|
||||
DVAR_FLAG_WRITEPROTECTED = 0x10,
|
||||
DVAR_FLAG_READONLY = 0x40,
|
||||
DVAR_FLAG_CHEAT = 0x80,
|
||||
DVAR_FLAG_REPLICATED = 0x100,
|
||||
};
|
||||
|
||||
typedef enum : char
|
||||
{
|
||||
DVAR_TYPE_BOOL = 0x0,
|
||||
DVAR_TYPE_FLOAT = 0x1,
|
||||
DVAR_TYPE_FLOAT_2 = 0x2,
|
||||
DVAR_TYPE_FLOAT_3 = 0x3,
|
||||
DVAR_TYPE_FLOAT_4 = 0x4,
|
||||
DVAR_TYPE_INT = 0x5,
|
||||
DVAR_TYPE_ENUM = 0x6,
|
||||
DVAR_TYPE_STRING = 0x7,
|
||||
DVAR_TYPE_COLOR = 0x8,
|
||||
DVAR_TYPE_INT64 = 0x9,
|
||||
DVAR_TYPE_LINEAR_COLOR_RGB = 0xA,
|
||||
DVAR_TYPE_COLOR_XYZ = 0xB,
|
||||
DVAR_TYPE_COUNT = 0xC
|
||||
} dvarType_t;
|
||||
|
||||
union DvarValue
|
||||
{
|
||||
bool enabled;
|
||||
int integer;
|
||||
unsigned int unsignedInt;
|
||||
__int64 integer64;
|
||||
unsigned __int64 unsignedInt64;
|
||||
float value;
|
||||
float vector[4];
|
||||
const char* string;
|
||||
unsigned char color[4];
|
||||
};
|
||||
|
||||
struct enum_limit
|
||||
{
|
||||
int stringCount;
|
||||
const char** strings;
|
||||
};
|
||||
|
||||
struct int_limit
|
||||
{
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
struct int64_limit
|
||||
{
|
||||
__int64 min;
|
||||
__int64 max;
|
||||
};
|
||||
|
||||
struct float_limit
|
||||
{
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
|
||||
union DvarLimits
|
||||
{
|
||||
enum_limit enumeration;
|
||||
int_limit integer;
|
||||
float_limit value;
|
||||
float_limit vector;
|
||||
};
|
||||
|
||||
typedef struct dvar_s
|
||||
{
|
||||
const char* name;
|
||||
const char* description;
|
||||
long hash;
|
||||
unsigned int flags;
|
||||
dvarType_t type;
|
||||
bool modified;
|
||||
bool loadedFromSaveGame;
|
||||
DvarValue current;
|
||||
DvarValue latched;
|
||||
DvarValue reset;
|
||||
DvarValue saved;
|
||||
DvarLimits domain;
|
||||
dvar_s* hashNext;
|
||||
unsigned char pad0[8];
|
||||
} dvar_t;
|
||||
|
||||
static_assert(sizeof(dvar_s) == 112);
|
||||
|
||||
enum playerFlag
|
||||
{
|
||||
PLAYER_FLAG_NOCLIP = 1 << 0,
|
||||
PLAYER_FLAG_UFO = 1 << 1,
|
||||
PLAYER_FLAG_FROZEN = 1 << 2,
|
||||
};
|
||||
|
||||
struct gclient_s
|
||||
{
|
||||
char __pad0[10396];
|
||||
int flags;
|
||||
char __pad1[320];
|
||||
};
|
||||
|
||||
static_assert(sizeof(gclient_s) == 10720);
|
||||
|
||||
enum entityFlag
|
||||
{
|
||||
FL_GODMODE = 1,
|
||||
FL_DEMI_GODMODE = 2,
|
||||
FL_NOTARGET = 4,
|
||||
FL_NO_KNOCKBACK = 8,
|
||||
FL_DROPPED_ITEM = 16,
|
||||
FL_NO_BOTS = 32,
|
||||
FL_NO_HUMANS = 64,
|
||||
FL_TOGGLE = 128,
|
||||
FL_SOFTACTIVATE = 256,
|
||||
FL_LOW_PRIORITY_USEABLE = 512,
|
||||
FL_NO_HEADCHECK = 1024,
|
||||
FL_DYNAMICPATH = 2048,
|
||||
FL_SUPPORTS_LINKTO = 4096,
|
||||
FL_NO_AUTO_ANIM_UPDATE = 8192,
|
||||
FL_GRENADE_TOUCH_DAMAGE = 16384,
|
||||
FL_GRENADE_MARTYRDOM = 32768,
|
||||
FL_MISSILE_DESTABILIZED = 65536,
|
||||
FL_STABLE_MISSILES = 131072,
|
||||
FL_REPEAT_ANIM_UPDATE = 262144,
|
||||
FL_VEHICLE_TARGET = 524288,
|
||||
FL_GROUND_ENT = 1048576,
|
||||
FL_CURSOR_HINT = 2097152,
|
||||
FL_USE_TURRET = 4194304,
|
||||
FL_MISSILE_ATTRACTOR = 8388608,
|
||||
FL_TARGET = 16777216,
|
||||
FL_WEAPON_BEING_GRABBED = 33554432,
|
||||
FL_OBSTACLE = 67108864,
|
||||
FL_DODGE_LEFT = 134217728,
|
||||
FL_DODGE_RIGHT = 268435456,
|
||||
FL_BADPLACE_VOLUME = 536870912,
|
||||
FL_AUTO_BLOCKPATHS = 1073741824
|
||||
};
|
||||
|
||||
enum playerStateFlag
|
||||
{
|
||||
PMF_PRONE = 0x1,
|
||||
PMF_DUCKED = 0x2
|
||||
};
|
||||
|
||||
struct gentity_s
|
||||
{
|
||||
int entnum;
|
||||
char __pad0[320];
|
||||
gclient_s* client;
|
||||
char __pad1[44];
|
||||
int flags;
|
||||
char __pad2[384];
|
||||
};
|
||||
|
||||
static_assert(sizeof(gentity_s) == 760);
|
||||
|
||||
struct netProfilePacket_t
|
||||
{
|
||||
int iTime;
|
||||
int iSize;
|
||||
int bFragment;
|
||||
};
|
||||
|
||||
struct netProfileStream_t
|
||||
{
|
||||
netProfilePacket_t packets[60];
|
||||
int iCurrPacket;
|
||||
int iBytesPerSecond;
|
||||
int iLastBPSCalcTime;
|
||||
int iCountedPackets;
|
||||
int iCountedFragments;
|
||||
int iFragmentPercentage;
|
||||
int iLargestPacket;
|
||||
int iSmallestPacket;
|
||||
};
|
||||
|
||||
struct netProfileInfo_t
|
||||
{
|
||||
netProfileStream_t send;
|
||||
netProfileStream_t recieve;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netProfileInfo_t) == 1504);
|
||||
|
||||
struct netchan_t
|
||||
{
|
||||
int outgoingSequence;
|
||||
netsrc_t sock;
|
||||
int dropped;
|
||||
int incomingSequence;
|
||||
netadr_s remoteAddress;
|
||||
int qport;
|
||||
int fragmentSequence;
|
||||
int fragmentLength;
|
||||
unsigned char* fragmentBuffer;
|
||||
int fragmentBufferSize;
|
||||
int unsentFragments;
|
||||
int unsentFragmentStart;
|
||||
int unsentLength;
|
||||
unsigned char* unsentBuffer;
|
||||
int unsentBufferSize;
|
||||
int reliable_fragments;
|
||||
unsigned char fragment_send_count[128];
|
||||
unsigned int fragment_ack[4];
|
||||
int lowest_send_count;
|
||||
netProfileInfo_t prof;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netchan_t) == 1728);
|
||||
|
||||
struct MantleState
|
||||
{
|
||||
float yaw;
|
||||
int timer;
|
||||
int transIndex;
|
||||
int flags;
|
||||
};
|
||||
|
||||
enum EffectiveStance
|
||||
{
|
||||
PM_EFF_STANCE_DEFAULT = 0,
|
||||
PM_EFF_STANCE_PRONE = 1,
|
||||
PM_EFF_STANCE_DUCKED = 2,
|
||||
PM_EFF_STANCE_LASTSTANDCRAWL = 3,
|
||||
PM_EFF_STANCE_COUNT = 4
|
||||
};
|
||||
|
||||
enum ViewLockTypes
|
||||
{
|
||||
PLAYERVIEWLOCK_NONE = 0,
|
||||
PLAYERVIEWLOCK_FULL = 1,
|
||||
PLAYERVIEWLOCK_WEAPONJITTER = 2,
|
||||
PLAYERVIEWLOCKCOUNT = 3
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PM_NORMAL = 0,
|
||||
PM_NORMAL_LINKED = 1,
|
||||
PM_NOCLIP = 2,
|
||||
PM_UFO = 3,
|
||||
PM_SPECTATOR = 4,
|
||||
PM_INTERMISSION = 5,
|
||||
PM_LASTSTAND = 6,
|
||||
PM_REVIVEE = 7,
|
||||
PM_LASTSTAND_TRANSITION = 8,
|
||||
PM_DEAD = 9,
|
||||
PM_DEAD_LINKED = 10
|
||||
} pmtype_t;
|
||||
|
||||
enum clientState_t
|
||||
{
|
||||
CS_FREE = 0,
|
||||
CS_ZOMBIE = 1,
|
||||
CS_RECONNECTING = 2,
|
||||
CS_CONNECTED = 3,
|
||||
CS_CLIENTLOADING = 4,
|
||||
CS_ACTIVE = 5
|
||||
};
|
||||
|
||||
struct PredictedVehicleInfo
|
||||
{
|
||||
bool inVehicle;
|
||||
vec3_t origin;
|
||||
vec3_t angles;
|
||||
vec3_t tVel;
|
||||
vec3_t aVel;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PredictedVehicleInfo) == 52);
|
||||
|
||||
struct clientHeader_t
|
||||
{
|
||||
clientState_t state; // 0
|
||||
int sendAsActive; // 4
|
||||
int deltaMessage; // 8
|
||||
int rateDealyed; // 12
|
||||
netchan_t netchan; // 24
|
||||
vec3_t predictedOrigin;
|
||||
int predictedOriginServerTime;
|
||||
PredictedVehicleInfo vehicle;
|
||||
};
|
||||
|
||||
static_assert(sizeof(clientHeader_t) == 1812);
|
||||
|
||||
struct svscmd_info_t
|
||||
{
|
||||
const char* cmd;
|
||||
int time;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct client_s
|
||||
{
|
||||
clientHeader_t header;
|
||||
const char* dropReason;
|
||||
char userinfo[1024];
|
||||
char reliableCommandBuffer[16384];
|
||||
int reliableCommandBufferNext;
|
||||
svscmd_info_t reliableCommandInfo[128];
|
||||
};
|
||||
}
|
26
src/game/symbols.hpp
Normal file
26
src/game/symbols.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game
|
||||
{
|
||||
// Functions
|
||||
WEAK symbol<void(errorParm_t, const char*, ...)> Com_Error{0x627380, 0x0};
|
||||
WEAK symbol<void(conChannel_t, const char*, ...)> Com_Printf{0x4126C0, 0x0};
|
||||
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x56EF70, 0x0};
|
||||
WEAK symbol<void(LocalClientNum_t, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x50B470, 0x0};
|
||||
|
||||
WEAK symbol<dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x0};
|
||||
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0, 0x0};
|
||||
|
||||
WEAK symbol<void(const char*, void(), cmd_function_t*)> Cmd_AddCommandInternal{0x6AD580, 0x0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x527EA0, 0x0};
|
||||
|
||||
WEAK symbol<char*(int)> ConcatArgs{0x5D5F10, 0x0};
|
||||
|
||||
WEAK symbol<CmdArgs> cmd_args{0x355BD88, 0x0};
|
||||
WEAK symbol<int> dvarCount{0x385BE74, 0x0};
|
||||
WEAK symbol<dvar_t*> sortedDvars{0x385BE88, 0x0};
|
||||
WEAK symbol<gentity_s> g_entities{0x32E5640, 0x0};
|
||||
}
|
35
src/loader/component_interface.hpp
Normal file
35
src/loader/component_interface.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
class component_interface
|
||||
{
|
||||
public:
|
||||
virtual ~component_interface()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_start()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_load()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void pre_destroy()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_unpack()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool is_supported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
127
src/loader/component_loader.cpp
Normal file
127
src/loader/component_loader.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(std::unique_ptr<component_interface>&& component_)
|
||||
{
|
||||
get_components().push_back(std::move(component_));
|
||||
}
|
||||
|
||||
bool component_loader::post_start()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
handled = true;
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_start();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool component_loader::post_load()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
handled = true;
|
||||
|
||||
clean();
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_load();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void component_loader::post_unpack()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_unpack();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::pre_destroy()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->pre_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::clean()
|
||||
{
|
||||
auto& components = get_components();
|
||||
for (auto i = components.begin(); i != components.end();)
|
||||
{
|
||||
if (!(*i)->is_supported())
|
||||
{
|
||||
(*i)->pre_destroy();
|
||||
i = components.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* component_loader::load_import(const std::string& library, const std::string& function)
|
||||
{
|
||||
void* function_ptr = nullptr;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
auto* const component_function_ptr = component_->load_import(library, function);
|
||||
if (component_function_ptr)
|
||||
{
|
||||
function_ptr = component_function_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return function_ptr;
|
||||
}
|
||||
|
||||
void component_loader::trigger_premature_shutdown()
|
||||
{
|
||||
throw premature_shutdown_trigger();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component_interface>>& component_loader::get_components()
|
||||
{
|
||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
|
||||
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
||||
{
|
||||
pre_destroy();
|
||||
delete component_vector;
|
||||
});
|
||||
|
||||
return *components;
|
||||
}
|
61
src/loader/component_loader.hpp
Normal file
61
src/loader/component_loader.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "component_interface.hpp"
|
||||
|
||||
class component_loader final
|
||||
{
|
||||
public:
|
||||
class premature_shutdown_trigger final : public std::exception
|
||||
{
|
||||
[[nodiscard]] const char* what() const noexcept override
|
||||
{
|
||||
return "Premature shutdown requested";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class installer final
|
||||
{
|
||||
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
||||
|
||||
public:
|
||||
installer()
|
||||
{
|
||||
register_component(std::make_unique<T>());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static T* get()
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
if (typeid(*component_.get()) == typeid(T))
|
||||
{
|
||||
return reinterpret_cast<T*>(component_.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void register_component(std::unique_ptr<component_interface>&& component);
|
||||
|
||||
static bool post_start();
|
||||
static bool post_load();
|
||||
static void post_unpack();
|
||||
static void pre_destroy();
|
||||
static void clean();
|
||||
|
||||
static void* load_import(const std::string& library, const std::string& function);
|
||||
|
||||
static void trigger_premature_shutdown();
|
||||
|
||||
private:
|
||||
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
||||
};
|
||||
|
||||
#define REGISTER_COMPONENT(name) \
|
||||
namespace \
|
||||
{ \
|
||||
static component_loader::installer<name> __component; \
|
||||
}
|
16
src/main.cpp
Normal file
16
src/main.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
||||
{
|
||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
else if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
||||
{
|
||||
component_loader::pre_destroy();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
1
src/stdinc.cpp
Normal file
1
src/stdinc.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include <stdinc.hpp>
|
33
src/stdinc.hpp
Normal file
33
src/stdinc.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#define BINARY_PAYLOAD_SIZE 0x0A000000
|
||||
#define TLS_PAYLOAD_SIZE 0x2000
|
||||
|
||||
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <csetjmp>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
46
src/utils/concurrency.hpp
Normal file
46
src/utils/concurrency.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace utils::concurrency
|
||||
{
|
||||
template <typename T, typename MutexType = std::mutex>
|
||||
class container
|
||||
{
|
||||
public:
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor) const
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor)
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor) const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor)
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
T& get_raw() { return object_; }
|
||||
const T& get_raw() const { return object_; }
|
||||
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
};
|
||||
}
|
26
src/utils/cryptography.cpp
Normal file
26
src/utils/cryptography.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "string.hpp"
|
||||
#include "cryptography.hpp"
|
||||
|
||||
namespace jenkins_one_at_a_time
|
||||
{
|
||||
unsigned int jenkins_one_at_a_time::compute(const std::string& data)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
7
src/utils/cryptography.hpp
Normal file
7
src/utils/cryptography.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace jenkins_one_at_a_time
|
||||
{
|
||||
unsigned int compute(const std::string& data);
|
||||
unsigned int compute(const char* key, size_t len);
|
||||
}
|
193
src/utils/hook.cpp
Normal file
193
src/utils/hook.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include "hook.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#include <MinHook.h>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
namespace
|
||||
{
|
||||
[[maybe_unused]] class _
|
||||
{
|
||||
public:
|
||||
_()
|
||||
{
|
||||
if (MH_Initialize() != MH_OK)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize MinHook");
|
||||
}
|
||||
}
|
||||
|
||||
~_()
|
||||
{
|
||||
MH_Uninitialize();
|
||||
}
|
||||
} __;
|
||||
}
|
||||
|
||||
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target)
|
||||
{
|
||||
}
|
||||
|
||||
detour::detour(void* place, void* target)
|
||||
{
|
||||
this->create(place, target);
|
||||
}
|
||||
|
||||
detour::~detour()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void detour::enable() const
|
||||
{
|
||||
MH_EnableHook(this->place_);
|
||||
}
|
||||
|
||||
void detour::disable() const
|
||||
{
|
||||
MH_DisableHook(this->place_);
|
||||
}
|
||||
|
||||
void detour::create(void* place, void* target)
|
||||
{
|
||||
this->clear();
|
||||
this->place_ = place;
|
||||
|
||||
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
|
||||
{
|
||||
throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
120
src/utils/hook.hpp
Normal file
120
src/utils/hook.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include "signature.hpp"
|
||||
|
||||
#define CalculateRelativeJMPAddress(X, Y) (((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
class detour
|
||||
{
|
||||
public:
|
||||
detour() = default;
|
||||
detour(void* place, void* target);
|
||||
detour(size_t place, void* target);
|
||||
~detour();
|
||||
|
||||
detour(detour&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
detour& operator= (detour&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->~detour();
|
||||
|
||||
this->place_ = other.place_;
|
||||
this->original_ = other.original_;
|
||||
|
||||
other.place_ = nullptr;
|
||||
other.original_ = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
detour(const detour&) = delete;
|
||||
detour& operator= (const detour&) = delete;
|
||||
|
||||
void enable() const;
|
||||
void disable() const;
|
||||
|
||||
void create(void* place, void* target);
|
||||
void create(size_t place, void* target);
|
||||
void clear();
|
||||
|
||||
template <typename T>
|
||||
T* get() const
|
||||
{
|
||||
return static_cast<T*>(this->get_original());
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(Args... args)
|
||||
{
|
||||
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||
}
|
||||
|
||||
[[nodiscard]] void* get_original() const;
|
||||
|
||||
private:
|
||||
void* place_{};
|
||||
void* original_{};
|
||||
};
|
||||
|
||||
void nop(void* place, size_t length);
|
||||
void nop(size_t place, size_t length);
|
||||
|
||||
void copy(void* place, const void* data, size_t length);
|
||||
void copy(size_t place, const void* data, size_t length);
|
||||
|
||||
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||
|
||||
void call(void* pointer, void* data);
|
||||
void call(size_t pointer, void* data);
|
||||
void call(size_t pointer, size_t data);
|
||||
|
||||
void jump(std::uintptr_t address, void* destination);
|
||||
|
||||
void redirect_jump(void* pointer, void* data);
|
||||
void redirect_jump(size_t pointer, void* data);
|
||||
|
||||
template <typename T>
|
||||
T extract(void* address)
|
||||
{
|
||||
const auto data = static_cast<uint8_t*>(address);
|
||||
const auto offset = *reinterpret_cast<int32_t*>(data);
|
||||
return reinterpret_cast<T>(data + offset + 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(void* place, T value)
|
||||
{
|
||||
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...);
|
||||
}
|
||||
}
|
65
src/utils/info_string.cpp
Normal file
65
src/utils/info_string.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "info_string.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
info_string::info_string(const std::string& buffer)
|
||||
{
|
||||
this->parse(buffer);
|
||||
}
|
||||
|
||||
info_string::info_string(const std::string_view& buffer)
|
||||
: info_string(std::string{ buffer })
|
||||
{
|
||||
}
|
||||
|
||||
void info_string::set(const std::string& key, const std::string& value)
|
||||
{
|
||||
this->key_value_pairs_[key] = value;
|
||||
}
|
||||
|
||||
std::string info_string::get(const std::string& key) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
24
src/utils/info_string.hpp
Normal file
24
src/utils/info_string.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class info_string
|
||||
{
|
||||
public:
|
||||
info_string() = default;
|
||||
info_string(const std::string& buffer);
|
||||
info_string(const std::string_view& buffer);
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
std::string get(const std::string& key) const;
|
||||
std::string build() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> key_value_pairs_{};
|
||||
|
||||
void parse(std::string buffer);
|
||||
};
|
||||
}
|
125
src/utils/io.cpp
Normal file
125
src/utils/io.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "io.hpp"
|
||||
#include "nt.hpp"
|
||||
#include <fstream>
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
21
src/utils/io.hpp
Normal file
21
src/utils/io.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file);
|
||||
bool move_file(const std::string& src, const std::string& target);
|
||||
bool file_exists(const std::string& file);
|
||||
bool write_file(const std::string& file, const std::string& data, bool append = false);
|
||||
bool read_file(const std::string& file, std::string* data);
|
||||
std::string read_file(const std::string& file);
|
||||
size_t file_size(const std::string& file);
|
||||
bool create_directory(const std::string& directory);
|
||||
bool directory_exists(const std::string& directory);
|
||||
bool directory_is_empty(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);
|
||||
}
|
165
src/utils/memory.cpp
Normal file
165
src/utils/memory.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
#include "memory.hpp"
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
memory::allocator memory::mem_allocator_;
|
||||
|
||||
memory::allocator::~allocator()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void memory::allocator::clear()
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
for (auto& data : this->pool_)
|
||||
{
|
||||
memory::free(data);
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
}
|
75
src/utils/memory.hpp
Normal file
75
src/utils/memory.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class memory final
|
||||
{
|
||||
public:
|
||||
class allocator final
|
||||
{
|
||||
public:
|
||||
~allocator();
|
||||
|
||||
void clear();
|
||||
|
||||
void free(void* data);
|
||||
|
||||
void free(const void* data);
|
||||
|
||||
void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate()
|
||||
{
|
||||
return this->allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
bool empty() const;
|
||||
|
||||
char* duplicate_string(const std::string& string);
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::vector<void*> pool_;
|
||||
};
|
||||
|
||||
static void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate()
|
||||
{
|
||||
return allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
static char* duplicate_string(const std::string& string);
|
||||
|
||||
static void free(void* data);
|
||||
static void free(const void* data);
|
||||
|
||||
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_code_ptr(const void* ptr);
|
||||
static bool is_rdata_ptr(void* ptr);
|
||||
|
||||
static allocator* get_allocator();
|
||||
|
||||
private:
|
||||
static allocator mem_allocator_;
|
||||
};
|
||||
}
|
254
src/utils/nt.cpp
Normal file
254
src/utils/nt.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils::nt
|
||||
{
|
||||
library library::load(const std::string& name)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
110
src/utils/nt.hpp
Normal file
110
src/utils/nt.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
// min and max is required by gdi, therefore NOMINMAX won't work
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::nt
|
||||
{
|
||||
class library final
|
||||
{
|
||||
public:
|
||||
static library load(const std::string& name);
|
||||
static library load(const std::filesystem::path& path);
|
||||
static library get_by_address(void* address);
|
||||
|
||||
library();
|
||||
explicit library(const std::string& name);
|
||||
explicit library(HMODULE handle);
|
||||
|
||||
library(const library& a) : module_(a.module_)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator!=(const library& obj) const { return !(*this == obj); };
|
||||
bool operator==(const library& obj) const;
|
||||
|
||||
operator bool() const;
|
||||
operator HMODULE() const;
|
||||
|
||||
void unprotect() const;
|
||||
void* get_entry_point() const;
|
||||
size_t get_relative_entry_point() const;
|
||||
|
||||
bool is_valid() const;
|
||||
std::string get_name() const;
|
||||
std::string get_path() const;
|
||||
std::string get_folder() const;
|
||||
std::uint8_t* get_ptr() const;
|
||||
void free();
|
||||
|
||||
HMODULE get_handle() const;
|
||||
|
||||
template <typename T>
|
||||
T get_proc(const std::string& process) const
|
||||
{
|
||||
if (!this->is_valid()) T{};
|
||||
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::function<T> get(const std::string& process) const
|
||||
{
|
||||
if (!this->is_valid()) return std::function<T>();
|
||||
return static_cast<T*>(this->get_proc<void*>(process));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(const std::string& process, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__cdecl)(Args ...)>(process);
|
||||
if (method) return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke_pascal(const std::string& process, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__stdcall)(Args ...)>(process);
|
||||
if (method) return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke_this(const std::string& process, void* this_ptr, Args ... args) const
|
||||
{
|
||||
auto method = this->get<T(__thiscall)(void*, Args ...)>(this_ptr, process);
|
||||
if (method) return method(args...);
|
||||
return T();
|
||||
}
|
||||
|
||||
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
|
||||
|
||||
PIMAGE_NT_HEADERS get_nt_headers() const;
|
||||
PIMAGE_DOS_HEADER get_dos_header() const;
|
||||
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
||||
|
||||
void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const;
|
||||
|
||||
private:
|
||||
HMODULE module_;
|
||||
};
|
||||
|
||||
__declspec(noreturn) void raise_hard_exception();
|
||||
std::string load_resource(int id);
|
||||
|
||||
void relaunch_self();
|
||||
__declspec(noreturn) void terminate(uint32_t code = 0);
|
||||
}
|
212
src/utils/signature.cpp
Normal file
212
src/utils/signature.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "signature.hpp"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
void signature::load_pattern(const std::string& pattern)
|
||||
{
|
||||
this->mask_.clear();
|
||||
this->pattern_.clear();
|
||||
|
||||
uint8_t nibble = 0;
|
||||
auto has_nibble = false;
|
||||
|
||||
for (auto val : pattern)
|
||||
{
|
||||
if (val == ' ') continue;
|
||||
if (val == '?')
|
||||
{
|
||||
this->mask_.push_back(val);
|
||||
this->pattern_.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((val < '0' || val > '9') && (val < 'A' || val > 'F') && (val < 'a' || val > 'f'))
|
||||
{
|
||||
throw std::runtime_error("Invalid pattern");
|
||||
}
|
||||
|
||||
char str[] = {val, 0};
|
||||
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
|
||||
|
||||
if (!has_nibble)
|
||||
{
|
||||
has_nibble = true;
|
||||
nibble = current_nibble;
|
||||
}
|
||||
else
|
||||
{
|
||||
has_nibble = false;
|
||||
const uint8_t byte = current_nibble | (nibble << 4);
|
||||
|
||||
this->mask_.push_back('x');
|
||||
this->pattern_.push_back(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!this->mask_.empty() && this->mask_.back() == '?')
|
||||
{
|
||||
this->mask_.pop_back();
|
||||
this->pattern_.pop_back();
|
||||
}
|
||||
|
||||
if (this->has_sse_support())
|
||||
{
|
||||
while (this->pattern_.size() < 16)
|
||||
{
|
||||
this->pattern_.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_nibble)
|
||||
{
|
||||
throw std::runtime_error("Invalid pattern");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> signature::process_range(uint8_t* start, const size_t length) const
|
||||
{
|
||||
if (this->has_sse_support()) return this->process_range_vectorized(start, length);
|
||||
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)
|
||||
{
|
||||
return utils::hook::signature(std::string(str, len)).process();
|
||||
}
|
73
src/utils/signature.hpp
Normal file
73
src/utils/signature.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "nt.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
class signature final
|
||||
{
|
||||
public:
|
||||
class signature_result
|
||||
{
|
||||
public:
|
||||
signature_result(std::vector<size_t>&& matches) : matches_(std::move(matches))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] uint8_t* get(const size_t index) const
|
||||
{
|
||||
if (index >= this->count())
|
||||
{
|
||||
throw std::runtime_error("Invalid index");
|
||||
}
|
||||
|
||||
return reinterpret_cast<uint8_t*>(this->matches_[index]);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t count() const
|
||||
{
|
||||
return this->matches_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<size_t> matches_;
|
||||
};
|
||||
|
||||
explicit signature(const std::string& pattern, const nt::library library = {})
|
||||
: signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
|
||||
{
|
||||
}
|
||||
|
||||
signature(const std::string& pattern, void* start, void* end)
|
||||
: signature(pattern, start, size_t(end) - size_t(start))
|
||||
{
|
||||
}
|
||||
|
||||
signature(const std::string& pattern, void* start, const size_t length)
|
||||
: start_(static_cast<uint8_t*>(start)), length_(length)
|
||||
{
|
||||
this->load_pattern(pattern);
|
||||
}
|
||||
|
||||
signature_result process() const;
|
||||
|
||||
private:
|
||||
std::string mask_;
|
||||
std::basic_string<uint8_t> pattern_;
|
||||
|
||||
uint8_t* start_;
|
||||
size_t length_;
|
||||
|
||||
void load_pattern(const std::string& pattern);
|
||||
|
||||
signature_result process_parallel() 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_linear(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;
|
||||
};
|
||||
}
|
||||
|
||||
utils::hook::signature::signature_result operator"" _sig(const char* str, size_t len);
|
179
src/utils/string.cpp
Normal file
179
src/utils/string.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include "string.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
|
||||
#include "nt.hpp"
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
const char* va(const char* fmt, ...)
|
||||
{
|
||||
static thread_local va_provider<8, 256> provider;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
const char* result = provider.get(fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
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;
|
||||
}
|
||||
}
|
100
src/utils/string.hpp
Normal file
100
src/utils/string.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
template <class Type, size_t n>
|
||||
size_t ARRAYSIZE(Type(&)[n]) { return n; }
|
||||
#endif
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
template <size_t Buffers, size_t MinBufferSize>
|
||||
class va_provider final
|
||||
{
|
||||
public:
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||
|
||||
va_provider() : current_buffer_(0)
|
||||
{
|
||||
}
|
||||
|
||||
char* get(const char* format, const va_list ap)
|
||||
{
|
||||
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
|
||||
auto entry = &this->string_pool_[this->current_buffer_];
|
||||
|
||||
if (!entry->size || !entry->buffer)
|
||||
{
|
||||
throw std::runtime_error("String pool not initialized");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return nullptr; // Error
|
||||
|
||||
entry->double_size();
|
||||
}
|
||||
|
||||
return entry->buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
class entry final
|
||||
{
|
||||
public:
|
||||
explicit entry(const size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||
{
|
||||
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
~entry()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
this->size = 0;
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1);
|
||||
}
|
||||
|
||||
void double_size()
|
||||
{
|
||||
this->size *= 2;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
size_t size;
|
||||
char* buffer;
|
||||
};
|
||||
|
||||
size_t current_buffer_;
|
||||
entry string_pool_[Buffers];
|
||||
};
|
||||
|
||||
const char* va(const char* fmt, ...);
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delim);
|
||||
|
||||
std::string to_lower(std::string text);
|
||||
std::string to_upper(std::string text);
|
||||
bool starts_with(const std::string& text, const std::string& substring);
|
||||
bool ends_with(const std::string& text, const std::string& substring);
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||
|
||||
std::string get_clipboard_data();
|
||||
|
||||
void strip(const char* in, char* out, int max);
|
||||
|
||||
std::string convert(const std::wstring& wstr);
|
||||
std::wstring convert(const std::string& str);
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to);
|
||||
}
|
BIN
tools/premake5.exe
Normal file
BIN
tools/premake5.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user