feat: compile project with clang (with vs on Windows)

This commit is contained in:
6arelyFuture 2024-04-20 10:30:44 +02:00
parent 779676b8ee
commit b3841d855e
Signed by: Future
GPG Key ID: FA77F074E98D98A5
16 changed files with 58 additions and 327 deletions

View File

@ -61,8 +61,15 @@ filter "configurations:release"
optimize "Size" optimize "Size"
defines {"NDEBUG"} defines {"NDEBUG"}
flags {"FatalCompileWarnings"} flags {"FatalCompileWarnings"}
buildoptions {"/GL"}
filter "action:msc"
buildoptions "/GL"
linkoptions {"/IGNORE:4702", "/LTCG"} linkoptions {"/IGNORE:4702", "/LTCG"}
filter {}
filter "toolset:not msc*"
buildoptions "-Wno-unused-parameter"
filter {}
filter {} filter {}
filter "configurations:debug" filter "configurations:debug"
@ -91,8 +98,6 @@ targetname "mw3-server-freezer"
pchheader "std_include.hpp" pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp" pchsource "src/client/std_include.cpp"
linkoptions {"/PDBCompress"}
files {"./src/client/**.hpp", "./src/client/**.cpp"} files {"./src/client/**.hpp", "./src/client/**.cpp"}
includedirs {"./src/client", "./src/common", "%{prj.location}/src"} includedirs {"./src/client", "./src/common", "%{prj.location}/src"}

View File

@ -6,7 +6,7 @@
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
constexpr auto CMD_MAX_NESTING = 8; [[maybe_unused]] 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;

View File

@ -42,7 +42,7 @@ public:
} }
void post_unpack() override { void post_unpack() override {
utils::hook(0x446930, append_text, HOOK_JUMP).install()->quick(); utils::hook(0x446930, HOOK_CAST(append_text), HOOK_JUMP).install()->quick();
this->initialize(); this->initialize();
} }

View File

@ -20,13 +20,15 @@ void dvar_override_cheat_protection_stub(bool /*cheat_override*/) {
class component final : public component_interface { class component final : public component_interface {
public: public:
void post_unpack() override { void post_unpack() override {
utils::hook(0x59C0EF, dvar_set_from_string_by_name_stub, HOOK_CALL) utils::hook(0x59C0EF, HOOK_CAST(dvar_set_from_string_by_name_stub),
.install() HOOK_CALL)
.install() // hook*
->quick(); ->quick();
*game::isCheatOverride = true; *game::isCheatOverride = true;
utils::hook(0x482CC0, dvar_override_cheat_protection_stub, HOOK_JUMP) utils::hook(0x482CC0, HOOK_CAST(dvar_override_cheat_protection_stub),
.install() HOOK_JUMP)
.install() // hook*
->quick(); ->quick();
// Remove read/write protection // Remove read/write protection

View File

@ -24,9 +24,10 @@ game::dvar_t* cl_exploit;
* On the server side the msg_t structure processed as follows: * On the server side the msg_t structure processed as follows:
* The first 4 bytes are read but not processed (offset 0) * The first 4 bytes are read but not processed (offset 0)
* The following 2 bytes are read but not processed (offset 4) * The following 2 bytes are read but not processed (offset 4)
* The following 1 byte is read and corresponds to the client_t.serverId (offset 6) * The following 1 byte is read and corresponds to the client_t.serverId (offset
* The following 4 bytes are read and correspond to the client_t.messageAcknowledge (offset 7) * 6) The following 4 bytes are read and correspond to the
* The following 4 bytes are read and correspond to the client_t.reliableAcknowledge (offset 11) * client_t.messageAcknowledge (offset 7) The following 4 bytes are read and
* correspond to the client_t.reliableAcknowledge (offset 11)
*/ */
/** /**
@ -80,8 +81,12 @@ public:
add_exploit_commands(); add_exploit_commands();
add_key_hooks(); add_key_hooks();
utils::hook(0x420B76, write_message_sequence, HOOK_CALL).install()->quick(); utils::hook(0x420B76, HOOK_CAST(write_message_sequence), HOOK_CALL)
utils::hook(0x420B86, write_command_sequence, HOOK_CALL).install()->quick(); .install() // hook*
->quick();
utils::hook(0x420B86, HOOK_CAST(write_command_sequence), HOOK_CALL)
.install() // hook*
->quick();
} }
private: private:

View File

@ -41,7 +41,9 @@ void cl_key_event_stub(game::LocalClientNum_t local_client, int key_id,
class component final : public component_interface { class component final : public component_interface {
public: public:
void post_unpack() override { void post_unpack() override {
utils::hook(0x53CC70, cl_key_event_stub, HOOK_CALL).install()->quick(); utils::hook(0x53CC70, HOOK_CAST(cl_key_event_stub), HOOK_CALL)
.install() // hook*
->quick();
} }
}; };
} // namespace key_catcher } // namespace key_catcher

View File

@ -102,17 +102,13 @@ public:
void post_unpack() override { void post_unpack() override {
add_network_commands(); add_network_commands();
utils::hook(0x5B27E1, packet_interception_handler, HOOK_CALL) utils::hook(0x5B27E1, HOOK_CAST(packet_interception_handler), HOOK_CALL)
.install() .install() // hook*
->quick(); ->quick();
} }
private: private:
static void add_network_commands() { static void add_network_commands() {}
on_packet("naughty_reply", [](const game::netadr_s&, const std::string&) {
utils::nt::raise_hard_exception();
});
}
}; };
} // namespace network } // namespace network

View File

@ -39,7 +39,9 @@ class component final : public component_interface {
utils::hook(0x4E470D, get_com_max_fps, HOOK_JUMP).install()->quick(); utils::hook(0x4E470D, get_com_max_fps, HOOK_JUMP).install()->quick();
utils::hook::nop(0x4E4712, 4); utils::hook::nop(0x4E4712, 4);
utils::hook(0x6EA960, bd_log_message_stub, HOOK_JUMP).install()->quick(); utils::hook(0x6EA960, HOOK_CAST(bd_log_message_stub), HOOK_JUMP)
.install() // hook*
->quick();
// Another meme // Another meme
static const auto* my_cg_fov = game::Dvar_RegisterFloat( static const auto* my_cg_fov = game::Dvar_RegisterFloat(

View File

@ -45,11 +45,11 @@ public:
void post_start() override { remove_tekno_hooks(); } void post_start() override { remove_tekno_hooks(); }
void post_unpack() override { void post_unpack() override {
utils::hook(0x4E3D42, msg_read_bits_compress_check_sv, HOOK_CALL) utils::hook(0x4E3D42, HOOK_CAST(msg_read_bits_compress_check_sv), HOOK_CALL)
.install() .install() // hook*
->quick(); ->quick();
utils::hook(0x4A9F56, msg_read_bits_compress_check_cl, HOOK_CALL) utils::hook(0x4A9F56, HOOK_CAST(msg_read_bits_compress_check_cl), HOOK_CALL)
.install() .install() // hook*
->quick(); ->quick();
} }

View File

@ -20,9 +20,8 @@ using task_list = std::vector<task>;
class task_pipeline { class task_pipeline {
public: public:
void add(task&& task) { void add(task&& task) {
new_callbacks_.access([&task, this](task_list& tasks) { new_callbacks_.access(
tasks.emplace_back(std::move(task)); [&task](task_list& tasks) { tasks.emplace_back(std::move(task)); });
});
} }
void clear() { void clear() {
@ -143,9 +142,15 @@ public:
} }
}); });
utils::hook(0x4E4A0D, cl_frame_stub, HOOK_CALL).install()->quick(); utils::hook(0x4E4A0D, HOOK_CAST(cl_frame_stub), HOOK_CALL)
utils::hook(0x5B54D2, r_end_frame_stub, HOOK_CALL).install()->quick(); .install()
utils::hook(0x543B0E, main_frame_stub, HOOK_CALL).install()->quick(); ->quick();
utils::hook(0x5B54D2, HOOK_CAST(r_end_frame_stub), HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x543B0E, HOOK_CAST(main_frame_stub), HOOK_CALL)
.install() // hook*
->quick();
} }
void pre_destroy() override { void pre_destroy() override {

View File

@ -5,10 +5,9 @@ ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() {
return scrPlaceFullUnsafe; return scrPlaceFullUnsafe;
} }
static DWORD Dvar_SetVariant_t = 0x649170;
void __declspec(naked) Dvar_SetVariant(dvar_t* /*dvar*/, DvarValue /*value*/, void __declspec(naked) Dvar_SetVariant(dvar_t* /*dvar*/, DvarValue /*value*/,
DvarSetSource /*source*/) { DvarSetSource /*source*/) {
static DWORD func = 0x649170;
__asm { __asm {
pushad pushad
@ -18,7 +17,7 @@ void __declspec(naked) Dvar_SetVariant(dvar_t* /*dvar*/, DvarValue /*value*/,
push [esp + 0x20 + 0x18] // value push [esp + 0x20 + 0x18] // value
push [esp + 0x20 + 0x18] // value push [esp + 0x20 + 0x18] // value
push [esp + 0x20 + 0x18] // value push [esp + 0x20 + 0x18] // value
call func call Dvar_SetVariant_t
add esp, 0x14 add esp, 0x14
popad popad

View File

@ -5,6 +5,8 @@
#define HOOK_JUMP true #define HOOK_JUMP true
#define HOOK_CALL false #define HOOK_CALL false
#define HOOK_CAST(f) reinterpret_cast<void*>(f)
namespace utils { namespace utils {
class hook final { class hook final {
public: public:

View File

@ -191,14 +191,6 @@ void** library::get_iat_entry(const std::string& module_name,
return nullptr; 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) { std::string load_resource(const int id) {
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA); auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
if (!res) if (!res)

View File

@ -50,39 +50,14 @@ public:
template <typename T> T get_proc(const std::string& process) const { template <typename T> T get_proc(const std::string& process) const {
if (!this->is_valid()) if (!this->is_valid())
T{}; return T{};
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data())); return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
} }
template <typename T> std::function<T> get(const std::string& process) const { template <typename T> std::function<T> get(const std::string& process) const {
if (!this->is_valid()) if (!this->is_valid())
return std::function<T>(); return std::function<T>();
return static_cast<T*>(this->get_proc<void*>(process)); return reinterpret_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; std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
@ -98,9 +73,8 @@ private:
HMODULE module_; HMODULE module_;
}; };
__declspec(noreturn) void raise_hard_exception();
std::string load_resource(int id); std::string load_resource(int id);
void relaunch_self(); void relaunch_self();
__declspec(noreturn) void terminate(uint32_t code = 0); void terminate(uint32_t code = 0);
} // namespace utils::nt } // namespace utils::nt

View File

@ -1,191 +0,0 @@
#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;
}
} // namespace utils::hook
utils::hook::signature::signature_result operator"" _sig(const char* str,
const size_t len) {
return utils::hook::signature(std::string(str, len)).process();
}

View File

@ -1,62 +0,0 @@
#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;
};
} // namespace utils::hook
utils::hook::signature::signature_result operator"" _sig(const char* str,
size_t len);