Fix crash

This commit is contained in:
6arelyFuture 2022-04-18 12:32:56 +02:00
parent 2793afac0e
commit d20cdfbb41
Signed by: Future
GPG Key ID: FA77F074E98D98A5
40 changed files with 224 additions and 114 deletions

3
.gitmodules vendored
View File

@ -7,3 +7,6 @@
[submodule "deps/rapidjson"] [submodule "deps/rapidjson"]
path = deps/rapidjson path = deps/rapidjson
url = https://github.com/Tencent/rapidjson.git url = https://github.com/Tencent/rapidjson.git
[submodule "deps/asmjit"]
path = deps/asmjit
url = https://github.com/asmjit/asmjit.git

View File

@ -1,7 +1,7 @@
[![build](https://github.com/diamante0018/BlackOpsPlugin/workflows/Build/badge.svg)](https://github.com/diamante0018/BlackOpsPlugin/actions) [![build](https://github.com/diamante0018/BlackOpsPlugin/workflows/Build/badge.svg)](https://github.com/diamante0018/BlackOpsPlugin/actions)
# Black-Ops-Plugin # Black-Ops-Plugin
I have no clue as to what this does. I use this program to test experimental features. I use this program to test experimental features. Some may end up in the plutonium client. Some may not.
## Thanks to ## Thanks to
* [momo5502](https://github.com/momo5502) - Core component loader & utils * [momo5502](https://github.com/momo5502) - Core component loader & utils

1
deps/asmjit vendored Submodule

@ -0,0 +1 @@
Subproject commit a4cb51b532af0f8137c4182914244c3b05d7745f

34
deps/premake/asmjit.lua vendored Normal file
View File

@ -0,0 +1,34 @@
asmjit = {
source = path.join(dependencies.basePath, "asmjit"),
}
function asmjit.import()
links { "asmjit" }
asmjit.includes()
end
function asmjit.includes()
includedirs {
path.join(asmjit.source, "src")
}
defines {
"ASMJIT_STATIC"
}
end
function asmjit.project()
project "asmjit"
language "C++"
asmjit.includes()
files {
path.join(asmjit.source, "src/**.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, asmjit)

View File

@ -70,13 +70,36 @@ filter "configurations:Debug"
defines {"DEBUG", "_DEBUG"} defines {"DEBUG", "_DEBUG"}
filter {} filter {}
project "black-ops-plugin" project "common"
kind "StaticLib"
language "C++"
files {"./src/common/**.hpp", "./src/common/**.cpp"}
includedirs {"./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
dependencies.imports()
project "client"
kind "SharedLib" kind "SharedLib"
language "C++" language "C++"
files {"./src/**.hpp", "./src/**.cpp"} targetname "black-ops-plugin"
includedirs {"src"} pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp"
linkoptions {"/IGNORE:4254", "/PDBCompress"}
files {"./src/client/**.hpp", "./src/client/**.cpp"}
includedirs {"./src/client", "./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
links {"common"}
dependencies.imports() dependencies.imports()

View File

@ -1,7 +1,8 @@
#include <stdinc.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "../loader/component_loader.hpp"
#include "utils/hook.hpp"
#include <utils/hook.hpp>
namespace ban { namespace ban {
namespace { namespace {

View File

@ -1,9 +1,9 @@
#include <stdinc.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "../loader/component_loader.hpp"
#include "utils/hook.hpp" #include <utils/hook.hpp>
#include "utils/io.hpp" #include <utils/io.hpp>
namespace bots { namespace bots {
namespace { namespace {
@ -14,7 +14,7 @@ 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 // Json file is expected to contain a key for the bot's name. Value should be a
// string for the clantag // string for the clantag
void load_bot_data() { 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")) {

View File

@ -1,8 +1,8 @@
#include <stdinc.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "../loader/component_loader.hpp"
#include "utils/hook.hpp" #include <utils/hook.hpp>
#include "utils/string.hpp" #include <utils/string.hpp>
#include "command.hpp" #include "command.hpp"
@ -11,26 +11,26 @@ namespace {
std::mutex chat_mutex; std::mutex chat_mutex;
std::unordered_set<std::uint64_t> mute_list{}; std::unordered_set<std::uint64_t> mute_list{};
void mute_player(const game::client_s* cl) { void mute_player(const game::client_s* client) {
std::unique_lock<std::mutex> _(chat_mutex); std::unique_lock<std::mutex> _(chat_mutex);
if (mute_list.contains(cl->xuid)) { if (mute_list.contains(client->xuid)) {
game::SV_GameSendServerCommand( game::SV_GameSendServerCommand(
-1, game::SV_CMD_CAN_IGNORE, -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, client->name));
return; return;
} }
mute_list.insert(cl->xuid); mute_list.insert(client->xuid);
} }
void unmute_player(const game::client_s* cl) { void unmute_player(const game::client_s* client) {
std::unique_lock<std::mutex> _(chat_mutex); std::unique_lock<std::mutex> _(chat_mutex);
mute_list.erase(cl->xuid); mute_list.erase(client->xuid);
game::SV_GameSendServerCommand( game::SV_GameSendServerCommand(
cl->gentity->entnum, game::SV_CMD_CAN_IGNORE, client->gentity->entnum, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You were unmuted\"", 0x65)); utils::string::va("%c \"You were unmuted\"", 0x65));
} }

View File

@ -1,9 +1,9 @@
#include <stdinc.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "../loader/component_loader.hpp"
#include "utils/string.hpp"
#include "utils/nt.hpp" #include <utils/string.hpp>
#include "utils/io.hpp" #include <utils/nt.hpp>
#include "command.hpp" #include "command.hpp"
@ -12,6 +12,33 @@ 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;
namespace {
void cmd_vstr_f(const params& params) {
if (params.size() < 2) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"vstr <variablename> : execute a variable command\n");
return;
}
const auto* dvar_name = params.get(1);
const auto* dvar = game::Dvar_FindVar(dvar_name);
if (dvar == nullptr) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s doesn't exist\n",
dvar_name);
return;
}
if (dvar->type == game::DVAR_TYPE_STRING ||
dvar->type == game::DVAR_TYPE_ENUM) {
execute(dvar->current.string);
} else {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"%s is not a string-based dvar\n", dvar->name);
}
}
} // namespace
void main_handler() { void main_handler() {
params params = {}; params params = {};
@ -63,26 +90,6 @@ void add(const char* name, const std::function<void(const params&)>& callback) {
handlers[command] = callback; 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) { void execute(std::string command, const bool sync) {
command += "\n"; command += "\n";
@ -97,11 +104,20 @@ class component final : public component_interface {
public: public:
void post_unpack() override { add_commands_generic(); } void post_unpack() override { add_commands_generic(); }
void pre_destroy() override { clear_script_commands(); }
private: private:
static void add_commands_generic() { static void add_commands_generic() {
add("properQuit", [](const params&) { utils::nt::raise_hard_exception(); }); add("properQuit", [](const params&) { utils::nt::raise_hard_exception(); });
add("echo", [](const params& params) {
for (auto i = 1; i < params.size(); i++) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s ", params.get(i));
}
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "\n");
});
game::Cmd_RemoveCommand("vstr");
add("vstr", cmd_vstr_f);
} }
}; };
} // namespace command } // namespace command

View File

@ -18,9 +18,5 @@ private:
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 clear_script_commands();
void execute(std::string command, bool sync = false); void execute(std::string command, bool sync = false);
} // namespace command } // namespace command

View File

@ -0,0 +1,30 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
namespace gameplay {
namespace {
game::dvar_s** player_sustainAmmo = nullptr;
utils::hook::detour pm_weapon_use_ammo_hook;
void pm_weapon_use_ammo_stub(void* ps, int wp, int amount) {
if (!(*player_sustainAmmo)->current.enabled) {
pm_weapon_use_ammo_hook.invoke<void>(ps, wp, amount);
}
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
if (game::environment::t5zm()) {
player_sustainAmmo = reinterpret_cast<game::dvar_s**>(0xBCD250);
pm_weapon_use_ammo_hook.create(0x6979B0, &pm_weapon_use_ammo_stub);
}
}
};
} // namespace gameplay
REGISTER_COMPONENT(gameplay::component)

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp> #include "std_include.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call,

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp> #include <std_include.hpp>
namespace game { namespace game {
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s

View File

@ -19,6 +19,7 @@ public:
T* get() const { T* get() const {
if (environment::t5mp()) { if (environment::t5mp()) {
return t5mp_; return t5mp_;
} }

View File

@ -276,10 +276,9 @@ typedef struct dvar_s {
DvarValue saved; DvarValue saved;
DvarLimits domain; DvarLimits domain;
dvar_s* hashNext; dvar_s* hashNext;
unsigned char pad0[8];
} dvar_t; } dvar_t;
static_assert(sizeof(dvar_s) == 112); static_assert(sizeof(dvar_s) == 104);
enum playerFlag { enum playerFlag {
PLAYER_FLAG_NOCLIP = 1 << 0, PLAYER_FLAG_NOCLIP = 1 << 0,

View File

@ -32,7 +32,7 @@ WEAK symbol<bool(netsrc_t, netadr_s, const char*)> NET_OutOfBandPrint{0x560BB0,
0x472850}; 0x472850};
WEAK symbol<const char*(netadr_s)> NET_AdrToString{0x49F970, 0x40D790}; WEAK symbol<const char*(netadr_s)> NET_AdrToString{0x49F970, 0x40D790};
WEAK symbol<dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810}; WEAK symbol<const dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810};
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0, WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0,
0x5B56F0}; 0x5B56F0};
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableLatchedValue{0x4AE1A0, WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableLatchedValue{0x4AE1A0,

View File

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

View File

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

View File

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

View File

@ -17,6 +17,14 @@ public:
} __; } __;
} // namespace } // namespace
asmjit::Error assembler::call(void* target) {
return Assembler::call(size_t(target));
}
asmjit::Error assembler::jmp(void* target) {
return Assembler::jmp(size_t(target));
}
detour::detour(const size_t place, void* target) detour::detour(const size_t place, void* target)
: detour(reinterpret_cast<void*>(place), target) {} : detour(reinterpret_cast<void*>(place), target) {}
@ -153,4 +161,20 @@ void redirect_jump(void* pointer, void* data) {
void redirect_jump(size_t pointer, void* data) { void redirect_jump(size_t pointer, void* data) {
redirect_jump(reinterpret_cast<void*>(pointer), data); redirect_jump(reinterpret_cast<void*>(pointer), data);
} }
void* assemble(const std::function<void(assembler&)>& asm_function) {
static asmjit::JitRuntime runtime;
asmjit::CodeHolder code;
code.init(runtime.environment());
assembler a(&code);
asm_function(a);
void* result = nullptr;
runtime.add(&result, &code);
return result;
}
} // namespace utils::hook } // namespace utils::hook

View File

@ -2,9 +2,24 @@
#include "signature.hpp" #include "signature.hpp"
#define CalculateRelativeJMPAddress(X, Y) \ #define CalculateRelativeJMPAddress(X, Y) \
(((std::uintptr_t)Y - (std::uintptr_t)X) - 5) (((std::uintptr_t)(Y) - (std::uintptr_t)(X)) - 5)
#include <asmjit/core/jitruntime.h>
#include <asmjit/x86/x86assembler.h>
using namespace asmjit::x86;
namespace utils::hook { namespace utils::hook {
class assembler : public Assembler {
public:
using Assembler::Assembler;
using Assembler::call;
using Assembler::jmp;
asmjit::Error call(void* target);
asmjit::Error jmp(void* target);
};
class detour { class detour {
public: public:
detour() = default; detour() = default;
@ -70,6 +85,8 @@ 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);
void* assemble(const std::function<void(assembler&)>& asm_function);
template <typename T> T extract(void* address) { template <typename T> T extract(void* address) {
const auto data = static_cast<uint8_t*>(address); const auto data = static_cast<uint8_t*>(address);
const auto offset = *reinterpret_cast<int32_t*>(data); const auto offset = *reinterpret_cast<int32_t*>(data);

View File

@ -11,7 +11,9 @@ bool move_file(const std::string& src, const std::string& target) {
return MoveFileA(src.data(), target.data()) == TRUE; return MoveFileA(src.data(), target.data()) == TRUE;
} }
bool file_exists(const std::string& file) { return std::ifstream(file).good(); } bool file_exists(const std::string& file) {
return std::filesystem::exists(file);
}
bool write_file(const std::string& file, const std::string& data, bool write_file(const std::string& file, const std::string& data,
const bool append) { const bool append) {
@ -54,7 +56,7 @@ bool read_file(const std::string& file, std::string* data) {
if (size > -1) { if (size > -1) {
data->resize(static_cast<uint32_t>(size)); data->resize(static_cast<uint32_t>(size));
stream.read(const_cast<char*>(data->data()), size); stream.read(data->data(), size);
stream.close(); stream.close();
return true; return true;
} }

View File

@ -9,7 +9,7 @@ memory::allocator::~allocator() { 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 (const auto& data : this->pool_) {
memory::free(data); memory::free(data);
} }

View File

@ -18,11 +18,9 @@ public:
void* allocate(size_t length); void* allocate(size_t length);
template <typename T> inline T* allocate() { template <typename T> T* allocate() { return this->allocate_array<T>(1); }
return this->allocate_array<T>(1);
}
template <typename T> inline T* allocate_array(const size_t count = 1) { template <typename T> T* allocate_array(const size_t count = 1) {
return static_cast<T*>(this->allocate(count * sizeof(T))); return static_cast<T*>(this->allocate(count * sizeof(T)));
} }
@ -37,12 +35,9 @@ public:
static void* allocate(size_t length); static void* allocate(size_t length);
template <typename T> static inline T* allocate() { template <typename T> static T* allocate() { return allocate_array<T>(1); }
return allocate_array<T>(1);
}
template <typename T> template <typename T> static 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)));
} }

View File

@ -24,8 +24,7 @@ std::vector<std::string> split(const std::string& s, const char delim) {
std::vector<std::string> elems; std::vector<std::string> elems;
while (std::getline(ss, item, delim)) { while (std::getline(ss, item, delim)) {
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 elems.push_back(item); // elems.push_back(std::move(item)); if C++11
// (based on comment from @mchiasson)
} }
return elems; return elems;
@ -92,12 +91,12 @@ std::string get_clipboard_data() {
return {}; return {};
} }
void strip(const char* in, char* out, int max) { void strip(const char* in, char* out, size_t max) {
if (!in || !out) if (!in || !out)
return; return;
max--; max--;
auto current = 0; size_t current = 0;
while (*in != 0 && current < max) { while (*in != 0 && current < max) {
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48); const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
@ -111,6 +110,7 @@ void strip(const char* in, char* out, int max) {
++in; ++in;
} }
*out = '\0'; *out = '\0';
} }

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "memory.hpp" #include "memory.hpp"
#include <cstdint>
#ifndef ARRAYSIZE #ifndef ARRAYSIZE
template <class Type, size_t n> size_t ARRAYSIZE(Type (&)[n]) { return n; } template <class Type, size_t n> size_t ARRAYSIZE(Type (&)[n]) { return n; }
@ -87,7 +86,7 @@ std::string dump_hex(const std::string& data,
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, size_t 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);

View File

@ -1,31 +0,0 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "utils/hook.hpp"
namespace gameplay {
namespace {
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) {
if (player_meleeRange->current.value == 0.0f)
return;
fire_weapon_melee_hook.invoke<void>(ent, time);
}
} // 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)

View File

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