update arxan component

includes anti anti debug
This commit is contained in:
quaK 2024-01-11 19:43:49 +02:00
parent 24a8c68abf
commit 820aa6788e
8 changed files with 1255 additions and 299 deletions

View File

@ -1,151 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "arxan.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
namespace arxan
{
namespace
{
utils::hook::detour nt_close_hook;
utils::hook::detour nt_query_information_process_hook;
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
const PVOID info,
const ULONG info_length, const PULONG ret_length)
{
auto* orig = static_cast<decltype(NtQueryInformationProcess)*>(nt_query_information_process_hook.
get_original());
const auto status = orig(handle, info_class, info, info_length, ret_length);
if (NT_SUCCESS(status))
{
if (info_class == ProcessBasicInformation)
{
static DWORD explorer_pid = 0;
if (!explorer_pid)
{
auto* const shell_window = GetShellWindow();
GetWindowThreadProcessId(shell_window, &explorer_pid);
}
static_cast<PPROCESS_BASIC_INFORMATION>(info)->Reserved3 = PVOID(DWORD64(explorer_pid));
}
else if (info_class == 30) // ProcessDebugObjectHandle
{
*static_cast<HANDLE*>(info) = nullptr;
return 0xC0000353;
}
else if (info_class == 7) // ProcessDebugPort
{
*static_cast<HANDLE*>(info) = nullptr;
}
else if (info_class == 31)
{
*static_cast<ULONG*>(info) = 1;
}
//https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
}
return status;
}
NTSTATUS NTAPI nt_close_stub(const HANDLE handle)
{
char info[16];
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345)
{
auto* orig = static_cast<decltype(NtClose)*>(nt_close_hook.get_original());
return orig(handle);
}
return STATUS_INVALID_HANDLE;
}
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
void hide_being_debugged()
{
auto* const peb = PPEB(__readgsqword(0x60));
peb->BeingDebugged = false;
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70;
}
void remove_hardware_breakpoints()
{
CONTEXT context;
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
auto* const thread = GetCurrentThread();
GetThreadContext(thread, &context);
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr6 = 0;
context.Dr7 = 0;
SetThreadContext(thread, &context);
}
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{
return SetThreadContext(thread, context);
}
}
class component final : public component_interface
{
public:
void* load_import(const std::string& library, const std::string& function) override
{
if (function == "SetThreadContext")
{
//return set_thread_context_stub;
}
return nullptr;
}
void post_load() override
{
hide_being_debugged();
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
const utils::nt::library ntdll("ntdll.dll");
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
nt_query_information_process_hook.create(ntdll.get_proc<void*>("NtQueryInformationProcess"),
nt_query_information_process_stub);
// https://www.geoffchappell.com/studies/windows/win32/ntdll/api/index.htm
AddVectoredExceptionHandler(1, exception_filter);
}
void post_unpack() override
{
// cba to implement sp, not sure if it's even needed
if (game::environment::is_sp())
{
return;
}
}
};
}
REGISTER_COMPONENT(arxan::component)

View File

@ -0,0 +1,587 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "component/game_module.hpp"
#include "component/scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "integrity.hpp"
#include "breakpoints.hpp"
#define PRECOMPUTED_INTEGRITY_CHECKS
#define PRECOMPUTED_BREAKPOINTS
#define ProcessDebugPort 7
#define ProcessDebugObjectHandle 30
#define ProcessDebugFlags 31
namespace arxan
{
namespace integrity
{
const std::vector<std::pair<uint8_t*, size_t>>& get_text_sections()
{
static const std::vector<std::pair<uint8_t*, size_t>> text = []
{
std::vector<std::pair<uint8_t*, size_t>> texts{};
const utils::nt::library game{ game_module::get_game_module() };
for (const auto& section : game.get_section_headers())
{
if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
texts.emplace_back(game.get_ptr() + section->VirtualAddress, section->Misc.VirtualSize);
}
}
return texts;
}();
return text;
}
bool is_in_texts(const uint64_t addr)
{
const auto& texts = get_text_sections();
for (const auto& text : texts)
{
const auto start = reinterpret_cast<ULONG_PTR>(text.first);
if (addr >= start && addr <= (start + text.second))
{
return true;
}
}
return false;
}
bool is_in_texts(const void* addr)
{
return is_in_texts(reinterpret_cast<uint64_t>(addr));
}
struct integrity_handler_context
{
uint32_t* computed_checksum;
uint32_t* original_checksum;
};
bool is_on_stack(uint8_t* stack_frame, const void* pointer)
{
const auto stack_value = reinterpret_cast<uint64_t>(stack_frame);
const auto pointer_value = reinterpret_cast<uint64_t>(pointer);
const auto diff = static_cast<int64_t>(stack_value - pointer_value);
return std::abs(diff) < 0x1000;
}
// Pretty trashy, but working, heuristic to search the integrity handler context
bool is_handler_context(uint8_t* stack_frame, const uint32_t computed_checksum, const uint32_t frame_offset)
{
const auto* potential_context = reinterpret_cast<integrity_handler_context*>(stack_frame + frame_offset);
return is_on_stack(stack_frame, potential_context->computed_checksum)
&& *potential_context->computed_checksum == computed_checksum
&& is_in_texts(potential_context->original_checksum);
}
integrity_handler_context* search_handler_context(uint8_t* stack_frame, const uint32_t computed_checksum)
{
for (uint32_t frame_offset = 0; frame_offset < 0x90; frame_offset += 8)
{
if (is_handler_context(stack_frame, computed_checksum, frame_offset))
{
return reinterpret_cast<integrity_handler_context*>(stack_frame + frame_offset);
}
}
return nullptr;
}
uint32_t adjust_integrity_checksum(const uint64_t return_address, uint8_t* stack_frame,
const uint32_t current_checksum)
{
[[maybe_unused]] const auto handler_address = return_address - 5;
const auto* context = search_handler_context(stack_frame, current_checksum);
if (!context)
{
OutputDebugStringA(utils::string::va("Unable to find frame offset for: %llX", return_address));
return current_checksum;
}
const auto correct_checksum = *context->original_checksum;
*context->computed_checksum = correct_checksum;
if (current_checksum != correct_checksum)
{
#ifdef DEV_BUILD
OutputDebugStringA(utils::string::va("Adjusting checksum (%llX): %X -> %X", handler_address,
current_checksum, correct_checksum));
#endif
}
return correct_checksum;
}
void patch_intact_basic_block_integrity_check(void* address)
{
const auto game_address = reinterpret_cast<uint64_t>(address);
constexpr auto inst_len = 3;
const auto next_inst_addr = game_address + inst_len;
const auto next_inst = *reinterpret_cast<uint32_t*>(next_inst_addr);
if ((next_inst & 0xFF00FFFF) != 0xFF004583)
{
throw std::runtime_error(utils::string::va("Unable to patch intact basic block: %llX", game_address));
}
const auto other_frame_offset = static_cast<uint8_t>(next_inst >> 16);
static const auto stub = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(rax);
a.mov(rax, qword_ptr(rsp, 8));
a.sub(rax, 2); // Skip the push we inserted
a.push(rax);
a.pushad64();
a.mov(r8, qword_ptr(rsp, 0x88));
a.mov(rcx, rax);
a.mov(rdx, rbp);
a.call_aligned(adjust_integrity_checksum);
a.mov(qword_ptr(rsp, 0x80), rax);
a.popad64();
a.pop(rax);
a.add(rsp, 8);
a.mov(dword_ptr(rdx, rcx, 4), eax);
a.pop(rax); // return addr
a.xchg(rax, qword_ptr(rsp)); // switch with push
a.add(dword_ptr(rbp, rax), 0xFFFFFFFF);
a.mov(rax, dword_ptr(rdx, rcx, 4)); // restore rax
a.ret();
});
// push other_frame_offset
utils::hook::set<uint16_t>(game_address, static_cast<uint16_t>(0x6A | (other_frame_offset << 8)));
utils::hook::call(game_address + 2, stub);
}
void patch_split_basic_block_integrity_check(void* address)
{
const auto game_address = reinterpret_cast<uint64_t>(address);
constexpr auto inst_len = 3;
const auto next_inst_addr = game_address + inst_len;
if (*reinterpret_cast<uint8_t*>(next_inst_addr) != 0xE9)
{
throw std::runtime_error(utils::string::va("Unable to patch split basic block: %llX", game_address));
}
const auto jump_target = utils::hook::extract<void*>(reinterpret_cast<void*>(next_inst_addr + 1));
const auto stub = utils::hook::assemble([jump_target](utils::hook::assembler& a)
{
a.push(rax);
a.mov(rax, qword_ptr(rsp, 8));
a.push(rax);
a.pushad64();
a.mov(r8, qword_ptr(rsp, 0x88));
a.mov(rcx, rax);
a.mov(rdx, rbp);
a.call_aligned(adjust_integrity_checksum);
a.mov(qword_ptr(rsp, 0x80), rax);
a.popad64();
a.pop(rax);
a.add(rsp, 8);
a.mov(dword_ptr(rdx, rcx, 4), eax);
a.add(rsp, 8);
a.jmp(jump_target);
});
utils::hook::call(game_address, stub);
}
#ifdef PRECOMPUTED_INTEGRITY_CHECKS
void search_and_patch_integrity_checks_precomputed()
{
if (game::environment::is_sp())
{
for (const auto i : sp::intact_integrity_check_blocks)
{
patch_intact_basic_block_integrity_check(reinterpret_cast<void*>(i));
}
for (const auto i : sp::split_integrity_check_blocks)
{
patch_split_basic_block_integrity_check(reinterpret_cast<void*>(i));
}
}
else
{
for (const auto i : mp::intact_integrity_check_blocks)
{
patch_intact_basic_block_integrity_check(reinterpret_cast<void*>(i));
}
for (const auto i : mp::split_integrity_check_blocks)
{
patch_split_basic_block_integrity_check(reinterpret_cast<void*>(i));
}
}
}
#endif
void search_and_patch_integrity_checks()
{
#ifdef PRECOMPUTED_INTEGRITY_CHECKS
assert(game::base_address == 0x140000000);
search_and_patch_integrity_checks_precomputed();
#else
const auto intact_results = "89 04 8A 83 45 ? FF"_sig;
const auto split_results = "89 04 8A E9"_sig;
for (auto* i : intact_results)
{
patch_intact_basic_block_integrity_check(i);
}
for (auto* i : split_results)
{
patch_split_basic_block_integrity_check(i);
}
#endif
}
}
using namespace integrity;
namespace anti_debug
{
utils::hook::detour nt_close_hook;
utils::hook::detour nt_query_information_process_hook;
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
const PVOID info,
const ULONG info_length, const PULONG ret_length)
{
auto* orig = static_cast<decltype(NtQueryInformationProcess)*>(nt_query_information_process_hook.get_original());
auto status = orig(handle, info_class, info, info_length, ret_length);
if (NT_SUCCESS(status))
{
if (info_class == ProcessDebugObjectHandle)
{
*static_cast<HANDLE*>(info) = nullptr;
return static_cast<LONG>(0xC0000353);
}
else if (info_class == ProcessDebugPort)
{
*static_cast<HANDLE*>(info) = nullptr;
}
else if (info_class == ProcessDebugFlags)
{
*static_cast<ULONG*>(info) = 1;
}
}
return status;
}
NTSTATUS NTAPI nt_close_stub(const HANDLE handle)
{
char info[16];
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345)
{
auto* orig = static_cast<decltype(NtClose)*>(nt_close_hook.get_original());
return orig(handle);
}
return STATUS_INVALID_HANDLE;
}
void hide_being_debugged()
{
auto* const peb = PPEB(__readgsqword(0x60));
peb->BeingDebugged = false;
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70; // NtGlobalFlag
}
void remove_hardware_breakpoints()
{
CONTEXT context;
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
auto* const thread = GetCurrentThread();
GetThreadContext(thread, &context);
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr6 = 0;
context.Dr7 = 0;
SetThreadContext(thread, &context);
}
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{
if (context->ContextFlags == CONTEXT_DEBUG_REGISTERS)
{
return TRUE;
}
return SetThreadContext(thread, context);
}
enum dbg_funcs_e
{
DbgBreakPoint,
DbgUserBreakPoint,
DbgUiConnectToDbg,
DbgUiContinue,
DbgUiConvertStateChangeStructure,
DbgUiDebugActiveProcess,
DbgUiGetThreadDebugObject,
DbgUiIssueRemoteBreakin,
DbgUiRemoteBreakin,
DbgUiSetThreadDebugObject,
DbgUiStopDebugging,
DbgUiWaitStateChange,
DbgPrintReturnControlC,
DbgPrompt,
DBG_FUNCS_COUNT,
};
const char* dbg_funcs_names[] =
{
"DbgBreakPoint",
"DbgUserBreakPoint",
"DbgUiConnectToDbg",
"DbgUiContinue",
"DbgUiConvertStateChangeStructure",
"DbgUiDebugActiveProcess",
"DbgUiGetThreadDebugObject",
"DbgUiIssueRemoteBreakin",
"DbgUiRemoteBreakin",
"DbgUiSetThreadDebugObject",
"DbgUiStopDebugging",
"DbgUiWaitStateChange",
"DbgPrintReturnControlC",
"DbgPrompt",
};
struct dbg_func_bytes_s
{
std::uint8_t buffer[15];
};
dbg_func_bytes_s dbg_func_bytes[DBG_FUNCS_COUNT];
void* dbg_func_procs[DBG_FUNCS_COUNT]{};
void store_debug_functions()
{
const utils::nt::library ntdll("ntdll.dll");
for (auto i = 0; i < DBG_FUNCS_COUNT; i++)
{
dbg_func_procs[i] = ntdll.get_proc<void*>(dbg_funcs_names[i]);
memcpy(dbg_func_bytes[i].buffer, dbg_func_procs[i], sizeof(dbg_func_bytes[i].buffer));
}
}
void restore_debug_functions()
{
for (auto i = 0; i < DBG_FUNCS_COUNT; i++)
{
utils::hook::copy(dbg_func_procs[i], dbg_func_bytes[i].buffer, sizeof(dbg_func_bytes[i].buffer));
}
}
namespace breakpoints
{
std::unordered_map<PVOID, void*> handle_handler;
void fake_breakpoint_trigger(void* address, _CONTEXT* fake_context)
{
_EXCEPTION_POINTERS fake_info{};
_EXCEPTION_RECORD fake_record{};
fake_info.ExceptionRecord = &fake_record;
fake_info.ContextRecord = fake_context;
fake_record.ExceptionAddress = reinterpret_cast<void*>(reinterpret_cast<std::uint64_t>(address) + 3);
fake_record.ExceptionCode = EXCEPTION_BREAKPOINT;
for (auto handler : handle_handler)
{
if (handler.second)
{
auto result = utils::hook::invoke<LONG>(handler.second, &fake_info);
if (result)
{
memset(fake_context, 0, sizeof(_CONTEXT));
break;
}
}
}
}
void patch_int2d_trap(void* address)
{
const auto game_address = reinterpret_cast<std::uint64_t>(address);
const auto jump_target = utils::hook::extract<void*>(reinterpret_cast<void*>(game_address + 3));
_CONTEXT* fake_context = new _CONTEXT{};
const auto stub = utils::hook::assemble([address, jump_target, fake_context](utils::hook::assembler& a)
{
a.push(rcx);
a.mov(rcx, fake_context);
a.call_aligned(RtlCaptureContext);
a.pop(rcx);
a.pushad64();
a.mov(rcx, address);
a.mov(rdx, fake_context);
a.call_aligned(fake_breakpoint_trigger);
a.popad64();
a.jmp(jump_target);
});
utils::hook::nop(game_address, 7);
utils::hook::jump(game_address, stub, false);
}
#ifdef PRECOMPUTED_BREAKPOINTS
void patch_breakpoints_precomputed()
{
for (const auto i : mp::int2d_breakpoint_addresses)
{
patch_int2d_trap(reinterpret_cast<void*>(i));
}
}
#endif
void patch_breakpoints()
{
static bool once = false;
if (once)
{
return;
}
once = true;
// sp has no breakpoints
if (game::environment::is_sp())
{
return;
}
#ifdef PRECOMPUTED_BREAKPOINTS
assert(game::base_address == 0x140000000);
patch_breakpoints_precomputed();
#else
const auto int2d_results = utils::hook::signature("CD 2D E9 ? ? ? ?", game_module::get_game_module()).process();
for (auto* i : int2d_results)
{
patch_int2d_trap(i);
}
#endif
}
PVOID WINAPI add_vectored_exception_handler_stub(ULONG first, PVECTORED_EXCEPTION_HANDLER handler)
{
breakpoints::patch_breakpoints();
auto handle = AddVectoredExceptionHandler(first, handler);
handle_handler[handle] = handler;
return handle;
}
ULONG WINAPI remove_vectored_exception_handler_stub(PVOID handle)
{
handle_handler[handle] = nullptr;
return RemoveVectoredExceptionHandler(handle);
}
}
}
using namespace anti_debug;
class component final : public component_interface
{
public:
void* load_import(const std::string& library, const std::string& function) override
{
if (function == "SetThreadContext")
{
return set_thread_context_stub;
}
else if (function == "AddVectoredExceptionHandler")
{
return breakpoints::add_vectored_exception_handler_stub;
}
else if (function == "RemoveVectoredExceptionHandler")
{
return breakpoints::remove_vectored_exception_handler_stub;
}
return nullptr;
}
void post_load() override
{
remove_hardware_breakpoints();
hide_being_debugged();
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
store_debug_functions();
const utils::nt::library ntdll("ntdll.dll");
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
const auto nt_query_information_process = ntdll.get_proc<void*>("NtQueryInformationProcess");
nt_query_information_process_hook.create(nt_query_information_process, nt_query_information_process_stub);
nt_query_information_process_hook.move();
AddVectoredExceptionHandler(1, exception_filter);
}
void post_unpack() override
{
remove_hardware_breakpoints();
search_and_patch_integrity_checks();
restore_debug_functions();
}
};
}
REGISTER_COMPONENT(arxan::component)

View File

@ -0,0 +1,165 @@
#pragma once
#include <cstdint>
namespace mp
{
constexpr uint64_t int2d_breakpoint_addresses[] =
{
0x1400837C9,
0x140088C28,
0x140089CB9,
0x1400922E9,
0x140119A39,
0x140199679,
0x1401CBB59,
0x140205DD9,
0x140244A39,
0x1402BCFC9,
0x140346C39,
0x14037E2A9,
0x1404EB459,
0x1404EB7C9,
0x1405FCD09,
0x140612AB3,
0x140622D2E,
0x1407D82DB,
0x1407DC18C,
0x140847869,
0x140867E53,
0x15143E836,
0x15144B18C,
0x15144BE27,
0x1515E11E0,
0x1515F18C9,
0x1515F2ADE,
0x15161E477,
0x15162189F,
0x151623FC5,
0x151637C0D,
0x151640170,
0x151641309,
0x1516514DA,
0x1516528BC,
0x151653351,
0x15165627D,
0x151656436,
0x151683F6C,
0x151686F24,
0x1516881C5,
0x151695BAD,
0x15169714D,
0x1516977E9,
0x1516B6978,
0x1516B933F,
0x1516D75C7,
0x1516D8B61,
0x1516DDA42,
0x1517064EC,
0x151706519,
0x151712445,
0x151735FFA,
0x151737872,
0x151738660,
0x151748404,
0x151748DB3,
0x15174D408,
0x15174F7D2,
0x151763BDE,
0x151778988,
0x1517886DB,
0x15178DDAB,
0x151793BB6,
0x15179DC17,
0x1517A6BC5,
0x1517ACBA3,
0x1517B3502,
0x1517BAE0E,
0x1517BB039,
0x1517BB049,
0x1517BC58E,
0x1517BC7A0,
0x1517BCF51,
0x1517BFF71,
0x1517C0761,
0x1517C33F1,
0x1517C44C4,
0x1517C75AE,
0x1517CDB3B,
0x1517CE48A,
0x1517D0559,
0x1517D8E0F,
0x1517D95C0,
0x1517DA7FF,
0x1517DD407,
0x1517E11D2,
0x1517E5C08,
0x1517ED03C,
0x1517EE9CC,
0x1517EFB56,
0x1517F31AC,
0x1517F73D1,
0x1517FB1CA,
0x1518050D4,
0x15180EFA2,
0x15181A3BE,
0x15181A5C4,
0x151823361,
0x1518233AC,
0x151862C5B,
0x151863913,
0x15188E396,
0x15188EA8D,
0x15189760A,
0x1518977B0,
0x1518B14C9,
0x1518B7227,
0x1518B73AF,
0x1518B84F9,
0x1518C562A,
0x1518EA89B,
0x1518EFC20,
0x1518F10A0,
0x1518F4D00,
0x1518FD8B3,
0x1519033DA,
0x151904C1E,
0x151908F69,
0x15190D0DB,
0x151988E27,
0x15198FDD5,
0x151994C76,
0x151995C6B,
0x1519A66F1,
0x1519A7438,
0x1519A9B02,
0x1519C4269,
0x1519C4C75,
0x1519EE7BB,
0x1519EFFA8,
0x1519F0368,
0x1519F286A,
0x151A04D7E,
0x151A35E78,
0x151A38F2F,
0x151A450E4,
0x151A76754,
0x151A89BD1,
0x151A89C0A,
0x151AAB0EA,
0x151AB3E09,
0x151ABB764,
0x151ABCC16,
0x151ABF3F0,
0x151AC0659,
0x151AC5C1B,
0x151AC8705,
0x151AC88E2,
0x151ACA788,
0x151ACED96,
0x151AD7F8A,
0x151ADA318,
0x151ADC9BE,
0x151AE1BAF,
};
}

View File

@ -0,0 +1,92 @@
#pragma once
#include <cstdint>
namespace mp
{
constexpr uint64_t intact_integrity_check_blocks[] =
{
0x140000519,
0x1407DB02E,
0x140AFF08D,
0x15143E06E,
0x151440DC4,
0x151455F3B,
0x151489363,
0x1515F10AB,
0x1515F66D0,
0x15161DB84,
0x15161EAA8,
0x151622530,
0x151687D47,
0x151690107,
0x1516979D7,
0x1516D3ED0,
0x1516E2FC2,
0x15173F3E9,
0x15174B9FA,
0x15175EAE1,
0x15176E778,
0x151787948,
0x15179956D,
0x1517B36C8,
0x1517DE653,
0x1517E2CE0,
0x1517EEAE2,
0x1517F92E1,
0x1517FB858,
0x151809719,
0x151868985,
0x1518BB832,
0x1518EE362,
0x1518FE162,
0x1519024C3,
0x151908639,
0x151985713,
0x15199565C,
0x1519C6C5B,
0x1519EB05E,
0x1519F0EE1,
0x1519F4A25,
0x151A0503F,
0x151A45752,
0x151A7E83E,
0x151A87C67,
0x151AAF804,
0x151AB3E72,
0x151AC3482,
0x151AC5B0F,
0x151ACBE3C,
};
constexpr uint64_t split_integrity_check_blocks[] =
{
0x1514B9FC8,
0x15161D235,
0x1516DE9FF,
0x151754691,
0x151772B7F,
0x1517B3CA9,
0x1517DB0D0,
0x1517E65DF,
0x15181407C,
0x151832847,
0x15183B664,
0x15199ABAA,
0x1519AA163,
0x1519C5852,
0x1519C7B0E,
0x1519C7F63,
0x1519F3291,
};
}
namespace sp
{
constexpr uint64_t intact_integrity_check_blocks[] =
{
0x15263308B,
};
constexpr uint64_t split_integrity_check_blocks[] =
{
0x152630C3F,
};
}

View File

@ -1,8 +1,20 @@
#include "hook.hpp" #include "hook.hpp"
#include "string.hpp"
#include <map>
#include <MinHook.h> #include <MinHook.h>
#include "concurrency.hpp"
#include "string.hpp"
#include "nt.hpp"
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
Mem seg_ptr(const SReg& segment, const uint64_t off) Mem seg_ptr(const SReg& segment, const uint64_t off)
{ {
auto mem = ptr_abs(off); auto mem = ptr_abs(off);
@ -14,22 +26,164 @@ namespace utils::hook
{ {
namespace namespace
{ {
[[maybe_unused]] class _ size_t get_allocation_granularity()
{
SYSTEM_INFO info{};
GetSystemInfo(&info);
return info.dwAllocationGranularity;
}
uint8_t* allocate_somewhere_near(const void* base_address, const size_t granularity, const size_t size)
{
size_t target_address = reinterpret_cast<size_t>(base_address) - (1ull << 31);
target_address &= ~(granularity - 1);
while (true)
{
target_address += granularity;
auto* target_ptr = reinterpret_cast<uint8_t*>(target_address);
if (is_relatively_far(base_address, target_ptr))
{
return nullptr;
}
const auto res = VirtualAlloc(target_ptr, size, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (res)
{
if (is_relatively_far(base_address, target_ptr))
{
VirtualFree(res, 0, MEM_RELEASE);
return nullptr;
}
return static_cast<uint8_t*>(res);
}
}
}
class memory
{ {
public: public:
_() memory() = default;
memory(const void* ptr)
: memory()
{ {
if (MH_Initialize() != MH_OK) static const auto allocation_granularity = get_allocation_granularity();
this->length_ = allocation_granularity;
this->buffer_ = allocate_somewhere_near(ptr, allocation_granularity, this->length_);
if (!this->buffer_)
{ {
throw std::runtime_error("Failed to initialize MinHook"); throw std::runtime_error("Failed to allocate");
} }
} }
~_() ~memory()
{ {
MH_Uninitialize(); if (this->buffer_)
{
VirtualFree(this->buffer_, 0, MEM_RELEASE);
}
} }
} __;
memory(memory&& obj) noexcept
: memory()
{
this->operator=(std::move(obj));
}
memory& operator=(memory&& obj) noexcept
{
if (this != &obj)
{
this->~memory();
this->buffer_ = obj.buffer_;
this->length_ = obj.length_;
this->offset_ = obj.offset_;
obj.buffer_ = nullptr;
obj.length_ = 0;
obj.offset_ = 0;
}
return *this;
}
void* allocate(const size_t length)
{
if (!this->buffer_)
{
return nullptr;
}
if (this->offset_ + length > this->length_)
{
return nullptr;
}
const auto ptr = this->get_ptr();
this->offset_ += length;
return ptr;
}
void* get_ptr() const
{
return this->buffer_ + this->offset_;
}
private:
uint8_t* buffer_{};
size_t length_{};
size_t offset_{};
};
void* get_memory_near(const void* address, const size_t size)
{
static concurrency::container<std::vector<memory>> memory_container{};
return memory_container.access<void*>([&](std::vector<memory>& memories)
{
for (auto& memory : memories)
{
if (!is_relatively_far(address, memory.get_ptr()))
{
const auto buffer = memory.allocate(size);
if (buffer)
{
return buffer;
}
}
}
memories.emplace_back(address);
return memories.back().allocate(size);
});
}
void* initialize_min_hook()
{
static class min_hook_init
{
public:
min_hook_init()
{
if (MH_Initialize() != MH_OK)
{
throw std::runtime_error("Failed to initialize MinHook");
}
}
~min_hook_init()
{
MH_Uninitialize();
}
} min_hook_init;
return &min_hook_init;
}
} }
void assembler::pushad64() void assembler::pushad64()
@ -43,12 +197,26 @@ namespace utils::hook
this->push(rsi); this->push(rsi);
this->push(rdi); this->push(rdi);
this->sub(rsp, 0x40); this->push(r8);
this->push(r9);
this->push(r10);
this->push(r11);
this->push(r12);
this->push(r13);
this->push(r14);
this->push(r15);
} }
void assembler::popad64() void assembler::popad64()
{ {
this->add(rsp, 0x40); this->pop(r15);
this->pop(r14);
this->pop(r13);
this->pop(r12);
this->pop(r11);
this->pop(r10);
this->pop(r9);
this->pop(r8);
this->pop(rdi); this->pop(rdi);
this->pop(rsi); this->pop(rsi);
@ -94,19 +262,26 @@ namespace utils::hook
asmjit::Error assembler::call(void* target) asmjit::Error assembler::call(void* target)
{ {
return Assembler::call(size_t(target)); return Assembler::call(reinterpret_cast<size_t>(target));
} }
asmjit::Error assembler::jmp(void* target) asmjit::Error assembler::jmp(void* target)
{ {
return Assembler::jmp(size_t(target)); return Assembler::jmp(reinterpret_cast<size_t>(target));
} }
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target) detour::detour()
{
(void)initialize_min_hook();
}
detour::detour(const size_t place, void* target)
: detour(reinterpret_cast<void*>(place), target)
{ {
} }
detour::detour(void* place, void* target) detour::detour(void* place, void* target)
: detour()
{ {
this->create(place, target); this->create(place, target);
} }
@ -116,13 +291,19 @@ namespace utils::hook
this->clear(); this->clear();
} }
void detour::enable() const void detour::enable()
{ {
MH_EnableHook(this->place_); MH_EnableHook(this->place_);
if (!this->moved_data_.empty())
{
this->move();
}
} }
void detour::disable() const void detour::disable()
{ {
this->un_move();
MH_DisableHook(this->place_); MH_DisableHook(this->place_);
} }
@ -148,11 +329,23 @@ namespace utils::hook
{ {
if (this->place_) if (this->place_)
{ {
this->un_move();
MH_RemoveHook(this->place_); MH_RemoveHook(this->place_);
} }
this->place_ = nullptr; this->place_ = nullptr;
this->original_ = nullptr; this->original_ = nullptr;
this->moved_data_ = {};
}
void detour::move()
{
this->moved_data_ = move_hook(this->place_);
}
void* detour::get_place() const
{
return this->place_;
} }
void* detour::get_original() const void* detour::get_original() const
@ -160,20 +353,29 @@ namespace utils::hook
return this->original_; return this->original_;
} }
bool iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub) void detour::un_move()
{ {
if (!library.is_valid()) return false; if (!this->moved_data_.empty())
{
copy(this->place_, this->moved_data_.data(), this->moved_data_.size());
}
}
std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library,
const std::string& process, void* stub)
{
if (!library.is_valid()) return {};
auto* const ptr = library.get_iat_entry(target_library, process); auto* const ptr = library.get_iat_entry(target_library, process);
if (!ptr) return false; if (!ptr) return {};
DWORD protect; DWORD protect;
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
*ptr = stub; std::swap(*ptr, stub);
VirtualProtect(ptr, sizeof(*ptr), protect, &protect); VirtualProtect(ptr, sizeof(*ptr), protect, &protect);
return true; return { {ptr, stub} };
} }
void nop(void* place, const size_t length) void nop(void* place, const size_t length)
@ -208,23 +410,50 @@ namespace utils::hook
copy(reinterpret_cast<void*>(place), data, length); copy(reinterpret_cast<void*>(place), data, length);
} }
void copy_string(void* place, const char* str)
{
copy(reinterpret_cast<void*>(place), str, strlen(str) + 1);
}
void copy_string(const size_t place, const char* str)
{
copy_string(reinterpret_cast<void*>(place), str);
}
bool is_relatively_far(const void* pointer, const void* data, const int offset) bool is_relatively_far(const void* pointer, const void* data, const int offset)
{ {
const int64_t diff = size_t(data) - (size_t(pointer) + offset); return is_relatively_far(reinterpret_cast<size_t>(pointer), reinterpret_cast<size_t>(data), offset);
const auto small_diff = int32_t(diff); }
return diff != int64_t(small_diff);
bool is_relatively_far(const size_t pointer, const size_t data, const int offset)
{
const auto diff = static_cast<int64_t>(data - (pointer + offset));
const auto small_diff = static_cast<int32_t>(diff);
return diff != static_cast<int64_t>(small_diff);
} }
void call(void* pointer, void* data) void call(void* pointer, void* data)
{ {
if (is_relatively_far(pointer, data)) if (is_relatively_far(pointer, data))
{ {
throw std::runtime_error("Too far away to create 32bit relative branch"); auto* trampoline = get_memory_near(pointer, 14);
if (!trampoline)
{
throw std::runtime_error("Too far away to create 32bit relative branch");
}
call(pointer, trampoline);
jump(trampoline, data, true, true);
return;
} }
auto* patch_pointer = PBYTE(pointer); uint8_t copy_data[5];
set<uint8_t>(patch_pointer, 0xE8); copy_data[0] = 0xE8;
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); *reinterpret_cast<int32_t*>(&copy_data[1]) = static_cast<int32_t>(reinterpret_cast<size_t>(data) - (
reinterpret_cast<size_t>(pointer) + 5));
auto* patch_pointer = static_cast<PBYTE>(pointer);
copy(patch_pointer, copy_data, sizeof(copy_data));
} }
void call(const size_t pointer, void* data) void call(const size_t pointer, void* data)
@ -237,39 +466,67 @@ namespace utils::hook
return call(pointer, reinterpret_cast<void*>(data)); return call(pointer, reinterpret_cast<void*>(data));
} }
void jump(void* pointer, void* data, const bool use_far) void jump(void* pointer, void* data, const bool use_far, const bool use_safe)
{ {
static const unsigned char jump_data[] = { static const unsigned char jump_data[] = {
0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xe0 0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xe0
}; };
static const unsigned char jump_data_safe[] = {
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00
};
if (!use_far && is_relatively_far(pointer, data)) if (!use_far && is_relatively_far(pointer, data))
{ {
throw std::runtime_error("Too far away to create 32bit relative branch"); auto* trampoline = get_memory_near(pointer, 14);
if (!trampoline)
{
throw std::runtime_error("Too far away to create 32bit relative branch");
}
jump(pointer, trampoline, false, false);
jump(trampoline, data, true, true);
return;
} }
auto* patch_pointer = PBYTE(pointer); auto* patch_pointer = PBYTE(pointer);
if (use_far) if (use_far)
{ {
copy(patch_pointer, jump_data, sizeof(jump_data)); if (use_safe)
copy(patch_pointer + 2, &data, sizeof(data)); {
uint8_t copy_data[sizeof(jump_data_safe) + sizeof(data)];
memcpy(copy_data, jump_data_safe, sizeof(jump_data_safe));
memcpy(copy_data + sizeof(jump_data_safe), &data, sizeof(data));
copy(patch_pointer, copy_data, sizeof(copy_data));
}
else
{
uint8_t copy_data[sizeof(jump_data)];
memcpy(copy_data, jump_data, sizeof(jump_data));
memcpy(copy_data + 2, &data, sizeof(data));
copy(patch_pointer, copy_data, sizeof(copy_data));
}
} }
else else
{ {
set<uint8_t>(patch_pointer, 0xE9); uint8_t copy_data[5];
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); copy_data[0] = 0xE9;
*reinterpret_cast<int32_t*>(&copy_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5));
copy(patch_pointer, copy_data, sizeof(copy_data));
} }
} }
void jump(const size_t pointer, void* data, const bool use_far) void jump(const size_t pointer, void* data, const bool use_far, const bool use_safe)
{ {
return jump(reinterpret_cast<void*>(pointer), data, use_far); return jump(reinterpret_cast<void*>(pointer), data, use_far, use_safe);
} }
void jump(const size_t pointer, const size_t data, const bool use_far) void jump(const size_t pointer, const size_t data, const bool use_far, const bool use_safe)
{ {
return jump(pointer, reinterpret_cast<void*>(data), use_far); return jump(pointer, reinterpret_cast<void*>(data), use_far, use_safe);
} }
void* assemble(const std::function<void(assembler&)>& asm_function) void* assemble(const std::function<void(assembler&)>& asm_function)
@ -284,24 +541,70 @@ namespace utils::hook
asm_function(a); asm_function(a);
void* result = nullptr; void* result = nullptr;
runtime.add(&result, &code); auto err_result = runtime.add(&result, &code);
if (err_result != asmjit::ErrorCode::kErrorOk)
{
printf("ASMJIT ERROR: %s\n", asmjit::DebugUtils::errorAsString(err_result));
}
return result; return result;
} }
void inject(void* pointer, const void* data) void inject(size_t pointer, size_t data)
{ {
if (is_relatively_far(pointer, data, 4)) if (is_relatively_far(pointer, data, 4))
{ {
throw std::runtime_error("Too far away to create 32bit relative branch"); throw std::runtime_error("Too far away to create 32bit relative branch");
} }
set<int32_t>(pointer, int32_t(size_t(data) - (size_t(pointer) + 4))); set<int32_t>(
pointer, static_cast<int32_t>(data - (pointer + 4)));
}
void inject(void* pointer, const void* data)
{
return inject(reinterpret_cast<size_t>(pointer), reinterpret_cast<size_t>(data));
} }
void inject(const size_t pointer, const void* data) void inject(const size_t pointer, const void* data)
{ {
return inject(reinterpret_cast<void*>(pointer), data); return inject(pointer, reinterpret_cast<size_t>(data));
}
std::vector<uint8_t> move_hook(void* pointer)
{
std::vector<uint8_t> original_data{};
auto* data_ptr = static_cast<uint8_t*>(pointer);
if (data_ptr[0] == 0xE9)
{
original_data.resize(6);
memmove(original_data.data(), pointer, original_data.size());
auto* target = follow_branch(data_ptr);
nop(data_ptr, 1);
jump(data_ptr + 1, target);
}
else if (data_ptr[0] == 0xFF && data_ptr[1] == 0x25)
{
original_data.resize(15);
memmove(original_data.data(), pointer, original_data.size());
copy(data_ptr + 1, data_ptr, 14);
nop(data_ptr, 1);
}
else
{
throw std::runtime_error("No branch instruction found");
}
return original_data;
}
std::vector<uint8_t> move_hook(const size_t pointer)
{
return move_hook(reinterpret_cast<void*>(pointer));
} }
void* follow_branch(void* address) void* follow_branch(void* address)
@ -314,31 +617,4 @@ namespace utils::hook
return extract<void*>(data + 1); return extract<void*>(data + 1);
} }
uint8_t* allocate_somewhere_near(const void* base_address, const size_t size)
{
size_t offset = 0;
while (true)
{
offset += size;
auto* target_address = static_cast<const uint8_t*>(base_address) - offset;
if (utils::hook::is_relatively_far(base_address, target_address))
{
return nullptr;
}
const auto res = VirtualAlloc(const_cast<uint8_t*>(target_address), size, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (res)
{
if (utils::hook::is_relatively_far(base_address, target_address))
{
VirtualFree(res, 0, MEM_RELEASE);
return nullptr;
}
return static_cast<uint8_t*>(res);
}
}
}
} }

View File

@ -13,20 +13,20 @@ namespace utils::hook
{ {
namespace detail namespace detail
{ {
template<size_t entries> template <size_t Entries>
std::vector<size_t(*)()> get_iota_functions() std::vector<size_t(*)()> get_iota_functions()
{ {
if constexpr (entries == 0) if constexpr (Entries == 0)
{ {
std::vector<size_t(*)()> functions; std::vector<size_t(*)()> functions;
return functions; return functions;
} }
else else
{ {
auto functions = get_iota_functions<entries - 1>(); auto functions = get_iota_functions<Entries - 1>();
functions.emplace_back([]() functions.emplace_back([]()
{ {
return entries - 1; return Entries - 1;
}); });
return functions; return functions;
} }
@ -39,8 +39,8 @@ namespace utils::hook
// Example: // Example:
// ID3D11Device* device = ... // ID3D11Device* device = ...
// auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D); // auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D);
template <size_t entries = 100, typename Class, typename T, typename... Args> template <size_t Entries = 100, typename Class, typename T, typename... Args>
void** get_vtable_entry(Class* obj, T (Class::* entry)(Args ...)) void** get_vtable_entry(Class* obj, T(Class::* entry)(Args ...))
{ {
union union
{ {
@ -50,11 +50,11 @@ namespace utils::hook
func = entry; func = entry;
auto iota_functions = detail::get_iota_functions<entries>(); auto iota_functions = detail::get_iota_functions<Entries>();
auto* object = iota_functions.data(); auto* object = iota_functions.data();
using FakeFunc = size_t(__thiscall*)(void* self); using fake_func = size_t(__thiscall*)(void* self);
auto index = static_cast<FakeFunc>(pointer)(&object); auto index = static_cast<fake_func>(pointer)(&object);
void** obj_v_table = *reinterpret_cast<void***>(obj); void** obj_v_table = *reinterpret_cast<void***>(obj);
return &obj_v_table[index]; return &obj_v_table[index];
@ -88,7 +88,7 @@ namespace utils::hook
class detour class detour
{ {
public: public:
detour() = default; detour();
detour(void* place, void* target); detour(void* place, void* target);
detour(size_t place, void* target); detour(size_t place, void* target);
~detour(); ~detour();
@ -102,13 +102,15 @@ namespace utils::hook
{ {
if (this != &other) if (this != &other)
{ {
this->~detour(); this->clear();
this->place_ = other.place_; this->place_ = other.place_;
this->original_ = other.original_; this->original_ = other.original_;
this->moved_data_ = other.moved_data_;
other.place_ = nullptr; other.place_ = nullptr;
other.original_ = nullptr; other.original_ = nullptr;
other.moved_data_ = {};
} }
return *this; return *this;
@ -117,13 +119,17 @@ namespace utils::hook
detour(const detour&) = delete; detour(const detour&) = delete;
detour& operator=(const detour&) = delete; detour& operator=(const detour&) = delete;
void enable() const; void enable();
void disable() const; void disable();
void create(void* place, void* target); void create(void* place, void* target);
void create(size_t place, void* target); void create(size_t place, void* target);
void clear(); void clear();
void move();
void* get_place() const;
template <typename T> template <typename T>
T* get() const T* get() const
{ {
@ -139,11 +145,15 @@ namespace utils::hook
[[nodiscard]] void* get_original() const; [[nodiscard]] void* get_original() const;
private: private:
std::vector<uint8_t> moved_data_{};
void* place_{}; void* place_{};
void* original_{}; void* original_{};
void un_move();
}; };
bool iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library,
const std::string& process, void* stub);
void nop(void* place, size_t length); void nop(void* place, size_t length);
void nop(size_t place, size_t length); void nop(size_t place, size_t length);
@ -151,20 +161,28 @@ namespace utils::hook
void copy(void* place, const void* data, size_t length); void copy(void* place, const void* data, size_t length);
void copy(size_t place, const void* data, size_t length); void copy(size_t place, const void* data, size_t length);
void copy_string(void* place, const char* str);
void copy_string(size_t place, const char* str);
bool is_relatively_far(const void* pointer, const void* data, int offset = 5); bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
bool is_relatively_far(size_t pointer, size_t data, int offset = 5);
void call(void* pointer, void* data); void call(void* pointer, void* data);
void call(size_t pointer, void* data); void call(size_t pointer, void* data);
void call(size_t pointer, size_t data); void call(size_t pointer, size_t data);
void jump(void* pointer, void* data, bool use_far = false); void jump(void* pointer, void* data, bool use_far = false, bool use_safe = false);
void jump(size_t pointer, void* data, bool use_far = false); void jump(size_t pointer, void* data, bool use_far = false, bool use_safe = false);
void jump(size_t pointer, size_t data, bool use_far = false); void jump(size_t pointer, size_t data, bool use_far = false, bool use_safe = false);
void* assemble(const std::function<void(assembler&)>& asm_function); void* assemble(const std::function<void(assembler&)>& asm_function);
void inject(void* pointer, const void* data); void inject(void* pointer, const void* data);
void inject(size_t pointer, const void* data); void inject(size_t pointer, const void* data);
void inject(size_t pointer, size_t data);
std::vector<uint8_t> move_hook(void* pointer);
std::vector<uint8_t> move_hook(size_t pointer);
template <typename T> template <typename T>
T extract(void* address) T extract(void* address)
@ -177,19 +195,13 @@ namespace utils::hook
void* follow_branch(void* address); void* follow_branch(void* address);
template <typename T> template <typename T>
static void set(void* place, T value) static void set(void* place, T value = false)
{ {
DWORD old_protect; copy(place, &value, sizeof(value));
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> template <typename T>
static void set(const size_t place, T value) static void set(const size_t place, T value = false)
{ {
return set<T>(reinterpret_cast<void*>(place), value); return set<T>(reinterpret_cast<void*>(place), value);
} }
@ -206,8 +218,6 @@ namespace utils::hook
return static_cast<T(*)(Args ...)>(func)(args...); return static_cast<T(*)(Args ...)>(func)(args...);
} }
uint8_t* allocate_somewhere_near(const void* base_address, const size_t size);
template <size_t Base> template <size_t Base>
void* allocate_far_jump() void* allocate_far_jump()
{ {
@ -255,11 +265,4 @@ namespace utils::hook
const auto pos = create_far_jump<Base>(dest); const auto pos = create_far_jump<Base>(dest);
jump(address, pos, false); jump(address, pos, false);
} }
template <size_t Base, typename T>
void far_call(const size_t address, const T dest)
{
const auto pos = create_far_jump<Base>(dest);
call(address, pos);
}
} }

View File

@ -4,6 +4,14 @@
#include <intrin.h> #include <intrin.h>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace utils::hook namespace utils::hook
{ {
void signature::load_pattern(const std::string& pattern) void signature::load_pattern(const std::string& pattern)
@ -29,7 +37,7 @@ namespace utils::hook
throw std::runtime_error("Invalid pattern"); throw std::runtime_error("Invalid pattern");
} }
char str[] = {val, 0}; char str[] = { val, 0 };
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16)); const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
if (!has_nibble) if (!has_nibble)
@ -68,15 +76,15 @@ namespace utils::hook
} }
} }
std::vector<size_t> signature::process_range(uint8_t* start, const size_t length) const signature::signature_result signature::process_range(uint8_t* start, const size_t length) const
{ {
if (this->has_sse_support()) return this->process_range_vectorized(start, length); if (this->has_sse_support()) return this->process_range_vectorized(start, length);
return this->process_range_linear(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 signature::signature_result signature::process_range_linear(uint8_t* start, const size_t length) const
{ {
std::vector<size_t> result; std::vector<uint8_t*> result;
for (size_t i = 0; i < length; ++i) for (size_t i = 0; i < length; ++i)
{ {
@ -93,17 +101,17 @@ namespace utils::hook
if (j == this->mask_.size()) if (j == this->mask_.size())
{ {
result.push_back(size_t(address)); result.push_back(address);
} }
} }
return result; return result;
} }
std::vector<size_t> signature::process_range_vectorized(uint8_t* start, const size_t length) const signature::signature_result signature::process_range_vectorized(uint8_t* start, const size_t length) const
{ {
std::vector<size_t> result; std::vector<uint8_t*> result;
__declspec(align(16)) char desired_mask[16] = {0}; __declspec(align(16)) char desired_mask[16] = { 0 };
for (size_t i = 0; i < this->mask_.size(); i++) for (size_t i = 0; i < this->mask_.size(); i++)
{ {
@ -118,14 +126,14 @@ namespace utils::hook
const auto address = start + i; const auto address = start + i;
const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address)); const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()), const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
_SIDD_CMP_EQUAL_EACH); _SIDD_CMP_EQUAL_EACH);
const auto matches = _mm_and_si128(mask, comparison); const auto matches = _mm_and_si128(mask, comparison);
const auto equivalence = _mm_xor_si128(mask, matches); const auto equivalence = _mm_xor_si128(mask, matches);
if (_mm_test_all_zeros(equivalence, equivalence)) if (_mm_test_all_zeros(equivalence, equivalence))
{ {
result.push_back(size_t(address)); result.push_back(address);
} }
} }
@ -144,7 +152,7 @@ namespace utils::hook
signature::signature_result signature::process_serial() const signature::signature_result signature::process_serial() const
{ {
const auto sub = this->has_sse_support() ? 16 : this->mask_.size(); const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
return {this->process_range(this->start_, this->length_ - sub)}; return { this->process_range(this->start_, this->length_ - sub) };
} }
signature::signature_result signature::process_parallel() const signature::signature_result signature::process_parallel() const
@ -156,7 +164,7 @@ namespace utils::hook
const auto grid = range / cores; const auto grid = range / cores;
std::mutex mutex; std::mutex mutex;
std::vector<size_t> result; std::vector<uint8_t*> result;
std::vector<std::thread> threads; std::vector<std::thread> threads;
for (auto i = 0u; i < cores; ++i) for (auto i = 0u; i < cores; ++i)
@ -165,7 +173,7 @@ namespace utils::hook
const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid; const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
threads.emplace_back([&, start, length]() threads.emplace_back([&, start, length]()
{ {
auto local_result = this->process_range(start, length); const auto local_result = this->process_range(start, length);
if (local_result.empty()) return; if (local_result.empty()) return;
std::lock_guard _(mutex); std::lock_guard _(mutex);
@ -185,7 +193,7 @@ namespace utils::hook
} }
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
return {std::move(result)}; return { std::move(result) };
} }
bool signature::has_sse_support() const bool signature::has_sse_support() const

View File

@ -7,33 +7,9 @@ namespace utils::hook
class signature final class signature final
{ {
public: public:
class signature_result using signature_result = std::vector<uint8_t*>;
{
public:
signature_result(std::vector<size_t>&& matches) : matches_(std::move(matches))
{
}
[[nodiscard]] uint8_t* get(const size_t index) const explicit signature(const std::string& pattern, const nt::library& library = {})
{
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(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
{ {
} }
@ -62,9 +38,9 @@ namespace utils::hook
signature_result process_parallel() const; signature_result process_parallel() const;
signature_result process_serial() const; signature_result process_serial() const;
std::vector<size_t> process_range(uint8_t* start, size_t length) const; signature_result process_range(uint8_t* start, size_t length) const;
std::vector<size_t> process_range_linear(uint8_t* start, size_t length) const; signature_result process_range_linear(uint8_t* start, size_t length) const;
std::vector<size_t> process_range_vectorized(uint8_t* start, size_t length) const; signature_result process_range_vectorized(uint8_t* start, size_t length) const;
bool has_sse_support() const; bool has_sse_support() const;
}; };