├── .gitignore ├── .clangd ├── .clang-format ├── Example ├── Target │ └── Target.cpp ├── CMakeLists.txt └── Source │ └── Main.cpp ├── README.md ├── Include └── BCRL │ ├── detail │ ├── ConditionalField.hpp │ └── LambdaInserter.hpp │ ├── FlagSpecification.hpp │ ├── SearchConstraints.hpp │ ├── SafePointer.hpp │ └── Session.hpp ├── .github └── workflows │ └── cmake.yml ├── LICENSE ├── CMakeLists.txt └── .clang-tidy /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .cache/ 3 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Add: [ "-Wall", "-Wextra", "-Wpedantic" ] 3 | Diagnostics: 4 | ClangTidy: 5 | FastCheckFilter: None 6 | UnusedIncludes: Strict 7 | MissingIncludes: Strict 8 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | NamespaceIndentation: All 4 | SortIncludes: CaseInsensitive 5 | TabWidth: 4 6 | UseTab: Always 7 | PointerAlignment: Left 8 | ReferenceAlignment: Left 9 | DerivePointerAlignment: false 10 | AlignEscapedNewlines: Right 11 | QualifierAlignment: Left 12 | SpaceBeforeCpp11BracedList: false 13 | -------------------------------------------------------------------------------- /Example/Target/Target.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | 5 | void another_secret_method(); 6 | 7 | void super_secret_method() 8 | { 9 | puts("You will never find me!"); 10 | another_secret_method(); 11 | } 12 | 13 | void another_secret_method() 14 | { 15 | puts("I really really really really really love Linux!"); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ExampleTarget SHARED "Target/Target.cpp") 2 | 3 | add_executable(BCRLExample "Source/Main.cpp") 4 | add_subdirectory("${MemoryManager_SOURCE_DIR}/Modules/Linux" "LinuxMemoryManager") 5 | target_link_libraries(BCRLExample PRIVATE ExampleTarget BCRL LinuxMemoryManager) 6 | target_compile_features(BCRLExample PRIVATE cxx_std_23) 7 | add_test(NAME TestBCRLExample COMMAND $) 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **B**inary **C**ode **R**ecognition **L**ibrary 2 | 3 | *Still Stuck in the Stone Age? Upgrade to Our Next-Level Code Scanner! Say Goodbye to Outdated Signature Scanners and Embrace the Power of String and Xref Hunting. Leave the Competition in the Dust!* - A wise LLM once said 4 | 5 | ## Features 6 | - Search Strings 7 | - Analyse XREFs 8 | - Find signatures 9 | - Builder-like syntax 10 | - Simultaneously handle multiple pointers 11 | - Extendable API, which does not omit security features 12 | - Feature-rich, yet lightweight 13 | - Fast 14 | -------------------------------------------------------------------------------- /Include/BCRL/detail/ConditionalField.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_DETAIL_CONDITIONALFIELD_HPP 2 | #define BCRL_DETAIL_CONDITIONALFIELD_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace BCRL::detail { 8 | template 9 | using ConditionalField = std::conditional_t; 10 | 11 | template 12 | auto conditional_init(auto... pack) 13 | { 14 | if constexpr (std::is_same_v) 15 | return std::monostate{}; 16 | else 17 | return T(pack...); 18 | } 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | BUILD_TYPE: Debug 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-24.04 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Configure CMake 16 | run: CC=gcc-14 CXX=g++-14 cmake -B ${{github.workspace}}/Build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 17 | 18 | - name: Build 19 | run: cmake --build ${{github.workspace}}/Build --config ${{env.BUILD_TYPE}} 20 | 21 | - name: Test 22 | working-directory: ${{github.workspace}}/Build/Example 23 | run: ctest -C ${{env.BUILD_TYPE}} 24 | -------------------------------------------------------------------------------- /Include/BCRL/detail/LambdaInserter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_LAMBDAINSERTER_HPP 2 | #define BCRL_LAMBDAINSERTER_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace BCRL::detail { 8 | template 9 | class LambdaInserter { 10 | F callback; 11 | 12 | public: 13 | // NOLINTNEXTLINE(readability-identifier-naming) 14 | using difference_type = std::ptrdiff_t; 15 | 16 | constexpr explicit LambdaInserter(F&& callback) 17 | : callback(std::move(callback)) 18 | { 19 | } 20 | 21 | constexpr LambdaInserter& operator=(auto&& element) 22 | { 23 | callback(element); 24 | return *this; 25 | } 26 | 27 | constexpr LambdaInserter& operator*() 28 | { 29 | return *this; 30 | } 31 | 32 | constexpr LambdaInserter& operator++() 33 | { 34 | return *this; 35 | } 36 | 37 | constexpr LambdaInserter& operator++(int) 38 | { 39 | return *this; 40 | } 41 | }; 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Johannes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | include_guard() 4 | 5 | project(BCRL) 6 | 7 | add_library(BCRL INTERFACE) 8 | target_include_directories(BCRL INTERFACE "${PROJECT_SOURCE_DIR}/Include") 9 | target_compile_features(BCRL INTERFACE cxx_std_23) 10 | 11 | include(FetchContent) 12 | 13 | if(NOT TARGET SignatureScanner) 14 | FetchContent_Declare( 15 | SignatureScanner 16 | GIT_REPOSITORY https://github.com/Sumandora/SignatureScanner 17 | GIT_PROGRESS TRUE 18 | GIT_TAG 3.3.1) 19 | FetchContent_MakeAvailable(SignatureScanner) 20 | endif() 21 | target_link_libraries(BCRL INTERFACE SignatureScanner) 22 | 23 | if(NOT TARGET LengthDisassembler) 24 | FetchContent_Declare( 25 | LengthDisassembler 26 | GIT_REPOSITORY https://github.com/Sumandora/LengthDisassembler 27 | GIT_PROGRESS TRUE 28 | GIT_TAG 1.0.1) 29 | FetchContent_MakeAvailable(LengthDisassembler) 30 | endif() 31 | target_link_libraries(BCRL INTERFACE LengthDisassembler) 32 | 33 | if(NOT TARGET MemoryManager) 34 | FetchContent_Declare( 35 | MemoryManager 36 | GIT_REPOSITORY https://github.com/Sumandora/MemoryManager 37 | GIT_PROGRESS TRUE 38 | GIT_TAG 2.6.1) 39 | FetchContent_MakeAvailable(MemoryManager) 40 | endif() 41 | target_link_libraries(BCRL INTERFACE MemoryManager) 42 | 43 | if(PROJECT_IS_TOP_LEVEL) 44 | enable_testing() 45 | add_subdirectory("Example") 46 | endif() 47 | -------------------------------------------------------------------------------- /Include/BCRL/FlagSpecification.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_FLAGSPECIFICATION_HPP 2 | #define BCRL_FLAGSPECIFICATION_HPP 3 | 4 | #include "MemoryManager/MemoryManager.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace BCRL { 10 | struct FlagSpecification { 11 | std::optional readable; 12 | std::optional writable; 13 | std::optional executable; 14 | 15 | private: 16 | template 17 | constexpr static std::optional parse(char c) 18 | { 19 | switch (c) { 20 | case '-': 21 | return false; 22 | case Default: 23 | return true; 24 | case '*': 25 | return std::nullopt; 26 | default: 27 | std::unreachable(); 28 | } 29 | } 30 | 31 | constexpr static bool matches(const std::optional& op, bool state) 32 | { 33 | return !op.has_value() || op.value() == state; 34 | } 35 | 36 | public: 37 | /** 38 | * 'r/w/x' -> Enabled 39 | * '-' -> Disabled 40 | * '*' -> Ignored 41 | * 42 | * Examples: 43 | * r*x specifies a region which is readable and executable, but may or may not be writable 44 | * rwx specifies a region which is readable, writable and executable 45 | * **x specifies a region which is definitely executable, but the rest is ignored 46 | * r-x specifies a region which is readable and executable, but not writable 47 | * r-- specifies a region which is read-only, meaning readable, but not executable/writable 48 | */ 49 | constexpr FlagSpecification(const char rwx[3]) // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) 50 | : readable(parse<'r'>(rwx[0])) 51 | , writable(parse<'w'>(rwx[1])) 52 | , executable(parse<'x'>(rwx[2])) 53 | { 54 | } 55 | 56 | [[nodiscard]] bool matches_readable(bool readable) const 57 | { 58 | return matches(this->readable, readable); 59 | } 60 | 61 | [[nodiscard]] bool matches_writable(bool writable) const 62 | { 63 | return matches(this->writable, writable); 64 | } 65 | [[nodiscard]] bool matches_executable(bool executable) const 66 | { 67 | return matches(this->executable, executable); 68 | } 69 | 70 | bool operator==(MemoryManager::Flags flags) const 71 | { 72 | return matches_readable(flags.is_readable()) && matches_writable(flags.is_writeable()) && matches_executable(flags.is_executable()); 73 | } 74 | }; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /Example/Source/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "BCRL/SearchConstraints.hpp" 2 | #include "BCRL/Session.hpp" 3 | 4 | #include "MemoryManager/LinuxMemoryManager.hpp" 5 | #include "SignatureScanner/PatternSignature.hpp" 6 | #include "SignatureScanner/XRefSignature.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int main() 15 | { 16 | void* handle = dlopen("libExampleTarget.so", RTLD_NOW); // Force load 17 | link_map* lmap = nullptr; 18 | // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) 19 | dlinfo(handle, RTLD_DI_LINKMAP, &lmap); 20 | std::println("Loaded at 0x{:x}", lmap->l_addr); 21 | 22 | using namespace BCRL; 23 | MemoryManager::LinuxMemoryManager local_memory_manager; 24 | local_memory_manager.sync_layout(); 25 | 26 | /** 27 | * super_secret_method: 28 | * f3 0f 1e fa endbr64 29 | * 55 push %rbp 30 | * 48 89 e5 mov %rsp,%rbp 31 | * 48 8d 05 88 0e 00 00 lea 0xe88(%rip),%rax # "You will never find me!" 32 | * 48 89 c7 mov %rax,%rdi 33 | * e8 f0 fe ff ff call 1070 34 | * e8 db fe ff ff call 1060 35 | * 90 nop 36 | * 5d pop %rbp 37 | * c3 ret 38 | */ 39 | 40 | auto another_secret_method = BCRL::signature(local_memory_manager, SignatureScanner::PatternSignature::for_literal_string<"You will never find me!">()) 41 | .find_xrefs(SignatureScanner::XRefTypes::relative_and_absolute(), BCRL::everything(local_memory_manager).thats_readable().with_name("libExampleTarget.so")) // This lands us in super_secret_method at the offset of the lea instruction ("48 8d 05 HERE-> 88 0e 00 00") 42 | .add(4) // Skip the offset 43 | .repeat_while([](auto& s) { 44 | const bool has_call = s.does_match(SignatureScanner::PatternSignature::for_array_of_bytes<"e8">()); 45 | if (!has_call) 46 | s.next_instruction(); 47 | return !has_call; 48 | }) // Find the two call instructions by stepping through the function 49 | .next_instruction() // This is puts, not another_secret_method 50 | .filter([](const auto& ptr) { return ptr.does_match(SignatureScanner::PatternSignature::for_array_of_bytes<"e8">()); }) // Verify that there's another call instruction here 51 | .add(1) // Skip the opcode 52 | .relative_to_absolute() // Jump to the target of the relative offset 53 | .filter(BCRL::everything(local_memory_manager).with_flags("r-x").with_name("libExampleTarget.so")) 54 | .for_each([](const auto& ptr) { std::println("another_secret_method: 0x{:x}", ptr.get_pointer()); }) 55 | .expect("Couldn't find another_secret_method", "Found too many solutions."); 56 | 57 | auto strings = BCRL::signature(local_memory_manager, SignatureScanner::PatternSignature::for_literal_string<"I really really really really really love Linux!">()) 58 | .filter(BCRL::everything(local_memory_manager).thats_readable().with_name("libExampleTarget.so")) 59 | .peek(); 60 | 61 | assert(!strings.empty()); 62 | for (auto string : strings) { 63 | const char* interjection = "I'd just like to interject for moment."; 64 | local_memory_manager.write(string.get_pointer(), interjection, strlen(interjection) + 1 /*null terminator*/); // Get Stallman'd 65 | } 66 | 67 | another_secret_method(); // Invoke 'another_secret_method' without linking against it, but its string has been overwritten, so the GNU Linux interjection appears 68 | } 69 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: [ 2 | # enabled checks: 3 | 'bugprone*', 'clang-analyzer*', 'cert*', 4 | 'cppcore*', 'misc*', 'performance*', 5 | 'modernize*', 'google*', 'hicpp*', 6 | 'llvm*', 'readability*', 7 | 8 | # disabled checks: 9 | '-*-avoid-c-arrays', 10 | '-*-braces-around-statements', 11 | '-bugprone-easily-swappable-parameters', 12 | '-bugprone-empty-catch', 13 | '-cppcoreguidelines-avoid-const-or-ref-data-members', 14 | '-cppcoreguidelines-avoid-magic-numbers', 15 | '-cppcoreguidelines-avoid-non-const-global-variables', 16 | '-cppcoreguidelines-owning-memory', 17 | '-cppcoreguidelines-pro-bounds-array-to-pointer-decay', 18 | '-cppcoreguidelines-pro-bounds-constant-array-index', 19 | '-cppcoreguidelines-pro-bounds-pointer-arithmetic', 20 | '-cppcoreguidelines-pro-type-reinterpret-cast', 21 | '-cppcoreguidelines-pro-type-static-cast-downcast', 22 | '-cppcoreguidelines-pro-type-union-access', 23 | '-cppcoreguidelines-pro-type-vararg', 24 | '-google-build-using-namespace', 25 | '-google-readability-todo', 26 | '-hicpp-no-array-decay', 27 | '-hicpp-signed-bitwise', 28 | '-hicpp-vararg', 29 | '-llvmlibc*', 30 | '-misc-non-private-member-variables-in-classes', 31 | '-misc-use-anonymous-namespace', 32 | '-modernize-use-trailing-return-type', 33 | '-*-namespace-comment*', 34 | '-*-objc-*', 35 | '-performance-no-int-to-ptr', 36 | '-readability-container-data-pointer', 37 | '-readability-function-cognitive-complexity', 38 | '-readability-identifier-length', 39 | '-readability-implicit-bool-conversion', 40 | '-readability-magic-numbers', 41 | '-readability-math-missing-parentheses', 42 | '-readability-named-parameter', 43 | '-*-special-member-functions'] 44 | 45 | CheckOptions: 46 | # classes 47 | - key: readability-identifier-naming.AbstractClassCase 48 | value: CamelCase 49 | - key: readability-identifier-naming.ClassCase 50 | value: CamelCase 51 | - key: readability-identifier-naming.ClassConstantCase 52 | value: UPPER_CASE 53 | - key: readability-identifier-naming.ClassMemberCase 54 | value: lower_case 55 | - key: readability-identifier-naming.ClassMethodCase 56 | value: lower_case 57 | - key: readability-identifier-naming.ConceptCase 58 | value: CamelCase 59 | - key: readability-identifier-naming.ConstantCase 60 | value: UPPER_CASE 61 | - key: readability-identifier-naming.ConstantMemberCase 62 | value: lower_case 63 | - key: readability-identifier-naming.ConstantParameterCase 64 | value: lower_case 65 | - key: readability-identifier-naming.ConstantPointerParameterCase 66 | value: lower_case 67 | - key: readability-identifier-naming.ConstexprFunctionCase 68 | value: lower_case 69 | - key: readability-identifier-naming.ConstexprMethodCase 70 | value: lower_case 71 | - key: readability-identifier-naming.ConstexprVariableCase 72 | value: UPPER_CASE 73 | - key: readability-identifier-naming.EnumCase 74 | value: CamelCase 75 | - key: readability-identifier-naming.EnumConstantCase 76 | value: UPPER_CASE 77 | - key: readability-identifier-naming.FunctionCase 78 | value: lower_case 79 | - key: readability-identifier-naming.GlobalConstantCase 80 | value: UPPER_CASE 81 | - key: readability-identifier-naming.GlobalConstantPointerCase 82 | value: UPPER_CASE 83 | - key: readability-identifier-naming.GlobalFunctionCase 84 | value: lower_case 85 | - key: readability-identifier-naming.GlobalPointerCase 86 | value: lower_case 87 | - key: readability-identifier-naming.GlobalVariableCase 88 | value: lower_case 89 | - key: readability-identifier-naming.InlineNamespaceCase 90 | value: CamelCase 91 | - key: readability-identifier-naming.LocalConstantCase 92 | value: lower_case 93 | - key: readability-identifier-naming.LocalConstantPointerCase 94 | value: lower_case 95 | - key: readability-identifier-naming.LocalPointerCase 96 | value: lower_case 97 | - key: readability-identifier-naming.LocalVariableCase 98 | value: lower_case 99 | - key: readability-identifier-naming.MacroDefinitionCase 100 | value: UPPER_CASE 101 | - key: readability-identifier-naming.MemberCase 102 | value: lower_case 103 | - key: readability-identifier-naming.MethodCase 104 | value: lower_case 105 | - key: readability-identifier-naming.NamespaceCase 106 | value: CamelCase 107 | - key: readability-identifier-naming.NamespaceIgnoredRegexp 108 | value: detail 109 | - key: readability-identifier-naming.ParameterCase 110 | value: lower_case 111 | - key: readability-identifier-naming.ParameterPackCase 112 | value: lower_case 113 | - key: readability-identifier-naming.PointerParameterCase 114 | value: lower_case 115 | - key: readability-identifier-naming.PrivateMemberCase 116 | value: lower_case 117 | - key: readability-identifier-naming.PrivateMethodCase 118 | value: lower_case 119 | - key: readability-identifier-naming.ProtectedMemberCase 120 | value: lower_case 121 | - key: readability-identifier-naming.ProtectedMethodCase 122 | value: lower_case 123 | - key: readability-identifier-naming.PublicMemberCase 124 | value: lower_case 125 | - key: readability-identifier-naming.PublicMethodCase 126 | value: lower_case 127 | - key: readability-identifier-naming.ScopedEnumConstantCase 128 | value: UPPER_CASE 129 | - key: readability-identifier-naming.StaticConstantCase 130 | value: UPPER_CASE 131 | - key: readability-identifier-naming.StaticVariableCase 132 | value: lower_case 133 | - key: readability-identifier-naming.StructCase 134 | value: CamelCase 135 | - key: readability-identifier-naming.TemplateParameterCase 136 | value: CamelCase 137 | - key: readability-identifier-naming.TemplateTemplateParameterCase 138 | value: CamelCase 139 | - key: readability-identifier-naming.TypeAliasCase 140 | value: CamelCase 141 | - key: readability-identifier-naming.TypedefCase 142 | value: CamelCase 143 | - key: readability-identifier-naming.TypeTemplateParameterCase 144 | value: CamelCase 145 | - key: readability-identifier-naming.UnionCase 146 | value: CamelCase 147 | - key: readability-identifier-naming.ValueTemplateParameterCase 148 | value: CamelCase 149 | - key: readability-identifier-naming.VariableCase 150 | value: lower_case 151 | - key: readability-identifier-naming.VirtualMethodCase 152 | value: lower_case 153 | -------------------------------------------------------------------------------- /Include/BCRL/SearchConstraints.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_SEARCHCONSTRAINTS_HPP 2 | #define BCRL_SEARCHCONSTRAINTS_HPP 3 | 4 | #include "detail/ConditionalField.hpp" 5 | 6 | #include "FlagSpecification.hpp" 7 | 8 | #include "MemoryManager/MemoryManager.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace BCRL { 20 | template 21 | requires MemoryManager::MemoryRegion 22 | using MapPredicate = std::function; 23 | 24 | template 25 | requires MemoryManager::MemoryRegion 26 | class SearchConstraints { 27 | std::vector> predicates; 28 | // Remove what's not needed 29 | [[no_unique_address]] detail::ConditionalField< 30 | MemoryManager::AddressAware && MemoryManager::LengthAware, 31 | std::pair> 32 | address_range; 33 | [[no_unique_address]] detail::ConditionalField, FlagSpecification> flags; 34 | [[no_unique_address]] detail::ConditionalField, std::optional> shared; 35 | 36 | public: 37 | SearchConstraints() 38 | : predicates() 39 | , address_range(detail::conditional_init( 40 | std::numeric_limits::min(), std::numeric_limits::max())) 41 | , flags(detail::conditional_init("***")) 42 | , shared(detail::conditional_init(std::nullopt)) 43 | { 44 | } 45 | 46 | SearchConstraints( 47 | decltype(predicates)&& predicates, 48 | decltype(address_range)&& address_range, 49 | decltype(flags) flags) 50 | : predicates(std::move(predicates)) 51 | , address_range(std::move(address_range)) 52 | , flags(flags) 53 | { 54 | } 55 | 56 | SearchConstraints& with_name(std::string_view name) 57 | requires MemoryManager::NameAware 58 | { 59 | predicates.push_back([name](const Region& r) { 60 | return r.get_name() == name; 61 | }); 62 | 63 | return *this; 64 | } 65 | 66 | SearchConstraints& with_path(std::string_view path) 67 | requires MemoryManager::PathAware 68 | { 69 | predicates.push_back([path](const Region& r) { 70 | return r.get_path() == path; 71 | }); 72 | 73 | return *this; 74 | } 75 | 76 | SearchConstraints& from(std::uintptr_t address) 77 | requires MemoryManager::AddressAware && MemoryManager::LengthAware 78 | { 79 | address_range.first = address; 80 | address_range.second = std::max(address_range.first, address_range.second); 81 | 82 | return *this; 83 | } 84 | 85 | SearchConstraints& to(std::uintptr_t address) 86 | requires MemoryManager::AddressAware && MemoryManager::LengthAware 87 | { 88 | address_range.second = address; 89 | address_range.first = std::min(address_range.first, address_range.second); 90 | 91 | return *this; 92 | } 93 | 94 | SearchConstraints& with_flags(FlagSpecification specification) 95 | requires MemoryManager::FlagAware 96 | { 97 | flags = specification; 98 | 99 | return *this; 100 | } 101 | 102 | SearchConstraints& thats_readable() 103 | requires MemoryManager::FlagAware 104 | { 105 | flags.readable = true; 106 | 107 | return *this; 108 | } 109 | 110 | SearchConstraints& thats_not_readable() 111 | requires MemoryManager::FlagAware 112 | { 113 | flags.readable = false; 114 | 115 | return *this; 116 | } 117 | 118 | SearchConstraints& thats_writable() 119 | requires MemoryManager::FlagAware 120 | { 121 | flags.writable = true; 122 | 123 | return *this; 124 | } 125 | 126 | SearchConstraints& thats_not_writable() 127 | requires MemoryManager::FlagAware 128 | { 129 | flags.writable = false; 130 | 131 | return *this; 132 | } 133 | 134 | SearchConstraints& thats_executable() 135 | requires MemoryManager::FlagAware 136 | { 137 | flags.executable = true; 138 | 139 | return *this; 140 | } 141 | 142 | SearchConstraints& thats_not_executable() 143 | requires MemoryManager::FlagAware 144 | { 145 | flags.executable = false; 146 | 147 | return *this; 148 | } 149 | 150 | SearchConstraints& thats_shared() 151 | requires MemoryManager::SharedAware 152 | { 153 | shared = true; 154 | 155 | return *this; 156 | } 157 | 158 | SearchConstraints& thats_private() 159 | requires MemoryManager::SharedAware 160 | { 161 | shared = false; 162 | 163 | return *this; 164 | } 165 | 166 | SearchConstraints& also(MapPredicate&& predicate) 167 | { 168 | predicates.emplace_back(std::move(predicate)); 169 | 170 | return *this; 171 | } 172 | 173 | // Past-initialization usage 174 | [[nodiscard]] bool allows_address(std::uintptr_t address) const 175 | requires MemoryManager::AddressAware && MemoryManager::LengthAware 176 | { 177 | return address >= address_range.first && address < address_range.second; 178 | } 179 | 180 | bool allows_region(const Region& region) const 181 | { 182 | for (MapPredicate predicate : predicates) { 183 | if (!predicate(region)) 184 | return false; 185 | } 186 | 187 | if constexpr (MemoryManager::AddressAware && MemoryManager::LengthAware) 188 | if (address_range.first > region.get_address() + region.get_length() || address_range.second < region.get_address()) 189 | return false; 190 | 191 | if constexpr (MemoryManager::FlagAware) 192 | if (region.get_flags() != flags) 193 | return false; 194 | 195 | if constexpr (MemoryManager::SharedAware) 196 | if (shared.has_value() && region.is_shared() != shared.value()) 197 | return false; 198 | 199 | return true; 200 | } 201 | 202 | void clamp_to_address_range(const Region& r, const auto& actual_begin, auto& begin, auto& end) const // TODO improve parameters 203 | requires MemoryManager::AddressAware && MemoryManager::LengthAware 204 | { 205 | std::uintptr_t pointer_begin = r.get_address() + std::distance(actual_begin, begin); 206 | std::uintptr_t pointer_end = r.get_address() + std::distance(actual_begin, end); 207 | if (pointer_begin < address_range.first) 208 | std::advance(begin, address_range.first - pointer_begin); 209 | 210 | if (pointer_end > address_range.second) 211 | std::advance(end, address_range.second - pointer_end); 212 | } 213 | }; 214 | 215 | template 216 | static SearchConstraints everything() 217 | { 218 | return SearchConstraints{}; 219 | } 220 | 221 | template 222 | static SearchConstraints everything([[maybe_unused]] const MemMgr& _) // Deduction helper 223 | { 224 | return everything(); 225 | } 226 | } 227 | 228 | #endif 229 | -------------------------------------------------------------------------------- /Include/BCRL/SafePointer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_SAFEPOINTER_HPP 2 | #define BCRL_SAFEPOINTER_HPP 3 | 4 | #include "detail/LambdaInserter.hpp" 5 | 6 | #include "SearchConstraints.hpp" 7 | 8 | #include "MemoryManager/MemoryManager.hpp" 9 | 10 | #include "SignatureScanner/PatternSignature.hpp" 11 | #include "SignatureScanner/XRefSignature.hpp" 12 | 13 | #include "LengthDisassembler/LengthDisassembler.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace BCRL { 29 | template 30 | requires MemoryManager::LayoutAware && MemoryManager::Reader && MemoryManager::AddressAware && MemoryManager::LengthAware 31 | class SafePointer { // A pointer which can't cause read access violations 32 | const MemMgr* memory_manager; 33 | std::uintptr_t pointer; 34 | bool invalid; // Set to true, when an operation failed 35 | 36 | public: 37 | SafePointer() = delete; 38 | explicit SafePointer(const MemMgr& memory_manager, std::uintptr_t pointer, bool invalid = false) 39 | : memory_manager(&memory_manager) 40 | , pointer(pointer) 41 | , invalid(invalid) 42 | { 43 | } 44 | 45 | [[nodiscard]] bool is_valid(std::size_t length = 1) const 46 | { 47 | if (is_marked_invalid()) 48 | return false; // It was already eliminated 49 | 50 | std::uintptr_t p = pointer; 51 | 52 | // Check if p + length would overflow: 53 | if (std::numeric_limits::max() - p < length) 54 | return false; 55 | 56 | std::uintptr_t end = pointer + length; 57 | 58 | while (end > p) { 59 | auto* region = memory_manager->get_layout().find_region(p); 60 | if (!region) 61 | return false; 62 | if constexpr (MemMgr::REQUIRES_PERMISSIONS_FOR_READING) 63 | if (!region->get_flags().is_readable()) 64 | return false; 65 | p = region->get_address() + region->get_length(); 66 | } 67 | 68 | return true; 69 | } 70 | 71 | [[nodiscard]] bool read(void* to, size_t len) const 72 | { 73 | if (is_valid(len)) { 74 | memory_manager->read(pointer, to, len); 75 | return true; 76 | } 77 | return false; 78 | } 79 | 80 | template 81 | requires std::is_trivially_copyable_v 82 | [[nodiscard]] std::optional read() const 83 | { 84 | T obj; 85 | if (read(&obj, sizeof(T))) 86 | return obj; 87 | return std::nullopt; 88 | } 89 | 90 | SafePointer& invalidate() // Marks safe pointer as invalid 91 | { 92 | invalid = true; 93 | return *this; 94 | } 95 | SafePointer& revalidate() // Marks safe pointer as valid 96 | { 97 | invalid = false; 98 | return *this; 99 | } 100 | 101 | // Manipulation 102 | SafePointer& add(std::integral auto operand) // Advances pointer forward 103 | { 104 | pointer += operand; 105 | return *this; 106 | } 107 | 108 | SafePointer& sub(std::integral auto operand) // Inverse of above 109 | { 110 | pointer -= operand; 111 | return *this; 112 | } 113 | 114 | SafePointer& dereference() // Follows a pointer 115 | { 116 | std::optional deref = read(); 117 | if (deref.has_value()) { 118 | pointer = deref.value(); 119 | return revalidate(); 120 | } 121 | return invalidate(); 122 | } 123 | 124 | // Patterns 125 | // Previous occurrence of pattern signature 126 | SafePointer& prev_signature_occurrence( 127 | const SignatureScanner::PatternSignature& signature, 128 | const SearchConstraints& search_constraints = everything().thats_readable()) 129 | requires MemoryManager::Viewable 130 | { 131 | auto* region = memory_manager->get_layout().find_region(pointer); 132 | if (!region || !search_constraints.allows_region(*region)) 133 | return invalidate(); 134 | 135 | auto view = region->view(); 136 | 137 | auto begin = view.cbegin(); 138 | auto end = view.cend(); 139 | 140 | std::uintptr_t pointer_end = region->get_address() + region->get_length(); 141 | 142 | if (pointer < pointer_end) 143 | std::advance(end, pointer - pointer_end); 144 | 145 | search_constraints.clamp_to_address_range(*region, view.cbegin(), begin, end); 146 | 147 | auto rbegin = std::make_reverse_iterator(end); 148 | auto rend = std::make_reverse_iterator(begin); 149 | 150 | auto hit = signature.prev(rbegin, rend); 151 | 152 | if (hit == rend) 153 | return invalidate(); 154 | 155 | pointer = region->get_address() 156 | + region->get_length() 157 | - 1 158 | - std::distance(std::make_reverse_iterator(view.cend()), hit); 159 | return revalidate(); 160 | } 161 | 162 | // Next occurrence of pattern signature 163 | SafePointer& next_signature_occurrence( 164 | const SignatureScanner::PatternSignature& signature, 165 | const SearchConstraints& search_constraints = everything().thats_readable()) 166 | requires MemoryManager::Viewable 167 | { 168 | auto* region = memory_manager->get_layout().find_region(pointer); 169 | if (!region || !search_constraints.allows_region(*region)) 170 | return invalidate(); 171 | 172 | auto view = region->view(); 173 | 174 | auto begin = view.cbegin(); 175 | auto end = view.cend(); 176 | 177 | if (pointer > region->get_address()) 178 | std::advance(begin, pointer - region->get_address()); 179 | 180 | search_constraints.clamp_to_address_range(*region, view.cbegin(), begin, end); 181 | 182 | auto hit = signature.next(begin, end); 183 | 184 | if (hit == end) 185 | return invalidate(); 186 | 187 | pointer = region->get_address() + std::distance(view.cbegin(), hit); 188 | return revalidate(); 189 | } 190 | 191 | // Tests if the given pattern signature matches the current address 192 | [[nodiscard]] bool does_match(const SignatureScanner::PatternSignature& signature) const 193 | { 194 | std::size_t length = signature.get_elements().size(); 195 | auto* bytes = static_cast(alloca(length)); 196 | if (!read(bytes, length)) 197 | return false; 198 | return signature.does_match(&bytes[0], &bytes[length]); 199 | } 200 | 201 | // X86 202 | private: 203 | static constexpr bool IS_64_BIT = sizeof(void*) == 8; 204 | static constexpr LengthDisassembler::MachineMode DEFAULT_MACHINE_MODE = IS_64_BIT 205 | ? LengthDisassembler::MachineMode::LONG_MODE 206 | : LengthDisassembler::MachineMode::LONG_COMPATIBILITY_MODE; 207 | using RelAddrType = std::conditional_t; 208 | 209 | public: 210 | // Since there can be multiple xrefs, this returns multiple addresses 211 | [[nodiscard]] std::vector find_xrefs( 212 | SignatureScanner::XRefTypes types, 213 | std::uint8_t instruction_length, 214 | const SearchConstraints& search_constraints = everything().thats_readable()) const 215 | requires MemoryManager::Viewable 216 | { 217 | std::vector new_pointers; 218 | 219 | SignatureScanner::XRefSignature signature{ types, pointer, instruction_length }; 220 | for (const auto& region : memory_manager->get_layout()) { 221 | if (!search_constraints.allows_region(region)) 222 | continue; 223 | 224 | auto view = region.view(); 225 | 226 | auto begin = view.cbegin(); 227 | auto end = view.cend(); 228 | 229 | search_constraints.clamp_to_address_range(region, view.cbegin(), begin, end); 230 | 231 | signature.all(begin, end, detail::LambdaInserter([&](decltype(begin) match) { 232 | new_pointers.emplace_back(*memory_manager, region.get_address() + std::distance(view.cbegin(), match)); 233 | }), 234 | region.get_address() + std::distance(view.cbegin(), begin)); 235 | } 236 | 237 | return new_pointers; 238 | } 239 | 240 | [[nodiscard]] std::vector find_xrefs( 241 | SignatureScanner::XRefTypes types, 242 | const SearchConstraints& search_constraints = everything().thats_readable()) const 243 | requires MemoryManager::Viewable 244 | { 245 | return find_xrefs(types, sizeof(RelAddrType), search_constraints); 246 | } 247 | 248 | SafePointer& relative_to_absolute() 249 | { 250 | 251 | std::optional offset = read(); 252 | if (!offset.has_value()) { 253 | return invalidate(); 254 | } 255 | 256 | return add(sizeof(RelAddrType) + offset.value()); 257 | } 258 | 259 | SafePointer& next_instruction(LengthDisassembler::MachineMode mode = DEFAULT_MACHINE_MODE) 260 | { 261 | auto* region = memory_manager->get_layout().find_region(pointer); 262 | if (!region) 263 | return invalidate(); 264 | 265 | std::uintptr_t end = region->get_address() + region->get_length(); 266 | 267 | static constexpr std::size_t LONGEST_X86_INSN = LengthDisassembler::MAX_INSTRUCTION_LENGTH; 268 | 269 | std::size_t max_length = std::min(end - pointer, LONGEST_X86_INSN); 270 | 271 | std::array bytes{}; 272 | if (!read(&bytes, max_length)) { 273 | return invalidate(); 274 | } 275 | 276 | using enum LengthDisassembler::MachineMode; 277 | 278 | std::expected 279 | instruction = LengthDisassembler::disassemble(bytes.data(), 280 | mode, 281 | max_length); 282 | 283 | if (!instruction.has_value()) { 284 | return invalidate(); 285 | } 286 | 287 | return add(instruction.value().length); 288 | } 289 | 290 | // Filters 291 | [[nodiscard]] bool filter(const SearchConstraints& search_constraints = everything().thats_readable()) const 292 | { 293 | auto* region = memory_manager->get_layout().find_region(pointer); 294 | if (!region || !search_constraints.allows_region(*region)) 295 | return false; 296 | 297 | return search_constraints.allows_address(pointer); 298 | } 299 | 300 | constexpr std::strong_ordering operator<=>(const SafePointer& other) const 301 | { 302 | return pointer <=> other.pointer; 303 | } 304 | 305 | constexpr bool operator==(const SafePointer& other) const 306 | { 307 | return pointer == other.pointer; 308 | } 309 | 310 | [[nodiscard]] constexpr const MemMgr& get_memory_manager() const 311 | { 312 | return *memory_manager; 313 | } 314 | 315 | /** 316 | * @returns if a previous operation failed 317 | */ 318 | [[nodiscard]] constexpr bool is_marked_invalid() const 319 | { 320 | return invalid; 321 | } 322 | 323 | [[nodiscard]] constexpr std::uintptr_t get_pointer() const 324 | { 325 | return pointer; 326 | }; 327 | 328 | [[nodiscard]] constexpr SafePointer clone() const 329 | { 330 | return *this; 331 | } 332 | }; 333 | } 334 | 335 | #endif 336 | -------------------------------------------------------------------------------- /Include/BCRL/Session.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BCRL_HPP 2 | #define BCRL_HPP 3 | 4 | #include "detail/LambdaInserter.hpp" 5 | 6 | #include "SafePointer.hpp" 7 | #include "SearchConstraints.hpp" 8 | 9 | #include "MemoryManager/MemoryManager.hpp" 10 | 11 | #include "SignatureScanner/PatternSignature.hpp" 12 | #include "SignatureScanner/XRefSignature.hpp" 13 | 14 | #include "LengthDisassembler/LengthDisassembler.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace BCRL { 32 | enum class FinalizationError : std::uint8_t { 33 | NO_POINTERS_LEFT, 34 | TOO_MANY_POINTERS_LEFT, 35 | }; 36 | 37 | template 38 | class Session { 39 | using InnerSafePointer = SafePointer; 40 | 41 | const MemMgr* memory_manager; 42 | std::vector pointers; 43 | 44 | public: 45 | constexpr Session(const MemMgr& memory_manager, std::vector&& pointers) 46 | : memory_manager(&memory_manager) 47 | , pointers(std::move(pointers)) 48 | { 49 | } 50 | 51 | constexpr Session(const MemMgr& memory_manager, const std::ranges::range auto& pointers) 52 | : memory_manager(&memory_manager) 53 | , pointers() 54 | { 55 | this->pointers.reserve(pointers.size()); 56 | for (auto pointer : pointers) { 57 | this->pointers.emplace_back(memory_manager, pointer); 58 | } 59 | } 60 | 61 | Session() = delete; 62 | 63 | // Manipulation 64 | Session& add(std::integral auto operand) // Advances all pointers forward 65 | { 66 | return for_each([operand](InnerSafePointer& safe_pointer) { 67 | safe_pointer.add(operand); 68 | }); 69 | } 70 | 71 | Session& sub(std::integral auto operand) // Inverse of above 72 | { 73 | return for_each([operand](InnerSafePointer& safe_pointer) { 74 | safe_pointer.sub(operand); 75 | }); 76 | } 77 | 78 | Session& dereference() // Follows a pointer 79 | { 80 | return for_each([](InnerSafePointer& safe_pointer) { 81 | safe_pointer.dereference(); 82 | }); 83 | } 84 | 85 | // Signatures 86 | // Prev occurrence of signature 87 | Session& prev_signature_occurrence( 88 | const SignatureScanner::PatternSignature& signature, 89 | const SearchConstraints& search_constraints = everything().thats_readable()) 90 | { 91 | return for_each([&signature, &search_constraints](InnerSafePointer& safe_pointer) { 92 | safe_pointer.prev_signature_occurrence(signature, search_constraints); 93 | }); 94 | } 95 | 96 | // Next occurrence of signature 97 | Session& next_signature_occurrence( 98 | const SignatureScanner::PatternSignature& signature, 99 | const SearchConstraints& search_constraints = everything().thats_readable()) 100 | { 101 | return for_each([&signature, &search_constraints](InnerSafePointer& safe_pointer) { 102 | safe_pointer.next_signature_occurrence(signature, search_constraints); 103 | }); 104 | } 105 | 106 | // Filters 107 | Session& filter(const SearchConstraints& search_constraints = everything().thats_readable()) 108 | { 109 | return filter([&search_constraints](const InnerSafePointer& safe_pointer) { 110 | return safe_pointer.filter(search_constraints); 111 | }); 112 | } 113 | 114 | // X86 115 | Session& find_xrefs( 116 | SignatureScanner::XRefTypes types, 117 | std::uint8_t instruction_length, 118 | const SearchConstraints& search_constraints = everything().thats_readable()) 119 | { 120 | return flat_map([types, instruction_length, &search_constraints](const InnerSafePointer& safe_pointer) { 121 | return safe_pointer.find_xrefs(types, instruction_length, search_constraints); 122 | }); 123 | } 124 | 125 | Session& find_xrefs( 126 | SignatureScanner::XRefTypes types, 127 | const SearchConstraints& search_constraints = everything().thats_readable()) 128 | { 129 | return flat_map([types, &search_constraints](const InnerSafePointer& safe_pointer) { 130 | return safe_pointer.find_xrefs(types, search_constraints); 131 | }); 132 | } 133 | 134 | Session& relative_to_absolute() 135 | { 136 | return for_each([](InnerSafePointer& safe_pointer) { 137 | safe_pointer.relative_to_absolute(); 138 | }); 139 | } 140 | 141 | Session& next_instruction(LengthDisassembler::MachineMode mode = (sizeof(void*) == 8) 142 | ? LengthDisassembler::MachineMode::LONG_MODE 143 | : LengthDisassembler::MachineMode::LONG_COMPATIBILITY_MODE) 144 | { 145 | return for_each([mode](InnerSafePointer& safe_pointer) { 146 | safe_pointer.next_instruction(mode); 147 | }); 148 | } 149 | 150 | // Advanced Flow 151 | template 152 | requires std::invocable 153 | Session& for_each(const F& body) // Calls action on each pointer 154 | { 155 | // This looks a bit weird, but it basically acts as a for loop which can also delete invalid entries 156 | std::erase_if(pointers, [&body](InnerSafePointer& safe_pointer) { 157 | body(safe_pointer); 158 | return !safe_pointer.is_valid(); 159 | }); 160 | 161 | // Remove duplicates 162 | std::ranges::sort(pointers); 163 | auto r = std::ranges::unique(pointers); 164 | pointers.erase(r.begin(), r.end()); 165 | 166 | return *this; 167 | } 168 | template 169 | requires std::is_invocable_r_v 170 | Session& repeat_while(const F& action) // Repeats action until false is returned 171 | { 172 | return for_each([&action](InnerSafePointer& safe_pointer) { 173 | while (action(safe_pointer)) 174 | ; 175 | }); 176 | } 177 | template 178 | requires std::invocable 179 | Session& repeat_n(std::size_t iterations, const F& action) // Repeats action `iterations` times 180 | { 181 | return for_each([iterations, &action](InnerSafePointer& safe_pointer) { 182 | for (std::size_t i = 0; i < iterations; i++) 183 | action(safe_pointer); 184 | }); 185 | } 186 | template 187 | requires std::is_invocable_r_v 188 | Session& filter(const F& predicate) // Filters out non-conforming pointers 189 | { 190 | return for_each([&predicate](InnerSafePointer& safe_pointer) { 191 | if (!predicate(safe_pointer)) 192 | safe_pointer.invalidate(); 193 | }); 194 | } 195 | template 196 | requires std::is_invocable_r_v, F, const InnerSafePointer&> 197 | Session& flat_map(const F& transformer) // Maps pointer to other pointers 198 | { 199 | std::set new_safe_pointers; 200 | for (InnerSafePointer& safe_pointer : pointers) { 201 | auto transformed = transformer(safe_pointer); 202 | for (InnerSafePointer& new_safe_pointer : transformed) { 203 | if (!new_safe_pointer.is_valid()) 204 | continue; 205 | 206 | new_safe_pointers.emplace(new_safe_pointer); 207 | } 208 | } 209 | 210 | pointers = std::vector(new_safe_pointers.begin(), new_safe_pointers.end()); 211 | return *this; 212 | } 213 | 214 | [[nodiscard]] constexpr Session clone() const 215 | { 216 | return *this; 217 | } 218 | 219 | [[nodiscard]] constexpr const MemMgr& get_memory_manager() const 220 | { 221 | return *memory_manager; 222 | } 223 | 224 | // Finalizing 225 | [[nodiscard]] const std::vector& peek() const // Allows to peek at all remaining pointers 226 | { 227 | return pointers; 228 | } 229 | 230 | [[nodiscard]] std::expected finalize() const // Returns a std::expected based on if there is a clear result 231 | { 232 | if (pointers.size() == 1) 233 | return pointers.begin()->get_pointer(); 234 | 235 | if (pointers.empty()) 236 | return std::unexpected(FinalizationError::NO_POINTERS_LEFT); 237 | 238 | return std::unexpected(FinalizationError::TOO_MANY_POINTERS_LEFT); 239 | } 240 | 241 | template 242 | [[nodiscard]] std::expected finalize() const 243 | { 244 | return finalize().transform([](std::uintptr_t p) { 245 | return T(p); 246 | }); 247 | } 248 | 249 | // Gets the last remaining pointer, but throws a std::runtime_error with a user-defined message if the pool doesn't contain exactly one pointer 250 | template 251 | [[nodiscard]] T expect(const std::string& none, const std::string& too_many) const 252 | { 253 | auto result = finalize(); 254 | 255 | if (!result.has_value()) 256 | throw std::runtime_error{ [&none, &too_many, error = result.error()]() { 257 | switch (error) { 258 | case FinalizationError::NO_POINTERS_LEFT: 259 | return none; 260 | case FinalizationError::TOO_MANY_POINTERS_LEFT: 261 | return too_many; 262 | } 263 | std::unreachable(); 264 | }() }; 265 | 266 | return result.value(); 267 | } 268 | 269 | // Same as expect with a uniform exception message 270 | template 271 | [[nodiscard]] T expect(const std::string& message) const 272 | { 273 | return expect(message, message); 274 | } 275 | }; 276 | 277 | // Openers/Initializers 278 | template 279 | requires std::same_as, std::uintptr_t> 280 | [[nodiscard]] inline Session pointer_list(const MemMgr& memory_manager, const Range& pointers) 281 | { 282 | return { memory_manager, pointers }; 283 | } 284 | 285 | template 286 | [[nodiscard]] inline Session pointer(const MemMgr& memory_manager, std::uintptr_t pointer) 287 | { 288 | return pointer_list(memory_manager, std::initializer_list{ pointer }); 289 | } 290 | 291 | template 292 | [[nodiscard]] inline Session pointer_array(const MemMgr& memory_manager, std::uintptr_t array, std::size_t index) // e.g. Virtual function tables 293 | { 294 | return { memory_manager, std::vector>{ SafePointer(memory_manager, array).dereference().add(index * sizeof(std::uintptr_t)).dereference() } }; 295 | } 296 | 297 | template 298 | requires MemoryManager::LayoutAware && MemoryManager::AddressAware && MemoryManager::NameAware && MemoryManager::FlagAware 299 | [[nodiscard]] inline Session regions( 300 | const MemMgr& memory_manager, 301 | const SearchConstraints& search_constraints = everything().thats_readable()) 302 | { 303 | std::vector bases; 304 | for (const auto& region : memory_manager.get_layout()) 305 | if (search_constraints.allows_region(region)) 306 | bases.push_back(region.get_address()); 307 | return pointer_list(memory_manager, bases); 308 | } 309 | 310 | template 311 | requires MemoryManager::Viewable 312 | [[nodiscard]] inline Session signature( 313 | const MemMgr& memory_manager, 314 | const SignatureScanner::PatternSignature& signature, 315 | const SearchConstraints& search_constraints = everything().thats_readable()) 316 | { 317 | std::vector pointers{}; 318 | 319 | for (const auto& region : memory_manager.get_layout()) { 320 | if (!search_constraints.allows_region(region)) 321 | continue; 322 | 323 | auto view = region.view(); 324 | 325 | auto begin = view.cbegin(); 326 | auto end = view.cend(); 327 | 328 | search_constraints.clamp_to_address_range(region, view.cbegin(), begin, end); 329 | 330 | if constexpr (std::same_as) { 331 | signature.all(begin, end, detail::LambdaInserter([&](decltype(begin) p) { 332 | pointers.push_back(region.get_address() + std::distance(view.cbegin(), p)); 333 | }), 334 | region.get_address() + std::distance(view.cbegin(), begin)); 335 | } else { 336 | signature.all(begin, end, detail::LambdaInserter([&](decltype(begin) p) { 337 | pointers.push_back(region.get_address() + std::distance(view.cbegin(), p)); 338 | })); 339 | } 340 | } 341 | 342 | return { memory_manager, pointers }; 343 | } 344 | 345 | template 346 | requires(MemoryManager::LocalAware && MemMgr::IS_LOCAL && std::is_pointer_v>) 347 | [[nodiscard]] inline Session pointer_list(const MemMgr& memory_manager, const Range& pointers) 348 | { 349 | return { memory_manager, pointers | std::views::transform([](const auto* ptr) { return reinterpret_cast(ptr); }) }; 350 | } 351 | 352 | template 353 | requires(MemoryManager::LocalAware && MemMgr::IS_LOCAL) 354 | [[nodiscard]] inline Session pointer(const MemMgr& memory_manager, void* pointer) 355 | { 356 | return BCRL::pointer(memory_manager, reinterpret_cast(pointer)); 357 | } 358 | 359 | template 360 | requires(MemoryManager::LocalAware && MemMgr::IS_LOCAL) 361 | [[nodiscard]] inline Session pointer_array(const MemMgr& memory_manager, const void* array, std::size_t index) // e.g. Virtual function tables 362 | { 363 | return pointer_array(memory_manager, reinterpret_cast(array), index); 364 | } 365 | } 366 | 367 | #endif 368 | --------------------------------------------------------------------------------