From 7135bdf3825492f7d328b388c933bd2cd7811e7f Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 10 Aug 2024 13:27:36 +1200 Subject: [PATCH 1/4] allow specifying sigscan pattern offset inside hex string got annoying counting bytes --- src/unrealsdk/game/bl2/globals.cpp | 20 ++++---- src/unrealsdk/game/bl2/memory.cpp | 7 ++- src/unrealsdk/game/bl3/globals.cpp | 28 +++++------ src/unrealsdk/game/bl3/hooks.cpp | 1 - src/unrealsdk/memory.h | 78 +++++++++++++++++++----------- 5 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/unrealsdk/game/bl2/globals.cpp b/src/unrealsdk/game/bl2/globals.cpp index b83c9dc..f490bb4 100644 --- a/src/unrealsdk/game/bl2/globals.cpp +++ b/src/unrealsdk/game/bl2/globals.cpp @@ -17,12 +17,11 @@ namespace { GObjects gobjects_wrapper{}; const constinit Pattern<17> GOBJECTS_SIG{ - "8B 0D ????????" // mov ecx, [Borderlands2.exe+1682BD0] - "8B 04 ??" // mov eax, [ecx+esi*4] - "8B 40 ??" // mov eax, [eax+08] - "25 00020000" // and eax, 00000200 - , - 2}; + "8B 0D {????????}" // mov ecx, [Borderlands2.exe+1682BD0] + "8B 04 ??" // mov eax, [ecx+esi*4] + "8B 40 ??" // mov eax, [eax+08] + "25 00020000" // and eax, 00000200 +}; } // namespace @@ -42,11 +41,10 @@ namespace { GNames gnames_wrapper{}; const constinit Pattern<10> GNAMES_SIG{ - "A3 ????????" // mov [BorderlandsPreSequel.exe+1520214], eax - "8B 45 ??" // mov eax, [ebp+10] - "89 03" // mov [ebx], eax - , - 1}; + "A3 {????????}" // mov [BorderlandsPreSequel.exe+1520214], eax + "8B 45 ??" // mov eax, [ebp+10] + "89 03" // mov [ebx], eax +}; } // namespace diff --git a/src/unrealsdk/game/bl2/memory.cpp b/src/unrealsdk/game/bl2/memory.cpp index 70dba95..23becec 100644 --- a/src/unrealsdk/game/bl2/memory.cpp +++ b/src/unrealsdk/game/bl2/memory.cpp @@ -36,10 +36,9 @@ struct FMalloc { FMalloc* gmalloc; const constinit Pattern<8> GMALLOC_PATTERN{ - "89 35 ????????" // mov [Borderlands2.GDebugger+A95C], esi - "FF D7" // call edi - , - 2}; + "89 35 {????????}" // mov [Borderlands2.GDebugger+A95C], esi + "FF D7" // call edi +}; } // namespace diff --git a/src/unrealsdk/game/bl3/globals.cpp b/src/unrealsdk/game/bl3/globals.cpp index 21969e9..b716e5d 100644 --- a/src/unrealsdk/game/bl3/globals.cpp +++ b/src/unrealsdk/game/bl3/globals.cpp @@ -17,12 +17,11 @@ namespace { GObjects gobjects_wrapper{}; const constinit Pattern<26> GOBJECTS_SIG{ - "48 8D 0D ????????" // lea rcx, [Borderlands3.exe+69EBDA0] - "C6 05 ???????? 01" // mov byte ptr [Borderlands3.exe+69EA290], 01 - "E8 ????????" // call Borderlands3.exe+17854D0 - "C6 05 ???????? 01" // mov byte ptr [Borderlands3.exe+64B78E0], 01 - , - 3}; + "48 8D 0D {????????}" // lea rcx, [Borderlands3.exe+69EBDA0] + "C6 05 ???????? 01" // mov byte ptr [Borderlands3.exe+69EA290], 01 + "E8 ????????" // call Borderlands3.exe+17854D0 + "C6 05 ???????? 01" // mov byte ptr [Borderlands3.exe+64B78E0], 01 +}; } // namespace @@ -42,15 +41,14 @@ namespace { GNames gnames_wrapper{}; const constinit Pattern<27> GNAMES_SIG{ - "E8 ????????" // call Borderlands3.exe+3DDBB7C - "48 8B C3" // mov rax, rbx - "48 89 1D ????????" // mov [Borderlands3.exe+69E71E8], rbx - "48 8B 5C 24 ??" // mov rbx, [rsp+20] - "48 83 C4 28" // add rsp, 28 - "C3" // ret - "33 DB" // xor ebx, ebx - , - 11}; + "E8 ????????" // call Borderlands3.exe+3DDBB7C + "48 8B C3" // mov rax, rbx + "48 89 1D {????????}" // mov [Borderlands3.exe+69E71E8], rbx + "48 8B 5C 24 ??" // mov rbx, [rsp+20] + "48 83 C4 28" // add rsp, 28 + "C3" // ret + "33 DB" // xor ebx, ebx +}; } // namespace diff --git a/src/unrealsdk/game/bl3/hooks.cpp b/src/unrealsdk/game/bl3/hooks.cpp index be465ed..083608f 100644 --- a/src/unrealsdk/game/bl3/hooks.cpp +++ b/src/unrealsdk/game/bl3/hooks.cpp @@ -133,7 +133,6 @@ const constinit Pattern<20> CALL_FUNCTION_SIG{ "41 56" // push r14 "41 57" // push r15 "48 81 EC 28010000" // sub rsp, 00000128 - }; } // namespace diff --git a/src/unrealsdk/memory.h b/src/unrealsdk/memory.h index 047e2ad..c4ddb14 100644 --- a/src/unrealsdk/memory.h +++ b/src/unrealsdk/memory.h @@ -1,5 +1,5 @@ -#ifndef UNREALSDK_SIGSCAN_H -#define UNREALSDK_SIGSCAN_H +#ifndef UNREALSDK_MEMORY_H +#define UNREALSDK_MEMORY_H #include "unrealsdk/pch.h" @@ -107,10 +107,35 @@ struct Pattern { static_assert(sizeof(uint8_t) == sizeof(char), "uint8_t is different size to char"); } + private: + /** + * @brief Converts a hex character to it's nibble and a mask. + * + * @param character The character. + * @return A pair of the nibble and it's mask. + */ + consteval std::pair char_to_nibble_and_mask(char character) { + // NOLINTBEGIN(readability-magic-numbers) + if ('0' <= character && character <= '9') { + return {(uint8_t)(character - '0'), (uint8_t)0xF}; + } + if ('A' <= character && character <= 'F') { + return {(uint8_t)(character - 'A' + 0xA), (uint8_t)0xF}; + } + if ('a' <= character && character <= 'f') { + return {(uint8_t)(character - 'a' + 0xA), (uint8_t)0xF}; + } + return {(uint8_t)0, (uint8_t)0}; + // NOLINTEND(readability-magic-numbers) + } + + public: /** * @brief Constructs a pattern from a hex string, at compile time. - * @note Spaces are ignored, all other non hex characters get converted into a wildcard. - * @note The string must contain a whole number of bytes. + * @note An opening curly bracket sets the offset - only the first instance is used. + * @note Spaces and closing curly brackets are ignored. + * @note All other characters are considered wildcards. + * @note The string must contain a whole number of bytes. Nibble wildcards are allowed. * * @tparam m The size of the passed hex string - should be picked up automatically. * @param hex The hex string to convert. @@ -118,7 +143,8 @@ struct Pattern { * @return A sigscan pattern. */ template - consteval Pattern(const char (&hex)[m], ptrdiff_t offset = 0) + consteval Pattern(const char (&hex)[m], + ptrdiff_t offset = std::numeric_limits::max()) : bytes(), mask(), offset(offset) { size_t idx = 0; bool upper_nibble = true; @@ -127,34 +153,28 @@ struct Pattern { if (character == '\0') { break; } - if (character == ' ') { + if (character == ' ' || character == '}') { continue; } - - uint8_t byte = 0; - uint8_t mask_byte = 0; - - // NOLINTBEGIN(readability-magic-numbers) - if ('0' <= character && character <= '9') { - byte = character - '0'; - mask_byte = 0xF; - } else if ('A' <= character && character <= 'F') { - byte = character - 'A' + 0xA; - mask_byte = 0xF; - } else if ('a' <= character && character <= 'f') { - byte = character - 'a' + 0xA; - mask_byte = 0xF; + if (character == '{') { + if (!upper_nibble) { + throw std::logic_error("Cannot start pattern offset halfway through a byte"); + } + if (this->offset == std::numeric_limits::max()) { + this->offset = idx; + } + continue; } - // NOLINTEND(readability-magic-numbers) + auto [nibble, nibble_mask] = char_to_nibble_and_mask(character); if (upper_nibble) { - this->bytes[idx] = byte << 4; - this->mask[idx] = mask_byte << 4; + this->bytes[idx] = nibble << 4; + this->mask[idx] = nibble_mask << 4; upper_nibble = false; } else { - this->bytes[idx] |= byte; - this->mask[idx] |= mask_byte; + this->bytes[idx] |= nibble; + this->mask[idx] |= nibble_mask; idx++; upper_nibble = true; @@ -164,7 +184,11 @@ struct Pattern { // Make sure we completely filled the pattern, there are no missing or extra bytes, and // we're not halfway through one. if (idx != n || !upper_nibble) { - throw "Invalid pattern size"; + throw std::logic_error("Invalid pattern size"); + } + + if (this->offset == std::numeric_limits::max()) { + this->offset = 0; } } @@ -196,4 +220,4 @@ struct Pattern { } // namespace unrealsdk::memory -#endif /* UNREALSDK_SIGSCAN_H */ +#endif /* UNREALSDK_MEMORY_H */ From b849b0e8a16a33bfe5d73a9c22308382521357d1 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 10 Aug 2024 14:15:02 +1200 Subject: [PATCH 2/4] rework handling of failled sigscan errors motivated by the bl3 mod menu breaking with no warnings or error messages, since it wasn't catching any errors --- src/unrealsdk/game/bl2/bl2.cpp | 4 +-- src/unrealsdk/game/bl2/globals.cpp | 4 +-- src/unrealsdk/game/bl2/hexedits.cpp | 6 ++-- src/unrealsdk/game/bl2/hooks.cpp | 4 +-- src/unrealsdk/game/bl2/memory.cpp | 4 ++- src/unrealsdk/game/bl2/object.cpp | 8 ++--- src/unrealsdk/game/bl3/bl3.cpp | 6 ++-- src/unrealsdk/game/bl3/globals.cpp | 6 ++-- src/unrealsdk/game/bl3/hooks.cpp | 4 +-- src/unrealsdk/game/bl3/memory.cpp | 6 ++-- src/unrealsdk/game/bl3/object.cpp | 9 +++--- src/unrealsdk/game/tps/hexedits.cpp | 2 +- src/unrealsdk/memory.cpp | 9 ++++++ src/unrealsdk/memory.h | 49 +++++++++++++++++++++-------- 14 files changed, 79 insertions(+), 42 deletions(-) diff --git a/src/unrealsdk/game/bl2/bl2.cpp b/src/unrealsdk/game/bl2/bl2.cpp index 99e4348..38a5afb 100644 --- a/src/unrealsdk/game/bl2/bl2.cpp +++ b/src/unrealsdk/game/bl2/bl2.cpp @@ -64,7 +64,7 @@ const constinit Pattern<23> FNAME_INIT_SIG{ } void BL2Hook::find_fname_init(void) { - this->fname_init_ptr = FNAME_INIT_SIG.sigscan(); + this->fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); LOG(MISC, "FName::Init: {:p}", this->fname_init_ptr); } @@ -96,7 +96,7 @@ const constinit Pattern<9> FFRAME_STEP_SIG{ } // namespace void BL2Hook::find_fframe_step(void) { - fframe_step_ptr = FFRAME_STEP_SIG.sigscan(); + fframe_step_ptr = FFRAME_STEP_SIG.sigscan_nullable(); LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_ptr)); } void BL2Hook::fframe_step(FFrame* frame, UObject* obj, void* param) const { diff --git a/src/unrealsdk/game/bl2/globals.cpp b/src/unrealsdk/game/bl2/globals.cpp index f490bb4..076f863 100644 --- a/src/unrealsdk/game/bl2/globals.cpp +++ b/src/unrealsdk/game/bl2/globals.cpp @@ -26,7 +26,7 @@ const constinit Pattern<17> GOBJECTS_SIG{ } // namespace void BL2Hook::find_gobjects(void) { - auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan()); + auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); gobjects_wrapper = GObjects(gobjects_ptr); @@ -49,7 +49,7 @@ const constinit Pattern<10> GNAMES_SIG{ } // namespace void BL2Hook::find_gnames(void) { - auto gnames_ptr = read_offset(GNAMES_SIG.sigscan()); + auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); gnames_wrapper = GNames(gnames_ptr); diff --git a/src/unrealsdk/game/bl2/hexedits.cpp b/src/unrealsdk/game/bl2/hexedits.cpp index 49e93b3..947970f 100644 --- a/src/unrealsdk/game/bl2/hexedits.cpp +++ b/src/unrealsdk/game/bl2/hexedits.cpp @@ -40,7 +40,7 @@ const constinit Pattern<15> ARRAY_LIMIT_MESSAGE{ } // namespace void BL2Hook::hexedit_set_command(void) { - auto set_command = SET_COMMAND_SIG.sigscan(); + auto set_command = SET_COMMAND_SIG.sigscan_nullable(); if (set_command == nullptr) { LOG(ERROR, "Couldn't find set command signature"); } else { @@ -55,7 +55,7 @@ void BL2Hook::hexedit_set_command(void) { } void BL2Hook::hexedit_array_limit(void) { - auto array_limit = ARRAY_LIMIT_SIG.sigscan(); + auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); if (array_limit == nullptr) { LOG(ERROR, "Couldn't find array limit signature"); } else { @@ -69,7 +69,7 @@ void BL2Hook::hexedit_array_limit(void) { } void BL2Hook::hexedit_array_limit_message(void) const { - auto array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan(); + auto array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); if (array_limit_msg == nullptr) { LOG(ERROR, "Couldn't find array limit message signature"); } else { diff --git a/src/unrealsdk/game/bl2/hooks.cpp b/src/unrealsdk/game/bl2/hooks.cpp index 9db191d..04ec8ca 100644 --- a/src/unrealsdk/game/bl2/hooks.cpp +++ b/src/unrealsdk/game/bl2/hooks.cpp @@ -140,7 +140,7 @@ bool locking(void) { } // namespace void BL2Hook::hook_process_event(void) { - detour(PROCESS_EVENT_SIG.sigscan(), locking() ? locking_process_event_hook : process_event_hook, + detour(PROCESS_EVENT_SIG, locking() ? locking_process_event_hook : process_event_hook, &process_event_ptr, "ProcessEvent"); } @@ -232,7 +232,7 @@ static_assert(std::is_same_v, } // namespace void BL2Hook::hook_call_function(void) { - detour(CALL_FUNCTION_SIG.sigscan(), call_function_hook, &call_function_ptr, "CallFunction"); + detour(CALL_FUNCTION_SIG, call_function_hook, &call_function_ptr, "CallFunction"); } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl2/memory.cpp b/src/unrealsdk/game/bl2/memory.cpp index 23becec..14c2540 100644 --- a/src/unrealsdk/game/bl2/memory.cpp +++ b/src/unrealsdk/game/bl2/memory.cpp @@ -43,7 +43,9 @@ const constinit Pattern<8> GMALLOC_PATTERN{ } // namespace void BL2Hook::find_gmalloc(void) { - gmalloc = *read_offset(GMALLOC_PATTERN.sigscan()); + // Using plain `sigscan` since there's an extra level of indirection here, want to make sure to + // print an error before we potentially dereference it + gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); } void* BL2Hook::u_malloc(size_t len) const { diff --git a/src/unrealsdk/game/bl2/object.cpp b/src/unrealsdk/game/bl2/object.cpp index 18ebf71..397975e 100644 --- a/src/unrealsdk/game/bl2/object.cpp +++ b/src/unrealsdk/game/bl2/object.cpp @@ -56,7 +56,7 @@ const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ } // namespace void BL2Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); } @@ -106,7 +106,7 @@ const constinit Pattern<15> GET_PATH_NAME_PATTERN{ }; void BL2Hook::find_get_path_name(void) { - get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan(); + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan_nullable(); LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); } @@ -156,7 +156,7 @@ const constinit Pattern<56> STATIC_FIND_OBJECT_PATTERN{ } // namespace void BL2Hook::find_static_find_object(void) { - static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan(); + static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); } @@ -196,7 +196,7 @@ const constinit Pattern<46> LOAD_PACKAGE_PATTERN{ } // namespace void BL2Hook::find_load_package(void) { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan(); + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); } diff --git a/src/unrealsdk/game/bl3/bl3.cpp b/src/unrealsdk/game/bl3/bl3.cpp index 3efd0b1..52615fa 100644 --- a/src/unrealsdk/game/bl3/bl3.cpp +++ b/src/unrealsdk/game/bl3/bl3.cpp @@ -57,7 +57,7 @@ const constinit Pattern<15> FNAME_INIT_PATTERN{ } // namespace void BL3Hook::find_fname_init(void) { - fname_init_ptr = FNAME_INIT_PATTERN.sigscan(); + fname_init_ptr = FNAME_INIT_PATTERN.sigscan_nullable(); LOG(MISC, "FName::Init: {:p}", reinterpret_cast(fname_init_ptr)); } @@ -83,7 +83,7 @@ const constinit Pattern<10> FFRAME_STEP_SIG{ } // namespace void BL3Hook::find_fframe_step(void) { - fframe_step_ptr = FFRAME_STEP_SIG.sigscan(); + fframe_step_ptr = FFRAME_STEP_SIG.sigscan_nullable(); LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_ptr)); } void BL3Hook::fframe_step(FFrame* frame, UObject* obj, void* param) const { @@ -145,7 +145,7 @@ const constinit Pattern<131> FTEXT_AS_CULTURE_INVARIANT_PATTERN{ void BL3Hook::find_ftext_as_culture_invariant(void) { ftext_as_culture_invariant_ptr = - FTEXT_AS_CULTURE_INVARIANT_PATTERN.sigscan(); + FTEXT_AS_CULTURE_INVARIANT_PATTERN.sigscan_nullable(); LOG(MISC, "FText::AsCultureInvariant: {:p}", reinterpret_cast(fname_init_ptr)); } diff --git a/src/unrealsdk/game/bl3/globals.cpp b/src/unrealsdk/game/bl3/globals.cpp index b716e5d..4491a73 100644 --- a/src/unrealsdk/game/bl3/globals.cpp +++ b/src/unrealsdk/game/bl3/globals.cpp @@ -26,7 +26,7 @@ const constinit Pattern<26> GOBJECTS_SIG{ } // namespace void BL3Hook::find_gobjects(void) { - auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan()); + auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); gobjects_wrapper = GObjects(gobjects_ptr); @@ -53,7 +53,9 @@ const constinit Pattern<27> GNAMES_SIG{ } // namespace void BL3Hook::find_gnames(void) { - auto gnames_ptr = *read_offset(GNAMES_SIG.sigscan()); + // Using plain `sigscan` since there's an extra level of indirection here, want to make sure to + // print an error before we potentially dereference it + auto gnames_ptr = *read_offset(GNAMES_SIG.sigscan("GNames")); LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); gnames_wrapper = GNames(gnames_ptr); diff --git a/src/unrealsdk/game/bl3/hooks.cpp b/src/unrealsdk/game/bl3/hooks.cpp index 083608f..b30709f 100644 --- a/src/unrealsdk/game/bl3/hooks.cpp +++ b/src/unrealsdk/game/bl3/hooks.cpp @@ -105,7 +105,7 @@ bool locking(void) { } void BL3Hook::hook_process_event(void) { - detour(PROCESS_EVENT_SIG.sigscan(), locking() ? locking_process_event_hook : process_event_hook, + detour(PROCESS_EVENT_SIG, locking() ? locking_process_event_hook : process_event_hook, &process_event_ptr, "ProcessEvent"); } @@ -213,7 +213,7 @@ static_assert(std::is_same_v, "call_function signature is incorrect"); void BL3Hook::hook_call_function(void) { - detour(CALL_FUNCTION_SIG.sigscan(), call_function_hook, &call_function_ptr, "CallFunction"); + detour(CALL_FUNCTION_SIG, call_function_hook, &call_function_ptr, "CallFunction"); } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl3/memory.cpp b/src/unrealsdk/game/bl3/memory.cpp index 207d8f4..6b7f1a0 100644 --- a/src/unrealsdk/game/bl3/memory.cpp +++ b/src/unrealsdk/game/bl3/memory.cpp @@ -53,9 +53,9 @@ const constinit Pattern<20> FREE_PATTERN{ } // namespace void BL3Hook::find_gmalloc(void) { - fmemory_malloc_ptr = MALLOC_PATTERN.sigscan(); - fmemory_realloc_ptr = REALLOC_PATTERN.sigscan(); - fmemory_free_ptr = FREE_PATTERN.sigscan(); + fmemory_malloc_ptr = MALLOC_PATTERN.sigscan_nullable(); + fmemory_realloc_ptr = REALLOC_PATTERN.sigscan_nullable(); + fmemory_free_ptr = FREE_PATTERN.sigscan_nullable(); LOG(MISC, "FMemory::Malloc: {:p}", reinterpret_cast(fmemory_malloc_ptr)); LOG(MISC, "FMemory::Realloc: {:p}", reinterpret_cast(fmemory_realloc_ptr)); diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index eca6a0a..30348f9 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -48,7 +48,7 @@ const constinit Pattern<55> CONSTRUCT_OBJECT_PATTERN{ } // namespace void BL3Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); } @@ -84,7 +84,7 @@ const constinit Pattern<21> GET_PATH_NAME_PATTERN{ } // namespace void BL3Hook::find_get_path_name(void) { - get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan(); + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan_nullable(); LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); } @@ -120,7 +120,8 @@ const constexpr intptr_t ANY_PACKAGE = -1; } // namespace void BL3Hook::find_static_find_object(void) { - static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan(); + static_find_object_ptr = + STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); LOG(MISC, "StaticFindObjectSafe: {:p}", reinterpret_cast(static_find_object_ptr)); } @@ -152,7 +153,7 @@ const constinit Pattern<16> LOAD_PACKAGE_PATTERN{ } // namespace void BL3Hook::find_load_package(void) { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan(); + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); } diff --git a/src/unrealsdk/game/tps/hexedits.cpp b/src/unrealsdk/game/tps/hexedits.cpp index 44ec421..c8f658f 100644 --- a/src/unrealsdk/game/tps/hexedits.cpp +++ b/src/unrealsdk/game/tps/hexedits.cpp @@ -20,7 +20,7 @@ const constinit Pattern<11> ARRAY_LIMIT_MESSAGE{ } // namespace void TPSHook::hexedit_array_limit_message(void) const { - auto array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan(); + auto array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); if (array_limit_msg == nullptr) { LOG(ERROR, "Couldn't find array limit message signature"); } else { diff --git a/src/unrealsdk/memory.cpp b/src/unrealsdk/memory.cpp index 9f05fa2..38493fb 100644 --- a/src/unrealsdk/memory.cpp +++ b/src/unrealsdk/memory.cpp @@ -84,6 +84,11 @@ bool detour(uintptr_t addr, void* detour_func, void** original_func, std::string } #else bool detour(uintptr_t addr, void* detour_func, void** original_func, std::string_view name) { + if (addr == 0) { + LOG(ERROR, "Detour for {} was passed a null address!", name); + return false; + } + MH_STATUS status = MH_OK; status = MH_CreateHook(reinterpret_cast(addr), detour_func, original_func); @@ -114,6 +119,10 @@ UNREALSDK_CAPI(bool, #endif uintptr_t read_offset(uintptr_t address) { + if (address == 0) { + LOG(ERROR, "Attempted to read a null offset!"); + return 0; + } #ifdef ARCH_X64 return address + *reinterpret_cast(address) + 4; #else diff --git a/src/unrealsdk/memory.h b/src/unrealsdk/memory.h index c4ddb14..2dbac55 100644 --- a/src/unrealsdk/memory.h +++ b/src/unrealsdk/memory.h @@ -5,6 +5,9 @@ namespace unrealsdk::memory { +template +struct Pattern; + /** * @brief Performs a sigscan. * @@ -39,7 +42,9 @@ T sigscan(const uint8_t* bytes, * @brief Detours a function. * * @tparam T The signature of the detour'd function (should be picked up automatically). + * @tparam n The size of the sigscan pattern (should be picked up automatically). * @param addr The address of the function. + * @param pattern A sigscan pattern matching the function to detour. * @param detour_func The detour function. * @param original_func Pointer to store the original function. * @param name Name of the detour, to be used in log messages on error. @@ -51,6 +56,19 @@ bool detour(uintptr_t addr, T detour_func, T* original_func, std::string_view na return detour(addr, reinterpret_cast(detour_func), reinterpret_cast(original_func), name); } +template +bool detour(const Pattern& pattern, + void* detour_func, + void** original_func, + std::string_view name) { + // Use sigscan nullable since the base detour function will warn about a null address + return detour(pattern.sigscan_nullable(), detour_func, original_func, name); +} +template +bool detour(const Pattern& pattern, T detour_func, T* original_func, std::string_view name) { + return detour(pattern.sigscan_nullable(), reinterpret_cast(detour_func), + reinterpret_cast(original_func), name); +} /** * @brief Reads an assembly offset, and gets the address it points to. @@ -193,28 +211,33 @@ struct Pattern { } /** - * @brief Performs a sigscan for this pattern. + * @brief Performs a sigscan for this pattern across the main executable. + * @note When not found, `sigscan` throws, while `sigscan_nullable` returns 0. * * @tparam T The type to cast the result to. - * @param start The address to start the search at. Defaults to the start of the exe. - * @param size The length of the region to search. Defaults to the exe size - * @return The found location, or nullptr. + * @param name The name of this pattern, to use in error messages. + * @return The found location, or 0. */ - [[nodiscard]] uintptr_t sigscan(void) const { + [[nodiscard]] uintptr_t sigscan(std::string_view name) const { auto addr = memory::sigscan(this->bytes.data(), this->mask.data(), n); - return addr == 0 ? 0 : addr + offset; - } - [[nodiscard]] uintptr_t sigscan(uintptr_t start, size_t size) const { - auto addr = memory::sigscan(this->bytes.data(), this->mask.data(), n, start, size); + if (addr == 0) { + // Make sure to log something on error, even if calling code doesn't catch it + LOG(ERROR, "Sigscan for {} failed!", name); + throw std::runtime_error("sigscan failed"); + } return addr == 0 ? 0 : addr + offset; } template - [[nodiscard]] T sigscan(void) const { - return reinterpret_cast(this->sigscan()); + [[nodiscard]] T sigscan(std::string_view name) const { + return reinterpret_cast(this->sigscan(name)); + } + [[nodiscard]] uintptr_t sigscan_nullable(void) const { + auto addr = memory::sigscan(this->bytes.data(), this->mask.data(), n); + return addr == 0 ? 0 : addr + offset; } template - [[nodiscard]] T sigscan(uintptr_t start, size_t size) const { - return reinterpret_cast(this->sigscan(start, size)); + [[nodiscard]] T sigscan_nullable(void) const { + return reinterpret_cast(this->sigscan_nullable()); } }; From 16c9d2d28b488b4ec0abf641dc7d3e40c303d15f Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 10 Aug 2024 14:57:37 +1200 Subject: [PATCH 3/4] update libraries --- common_cmake | 2 +- libs/fmt | 2 +- libs/minhook | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common_cmake b/common_cmake index c953298..e1d46e3 160000 --- a/common_cmake +++ b/common_cmake @@ -1 +1 @@ -Subproject commit c95329887138e361a46b1129b640c77f00df7086 +Subproject commit e1d46e3bc526f5a53ca08fb5592f3b7fb6f37349 diff --git a/libs/fmt b/libs/fmt index f5e5435..0c9fce2 160000 --- a/libs/fmt +++ b/libs/fmt @@ -1 +1 @@ -Subproject commit f5e54359df4c26b6230fc61d38aa294581393084 +Subproject commit 0c9fce2ffefecfdce794e1859584e25877b7b592 diff --git a/libs/minhook b/libs/minhook index 0f25a24..c1a7c38 160000 --- a/libs/minhook +++ b/libs/minhook @@ -1 +1 @@ -Subproject commit 0f25a2449b3cf878bcbdbf91b693c38149ecf029 +Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339 From 979b2fcca73c935b271a64bcd8a46c335850cc0e Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 10 Aug 2024 15:13:54 +1200 Subject: [PATCH 4/4] update changelog --- changelog.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2183c23..9b23880 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v1.2.0 (Upcoming) +## v1.2.0 - When an exception occurs during a hook, now mention what function it was under, to make debugging easier. @@ -59,6 +59,13 @@ [91e3fcd5](https://github.com/bl-sdk/unrealsdk/commit/91e3fcd5), [8ec285fc](https://github.com/bl-sdk/unrealsdk/commit/8ec285fc) +- Tweaked `Pattern::sigscan` to more explicitly deal with sigscans failing. Existing code will have + to be updated to either call `sigscan_nullable` (if failing is ok), or to pass a name to use in + case of error (if throwing is ok). + + [7135bdf3](https://github.com/bl-sdk/unrealsdk/commit/7135bdf3), + [b849b0e8](https://github.com/bl-sdk/unrealsdk/commit/91e3fcd5) + - Made the `UnrealPointer` constructor explicit. [26a47713](https://github.com/bl-sdk/unrealsdk/commit/26a47713)