├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── resources └── VersionInfo.rc ├── .gitmodules ├── cmake └── zyrex-config.cmake.in ├── LICENSE ├── .gitignore ├── src ├── Utils.c ├── Zyrex.c ├── InlineHook.c ├── Barrier.c ├── Transaction.c ├── Trampoline.c └── Relocation.c ├── examples ├── InlineHook.c └── Barrier.c ├── include └── Zyrex │ ├── Status.h │ ├── Internal │ ├── Relocation.h │ ├── InlineHook.h │ ├── Utils.h │ └── Trampoline.h │ ├── Zyrex.h │ ├── Barrier.h │ └── Transaction.h ├── README.md ├── doc └── Barrier.md └── CMakeLists.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: flobernd -------------------------------------------------------------------------------- /resources/VersionInfo.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyantific/zyan-hook-engine/HEAD/resources/VersionInfo.rc -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dependencies/zydis"] 2 | path = dependencies/zydis 3 | url = https://github.com/zyantific/zydis.git 4 | [submodule "dependencies/zycore"] 5 | path = dependencies/zycore 6 | url = https://github.com/zyantific/zycore-c.git 7 | -------------------------------------------------------------------------------- /cmake/zyrex-config.cmake.in: -------------------------------------------------------------------------------- 1 | set(zyrex_VERSION @PROJECT_VERSION@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | include(CMakeFindDependencyMacro) 6 | find_dependency(Zycore) 7 | find_dependency(Zydis) 8 | 9 | include("${CMAKE_CURRENT_LIST_DIR}/zyrex-targets.cmake") 10 | 11 | set_and_check(zyrex_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") 12 | set_and_check(zyrex_LIB_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@") 13 | 14 | check_required_components(zyrex) 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | cmake-build: 7 | name: CMake build (${{ matrix.image_name }}) 8 | runs-on: ${{ matrix.image_name }} 9 | 10 | strategy: 11 | matrix: 12 | image_name: [macOS-latest, windows-2019, ubuntu-20.04] 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | with: { submodules: recursive } 18 | - name: Configuring 19 | run: | 20 | mkdir build 21 | cd build 22 | cmake .. 23 | - name: Building 24 | run: | 25 | cmake --build build --config Release 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2022 Florian Bernd 4 | Copyright (c) 2015-2022 Joel Höner 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/c,c++,cmake 2 | 3 | ### C ### 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Libraries 15 | *.lib 16 | *.a 17 | *.la 18 | *.lo 19 | 20 | # Shared objects (inc. Windows DLLs) 21 | *.dll 22 | *.so 23 | *.so.* 24 | *.dylib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.i*86 31 | *.x86_64 32 | *.hex 33 | 34 | # Debug files 35 | *.dSYM/ 36 | *.su 37 | 38 | 39 | ### C++ ### 40 | # Compiled Object files 41 | *.slo 42 | *.lo 43 | *.o 44 | *.obj 45 | 46 | # Precompiled Headers 47 | *.gch 48 | *.pch 49 | 50 | # Compiled Dynamic libraries 51 | *.so 52 | *.dylib 53 | *.dll 54 | 55 | # Fortran module files 56 | *.mod 57 | 58 | # Compiled Static libraries 59 | *.lai 60 | *.la 61 | *.a 62 | *.lib 63 | 64 | # Executables 65 | *.exe 66 | *.out 67 | *.app 68 | 69 | 70 | ### CMake ### 71 | CMakeCache.txt 72 | CMakeFiles 73 | CMakeScripts 74 | Makefile 75 | cmake_install.cmake 76 | install_manifest.txt 77 | CTestTestfile.cmake 78 | 79 | 80 | # MacOS 81 | .DS_Store 82 | 83 | build* 84 | 85 | # MSVC 86 | .vs 87 | *.vcxproj.user 88 | *.suo 89 | *.sdf 90 | *.opensdf 91 | *.VC.db 92 | *.VC.opendb 93 | msvc/**/obj/ 94 | msvc/**/bin/ 95 | 96 | doc/html 97 | -------------------------------------------------------------------------------- /src/Utils.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | int b = 0; 28 | -------------------------------------------------------------------------------- /examples/InlineHook.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | /** 28 | * @file 29 | * @brief Demonstrates the inline-hook. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* ============================================================================================== */ 38 | /* Entry point */ 39 | /* ============================================================================================== */ 40 | 41 | typedef ZyanU32 (FnHookType)(ZyanU32 param); 42 | 43 | ZyanU32 ZYAN_NOINLINE FnHookTarget(ZyanU32 param) 44 | { 45 | puts("hello from original"); 46 | 47 | return param; 48 | } 49 | 50 | static FnHookType* volatile FnHookOriginal = &FnHookTarget; 51 | 52 | ZyanU32 ZYAN_NOINLINE FnHookCallback(ZyanU32 param) 53 | { 54 | puts("hello from callback"); 55 | 56 | return (*FnHookOriginal)(param) + 1; 57 | } 58 | 59 | int main() 60 | { 61 | ZyrexInitialize(); 62 | 63 | ZyrexTransactionBegin(); 64 | ZyrexInstallInlineHook( 65 | (void*)((ZyanUPointer)&FnHookTarget), 66 | (const void*)((ZyanUPointer)&FnHookCallback), 67 | (ZyanConstVoidPointer*)&FnHookOriginal 68 | ); 69 | ZyrexUpdateAllThreads(); 70 | ZyrexTransactionCommit(); 71 | 72 | printf("%x\n", FnHookTarget(0x1337)); 73 | 74 | ZyrexTransactionBegin(); 75 | ZyrexRemoveInlineHook((ZyanConstVoidPointer*)&FnHookOriginal); 76 | ZyrexUpdateAllThreads(); 77 | ZyrexTransactionCommit(); 78 | 79 | printf("%x\n", FnHookTarget(0x1337)); 80 | 81 | return 0; 82 | } 83 | 84 | /* ============================================================================================== */ -------------------------------------------------------------------------------- /include/Zyrex/Status.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | /** 28 | * @file 29 | * @brief Status code definitions and check macros. 30 | */ 31 | 32 | #ifndef ZYREX_STATUS_H 33 | #define ZYREX_STATUS_H 34 | 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | /* ============================================================================================== */ 42 | /* Status codes */ 43 | /* ============================================================================================== */ 44 | 45 | /* ---------------------------------------------------------------------------------------------- */ 46 | /* Module IDs */ 47 | /* ---------------------------------------------------------------------------------------------- */ 48 | 49 | /** 50 | * @brief The Zyrex module id. 51 | */ 52 | #define ZYAN_MODULE_ZYREX 0x200 53 | 54 | /* ---------------------------------------------------------------------------------------------- */ 55 | /* Status codes */ 56 | /* ---------------------------------------------------------------------------------------------- */ 57 | 58 | /** 59 | * @brief Could not allocate a suitable trampoline memory region. 60 | */ 61 | #define ZYREX_STATUS_COULD_NOT_ALLOCATE_TRAMPOLINE \ 62 | ZYAN_MAKE_STATUS(1, ZYAN_MODULE_ZYDIS, 0x00) 63 | 64 | /* ---------------------------------------------------------------------------------------------- */ 65 | 66 | /* ============================================================================================== */ 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* ZYREX_STATUS_H */ 73 | -------------------------------------------------------------------------------- /src/Zyrex.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | /* ============================================================================================== */ 32 | /* Exported functions */ 33 | /* ============================================================================================== */ 34 | 35 | /* ---------------------------------------------------------------------------------------------- */ 36 | /* Initialization & Finalization */ 37 | /* ---------------------------------------------------------------------------------------------- */ 38 | 39 | ZyanStatus ZyrexInitialize(void) 40 | { 41 | if (ZycoreGetVersion() != ZYCORE_VERSION) 42 | { 43 | return ZYAN_STATUS_MISSING_DEPENDENCY; 44 | } 45 | if (ZydisGetVersion() != ZYDIS_VERSION) 46 | { 47 | return ZYAN_STATUS_MISSING_DEPENDENCY; 48 | } 49 | if (!ZydisIsFeatureEnabled(ZYDIS_FEATURE_DECODER)) 50 | { 51 | return ZYAN_STATUS_MISSING_DEPENDENCY; 52 | } 53 | 54 | return ZYAN_STATUS_SUCCESS; 55 | } 56 | 57 | ZyanStatus ZyrexShutdown(void) 58 | { 59 | // nothing to do here at the moment 60 | return ZYAN_STATUS_SUCCESS; 61 | } 62 | 63 | /* ---------------------------------------------------------------------------------------------- */ 64 | /* Information */ 65 | /* ---------------------------------------------------------------------------------------------- */ 66 | 67 | ZyanU64 ZyrexGetVersion(void) 68 | { 69 | return ZYREX_VERSION; 70 | } 71 | 72 | /* ---------------------------------------------------------------------------------------------- */ 73 | 74 | /* ============================================================================================== */ 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zyrex 2 | 3 | License: MIT 4 | GitHub Actions 5 | Gitter 6 | Discord 7 | 8 | Advanced x86/x86-64 hooking library for Windows 9 | 10 | ## Readme 11 | 12 | Everything in this repository is highly WiP and will probably not work as intended right now. Due to lack of time, development is currently on halt, but will hopefully resumed soon. 13 | 14 | ## Features 15 | 16 | - Supports x86 and x86-64 (uses our [Zydis](https://github.com/zyantific/zydis) diassembler library) 17 | - Extremely safe and easy to use ([read more](./doc/Safety.md)) 18 | - Thread-safe by design due to a [Transactional API](./doc/Transaction.md) 19 | - Inbuild [Hook Barrier API](./doc/Barrier.md) to prevent unwanted hook recursion 20 | - Complete doxygen documentation ([master](insert_link_here)) 21 | 22 | ### Hooking methods 23 | 24 | #### Inline Hook 25 | 26 | Patches the prologue of a function to redirect its codeflow and allocates a trampoline which can be used to continue execution of the original function. 27 | 28 | ## Roadmap 29 | 30 | - Windows kernel-mode support 31 | - Multi-platform support (macOS, FreeBSD, Linux and UEFI) 32 | - Software-Breakpoint (SWBP) Hook 33 | - Writes an interrupt/privileged instruction at the begin of a target function and redirects codeflow by catching the resulting exceptions in an unhandled exception handler (Windows only). 34 | - Hardware-Breakpoint (HWBP) Hook 35 | - Hooks code using the CPU debug registers. Not a single byte of code is changed (Windows only). 36 | - Import/Export Address Table Hook 37 | - Hooks code by replacing import-address table (IAT) and export-address table (EAT) entries of COFF binaries at runtime (Windows only). 38 | - Virtual-Method-Table Hook 39 | - Hooks code by replacing virtual-method-table (VMT) entries of object instances at runtime. 40 | 41 | ## Build 42 | 43 | #### Unix 44 | 45 | Zyrex builds cleanly on most platforms without any external dependencies. You can use CMake to generate project files for your favorite C99 compiler. 46 | 47 | ```bash 48 | git clone --recursive 'https://github.com/zyantific/zyrex.git' 49 | cd zyrex 50 | mkdir build && cd build 51 | cmake .. 52 | make 53 | ``` 54 | 55 | #### Windows 56 | 57 | Either use the [Visual Studio 2017 project](./msvc/) or build Zyrex using [CMake](https://cmake.org/download/) ([video guide](https://www.youtube.com/watch?v=fywLDK1OAtQ)). 58 | 59 | ## Versions 60 | 61 | #### Scheme 62 | 63 | Versions follow the [semantic versioning scheme](https://semver.org/). All stability guarantees apply to the API only — ABI stability between patches cannot be assumed unless explicitly mentioned in the release notes. 64 | 65 | #### Branches 66 | 67 | - `master` holds the bleeding edge code of the next, unreleased Zyrex version. Elevated amounts of bugs and issues must be expected, API stability is not guaranteed outside of tagged commits. 68 | - `maintenance/v1` contains the code of the latest stable v1 release. 69 | 70 | ## License 71 | 72 | Zyrex is licensed under the MIT License. 73 | -------------------------------------------------------------------------------- /include/Zyrex/Internal/Relocation.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_INTERNAL_RELOCATION_H 28 | #define ZYREX_INTERNAL_RELOCATION_H 29 | 30 | #include 31 | #include 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /* ============================================================================================== */ 38 | /* Functions */ 39 | /* ============================================================================================== */ 40 | 41 | /* ---------------------------------------------------------------------------------------------- */ 42 | /* */ 43 | /* ---------------------------------------------------------------------------------------------- */ 44 | 45 | /** 46 | * @brief Copies all instructions from the `source` address to the given `trampoline` chunk 47 | * until at least `min_bytes_to_reloc` bytes has been moved. 48 | * 49 | * @param source A pointer to the source buffer. 50 | * @param source_length The maximum amount of bytes that can be safely read from the 51 | * source buffer. 52 | * @param trampoline A pointer to the destination trampoline chunk. 53 | * @param min_bytes_to_reloc Specifies the minimum amount of bytes that should be relocated. 54 | * This function might copy more bytes on demand to keep individual 55 | * instructions intact. 56 | * @param bytes_read Returns the number of bytes read from the source buffer. 57 | * @param bytes_written Returns the number of bytes written to the destination buffer. 58 | * 59 | * @return A zyan status code. 60 | */ 61 | ZyanStatus ZyrexRelocateCode(const void* source, ZyanUSize source_length, 62 | ZyrexTrampolineChunk* trampoline, ZyanUSize min_bytes_to_reloc, ZyanUSize* bytes_read, 63 | ZyanUSize* bytes_written); 64 | 65 | /* ---------------------------------------------------------------------------------------------- */ 66 | 67 | /* ============================================================================================== */ 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif /* ZYREX_INTERNAL_RELOCATION_H */ 74 | -------------------------------------------------------------------------------- /include/Zyrex/Internal/InlineHook.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_INLINE_HOOK_H 28 | #define ZYREX_INLINE_HOOK_H 29 | 30 | #include 31 | #ifdef ZYAN_WINDOWS 32 | # include 33 | #endif 34 | #include 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | /* ============================================================================================== */ 42 | /* Enums and types */ 43 | /* ============================================================================================== */ 44 | 45 | typedef enum ZyrexThreadMigrationDirection_ 46 | { 47 | /** 48 | * @brief Uses the 'source' offsets in the translation map to map them to the 'destination' 49 | * offsets. 50 | */ 51 | ZYREX_THREAD_MIGRATION_DIRECTION_SRC_DST, 52 | /** 53 | * @brief Uses the 'destination' offsets in the translation map to map them to the 'source' 54 | * offsets. 55 | */ 56 | ZYREX_THREAD_MIGRATION_DIRECTION_DST_SRC, 57 | } ZyrexThreadMigrationDirection; 58 | 59 | /* ============================================================================================== */ 60 | /* Functions */ 61 | /* ============================================================================================== */ 62 | 63 | /* ---------------------------------------------------------------------------------------------- */ 64 | /* Attaching and detaching */ 65 | /* ---------------------------------------------------------------------------------------------- */ 66 | 67 | #ifdef ZYAN_WINDOWS 68 | 69 | ZyanStatus ZyrexMigrateThread(HANDLE thread_handle, const void* source, ZyanUSize source_length, 70 | const void* destination, ZyanUSize destination_length, 71 | const ZyrexInstructionTranslationMap* translation_map, 72 | ZyrexThreadMigrationDirection direction); 73 | 74 | #endif 75 | 76 | /* ---------------------------------------------------------------------------------------------- */ 77 | 78 | /* ============================================================================================== */ 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | 84 | #endif /* ZYREX_INLINE_HOOK_H */ 85 | -------------------------------------------------------------------------------- /doc/Barrier.md: -------------------------------------------------------------------------------- 1 | # Hook Barrier API 2 | 3 | ## Introduction 4 | 5 | The Hook Barrier API was developed to prevent unwanted hook recursion. Unwanted hook recursion is a serious issue in many hooking libraries. 6 | 7 | #### Example 8 | 9 | Lets assume we hooked the Windows `NtResumeThread` API which can be used as a generic way to track process creation. In our callback we want to start a second process (e.g. a debugger) and attach it to the new process: 10 | 11 | ```c 12 | NTSTATUS NtResumeThread_Callback(HANDLE ThreadHandle, PULONG SuspendCount) 13 | { 14 | if (BelongsToNewlyCreatedProcess(ThreadHandle)) 15 | { 16 | if (CreateProcessW("debugger.exe", "-attach {pid}", ...)) 17 | { 18 | // ... 19 | } 20 | } 21 | 22 | return NtResumeThread_Trampoline(ThreadHandle, SuspendCount); 23 | } 24 | ``` 25 | 26 | This small code does already trigger an endless recursion loop. 27 | 28 | The `CreateProcessW` function internally invokes some other APIs like `CreateProcessInternalW` and `NtCreateProcess` which then finally calls `NtResumeThread` to resume the main thread of the new process. 29 | 30 | This last call to `NtResumeThread` will of course be redirected to our callback function which then starts the recursion. 31 | 32 | #### Prevention 33 | 34 | To prevent unwanted hook recursion, every thread needs to track the current recursion level for each hooked function. Temporarily removing the hook is no solution in a preemptive multitasking system, as this could cause unwanted side-effects like e.g. the hook not triggering for an other thread. 35 | 36 | ## Usage 37 | 38 | As the `trampoline` pointer might get modified from another thread (e.g. during hook removal), it is neccessary to obtain a constant barrier handle before invoking a barrier API function inside a hook callback. The returned handle should be saved and then used for all subsequent calls to the barrier API inside the current hook callback. 39 | 40 | ```c 41 | ZYREX_EXPORT ZyrexBarrierHandle ZyrexBarrierGetHandle(const void* trampoline); 42 | ``` 43 | 44 | After a barrier handle got obtained, it can be used to enter the barrier. The function returns `ZYAN_STATUS_TRUE` if the current recursion level is 0 or `ZYAN_STATUS_FALSE`, if not. The callback should invoke the trampoline function with unchanged parameters and immediately return afterwards in the latter case. 45 | 46 | ```c 47 | ZYREX_EXPORT ZyanStatus ZyrexBarrierTryEnter(ZyrexBarrierHandle handle); 48 | ``` 49 | 50 | If the barrier was successfully entered before, it needs to be leaved before returning from the callback. 51 | 52 | ```c 53 | ZYREX_EXPORT ZyanStatus ZyrexBarrierLeave(ZyrexBarrierHandle handle); 54 | ``` 55 | 56 | #### Example 57 | 58 | Here is the example from the introduction rewritten to use the Hook Barrier API: 59 | 60 | ```c 61 | NTSTATUS NtResumeThread_Callback(HANDLE ThreadHandle, PULONG SuspendCount) 62 | { 63 | // Obtain the barrier handle identified by the given trampoline 64 | const ZyrexBarrierHandle barrier_handle = ZyrexBarrierGetHandle(&NtResumeThread_Tampoline); 65 | 66 | // Try to enter the Hook Barrier 67 | if (ZyrexBarrierTryEnter(barrier_handle) != ZYAN_STATUS_TRUE) 68 | { 69 | // Failed to pass the barrier. Invoke the trampoline function and immediately leave the 70 | // callback 71 | return NtResumeThread_Trampoline(ThreadHandle, SuspendCount); 72 | } 73 | 74 | // We successfully passed the barrier .. 75 | if (BelongsToNewlyCreatedProcess(ThreadHandle)) 76 | { 77 | if (CreateProcessW("debugger.exe", "-attach {pid}", ...)) 78 | { 79 | // ... 80 | } 81 | } 82 | 83 | // Leave the barrier 84 | ZyrexBarrierLeave(barrier_handle); 85 | 86 | return NtResumeThread_Trampoline(ThreadHandle, SuspendCount); 87 | } 88 | ``` 89 | 90 | ## Internals 91 | 92 | The Hook Barrier API uses Thread-Local-Storage (TLS) to store a vector which contains the current recursion level for every hook that called `ZyrexBarrierTryEnter` at least once. 93 | 94 | TLS functionality is abstracted by Zycore and thus available on Windows and all POSIX compliant platforms. -------------------------------------------------------------------------------- /include/Zyrex/Zyrex.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | /** 28 | * @file 29 | * @brief Master include file, including everything else. 30 | */ 31 | 32 | #ifndef ZYREX_H 33 | #define ZYREX_H 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | // TODO: 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /* ============================================================================================== */ 46 | /* Macros */ 47 | /* ============================================================================================== */ 48 | 49 | /* ---------------------------------------------------------------------------------------------- */ 50 | /* Constants */ 51 | /* ---------------------------------------------------------------------------------------------- */ 52 | 53 | /** 54 | * @brief A macro that defines the zyrex version. 55 | */ 56 | #define ZYREX_VERSION (ZyanU64)0x0001000000000000 57 | 58 | /* ---------------------------------------------------------------------------------------------- */ 59 | /* Helper macros */ 60 | /* ---------------------------------------------------------------------------------------------- */ 61 | 62 | /** 63 | * @brief Extracts the major-part of the zyrex version. 64 | * 65 | * @param version The zyrex version value 66 | */ 67 | #define ZYREX_VERSION_MAJOR(version) (ZyanU16)(((version) & 0xFFFF000000000000) >> 48) 68 | 69 | /** 70 | * @brief Extracts the minor-part of the zyrex version. 71 | * 72 | * @param version The zyrex version value 73 | */ 74 | #define ZYREX_VERSION_MINOR(version) (ZyanU16)(((version) & 0x0000FFFF00000000) >> 32) 75 | 76 | /** 77 | * @brief Extracts the patch-part of the zyrex version. 78 | * 79 | * @param version The zyrex version value 80 | */ 81 | #define ZYREX_VERSION_PATCH(version) (ZyanU16)(((version) & 0x00000000FFFF0000) >> 16) 82 | 83 | /** 84 | * @brief Extracts the build-part of the zyrex version. 85 | * 86 | * @param version The zyrex version value 87 | */ 88 | #define ZYREX_VERSION_BUILD(version) (ZyanU16)((version) & 0x000000000000FFFF) 89 | 90 | /* ---------------------------------------------------------------------------------------------- */ 91 | 92 | /* ============================================================================================== */ 93 | /* Exported functions */ 94 | /* ============================================================================================== */ 95 | 96 | /* ---------------------------------------------------------------------------------------------- */ 97 | /* Initialization & Finalization */ 98 | /* ---------------------------------------------------------------------------------------------- */ 99 | 100 | /** 101 | * @brief Initializes the `Zyrex` hook engine. 102 | * 103 | * @return A zyan status code. 104 | * 105 | * This function has to be called before invoking any other `Zyrex*` API. 106 | */ 107 | ZYREX_EXPORT ZyanStatus ZyrexInitialize(void); 108 | 109 | /** 110 | * @brief Releases global resources allocated by the `Zyrex` hook engine. 111 | * 112 | * @return A zyan status code. 113 | * 114 | * No `Zyrex*` API function should be called after invoking this function. 115 | */ 116 | ZYREX_EXPORT ZyanStatus ZyrexShutdown(void); 117 | 118 | /* ---------------------------------------------------------------------------------------------- */ 119 | /* Information */ 120 | /* ---------------------------------------------------------------------------------------------- */ 121 | 122 | /** 123 | * @brief Returns the zyrex version. 124 | * 125 | * @return The zyrex version. 126 | * 127 | * Use the macros provided in this file to extract the major, minor, patch and build part from the 128 | * returned version value. 129 | */ 130 | ZYREX_EXPORT ZyanU64 ZyrexGetVersion(void); 131 | 132 | /* ---------------------------------------------------------------------------------------------- */ 133 | 134 | /* ============================================================================================== */ 135 | 136 | #ifdef __cplusplus 137 | } 138 | #endif 139 | 140 | #endif /* ZYREX_H */ 141 | -------------------------------------------------------------------------------- /examples/Barrier.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | /** 28 | * @file 29 | * @brief Demonstrates the 'Hook Barrier' functionality. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | /* ============================================================================================== */ 38 | /* Example functions */ 39 | /* ============================================================================================== */ 40 | 41 | /* ---------------------------------------------------------------------------------------------- */ 42 | /* Target functions */ 43 | /* ---------------------------------------------------------------------------------------------- */ 44 | 45 | static int ZYAN_NOINLINE LogMessage(const char* message); 46 | 47 | /** 48 | * Delays execution of the current thread for a certain amount of time by performing loop 49 | * iterations. 50 | * 51 | * @param iterations The number of iterations to perform. 52 | */ 53 | static void ZYAN_NOINLINE Delay(int iterations) 54 | { 55 | for (int i = 0; i < iterations; ++i) 56 | { 57 | } 58 | 59 | LogMessage("Execution has been delayed\n"); 60 | } 61 | 62 | /** 63 | * Logs a message to `stdout`. 64 | * 65 | * @param message The message to log. 66 | * 67 | * @return The number of characters written to `stdout`. 68 | */ 69 | static int ZYAN_NOINLINE LogMessage(const char* message) 70 | { 71 | return printf("%s", message); 72 | } 73 | 74 | /* ---------------------------------------------------------------------------------------------- */ 75 | /* Hook callback */ 76 | /* ---------------------------------------------------------------------------------------------- */ 77 | 78 | typedef int (SignatureLogMessage)(const char* message); 79 | 80 | static SignatureLogMessage* volatile OriginalLogMessage = &LogMessage; 81 | 82 | /** 83 | * Logs a message to `stdout` and prepends the `Intercepted: ` string. 84 | * 85 | * @param message The message to log. 86 | * 87 | * @return The number of characters written to `stdout`. 88 | */ 89 | static int CallbackLogMessage(const char* message) 90 | { 91 | // Obtain the barrier handle identified by the given trampoline: 92 | // It's important to only call `ZyrexBarrierGetHandle` once as the value of `trampoline` 93 | // will change if the hook got removed by a different thread between the first- and the 94 | // second- call. 95 | const ZyrexBarrierHandle barrier_handle = ZyrexBarrierGetHandle((const void*)OriginalLogMessage); 96 | 97 | // Try to enter the Hook Barrier 98 | if (ZyrexBarrierTryEnter(barrier_handle) != ZYAN_STATUS_TRUE) 99 | { 100 | // Failed to pass the barrier. Invoke the trampoline function and immediately leave the 101 | // callback 102 | return OriginalLogMessage(message); 103 | } 104 | 105 | // We successfully passed the barrier .. 106 | const int result = OriginalLogMessage("Intercepted: ") + OriginalLogMessage(message); 107 | 108 | // We can now execute a function that internally calls `LogMessage` without risking infinite 109 | // recursion 110 | Delay(1000); 111 | 112 | // We can even execute the original function without involving the trampoline 113 | LogMessage("NOT intercepted\n"); 114 | 115 | // Leave the barrier: 116 | // This is very important! If we do not leave the barrier, all subsequent callback executions 117 | // won't be able to enter it again. 118 | ZyrexBarrierLeave(barrier_handle); 119 | 120 | return result; 121 | } 122 | 123 | /* ---------------------------------------------------------------------------------------------- */ 124 | 125 | /* ============================================================================================== */ 126 | /* Entry point */ 127 | /* ============================================================================================== */ 128 | 129 | int main() 130 | { 131 | // Must be called once at program start! 132 | ZyrexBarrierSystemInitialize(); 133 | 134 | CallbackLogMessage("Example log message 1\n"); 135 | CallbackLogMessage("Example log message 2\n"); 136 | 137 | ZyrexBarrierSystemShutdown(); 138 | 139 | return 0; 140 | } 141 | 142 | /* ============================================================================================== */ 143 | -------------------------------------------------------------------------------- /src/InlineHook.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | /* ============================================================================================== */ 32 | /* Functions */ 33 | /* ============================================================================================== */ 34 | 35 | /* ---------------------------------------------------------------------------------------------- */ 36 | /* Runtime thread migration */ 37 | /* ---------------------------------------------------------------------------------------------- */ 38 | 39 | #ifdef ZYAN_WINDOWS 40 | 41 | ZyanStatus ZyrexMigrateThread(HANDLE thread_handle, const void* source, ZyanUSize source_length, 42 | const void* destination, ZyanUSize destination_length, 43 | const ZyrexInstructionTranslationMap* translation_map, 44 | ZyrexThreadMigrationDirection direction) 45 | { 46 | ZYAN_UNUSED(destination_length); 47 | 48 | ZYAN_ASSERT(thread_handle); 49 | ZYAN_ASSERT(source); 50 | ZYAN_ASSERT(source_length); 51 | ZYAN_ASSERT(destination); 52 | ZYAN_ASSERT(destination_length); 53 | ZYAN_ASSERT(translation_map); 54 | 55 | ZyanStatus status = ZYAN_STATUS_SUCCESS; 56 | 57 | const DWORD suspend_count = SuspendThread(thread_handle); 58 | if (suspend_count == (DWORD)(-1)) 59 | { 60 | CloseHandle(thread_handle); 61 | return ZYAN_STATUS_BAD_SYSTEMCALL; 62 | } 63 | 64 | CONTEXT context = { 0 }; 65 | //ZYAN_MEMSET(&context, 0, sizeof(context)); 66 | context.ContextFlags = CONTEXT_CONTROL; 67 | if (!GetThreadContext(thread_handle, &context)) 68 | { 69 | status = ZYAN_STATUS_BAD_SYSTEMCALL; 70 | goto CleanupAndResume; 71 | } 72 | 73 | #if defined(ZYAN_X64) 74 | const ZyanUPointer current_ip = context.Rip; 75 | #elif defined(ZYAN_X86) 76 | const ZyanUPointer current_ip = context.Eip; 77 | #else 78 | # error "Unsupported architecture detected" 79 | #endif 80 | if ((current_ip < (ZyanUPointer)source) || (current_ip > (ZyanUPointer)source + source_length)) 81 | { 82 | goto CleanupAndResume; 83 | } 84 | 85 | const ZyanUPointer source_offset = (ZyanUPointer)source - current_ip; 86 | for (ZyanUSize i = 0; i < translation_map->count; ++i) 87 | { 88 | switch (direction) 89 | { 90 | case ZYREX_THREAD_MIGRATION_DIRECTION_SRC_DST: 91 | if (translation_map->items[i].offset_source == (ZyanU8)source_offset) 92 | { 93 | #if defined(ZYAN_X64) 94 | context.Rip = (ZyanUPointer)destination + translation_map->items[i].offset_destination; 95 | #elif defined(ZYAN_X86) 96 | context.Eip = (ZyanUPointer)destination + translation_map->items[i].offset_destination; 97 | #else 98 | # error "Unsupported architecture detected" 99 | #endif 100 | 101 | if (!SetThreadContext(thread_handle, &context)) 102 | { 103 | status = ZYAN_STATUS_BAD_SYSTEMCALL; 104 | } 105 | 106 | goto CleanupAndResume; 107 | } 108 | break; 109 | case ZYREX_THREAD_MIGRATION_DIRECTION_DST_SRC: 110 | if (translation_map->items[i].offset_destination == (ZyanU8)source_offset) 111 | { 112 | #if defined(ZYAN_X64) 113 | context.Rip = (ZyanUPointer)destination + translation_map->items[i].offset_source; 114 | #elif defined(ZYAN_X86) 115 | context.Eip = (ZyanUPointer)destination + translation_map->items[i].offset_source; 116 | #else 117 | # error "Unsupported architecture detected" 118 | #endif 119 | 120 | if (!SetThreadContext(thread_handle, &context)) 121 | { 122 | status = ZYAN_STATUS_BAD_SYSTEMCALL; 123 | } 124 | 125 | goto CleanupAndResume; 126 | } 127 | break; 128 | default: 129 | ZYAN_UNREACHABLE; 130 | } 131 | } 132 | 133 | // This should never happen 134 | ZYAN_UNREACHABLE; 135 | 136 | CleanupAndResume: 137 | while (ZYAN_TRUE) 138 | { 139 | const DWORD value = ResumeThread(thread_handle); 140 | if (value == (DWORD)(-1)) 141 | { 142 | CloseHandle(thread_handle); 143 | return ZYAN_STATUS_BAD_SYSTEMCALL; 144 | } 145 | if (value <= suspend_count + 1) 146 | { 147 | break; 148 | } 149 | } 150 | 151 | return status; 152 | } 153 | 154 | #endif 155 | 156 | /* ---------------------------------------------------------------------------------------------- */ 157 | /* Attaching and detaching */ 158 | /* ---------------------------------------------------------------------------------------------- */ 159 | 160 | /* ---------------------------------------------------------------------------------------------- */ 161 | 162 | /* ============================================================================================== */ 163 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR) 2 | 3 | project(Zyrex VERSION 1.0.0.0 LANGUAGES C CXX) 4 | 5 | include(GenerateExportHeader) 6 | include(GNUInstallDirs) 7 | 8 | if (NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) 9 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 10 | endif() 11 | if (NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) 12 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 13 | endif() 14 | if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 16 | endif() 17 | 18 | # =============================================================================================== # 19 | # Overridable options # 20 | # =============================================================================================== # 21 | 22 | # Build configuration 23 | option(ZYREX_BUILD_SHARED_LIB 24 | "Build shared library" 25 | OFF) 26 | option(ZYREX_BUILD_EXAMPLES 27 | "Build examples" 28 | ON) 29 | 30 | # Dependencies 31 | option(ZYAN_SYSTEM_ZYCORE 32 | "Use system Zycore library" 33 | OFF) 34 | set(ZYAN_ZYCORE_PATH 35 | "${CMAKE_CURRENT_LIST_DIR}/dependencies/zycore" 36 | CACHE 37 | PATH 38 | "The path to look for Zydis") 39 | option(ZYAN_SYSTEM_ZYDIS 40 | "Use system Zydis library" 41 | OFF) 42 | set(ZYAN_ZYDIS_PATH 43 | "${CMAKE_CURRENT_LIST_DIR}/dependencies/zydis" 44 | CACHE 45 | PATH 46 | "The path to look for Zycore") 47 | 48 | # =============================================================================================== # 49 | # Dependencies # 50 | # =============================================================================================== # 51 | 52 | if (ZYAN_SYSTEM_ZYCORE) 53 | find_package(Zycore) 54 | else () 55 | # Try to initialize the Zycore submodule using Git 56 | if (NOT EXISTS "${ZYAN_ZYCORE_PATH}/CMakeLists.txt" AND 57 | "${ZYAN_ZYCORE_PATH}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}/dependencies/zycore") 58 | find_package(Git QUIET) 59 | if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") 60 | execute_process( 61 | COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive 62 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 63 | ) 64 | endif() 65 | endif () 66 | 67 | if (NOT EXISTS "${ZYAN_ZYCORE_PATH}/CMakeLists.txt") 68 | message( 69 | FATAL_ERROR 70 | "Can't find zycore submodule. Please make sure to clone the repo recursively.\n" 71 | "You can fix this by running\n" 72 | " git submodule update --init\n" 73 | "or by cloning using\n" 74 | " git clone --recursive \n" 75 | "Alternatively, you can manually clone zycore to some path and set ZYDIS_ZYCORE_PATH." 76 | ) 77 | endif () 78 | 79 | add_subdirectory(${ZYAN_ZYCORE_PATH} "zycore" EXCLUDE_FROM_ALL) 80 | endif () 81 | 82 | if (ZYAN_SYSTEM_ZYDIS) 83 | find_package(Zydis) 84 | else () 85 | # Try to initialize the Zydis submodule using Git 86 | if (NOT EXISTS "${ZYAN_ZYDIS_PATH}/CMakeLists.txt" AND 87 | "${ZYAN_ZYDIS_PATH}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}/dependencies/zydis") 88 | find_package(Git QUIET) 89 | if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") 90 | execute_process( 91 | COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive 92 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 93 | ) 94 | endif() 95 | endif () 96 | 97 | if (NOT EXISTS "${ZYAN_ZYDIS_PATH}/CMakeLists.txt") 98 | message( 99 | FATAL_ERROR 100 | "Can't find zydis submodule. Please make sure to clone the repo recursively.\n" 101 | "You can fix this by running\n" 102 | " git submodule update --init\n" 103 | "or by cloning using\n" 104 | " git clone --recursive \n" 105 | "Alternatively, you can manually clone zycore to some path and set ZYDIS_ZYDIS_PATH." 106 | ) 107 | endif () 108 | 109 | add_subdirectory(${ZYAN_ZYDIS_PATH} "zydis" EXCLUDE_FROM_ALL) 110 | endif () 111 | 112 | # =============================================================================================== # 113 | # Library configuration # 114 | # =============================================================================================== # 115 | 116 | if (ZYREX_BUILD_SHARED_LIB) 117 | add_library("Zyrex" SHARED) 118 | else () 119 | add_library("Zyrex" STATIC) 120 | endif () 121 | 122 | option(ZYDIS_MINIMAL_MODE "" ON) 123 | option(ZYDIS_FEATURE_FORMATTER "" OFF) 124 | option(ZYDIS_FEATURE_AVX512 "" OFF) 125 | option(ZYDIS_FEATURE_KNC "" OFF) 126 | option(ZYDIS_FEATURE_ENCODER "" OFF) 127 | option(ZYDIS_BUILD_EXAMPLES "" OFF) 128 | option(ZYDIS_BUILD_TOOLS "" OFF) 129 | 130 | target_link_libraries("Zyrex" PUBLIC "Zycore") 131 | target_link_libraries("Zyrex" PUBLIC "Zydis") 132 | 133 | target_include_directories("Zyrex" 134 | PUBLIC 135 | $ 136 | $ 137 | $ 138 | PRIVATE "src") 139 | target_compile_definitions("Zyrex" PRIVATE "_CRT_SECURE_NO_WARNINGS" "ZYREX_EXPORTS") 140 | set_target_properties("Zyrex" PROPERTIES 141 | VERSION ${Zyrex_VERSION} 142 | SOVERSION ${Zyrex_VERSION_MAJOR}.${Zyrex_VERSION_MINOR}) 143 | zyan_set_common_flags("Zyrex") 144 | zyan_maybe_enable_wpo_for_lib("Zyrex") 145 | generate_export_header("Zyrex" BASE_NAME "ZYREX" EXPORT_FILE_NAME "ZyrexExportConfig.h") 146 | 147 | target_sources("Zyrex" 148 | PRIVATE 149 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Barrier.h" 150 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Status.h" 151 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Transaction.h" 152 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Zyrex.h" 153 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Internal/InlineHook.h" 154 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Internal/Relocation.h" 155 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Internal/Trampoline.h" 156 | "${CMAKE_CURRENT_LIST_DIR}/include/Zyrex/Internal/Utils.h" 157 | "src/Barrier.c" 158 | "src/Relocation.c" 159 | "src/InlineHook.c" 160 | "src/Trampoline.c" 161 | "src/Transaction.c" 162 | "src/Utils.c" 163 | "src/Zyrex.c") 164 | 165 | if (ZYREX_BUILD_SHARED_LIB AND WIN32) 166 | target_sources("Zyrex" PRIVATE "resources/VersionInfo.rc") 167 | endif () 168 | 169 | zyan_set_source_group("Zyrex") 170 | 171 | configure_package_config_file(cmake/zyrex-config.cmake.in 172 | "${CMAKE_CURRENT_BINARY_DIR}/zyrex-config.cmake" 173 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zyrex" 174 | ) 175 | write_basic_package_version_file( 176 | "${CMAKE_CURRENT_BINARY_DIR}/zyrex-config-version.cmake" 177 | COMPATIBILITY SameMajorVersion 178 | ) 179 | install(FILES 180 | "${CMAKE_CURRENT_BINARY_DIR}/zyrex-config.cmake" 181 | "${CMAKE_CURRENT_BINARY_DIR}/zyrex-config-version.cmake" 182 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zyrex" 183 | ) 184 | 185 | install(TARGETS "Zyrex" 186 | EXPORT "zyrex-targets" 187 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 188 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 189 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 190 | install(EXPORT "zyrex-targets" 191 | NAMESPACE "Zydis::" 192 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zyrex") 193 | install(FILES 194 | "${PROJECT_BINARY_DIR}/ZyrexExportConfig.h" 195 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 196 | install(DIRECTORY "include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 197 | 198 | # =============================================================================================== # 199 | # Examples # 200 | # =============================================================================================== # 201 | 202 | if (ZYREX_BUILD_EXAMPLES) 203 | add_executable("InlineHook" "examples/InlineHook.c") 204 | target_link_libraries("InlineHook" "Zycore") 205 | target_link_libraries("InlineHook" "Zyrex") 206 | set_target_properties("InlineHook" PROPERTIES FOLDER "Examples/InlineHook") 207 | target_compile_definitions("InlineHook" PRIVATE "_CRT_SECURE_NO_WARNINGS") 208 | zyan_set_common_flags("InlineHook") 209 | zyan_maybe_enable_wpo("InlineHook") 210 | 211 | add_executable("Barrier" "examples/Barrier.c") 212 | target_link_libraries("Barrier" "Zycore") 213 | target_link_libraries("Barrier" "Zyrex") 214 | set_target_properties("Barrier" PROPERTIES FOLDER "Examples/Barrier") 215 | target_compile_definitions("Barrier" PRIVATE "_CRT_SECURE_NO_WARNINGS") 216 | zyan_set_common_flags("Barrier") 217 | zyan_maybe_enable_wpo("Barrier") 218 | endif () 219 | -------------------------------------------------------------------------------- /include/Zyrex/Internal/Utils.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_INTERNAL_UTILS_H 28 | #define ZYREX_INTERNAL_UTILS_H 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /* ============================================================================================== */ 39 | /* Macros */ 40 | /* ============================================================================================== */ 41 | 42 | /* ---------------------------------------------------------------------------------------------- */ 43 | /* Constants */ 44 | /* ---------------------------------------------------------------------------------------------- */ 45 | 46 | /** 47 | * @brief The size of the relative jump instruction (in bytes). 48 | */ 49 | #define ZYREX_SIZEOF_RELATIVE_JUMP 5 50 | 51 | /** 52 | * @brief The size of the absolute jump instruction (in bytes). 53 | */ 54 | #define ZYREX_SIZEOF_ABSOLUTE_JUMP 6 55 | 56 | /** 57 | * @brief The target range of the relative jump instruction. 58 | */ 59 | #define ZYREX_RANGEOF_RELATIVE_JUMP 0x7FFFFFFF 60 | 61 | /* ---------------------------------------------------------------------------------------------- */ 62 | 63 | /* ============================================================================================== */ 64 | /* Utility functions */ 65 | /* ============================================================================================== */ 66 | 67 | /* ---------------------------------------------------------------------------------------------- */ 68 | /* General */ 69 | /* ---------------------------------------------------------------------------------------------- */ 70 | 71 | /** 72 | * @brief Calculates a relative offset from the given `source` to the given `destination` 73 | * address. 74 | * 75 | * @param instruction_length The length of the instruction the offset is calculated for. 76 | * @param source_address The source address of the instruction. 77 | * @param destination_address The destination address. 78 | * 79 | * @return The relative offset from the given `source` to the given `destination` address. 80 | */ 81 | ZYAN_INLINE ZyanI32 ZyrexCalculateRelativeOffset(ZyanU8 instruction_length, 82 | ZyanUPointer source_address, ZyanUPointer destination_address) 83 | { 84 | return (ZyanI32)(destination_address - source_address - instruction_length); 85 | } 86 | 87 | /* ---------------------------------------------------------------------------------------------- */ 88 | /* Jumps */ 89 | /* ---------------------------------------------------------------------------------------------- */ 90 | 91 | /** 92 | * @brief Writes a relative jump instruction at the given `address`. 93 | * 94 | * @param address The jump address. 95 | * @param destination The absolute destination address of the jump. 96 | * 97 | * This function does not perform any checks, like if the target destination is within the range 98 | * of a relative jump. 99 | */ 100 | ZYAN_INLINE void ZyrexWriteRelativeJump(void* address, ZyanUPointer destination) 101 | { 102 | ZyanU8* instr = (ZyanU8*)address; 103 | 104 | *instr++ = 0xE9; 105 | *(ZyanI32*)(instr) = ZyrexCalculateRelativeOffset(ZYREX_SIZEOF_RELATIVE_JUMP, 106 | (ZyanUPointer)address, destination); 107 | } 108 | 109 | /** 110 | * @brief Writes an absolute indirect jump instruction at the given `address`. 111 | * 112 | * @param address The jump address. 113 | * @param destination The memory address that contains the absolute destination for the jump. 114 | */ 115 | ZYAN_INLINE void ZyrexWriteAbsoluteJump(void* address, ZyanUPointer destination) 116 | { 117 | ZyanU16* instr = (ZyanU16*)address; 118 | 119 | *instr++ = 0x25FF; 120 | #if defined(ZYAN_X64) 121 | *(ZyanI32*)(instr) = ZyrexCalculateRelativeOffset(ZYREX_SIZEOF_ABSOLUTE_JUMP, 122 | (ZyanUPointer)address, destination); 123 | #else 124 | *(ZyanU32*)(instr) = (ZyanU32)destination; 125 | #endif 126 | } 127 | 128 | /* ---------------------------------------------------------------------------------------------- */ 129 | /* Instruction decoding */ 130 | /* ---------------------------------------------------------------------------------------------- */ 131 | 132 | // TODO: Integrate this function in Zydis `ZyrexCalcAbsoluteAddressRaw` 133 | 134 | /** 135 | * @brief Calculates the absolute target address value for a relative-branch instruction 136 | * or an instruction with `EIP/RIP`-relative memory operand. 137 | * 138 | * @param instruction A pointer to the `ZydisDecodedInstruction` struct. 139 | * @param runtime_address The runtime address of the instruction. 140 | * @param result_address A pointer to the memory that receives the absolute address. 141 | * 142 | * @return A zyan status code. 143 | */ 144 | ZYAN_INLINE ZyanStatus ZyrexCalcAbsoluteAddress(const ZydisDecodedInstruction* instruction, 145 | ZyanU64 runtime_address, ZyanU64* result_address) 146 | { 147 | ZYAN_ASSERT(instruction); 148 | ZYAN_ASSERT(result_address); 149 | ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_IS_RELATIVE); 150 | 151 | // Instruction with EIP/RIP-relative memory operand 152 | if ((instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM) && 153 | (instruction->raw.modrm.mod == 0) && 154 | (instruction->raw.modrm.rm == 5)) 155 | { 156 | if (instruction->address_width == 32) 157 | { 158 | *result_address = ((ZyanU32)runtime_address + instruction->length + 159 | (ZyanU32)instruction->raw.disp.value); 160 | 161 | return ZYAN_STATUS_SUCCESS; 162 | } 163 | if (instruction->address_width == 64) 164 | { 165 | *result_address = (ZyanU64)(runtime_address + instruction->length + 166 | instruction->raw.disp.value); 167 | 168 | return ZYAN_STATUS_SUCCESS; 169 | } 170 | } 171 | 172 | // Relative branch instruction 173 | if (instruction->raw.imm[0].is_signed && instruction->raw.imm[0].is_relative) 174 | { 175 | *result_address = (ZyanU64)((ZyanI64)runtime_address + instruction->length + 176 | instruction->raw.imm[0].value.s); 177 | switch (instruction->machine_mode) 178 | { 179 | case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: 180 | case ZYDIS_MACHINE_MODE_LEGACY_16: 181 | case ZYDIS_MACHINE_MODE_REAL_16: 182 | case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: 183 | case ZYDIS_MACHINE_MODE_LEGACY_32: 184 | if (instruction->operand_width == 16) 185 | { 186 | *result_address &= 0xFFFF; 187 | } 188 | break; 189 | case ZYDIS_MACHINE_MODE_LONG_64: 190 | break; 191 | default: 192 | ZYAN_UNREACHABLE; 193 | } 194 | 195 | return ZYAN_STATUS_SUCCESS; 196 | } 197 | 198 | ZYAN_UNREACHABLE; 199 | } 200 | 201 | /* ---------------------------------------------------------------------------------------------- */ 202 | 203 | /* ============================================================================================== */ 204 | 205 | #ifdef __cplusplus 206 | } 207 | #endif 208 | 209 | #endif /* ZYREX_INTERNAL_UTILS_H */ 210 | -------------------------------------------------------------------------------- /include/Zyrex/Barrier.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_BARRIER_H 28 | #define ZYREX_BARRIER_H 29 | 30 | #include 31 | #include 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /* ============================================================================================== */ 38 | /* Enums and types */ 39 | /* ============================================================================================== */ 40 | 41 | /* ---------------------------------------------------------------------------------------------- */ 42 | /* General */ 43 | /* ---------------------------------------------------------------------------------------------- */ 44 | 45 | /** 46 | * Defines the `ZyrexBarrierHandle` data-type. 47 | */ 48 | typedef ZyanUPointer ZyrexBarrierHandle; 49 | 50 | /* ---------------------------------------------------------------------------------------------- */ 51 | 52 | /* ============================================================================================== */ 53 | /* Macros */ 54 | /* ============================================================================================== */ 55 | 56 | /* ---------------------------------------------------------------------------------------------- */ 57 | /* Helper macros */ 58 | /* ---------------------------------------------------------------------------------------------- */ 59 | 60 | /** 61 | * Tries to enter the barrier for the given hook and automatically returns from the callback 62 | * function after invoking the given `trampoline` in case of failure. 63 | * 64 | * @param handle The barrier hook handle 65 | * @param trampoline The trampoline. 66 | */ 67 | #define ZYREX_BARRIER_ENTER_FUNC(handle, trampoline, ...) \ 68 | if (ZyrexBarrierTryEnter(handle) != ZYAN_STATUS_TRUE) \ 69 | { \ 70 | return trampoline(__VA_ARGS__); \ 71 | } 72 | 73 | /** 74 | * Tries to enter the barrier for the given hook and automatically returns from the callback 75 | * function after invoking the given `trampoline` in case of failure. 76 | * 77 | * @param handle The barrier hook handle 78 | * @param trampoline The trampoline. 79 | */ 80 | #define ZYREX_BARRIER_ENTER_PROC(handle, trampoline, ...) \ 81 | if (ZyrexBarrierTryEnter(handle) != ZYAN_STATUS_TRUE) \ 82 | { \ 83 | trampoline(__VA_ARGS__); \ 84 | return; \ 85 | } 86 | 87 | /* ---------------------------------------------------------------------------------------------- */ 88 | 89 | /* ============================================================================================== */ 90 | /* Exported functions */ 91 | /* ============================================================================================== */ 92 | 93 | /* ---------------------------------------------------------------------------------------------- */ 94 | /* Initialization and finalization */ 95 | /* ---------------------------------------------------------------------------------------------- */ 96 | 97 | /** 98 | * Initializes the barrier system. 99 | * 100 | * @return A zyan status code. 101 | * 102 | * This function must be called, before using any of the other barrier API functions. 103 | * 104 | * Calling this function multiple times might lead to unexpected behavior. 105 | */ 106 | ZYREX_EXPORT ZyanStatus ZyrexBarrierSystemInitialize(void); 107 | 108 | /** 109 | * Finalizes the barrier system. 110 | * 111 | * @return A zyan status code. 112 | * 113 | * This function should be called before the current process exits. 114 | */ 115 | ZYREX_EXPORT ZyanStatus ZyrexBarrierSystemShutdown(void); 116 | 117 | /* ---------------------------------------------------------------------------------------------- */ 118 | /* Barrier */ 119 | /* ---------------------------------------------------------------------------------------------- */ 120 | 121 | /** 122 | * Returns the barrier handle for the hook that is identified by the given `trampoline`. 123 | * 124 | * @param trampoline The `trampoline` that identifies the hook for which the barrier handle 125 | * should be obtained. 126 | * 127 | * @return The barrier handle for the hook that is identified by the given `trampoline`. 128 | * 129 | * As the `trampoline` pointer might get modified from another thread (e.g. during hook removal), 130 | * it is necessary to obtain a constant barrier handle before invoking a barrier API function 131 | * inside a hook callback. 132 | * 133 | * The returned handle should be saved and then used for all subsequent calls to the barrier API 134 | * inside the current hook callback. 135 | */ 136 | ZYREX_EXPORT ZyrexBarrierHandle ZyrexBarrierGetHandle(const void* trampoline); 137 | 138 | /** 139 | * Tries to enter the barrier for the given hook. 140 | * 141 | * @param handle The barrier hook handle. 142 | * 143 | * @return `ZYAN_STATUS_TRUE` if the barrier has been passed, `ZYAN_STATUS_FALSE` if not, or a 144 | * generic zyan status code if an error occured. 145 | * 146 | * This function passes the barrier, if the `current_recursion_depth` is `0`. 147 | */ 148 | ZYREX_EXPORT ZyanStatus ZyrexBarrierTryEnter(ZyrexBarrierHandle handle); 149 | 150 | /** 151 | * Tries to enter the barrier for the given hook. 152 | * 153 | * @param handle The barrier hook handle. 154 | * @param max_recursion_depth The maximum recursion depth to pass the barrier. 155 | * 156 | * @return `ZYAN_STATUS_TRUE` if the barrier has been passed, `ZYAN_STATUS_FALSE` if not, or a 157 | * generic zyan status code if an error occured. 158 | * 159 | * This function passes the barrier, if the `current_recursion_depth` is less than or equal to 160 | * the given `max_recursion_depth`. 161 | */ 162 | ZYREX_EXPORT ZyanStatus ZyrexBarrierTryEnterEx(ZyrexBarrierHandle handle, 163 | ZyanU32 max_recursion_depth); 164 | 165 | /** 166 | * Leaves the barrier for the given hook. 167 | * 168 | * @param handle The barrier hook handle. 169 | * 170 | * @return A zyan status code. 171 | */ 172 | ZYREX_EXPORT ZyanStatus ZyrexBarrierLeave(ZyrexBarrierHandle handle); 173 | 174 | /* ---------------------------------------------------------------------------------------------- */ 175 | /* Utils */ 176 | /* ---------------------------------------------------------------------------------------------- */ 177 | 178 | /** 179 | * Returns the current recursion depth for the given hook. 180 | * 181 | * @param handle The barrier hook handle. 182 | * @param current_depth Receives the current recursion depth for the given hook or `0`, if no 183 | * barrier context was found. 184 | * 185 | * @return `ZYAN_STATUS_TRUE` if a barrier context was found for the given hook, 186 | * `ZYAN_STATUS_FALSE` if not, or a generic zyan status code if an error occurred. 187 | */ 188 | ZYREX_EXPORT ZyanStatus ZyrexBarrierGetRecursionDepth(ZyrexBarrierHandle handle, 189 | ZyanU32* current_depth); 190 | 191 | /* ---------------------------------------------------------------------------------------------- */ 192 | 193 | /* ============================================================================================== */ 194 | 195 | #ifdef __cplusplus 196 | } 197 | #endif 198 | 199 | #endif /* ZYREX_BARRIER_H */ 200 | -------------------------------------------------------------------------------- /include/Zyrex/Transaction.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_TRANSACTION_H 28 | #define ZYREX_TRANSACTION_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | /* ============================================================================================== */ 40 | /* Enums and types */ 41 | /* ============================================================================================== */ 42 | 43 | /* ---------------------------------------------------------------------------------------------- */ 44 | /* Hook type */ 45 | /* ---------------------------------------------------------------------------------------------- */ 46 | 47 | /** 48 | * @brief Defines the `ZyrexHookType` enum. 49 | */ 50 | typedef enum ZyrexHookType_ 51 | { 52 | /** 53 | * @brief Inline hook. 54 | * 55 | * The inline hook uses a `jmp` instruction at the begin of the target function to redirect 56 | * code-fow to the callback function. 57 | */ 58 | ZYREX_HOOK_TYPE_INLINE, 59 | /** 60 | * @brief Exception hook. 61 | * 62 | * The exception hook uses a privileged instruction at the begin of the target function to 63 | * cause an exception which is later catched by an unhandled-exception-handler that redirects 64 | * code-flow to the callback function by modifying the instruction-pointer register of the 65 | * calling thread. 66 | */ 67 | ZYREX_HOOK_TYPE_EXCEPTION, 68 | /** 69 | * @brief Context/HWBP hook. 70 | * 71 | * The context/HWBP hook modifies the x86-64 debug registers to cause an exception which is 72 | * later catched by an unhandled-exception-handler that redirects code-flow to the callback 73 | * function by modifying the instruction-pointer register of the calling thread. 74 | * 75 | * This hook 76 | */ 77 | ZYREX_HOOK_TYPE_CONTEXT 78 | } ZyrexHookType; 79 | 80 | /* ---------------------------------------------------------------------------------------------- */ 81 | /* Hook */ 82 | /* ---------------------------------------------------------------------------------------------- */ 83 | 84 | /** 85 | * @brief Defines the `ZyrexHook` struct. 86 | * 87 | * All fields in this struct should be considered as "private". Any changes may lead to unexpected 88 | * behavior. 89 | */ 90 | typedef struct ZyrexHook_ 91 | { 92 | /** 93 | * @brief The hook type - just for reference. 94 | */ 95 | ZyrexHookType type; 96 | /** 97 | * @brief The address of the hooked function. 98 | */ 99 | void* address; 100 | } ZyrexHook; 101 | 102 | /* ---------------------------------------------------------------------------------------------- */ 103 | /* Hook operation */ 104 | /* ---------------------------------------------------------------------------------------------- */ 105 | 106 | /* ---------------------------------------------------------------------------------------------- */ 107 | 108 | /* ============================================================================================== */ 109 | /* Exported functions */ 110 | /* ============================================================================================== */ 111 | 112 | /* ---------------------------------------------------------------------------------------------- */ 113 | /* Transaction */ 114 | /* ---------------------------------------------------------------------------------------------- */ 115 | 116 | /** 117 | * @brief Starts a new transaction. 118 | * 119 | * @return A zyan status code. 120 | */ 121 | ZYREX_EXPORT ZyanStatus ZyrexTransactionBegin(void); 122 | 123 | /** 124 | * @brief Adds a specific thread to the thread-update list. 125 | * 126 | * The given thread is immediately suspended and later on resumed after the transaction was either 127 | * been committed or canceled. 128 | * 129 | * @param thread_id The id of the thread to add to the update list. 130 | * 131 | * @return A zyan status code. 132 | */ 133 | ZYREX_EXPORT ZyanStatus ZyrexUpdateThread(ZyanThreadId thread_id); 134 | 135 | /** 136 | * @brief Adds all threads (except the calling one) to the update list. 137 | * 138 | * All threads are immediately suspended and later on resumed after the transaction was either 139 | * been committed or canceled. 140 | * 141 | * @return A zyan status code. 142 | */ 143 | ZYREX_EXPORT ZyanStatus ZyrexUpdateAllThreads(void); 144 | 145 | /** 146 | * @brief Commits the current transaction. 147 | * 148 | * This function performs the pending hook attach/remove operations and updates all threads in the 149 | * thread-update list. 150 | * 151 | * @return A zyan status code. 152 | */ 153 | ZYREX_EXPORT ZyanStatus ZyrexTransactionCommit(void); 154 | 155 | /** 156 | * @brief Commits the current transaction. 157 | * 158 | * @param failed_operation Receives a pointer to the operation that failed the transaction. 159 | * 160 | * @return A zyan status code. 161 | */ 162 | ZYREX_EXPORT ZyanStatus ZyrexTransactionCommitEx(const void** failed_operation); 163 | 164 | /** 165 | * @brief Cancels the current transaction. 166 | * 167 | * @return A zyan status code. 168 | */ 169 | ZYREX_EXPORT ZyanStatus ZyrexTransactionAbort(void); 170 | 171 | /* ---------------------------------------------------------------------------------------------- */ 172 | /* Hook installation */ 173 | /* ---------------------------------------------------------------------------------------------- */ 174 | 175 | /** 176 | * @brief Installs an inline hook at the given `address`. 177 | * 178 | * @param address The address to hook. 179 | * @param callback The callback address. 180 | * @param trampoline Receives the address of the trampoline to the original function, if the 181 | * operation succeeded. 182 | * 183 | * @return A zyan status code. 184 | */ 185 | ZYREX_EXPORT ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, 186 | ZyanConstVoidPointer* trampoline); 187 | 188 | ///** 189 | // * @brief Attaches an exception hook. 190 | // * 191 | // * @param address Pointer to the code address. Receives the trampoline address, if the 192 | // * transaction succeeded. 193 | // * @param callback The callback address. 194 | // * 195 | // * @return A zyan status code. 196 | // */ 197 | //ZYREX_EXPORT ZyanStatus ZyrexAttachExceptionHook(const void** address, const void* callback); 198 | // 199 | ///** 200 | // * @brief Attaches a context hook. 201 | // * 202 | // * @param address Pointer to the code address. Receives the trampoline address, if the 203 | // * transaction succeeded. 204 | // * @param callback The callback address. 205 | // * 206 | // * @return A zyan status code. 207 | // */ 208 | //ZYREX_EXPORT ZyanStatus ZyrexAttachContextHook(const void** address, const void* callback); 209 | 210 | // TODO: IAT/EAT, VTable, .. 211 | 212 | /* ---------------------------------------------------------------------------------------------- */ 213 | /* Hook removal */ 214 | /* ---------------------------------------------------------------------------------------------- */ 215 | 216 | /** 217 | * @brief Removes an inline hook at the given `address`. 218 | * 219 | * @param original A pointer to the trampoline address received during the hook attaching. 220 | * Receives the address of the original function after removing the hook. 221 | * 222 | * @return A zyan status code. 223 | */ 224 | ZYREX_EXPORT ZyanStatus ZyrexRemoveInlineHook(ZyanConstVoidPointer* original); 225 | 226 | /* ---------------------------------------------------------------------------------------------- */ 227 | 228 | /* ============================================================================================== */ 229 | 230 | #ifdef __cplusplus 231 | } 232 | #endif 233 | 234 | #endif /* ZYREX_TRANSACTION_H */ 235 | -------------------------------------------------------------------------------- /src/Barrier.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* ============================================================================================== */ 33 | /* Globals */ 34 | /* ============================================================================================== */ 35 | 36 | /** 37 | * The TLS slot used by the barrier system. 38 | */ 39 | static ZyanThreadTlsIndex g_barrier_tls_index = 0; 40 | 41 | /* ============================================================================================== */ 42 | /* Enums and types */ 43 | /* ============================================================================================== */ 44 | 45 | /* ---------------------------------------------------------------------------------------------- */ 46 | /* Barrier context */ 47 | /* ---------------------------------------------------------------------------------------------- */ 48 | 49 | /** 50 | * Defines the `ZyrexBarrierContext` struct. 51 | */ 52 | typedef struct ZyrexBarrierContext_ 53 | { 54 | /** 55 | * The barrier context id. 56 | */ 57 | ZyrexBarrierHandle id; 58 | /** 59 | * The current recursion depth. 60 | */ 61 | ZyanU32 recursion_depth; 62 | } ZyrexBarrierContext; 63 | 64 | /* ---------------------------------------------------------------------------------------------- */ 65 | 66 | /* ============================================================================================== */ 67 | /* Internal functions */ 68 | /* ============================================================================================== */ 69 | 70 | /* ---------------------------------------------------------------------------------------------- */ 71 | /* TLS cleanup */ 72 | /* ---------------------------------------------------------------------------------------------- */ 73 | 74 | /** 75 | * This function is invoked every time a thread exists. 76 | * 77 | * @param data The data currently stored in the TLS slot. 78 | */ 79 | ZYAN_THREAD_DECLARE_TLS_CALLBACK(ZyrexBarrierTlsCleanup, ZyanVector, data) 80 | { 81 | if (!data) 82 | { 83 | return; 84 | } 85 | 86 | ZyanVectorDestroy(data); 87 | 88 | // TODO: Replace with ZyanMemoryFree in the future 89 | ZYAN_FREE(data); 90 | } 91 | 92 | /* ---------------------------------------------------------------------------------------------- */ 93 | /* Barrier context */ 94 | /* ---------------------------------------------------------------------------------------------- */ 95 | 96 | /** 97 | * Defines a comparison function for the `ZyrexBarrierContext` struct that uses the `id` field to 98 | * create an absolute order. 99 | */ 100 | ZYAN_DECLARE_COMPARISON_FOR_FIELD(ZyrexBarrierCompareContext, ZyrexBarrierContext, id) 101 | 102 | /* ---------------------------------------------------------------------------------------------- */ 103 | 104 | /* ============================================================================================== */ 105 | /* Exported functions */ 106 | /* ============================================================================================== */ 107 | 108 | /* ---------------------------------------------------------------------------------------------- */ 109 | /* Initialization and finalization */ 110 | /* ---------------------------------------------------------------------------------------------- */ 111 | 112 | ZyanStatus ZyrexBarrierSystemInitialize() 113 | { 114 | return ZyanThreadTlsAlloc(&g_barrier_tls_index, (ZyanThreadTlsCallback)&ZyrexBarrierTlsCleanup); 115 | } 116 | 117 | ZyanStatus ZyrexBarrierSystemShutdown() 118 | { 119 | return ZyanThreadTlsFree(g_barrier_tls_index); 120 | } 121 | 122 | /* ---------------------------------------------------------------------------------------------- */ 123 | /* Barrier */ 124 | /* ---------------------------------------------------------------------------------------------- */ 125 | 126 | ZyrexBarrierHandle ZyrexBarrierGetHandle(const void* trampoline) 127 | { 128 | return (ZyrexBarrierHandle)trampoline; 129 | } 130 | 131 | ZyanStatus ZyrexBarrierTryEnter(ZyrexBarrierHandle handle) 132 | { 133 | return ZyrexBarrierTryEnterEx(handle, 0); 134 | } 135 | 136 | ZyanStatus ZyrexBarrierTryEnterEx(ZyrexBarrierHandle handle, ZyanU32 max_recursion_depth) 137 | { 138 | ZyanVector* vector; 139 | ZYAN_CHECK(ZyanThreadTlsGetValue(g_barrier_tls_index, (void*)&vector)); 140 | 141 | if (vector == ZYAN_NULL) 142 | { 143 | // TODO: Replace with ZyanMemoryAlloc in the future 144 | vector = ZYAN_MALLOC(sizeof(ZyanVector)); 145 | ZYAN_CHECK(ZyanVectorInit(vector, sizeof(ZyrexBarrierContext), 32, ZYAN_NULL)); 146 | ZYAN_CHECK(ZyanThreadTlsSetValue(g_barrier_tls_index, vector)); 147 | } 148 | 149 | ZyrexBarrierContext context_element; 150 | context_element.id = handle; 151 | 152 | ZyanUSize found_index; 153 | const ZyanStatus status = 154 | ZyanVectorBinarySearch(vector, &context_element, &found_index, 155 | (ZyanComparison)&ZyrexBarrierCompareContext); 156 | ZYAN_CHECK(status); 157 | 158 | if (status == ZYAN_STATUS_TRUE) 159 | { 160 | ZyrexBarrierContext* const context = 161 | (ZyrexBarrierContext*)ZyanVectorGetMutable(vector, found_index); 162 | if (context->recursion_depth > max_recursion_depth) 163 | { 164 | return ZYAN_STATUS_FALSE; 165 | } 166 | 167 | ++context->recursion_depth; 168 | return ZYAN_STATUS_TRUE; 169 | } 170 | 171 | context_element.recursion_depth = 1; 172 | ZYAN_CHECK(ZyanVectorInsert(vector, found_index, &context_element)); 173 | 174 | return ZYAN_STATUS_TRUE; 175 | } 176 | 177 | ZyanStatus ZyrexBarrierLeave(ZyrexBarrierHandle handle) 178 | { 179 | ZyanVector* vector; 180 | ZYAN_CHECK(ZyanThreadTlsGetValue(g_barrier_tls_index, (void*)&vector)); 181 | 182 | if (vector == ZYAN_NULL) 183 | { 184 | return ZYAN_STATUS_INVALID_OPERATION; 185 | } 186 | 187 | ZyrexBarrierContext context_element; 188 | context_element.id = handle; 189 | 190 | ZyanUSize found_index; 191 | const ZyanStatus status = 192 | ZyanVectorBinarySearch(vector, &context_element, &found_index, 193 | (ZyanComparison)&ZyrexBarrierCompareContext); 194 | ZYAN_CHECK(status); 195 | 196 | if (status != ZYAN_STATUS_TRUE) 197 | { 198 | return ZYAN_STATUS_INVALID_OPERATION; 199 | } 200 | 201 | ZyrexBarrierContext* const context = 202 | (ZyrexBarrierContext*)ZyanVectorGetMutable(vector, found_index); 203 | if (context->recursion_depth == 0) 204 | { 205 | return ZYAN_STATUS_INVALID_OPERATION; 206 | } 207 | 208 | if (--context->recursion_depth == 0) 209 | { 210 | ZYAN_CHECK(ZyanVectorDelete(vector, found_index)); 211 | } 212 | 213 | return ZYAN_STATUS_TRUE; 214 | } 215 | 216 | /* ---------------------------------------------------------------------------------------------- */ 217 | /* Utils */ 218 | /* ---------------------------------------------------------------------------------------------- */ 219 | 220 | ZyanStatus ZyrexBarrierGetRecursionDepth(ZyrexBarrierHandle handle, ZyanU32* current_depth) 221 | { 222 | ZyanVector* vector; 223 | ZYAN_CHECK(ZyanThreadTlsGetValue(g_barrier_tls_index, (void*)&vector)); 224 | 225 | if (vector == ZYAN_NULL) 226 | { 227 | *current_depth = 0; 228 | return ZYAN_STATUS_FALSE; 229 | } 230 | 231 | ZyrexBarrierContext context_element; 232 | context_element.id = handle; 233 | 234 | ZyanUSize found_index; 235 | const ZyanStatus status = 236 | ZyanVectorBinarySearch(vector, &context_element, &found_index, 237 | (ZyanComparison)&ZyrexBarrierCompareContext); 238 | ZYAN_CHECK(status); 239 | 240 | if (status == ZYAN_STATUS_TRUE) 241 | { 242 | const ZyrexBarrierContext* const context = 243 | (ZyrexBarrierContext*)ZyanVectorGetMutable(vector, found_index); 244 | 245 | *current_depth = context->recursion_depth; 246 | return ZYAN_STATUS_TRUE; 247 | } 248 | 249 | return ZYAN_STATUS_FALSE; 250 | } 251 | 252 | /* ---------------------------------------------------------------------------------------------- */ 253 | 254 | /* ============================================================================================== */ 255 | -------------------------------------------------------------------------------- /include/Zyrex/Internal/Trampoline.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #ifndef ZYREX_INTERNAL_TRAMPOLINE_H 28 | #define ZYREX_INTERNAL_TRAMPOLINE_H 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /* ============================================================================================== */ 39 | /* Constants */ 40 | /* ============================================================================================== */ 41 | 42 | /** 43 | * @brief Defines the maximum amount of instruction bytes that can be saved to a trampoline. 44 | * 45 | * This formula is based on the following edge case consideration: 46 | * - If `SIZEOF_SAVED_INSTRUCTIONS == SIZEOF_RELATIVE_JUMP - 1 == 4` 47 | * - We have to save exactly one additional instruction 48 | * - We already saved 4 bytes 49 | * - The additional instructions maximum length is 15 bytes 50 | */ 51 | #define ZYREX_TRAMPOLINE_MAX_CODE_SIZE \ 52 | (ZYDIS_MAX_INSTRUCTION_LENGTH + ZYREX_SIZEOF_RELATIVE_JUMP - 1) 53 | 54 | /** 55 | * @brief Defines an additional amount of bytes to reserve in the trampoline code buffer which 56 | * is required in order to rewrite certain kinds of instructions. 57 | */ 58 | #define ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS \ 59 | 8 60 | 61 | /** 62 | * @brief Defines the maximum amount of instruction bytes that can be saved to a trampoline 63 | * including the backjump. 64 | */ 65 | #define ZYREX_TRAMPOLINE_MAX_CODE_SIZE_WITH_BACKJUMP \ 66 | (ZYREX_TRAMPOLINE_MAX_CODE_SIZE + ZYREX_SIZEOF_ABSOLUTE_JUMP) 67 | 68 | /** 69 | * @brief Defines the maximum amount of instructions that can be saved to a trampoline. 70 | */ 71 | #define ZYREX_TRAMPOLINE_MAX_INSTRUCTION_COUNT \ 72 | (ZYREX_SIZEOF_RELATIVE_JUMP) 73 | 74 | /** 75 | * @brief Defines an additional amount of slots to reserve in the instruction translation map 76 | * which is required in order to rewrite certain kinds of instructions. 77 | */ 78 | #define ZYREX_TRAMPOLINE_MAX_INSTRUCTION_COUNT_BONUS \ 79 | 2 80 | 81 | /** 82 | * @brief Defines the trampoline region signature. 83 | * 84 | * Represents the character-sequence 'zrex' if interpreted as ASCII string. 85 | */ 86 | #define ZYREX_TRAMPOLINE_REGION_SIGNATURE 0x7A726578 87 | 88 | /* ============================================================================================== */ 89 | /* Enums and types */ 90 | /* ============================================================================================== */ 91 | 92 | /* ---------------------------------------------------------------------------------------------- */ 93 | /* Translation map */ 94 | /* ---------------------------------------------------------------------------------------------- */ 95 | 96 | /** 97 | * @brief Defines the `ZyrexInstructionTranslationType` enum. 98 | */ 99 | typedef enum ZyrexInstructionTranslationType_ 100 | { 101 | /** 102 | * @brief This item represents a normal instruction. 103 | */ 104 | ZYREX_TRANSLATION_TYPE_DEFAULT, 105 | ///** 106 | // * @brief This item represents either the jump instruction from the original code to the 107 | // * trampoline or the backjump instruction from the trampoline to the original code. 108 | // * 109 | // * Depending on the case, the destination offset or the source offset should be ignored and the respective destination address used instead. 110 | // */ 111 | //ZYREX_TRANSLATION_TYPE_REDIRECT 112 | } ZyrexInstructionTranslationType; 113 | 114 | /** 115 | * @brief Defines the `ZyrexInstructionTranslationItem` struct. 116 | */ 117 | typedef struct ZyrexInstructionTranslationItem_ 118 | { 119 | /** 120 | * @brief The type of the instruction translation item. 121 | */ 122 | ZyrexInstructionTranslationType type; 123 | /** 124 | * @brief The offset of a single instruction relative to the beginning of the source buffer. 125 | */ 126 | ZyanU8 offset_source; 127 | /** 128 | * @brief The offset of a single instruction relative to the beginning of the destination 129 | * buffer. 130 | */ 131 | ZyanU8 offset_destination; 132 | /** 133 | * @brief An absolute target address outside the destination buffer. 134 | */ 135 | ZyanUPointer target_address; 136 | } ZyrexInstructionTranslationItem; 137 | 138 | /** 139 | * @brief Defines the `ZyrexInstructionTranslationMap` struct. 140 | */ 141 | typedef struct ZyrexInstructionTranslationMap_ 142 | { 143 | /** 144 | * @brief The number of items in the translation map. 145 | */ 146 | ZyanU8 count; 147 | /** 148 | * @brief The translation items. 149 | */ 150 | ZyrexInstructionTranslationItem items[ZYREX_TRAMPOLINE_MAX_INSTRUCTION_COUNT + 151 | ZYREX_TRAMPOLINE_MAX_INSTRUCTION_COUNT_BONUS]; 152 | } ZyrexInstructionTranslationMap; 153 | 154 | /* ---------------------------------------------------------------------------------------------- */ 155 | /* Trampoline chunk */ 156 | /* ---------------------------------------------------------------------------------------------- */ 157 | 158 | /** 159 | * @brief Defines the `ZyrexTrampolineChunk` struct. 160 | */ 161 | typedef struct ZyrexTrampolineChunk_ 162 | { 163 | /** 164 | * @brief Signals, if the trampoline chunk is currently in use. 165 | */ 166 | ZyanBool is_used; 167 | /** 168 | * @brief The address of the callback function. 169 | */ 170 | ZyanUPointer callback_address; 171 | 172 | #if defined(ZYAN_X64) 173 | 174 | /** 175 | * @brief The absolute jump to the callback function. 176 | */ 177 | ZyanU8 callback_jump[ZYREX_SIZEOF_ABSOLUTE_JUMP]; 178 | 179 | #endif 180 | 181 | /** 182 | * @brief The backjump address. 183 | */ 184 | ZyanUPointer backjump_address; 185 | /** 186 | * @brief The buffer that holds the trampoline code and the backjump to the hooked function. 187 | */ 188 | ZyanU8 code_buffer[ZYREX_TRAMPOLINE_MAX_CODE_SIZE_WITH_BACKJUMP + 189 | ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS]; 190 | /** 191 | * @brief The number of instruction bytes in the code buffer (not counting the backjump 192 | * instruction). 193 | */ 194 | ZyanU8 code_buffer_size; 195 | /** 196 | * @brief The instruction translation map. 197 | */ 198 | ZyrexInstructionTranslationMap translation_map; 199 | /** 200 | * @brief The buffer that holds the original instruction bytes saved from the hooked function. 201 | */ 202 | ZyanU8 original_code[ZYREX_TRAMPOLINE_MAX_CODE_SIZE]; 203 | /** 204 | * @brief The number of instruction bytes saved from the hooked function. 205 | */ 206 | ZyanU8 original_code_size; 207 | } ZyrexTrampolineChunk; 208 | 209 | /* ---------------------------------------------------------------------------------------------- */ 210 | 211 | /* ============================================================================================== */ 212 | /* Functions */ 213 | /* ============================================================================================== */ 214 | 215 | /* ---------------------------------------------------------------------------------------------- */ 216 | /* Creation and destruction */ 217 | /* ---------------------------------------------------------------------------------------------- */ 218 | 219 | /** 220 | * @brief Creates a new trampoline. 221 | * 222 | * @param address The address of the function to create the trampoline for. 223 | * @param callback The address of the callback function the hook will redirect to. 224 | * @param min_bytes_to_reloc Specifies the minimum amount of bytes that need to be relocated 225 | * to the trampoline (usually equals the size of the branch 226 | * instruction used for hooking). 227 | * This function might copy more bytes on demand to keep individual 228 | * instructions intact. 229 | * @param trampoline Receives the newly created trampoline chunk. 230 | * 231 | * @return A zyan status code. 232 | */ 233 | ZyanStatus ZyrexTrampolineCreate(const void* address, const void* callback, 234 | ZyanUSize min_bytes_to_reloc, ZyrexTrampolineChunk** trampoline); 235 | 236 | /** 237 | * @brief Destroys the given trampoline. 238 | * 239 | * @param trampoline The trampoline chunk. 240 | * 241 | * @return A zyan status code. 242 | */ 243 | ZyanStatus ZyrexTrampolineFree(ZyrexTrampolineChunk* trampoline); 244 | 245 | /* ---------------------------------------------------------------------------------------------- */ 246 | /* Searching */ 247 | /* ---------------------------------------------------------------------------------------------- */ 248 | 249 | /** 250 | * @brief Searches for a trampoline chunk using the given `original`-function pointer. 251 | * 252 | * @param original A pointer to the original function. 253 | * @param trampoline Receives the corresponding trampoline chunk, if found. 254 | * 255 | * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or an other 256 | * zyan status code if an error occured. 257 | */ 258 | ZyanStatus ZyrexTrampolineFind(const void* original, ZyrexTrampolineChunk** trampoline); 259 | 260 | /* ---------------------------------------------------------------------------------------------- */ 261 | 262 | /* ============================================================================================== */ 263 | 264 | #ifdef __cplusplus 265 | } 266 | #endif 267 | 268 | #endif /* ZYREX_INTERNAL_TRAMPOLINE_H */ 269 | -------------------------------------------------------------------------------- /src/Transaction.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef ZYAN_WINDOWS 40 | # include 41 | # include 42 | #endif 43 | 44 | /* ============================================================================================== */ 45 | /* Enums and types */ 46 | /* ============================================================================================== */ 47 | 48 | /** 49 | * @brief Defines the `ZyrexOperationAction` enum. 50 | */ 51 | typedef enum ZyrexOperationAction_ 52 | { 53 | /** 54 | * @brief Attach action. 55 | */ 56 | ZYREX_OPERATION_ACTION_ATTACH, 57 | /** 58 | * @brief Removal action. 59 | */ 60 | ZYREX_OPERATION_ACTION_REMOVE 61 | } ZyrexOperationAction; 62 | 63 | /** 64 | * @brief Defines the `ZyrexOperation` struct. 65 | */ 66 | typedef struct ZyrexOperation_ 67 | { 68 | /** 69 | * @brief The hook type. 70 | */ 71 | ZyrexHookType type; 72 | /** 73 | * @brief The operation action. 74 | */ 75 | ZyrexOperationAction action; 76 | /** 77 | * @brief The code address. 78 | */ 79 | void* address; 80 | /** 81 | * @brief The trampoline chunk. 82 | */ 83 | ZyrexTrampolineChunk* trampoline; 84 | /** 85 | * @brief This value points to the memory that is passed by the user to store the trampoline 86 | * pointer. 87 | */ 88 | //ZyanConstVoidPointer* trampoline_accessor; 89 | } ZyrexOperation; 90 | 91 | /* ============================================================================================== */ 92 | /* Globals */ 93 | /* ============================================================================================== */ 94 | 95 | /** 96 | * @brief Contains global transaction data. 97 | */ 98 | static struct 99 | { 100 | /** 101 | * @brief The id of the thread that started the current transaction. 102 | */ 103 | volatile ZyanThreadId transaction_thread_id; 104 | /** 105 | * @brief A list with all pending operations. 106 | */ 107 | ZyanVector/**/ pending_operations; 108 | 109 | #ifdef ZYAN_WINDOWS 110 | 111 | /** 112 | * @brief A list with all threads to update. 113 | */ 114 | ZyanVector/**/ threads_to_update; 115 | 116 | #endif 117 | } g_transaction_data = 118 | { 119 | 0, ZYAN_VECTOR_INITIALIZER, 120 | #ifdef ZYAN_WINDOWS 121 | ZYAN_VECTOR_INITIALIZER 122 | #endif 123 | }; 124 | 125 | /* ============================================================================================== */ 126 | /* Internal functions */ 127 | /* ============================================================================================== */ 128 | 129 | /* ---------------------------------------------------------------------------------------------- */ 130 | /* ZyanVector */ 131 | /* ---------------------------------------------------------------------------------------------- */ 132 | 133 | #ifdef ZYAN_WINDOWS 134 | 135 | /** 136 | * @brief Finalizes the given `HANDLE` item. 137 | * 138 | * @param item A pointer to the `HANDLE` item. 139 | */ 140 | static void ZyrexWindowsHandleDestroy(HANDLE* item) 141 | { 142 | ZYAN_ASSERT(item); 143 | 144 | CloseHandle(*item); 145 | } 146 | 147 | #endif 148 | 149 | /* ---------------------------------------------------------------------------------------------- */ 150 | /* Code Patching */ 151 | /* ---------------------------------------------------------------------------------------------- */ 152 | 153 | /** 154 | * @brief Writes the hook jump which redirects the code-flow from the given `address` to the 155 | * `trampoline`. 156 | * 157 | * @param address The target address. 158 | * @param trampoline A pointer to the `ZyrexTrampolineChunk` struct. 159 | * 160 | * @return A zyan status code. 161 | */ 162 | static ZyanStatus ZyrexWriteHookJump(void* address, const ZyrexTrampolineChunk* trampoline) 163 | { 164 | ZYAN_ASSERT(address); 165 | ZYAN_ASSERT(trampoline); 166 | 167 | ZYAN_CHECK(ZyanMemoryVirtualProtect(address, ZYREX_SIZEOF_RELATIVE_JUMP, 168 | ZYAN_PAGE_EXECUTE_READWRITE)); 169 | 170 | #if defined(ZYAN_X64) 171 | 172 | ZyrexWriteRelativeJump(address, (ZyanUPointer)&trampoline->callback_jump); 173 | 174 | #elif defined(ZYAN_X86) 175 | 176 | ZyrexWriteRelativeJump(address, (ZyanUPointer)trampoline->callback_address); 177 | 178 | #else 179 | # error "Unsupported platform" 180 | #endif 181 | 182 | // TODO: Restore actual protection 183 | ZYAN_CHECK(ZyanMemoryVirtualProtect(address, ZYREX_SIZEOF_RELATIVE_JUMP, 184 | ZYAN_PAGE_EXECUTE_READ)); 185 | 186 | return ZyanProcessFlushInstructionCache(address, ZYREX_SIZEOF_RELATIVE_JUMP); 187 | } 188 | 189 | /** 190 | * @brief Reads the original code instructions from the `trampoline` and restores them to the 191 | * given `address`. 192 | * 193 | * @param address The target address. 194 | * @param trampoline A pointer to the `ZyrexTrampolineChunk` struct. 195 | * 196 | * @return A zyan status code. 197 | */ 198 | static ZyanStatus ZyrexRestoreInstructions(void* address, const ZyrexTrampolineChunk* trampoline) 199 | { 200 | ZYAN_CHECK(ZyanMemoryVirtualProtect(address, ZYREX_SIZEOF_RELATIVE_JUMP, 201 | ZYAN_PAGE_EXECUTE_READWRITE)); 202 | 203 | ZYAN_MEMCPY(address, &trampoline->original_code, trampoline->original_code_size); 204 | 205 | // TODO: Restore actual protection 206 | ZYAN_CHECK(ZyanMemoryVirtualProtect(address, ZYREX_SIZEOF_RELATIVE_JUMP, 207 | ZYAN_PAGE_EXECUTE_READ)); 208 | 209 | return ZyanProcessFlushInstructionCache(address, ZYREX_SIZEOF_RELATIVE_JUMP); 210 | } 211 | 212 | /* ---------------------------------------------------------------------------------------------- */ 213 | 214 | /* ============================================================================================== */ 215 | /* Exported functions */ 216 | /* ============================================================================================== */ 217 | 218 | /* ---------------------------------------------------------------------------------------------- */ 219 | /* Transaction */ 220 | /* ---------------------------------------------------------------------------------------------- */ 221 | 222 | ZyanStatus ZyrexTransactionBegin(void) 223 | { 224 | if (g_transaction_data.transaction_thread_id != 0) 225 | { 226 | return ZYAN_STATUS_INVALID_OPERATION; 227 | } 228 | 229 | #ifdef ZYAN_WINDOWS 230 | 231 | // TODO: Use platform independent APIs 232 | if (InterlockedCompareExchange((volatile LONG*)&g_transaction_data.transaction_thread_id, 233 | (LONG)GetCurrentThreadId(), 0) != 0) 234 | { 235 | return ZYAN_STATUS_INVALID_OPERATION; 236 | } 237 | 238 | #else 239 | 240 | ZyanThreadId tid; 241 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 242 | g_transaction_data.transaction_thread_id = tid; 243 | 244 | #endif 245 | 246 | ZYAN_CHECK(ZyanVectorInit(&g_transaction_data.pending_operations, sizeof(ZyrexOperation), 247 | 16, ZYAN_NULL)); 248 | 249 | #ifdef ZYAN_WINDOWS 250 | 251 | const ZyanStatus status = ZyanVectorInit(&g_transaction_data.threads_to_update, 252 | sizeof(HANDLE), 16, (ZyanMemberProcedure)&ZyrexWindowsHandleDestroy); 253 | if (!ZYAN_SUCCESS(status)) 254 | { 255 | ZyanVectorDestroy(&g_transaction_data.pending_operations); 256 | return status; 257 | } 258 | 259 | #endif 260 | 261 | return ZYAN_STATUS_SUCCESS; 262 | } 263 | 264 | ZyanStatus ZyrexUpdateThread(ZyanThreadId thread_id) 265 | { 266 | ZyanThreadId tid; 267 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 268 | 269 | if (g_transaction_data.transaction_thread_id != tid) 270 | { 271 | return ZYAN_STATUS_INVALID_OPERATION; 272 | } 273 | 274 | #ifdef ZYAN_WINDOWS 275 | 276 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 277 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 278 | 279 | if (thread_id == GetCurrentThreadId()) 280 | { 281 | return ZYAN_STATUS_SUCCESS; 282 | } 283 | 284 | const DWORD desired_access = THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT; 285 | const HANDLE handle = OpenThread(desired_access, ZYAN_FALSE, thread_id); 286 | if (handle == ZYAN_NULL) 287 | { 288 | return ZYAN_STATUS_INVALID_ARGUMENT; 289 | } 290 | if (SuspendThread(handle) == (DWORD)(-1)) 291 | { 292 | CloseHandle(handle); 293 | return ZYAN_STATUS_BAD_SYSTEMCALL; 294 | } 295 | 296 | return ZyanVectorPushBack(&g_transaction_data.threads_to_update, &handle); 297 | 298 | #else 299 | 300 | ZYAN_UNUSED(thread_id); 301 | return ZYAN_STATUS_SUCCESS; 302 | 303 | #endif 304 | } 305 | 306 | ZyanStatus ZyrexUpdateAllThreads(void) 307 | { 308 | ZyanThreadId tid; 309 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 310 | 311 | if (g_transaction_data.transaction_thread_id != tid) 312 | { 313 | return ZYAN_STATUS_INVALID_OPERATION; 314 | } 315 | 316 | #ifdef ZYAN_WINDOWS 317 | 318 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 319 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 320 | 321 | const DWORD pid = GetCurrentProcessId(); 322 | 323 | const HANDLE h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid); 324 | if (h_snapshot == INVALID_HANDLE_VALUE) 325 | { 326 | return ZYAN_STATUS_BAD_SYSTEMCALL; 327 | } 328 | 329 | THREADENTRY32 thread; 330 | ZYAN_MEMSET(&thread, 0, sizeof(thread)); 331 | thread.dwSize = sizeof(thread); 332 | 333 | if (Thread32First(h_snapshot, &thread)) 334 | { 335 | do 336 | { 337 | if ((thread.th32OwnerProcessID == pid) && (thread.th32ThreadID != tid)) 338 | { 339 | const HANDLE h_thread = 340 | OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, 341 | ZYAN_FALSE, thread.th32ThreadID); 342 | if (h_thread != ZYAN_NULL) 343 | { 344 | if (SuspendThread(h_thread) == (DWORD)(-1)) 345 | { 346 | CloseHandle(h_thread); 347 | } 348 | else 349 | { 350 | ZyanVectorPushBack(&g_transaction_data.threads_to_update, &h_thread); 351 | } 352 | } 353 | } 354 | } while (Thread32Next(h_snapshot, &thread)); 355 | } 356 | 357 | if (!CloseHandle(h_snapshot)) 358 | { 359 | return ZYAN_STATUS_BAD_SYSTEMCALL; 360 | } 361 | 362 | #endif 363 | 364 | return ZYAN_STATUS_SUCCESS; 365 | } 366 | 367 | ZyanStatus ZyrexTransactionCommit(void) 368 | { 369 | return ZyrexTransactionCommitEx(NULL); 370 | } 371 | 372 | ZyanStatus ZyrexTransactionCommitEx(const void** failed_operation) 373 | { 374 | ZYAN_UNUSED(failed_operation); 375 | 376 | ZyanThreadId tid; 377 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 378 | 379 | if (g_transaction_data.transaction_thread_id != tid) 380 | { 381 | return ZYAN_STATUS_INVALID_OPERATION; 382 | } 383 | 384 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 385 | #ifdef ZYAN_WINDOWS 386 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 387 | #endif 388 | 389 | ZyanISize revert_index = (ZyanISize)(-1); 390 | ZyanStatus status = ZYAN_STATUS_SUCCESS; 391 | for (ZyanISize i = 0; i < (ZyanISize)g_transaction_data.pending_operations.size; ++i) 392 | { 393 | const ZyrexOperation* item = ZyanVectorGet(&g_transaction_data.pending_operations, i); 394 | ZYAN_ASSERT(item); 395 | 396 | switch (item->type) 397 | { 398 | case ZYREX_HOOK_TYPE_INLINE: 399 | switch (item->action) 400 | { 401 | case ZYREX_OPERATION_ACTION_ATTACH: 402 | { 403 | #ifdef ZYAN_WINDOWS 404 | 405 | for (ZyanISize j = 0; j < (ZyanISize)g_transaction_data.threads_to_update.size; 406 | ++j) 407 | { 408 | const HANDLE* const thread_handle = 409 | (const HANDLE*)ZyanVectorGet(&g_transaction_data.threads_to_update, j); 410 | ZYAN_ASSERT(thread_handle); 411 | 412 | // TODO: Handle status code 413 | ZyrexMigrateThread(*thread_handle, item->address, 414 | item->trampoline->original_code_size, &item->trampoline->code_buffer, 415 | item->trampoline->code_buffer_size, &item->trampoline->translation_map, 416 | ZYREX_THREAD_MIGRATION_DIRECTION_SRC_DST); 417 | } 418 | 419 | #endif 420 | 421 | // TODO: Check if code has changed between this call and the Attach* 422 | status = ZyrexWriteHookJump(item->address, item->trampoline); 423 | break; 424 | } 425 | case ZYREX_OPERATION_ACTION_REMOVE: 426 | { 427 | #ifdef ZYAN_WINDOWS 428 | 429 | for (ZyanISize j = 0; j < (ZyanISize)g_transaction_data.threads_to_update.size; 430 | ++j) 431 | { 432 | const HANDLE* const thread_handle = 433 | (const HANDLE*)ZyanVectorGet(&g_transaction_data.threads_to_update, j); 434 | ZYAN_ASSERT(thread_handle); 435 | 436 | // TODO: Handle status code 437 | ZyrexMigrateThread(*thread_handle, &item->trampoline->code_buffer, 438 | item->trampoline->code_buffer_size, item->address, 439 | item->trampoline->original_code_size, &item->trampoline->translation_map, 440 | ZYREX_THREAD_MIGRATION_DIRECTION_DST_SRC); 441 | } 442 | 443 | #endif 444 | 445 | status = ZyrexRestoreInstructions(item->address, item->trampoline); 446 | if (ZYAN_SUCCESS(status)) 447 | { 448 | status = ZyrexTrampolineFree(item->trampoline); 449 | if (status == ZYAN_STATUS_FALSE) 450 | { 451 | status = ZYAN_STATUS_NOT_FOUND; 452 | } 453 | } 454 | break; 455 | } 456 | default: 457 | ZYAN_UNREACHABLE; 458 | } 459 | break; 460 | case ZYREX_HOOK_TYPE_EXCEPTION: 461 | break; 462 | case ZYREX_HOOK_TYPE_CONTEXT: 463 | break; 464 | default: 465 | ZYAN_UNREACHABLE; 466 | } 467 | 468 | if (!ZYAN_SUCCESS(status)) 469 | { 470 | revert_index = i - 1; 471 | break; 472 | } 473 | } 474 | 475 | if (revert_index >= 0) 476 | { 477 | // TODO: Revert changes 478 | } 479 | 480 | #ifdef ZYAN_WINDOWS 481 | 482 | ZyanVectorDestroy(&g_transaction_data.threads_to_update); 483 | 484 | #endif 485 | 486 | ZyanVectorDestroy(&g_transaction_data.pending_operations); 487 | g_transaction_data.transaction_thread_id = 0; 488 | 489 | return ZYAN_STATUS_SUCCESS; 490 | } 491 | 492 | ZyanStatus ZyrexTransactionAbort(void) 493 | { 494 | ZyanThreadId tid; 495 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 496 | 497 | if (g_transaction_data.transaction_thread_id != tid) 498 | { 499 | return ZYAN_STATUS_INVALID_OPERATION; 500 | } 501 | 502 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 503 | #ifdef ZYAN_WINDOWS 504 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 505 | #endif 506 | 507 | ZYAN_VECTOR_FOREACH_MUTABLE(const ZyrexOperation, &g_transaction_data.pending_operations, 508 | operation, 509 | { 510 | ZyrexTrampolineFree(operation->trampoline); 511 | }); 512 | 513 | #ifdef ZYAN_WINDOWS 514 | 515 | ZYAN_VECTOR_FOREACH(HANDLE, &g_transaction_data.threads_to_update, handle, 516 | { 517 | ResumeThread(handle); 518 | }); 519 | 520 | ZyanVectorDestroy(&g_transaction_data.threads_to_update); 521 | 522 | #endif 523 | 524 | ZyanVectorDestroy(&g_transaction_data.pending_operations); 525 | g_transaction_data.transaction_thread_id = 0; 526 | 527 | return ZYAN_STATUS_SUCCESS; 528 | } 529 | 530 | /* ---------------------------------------------------------------------------------------------- */ 531 | /* Hook installation */ 532 | /* ---------------------------------------------------------------------------------------------- */ 533 | 534 | ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, 535 | ZyanConstVoidPointer* trampoline) 536 | { 537 | if (!address || !callback || !trampoline) 538 | { 539 | return ZYAN_STATUS_INVALID_ARGUMENT; 540 | } 541 | 542 | ZyanThreadId tid; 543 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 544 | 545 | if (g_transaction_data.transaction_thread_id != tid) 546 | { 547 | return ZYAN_STATUS_INVALID_OPERATION; 548 | } 549 | 550 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 551 | #ifdef ZYAN_WINDOWS 552 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 553 | #endif 554 | 555 | ZyrexOperation operation = 556 | { 557 | /* type */ ZYREX_HOOK_TYPE_INLINE, 558 | /* action */ ZYREX_OPERATION_ACTION_ATTACH, 559 | /* address */ ZYAN_NULL, 560 | /* trampoline */ ZYAN_NULL 561 | }; 562 | operation.address = address; 563 | ZYAN_CHECK(ZyrexTrampolineCreate(address, callback, ZYREX_SIZEOF_RELATIVE_JUMP, 564 | &operation.trampoline)); 565 | 566 | *trampoline = &operation.trampoline->code_buffer; 567 | 568 | return ZyanVectorPushBack(&g_transaction_data.pending_operations, &operation); 569 | } 570 | 571 | /* ---------------------------------------------------------------------------------------------- */ 572 | /* Hook installation */ 573 | /* ---------------------------------------------------------------------------------------------- */ 574 | 575 | ZyanStatus ZyrexRemoveInlineHook(ZyanConstVoidPointer* original) 576 | { 577 | ZyanThreadId tid; 578 | ZYAN_CHECK(ZyanThreadGetCurrentThreadId(&tid)); 579 | 580 | if (g_transaction_data.transaction_thread_id != tid) 581 | { 582 | return ZYAN_STATUS_INVALID_OPERATION; 583 | } 584 | 585 | ZYAN_ASSERT(g_transaction_data.pending_operations.data); 586 | #ifdef ZYAN_WINDOWS 587 | ZYAN_ASSERT(g_transaction_data.threads_to_update.data); 588 | #endif 589 | 590 | ZyrexTrampolineChunk* trampoline; 591 | ZYAN_CHECK(ZyrexTrampolineFind(*original, &trampoline)); 592 | 593 | ZyanVoidPointer const target = 594 | (ZyanVoidPointer)(trampoline->backjump_address - trampoline->original_code_size); 595 | 596 | ZyrexOperation operation = 597 | { 598 | /* type */ ZYREX_HOOK_TYPE_INLINE, 599 | /* action */ ZYREX_OPERATION_ACTION_REMOVE, 600 | /* address */ ZYAN_NULL, 601 | /* trampoline */ ZYAN_NULL 602 | }; 603 | operation.address = target; 604 | operation.trampoline = trampoline; 605 | 606 | *original = target; 607 | 608 | return ZyanVectorPushBack(&g_transaction_data.pending_operations, &operation); 609 | } 610 | 611 | /* ---------------------------------------------------------------------------------------------- */ 612 | 613 | /* ============================================================================================== */ 614 | -------------------------------------------------------------------------------- /src/Trampoline.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #if defined(ZYAN_WINDOWS) 37 | # include 38 | #elif defined(ZYAN_POSIX) 39 | # include 40 | # include 41 | #else 42 | # error "Unsupported platform detected" 43 | #endif 44 | 45 | /* ============================================================================================== */ 46 | /* Enums and types */ 47 | /* ============================================================================================== */ 48 | 49 | /* ---------------------------------------------------------------------------------------------- */ 50 | /* Trampoline region */ 51 | /* ---------------------------------------------------------------------------------------------- */ 52 | 53 | /** 54 | * @brief Defines the `ZyrexTrampolineRegion` union. 55 | * 56 | * Note that the header shares memory with the first chunk in the trampoline-region. 57 | */ 58 | typedef union ZyrexTrampolineRegion_ 59 | { 60 | /** 61 | * @brief The header of the trampoline-region. 62 | */ 63 | struct 64 | { 65 | /** 66 | * @brief The signature of the trampoline-region. 67 | */ 68 | ZyanU32 signature; 69 | /** 70 | * @rief The number of unused trampoline-chunks. 71 | */ 72 | ZyanUSize number_of_unused_chunks; 73 | } header; 74 | /** 75 | * @brief The trampoline-chunks. 76 | */ 77 | ZyrexTrampolineChunk chunks[1]; 78 | } ZyrexTrampolineRegion; 79 | 80 | ZYAN_STATIC_ASSERT(sizeof(ZyrexTrampolineRegion) == sizeof(ZyrexTrampolineChunk)); 81 | 82 | /* ---------------------------------------------------------------------------------------------- */ 83 | 84 | /* ============================================================================================== */ 85 | /* Globals */ 86 | /* ============================================================================================== */ 87 | 88 | /** 89 | * @brief Contains global trampoline API data. 90 | * 91 | * Thread-safety is implicitly guaranteed by the transactional API as only one transaction can be 92 | * started at a time. 93 | */ 94 | static struct 95 | { 96 | /** 97 | * @brief Signals, if the trampoline API is initialized. 98 | */ 99 | ZyanBool is_initialized; 100 | /** 101 | * @brief The size of a trampoline-region. 102 | * 103 | * This value is platform specific and defaults to the allocation-granularity on Windows and 104 | * the page-size on most other platforms. 105 | */ 106 | ZyanUSize region_size; 107 | /** 108 | * @brief The maximum amount of chunks per trampoline-region. 109 | */ 110 | ZyanUSize chunks_per_region; 111 | /** 112 | * @brief Contains a list of all allocated trampoline-regions. 113 | */ 114 | ZyanVector regions; 115 | } g_trampoline_data = 116 | { 117 | ZYAN_FALSE, 0, 0, ZYAN_VECTOR_INITIALIZER 118 | }; 119 | 120 | /* ============================================================================================== */ 121 | /* Internal functions */ 122 | /* ============================================================================================== */ 123 | 124 | /* ---------------------------------------------------------------------------------------------- */ 125 | /* Helper functions */ 126 | /* ---------------------------------------------------------------------------------------------- */ 127 | 128 | #ifdef ZYAN_WINDOWS 129 | 130 | /** 131 | * @brief Returns the amount of bytes that can be read from the memory region starting at the 132 | * given `address` up to a maximum size of `size`. 133 | * 134 | * @param address The memory address. 135 | * @param size Receives the amount of bytes that can be read from the memory region which 136 | * contains `address` and defines the upper limit. 137 | * 138 | * @return A zyan status code. 139 | * 140 | * This function is used to avoid invalid memory access. Note that this can not be guaranteed in 141 | * a preemptive multi-threading environment. 142 | */ 143 | static ZyanStatus ZyrexGetSizeOfReadableMemoryRegion(const void* address, ZyanUSize* size) 144 | { 145 | ZYAN_ASSERT(address); 146 | ZYAN_ASSERT(size); 147 | 148 | static const DWORD read_mask = 149 | PAGE_EXECUTE_READ | 150 | PAGE_EXECUTE_READWRITE | 151 | PAGE_EXECUTE_WRITECOPY | 152 | PAGE_READONLY | 153 | PAGE_READWRITE | 154 | PAGE_WRITECOPY; 155 | 156 | MEMORY_BASIC_INFORMATION info; 157 | ZyanU8* current_address = (ZyanU8*)address; 158 | ZyanUSize current_size = 0; 159 | while (current_size < *size) 160 | { 161 | ZYAN_MEMSET(&info, 0, sizeof(info)); 162 | if (!VirtualQuery(current_address, &info, sizeof(info))) 163 | { 164 | return ZYAN_STATUS_BAD_SYSTEMCALL; 165 | } 166 | if ((info.State != MEM_COMMIT) || !(info.Protect & read_mask)) 167 | { 168 | *size = current_size; 169 | return ZYAN_STATUS_SUCCESS; 170 | } 171 | current_address = (ZyanU8*)info.BaseAddress + info.RegionSize; 172 | if (current_size == 0) 173 | { 174 | current_size = (ZyanUPointer)current_address - (ZyanUPointer)address; 175 | continue; 176 | } 177 | current_size += info.RegionSize; 178 | } 179 | 180 | return ZYAN_STATUS_SUCCESS; 181 | } 182 | 183 | #endif 184 | 185 | /* ---------------------------------------------------------------------------------------------- */ 186 | 187 | #ifdef ZYAN_X64 188 | 189 | /** 190 | * @brief Decodes the assembly code in the given `buffer` and returns the lowest and highest 191 | * absolute target addresses of all relative branch instructions and `EIP/RIP`-relative 192 | * memory operands. 193 | * 194 | * @param buffer The buffer to decode. 195 | * @param size The size of the buffer. 196 | * @param min_bytes_to_decode The minimum amount of bytes to decode. 197 | * @param address_lo Receives the lowest absolute target address. 198 | * @param address_hi Receives the highest absolute target address. 199 | * 200 | * @return `ZYAN_STATUS_TRUE` if at least one instruction with an relative address was found, 201 | * `ZYAN_STATUS_FALSE` if not, or a generic zyan status code if an error occured. 202 | */ 203 | static ZyanStatus ZyrexGetAddressRangeOfRelativeInstructions(const void* buffer, ZyanUSize size, 204 | ZyanUSize min_bytes_to_decode, ZyanUPointer* address_lo, ZyanUPointer* address_hi) 205 | { 206 | ZyanStatus result = ZYAN_STATUS_FALSE; 207 | 208 | ZydisDecoder decoder; 209 | #if defined(ZYAN_X86) 210 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_COMPAT_32, ZYDIS_STACK_WIDTH_32); 211 | #elif defined(ZYAN_X64) 212 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); 213 | #else 214 | # error "Unsupported architecture detected" 215 | #endif 216 | 217 | ZyanUPointer lo = (ZyanUPointer)(-1); 218 | ZyanUPointer hi = 0; 219 | ZydisDecodedInstruction instruction; 220 | ZyanUSize offset = 0; 221 | while (offset < min_bytes_to_decode) 222 | { 223 | const ZyanStatus status = ZydisDecoderDecodeInstruction(&decoder, ZYAN_NULL, 224 | (ZyanU8*)buffer + offset, size - offset, &instruction); 225 | 226 | ZYAN_CHECK(status); 227 | 228 | if (!(instruction.attributes & ZYDIS_ATTRIB_IS_RELATIVE)) 229 | { 230 | offset += instruction.length; 231 | continue; 232 | } 233 | result = ZYAN_STATUS_TRUE; 234 | 235 | ZyanU64 result_address; 236 | ZYAN_CHECK(ZyrexCalcAbsoluteAddress(&instruction, (ZyanU64)buffer + offset, 237 | &result_address)); 238 | 239 | if (result_address < lo) 240 | { 241 | lo = result_address; 242 | } 243 | if (result_address > hi) 244 | { 245 | hi = result_address; 246 | } 247 | 248 | offset += instruction.length; 249 | } 250 | 251 | if (result == ZYAN_STATUS_TRUE) 252 | { 253 | *address_lo = lo; 254 | *address_hi = hi; 255 | } 256 | 257 | return result; 258 | } 259 | 260 | #endif 261 | 262 | /* ---------------------------------------------------------------------------------------------- */ 263 | /* Trampoline region */ 264 | /* ---------------------------------------------------------------------------------------------- */ 265 | 266 | /** 267 | * @brief Checks, if the given region is in a +/-2GiB range to both passed address values. 268 | * 269 | * @param region_address The base address of the trampoline region to check. 270 | * @param address_lo The memory address lower bound to be used as condition. 271 | * @param address_hi The memory address upper bound to be used as condition. 272 | * 273 | * @return `ZYAN_STATUS_TRUE` if both address values are in range, 274 | * `ZYAN_STATUS_FALSE` if only one address value is in range, or 275 | * `ZYAN_STATUS_OUT_OF_RANGE`, if both address values are out of range. 276 | */ 277 | static ZyanBool ZyrexTrampolineRegionInRange(ZyanUPointer region_address, 278 | ZyanUPointer address_lo, ZyanUPointer address_hi) 279 | { 280 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 281 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO(region_address, g_trampoline_data.region_size)); 282 | 283 | // Skip the first chunk as it shares memory with the region-header 284 | const ZyanIPointer region_base = region_address + sizeof(ZyrexTrampolineChunk); 285 | 286 | const ZyanIPointer distance_lo = 287 | region_base - (ZyanIPointer)address_lo + (ZyanIPointer)address_lo < region_base 288 | ? sizeof(ZyrexTrampolineChunk) 289 | : sizeof(ZyrexTrampolineChunk) * (g_trampoline_data.chunks_per_region - 1); 290 | if ((ZYAN_ABS(distance_lo) > ZYREX_RANGEOF_RELATIVE_JUMP)) 291 | { 292 | return ZYAN_FALSE; 293 | } 294 | 295 | const ZyanIPointer distance_hi = 296 | region_base - (ZyanIPointer)address_hi + (ZyanIPointer)address_hi < region_base 297 | ? sizeof(ZyrexTrampolineChunk) 298 | : sizeof(ZyrexTrampolineChunk) * (g_trampoline_data.chunks_per_region - 1); 299 | if ((ZYAN_ABS(distance_hi) > ZYREX_RANGEOF_RELATIVE_JUMP)) 300 | { 301 | return ZYAN_FALSE; 302 | } 303 | 304 | return ZYAN_TRUE; 305 | } 306 | 307 | /** 308 | * @brief Searches the given trampoline-region for an unused `ZyrexTrampolineChunk` item that 309 | * lies in a +/-2GiB range to both given addresses. 310 | * 311 | * @param region A pointer to the `ZyrexTrampolineRegion` struct. 312 | * @param address_lo The memory address lower bound to be used as search condition. 313 | * @param address_hi The memory address upper bound to be used as search condition. 314 | * @param chunk Receives a pointer to a matching `ZyrexTrampolineChunk` struct. 315 | */ 316 | static ZyanBool ZyrexTrampolineRegionFindChunkInRegion(ZyrexTrampolineRegion* region, 317 | ZyanUPointer address_lo, ZyanUPointer address_hi, ZyrexTrampolineChunk** chunk) 318 | { 319 | ZYAN_ASSERT(region); 320 | ZYAN_ASSERT(chunk); 321 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 322 | 323 | if (region->header.number_of_unused_chunks == 0) 324 | { 325 | return ZYAN_FALSE; 326 | } 327 | 328 | if (!ZyrexTrampolineRegionInRange((ZyanUPointer)region, address_lo, address_hi)) 329 | { 330 | return ZYAN_FALSE; 331 | } 332 | 333 | // Skip the first chunk as it shares memory with the region-header 334 | for (ZyanUSize i = 1; i < g_trampoline_data.chunks_per_region; ++i) 335 | { 336 | if (region->chunks[i].is_used) 337 | { 338 | continue; 339 | } 340 | 341 | const ZyanIPointer chunk_base = (ZyanIPointer)®ion->chunks[i]; 342 | 343 | const ZyanIPointer distance_lo_chunk = chunk_base - (ZyanIPointer)address_lo + 344 | (((ZyanIPointer)address_lo < chunk_base) ? sizeof(ZyrexTrampolineChunk) : 0); 345 | if ((ZYAN_ABS(distance_lo_chunk) > ZYREX_RANGEOF_RELATIVE_JUMP)) 346 | { 347 | continue; 348 | } 349 | 350 | const ZyanIPointer distance_hi_chunk = chunk_base - (ZyanIPointer)address_hi + 351 | (((ZyanIPointer)address_hi < chunk_base) ? sizeof(ZyrexTrampolineChunk) : 0); 352 | if ((ZYAN_ABS(distance_hi_chunk) > ZYREX_RANGEOF_RELATIVE_JUMP)) 353 | { 354 | continue; 355 | } 356 | 357 | *chunk = ®ion->chunks[i]; 358 | return ZYAN_TRUE; 359 | } 360 | 361 | return ZYAN_FALSE; 362 | } 363 | 364 | /** 365 | * @brief Searches the global trampoline-region list for an unused `ZyrexTrampolineChunk` item 366 | * that lies in a +/-2GiB range to both given addresses. 367 | * 368 | * @param address_lo The memory address lower bound to be used as search condition. 369 | * @param address_hi The memory address upper bound to be used as search condition. 370 | * @param region Receives a pointer to a matching `ZyrexTrampolineRegion` struct. 371 | * @param chunk Receives a pointer to a matching `ZyrexTrampolineChunk` struct. 372 | * 373 | * @return `ZYAN_STATUS_TRUE` if a valid chunk was found in an already allocated trampoline region, 374 | * `ZYAN_STATUS_FALSE` if not, or a generic zyan status code if an error occured. 375 | */ 376 | static ZyanStatus ZyrexTrampolineRegionFindChunk(ZyanUPointer address_lo, ZyanUPointer address_hi, 377 | ZyrexTrampolineRegion** region, ZyrexTrampolineChunk** chunk) 378 | { 379 | ZYAN_ASSERT(region); 380 | ZYAN_ASSERT(chunk); 381 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 382 | 383 | ZyanUSize size; 384 | ZYAN_CHECK(ZyanVectorGetSize(&g_trampoline_data.regions, &size)); 385 | if (size == 0) 386 | { 387 | goto RegionNotFound; 388 | } 389 | 390 | const ZyanUSize mid = (address_lo + address_hi) / 2; 391 | ZyanUSize found_index; 392 | const ZyanStatus status = 393 | ZyanVectorBinarySearch(&g_trampoline_data.regions, &mid, &found_index, 394 | (ZyanComparison)&ZyanComparePointer); 395 | ZYAN_CHECK(status); 396 | 397 | if (found_index == size) 398 | { 399 | --found_index; 400 | } 401 | ZyanISize lo = found_index; 402 | ZyanISize hi = found_index + 1; 403 | ZyrexTrampolineRegion** element; 404 | while (ZYAN_TRUE) 405 | { 406 | ZyanU8 c = 0; 407 | 408 | if (lo >= 0) 409 | { 410 | element = (ZyrexTrampolineRegion**)ZyanVectorGet(&g_trampoline_data.regions, lo--); 411 | ZYAN_ASSERT(element); 412 | if (ZyrexTrampolineRegionFindChunkInRegion(*element, address_lo, address_hi, chunk)) 413 | { 414 | break; 415 | } 416 | ++c; 417 | } 418 | if (hi < (ZyanISize)size) 419 | { 420 | element = (ZyrexTrampolineRegion**)ZyanVectorGet(&g_trampoline_data.regions, hi++); 421 | ZYAN_ASSERT(element); 422 | if (ZyrexTrampolineRegionFindChunkInRegion(*element, address_lo, address_hi, chunk)) 423 | { 424 | break; 425 | } 426 | ++c; 427 | } 428 | 429 | if (c == 0) 430 | { 431 | goto RegionNotFound; 432 | } 433 | } 434 | 435 | *region = *element; 436 | return ZYAN_STATUS_TRUE; 437 | 438 | RegionNotFound: 439 | return ZYAN_STATUS_FALSE; 440 | } 441 | 442 | /** 443 | * @brief Inserts a new `ZyrexTrampolineRegion` item to the global trampoline-region list. 444 | * 445 | * @param region A pointer to the `ZyrexTrampolineRegion` item. 446 | * 447 | * @return A zyan status code. 448 | */ 449 | static ZyanStatus ZyrexTrampolineRegionInsert(ZyrexTrampolineRegion* region) 450 | { 451 | ZYAN_ASSERT(region); 452 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 453 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); 454 | 455 | ZyanUSize found_index; 456 | const ZyanStatus status = 457 | ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion, &found_index, 458 | (ZyanComparison)&ZyanComparePointer); 459 | ZYAN_CHECK(status); 460 | 461 | ZYAN_ASSERT(status == ZYAN_STATUS_FALSE); 462 | return ZyanVectorInsert(&g_trampoline_data.regions, found_index, ®ion); 463 | } 464 | 465 | /** 466 | * @brief Removes the given `ZyrexTrampolineRegion` item from the global trampoline-region list. 467 | * 468 | * @param region A pointer to the `ZyrexTrampolineRegion` item. 469 | * 470 | * @return A zyan status code. 471 | */ 472 | static ZyanStatus ZyrexTrampolineRegionRemove(ZyrexTrampolineRegion* region) 473 | { 474 | ZYAN_ASSERT(region); 475 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 476 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); 477 | 478 | ZyanUSize found_index; 479 | const ZyanStatus status = 480 | ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion, &found_index, 481 | (ZyanComparison)&ZyanComparePointer); 482 | ZYAN_CHECK(status); 483 | 484 | ZYAN_ASSERT(status == ZYAN_STATUS_TRUE); 485 | return ZyanVectorDelete(&g_trampoline_data.regions, found_index); 486 | } 487 | 488 | /* ---------------------------------------------------------------------------------------------- */ 489 | 490 | /** 491 | * @brief Changes the memory protection of the passed trampoline-region to `RX`. 492 | * 493 | * @param region A pointer to the `ZyrexTrampolineRegion` struct. 494 | * 495 | * @return A zyan status code. 496 | */ 497 | static ZyanStatus ZyrexTrampolineRegionProtect(ZyrexTrampolineRegion* region) 498 | { 499 | ZYAN_ASSERT(region); 500 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 501 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); 502 | 503 | return ZyanMemoryVirtualProtect(region, sizeof(ZyrexTrampolineChunk), 504 | ZYAN_PAGE_EXECUTE_READ); 505 | } 506 | 507 | /** 508 | * @brief Changes the memory protection of the passed trampoline-region to `RWX`. 509 | * 510 | * @param region A pointer to the `ZyrexTrampolineRegion` struct. 511 | * 512 | * @return A zyan status code. 513 | */ 514 | static ZyanStatus ZyrexTrampolineRegionUnprotect(ZyrexTrampolineRegion* region) 515 | { 516 | ZYAN_ASSERT(region); 517 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 518 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); 519 | 520 | return ZyanMemoryVirtualProtect(region, sizeof(ZyrexTrampolineChunk), 521 | ZYAN_PAGE_EXECUTE_READWRITE); 522 | } 523 | 524 | /** 525 | * Allocates memory for a new trampoline region in a +/-2GiB range of both passed address values 526 | * and initializes it. 527 | * 528 | * @param address_lo The memory address lower bound. 529 | * @param address_hi The memory address upper bound. 530 | * @param region Receives a pointer to the new `ZyrexTrampolineRegion` struct. 531 | * 532 | * Regions allocated by this function will have `RWX` memory protection. 533 | * 534 | * @return A zyan status code. 535 | */ 536 | static ZyanStatus ZyrexTrampolineRegionAllocate(ZyanUPointer address_lo, ZyanUPointer address_hi, 537 | ZyrexTrampolineRegion** region) 538 | { 539 | ZYAN_ASSERT(region); 540 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 541 | 542 | #ifdef ZYAN_WINDOWS 543 | 544 | SYSTEM_INFO system_info; 545 | GetSystemInfo(&system_info); 546 | 547 | const ZyanUSize region_size = g_trampoline_data.region_size; 548 | const ZyanUPointer mid = (address_lo + address_hi) / 2; 549 | const ZyanU8* alloc_address_lo = 550 | (const ZyanU8*)ZYAN_ALIGN_DOWN(mid, (ZyanUPointer)region_size); 551 | const ZyanU8* alloc_address_hi = 552 | (const ZyanU8*)ZYAN_ALIGN_UP(mid, (ZyanUPointer)region_size); 553 | 554 | MEMORY_BASIC_INFORMATION memory_info; 555 | 556 | #endif 557 | 558 | while (ZYAN_TRUE) 559 | { 560 | #ifdef ZYAN_WINDOWS 561 | 562 | // Skip reserved address regions 563 | if (alloc_address_lo < (ZyanU8*)system_info.lpMinimumApplicationAddress) 564 | { 565 | alloc_address_lo = (const ZyanU8*)system_info.lpMinimumApplicationAddress; 566 | alloc_address_lo = 567 | (const ZyanU8*)(ZYAN_ALIGN_UP((ZyanUPointer)alloc_address_lo, region_size)); 568 | } 569 | if (alloc_address_lo > (ZyanU8*)system_info.lpMaximumApplicationAddress) 570 | { 571 | alloc_address_lo = (const ZyanU8*)system_info.lpMaximumApplicationAddress; 572 | alloc_address_lo = 573 | (const ZyanU8*)(ZYAN_ALIGN_DOWN((ZyanUPointer)alloc_address_lo, region_size)); 574 | } 575 | if (alloc_address_hi < (ZyanU8*)system_info.lpMinimumApplicationAddress) 576 | { 577 | alloc_address_hi = (const ZyanU8*)system_info.lpMinimumApplicationAddress; 578 | alloc_address_lo = 579 | (const ZyanU8*)(ZYAN_ALIGN_UP((ZyanUPointer)alloc_address_lo, region_size)); 580 | } 581 | if (alloc_address_hi > (ZyanU8*)system_info.lpMaximumApplicationAddress) 582 | { 583 | alloc_address_hi = (const ZyanU8*)system_info.lpMaximumApplicationAddress; 584 | alloc_address_lo = 585 | (const ZyanU8*)(ZYAN_ALIGN_DOWN((ZyanUPointer)alloc_address_lo, region_size)); 586 | } 587 | 588 | //#endif 589 | 590 | // TODO: Only `RESERVE` the memory region and `COMMIT` pages on demand to reduce memory 591 | // TODO: imprint 592 | 593 | ZyanU8 c = 0; 594 | 595 | if (ZyrexTrampolineRegionInRange((ZyanUPointer)alloc_address_lo, address_lo, address_hi)) 596 | { 597 | ZYAN_MEMSET(&memory_info, 0, sizeof(memory_info)); 598 | if (!VirtualQuery(alloc_address_lo, &memory_info, sizeof(memory_info))) 599 | { 600 | return ZYAN_STATUS_BAD_SYSTEMCALL; 601 | } 602 | if ((memory_info.State == MEM_FREE) && (memory_info.RegionSize >= region_size)) 603 | { 604 | *region = VirtualAlloc((void*)alloc_address_lo, region_size, 605 | MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 606 | if (*region) 607 | { 608 | goto InitializeRegion; 609 | } 610 | } 611 | alloc_address_lo = (const ZyanU8*)((ZyanUPointer)memory_info.BaseAddress - region_size); 612 | ++c; 613 | } 614 | 615 | if (ZyrexTrampolineRegionInRange((ZyanUPointer)alloc_address_hi, address_lo, address_hi)) 616 | { 617 | memset(&memory_info, 0, sizeof(memory_info)); 618 | if (!VirtualQuery(alloc_address_hi, &memory_info, sizeof(memory_info))) 619 | { 620 | return ZYAN_STATUS_BAD_SYSTEMCALL; 621 | } 622 | if ((memory_info.State == MEM_FREE) && (memory_info.RegionSize >= region_size)) 623 | { 624 | *region = VirtualAlloc((void*)alloc_address_hi, region_size, 625 | MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 626 | if (*region) 627 | { 628 | goto InitializeRegion; 629 | } 630 | } 631 | alloc_address_hi = (const ZyanU8*)((ZyanUPointer)memory_info.BaseAddress + region_size); 632 | ++c; 633 | } 634 | 635 | if (c == 0) 636 | { 637 | return ZYAN_STATUS_OUT_OF_RANGE; 638 | } 639 | #else 640 | ZYAN_UNUSED(address_lo); 641 | ZYAN_UNUSED(address_hi); 642 | #endif 643 | } 644 | 645 | // ZYAN_UNREACHABLE; 646 | 647 | #ifdef ZYAN_WINDOWS 648 | InitializeRegion: 649 | (*region)->header.signature = ZYREX_TRAMPOLINE_REGION_SIGNATURE; 650 | (*region)->header.number_of_unused_chunks = g_trampoline_data.chunks_per_region - 1; 651 | #endif 652 | 653 | return ZYAN_STATUS_SUCCESS; 654 | } 655 | 656 | /** 657 | * @brief Frees the memory of the given trampoline region. 658 | * 659 | * @param region A pointer to the `ZyrexTrampolineRegion` struct. 660 | * 661 | * @return A zyan status code. 662 | */ 663 | static ZyanStatus ZyrexTrampolineRegionFree(ZyrexTrampolineRegion* region) 664 | { 665 | ZYAN_ASSERT(region); 666 | ZYAN_ASSERT(g_trampoline_data.is_initialized); 667 | ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); 668 | 669 | return ZyanMemoryVirtualFree(region, g_trampoline_data.region_size); 670 | } 671 | 672 | /* ---------------------------------------------------------------------------------------------- */ 673 | /* Trampoline chunk */ 674 | /* ---------------------------------------------------------------------------------------------- */ 675 | 676 | /** 677 | * @brief Initializes a new trampoline chunk and relocates the instructions from the original 678 | * function. 679 | * 680 | * @param chunk A pointer to the `ZyrexTrampolineChunk` struct. 681 | * @param address The address of the function to create the trampoline for. 682 | * @param callback The address of the callback function the hook will redirect to. 683 | * @param min_bytes_to_reloc Specifies the minimum amount of bytes that need to be relocated 684 | * to the trampoline (usually equals the size of the branch 685 | * instruction used for hooking). 686 | * This function might copy more bytes on demand to keep individual 687 | * instructions intact. 688 | * @param max_bytes_to_read The maximum amount of bytes that can be safely read from the given 689 | * `address`. 690 | * 691 | * @return A zyan status code. 692 | */ 693 | static ZyanStatus ZyrexTrampolineChunkInit(ZyrexTrampolineChunk* chunk, const void* address, 694 | const void* callback, ZyanUSize min_bytes_to_reloc, ZyanUSize max_bytes_to_read) 695 | { 696 | ZYAN_ASSERT(chunk); 697 | ZYAN_ASSERT(address); 698 | ZYAN_ASSERT(callback); 699 | ZYAN_ASSERT(min_bytes_to_reloc <= max_bytes_to_read); 700 | 701 | chunk->is_used = ZYAN_TRUE; 702 | chunk->callback_address = (ZyanUPointer)callback; 703 | 704 | #if defined(ZYAN_X64) 705 | 706 | ZyrexWriteAbsoluteJump(&chunk->callback_jump, (ZyanUPointer)&chunk->callback_address); 707 | ZYAN_CHECK(ZyanProcessFlushInstructionCache(&chunk->callback_jump, 708 | ZYREX_SIZEOF_ABSOLUTE_JUMP)); 709 | 710 | #endif 711 | 712 | ZyanUSize bytes_read; 713 | ZyanUSize bytes_written; 714 | 715 | // Relocate instructions 716 | ZYAN_CHECK(ZyrexRelocateCode(address, max_bytes_to_read, chunk, min_bytes_to_reloc, 717 | &bytes_read, &bytes_written)); 718 | 719 | ZYAN_ASSERT(bytes_read <= ZYAN_ARRAY_LENGTH(chunk->original_code)); 720 | ZYAN_ASSERT(bytes_written <= ZYAN_ARRAY_LENGTH(chunk->code_buffer)); 721 | 722 | // Write backjump 723 | ZyrexWriteAbsoluteJump(&chunk->code_buffer[bytes_written], 724 | (ZyanUPointer)&chunk->backjump_address); 725 | chunk->code_buffer_size = (ZyanU8)bytes_written; 726 | chunk->backjump_address = (ZyanUPointer)address + bytes_read; 727 | 728 | // Fill remaining space with `INT 3` instructions 729 | const ZyanUSize bytes_remaining = sizeof(chunk->code_buffer) - bytes_written; 730 | if (bytes_remaining > 0) 731 | { 732 | ZYAN_MEMSET(&chunk->code_buffer[bytes_written + ZYREX_SIZEOF_ABSOLUTE_JUMP], 0xCC, 733 | bytes_remaining - ZYREX_SIZEOF_ABSOLUTE_JUMP); 734 | } 735 | 736 | ZYAN_CHECK(ZyanProcessFlushInstructionCache(&chunk->code_buffer, 737 | ZYREX_TRAMPOLINE_MAX_CODE_SIZE_WITH_BACKJUMP + ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS)); 738 | 739 | // Backup original instructions 740 | chunk->original_code_size = (ZyanU8)bytes_read; 741 | ZYAN_MEMCPY(chunk->original_code, address, bytes_read); 742 | 743 | return ZYAN_STATUS_SUCCESS; 744 | } 745 | 746 | /* ---------------------------------------------------------------------------------------------- */ 747 | 748 | /* ============================================================================================== */ 749 | /* Public functions */ 750 | /* ============================================================================================== */ 751 | 752 | /* ---------------------------------------------------------------------------------------------- */ 753 | /* Creation and destruction */ 754 | /* ---------------------------------------------------------------------------------------------- */ 755 | 756 | ZyanStatus ZyrexTrampolineCreate(const void* address, const void* callback, 757 | ZyanUSize min_bytes_to_reloc, ZyrexTrampolineChunk** trampoline) 758 | { 759 | if (!address || !callback || (min_bytes_to_reloc < 1) || !trampoline) 760 | { 761 | return ZYAN_STATUS_INVALID_ARGUMENT; 762 | } 763 | 764 | // Check if the memory region of the target function has enough space for the hook code 765 | ZyanUSize source_size = ZYREX_TRAMPOLINE_MAX_CODE_SIZE; 766 | #ifdef ZYAN_WINDOWS 767 | ZYAN_CHECK(ZyrexGetSizeOfReadableMemoryRegion(address, &source_size)); 768 | if (source_size < min_bytes_to_reloc) 769 | { 770 | return ZYAN_STATUS_INVALID_OPERATION; 771 | } 772 | #endif 773 | 774 | if (!g_trampoline_data.is_initialized) 775 | { 776 | ZYAN_CHECK(ZyanVectorInit(&g_trampoline_data.regions, sizeof(ZyrexTrampolineRegion*), 8, 777 | ZYAN_NULL)); 778 | 779 | g_trampoline_data.region_size = ZyanMemoryGetSystemAllocationGranularity(); 780 | g_trampoline_data.chunks_per_region = 781 | g_trampoline_data.region_size / sizeof(ZyrexTrampolineChunk); 782 | 783 | g_trampoline_data.is_initialized = ZYAN_TRUE; 784 | } 785 | 786 | #ifdef ZYAN_X64 787 | 788 | // Gather memory address lower and upper bounds in order to find a suitable memory region for 789 | // the trampoline 790 | ZyanUPointer lo = (ZyanUPointer)(-1); 791 | ZyanUPointer hi = 0; 792 | ZYAN_CHECK(ZyrexGetAddressRangeOfRelativeInstructions(address, source_size, 793 | ZYREX_SIZEOF_RELATIVE_JUMP, &lo, &hi)); 794 | 795 | const ZyanUPointer address_value = (ZyanUPointer)address; 796 | if (address_value < lo) 797 | { 798 | lo = address_value; 799 | } 800 | if (address_value > hi) 801 | { 802 | hi = address_value; 803 | } 804 | 805 | if ((hi - lo) > ZYREX_RANGEOF_RELATIVE_JUMP) 806 | { 807 | return ZYAN_STATUS_OUT_OF_RANGE; 808 | } 809 | 810 | #else 811 | 812 | const ZyanUPointer lo = (ZyanUPointer)address; 813 | const ZyanUPointer hi = (ZyanUPointer)address; 814 | 815 | #endif 816 | 817 | ZyanBool is_new_region = ZYAN_FALSE; 818 | ZyrexTrampolineRegion* region; 819 | ZyrexTrampolineChunk* chunk; 820 | ZyanStatus status = ZyrexTrampolineRegionFindChunk(lo, hi, ®ion, &chunk); 821 | ZYAN_CHECK(status); 822 | 823 | switch (status) 824 | { 825 | case ZYAN_STATUS_TRUE: 826 | { 827 | ZYAN_ASSERT(region); 828 | ZYAN_ASSERT(chunk); 829 | ZYAN_CHECK(ZyrexTrampolineRegionUnprotect(region)); 830 | break; 831 | } 832 | case ZYAN_STATUS_FALSE: 833 | { 834 | ZYAN_CHECK(ZyrexTrampolineRegionAllocate(lo, hi, ®ion)); 835 | is_new_region = ZyrexTrampolineRegionFindChunkInRegion(region, lo, hi, &chunk); 836 | ZYAN_ASSERT(is_new_region); 837 | ZYAN_ASSERT(region); 838 | ZYAN_ASSERT(chunk); 839 | break; 840 | } 841 | default: 842 | ZYAN_UNREACHABLE; 843 | } 844 | 845 | ZYAN_ASSERT(region->header.number_of_unused_chunks > 0); 846 | 847 | status = ZyrexTrampolineChunkInit(chunk, address, callback, min_bytes_to_reloc, source_size); 848 | if (!ZYAN_SUCCESS(status)) 849 | { 850 | if (is_new_region) 851 | { 852 | ZYAN_UNUSED(ZyrexTrampolineRegionFree(region)); 853 | } else 854 | { 855 | ZYAN_UNUSED(ZyrexTrampolineRegionProtect(region)); 856 | } 857 | return status; 858 | } 859 | 860 | --region->header.number_of_unused_chunks; 861 | ZYAN_UNUSED(ZyrexTrampolineRegionProtect(region)); 862 | 863 | if (is_new_region) 864 | { 865 | ZYAN_UNUSED(ZyrexTrampolineRegionInsert(region)); 866 | } 867 | 868 | *trampoline = chunk; 869 | return ZYAN_STATUS_SUCCESS; 870 | } 871 | 872 | ZyanStatus ZyrexTrampolineFree(ZyrexTrampolineChunk* trampoline) 873 | { 874 | if (!trampoline) 875 | { 876 | return ZYAN_STATUS_INVALID_ARGUMENT; 877 | } 878 | if (!g_trampoline_data.is_initialized) 879 | { 880 | return ZYAN_STATUS_INVALID_OPERATION; 881 | } 882 | 883 | const ZyanUPointer region_address = ZYAN_ALIGN_DOWN((ZyanUPointer)trampoline, 884 | g_trampoline_data.region_size); 885 | ZyanUSize found_index; 886 | const ZyanStatus status = 887 | ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion_address, &found_index, 888 | (ZyanComparison)&ZyanComparePointer); 889 | ZYAN_CHECK(status); 890 | 891 | if (status == ZYAN_STATUS_FALSE) 892 | { 893 | return ZYAN_STATUS_NOT_FOUND; 894 | } 895 | 896 | ZyrexTrampolineRegion* const region = (ZyrexTrampolineRegion*)region_address; 897 | if (region->header.number_of_unused_chunks == g_trampoline_data.chunks_per_region - 1 - 1) 898 | { 899 | ZYAN_CHECK(ZyrexTrampolineRegionRemove(region)); 900 | ZYAN_CHECK(ZyrexTrampolineRegionFree(region)); 901 | } 902 | else 903 | { 904 | ZYAN_CHECK(ZyrexTrampolineRegionUnprotect(region)); 905 | ++region->header.number_of_unused_chunks; 906 | trampoline->is_used = ZYAN_FALSE; 907 | ZYAN_CHECK(ZyrexTrampolineRegionProtect(region)); 908 | } 909 | 910 | ZyanUSize size; 911 | ZYAN_CHECK(ZyanVectorGetSize(&g_trampoline_data.regions, &size)); 912 | if (size == 0) 913 | { 914 | ZYAN_CHECK(ZyanVectorDestroy(&g_trampoline_data.regions)); 915 | g_trampoline_data.is_initialized = ZYAN_FALSE; 916 | } 917 | 918 | return ZYAN_STATUS_SUCCESS; 919 | } 920 | 921 | /* ---------------------------------------------------------------------------------------------- */ 922 | /* Searching */ 923 | /* ---------------------------------------------------------------------------------------------- */ 924 | 925 | ZyanStatus ZyrexTrampolineFind(const void* original, ZyrexTrampolineChunk** trampoline) 926 | { 927 | if (!original || !trampoline) 928 | { 929 | return ZYAN_STATUS_INVALID_ARGUMENT; 930 | } 931 | if (!g_trampoline_data.is_initialized) 932 | { 933 | return ZYAN_STATUS_INVALID_OPERATION; 934 | } 935 | 936 | const ZyanUPointer region_address = ZYAN_ALIGN_DOWN((ZyanUPointer)original, 937 | g_trampoline_data.region_size); 938 | 939 | ZyanUSize found_index; 940 | const ZyanStatus status = 941 | ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion_address, &found_index, 942 | (ZyanComparison)&ZyanComparePointer); 943 | ZYAN_CHECK(status); 944 | 945 | if (status == ZYAN_STATUS_FALSE) 946 | { 947 | return ZYAN_STATUS_FALSE; 948 | } 949 | 950 | ZyrexTrampolineRegion** const element = 951 | ZyanVectorGetMutable(&g_trampoline_data.regions, found_index); 952 | ZYAN_ASSERT(element && *element); 953 | 954 | ZyrexTrampolineRegion* const region = *element; 955 | ZYAN_ASSERT(region->header.signature == ZYREX_TRAMPOLINE_REGION_SIGNATURE); 956 | 957 | for (ZyanUSize i = 1; i < g_trampoline_data.chunks_per_region; ++i) 958 | { 959 | ZyrexTrampolineChunk* const chunk = ®ion->chunks[i]; 960 | ZYAN_ASSERT(chunk); 961 | 962 | if (!chunk->is_used) 963 | { 964 | continue; 965 | } 966 | 967 | if ((ZyanUPointer)&chunk->code_buffer == (ZyanUPointer)original) 968 | { 969 | *trampoline = chunk; 970 | return ZYAN_STATUS_TRUE; 971 | } 972 | } 973 | 974 | return ZYAN_STATUS_FALSE; 975 | } 976 | 977 | /* ---------------------------------------------------------------------------------------------- */ 978 | 979 | /* ============================================================================================== */ 980 | -------------------------------------------------------------------------------- /src/Relocation.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | 3 | Zyan Hook Library (Zyrex) 4 | 5 | Original Author : Florian Bernd 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | 25 | ***************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | /* ============================================================================================== */ 35 | /* Enums and types */ 36 | /* ============================================================================================== */ 37 | 38 | /* ---------------------------------------------------------------------------------------------- */ 39 | /* Analyzed instruction */ 40 | /* ---------------------------------------------------------------------------------------------- */ 41 | 42 | /** 43 | * @brief Defines the `ZyrexAnalyzedInstruction` struct. 44 | */ 45 | typedef struct ZyrexAnalyzedInstruction_ 46 | { 47 | /** 48 | * @brief The address of the instruction relative to the start of the source buffer. 49 | */ 50 | ZyanUSize address_offset; 51 | /** 52 | * @brief The absolute runtime/memory address of the instruction. 53 | */ 54 | ZyanUPointer address; 55 | /** 56 | * @brief The `ZydisDecodedInstruction` struct of the analyzed instruction. 57 | */ 58 | ZydisDecodedInstruction instruction; 59 | /** 60 | * @brief Signals, if the instruction refers to a target address using a relative offset. 61 | */ 62 | ZyanBool has_relative_target; 63 | /** 64 | * @brief Signals, if the target address referred by the relative offset is not inside the 65 | * analyzed code chunk. 66 | */ 67 | ZyanBool has_external_target; 68 | /** 69 | * @brief Signals, if this instruction is targeted by at least one instruction from inside 70 | * the analyzed code chunk. 71 | */ 72 | ZyanBool is_internal_target; 73 | /** 74 | * @brief The absolute target address of the instruction calculated from the relative offset, 75 | * if applicable. 76 | */ 77 | ZyanU64 absolute_target_address; 78 | /** 79 | * @brief Contains the ids of all instructions inside the analyzed code chunk that are 80 | * targeting this instruction using a relative offset. 81 | */ 82 | ZyanVector/**/ incoming; 83 | /** 84 | * @brief The id of an instruction inside the analyzed code chunk which is targeted by 85 | * this instruction using a relative offset, or `-1` if not applicable. 86 | */ 87 | ZyanU8 outgoing; 88 | } ZyrexAnalyzedInstruction; 89 | 90 | /* ---------------------------------------------------------------------------------------------- */ 91 | /* Relocation context */ 92 | /* ---------------------------------------------------------------------------------------------- */ 93 | 94 | /** 95 | * @brief Defines the `ZyrexRelocationContext` struct. 96 | */ 97 | typedef struct ZyrexRelocationContext_ 98 | { 99 | /** 100 | * @brief The exact amount of bytes that should be relocated. 101 | */ 102 | ZyanUSize bytes_to_reloc; 103 | /** 104 | * @brief Contains a `ZyrexAnalyzedInstruction` struct for each instruction in the source 105 | * buffer. 106 | */ 107 | ZyanVector/**/ instructions; 108 | /** 109 | * @brief A pointer to the source buffer. 110 | */ 111 | const void* source; 112 | /** 113 | * @brief The maximum amount of bytes that can be safely read from the source buffer. 114 | */ 115 | ZyanUSize source_length; 116 | /** 117 | * @brief A pointer to the destination buffer. 118 | */ 119 | void* destination; 120 | /** 121 | * @brief The maximum amount of bytes that can be safely written to the destination buffer. 122 | */ 123 | ZyanUSize destination_length; 124 | /** 125 | * @brief The instruction translation map. 126 | */ 127 | ZyrexInstructionTranslationMap* translation_map; 128 | /** 129 | * @brief The number of instructions read from the source buffer. 130 | */ 131 | ZyanU8 instructions_read; 132 | /** 133 | * @brief The number of instructions written to the destination buffer. 134 | */ 135 | ZyanU8 instructions_written; 136 | /** 137 | * @brief The number of bytes read from the source buffer. 138 | */ 139 | ZyanUSize bytes_read; 140 | /** 141 | * @brief The number of bytes written to the destination buffer. 142 | */ 143 | ZyanUSize bytes_written; 144 | } ZyrexRelocationContext; 145 | 146 | /* ---------------------------------------------------------------------------------------------- */ 147 | 148 | /* ============================================================================================== */ 149 | /* Internal functions */ 150 | /* ============================================================================================== */ 151 | 152 | /* ---------------------------------------------------------------------------------------------- */ 153 | /* ZyrexAnalyzedInstruction */ 154 | /* ---------------------------------------------------------------------------------------------- */ 155 | 156 | /** 157 | * @brief Finalizes the given `ZyrexAnalyzedInstruction` struct. 158 | * 159 | * @param item A pointer to the `ZyrexAnalyzedInstruction` struct. 160 | */ 161 | static void ZyrexAnalyzedInstructionDestroy(ZyrexAnalyzedInstruction* item) 162 | { 163 | ZYAN_ASSERT(item); 164 | 165 | if (item->is_internal_target) 166 | { 167 | ZyanVectorDestroy(&item->incoming); 168 | } 169 | } 170 | 171 | /* ---------------------------------------------------------------------------------------------- */ 172 | /* Instruction analysis */ 173 | /* ---------------------------------------------------------------------------------------------- */ 174 | 175 | /** 176 | * @brief Analyzes the code in the source buffer and updates the relocation-context. 177 | * 178 | * @param buffer A pointer to the buffer that contains the code to analyze. 179 | * @param length The length of the buffer. 180 | * @param bytes_to_analyze The minimum number of bytes to analyze. More bytes might get 181 | * accessed on demand to keep individual instructions intact. 182 | * @param instructions Returns a new `ZyanVector` instance which contains all analyzed 183 | * instructions. 184 | * The vector needs to manually get destroyed by calling 185 | * `ZyanVectorDestroy` when no longer needed. 186 | * @param bytes_read Returns the exact amount of bytes read from the buffer. 187 | * 188 | * @return A zyan status code. 189 | */ 190 | static ZyanStatus ZyrexAnalyzeCode(const void* buffer, ZyanUSize length, 191 | ZyanUSize bytes_to_analyze, ZyanVector/**/* instructions, 192 | ZyanUSize* bytes_read) 193 | { 194 | ZYAN_ASSERT(buffer); 195 | ZYAN_ASSERT(length); 196 | ZYAN_ASSERT(bytes_to_analyze); 197 | 198 | ZydisDecoder decoder; 199 | #if defined(ZYAN_X86) 200 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_COMPAT_32, ZYDIS_STACK_WIDTH_32); 201 | #elif defined(ZYAN_X64) 202 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); 203 | #else 204 | # error "Unsupported architecture detected" 205 | #endif 206 | 207 | ZYAN_CHECK(ZyanVectorInit(instructions, sizeof(ZyrexAnalyzedInstruction), 208 | ZYREX_TRAMPOLINE_MAX_INSTRUCTION_COUNT, 209 | (ZyanMemberProcedure)&ZyrexAnalyzedInstructionDestroy)); 210 | 211 | // First pass: 212 | // - Determine exact amount of instructions and instruction bytes 213 | // - Decode all instructions and calculate relative target address for instructions with 214 | // relative offsets 215 | // 216 | ZyanUSize offset = 0; 217 | while (offset < bytes_to_analyze) 218 | { 219 | ZyrexAnalyzedInstruction item; 220 | 221 | ZYAN_CHECK(ZydisDecoderDecodeInstruction(&decoder, ZYAN_NULL, 222 | (const ZyanU8*)buffer + offset, length - offset, &item.instruction)); 223 | 224 | item.address_offset = offset; 225 | item.address = (ZyanUPointer)(const ZyanU8*)buffer + offset; 226 | item.has_relative_target = (item.instruction.attributes & ZYDIS_ATTRIB_IS_RELATIVE) 227 | ? ZYAN_TRUE 228 | : ZYAN_FALSE; 229 | item.has_external_target = item.has_relative_target; 230 | item.absolute_target_address = 0; 231 | if (item.has_relative_target) 232 | { 233 | ZYAN_CHECK(ZyrexCalcAbsoluteAddress(&item.instruction, 234 | (ZyanU64)buffer + offset, &item.absolute_target_address)); 235 | } 236 | item.is_internal_target = ZYAN_FALSE; 237 | item.outgoing = (ZyanU8)(-1); 238 | ZYAN_CHECK(ZyanVectorPushBack(instructions, &item)); 239 | 240 | offset += item.instruction.length; 241 | } 242 | 243 | ZYAN_ASSERT(offset >= bytes_to_analyze); 244 | *bytes_read = offset; 245 | 246 | // Second pass: 247 | // - Find internal outgoing target for instructions with relative offsets 248 | // - Find internal incoming targets from instructions with relative offsets 249 | // 250 | for (ZyanUSize i = 0; i < instructions->size; ++i) 251 | { 252 | ZyrexAnalyzedInstruction* const current = ZyanVectorGetMutable(instructions, i); 253 | ZYAN_ASSERT(current); 254 | 255 | for (ZyanUSize j = 0; j < instructions->size; ++j) 256 | { 257 | ZyrexAnalyzedInstruction* const item = ZyanVectorGetMutable(instructions, j); 258 | ZYAN_ASSERT(item); 259 | 260 | if (item->has_relative_target && (item->absolute_target_address == current->address)) 261 | { 262 | // The `item` instruction targets the `current` instruction 263 | item->has_external_target = ZYAN_FALSE; 264 | item->outgoing = (ZyanU8)i; 265 | 266 | // The `current` instruction is an internal target of the `item` instruction 267 | if (!current->is_internal_target) 268 | { 269 | current->is_internal_target = ZYAN_TRUE; 270 | ZYAN_CHECK(ZyanVectorInit(¤t->incoming, sizeof(ZyanU8), 2, 271 | ZYAN_NULL)); 272 | } 273 | const ZyanU8 value = (ZyanU8)j; 274 | ZYAN_CHECK(ZyanVectorPushBack(¤t->incoming, &value)); 275 | } 276 | } 277 | } 278 | 279 | return ZYAN_STATUS_SUCCESS; 280 | } 281 | 282 | /** 283 | * @brief Checks if the given instruction is a relative branch instruction. 284 | * 285 | * @param instruction A pointer to the `ZydisDecodedInstruction` struct of the instruction to 286 | * check. 287 | * 288 | * @return `ZYAN_TRUE` if the instruction is a supported relative branch instruction or 289 | * `ZYAN_FALSE`, if not. 290 | */ 291 | static ZyanBool ZyrexIsRelativeBranchInstruction(const ZydisDecodedInstruction* instruction) 292 | { 293 | ZYAN_ASSERT(instruction); 294 | 295 | if (!instruction->raw.imm[0].is_relative) 296 | { 297 | return ZYAN_FALSE; 298 | } 299 | 300 | switch (instruction->mnemonic) 301 | { 302 | case ZYDIS_MNEMONIC_JMP: 303 | case ZYDIS_MNEMONIC_JO: 304 | case ZYDIS_MNEMONIC_JNO: 305 | case ZYDIS_MNEMONIC_JB: 306 | case ZYDIS_MNEMONIC_JNB: 307 | case ZYDIS_MNEMONIC_JZ: 308 | case ZYDIS_MNEMONIC_JNZ: 309 | case ZYDIS_MNEMONIC_JBE: 310 | case ZYDIS_MNEMONIC_JNBE: 311 | case ZYDIS_MNEMONIC_JS: 312 | case ZYDIS_MNEMONIC_JNS: 313 | case ZYDIS_MNEMONIC_JP: 314 | case ZYDIS_MNEMONIC_JNP: 315 | case ZYDIS_MNEMONIC_JL: 316 | case ZYDIS_MNEMONIC_JNL: 317 | case ZYDIS_MNEMONIC_JLE: 318 | case ZYDIS_MNEMONIC_JNLE: 319 | case ZYDIS_MNEMONIC_JCXZ: 320 | case ZYDIS_MNEMONIC_JECXZ: 321 | case ZYDIS_MNEMONIC_JRCXZ: 322 | case ZYDIS_MNEMONIC_LOOP: 323 | case ZYDIS_MNEMONIC_LOOPE: 324 | case ZYDIS_MNEMONIC_LOOPNE: 325 | return ZYAN_TRUE; 326 | default: 327 | return ZYAN_FALSE; 328 | } 329 | } 330 | 331 | /** 332 | * @brief Checks if the given instruction is an instruction with a relative memory operand. 333 | * 334 | * @param instruction A pointer to the `ZydisDecodedInstruction` struct of the instruction to 335 | * check. 336 | * 337 | * @return `ZYAN_TRUE` if the instruction is an instruction with a relative memory operand or 338 | * `ZYAN_FALSE`, if not. 339 | */ 340 | static ZyanBool ZyrexIsRelativeMemoryInstruction(const ZydisDecodedInstruction* instruction) 341 | { 342 | ZYAN_ASSERT(instruction); 343 | 344 | return ((instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM) && 345 | (instruction->raw.modrm.mod == 0) && (instruction->raw.modrm.rm == 5)) 346 | ? ZYAN_TRUE 347 | : ZYAN_FALSE; 348 | } 349 | 350 | /** 351 | * @brief Checks if the given relative branch instruction needs to be rewritten in order to 352 | * reach the destination address. 353 | * 354 | * @param context A pointer to the `ZyrexRelocationContext` struct. 355 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 356 | * check. 357 | * 358 | * @return `ZYAN_TRUE` if the given relative branch instruction needs to be rewritten in order to 359 | * reach the destination address or `ZYAN_FALSE`, if not. 360 | */ 361 | static ZyanBool ZyrexShouldRewriteBranchInstruction(ZyrexRelocationContext* context, 362 | const ZyrexAnalyzedInstruction* instruction) 363 | { 364 | ZYAN_ASSERT(context); 365 | ZYAN_ASSERT(instruction); 366 | ZYAN_ASSERT(instruction->has_relative_target); 367 | ZYAN_ASSERT(instruction->has_external_target); 368 | 369 | const ZyanU64 source_address = (ZyanU64)context->destination + context->bytes_written; 370 | 371 | switch (instruction->instruction.raw.imm[0].size) 372 | { 373 | case 8: 374 | { 375 | const ZyanI64 distance = (ZyanI64)(instruction->absolute_target_address - source_address - 376 | instruction->instruction.length); 377 | if ((distance < ZYAN_INT8_MIN) || (distance > ZYAN_INT8_MAX)) 378 | { 379 | return ZYAN_TRUE; 380 | } 381 | break; 382 | } 383 | case 16: 384 | { 385 | const ZyanI64 distance = (ZyanI64)(instruction->absolute_target_address - source_address - 386 | instruction->instruction.length); 387 | if ((distance < ZYAN_INT16_MIN) || (distance > ZYAN_INT16_MAX)) 388 | { 389 | return ZYAN_TRUE; 390 | } 391 | break; 392 | } 393 | case 32: 394 | { 395 | const ZyanI64 distance = (ZyanI64)(instruction->absolute_target_address - source_address - 396 | instruction->instruction.length); 397 | if ((distance < ZYAN_INT32_MIN) || (distance > ZYAN_INT32_MAX)) 398 | { 399 | return ZYAN_TRUE; 400 | } 401 | break; 402 | } 403 | default: 404 | ZYAN_UNREACHABLE; 405 | } 406 | 407 | return ZYAN_FALSE; 408 | } 409 | 410 | /** 411 | * @brief Checks if the given relative memory instruction should be redirected to not access 412 | * any memory inside the relocated code chunk. 413 | * 414 | * @param context A pointer to the `ZyrexRelocationContext` struct. 415 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 416 | * check. 417 | * 418 | * @return `ZYAN_STATUS_TRUE` if the given memory instruction should be redirected to not access 419 | * any memory inside the relocated code chunk or `ZYAN_STATUS_FALSE`, if not. 420 | * 421 | * The instruction should be redirected, if it would access any memory inside the relocated code 422 | * chunk. This prevents wrong data being read due to modifications of the instructions during the 423 | * relocation process. 424 | */ 425 | //static ZyanStatus ZyrexShouldRedirectMemoryInstruction(ZyrexRelocationContext* context, 426 | // const ZyrexAnalyzedInstruction* instruction) 427 | //{ 428 | // ZYAN_ASSERT(context); 429 | // ZYAN_ASSERT(instruction); 430 | // ZYAN_ASSERT(instruction->has_relative_target); 431 | // 432 | // return !instruction->has_external_target; 433 | //} 434 | 435 | /* ---------------------------------------------------------------------------------------------- */ 436 | /* Relocation */ 437 | /* ---------------------------------------------------------------------------------------------- */ 438 | 439 | /** 440 | * @brief Updates the given relocation context. 441 | * 442 | * This function will add a new instruction with the given length and offsets to the translation- 443 | * map and additionally updates the `instructions_written` and `bytes_written` fields. 444 | * 445 | * @param context A pointer to the `ZyrexRelocationContext` struct. 446 | * @param length The length of the written instruction. 447 | * @param offset_source The source offset of the instruction. 448 | * @param offset_destination The destination offset of the instruction. 449 | */ 450 | static void ZyrexUpdateRelocationContext(ZyrexRelocationContext* context, ZyanUSize length, 451 | ZyanU8 offset_source, ZyanU8 offset_destination) 452 | { 453 | ZYAN_ASSERT(context); 454 | ZYAN_ASSERT( 455 | context->translation_map->count < ZYAN_ARRAY_LENGTH(context->translation_map->items)); 456 | 457 | context->translation_map->items[context->translation_map->count].offset_source = 458 | offset_source; 459 | context->translation_map->items[context->translation_map->count].offset_destination = 460 | offset_destination; 461 | ++context->translation_map->count; 462 | ++context->instructions_written; 463 | context->bytes_written += length; 464 | } 465 | 466 | /** 467 | * @brief Relocates a single common instruction (without a relative offset) and updates the 468 | * relocation-context. 469 | * 470 | * @param context A pointer to the `ZyrexRelocationContext` struct. 471 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 472 | * relocate. 473 | * 474 | * @return A zyan status code. 475 | */ 476 | static ZyanStatus ZyrexRelocateCommonInstruction(ZyrexRelocationContext* context, 477 | const ZyrexAnalyzedInstruction* instruction) 478 | { 479 | ZYAN_ASSERT(context); 480 | ZYAN_ASSERT(instruction); 481 | 482 | // Relocate instruction 483 | ZYAN_MEMCPY((ZyanU8*)context->destination + context->bytes_written, 484 | (const ZyanU8*)context->source + context->bytes_read, instruction->instruction.length); 485 | 486 | // Update relocation context 487 | ZyrexUpdateRelocationContext(context, instruction->instruction.length, 488 | (ZyanU8)context->bytes_read, (ZyanU8)context->bytes_written); 489 | 490 | return ZYAN_STATUS_SUCCESS; 491 | } 492 | 493 | /** 494 | * @brief Relocates the given relative branch instruction and updates the relocation-context. 495 | * 496 | * @param context A pointer to the `ZyrexRelocationContext` struct. 497 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 498 | * relocate. 499 | * 500 | * @return A zyan status code. 501 | */ 502 | static ZyanStatus ZyrexRelocateRelativeBranchInstruction(ZyrexRelocationContext* context, 503 | const ZyrexAnalyzedInstruction* instruction) 504 | { 505 | ZYAN_ASSERT(context); 506 | ZYAN_ASSERT(instruction); 507 | 508 | if (!instruction->has_external_target) 509 | { 510 | // Offsets for relative instructions with internal target addresses are fixed up later by 511 | // the `ZyrexUpdateInstructionOffsets` function ... 512 | return ZyrexRelocateCommonInstruction(context, instruction); 513 | } 514 | 515 | if (ZyrexShouldRewriteBranchInstruction(context, instruction)) 516 | { 517 | // Rewrite branch instructions for which no alternative form with 32-bit offset exists 518 | switch (instruction->instruction.mnemonic) 519 | { 520 | case ZYDIS_MNEMONIC_JCXZ: 521 | case ZYDIS_MNEMONIC_JECXZ: 522 | case ZYDIS_MNEMONIC_JRCXZ: 523 | case ZYDIS_MNEMONIC_LOOP: 524 | case ZYDIS_MNEMONIC_LOOPE: 525 | case ZYDIS_MNEMONIC_LOOPNE: 526 | { 527 | // E.g. the following code: 528 | /* 529 | * @__START: 530 | * ... 531 | * JECXZ @__TARGET 532 | * ... 533 | * ... 534 | * @__TARGET: 535 | * ... 536 | */ 537 | 538 | // ... will be transformed to: 539 | /* 540 | * @__START: 541 | * ... 542 | * JECXZ @__CASE1 543 | * JMP SHORT @__CASE0 544 | * @__CASE1: 545 | * JMP @__TARGET 546 | * @__CASE0: 547 | * ... 548 | * ... 549 | * @__TARGET: (external) 550 | * ... 551 | */ 552 | 553 | ZyanU8* address = (ZyanU8*)context->destination + context->bytes_written; 554 | 555 | // Copy original instruction and modify relative offset 556 | ZYAN_MEMCPY(address, (const ZyanU8*)context->source + context->bytes_read, 557 | instruction->instruction.length); 558 | *(address + instruction->instruction.raw.imm[0].offset) = 0x02; 559 | address += instruction->instruction.length; 560 | 561 | ZyrexUpdateRelocationContext(context, instruction->instruction.length, 562 | (ZyanU8)context->bytes_read, (ZyanU8)context->bytes_written); 563 | 564 | // Generate `JMP` to `0` branch 565 | *address++ = 0xEB; 566 | *address++ = 0x05; 567 | ZyrexUpdateRelocationContext(context, 2, (ZyanU8)context->bytes_read, 568 | (ZyanU8)context->bytes_written + instruction->instruction.length); 569 | 570 | // Generate `JMP` to `1` branch 571 | ZyrexWriteRelativeJump(address, (ZyanUPointer)instruction->absolute_target_address); 572 | ZyrexUpdateRelocationContext(context, 2, (ZyanU8)context->bytes_read, 573 | (ZyanU8)context->bytes_written + instruction->instruction.length + 2); 574 | 575 | return ZYAN_STATUS_SUCCESS; 576 | } 577 | default: 578 | break; 579 | } 580 | 581 | // Enlarge branch instructions for which an alternative form with 32-bit offset exists 582 | ZyanU8 opcode; 583 | ZyanU8 length = 6; 584 | switch (instruction->instruction.mnemonic) 585 | { 586 | case ZYDIS_MNEMONIC_JMP: 587 | { 588 | opcode = 0xE9; 589 | length = 5; 590 | break; 591 | } 592 | case ZYDIS_MNEMONIC_JO : opcode = 0x80; break; 593 | case ZYDIS_MNEMONIC_JNO : opcode = 0x81; break; 594 | case ZYDIS_MNEMONIC_JB : opcode = 0x82; break; 595 | case ZYDIS_MNEMONIC_JNB : opcode = 0x83; break; 596 | case ZYDIS_MNEMONIC_JZ : opcode = 0x84; break; 597 | case ZYDIS_MNEMONIC_JNZ : opcode = 0x85; break; 598 | case ZYDIS_MNEMONIC_JBE : opcode = 0x86; break; 599 | case ZYDIS_MNEMONIC_JNBE: opcode = 0x87; break; 600 | case ZYDIS_MNEMONIC_JS : opcode = 0x88; break; 601 | case ZYDIS_MNEMONIC_JNS : opcode = 0x89; break; 602 | case ZYDIS_MNEMONIC_JP : opcode = 0x8A; break; 603 | case ZYDIS_MNEMONIC_JNP : opcode = 0x8B; break; 604 | case ZYDIS_MNEMONIC_JL : opcode = 0x8C; break; 605 | case ZYDIS_MNEMONIC_JNL : opcode = 0x8D; break; 606 | case ZYDIS_MNEMONIC_JLE : opcode = 0x8E; break; 607 | case ZYDIS_MNEMONIC_JNLE: opcode = 0x8F; break; 608 | default: 609 | ZYAN_UNREACHABLE; 610 | } 611 | 612 | // Write opcode 613 | ZyanU8* address = (ZyanU8*)context->destination + context->bytes_written; 614 | if (opcode == 0xE9) 615 | { 616 | *address++ = 0xE9; 617 | } else 618 | { 619 | *address++ = 0x0F; 620 | *address++ = opcode; 621 | } 622 | 623 | // Write relative offset 624 | *(ZyanI32*)(address) = 625 | ZyrexCalculateRelativeOffset(4, (ZyanUPointer)address, 626 | (ZyanUPointer)instruction->absolute_target_address); 627 | 628 | // Update relocation context 629 | ZyrexUpdateRelocationContext(context, length, (ZyanU8)context->bytes_read, 630 | (ZyanU8)context->bytes_written); 631 | 632 | return ZYAN_STATUS_SUCCESS; 633 | } 634 | 635 | void* const offset_address = (ZyanU8*)context->destination + context->bytes_written + 636 | instruction->instruction.raw.imm[0].offset; 637 | 638 | // First copy the instruction like it is ... 639 | ZYAN_CHECK(ZyrexRelocateCommonInstruction(context, instruction)); 640 | 641 | // Update the relative offset for the new instruction position 642 | const ZyanI32 value = ZyrexCalculateRelativeOffset(0, 643 | (ZyanUPointer)context->destination + context->bytes_written, 644 | (ZyanUPointer)instruction->absolute_target_address); 645 | 646 | switch (instruction->instruction.raw.imm[0].size) 647 | { 648 | case 8: *((ZyanI8* )offset_address) = (ZyanI8 )value; break; 649 | case 16: *((ZyanI16*)offset_address) = (ZyanI16)value; break; 650 | case 32: *((ZyanI32*)offset_address) = (ZyanI32)value; break; 651 | default: 652 | ZYAN_UNREACHABLE; 653 | } 654 | 655 | return ZYAN_STATUS_SUCCESS; 656 | } 657 | 658 | /** 659 | * @brief Relocates the given instruction with relative memory operand and updates the 660 | * relocation-context. 661 | * 662 | * @param context A pointer to the `ZyrexRelocationContext` struct. 663 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 664 | * relocate. 665 | * 666 | * @return A zyan status code. 667 | */ 668 | static ZyanStatus ZyrexRelocateRelativeMemoryInstruction(ZyrexRelocationContext* context, 669 | const ZyrexAnalyzedInstruction* instruction) 670 | { 671 | ZYAN_ASSERT(context); 672 | ZYAN_ASSERT(instruction); 673 | 674 | // We have to update the offset of relative memory instructions with targets outside the 675 | // relocated code chunk 676 | if (instruction->has_external_target) 677 | { 678 | void* const offset_address = (ZyanU8*)context->destination + context->bytes_written + 679 | instruction->instruction.raw.disp.offset; 680 | 681 | // First copy the instruction like it is ... 682 | ZYAN_CHECK(ZyrexRelocateCommonInstruction(context, instruction)); 683 | 684 | // Update the relative offset for the new instruction position 685 | const ZyanI32 value = ZyrexCalculateRelativeOffset(0, 686 | (ZyanUPointer)context->destination + context->bytes_written, 687 | (ZyanUPointer)instruction->absolute_target_address); 688 | 689 | switch (instruction->instruction.raw.disp.size) 690 | { 691 | case 8: *((ZyanI8* )offset_address) = (ZyanI8 )value; break; 692 | case 16: *((ZyanI16*)offset_address) = (ZyanI16)value; break; 693 | case 32: *((ZyanI32*)offset_address) = (ZyanI32)value; break; 694 | default: 695 | ZYAN_UNREACHABLE; 696 | } 697 | 698 | return ZYAN_STATUS_SUCCESS; 699 | } 700 | 701 | return ZyrexRelocateCommonInstruction(context, instruction); 702 | } 703 | 704 | /** 705 | * @brief Relocates a single relative instruction and updates the relocation-context. 706 | * 707 | * This function takes care of code rewriting and/or enlarging the instruction to 32-bit if needed. 708 | * 709 | * @param context A pointer to the `ZyrexRelocationContext` struct. 710 | * @param instruction A pointer to the `ZyrexAnalyzedInstruction` struct of the instruction to 711 | * relocate. 712 | * 713 | * @return A zyan status code. 714 | */ 715 | static ZyanStatus ZyrexRelocateRelativeInstruction(ZyrexRelocationContext* context, 716 | const ZyrexAnalyzedInstruction* instruction) 717 | { 718 | ZYAN_ASSERT(context); 719 | ZYAN_ASSERT(instruction); 720 | 721 | switch (instruction->instruction.mnemonic) 722 | { 723 | case ZYDIS_MNEMONIC_CALL: 724 | { 725 | // It's not safe to relocate a `CALL` instruction to the trampoline, as the code-flow 726 | // will return to the trampoline at some time. If the hook has been removed in the 727 | // meantime, the application will crash 728 | return ZYAN_STATUS_FAILED; // TODO: 729 | } 730 | default: 731 | break; 732 | } 733 | 734 | // Relocate relative branch instruction 735 | if (ZyrexIsRelativeBranchInstruction(&instruction->instruction)) 736 | { 737 | return ZyrexRelocateRelativeBranchInstruction(context, instruction); 738 | } 739 | 740 | // Relocate instruction with relative memory operand 741 | if (ZyrexIsRelativeMemoryInstruction(&instruction->instruction)) 742 | { 743 | return ZyrexRelocateRelativeMemoryInstruction(context, instruction); 744 | } 745 | 746 | // We should not be able to reach this code, if we correctly handled all existing relative 747 | // instructions 748 | ZYAN_UNREACHABLE; 749 | } 750 | 751 | /** 752 | * @brief Takes the offset of an instruction in the source buffer and returns the offset of the 753 | * same instruction in the destination buffer. 754 | * 755 | * @param context A pointer to the `ZyrexRelocationContext` struct. 756 | * @param offset_source The offset of the instruction in the source buffer. 757 | * @param offset_destination Receives the offset of the instruction in the destination buffer. 758 | * 759 | * If the source instruction has been rewritten into a code-block of multiple instructions, the 760 | * offset of the first instruction is returned. 761 | * 762 | * @return A zyan status code. 763 | */ 764 | static ZyanStatus ZyrexGetRelocatedInstructionOffset(ZyrexRelocationContext* context, 765 | ZyanU8 offset_source, ZyanU8* offset_destination) 766 | { 767 | ZYAN_ASSERT(context); 768 | ZYAN_ASSERT(offset_destination); 769 | ZYAN_ASSERT(context->instructions.size <= context->translation_map->count); 770 | 771 | for (ZyanUSize i = 0; i < context->translation_map->count; ++i) 772 | { 773 | const ZyrexInstructionTranslationItem* item = &context->translation_map->items[i]; 774 | if (item->offset_source == offset_source) 775 | { 776 | *offset_destination = item->offset_destination; 777 | return ZYAN_STATUS_SUCCESS; 778 | } 779 | } 780 | 781 | return ZYAN_STATUS_NOT_FOUND; 782 | } 783 | 784 | /** 785 | * @brief Updates the offsets of instructions with relative offsets pointing to instructions 786 | * inside the relocated code. 787 | * 788 | * @param context A pointer to the `ZyrexRelocationContext` struct. 789 | * 790 | * @return A zyan status code. 791 | * 792 | * As some of the instructions might have been enlarged or rewritten, there is a chance that the 793 | * relative offset of previous instructions does not point to the correct target any longer. This 794 | * function compensates all instruction shifts happened during the relocation process. 795 | */ 796 | static ZyanStatus ZyrexUpdateInstructionOffsets(ZyrexRelocationContext* context) 797 | { 798 | ZYAN_ASSERT(context); 799 | 800 | for (ZyanUSize i = 0; i < context->instructions.size; ++i) 801 | { 802 | const ZyrexAnalyzedInstruction* const instruction = 803 | ZyanVectorGet(&context->instructions, i); 804 | 805 | if (!instruction->has_relative_target || instruction->has_external_target) 806 | { 807 | // The instruction does not have a relative target or the relative offset is pointing 808 | // to an address outside of the destination buffer 809 | continue; 810 | } 811 | 812 | // TODO: Handle RIP-rel memory operand accessing memory of rewritten instructions 813 | // TODO: e.g. by redirecting access to the original data saved in the trampoline chunk 814 | // TODO: Do the same thing for (32-bit) instructions with absolute memory operand 815 | // TODO: (both situations should be really rare edge cases) 816 | 817 | ZyanU8 offset = 0; 818 | ZyanU8 size = 0; 819 | if (ZyrexIsRelativeBranchInstruction(&instruction->instruction)) 820 | { 821 | offset = instruction->instruction.raw.imm[0].offset; 822 | size = instruction->instruction.raw.imm[0].size; 823 | } 824 | if (ZyrexIsRelativeMemoryInstruction(&instruction->instruction)) 825 | { 826 | offset = instruction->instruction.raw.disp.offset; 827 | size = instruction->instruction.raw.disp.size; 828 | } 829 | ZYAN_ASSERT(size > 0); 830 | 831 | // Lookup the offset of the instruction in the destination buffer 832 | ZyanU8 offset_instruction; 833 | ZYAN_CHECK(ZyrexGetRelocatedInstructionOffset(context, (ZyanU8)instruction->address_offset, 834 | &offset_instruction)); 835 | 836 | // Lookup the offset of the destination instruction in the destination buffer 837 | const ZyrexAnalyzedInstruction* const destination = 838 | ZyanVectorGet(&context->instructions, instruction->outgoing); 839 | ZYAN_ASSERT(destination); 840 | ZyanU8 offset_destination; 841 | ZYAN_CHECK(ZyrexGetRelocatedInstructionOffset(context, (ZyanU8)destination->address_offset, 842 | &offset_destination)); 843 | 844 | void* const address_of_offset = (ZyanU8*)context->destination + offset_instruction + offset; 845 | const ZyanI32 value = ZyrexCalculateRelativeOffset(instruction->instruction.length, 846 | offset_instruction, offset_destination); 847 | 848 | switch (size) 849 | { 850 | case 8: *((ZyanI8* )address_of_offset) = (ZyanI8 )value; break; 851 | case 16: *((ZyanI16*)address_of_offset) = (ZyanI16)value; break; 852 | case 32: *((ZyanI32*)address_of_offset) = (ZyanI32)value; break; 853 | default: 854 | ZYAN_UNREACHABLE; 855 | } 856 | } 857 | 858 | return ZYAN_STATUS_SUCCESS; 859 | } 860 | 861 | /* ---------------------------------------------------------------------------------------------- */ 862 | 863 | /* ============================================================================================== */ 864 | /* Functions */ 865 | /* ============================================================================================== */ 866 | 867 | ZyanStatus ZyrexRelocateCode(const void* source, ZyanUSize source_length, 868 | ZyrexTrampolineChunk* trampoline, ZyanUSize min_bytes_to_reloc, ZyanUSize* bytes_read, 869 | ZyanUSize* bytes_written) 870 | { 871 | ZYAN_ASSERT(source); 872 | ZYAN_ASSERT(source_length); 873 | ZYAN_ASSERT(trampoline); 874 | ZYAN_ASSERT(min_bytes_to_reloc); 875 | ZYAN_ASSERT(bytes_read); 876 | ZYAN_ASSERT(bytes_written); 877 | 878 | ZyrexRelocationContext context; 879 | context.bytes_to_reloc = 0; 880 | context.source = source; 881 | context.source_length = source_length; 882 | context.destination = &trampoline->code_buffer; 883 | context.destination_length = ZYREX_TRAMPOLINE_MAX_CODE_SIZE + 884 | ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS; 885 | context.translation_map = &trampoline->translation_map; 886 | context.instructions_read = 0; 887 | context.instructions_written = 0; 888 | context.bytes_read = 0; 889 | context.bytes_written = 0; 890 | 891 | ZYAN_CHECK(ZyrexAnalyzeCode(source, source_length, min_bytes_to_reloc, &context.instructions, 892 | &context.bytes_to_reloc)); 893 | ZYAN_ASSERT(context.instructions.data); 894 | 895 | // Relocate instructions 896 | for (ZyanUSize i = 0; i < context.instructions.size; ++i) 897 | { 898 | // The code buffer is full 899 | ZYAN_ASSERT(context.bytes_written < context.destination_length); 900 | // The translation map is full 901 | ZYAN_ASSERT(context.instructions_read < ZYAN_ARRAY_LENGTH(context.translation_map->items)); 902 | 903 | const ZyrexAnalyzedInstruction* const item = ZyanVectorGet(&context.instructions, i); 904 | ZYAN_ASSERT(item); 905 | 906 | if (item->has_relative_target) 907 | { 908 | ZYAN_CHECK(ZyrexRelocateRelativeInstruction(&context, item)); 909 | } else 910 | { 911 | ZYAN_CHECK(ZyrexRelocateCommonInstruction(&context, item)); 912 | } 913 | 914 | context.bytes_read += item->instruction.length; 915 | ++context.instructions_read; 916 | } 917 | 918 | ZYAN_ASSERT(context.bytes_read == context.bytes_to_reloc); 919 | 920 | *bytes_read = context.bytes_read; 921 | *bytes_written = context.bytes_written; 922 | 923 | ZYAN_CHECK(ZyrexUpdateInstructionOffsets(&context)); 924 | 925 | return ZYAN_STATUS_SUCCESS; 926 | } 927 | 928 | /* ============================================================================================== */ 929 | --------------------------------------------------------------------------------