From af316e488e0b9e1ae98858ce1b7813fad4b6a604 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 17 May 2023 22:06:37 -0400 Subject: [PATCH 1/5] Initial test harness --- .gitignore | 1 + CMakeLists.txt | 35 ++++++++++---------- CMakePresets.json | 27 ++++++++++++++++ build.ps1 | 71 +++++++++++++++++++---------------------- build_desktop.ps1 | 42 ++++++++++++++++++++++++ qpm_defines_fixed.cmake | 60 ++++++++++++++++++++++++++++++++++ src/And64InlineHook.cpp | 10 ++++++ src/main.cpp | 2 +- test/main.cpp | 8 +++++ 9 files changed, 201 insertions(+), 55 deletions(-) create mode 100644 CMakePresets.json create mode 100644 build_desktop.ps1 create mode 100644 qpm_defines_fixed.cmake diff --git a/.gitignore b/.gitignore index a8529c7..120d0a7 100644 --- a/.gitignore +++ b/.gitignore @@ -56,5 +56,6 @@ extern/ extern.cmake qpm_defines.cmake build/ +build_desktop/ build-linux/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a4b6ea..d581c04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # include some defines automatically made by qpm -include(qpm_defines.cmake) +include(qpm_defines_fixed.cmake) cmake_minimum_required(VERSION 3.22) project(${COMPILE_ID}) @@ -69,7 +69,7 @@ target_include_directories(${COMPILE_ID} PRIVATE ${INCLUDE_DIR}) # add shared dir as include dir target_include_directories(${COMPILE_ID} PUBLIC ${SHARED_DIR}) -target_link_libraries(${COMPILE_ID} PRIVATE -llog GSL) +target_link_libraries(${COMPILE_ID} PRIVATE GSL) include(extern.cmake) MESSAGE(STATUS "extern added!") @@ -89,18 +89,21 @@ add_custom_command(TARGET ${COMPILE_ID} POST_BUILD COMMENT "Rename the stripped lib to regular" ) -enable_testing() -add_compile_options(-Wall -Wextra -Werror -Wpedantic) -add_executable( - test - test/main.cpp -) -target_include_directories(test PUBLIC ${SHARED_DIR}) -target_link_libraries( - test - GTest::gtest_main -) - -include(GoogleTest) -gtest_discover_tests(test) +if (DEFINED TEST_BUILD) + enable_testing() + + add_compile_options(-Wall -Wextra -Werror -Wpedantic) + add_executable( + test + test/main.cpp + ) + target_include_directories(test PUBLIC ${SHARED_DIR}) + target_link_libraries( + test + GTest::gtest_main + ) + + include(GoogleTest) + gtest_discover_tests(test) +endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..76e8df4 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,27 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "Android", + "generator": "Ninja", + "binaryDir": "./build" + }, + { + "name": "desktop", + "generator": "Ninja", + "binaryDir": "./build_desktop" + } + ], + "buildPresets": [ + { + "name": "Android", + "configurePreset": "Android", + "inheritConfigureEnvironment": true + }, + { + "name": "desktop", + "configurePreset": "desktop", + "inheritConfigureEnvironment": true + } + ] +} diff --git a/build.ps1 b/build.ps1 index 3b1fc72..56491c0 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,47 +1,42 @@ +Param( + [Parameter(Mandatory=$false)] + [Switch] $clean, -function Clean-Build-Folder { - if (Test-Path -Path "build") { - remove-item build -R - new-item -Path build -ItemType Directory - } - else { - new-item -Path build -ItemType Directory - } -} - -$NDKPath = Get-Content $PSScriptRoot/ndkpath.txt - -# Clean-Build-Folder -# build tests - -& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DTEST_BUILD=1 -B build -& cmake --build ./build + [Parameter(Mandatory=$false)] + [Switch] $help, -$ExitCode = $LastExitCode + [Parameter(Mandatory=$false)] + [Switch] $test +) +if ($help -eq $true) { + Write-Output "`"Build`" - Copiles your mod into a `".so`" or a `".a`" library" + Write-Output "`n-- Arguments --`n" -if (-not ($ExitCode -eq 0)) { - $msg = "ExitCode: " + $ExitCode - Write-Output $msg - exit $ExitCode + Write-Output "-clean `t`t Deletes the `"build`" folder, so that the entire library is rebuilt" + Write-Output "-test `t`t Creates a test build" + Write-Output "-help `t`t Prints this message" + exit } -# clean folder -Clean-Build-Folder -# build mod - -& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build -& cmake --build ./build - -$ExitCode = $LastExitCode +# if user specified clean, remove all build files +if ($clean.IsPresent) +{ + if (Test-Path -Path "build") + { + remove-item build -R + } +} -# Post build, we actually want to transform the compile_commands.json file such that it has no \\ characters and ONLY has / characters -# (Get-Content -Path build/compile_commands.json) | -# ForEach-Object {$_ -Replace '\\\\', '/'} | Set-Content -Path build/compile_commands.json -# To build tests, we just compile with our local clang++ into an executable -# Kind of wacky but will work on linux -# Requires libcapstone-dev installed, and GSL/gtest headers fetched from cmake -# clang++ test/main.cpp src/trampoline.cpp src/trampoline-allocator.cpp -o build/test -std=c++20 -I/usr/include/ -Ishared -Ibuild/_deps/googletest-src/googletest/include -Ibuild/_deps/gsl-src/include -lcapstone -Iextern/includes/fmt/fmt/include -L/usr/lib/x86_64-linux-gnu -DFMT_HEADER_ONLY -Wall -Wextra -Werror -g +if (($clean.IsPresent) -or (-not (Test-Path -Path "build"))) +{ + $out = new-item -Path build -ItemType Directory +} -exit $ExitCode \ No newline at end of file +if ($test.IsPresent) { + & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DTEST_BUILD=1 -B build --preset Android -DQPM_ANDROID= +} else { + & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build --preset Android -DQPM_ANDROID= +} +& cmake --build ./build diff --git a/build_desktop.ps1 b/build_desktop.ps1 new file mode 100644 index 0000000..67a8ffc --- /dev/null +++ b/build_desktop.ps1 @@ -0,0 +1,42 @@ +Param( + [Parameter(Mandatory=$false)] + [Switch] $clean, + + [Parameter(Mandatory=$false)] + [Switch] $help, + + [Parameter(Mandatory=$false)] + [Switch] $test +) + +if ($help -eq $true) { + Write-Output "`"Build`" - Copiles your mod into a `".so`" or a `".a`" library" + Write-Output "`n-- Arguments --`n" + + Write-Output "-clean `t`t Deletes the `"build`" folder, so that the entire library is rebuilt" + Write-Output "-test `t`t Creates a test build" + Write-Output "-help `t`t Prints this message" + exit +} + +# if user specified clean, remove all build files +if ($clean.IsPresent) +{ + if (Test-Path -Path "build") + { + remove-item build -R + } +} + + +if (($clean.IsPresent) -or (-not (Test-Path -Path "build"))) +{ + $out = new-item -Path build -ItemType Directory +} + +if ($test.IsPresent) { + & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DTEST_BUILD=1 -B build_desktop --preset desktop +} else { + & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build_desktop --preset desktop +} +& cmake --build ./build_desktop diff --git a/qpm_defines_fixed.cmake b/qpm_defines_fixed.cmake new file mode 100644 index 0000000..c497ce0 --- /dev/null +++ b/qpm_defines_fixed.cmake @@ -0,0 +1,60 @@ +# Modified qpm_defines.cmake to allow cross compiling and desktop compiling +# Version defines, pretty useful +set(MOD_VERSION "0.1.0") +# take the mod name and just remove spaces, that will be MOD_ID, if you don't like it change it after the include of this file +set(MOD_ID "flamingo") + +# derived from override .so name or just id_version +set(COMPILE_ID "flamingo") +# derived from whichever codegen package is installed, will default to just codegen +set(CODEGEN_ID "codegen") + +# given from qpm, automatically updated from qpm.json +set(EXTERN_DIR_NAME "extern") +set(SHARED_DIR_NAME "shared") + +# if no target given, use Debug +if (NOT DEFINED CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# defines used in ninja / cmake ndk builds + +if (DEFINED QPM_ANDROID) + if (NOT DEFINED CMAKE_ANDROID_NDK) + if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/ndkpath.txt") + file (STRINGS "ndkpath.txt" CMAKE_ANDROID_NDK) + else() + if(EXISTS $ENV{ANDROID_NDK_HOME}) + set(CMAKE_ANDROID_NDK $ENV{ANDROID_NDK_HOME}) + elseif(EXISTS $ENV{ANDROID_NDK_LATEST_HOME}) + set(CMAKE_ANDROID_NDK $ENV{ANDROID_NDK_LATEST_HOME}) + endif() + endif() + endif() + if (NOT DEFINED CMAKE_ANDROID_NDK) + message(Big time error buddy, no NDK) + endif() + message(Using NDK ${CMAKE_ANDROID_NDK}) + string(REPLACE "\\" "/" CMAKE_ANDROID_NDK ${CMAKE_ANDROID_NDK}) + + set(ANDROID_PLATFORM 24) + set(ANDROID_ABI arm64-v8a) + set(ANDROID_STL c++_static) + set(ANDROID_USE_LEGACY_TOOLCHAIN_FILE OFF) + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake) +endif() + +# define used for external data, mostly just the qpm dependencies +set(EXTERN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${EXTERN_DIR_NAME}) +set(SHARED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${SHARED_DIR_NAME}) +# get files by filter recursively +MACRO(RECURSE_FILES return_list filter) + FILE(GLOB_RECURSE new_list ${filter}) + SET(file_list "") + FOREACH(file_path ${new_list}) + SET(file_list ${file_list} ${file_path}) + ENDFOREACH() + LIST(REMOVE_DUPLICATES file_list) + SET(${return_list} ${file_list}) +ENDMACRO() \ No newline at end of file diff --git a/src/And64InlineHook.cpp b/src/And64InlineHook.cpp index b6876da..7c0a3db 100644 --- a/src/And64InlineHook.cpp +++ b/src/And64InlineHook.cpp @@ -29,7 +29,12 @@ #include #include #include + +#if __has_include() #include +#define FLAMINGO_ANDROID_LOG +#endif + #include #include #ifdef __aarch64__ @@ -39,7 +44,12 @@ #define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2) #define A64_NOP 0xd503201fu #define A64_JNIEXPORT __attribute__((visibility("default"))) +#ifdef FLAMINGO_ANDROID_LOG #define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__)) +#else +#define A64_LOGE(...) ((void)0) +#endif + #ifndef NDEBUG # define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__)) #else diff --git a/src/main.cpp b/src/main.cpp index 71e827e..191e991 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include "more_stuff.hpp" #include "hook-installer.hpp" // #include "hook-installer.hpp" -#include "modloader/shared/modloader.hpp" +// #include "modloader/shared/modloader.hpp" // #include #include #include "enum-helpers.hpp" diff --git a/test/main.cpp b/test/main.cpp index 27fe136..81dbcc8 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -8,6 +8,14 @@ #include "../shared/trampoline.hpp" #include "capstone/capstone.h" +#include "../shared/hook-installer.hpp" +#include "../shared/more_stuff.hpp" +// #include "hook-installer.hpp" +// #include "modloader/shared/modloader.hpp" +// #include +#include +#include "../shared/enum-helpers.hpp" + decltype(auto) test_near(uint32_t* target, uint32_t const* callback) { constexpr size_t hookSize = 32; constexpr size_t pageSize = 4096; From e3603cf83acbfe7bf4cbededd460f99ab5e07396 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 17 May 2023 22:10:54 -0400 Subject: [PATCH 2/5] Fix compile --- test/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/main.cpp b/test/main.cpp index 81dbcc8..a080cb4 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -8,13 +8,13 @@ #include "../shared/trampoline.hpp" #include "capstone/capstone.h" -#include "../shared/hook-installer.hpp" -#include "../shared/more_stuff.hpp" +// #include "../shared/hook-installer.hpp" +// #include "../shared/more_stuff.hpp" // #include "hook-installer.hpp" // #include "modloader/shared/modloader.hpp" // #include #include -#include "../shared/enum-helpers.hpp" +// #include "../shared/enum-helpers.hpp" decltype(auto) test_near(uint32_t* target, uint32_t const* callback) { constexpr size_t hookSize = 32; From 21bf0722d043416ac3e91c4d8109e35007434631 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 17 May 2023 22:19:16 -0400 Subject: [PATCH 3/5] Unused --- src/And64InlineHook.cpp | 601 ---------------------------------------- 1 file changed, 601 deletions(-) delete mode 100644 src/And64InlineHook.cpp diff --git a/src/And64InlineHook.cpp b/src/And64InlineHook.cpp deleted file mode 100644 index 7c0a3db..0000000 --- a/src/And64InlineHook.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/* - * @date : 2018/04/18 - * @author : Rprop (r_prop@outlook.com) - * https://github.com/Rprop/And64InlineHook - */ -/* - MIT License - - Copyright (c) 2018 Rprop (r_prop@outlook.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ -#include -#include -#include - -#if __has_include() -#include -#define FLAMINGO_ANDROID_LOG -#endif - -#include -#include -#ifdef __aarch64__ - -#include "And64InlineHook.hpp" -#define A64_MAX_INSTRUCTIONS 5 -#define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2) -#define A64_NOP 0xd503201fu -#define A64_JNIEXPORT __attribute__((visibility("default"))) -#ifdef FLAMINGO_ANDROID_LOG -#define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__)) -#else -#define A64_LOGE(...) ((void)0) -#endif - -#ifndef NDEBUG -# define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__)) -#else -# define A64_LOGI(...) ((void)0) -#endif // NDEBUG -using instruction = uint32_t *__restrict *__restrict; -struct context -{ - struct fix_info - { - uint32_t *bp; - uint32_t ls; // left-shift counts - uint32_t ad; // & operand - }; - struct insns_info - { - union - { - uint64_t insu; - int64_t ins; - void *insp; - }; - fix_info fmap[A64_MAX_REFERENCES]; - }; - int64_t basep; - int64_t endp; - insns_info dat[A64_MAX_INSTRUCTIONS]; - - inline bool is_in_fixing_range(const int64_t absolute_addr) { - return absolute_addr >= this->basep && absolute_addr < this->endp; - } - inline intptr_t get_ref_ins_index(const int64_t absolute_addr) { - return static_cast((absolute_addr - this->basep) / sizeof(uint32_t)); - } - inline intptr_t get_and_set_current_index(uint32_t *__restrict inp, uint32_t *__restrict outp) { - intptr_t current_idx = this->get_ref_ins_index(reinterpret_cast(inp)); - this->dat[current_idx].insp = outp; - return current_idx; - } - inline void reset_current_ins(const intptr_t idx, uint32_t *__restrict outp) { - this->dat[idx].insp = outp; - } - void insert_fix_map(const intptr_t idx, uint32_t *bp, uint32_t ls = 0u, uint32_t ad = 0xffffffffu) { - for (auto &f : this->dat[idx].fmap) { - if (f.bp == NULL) { - f.bp = bp; - f.ls = ls; - f.ad = ad; - return; - } //if - } - // What? GGing.. - } - void process_fix_map(const intptr_t idx) { - for (auto &f : this->dat[idx].fmap) { - if (f.bp == NULL) break; - *(f.bp) = *(f.bp) | (((int32_t(this->dat[idx].ins - reinterpret_cast(f.bp)) >> 2) << f.ls) & f.ad); - f.bp = NULL; - } - } -}; - -//------------------------------------------------------------------------- - -static bool __fix_branch_imm(instruction inpp, instruction outpp, context *ctxp) -{ - static constexpr uint32_t mbits = 6u; - static constexpr uint32_t mask = 0xfc000000u; // 0b11111100000000000000000000000000 - static constexpr uint32_t rmask = 0x03ffffffu; // 0b00000011111111111111111111111111 - static constexpr uint32_t op_b = 0x14000000u; // "b" ADDR_PCREL26 - static constexpr uint32_t op_bl = 0x94000000u; // "bl" ADDR_PCREL26 - - const uint32_t ins = *(*inpp); - const uint32_t opc = ins & mask; - switch (opc) { - case op_b: - case op_bl: - { - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + (static_cast(ins << mbits) >> (mbits - 2u)); // sign-extended - int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - // whether the branch should be converted to absolute jump - if (!special_fix_type && llabs(new_pc_offset) >= (rmask >> 1)) { - bool b_aligned = (reinterpret_cast(*outpp + 2) & 7u) == 0u; - if (opc == op_b) { - if (b_aligned != true) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = 0x58000051u; // LDR X17, #0x8 - (*outpp)[1] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); - *outpp += 4; - } else { - if (b_aligned == true) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = 0x58000071u; // LDR X17, #12 - (*outpp)[1] = 0x1000009eu; // ADR X30, #16 - (*outpp)[2] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 3, &absolute_addr, sizeof(absolute_addr)); - *outpp += 5; - } //if - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)) >> 2; - } else { - ctxp->insert_fix_map(ref_idx, *outpp, 0u, rmask); - new_pc_offset = 0; - } //if - } //if - - (*outpp)[0] = opc | (new_pc_offset & ~mask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; - } - } - return false; -} - -//------------------------------------------------------------------------- - -static bool __fix_cond_comp_test_branch(instruction inpp, instruction outpp, context *ctxp) -{ - static constexpr uint32_t lsb = 5u; - static constexpr uint32_t lmask01 = 0xff00001fu; // 0b11111111000000000000000000011111 - static constexpr uint32_t mask0 = 0xff000010u; // 0b11111111000000000000000000010000 - static constexpr uint32_t op_bc = 0x54000000u; // "b.c" ADDR_PCREL19 - static constexpr uint32_t mask1 = 0x7f000000u; // 0b01111111000000000000000000000000 - static constexpr uint32_t op_cbz = 0x34000000u; // "cbz" Rt, ADDR_PCREL19 - static constexpr uint32_t op_cbnz = 0x35000000u; // "cbnz" Rt, ADDR_PCREL19 - static constexpr uint32_t lmask2 = 0xfff8001fu; // 0b11111111111110000000000000011111 - static constexpr uint32_t mask2 = 0x7f000000u; // 0b01111111000000000000000000000000 - static constexpr uint32_t op_tbz = 0x36000000u; // 0b00110110000000000000000000000000 "tbz" Rt, BIT_NUM, ADDR_PCREL14 - static constexpr uint32_t op_tbnz = 0x37000000u; // 0b00110111000000000000000000000000 "tbnz" Rt, BIT_NUM, ADDR_PCREL14 - - const uint32_t ins = *(*inpp); - uint32_t lmask = lmask01; - if ((ins & mask0) != op_bc) { - uint32_t opc = ins & mask1; - if (opc != op_cbz && opc != op_cbnz) { - opc = ins & mask2; - if (opc != op_tbz && opc != op_tbnz) { - return false; - } //if - lmask = lmask2; - } //if - } //if - - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + ((ins & ~lmask) >> (lsb - 2u)); - int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - if (!special_fix_type && llabs(new_pc_offset) >= (~lmask >> (lsb + 1))) { - if ((reinterpret_cast(*outpp + 4) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = (((8u >> 2u) << lsb) & ~lmask) | (ins & lmask); // B.C #0x8 - (*outpp)[1] = 0x14000005u; // B #0x14 - (*outpp)[2] = 0x58000051u; // LDR X17, #0x8 - (*outpp)[3] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 4, &absolute_addr, sizeof(absolute_addr)); - *outpp += 6; - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)) >> 2; - } else { - ctxp->insert_fix_map(ref_idx, *outpp, lsb, ~lmask); - new_pc_offset = 0; - } //if - } //if - - (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~lmask) | (ins & lmask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; -} - -//------------------------------------------------------------------------- - -static bool __fix_loadlit(instruction inpp, instruction outpp, context *ctxp) -{ - const uint32_t ins = *(*inpp); - - // memory prefetch("prfm"), just skip it - // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897420050.html - if ((ins & 0xff000000u) == 0xd8000000u) { - ctxp->process_fix_map(ctxp->get_and_set_current_index(*inpp, *outpp)); - ++(*inpp); - return true; - } //if - - static constexpr uint32_t msb = 8u; - static constexpr uint32_t lsb = 5u; - static constexpr uint32_t mask_30 = 0x40000000u; // 0b01000000000000000000000000000000 - static constexpr uint32_t mask_31 = 0x80000000u; // 0b10000000000000000000000000000000 - static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 - static constexpr uint32_t mask_ldr = 0xbf000000u; // 0b10111111000000000000000000000000 - static constexpr uint32_t op_ldr = 0x18000000u; // 0b00011000000000000000000000000000 "LDR Wt/Xt, label" | ADDR_PCREL19 - static constexpr uint32_t mask_ldrv = 0x3f000000u; // 0b00111111000000000000000000000000 - static constexpr uint32_t op_ldrv = 0x1c000000u; // 0b00011100000000000000000000000000 "LDR St/Dt/Qt, label" | ADDR_PCREL19 - static constexpr uint32_t mask_ldrsw = 0xff000000u; // 0b11111111000000000000000000000000 - static constexpr uint32_t op_ldrsw = 0x98000000u; // "LDRSW Xt, label" | ADDR_PCREL19 | load register signed word - // LDR S0, #0 | 0b00011100000000000000000000000000 | 32-bit - // LDR D0, #0 | 0b01011100000000000000000000000000 | 64-bit - // LDR Q0, #0 | 0b10011100000000000000000000000000 | 128-bit - // INVALID | 0b11011100000000000000000000000000 | may be 256-bit - - uint32_t mask = mask_ldr; - uintptr_t faligned = (ins & mask_30) ? 7u : 3u; - if ((ins & mask_ldr) != op_ldr) { - mask = mask_ldrv; - if (faligned != 7u) - faligned = (ins & mask_31) ? 15u : 3u; - if ((ins & mask_ldrv) != op_ldrv) { - if ((ins & mask_ldrsw) != op_ldrsw) { - return false; - } //if - mask = mask_ldrsw; - faligned = 7u; - } //if - } //if - - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + ((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u); - int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - // special_fix_type may encounter issue when there are mixed data and code - if (special_fix_type || (llabs(new_pc_offset) + (faligned + 1u - 4u) / 4u) >= (~lmask >> (lsb + 1))) { // inaccurate, but it works - while ((reinterpret_cast(*outpp + 2) & faligned) != 0u) { - *(*outpp)++ = A64_NOP; - } - ctxp->reset_current_ins(current_idx, *outpp); - - // Note that if memory at absolute_addr is writeable (non-const), we will fail to fetch it. - // And what's worse, we may unexpectedly overwrite something if special_fix_type is true... - uint32_t ns = static_cast((faligned + 1) / sizeof(uint32_t)); - (*outpp)[0] = (((8u >> 2u) << lsb) & ~mask) | (ins & lmask); // LDR #0x8 - (*outpp)[1] = 0x14000001u + ns; // B #0xc - memcpy(*outpp + 2, reinterpret_cast(absolute_addr), faligned + 1); - *outpp += 2 + ns; - } else { - faligned >>= 2; // new_pc_offset is shifted and 4-byte aligned - while ((new_pc_offset & faligned) != 0) { - *(*outpp)++ = A64_NOP; - new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; - } - ctxp->reset_current_ins(current_idx, *outpp); - - (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~mask) | (ins & lmask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; -} - -//------------------------------------------------------------------------- - -static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) -{ - // Load a PC-relative address into a register - // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html - static constexpr uint32_t msb = 8u; - static constexpr uint32_t lsb = 5u; - static constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000 - static constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111 - static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 - static constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111 - static constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111 - static constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21 - static constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP - - const uint32_t ins = *(*inpp); - intptr_t current_idx; - switch (ins & mask) { - case op_adr: - { - current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t lsb_bytes = static_cast(ins << 1u) >> 30u; - int64_t absolute_addr = reinterpret_cast(*inpp) + (((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u) | lsb_bytes); - int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)); - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - if (!special_fix_type && llabs(new_pc_offset) >= (max_val >> 1)) { - if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - - (*outpp)[0] = 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 - (*outpp)[1] = 0x14000003u; // B #0xc - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); - *outpp += 4; - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)); - } else { - ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask); - new_pc_offset = 0; - } //if - } //if - - // the lsb_bytes will never be changed, so we can use lmask to keep it - (*outpp)[0] = (static_cast(new_pc_offset << (lsb - 2u)) & fmask) | (ins & lmask); - ++(*outpp); - } //if - } - break; - case op_adrp: - { - current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int32_t lsb_bytes = static_cast(ins << 1u) >> 30u; - int64_t absolute_addr = (reinterpret_cast(*inpp) & ~0xfffll) + ((((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u) | lsb_bytes) << 12); - A64_LOGI("ins = 0x%.8X, pc = %p, abs_addr = %p", - ins, *inpp, reinterpret_cast(absolute_addr)); - if (ctxp->is_in_fixing_range(absolute_addr)) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr/* & ~3ull*/); - if (ref_idx > current_idx) { - // the bottom 12 bits of absolute_addr are masked out, - // so ref_idx must be less than or equal to current_idx! - A64_LOGE("ref_idx must be less than or equal to current_idx!"); - } //if - - // *absolute_addr may be changed due to relocation fixing - A64_LOGI("What is the correct way to fix this?"); - *(*outpp)++ = ins; // 0x90000000u; - } else { - if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - - (*outpp)[0] = 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 - (*outpp)[1] = 0x14000003u; // B #0xc - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); // potential overflow? - *outpp += 4; - } //if - } - break; - default: - return false; - } - - ctxp->process_fix_map(current_idx); - ++(*inpp); - return true; -} - -//------------------------------------------------------------------------- -#define __flush_cache(c, n) __builtin___clear_cache(reinterpret_cast(c), reinterpret_cast(c) + n) -static void __fix_instructions(uint32_t *__restrict inp, int32_t count, uint32_t *__restrict outp) -{ - context ctx; - ctx.basep = reinterpret_cast(inp); - ctx.endp = reinterpret_cast(inp + count); - memset(ctx.dat, 0, sizeof(ctx.dat)); - static_assert(sizeof(ctx.dat) / sizeof(ctx.dat[0]) == A64_MAX_INSTRUCTIONS, - "please use A64_MAX_INSTRUCTIONS!"); -#ifndef NDEBUG - if (count > A64_MAX_INSTRUCTIONS) { - A64_LOGE("too many fixing instructions!"); - } //if -#endif // NDEBUG - - uint32_t *const outp_base = outp; - - while (--count >= 0) { - if (__fix_branch_imm(&inp, &outp, &ctx)) continue; - if (__fix_cond_comp_test_branch(&inp, &outp, &ctx)) continue; - if (__fix_loadlit(&inp, &outp, &ctx)) continue; - if (__fix_pcreladdr(&inp, &outp, &ctx)) continue; - - // without PC-relative offset - ctx.process_fix_map(ctx.get_and_set_current_index(inp, outp)); - *(outp++) = *(inp++); - } - - static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 - auto callback = reinterpret_cast(inp); - auto pc_offset = static_cast(callback - reinterpret_cast(outp)) >> 2; - if (llabs(pc_offset) >= (mask >> 1)) { - if ((reinterpret_cast(outp + 2) & 7u) != 0u) { - outp[0] = A64_NOP; - ++outp; - } //if - outp[0] = 0x58000051u; // LDR X17, #0x8 - outp[1] = 0xd61f0220u; // BR X17 - *reinterpret_cast(outp + 2) = callback; - outp += 4; - } else { - outp[0] = 0x14000000u | (pc_offset & mask); // "B" ADDR_PCREL26 - ++outp; - } //if - - const uintptr_t total = (outp - outp_base) * sizeof(uint32_t); - __flush_cache(outp_base, total); // necessary -} - -//------------------------------------------------------------------------- - -extern "C" { -#define __attribute __attribute__ -#define aligned(x) __aligned__(x) -#define __intval(p) reinterpret_cast(p) -#define __uintval(p) reinterpret_cast(p) -#define __ptr(p) reinterpret_cast(p) -#define __page_size 4096 -#define __page_align(n) __align_up(static_cast(n), __page_size) -#define __ptr_align(x) __ptr(__align_down(reinterpret_cast(x), __page_size)) -#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) -#define __align_down(x, n) ((x) & -(n)) -#define __countof(x) static_cast(sizeof(x) / sizeof((x)[0])) // must be signed -#define __atomic_increase(p) __sync_add_and_fetch(p, 1) -#define __sync_cmpswap(p, v, n) __sync_bool_compare_and_swap(p, v, n) -#define __predict_true(exp) __builtin_expect((exp) != 0, 1) - -#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \ - __page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \ - PROT_READ | PROT_WRITE | PROT_EXEC) - - //------------------------------------------------------------------------- - - static __attribute((aligned(__page_size))) uint32_t __insns_pool[A64_MAX_BACKUPS][A64_MAX_INSTRUCTIONS * 10]; - - //------------------------------------------------------------------------- - - class A64HookInit - { - public: - A64HookInit() - { - __make_rwx(__insns_pool, sizeof(__insns_pool)); - A64_LOGI("insns pool initialized."); - } - }; - static A64HookInit __init; - - //------------------------------------------------------------------------- - - static uint32_t *FastAllocateTrampoline() - { - static_assert((A64_MAX_INSTRUCTIONS * 10 * sizeof(uint32_t)) % 8 == 0, "8-byte align"); - static volatile int32_t __index = -1; - - int32_t i = __atomic_increase(&__index); - if (__predict_true(i >= 0 && i < __countof(__insns_pool))) { - return __insns_pool[i]; - } //if - - A64_LOGE("failed to allocate trampoline!"); - return NULL; - } - - //------------------------------------------------------------------------- - - A64_JNIEXPORT void *A64HookFunctionV(void *const symbol, void *const replace, - void *const rwx, const uintptr_t rwx_size) - { - static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 - - uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol); - - static_assert(A64_MAX_INSTRUCTIONS >= 5, "please fix A64_MAX_INSTRUCTIONS!"); - auto pc_offset = static_cast(__intval(replace) - __intval(symbol)) >> 2; - if (llabs(pc_offset) >= (mask >>1)) { - int32_t count = (reinterpret_cast(original + 2) & 7u) != 0u ? 5 : 4; - if (trampoline) { - if (rwx_size < count * 10u) { - // LOGW("rwx size is too small to hold %u bytes backup instructions!", count * 10u); - return NULL; - } //if - __fix_instructions(original, count, trampoline); - } //if - - if (__make_rwx(original, 5 * sizeof(uint32_t)) == 0) { - if (count == 5) { - original[0] = A64_NOP; - ++original; - } //if - original[0] = 0x58000051u; // LDR X17, #0x8 - original[1] = 0xd61f0220u; // BR X17 - *reinterpret_cast(original + 2) = __intval(replace); - __flush_cache(symbol, 5 * sizeof(uint32_t)); - - A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten", - symbol, replace, 5 * sizeof(uint32_t)); - } else { - A64_LOGE("mprotect failed with errno = %d, p = %p, size = %zu", - errno, original, 5 * sizeof(uint32_t)); - trampoline = NULL; - } //if - } else { - if (trampoline) { - if (rwx_size < 1u * 10u) { - // LOGW("rwx size is too small to hold %u bytes backup instructions!", 1u * 10u); - return NULL; - } //if - __fix_instructions(original, 1, trampoline); - } //if - - if (__make_rwx(original, 1 * sizeof(uint32_t)) == 0) { - __sync_cmpswap(original, *original, 0x14000000u | (pc_offset & mask)); // "B" ADDR_PCREL26 - __flush_cache(symbol, 1 * sizeof(uint32_t)); - - A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten", - symbol, replace, 1 * sizeof(uint32_t)); - } else { - A64_LOGE("mprotect failed with errno = %d, p = %p, size = %zu", - errno, original, 1 * sizeof(uint32_t)); - trampoline = NULL; - } //if - } //if - - return trampoline; - } - - //------------------------------------------------------------------------- - - A64_JNIEXPORT void A64HookFunction(void *const symbol, void *const replace, void **result) - { - void *trampoline = NULL; - if (result != NULL) { - trampoline = FastAllocateTrampoline(); - *result = trampoline; - if (trampoline == NULL) return; - } //if - - trampoline = A64HookFunctionV(symbol, replace, trampoline, A64_MAX_INSTRUCTIONS * 10u); - if (trampoline == NULL && result != NULL) { - *result = NULL; - } //if - } -} - -#endif // defined(__aarch64__) \ No newline at end of file From cbbf69ea8fbdc7946ab3315c707b4cc4f9495f01 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 17 May 2023 22:23:21 -0400 Subject: [PATCH 4/5] Use standard C++ STL --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d581c04..0067088 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ FetchContent_MakeAvailable(googletest) # c++ standard set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED 20) +set(CMAKE_CXX_EXTENSIONS OFF) # define that stores the actual source directory set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) From b562010a0aadaf9198c6ec39452f24cff21422d7 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 17 May 2023 22:23:27 -0400 Subject: [PATCH 5/5] Add optional include --- shared/hook-installer.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/hook-installer.hpp b/shared/hook-installer.hpp index 64efa24..d1d1d0f 100644 --- a/shared/hook-installer.hpp +++ b/shared/hook-installer.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "hook-data.hpp" #include "trampoline-allocator.hpp"