diff --git a/src/client/component/arxan.cpp b/src/client/component/arxan.cpp deleted file mode 100644 index 6bb915e7..00000000 --- a/src/client/component/arxan.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include "loader/component_loader.hpp" - -#include "arxan.hpp" -#include "scheduler.hpp" - -#include "game/game.hpp" - -#include - -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(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(info)->Reserved3 = PVOID(DWORD64(explorer_pid)); - } - else if (info_class == 30) // ProcessDebugObjectHandle - { - *static_cast(info) = nullptr; - - return 0xC0000353; - } - else if (info_class == 7) // ProcessDebugPort - { - *static_cast(info) = nullptr; - } - else if (info_class == 31) - { - *static_cast(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(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(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("NtClose"), nt_close_stub); - nt_query_information_process_hook.create(ntdll.get_proc("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) diff --git a/src/client/component/arxan/arxan.cpp b/src/client/component/arxan/arxan.cpp new file mode 100644 index 00000000..aa809560 --- /dev/null +++ b/src/client/component/arxan/arxan.cpp @@ -0,0 +1,587 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include "component/game_module.hpp" +#include "component/scheduler.hpp" + +#include +#include + +#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>& get_text_sections() + { + static const std::vector> text = [] + { + std::vector> 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(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(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(stack_frame); + const auto pointer_value = reinterpret_cast(pointer); + + const auto diff = static_cast(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(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(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(address); + constexpr auto inst_len = 3; + + const auto next_inst_addr = game_address + inst_len; + const auto next_inst = *reinterpret_cast(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(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(game_address, static_cast(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(address); + constexpr auto inst_len = 3; + + const auto next_inst_addr = game_address + inst_len; + + if (*reinterpret_cast(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(reinterpret_cast(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(i)); + } + for (const auto i : sp::split_integrity_check_blocks) + { + patch_split_basic_block_integrity_check(reinterpret_cast(i)); + } + } + else + { + for (const auto i : mp::intact_integrity_check_blocks) + { + patch_intact_basic_block_integrity_check(reinterpret_cast(i)); + } + for (const auto i : mp::split_integrity_check_blocks) + { + patch_split_basic_block_integrity_check(reinterpret_cast(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(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(info) = nullptr; + return static_cast(0xC0000353); + } + else if (info_class == ProcessDebugPort) + { + *static_cast(info) = nullptr; + } + else if (info_class == ProcessDebugFlags) + { + *static_cast(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(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(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(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 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(reinterpret_cast(address) + 3); + fake_record.ExceptionCode = EXCEPTION_BREAKPOINT; + + for (auto handler : handle_handler) + { + if (handler.second) + { + auto result = utils::hook::invoke(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(address); + + const auto jump_target = utils::hook::extract(reinterpret_cast(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(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("NtClose"), nt_close_stub); + + const auto nt_query_information_process = ntdll.get_proc("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) \ No newline at end of file diff --git a/src/client/component/arxan/breakpoints.hpp b/src/client/component/arxan/breakpoints.hpp new file mode 100644 index 00000000..7025f794 --- /dev/null +++ b/src/client/component/arxan/breakpoints.hpp @@ -0,0 +1,165 @@ +#pragma once + +#include + +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, + }; +} \ No newline at end of file diff --git a/src/client/component/arxan/integrity.hpp b/src/client/component/arxan/integrity.hpp new file mode 100644 index 00000000..32e68424 --- /dev/null +++ b/src/client/component/arxan/integrity.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +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, + }; +} diff --git a/src/common/utils/hook.cpp b/src/common/utils/hook.cpp index 5e6bc9bb..b58b34d3 100644 --- a/src/common/utils/hook.cpp +++ b/src/common/utils/hook.cpp @@ -1,8 +1,20 @@ #include "hook.hpp" -#include "string.hpp" +#include #include +#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) { auto mem = ptr_abs(off); @@ -14,22 +26,164 @@ namespace utils::hook { 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(base_address) - (1ull << 31); + target_address &= ~(granularity - 1); + + while (true) + { + target_address += granularity; + + auto* target_ptr = reinterpret_cast(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(res); + } + } + } + + class memory { 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> memory_container{}; + + return memory_container.access([&](std::vector& 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() @@ -43,12 +197,26 @@ namespace utils::hook this->push(rsi); 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() { - 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(rsi); @@ -94,19 +262,26 @@ namespace utils::hook asmjit::Error assembler::call(void* target) { - return Assembler::call(size_t(target)); + return Assembler::call(reinterpret_cast(target)); } asmjit::Error assembler::jmp(void* target) { - return Assembler::jmp(size_t(target)); + return Assembler::jmp(reinterpret_cast(target)); } - detour::detour(const size_t place, void* target) : detour(reinterpret_cast(place), target) + detour::detour() + { + (void)initialize_min_hook(); + } + + detour::detour(const size_t place, void* target) + : detour(reinterpret_cast(place), target) { } detour::detour(void* place, void* target) + : detour() { this->create(place, target); } @@ -116,13 +291,19 @@ namespace utils::hook this->clear(); } - void detour::enable() const + void detour::enable() { 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_); } @@ -148,11 +329,23 @@ namespace utils::hook { if (this->place_) { + this->un_move(); MH_RemoveHook(this->place_); } this->place_ = 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 @@ -160,20 +353,29 @@ namespace utils::hook 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> 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); - if (!ptr) return false; + if (!ptr) return {}; DWORD protect; VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); - *ptr = stub; + std::swap(*ptr, stub); VirtualProtect(ptr, sizeof(*ptr), protect, &protect); - return true; + return { {ptr, stub} }; } void nop(void* place, const size_t length) @@ -208,23 +410,50 @@ namespace utils::hook copy(reinterpret_cast(place), data, length); } + void copy_string(void* place, const char* str) + { + copy(reinterpret_cast(place), str, strlen(str) + 1); + } + + void copy_string(const size_t place, const char* str) + { + copy_string(reinterpret_cast(place), str); + } + bool is_relatively_far(const void* pointer, const void* data, const int offset) { - const int64_t diff = size_t(data) - (size_t(pointer) + offset); - const auto small_diff = int32_t(diff); - return diff != int64_t(small_diff); + return is_relatively_far(reinterpret_cast(pointer), reinterpret_cast(data), offset); + } + + bool is_relatively_far(const size_t pointer, const size_t data, const int offset) + { + const auto diff = static_cast(data - (pointer + offset)); + const auto small_diff = static_cast(diff); + return diff != static_cast(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* 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); - set(patch_pointer, 0xE8); - set(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); + uint8_t copy_data[5]; + copy_data[0] = 0xE8; + *reinterpret_cast(©_data[1]) = static_cast(reinterpret_cast(data) - ( + reinterpret_cast(pointer) + 5)); + + auto* patch_pointer = static_cast(pointer); + copy(patch_pointer, copy_data, sizeof(copy_data)); } void call(const size_t pointer, void* data) @@ -237,39 +466,67 @@ namespace utils::hook return call(pointer, reinterpret_cast(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[] = { 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)) { - 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); if (use_far) { - copy(patch_pointer, jump_data, sizeof(jump_data)); - copy(patch_pointer + 2, &data, sizeof(data)); + if (use_safe) + { + 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 { - set(patch_pointer, 0xE9); - set(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); + uint8_t copy_data[5]; + copy_data[0] = 0xE9; + *reinterpret_cast(©_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(pointer), data, use_far); + return jump(reinterpret_cast(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(data), use_far); + return jump(pointer, reinterpret_cast(data), use_far, use_safe); } void* assemble(const std::function& asm_function) @@ -284,24 +541,70 @@ namespace utils::hook asm_function(a); 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; } - void inject(void* pointer, const void* data) + void inject(size_t pointer, size_t data) { if (is_relatively_far(pointer, data, 4)) { throw std::runtime_error("Too far away to create 32bit relative branch"); } - set(pointer, int32_t(size_t(data) - (size_t(pointer) + 4))); + set( + pointer, static_cast(data - (pointer + 4))); + } + + void inject(void* pointer, const void* data) + { + return inject(reinterpret_cast(pointer), reinterpret_cast(data)); } void inject(const size_t pointer, const void* data) { - return inject(reinterpret_cast(pointer), data); + return inject(pointer, reinterpret_cast(data)); + } + + std::vector move_hook(void* pointer) + { + std::vector original_data{}; + + auto* data_ptr = static_cast(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 move_hook(const size_t pointer) + { + return move_hook(reinterpret_cast(pointer)); } void* follow_branch(void* address) @@ -314,31 +617,4 @@ namespace utils::hook return extract(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(base_address) - offset; - if (utils::hook::is_relatively_far(base_address, target_address)) - { - return nullptr; - } - - const auto res = VirtualAlloc(const_cast(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(res); - } - } - } -} +} \ No newline at end of file diff --git a/src/common/utils/hook.hpp b/src/common/utils/hook.hpp index 911b183d..6c8ba52b 100644 --- a/src/common/utils/hook.hpp +++ b/src/common/utils/hook.hpp @@ -13,20 +13,20 @@ namespace utils::hook { namespace detail { - template + template std::vector get_iota_functions() { - if constexpr (entries == 0) + if constexpr (Entries == 0) { std::vector functions; return functions; } else { - auto functions = get_iota_functions(); + auto functions = get_iota_functions(); functions.emplace_back([]() { - return entries - 1; + return Entries - 1; }); return functions; } @@ -39,8 +39,8 @@ namespace utils::hook // Example: // ID3D11Device* device = ... // auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D); - template - void** get_vtable_entry(Class* obj, T (Class::* entry)(Args ...)) + template + void** get_vtable_entry(Class* obj, T(Class::* entry)(Args ...)) { union { @@ -50,11 +50,11 @@ namespace utils::hook func = entry; - auto iota_functions = detail::get_iota_functions(); + auto iota_functions = detail::get_iota_functions(); auto* object = iota_functions.data(); - using FakeFunc = size_t(__thiscall*)(void* self); - auto index = static_cast(pointer)(&object); + using fake_func = size_t(__thiscall*)(void* self); + auto index = static_cast(pointer)(&object); void** obj_v_table = *reinterpret_cast(obj); return &obj_v_table[index]; @@ -88,7 +88,7 @@ namespace utils::hook class detour { public: - detour() = default; + detour(); detour(void* place, void* target); detour(size_t place, void* target); ~detour(); @@ -102,13 +102,15 @@ namespace utils::hook { if (this != &other) { - this->~detour(); + this->clear(); this->place_ = other.place_; this->original_ = other.original_; + this->moved_data_ = other.moved_data_; other.place_ = nullptr; other.original_ = nullptr; + other.moved_data_ = {}; } return *this; @@ -117,13 +119,17 @@ namespace utils::hook detour(const detour&) = delete; detour& operator=(const detour&) = delete; - void enable() const; - void disable() const; + void enable(); + void disable(); void create(void* place, void* target); void create(size_t place, void* target); void clear(); + void move(); + + void* get_place() const; + template T* get() const { @@ -139,11 +145,15 @@ namespace utils::hook [[nodiscard]] void* get_original() const; private: + std::vector moved_data_{}; void* place_{}; void* original_{}; + + void un_move(); }; - bool iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); + std::optional> 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(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(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(size_t pointer, size_t 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(void* pointer, void* data, bool use_far = false); - void jump(size_t pointer, void* data, bool use_far = false); - void jump(size_t pointer, size_t 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, bool use_safe = false); + void jump(size_t pointer, size_t data, bool use_far = false, bool use_safe = false); void* assemble(const std::function& asm_function); void inject(void* pointer, const void* data); void inject(size_t pointer, const void* data); + void inject(size_t pointer, size_t data); + + std::vector move_hook(void* pointer); + std::vector move_hook(size_t pointer); template T extract(void* address) @@ -177,19 +195,13 @@ namespace utils::hook void* follow_branch(void* address); template - static void set(void* place, T value) + static void set(void* place, T value = false) { - DWORD old_protect; - VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect); - - *static_cast(place) = value; - - VirtualProtect(place, sizeof(T), old_protect, &old_protect); - FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + copy(place, &value, sizeof(value)); } template - static void set(const size_t place, T value) + static void set(const size_t place, T value = false) { return set(reinterpret_cast(place), value); } @@ -206,8 +218,6 @@ namespace utils::hook return static_cast(func)(args...); } - uint8_t* allocate_somewhere_near(const void* base_address, const size_t size); - template void* allocate_far_jump() { @@ -255,11 +265,4 @@ namespace utils::hook const auto pos = create_far_jump(dest); jump(address, pos, false); } - - template - void far_call(const size_t address, const T dest) - { - const auto pos = create_far_jump(dest); - call(address, pos); - } -} +} \ No newline at end of file diff --git a/src/common/utils/signature.cpp b/src/common/utils/signature.cpp index 9bb8c621..6a9933c3 100644 --- a/src/common/utils/signature.cpp +++ b/src/common/utils/signature.cpp @@ -4,6 +4,14 @@ #include +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + namespace utils::hook { void signature::load_pattern(const std::string& pattern) @@ -29,7 +37,7 @@ namespace utils::hook throw std::runtime_error("Invalid pattern"); } - char str[] = {val, 0}; + char str[] = { val, 0 }; const auto current_nibble = static_cast(strtol(str, nullptr, 16)); if (!has_nibble) @@ -68,15 +76,15 @@ namespace utils::hook } } - std::vector 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); return this->process_range_linear(start, length); } - std::vector 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 result; + std::vector result; for (size_t i = 0; i < length; ++i) { @@ -93,17 +101,17 @@ namespace utils::hook if (j == this->mask_.size()) { - result.push_back(size_t(address)); + result.push_back(address); } } return result; } - std::vector 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 result; - __declspec(align(16)) char desired_mask[16] = {0}; + std::vector result; + __declspec(align(16)) char desired_mask[16] = { 0 }; for (size_t i = 0; i < this->mask_.size(); i++) { @@ -118,14 +126,14 @@ namespace utils::hook const auto address = start + i; const auto value = _mm_loadu_si128(reinterpret_cast(address)); const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast(this->mask_.size()), - _SIDD_CMP_EQUAL_EACH); + _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)); + result.push_back(address); } } @@ -144,7 +152,7 @@ namespace utils::hook 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)}; + return { this->process_range(this->start_, this->length_ - sub) }; } signature::signature_result signature::process_parallel() const @@ -156,7 +164,7 @@ namespace utils::hook const auto grid = range / cores; std::mutex mutex; - std::vector result; + std::vector result; std::vector threads; 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; 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; std::lock_guard _(mutex); @@ -185,7 +193,7 @@ namespace utils::hook } std::sort(result.begin(), result.end()); - return {std::move(result)}; + return { std::move(result) }; } bool signature::has_sse_support() const diff --git a/src/common/utils/signature.hpp b/src/common/utils/signature.hpp index a3728327..054e6b45 100644 --- a/src/common/utils/signature.hpp +++ b/src/common/utils/signature.hpp @@ -7,33 +7,9 @@ namespace utils::hook class signature final { public: - class signature_result - { - public: - signature_result(std::vector&& matches) : matches_(std::move(matches)) - { - } + using signature_result = std::vector; - [[nodiscard]] uint8_t* get(const size_t index) const - { - if (index >= this->count()) - { - throw std::runtime_error("Invalid index"); - } - - return reinterpret_cast(this->matches_[index]); - } - - [[nodiscard]] size_t count() const - { - return this->matches_.size(); - } - - private: - std::vector matches_; - }; - - explicit signature(const std::string& pattern, const nt::library library = {}) + explicit signature(const std::string& pattern, const nt::library& library = {}) : signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage) { } @@ -62,9 +38,9 @@ namespace utils::hook signature_result process_parallel() const; signature_result process_serial() const; - std::vector process_range(uint8_t* start, size_t length) const; - std::vector process_range_linear(uint8_t* start, size_t length) const; - std::vector process_range_vectorized(uint8_t* start, size_t length) const; + signature_result process_range(uint8_t* start, size_t length) const; + signature_result process_range_linear(uint8_t* start, size_t length) const; + signature_result process_range_vectorized(uint8_t* start, size_t length) const; bool has_sse_support() const; };