diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..9e1f4440 --- /dev/null +++ b/.clangd @@ -0,0 +1,6 @@ +If: + # Note: This is a regexp, notice '.*' at the end of PathMatch string. + PathMatch: ./extern/.* +Index: + # Disable slow background indexing of these files. + Background: Skip \ No newline at end of file diff --git a/.github/workflows/build-ndk.yml b/.github/workflows/build-ndk.yml index 5ec09a24..7787e0e1 100644 --- a/.github/workflows/build-ndk.yml +++ b/.github/workflows/build-ndk.yml @@ -32,7 +32,7 @@ jobs: - name: Create ndkpath.txt run: | - echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt + echo ${{ steps.setup-ndk.outputs.ndk-path }} > ${GITHUB_WORKSPACE}/ndkpath.txt cat ${GITHUB_WORKSPACE}/ndkpath.txt - name: QPM Rust Action @@ -71,7 +71,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: debug_${{ steps.libname.outputs.NAME }} - path: ./build/debug_${{ steps.libname.outputs.NAME }} + path: ./build/debug/${{ steps.libname.outputs.NAME }} if-no-files-found: error # TODO: Add auto-populating releases, auto update versions, auto publish package on release diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2c007ddb..7937dc14 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,15 +28,14 @@ jobs: - name: Create ndkpath.txt run: | - echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt + echo ${{ steps.setup-ndk.outputs.ndk-path }} > ${GITHUB_WORKSPACE}/ndkpath.txt cat ${GITHUB_WORKSPACE}/ndkpath.txt - - name: Get Tag Version - id: get_tag_version + - name: Extract version + id: version run: | - echo ${GITHUB_REF#refs/tags/} - echo ::set-output name=TAG::${GITHUB_REF#refs/tags/} - echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} + echo "TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_OUTPUT} + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> ${GITHUB_OUTPUT} - name: QPM Rust Action uses: Fernthedev/qpm-rust-action@main @@ -50,8 +49,8 @@ jobs: publish: true publish_token: ${{secrets.PUBLISH_KEY}} - version: ${{ steps.get_tag_version.outputs.VERSION }} - tag: ${{ steps.get_tag_version.outputs.TAG }} + version: ${{ steps.version.outputs.VERSION }} + tag: ${{ steps.version.outputs.TAG }} # set to true if applicable, ASSUMES the file is already a release asset qpm_release_bin: true @@ -68,14 +67,14 @@ jobs: cd ./build/ pattern="lib${module_id}*.so" files=( $pattern ) - echo ::set-output name=NAME::"${files[0]}" + echo "NAME=${files[0]}" >> ${GITHUB_OUTPUT} + - name: Rename debug lib + run: mv ./build/debug/${{ steps.libname.outputs.NAME }} ./build/debug_${{ steps.libname.outputs.NAME }} - name: Upload to Release - id: upload_file_release uses: softprops/action-gh-release@v0.1.15 with: - name: ${{ github.event.inputs.release_msg }} - tag_name: ${{ github.event.inputs.version }} + tag_name: ${{ steps.version.outputs.TAG }} files: | ./build/${{ steps.libname.outputs.NAME }} ./build/debug_${{ steps.libname.outputs.NAME }} diff --git a/CMakeLists.txt b/CMakeLists.txt index b64ca0d5..992025b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED 20) # LTO set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) # define that stores the actual source directory set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) @@ -17,62 +18,103 @@ set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) # compile options used add_compile_options(-frtti -fPIE -fPIC -fexceptions -flto) add_compile_options(-Wall -Wextra -Werror -Wno-unused-function) +# clangd bug https://github.com/clangd/clangd/issues/1167 +add_compile_options(-Wno-pragma-pack) # compile definitions used add_compile_definitions(VERSION=\"${MOD_VERSION}\") add_compile_definitions(MOD_ID=\"${MOD_ID}\") add_compile_definitions(VERSION_NUMBER=300150000) add_compile_definitions(UNITY_2019) +add_compile_definitions(UNITY_2021) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if (DEFINED TEST_BUILD) - MESSAGE(STATUS "Compiling with test defines") - add_compile_definitions(TEST_CALLBACKS) - add_compile_definitions(TEST_SAFEPTR) - add_compile_definitions(TEST_BYREF) - add_compile_definitions(TEST_ARRAY) - add_compile_definitions(TEST_LIST) - add_compile_definitions(TEST_STRING) - add_compile_definitions(TEST_HOOK) -endif() - -add_library( - ${COMPILE_ID} - SHARED +function(setup_target target add_test) + message(STATUS "Setting up target ${target}") + + add_library( + ${target} + SHARED + ) + + if (add_test) + target_compile_definitions( + ${target} + PRIVATE + + TEST_CALLBACKS + TEST_SAFEPTR + TEST_BYREF + TEST_ARRAY + TEST_LIST + TEST_STRING + TEST_HOOK + TEST_THREAD + TEST_UNITYW + ) + endif() + + # recursively get all src files + RECURSE_FILES(cpp_file_list_utils ${SOURCE_DIR}/utils/*.cpp) + RECURSE_FILES(c_file_list_utils ${SOURCE_DIR}/utils/*.c) + target_sources(${target} PRIVATE ${cpp_file_list_utils}) + target_sources(${target} PRIVATE ${c_file_list_utils}) + + RECURSE_FILES(cpp_file_list_config ${SOURCE_DIR}/config/*.cpp) + RECURSE_FILES(c_file_list_config ${SOURCE_DIR}/config/*.c) + target_sources(${target} PRIVATE ${cpp_file_list_config}) + target_sources(${target} PRIVATE ${c_file_list_config}) + + if (add_test) + message(STATUS "Adding tests source files to target ${target}") + RECURSE_FILES(cpp_file_list_tests ${SOURCE_DIR}/tests/*.cpp) + RECURSE_FILES(c_file_list_tests ${SOURCE_DIR}/tests/*.c) + target_sources(${target} PRIVATE ${cpp_file_list_tests}) + target_sources(${target} PRIVATE ${c_file_list_tests}) + endif() + + # add root dir as include dir + target_include_directories(${target} PRIVATE ${CMAKE_SOURCE_DIR}) + # add src dir as include dir + target_include_directories(${target} PRIVATE ${SOURCE_DIR}) + # add include dir as include dir + target_include_directories(${target} PRIVATE ${INCLUDE_DIR}) + # add shared dir as include dir + target_include_directories(${target} PUBLIC ${SHARED_DIR}) + + target_link_directories(${target} PRIVATE ${EXTERN_DIR}/libs) + target_link_libraries(${target} PRIVATE -llog) + target_link_options(${target} PRIVATE "-fuse-ld=ld") + + file(GLOB_RECURSE so_libs ${EXTERN_DIR}/libs/*.so) + file(GLOB_RECURSE a_libs ${EXTERN_DIR}/libs/*.a) + + target_link_libraries( + ${target} + PRIVATE + ${so_libs} + ${a_libs} + ) + + target_include_directories(${target} PRIVATE ${EXTERN_DIR}/includes) + target_include_directories(${target} SYSTEM PRIVATE ${EXTERN_DIR}/includes/libil2cpp/il2cpp/libil2cpp) +endfunction() + +# compile test as one project +setup_target(test-${COMPILE_ID} YES) +# regular bs hook +setup_target(${COMPILE_ID} NO) + +add_dependencies(${COMPILE_ID} test-${COMPILE_ID}) + +target_compile_definitions( + ${COMPILE_ID} + PRIVATE + NO_TEST ) -# recursively get all src files -RECURSE_FILES(cpp_file_list_utils ${SOURCE_DIR}/utils/*.cpp) -RECURSE_FILES(c_file_list_utils ${SOURCE_DIR}/utils/*.c) -target_sources(${COMPILE_ID} PRIVATE ${cpp_file_list_utils}) -target_sources(${COMPILE_ID} PRIVATE ${c_file_list_utils}) - -RECURSE_FILES(cpp_file_list_config ${SOURCE_DIR}/config/*.cpp) -RECURSE_FILES(c_file_list_config ${SOURCE_DIR}/config/*.c) -target_sources(${COMPILE_ID} PRIVATE ${cpp_file_list_config}) -target_sources(${COMPILE_ID} PRIVATE ${c_file_list_config}) - -if (DEFINED TEST_BUILD) -RECURSE_FILES(cpp_file_list_tests ${SOURCE_DIR}/tests/*.cpp) -RECURSE_FILES(c_file_list_tests ${SOURCE_DIR}/tests/*.c) -target_sources(${COMPILE_ID} PRIVATE ${cpp_file_list_tests}) -target_sources(${COMPILE_ID} PRIVATE ${c_file_list_tests}) -endif() - -# add root dir as include dir -target_include_directories(${COMPILE_ID} PRIVATE ${CMAKE_SOURCE_DIR}) -# add src dir as include dir -target_include_directories(${COMPILE_ID} PRIVATE ${SOURCE_DIR}) -# add include dir as include dir -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) -target_link_options(${COMPILE_ID} PRIVATE "-fuse-ld=ld") - -# add extern stuff like libs and other includes -include(extern.cmake) +# make bs hook build depend on the build of bs hook tests +# add_dependencies(${COMPILE_ID} ${COMPILE_ID}-test) add_custom_command(TARGET ${COMPILE_ID} POST_BUILD COMMAND ${CMAKE_STRIP} -g -S -d --strip-all @@ -80,7 +122,11 @@ add_custom_command(TARGET ${COMPILE_ID} POST_BUILD COMMENT "Strip debug symbols done on final binary.") add_custom_command(TARGET ${COMPILE_ID} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E rename lib${COMPILE_ID}.so debug_lib${COMPILE_ID}.so + COMMAND ${CMAKE_COMMAND} -E make_directory debug + COMMENT "Create the debug dir" +) +add_custom_command(TARGET ${COMPILE_ID} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename lib${COMPILE_ID}.so debug/lib${COMPILE_ID}.so COMMENT "Rename the lib to debug_ since it has debug symbols" ) diff --git a/build.ps1 b/build.ps1 index 9eaf8af2..21fa661e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,7 @@ +param ( + [Parameter(Mandatory=$false)] + [Switch] $clean +) function Clean-Build-Folder { if (Test-Path -Path "build") @@ -9,28 +13,14 @@ function Clean-Build-Folder { } } -$NDKPath = Get-Content $PSScriptRoot/ndkpath.txt - -Clean-Build-Folder - -& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DTEST_BUILD=1 . -B build -& cmake --build ./build - -$ExitCode = $LastExitCode - -if (-not ($ExitCode -eq 0)) { - $msg = "ExitCode: " + $ExitCode - Write-Output $msg - exit $ExitCode +if ($clean.IsPresent) { + Clean-Build-Folder } -# clean folder -Clean-Build-Folder # build mod - & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" . -B build & cmake --build ./build $ExitCode = $LastExitCode -exit $ExitCode \ No newline at end of file +exit $ExitCode diff --git a/qpm.json b/qpm.json index f9f93de7..ebfcc2ab 100644 --- a/qpm.json +++ b/qpm.json @@ -4,7 +4,7 @@ "info": { "name": "beatsaber-hook", "id": "beatsaber-hook", - "version": "1.2.3", + "version": "5.0.0", "url": "https://github.com/sc2ad/beatsaber-hook", "additionalData": { "soLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/libbeatsaber-hook.so", @@ -20,7 +20,7 @@ }, { "id": "libil2cpp", - "versionRange": ">=0.1.2, <0.3.0", + "versionRange": "^0.3.0", "additionalData": {} }, { diff --git a/qpm.shared.json b/qpm.shared.json index 2e5ac127..c1addb71 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -1,11 +1,12 @@ { "config": { + "version": "0.1.0", "sharedDir": "shared", "dependenciesDir": "extern", "info": { "name": "beatsaber-hook", "id": "beatsaber-hook", - "version": "1.2.3", + "version": "5.0.0", "url": "https://github.com/sc2ad/beatsaber-hook", "additionalData": { "soLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/libbeatsaber-hook.so", @@ -13,6 +14,13 @@ "branchName": "master" } }, + "workspace": { + "scripts": { + "build": [ + "pwsh ./build.ps1" + ] + } + }, "dependencies": [ { "id": "scotland2", @@ -21,7 +29,7 @@ }, { "id": "libil2cpp", - "versionRange": ">=0.1.2, <0.3.0", + "versionRange": "^0.3.0", "additionalData": {} }, { @@ -31,25 +39,18 @@ "private": true } } - ], - "workspace": { - "scripts": { - "build": [ - "pwsh ./build.ps1" - ] - } - } + ] }, "restoredDependencies": [ { "dependency": { "id": "libil2cpp", - "versionRange": "=0.2.3", + "versionRange": "=0.3.1", "additionalData": { "headersOnly": true } }, - "version": "0.2.3" + "version": "0.3.1" }, { "dependency": { diff --git a/shared/inline-hook/And64InlineHook.cpp b/shared/inline-hook/And64InlineHook.cpp new file mode 100644 index 00000000..c7f03bf6 --- /dev/null +++ b/shared/inline-hook/And64InlineHook.cpp @@ -0,0 +1,567 @@ +/* + * @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. + */ +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#ifdef __aarch64__ + +#include "../../shared/inline-hook/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"))) +#define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__)) +#ifndef NDEBUG +# define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__)) +#else +# define A64_LOGI(...) ((void)0) +#endif // NDEBUG +typedef uint32_t *__restrict *__restrict instruction; +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 (static_cast(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) + { + uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol); + + static_assert(A64_MAX_INSTRUCTIONS >= 5, "please fix A64_MAX_INSTRUCTIONS!"); + 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 + + 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__) diff --git a/shared/inline-hook/inlineHook.c b/shared/inline-hook/inlineHook.c new file mode 100644 index 00000000..ee544357 --- /dev/null +++ b/shared/inline-hook/inlineHook.c @@ -0,0 +1,427 @@ +/* +thumb16 thumb32 arm32 inlineHook +author: ele7enxxh +mail: ele7enxxh@qq.com +website: ele7enxxh.com +modified time: 2015-01-23 +created time: 2015-11-30 +*/ +#ifndef __aarch64__ +#include +#include +#include +#include +#include +#include +// #include +#include +#include + +#include "relocate.h" +#include "inlineHook.h" + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr)) +#define SET_BIT0(addr) (addr | 1) +#define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE) +#define TEST_BIT0(addr) (addr & 1) + +#define ACTION_ENABLE 0 +#define ACTION_DISABLE 1 +#ifndef __aarch64__ +enum hook_status { + REGISTERED, + HOOKED, +}; + +struct inlineHookItem { + uint32_t target_addr; + uint32_t new_addr; + uint32_t **proto_addr; + void *orig_instructions; + int orig_boundaries[4]; + int trampoline_boundaries[20]; + int count; + void *trampoline_instructions; + int length; + int status; + int mode; +}; + +struct inlineHookInfo { + struct inlineHookItem item[1024]; + int size; +}; + +static struct inlineHookInfo info = {0}; + +static int getAllTids(pid_t exclude_tid, pid_t *tids) +{ + char dir_path[32]; + DIR *dir; + int i; + struct dirent *entry; + pid_t tid; + + if (exclude_tid < 0) { + snprintf(dir_path, sizeof(dir_path), "/proc/self/task"); + } + else { + snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", exclude_tid); + } + + dir = opendir(dir_path); + if (dir == NULL) { + return 0; + } + + i = 0; + while((entry = readdir(dir)) != NULL) { + tid = atoi(entry->d_name); + if (tid != 0 && tid != exclude_tid) { + tids[i++] = tid; + } + } + closedir(dir); + return i; +} + +static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) +{ + int offset; + int i; + + switch (action) + { + case ACTION_ENABLE: + offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr); + for (i = 0; i < item->count; ++i) { + if (offset == item->orig_boundaries[i]) { + regs->ARM_pc = (uint32_t) item->trampoline_instructions + item->trampoline_boundaries[i]; + return true; + } + } + break; + case ACTION_DISABLE: + offset = regs->ARM_pc - (int) item->trampoline_instructions; + for (i = 0; i < item->count; ++i) { + if (offset == item->trampoline_boundaries[i]) { + regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i]; + return true; + } + } + break; + } + + return false; +} + +static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) +{ + struct pt_regs regs; + + if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) { + if (item == NULL) { + int pos; + + for (pos = 0; pos < info.size; ++pos) { + if (doProcessThreadPC(&info.item[pos], ®s, action) == true) { + break; + } + } + } + else { + doProcessThreadPC(item, ®s, action); + } + + ptrace(PTRACE_SETREGS, tid, NULL, ®s); + } +} + +static pid_t freeze(struct inlineHookItem *item, int action) +{ + int count; + pid_t tids[1024]; + pid_t pid; + + pid = -1; + count = getAllTids(gettid(), tids); + if (count > 0) { + pid = fork(); + + if (pid == 0) { + int i; + + for (i = 0; i < count; ++i) { + if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) { + waitpid(tids[i], NULL, WUNTRACED); + processThreadPC(tids[i], item, action); + } + } + + raise(SIGSTOP); + + for (i = 0; i < count; ++i) { + ptrace(PTRACE_DETACH, tids[i], NULL, NULL); + } + + raise(SIGKILL); + } + + else if (pid > 0) { + waitpid(pid, NULL, WUNTRACED); + } + } + + return pid; +} + +static void unFreeze(pid_t pid) +{ + if (pid < 0) { + return; + } + + kill(pid, SIGCONT); + wait(NULL); +} + +static bool isExecutableAddr(uint32_t addr) +{ + FILE *fp; + char line[1024]; + uint32_t start; + uint32_t end; + + fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) { + return false; + } + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, "r-xp") || strstr(line, "rwxp")) { + start = strtoul(strtok(line, "-"), NULL, 16); + end = strtoul(strtok(NULL, " "), NULL, 16); + if (addr >= start && addr <= end) { + fclose(fp); + return true; + } + } + } + + fclose(fp); + + return false; +} + +static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) +{ + int i; + + for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr) { + return &info.item[i]; + } + } + + return NULL; +} + +static struct inlineHookItem *addInlineHookItem() { + struct inlineHookItem *item; + + if (info.size >= 1024) { + return NULL; + } + + item = &info.item[info.size]; + ++info.size; + + return item; +} + +static void deleteInlineHookItem(int pos) +{ + info.item[pos] = info.item[info.size - 1]; + --info.size; +} + +enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) +{ + struct inlineHookItem *item; + + if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) { + return ELE7EN_ERROR_NOT_EXECUTABLE; + } + + item = findInlineHookItem(target_addr); + if (item != NULL) { + if (item->status == REGISTERED) { + return ELE7EN_ERROR_ALREADY_REGISTERED; + } + else if (item->status == HOOKED) { + return ELE7EN_ERROR_ALREADY_HOOKED; + } + else { + return ELE7EN_ERROR_UNKNOWN; + } + } + + item = addInlineHookItem(); + + item->target_addr = target_addr; + item->new_addr = new_addr; + item->proto_addr = proto_addr; + + item->length = TEST_BIT0(item->target_addr) ? 12 : 8; + // TODO: Allocate this in a place where GC doesn't ruin it + item->orig_instructions = malloc(item->length); + memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length); + + item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + relocateInstruction(item->target_addr, item->orig_instructions, item->length, item->trampoline_instructions, item->orig_boundaries, item->trampoline_boundaries, &item->count); + + item->status = REGISTERED; + + return ELE7EN_OK; +} + +static void doInlineUnHook(struct inlineHookItem *item, int pos) +{ + mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length); + mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); + munmap(item->trampoline_instructions, PAGE_SIZE); + free(item->orig_instructions); + + deleteInlineHookItem(pos); + + cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); +} + +enum ele7en_status inlineUnHook(uint32_t target_addr) +{ + int i; + + for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) { + pid_t pid; + + pid = freeze(&info.item[i], ACTION_DISABLE); + + doInlineUnHook(&info.item[i], i); + + unFreeze(pid); + + return ELE7EN_OK; + } + } + + return ELE7EN_ERROR_NOT_HOOKED; +} + +void inlineUnHookAll() +{ + pid_t pid; + int i; + + pid = freeze(NULL, ACTION_DISABLE); + + for (i = 0; i < info.size; ++i) { + if (info.item[i].status == HOOKED) { + doInlineUnHook(&info.item[i], i); + --i; + } + } + + unFreeze(pid); +} + +static void doInlineHook(struct inlineHookItem *item) +{ + mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + + if (item->proto_addr != NULL) { + *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions; + } + + if (TEST_BIT0(item->target_addr)) { + int i; + + i = 0; + if (CLEAR_BIT0(item->target_addr) % 4 != 0) { + ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP + } + ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF; + ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC] + ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF; + ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16; + } + else { + ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4] + ((uint32_t *) (item->target_addr))[1] = item->new_addr; + } + + mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); + + item->status = HOOKED; + + cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); +} + +enum ele7en_status inlineHook(uint32_t target_addr) +{ + int i; + struct inlineHookItem *item; + + item = NULL; + for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr) { + item = &info.item[i]; + break; + } + } + + if (item == NULL) { + return ELE7EN_ERROR_NOT_REGISTERED; + } + + if (item->status == REGISTERED) { + pid_t pid; + + pid = freeze(item, ACTION_ENABLE); + + doInlineHook(item); + + unFreeze(pid); + + return ELE7EN_OK; + } + else if (item->status == HOOKED) { + return ELE7EN_ERROR_ALREADY_HOOKED; + } + else { + return ELE7EN_ERROR_UNKNOWN; + } +} + +void inlineHookAll() +{ + pid_t pid; + int i; + + pid = freeze(NULL, ACTION_ENABLE); + + for (i = 0; i < info.size; ++i) { + if (info.item[i].status == REGISTERED) { + doInlineHook(&info.item[i]); + } + } + + unFreeze(pid); +} +#endif +#endif \ No newline at end of file diff --git a/shared/inline-hook/relocate.c b/shared/inline-hook/relocate.c new file mode 100644 index 00000000..bb11a20b --- /dev/null +++ b/shared/inline-hook/relocate.c @@ -0,0 +1,617 @@ +/* +relocate instruction +author: ele7enxxh +mail: ele7enxxh@qq.com +website: ele7enxxh.com +modified time: 2016-10-17 +created time: 2015-01-17 +*/ +#ifndef __aarch64__ +#include "relocate.h" + +#define ALIGN_PC(pc) (pc & 0xFFFFFFFC) + +enum INSTRUCTION_TYPE { + // B