mirror of
https://github.com/chiteroman/PlayIntegrityFix.git
synced 2025-01-19 02:52:20 +08:00
Bring back Dobby!
This commit is contained in:
parent
d846ccc772
commit
51350088ae
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "app/src/main/cpp/Dobby"]
|
||||||
|
path = app/src/main/cpp/Dobby
|
||||||
|
url = https://github.com/chiteroman/Dobby.git
|
@ -13,6 +13,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
|
jniLibs {
|
||||||
|
excludes += "**/libdobby.so"
|
||||||
|
}
|
||||||
resources {
|
resources {
|
||||||
excludes += "**"
|
excludes += "**"
|
||||||
}
|
}
|
||||||
@ -28,11 +31,6 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
abiFilters(
|
|
||||||
"arm64-v8a",
|
|
||||||
"armeabi-v7a"
|
|
||||||
)
|
|
||||||
|
|
||||||
arguments(
|
arguments(
|
||||||
"-DCMAKE_BUILD_TYPE=Release",
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
"-DANDROID_STL=none",
|
"-DANDROID_STL=none",
|
||||||
|
@ -6,32 +6,8 @@ find_package(cxx REQUIRED CONFIG)
|
|||||||
|
|
||||||
link_libraries(cxx::cxx)
|
link_libraries(cxx::cxx)
|
||||||
|
|
||||||
if (${ANDROID_ABI} STREQUAL "arm64-v8a")
|
add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp)
|
||||||
set(ARCH "arm64")
|
|
||||||
elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a")
|
|
||||||
set(ARCH "arm")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
file(GLOB SHADOWHOOK_SRC
|
add_subdirectory(Dobby)
|
||||||
shadowhook/*.c
|
|
||||||
shadowhook/arch/${ARCH}/*.c
|
|
||||||
shadowhook/common/*.c
|
|
||||||
shadowhook/third_party/xdl/*.c
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log dobby_static)
|
||||||
main.cpp
|
|
||||||
${SHADOWHOOK_SRC}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
|
||||||
shadowhook
|
|
||||||
shadowhook/arch/${ARCH}
|
|
||||||
shadowhook/include
|
|
||||||
shadowhook/common
|
|
||||||
shadowhook/third_party/bsd
|
|
||||||
shadowhook/third_party/lss
|
|
||||||
shadowhook/third_party/xdl
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log)
|
|
||||||
|
1
app/src/main/cpp/Dobby
Submodule
1
app/src/main/cpp/Dobby
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 7c1444df316b7e594cb9be4408017f67a5905396
|
@ -2,7 +2,7 @@
|
|||||||
#include <sys/system_properties.h>
|
#include <sys/system_properties.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "zygisk.hpp"
|
#include "zygisk.hpp"
|
||||||
#include "shadowhook.h"
|
#include "dobby.h"
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__)
|
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__)
|
||||||
@ -48,13 +48,10 @@ static std::string DEVICE_INITIAL_SDK_INT, SECURITY_PATCH, BUILD_ID;
|
|||||||
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
|
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
|
||||||
|
|
||||||
static T_Callback o_callback = nullptr;
|
static T_Callback o_callback = nullptr;
|
||||||
static void *o_cookie = nullptr;
|
|
||||||
|
|
||||||
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
|
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
|
||||||
|
|
||||||
if (!cookie || !name || !value || !o_callback || !o_cookie) return;
|
if (!cookie || !name || !value || !o_callback) return;
|
||||||
|
|
||||||
if (cookie != o_cookie) return;
|
|
||||||
|
|
||||||
std::string_view prop(name);
|
std::string_view prop(name);
|
||||||
|
|
||||||
@ -80,29 +77,17 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
|
|||||||
static void (*o_system_property_read_callback)(prop_info *, T_Callback, void *) = nullptr;
|
static void (*o_system_property_read_callback)(prop_info *, T_Callback, void *) = nullptr;
|
||||||
|
|
||||||
static void my_system_property_read_callback(prop_info *pi, T_Callback callback, void *cookie) {
|
static void my_system_property_read_callback(prop_info *pi, T_Callback callback, void *cookie) {
|
||||||
if (pi && callback && cookie) {
|
if (pi && callback && cookie) o_callback = callback;
|
||||||
o_callback = callback;
|
|
||||||
o_cookie = cookie;
|
|
||||||
}
|
|
||||||
return o_system_property_read_callback(pi, modify_callback, cookie);
|
return o_system_property_read_callback(pi, modify_callback, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool doHook() {
|
static bool doHook() {
|
||||||
if (shadowhook_init(SHADOWHOOK_MODE_UNIQUE, true) != 0) {
|
LOGD("loaded Dobby version: %s", DobbyGetVersion());
|
||||||
LOGE("shadowhook failed to init!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("loaded shadowhook version: %s", SHADOWHOOK_VERSION);
|
void *ptr = DobbySymbolResolver(nullptr, "__system_property_read_callback");
|
||||||
|
|
||||||
void *ptr = shadowhook_hook_sym_name(
|
if (ptr && !DobbyHook(ptr, (void *) my_system_property_read_callback,
|
||||||
"libc.so",
|
(void **) &o_system_property_read_callback)) {
|
||||||
"__system_property_read_callback",
|
|
||||||
(void *) my_system_property_read_callback,
|
|
||||||
(void **) &o_system_property_read_callback
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ptr) {
|
|
||||||
LOGD("hook __system_property_read_callback successful at %p", ptr);
|
LOGD("hook __system_property_read_callback successful at %p", ptr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,446 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_a32.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sh_util.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
|
|
||||||
// https://developer.arm.com/documentation/ddi0406/latest
|
|
||||||
// https://developer.arm.com/documentation/ddi0597/latest
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IGNORED = 0,
|
|
||||||
B_A1,
|
|
||||||
BX_A1,
|
|
||||||
BL_IMM_A1,
|
|
||||||
BLX_IMM_A2,
|
|
||||||
ADD_REG_A1,
|
|
||||||
ADD_REG_PC_A1,
|
|
||||||
SUB_REG_A1,
|
|
||||||
SUB_REG_PC_A1,
|
|
||||||
ADR_A1,
|
|
||||||
ADR_A2,
|
|
||||||
MOV_REG_A1,
|
|
||||||
MOV_REG_PC_A1,
|
|
||||||
LDR_LIT_A1,
|
|
||||||
LDR_LIT_PC_A1,
|
|
||||||
LDRB_LIT_A1,
|
|
||||||
LDRD_LIT_A1,
|
|
||||||
LDRH_LIT_A1,
|
|
||||||
LDRSB_LIT_A1,
|
|
||||||
LDRSH_LIT_A1,
|
|
||||||
LDR_REG_A1,
|
|
||||||
LDR_REG_PC_A1,
|
|
||||||
LDRB_REG_A1,
|
|
||||||
LDRD_REG_A1,
|
|
||||||
LDRH_REG_A1,
|
|
||||||
LDRSB_REG_A1,
|
|
||||||
LDRSH_REG_A1
|
|
||||||
} sh_a32_type_t;
|
|
||||||
|
|
||||||
static sh_a32_type_t sh_a32_get_type(uint32_t inst) {
|
|
||||||
if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return B_A1;
|
|
||||||
else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return BX_A1;
|
|
||||||
else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return BL_IMM_A1;
|
|
||||||
else if ((inst & 0xFE000000) == 0xFA000000)
|
|
||||||
return BLX_IMM_A2;
|
|
||||||
else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
|
|
||||||
(((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1;
|
|
||||||
else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
|
|
||||||
(((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1;
|
|
||||||
else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return ADR_A1;
|
|
||||||
else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return ADR_A2;
|
|
||||||
else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x0010F000u) != 0x0010F000) &&
|
|
||||||
(!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000))))
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1;
|
|
||||||
else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1;
|
|
||||||
else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return LDRB_LIT_A1;
|
|
||||||
else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return LDRD_LIT_A1;
|
|
||||||
else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return LDRH_LIT_A1;
|
|
||||||
else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return LDRSB_LIT_A1;
|
|
||||||
else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000))
|
|
||||||
return LDRSH_LIT_A1;
|
|
||||||
else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1;
|
|
||||||
else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return LDRB_REG_A1;
|
|
||||||
else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return LDRD_REG_A1;
|
|
||||||
else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return LDRH_REG_A1;
|
|
||||||
else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return LDRSB_REG_A1;
|
|
||||||
else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) &&
|
|
||||||
((inst & 0x01200000u) != 0x00200000))
|
|
||||||
return LDRSH_REG_A1;
|
|
||||||
else
|
|
||||||
return IGNORED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a32_get_rewrite_inst_len(uint32_t inst) {
|
|
||||||
static uint8_t map[] = {
|
|
||||||
4, // IGNORED
|
|
||||||
12, // B_A1
|
|
||||||
12, // BX_A1
|
|
||||||
16, // BL_IMM_A1
|
|
||||||
16, // BLX_IMM_A2
|
|
||||||
32, // ADD_REG_A1
|
|
||||||
32, // ADD_REG_PC_A1
|
|
||||||
32, // SUB_REG_A1
|
|
||||||
32, // SUB_REG_PC_A1
|
|
||||||
12, // ADR_A1
|
|
||||||
12, // ADR_A2
|
|
||||||
32, // MOV_REG_A1
|
|
||||||
12, // MOV_REG_PC_A1
|
|
||||||
24, // LDR_LIT_A1
|
|
||||||
36, // LDR_LIT_PC_A1
|
|
||||||
24, // LDRB_LIT_A1
|
|
||||||
24, // LDRD_LIT_A1
|
|
||||||
24, // LDRH_LIT_A1
|
|
||||||
24, // LDRSB_LIT_A1
|
|
||||||
24, // LDRSH_LIT_A1
|
|
||||||
32, // LDR_REG_A1
|
|
||||||
36, // LDR_REG_PC_A1
|
|
||||||
32, // LDRB_REG_A1
|
|
||||||
32, // LDRD_REG_A1
|
|
||||||
32, // LDRH_REG_A1
|
|
||||||
32, // LDRSB_REG_A1
|
|
||||||
32 // LDRSH_REG_A1
|
|
||||||
};
|
|
||||||
|
|
||||||
return (size_t)(map[sh_a32_get_type(inst)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) {
|
|
||||||
uintptr_t cursor_addr = rinfo->overwrite_start_addr;
|
|
||||||
size_t offset = 0;
|
|
||||||
for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) {
|
|
||||||
if (cursor_addr >= addr) break;
|
|
||||||
cursor_addr += 4;
|
|
||||||
offset += rinfo->rewrite_inst_lens[i];
|
|
||||||
}
|
|
||||||
uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset;
|
|
||||||
SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
|
|
||||||
return fixed_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
|
|
||||||
sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t cond;
|
|
||||||
if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1)
|
|
||||||
cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
else
|
|
||||||
// type == BLX_IMM_A2
|
|
||||||
cond = 0xE; // 1110 None (AL)
|
|
||||||
|
|
||||||
uint32_t addr;
|
|
||||||
if (type == B_A1 || type == BL_IMM_A1) {
|
|
||||||
uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u);
|
|
||||||
addr = pc + imm32; // arm -> arm
|
|
||||||
} else if (type == BLX_IMM_A2) {
|
|
||||||
uint32_t h = SH_UTIL_GET_BIT_32(inst, 24);
|
|
||||||
uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb
|
|
||||||
} else {
|
|
||||||
// type == BX_A1
|
|
||||||
// BX PC
|
|
||||||
// PC must be even, and the "arm" instruction must be at a 4-byte aligned address,
|
|
||||||
// so the instruction set must keep "arm" unchanged.
|
|
||||||
addr = pc; // arm -> arm
|
|
||||||
}
|
|
||||||
addr = sh_a32_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
if (type == BL_IMM_A1 || type == BLX_IMM_A2) {
|
|
||||||
buf[idx++] = 0x028FE008u | (cond << 28u); // ADD<c> LR, PC, #8
|
|
||||||
}
|
|
||||||
buf[idx++] = 0x059FF000u | (cond << 28u); // LDR<c> PC, [PC, #0]
|
|
||||||
buf[idx++] = 0xEA000000; // B #0
|
|
||||||
buf[idx++] = addr;
|
|
||||||
return idx * 4; // 12 or 16
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) {
|
|
||||||
// ADD{S}<c> <Rd>, <Rn>, PC{, <shift>} or ADD{S}<c> <Rd>, PC, <Rm>{, <shift>}
|
|
||||||
// SUB{S}<c> <Rd>, <Rn>, PC{, <shift>} or SUB{S}<c> <Rd>, PC, <Rm>{, <shift>}
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16);
|
|
||||||
uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0);
|
|
||||||
uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
|
|
||||||
|
|
||||||
uint32_t rx; // r0 - r3
|
|
||||||
for (rx = 3;; --rx)
|
|
||||||
if (rx != rn && rx != rm && rx != rd) break;
|
|
||||||
|
|
||||||
if (rd == 0xF) // Rd == PC
|
|
||||||
{
|
|
||||||
uint32_t ry; // r0 - r4
|
|
||||||
for (ry = 4;; --ry)
|
|
||||||
if (ry != rn && ry != rm && ry != rd && ry != rx) break;
|
|
||||||
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000005; // B #20
|
|
||||||
buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC}
|
|
||||||
buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
|
|
||||||
if (rn == 0xF)
|
|
||||||
// Rn == PC
|
|
||||||
buf[4] =
|
|
||||||
(inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, <shift>}
|
|
||||||
else
|
|
||||||
// Rm == PC
|
|
||||||
buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, <shift>}
|
|
||||||
buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8]
|
|
||||||
buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC}
|
|
||||||
buf[7] = pc;
|
|
||||||
return 32;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000005; // B #20
|
|
||||||
buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx}
|
|
||||||
buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
|
|
||||||
if (rn == 0xF)
|
|
||||||
// Rn == PC
|
|
||||||
buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, <shift>}
|
|
||||||
else
|
|
||||||
// Rm == PC
|
|
||||||
buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, <shift>}
|
|
||||||
buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx}
|
|
||||||
buf[6] = 0xEA000000; // B #0
|
|
||||||
buf[7] = pc;
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
|
|
||||||
sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15
|
|
||||||
uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0);
|
|
||||||
uint32_t imm32 = sh_util_arm_expand_imm(imm12);
|
|
||||||
uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
|
|
||||||
if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR<c> Rd, [PC, #0]
|
|
||||||
buf[1] = 0xEA000000; // B #0
|
|
||||||
buf[2] = addr;
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) {
|
|
||||||
// MOV{S}<c> <Rd>, PC
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
|
|
||||||
uint32_t rx = (rd == 0) ? 1 : 0;
|
|
||||||
|
|
||||||
if (rd == 0xF) // Rd == PC (MOV PC, PC)
|
|
||||||
{
|
|
||||||
buf[0] = 0x059FF000u | (cond << 28u); // LDR<c> PC, [PC, #0]
|
|
||||||
buf[1] = 0xEA000000; // B #0
|
|
||||||
buf[2] = pc;
|
|
||||||
return 12;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000005; // B #20
|
|
||||||
buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx}
|
|
||||||
buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
|
|
||||||
buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, <shift> #<amount>/RRX}
|
|
||||||
buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx}
|
|
||||||
buf[6] = 0xEA000000; // B #0
|
|
||||||
buf[7] = pc;
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
|
|
||||||
sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
uint32_t u = SH_UTIL_GET_BIT_32(inst, 23);
|
|
||||||
uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12);
|
|
||||||
|
|
||||||
uint32_t imm32;
|
|
||||||
if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1)
|
|
||||||
imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0);
|
|
||||||
else
|
|
||||||
imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0);
|
|
||||||
uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
|
|
||||||
if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
if (type == LDR_LIT_PC_A1 && rt == 0xF) {
|
|
||||||
// Rt == PC
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000006; // B #24
|
|
||||||
buf[2] = 0xE92D0003; // PUSH {R0, R1}
|
|
||||||
buf[3] = 0xE59F0000; // LDR R0, [PC, #0]
|
|
||||||
buf[4] = 0xEA000000; // B #0
|
|
||||||
buf[5] = addr; //
|
|
||||||
buf[6] = 0xE5900000; // LDR R0, [R0]
|
|
||||||
buf[7] = 0xE58D0004; // STR R0, [SP, #4]
|
|
||||||
buf[8] = 0xE8BD8001; // POP {R0, PC}
|
|
||||||
return 36;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000003; // B #12
|
|
||||||
buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0]
|
|
||||||
buf[3] = 0xEA000000; // B #0
|
|
||||||
buf[4] = addr; //
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wswitch"
|
|
||||||
switch (type) {
|
|
||||||
case LDR_LIT_A1:
|
|
||||||
buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt]
|
|
||||||
break;
|
|
||||||
case LDRB_LIT_A1:
|
|
||||||
buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt]
|
|
||||||
break;
|
|
||||||
case LDRD_LIT_A1:
|
|
||||||
buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt]
|
|
||||||
break;
|
|
||||||
case LDRH_LIT_A1:
|
|
||||||
buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt]
|
|
||||||
break;
|
|
||||||
case LDRSB_LIT_A1:
|
|
||||||
buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt]
|
|
||||||
break;
|
|
||||||
case LDRSH_LIT_A1:
|
|
||||||
buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt]
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
return 24;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) {
|
|
||||||
// LDR<c> <Rt>, [PC,+/-<Rm>{, <shift>}]{!}
|
|
||||||
// ......
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
|
|
||||||
uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12);
|
|
||||||
uint32_t rt2 = rt + 1;
|
|
||||||
uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0);
|
|
||||||
uint32_t rx; // r0 - r3
|
|
||||||
for (rx = 3;; --rx)
|
|
||||||
if (rx != rt && rx != rt2 && rx != rm) break;
|
|
||||||
|
|
||||||
if (type == LDR_REG_PC_A1 && rt == 0xF) {
|
|
||||||
// Rt == PC
|
|
||||||
uint32_t ry; // r0 - r4
|
|
||||||
for (ry = 4;; --ry)
|
|
||||||
if (ry != rt && ry != rt2 && ry != rm && ry != rx) break;
|
|
||||||
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000006; // B #24
|
|
||||||
buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC}
|
|
||||||
buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #8]
|
|
||||||
buf[4] = 0xEA000000; // B #0
|
|
||||||
buf[5] = pc;
|
|
||||||
buf[6] =
|
|
||||||
(inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u); // LDRxx Ry, [Rx],+/-Rm{, <shift>}
|
|
||||||
buf[7] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8]
|
|
||||||
buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC}
|
|
||||||
return 36;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0
|
|
||||||
buf[1] = 0xEA000005; // B #20
|
|
||||||
buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx}
|
|
||||||
buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #0]
|
|
||||||
buf[4] = 0xEA000000; // B #0
|
|
||||||
buf[5] = pc;
|
|
||||||
buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // LDRxx Rt, [Rx],+/-Rm{, <shift>}
|
|
||||||
buf[7] = 0xE49D0004 | (rx << 12u); // POP {Rx}
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) {
|
|
||||||
sh_a32_type_t type = sh_a32_get_type(inst);
|
|
||||||
SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst);
|
|
||||||
|
|
||||||
// We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage.
|
|
||||||
// In this case, the add/sub/mov/ldr_reg instruction does not need to consider
|
|
||||||
// the problem of PC in the coverage area when rewriting.
|
|
||||||
|
|
||||||
if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2)
|
|
||||||
return sh_a32_rewrite_b(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1)
|
|
||||||
return sh_a32_rewrite_add_or_sub(buf, inst, pc);
|
|
||||||
else if (type == ADR_A1 || type == ADR_A2)
|
|
||||||
return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1)
|
|
||||||
return sh_a32_rewrite_mov(buf, inst, pc);
|
|
||||||
else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 ||
|
|
||||||
type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1)
|
|
||||||
return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 ||
|
|
||||||
type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1)
|
|
||||||
return sh_a32_rewrite_ldr_reg(buf, inst, pc, type);
|
|
||||||
else {
|
|
||||||
// IGNORED
|
|
||||||
buf[0] = inst;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) {
|
|
||||||
buf[0] = 0xE51FF004; // LDR PC, [PC, #-4]
|
|
||||||
buf[1] = addr;
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) {
|
|
||||||
buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u); // B <label>
|
|
||||||
return 4;
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t overwrite_start_addr;
|
|
||||||
uintptr_t overwrite_end_addr;
|
|
||||||
uint32_t *rewrite_buf;
|
|
||||||
size_t rewrite_buf_offset;
|
|
||||||
size_t rewrite_inst_lens[2];
|
|
||||||
size_t rewrite_inst_lens_cnt;
|
|
||||||
} sh_a32_rewrite_info_t;
|
|
||||||
|
|
||||||
size_t sh_a32_get_rewrite_inst_len(uint32_t inst);
|
|
||||||
size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo);
|
|
||||||
|
|
||||||
size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr);
|
|
||||||
size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc);
|
|
@ -1,523 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_inst.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "sh_a32.h"
|
|
||||||
#include "sh_config.h"
|
|
||||||
#include "sh_enter.h"
|
|
||||||
#include "sh_exit.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_t16.h"
|
|
||||||
#include "sh_t32.h"
|
|
||||||
#include "sh_txx.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
static void sh_inst_get_thumb_rewrite_info(sh_inst_t *self, uintptr_t target_addr,
|
|
||||||
sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
memset(rinfo, 0, sizeof(sh_txx_rewrite_info_t));
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
uintptr_t target_addr_offset = 0;
|
|
||||||
uintptr_t pc = target_addr + 4;
|
|
||||||
size_t rewrite_len = 0;
|
|
||||||
|
|
||||||
while (rewrite_len < self->backup_len) {
|
|
||||||
// IT block
|
|
||||||
sh_t16_it_t it;
|
|
||||||
if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) {
|
|
||||||
rewrite_len += (2 + it.insts_len);
|
|
||||||
|
|
||||||
size_t it_block_idx = idx++;
|
|
||||||
size_t it_block_len = 4 + 4; // IT-else + IT-then
|
|
||||||
for (size_t i = 0, j = 0; i < it.insts_cnt; i++) {
|
|
||||||
bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j])));
|
|
||||||
if (is_thumb32) {
|
|
||||||
it_block_len += sh_t32_get_rewrite_inst_len(it.insts[j], it.insts[j + 1]);
|
|
||||||
rinfo->inst_lens[idx++] = 0;
|
|
||||||
rinfo->inst_lens[idx++] = 0;
|
|
||||||
j += 2;
|
|
||||||
} else {
|
|
||||||
it_block_len += sh_t16_get_rewrite_inst_len(it.insts[j]);
|
|
||||||
rinfo->inst_lens[idx++] = 0;
|
|
||||||
j += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rinfo->inst_lens[it_block_idx] = it_block_len;
|
|
||||||
|
|
||||||
target_addr_offset += (2 + it.insts_len);
|
|
||||||
pc += (2 + it.insts_len);
|
|
||||||
}
|
|
||||||
// not IT block
|
|
||||||
else {
|
|
||||||
bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset);
|
|
||||||
size_t inst_len = (is_thumb32 ? 4 : 2);
|
|
||||||
rewrite_len += inst_len;
|
|
||||||
|
|
||||||
if (is_thumb32) {
|
|
||||||
rinfo->inst_lens[idx++] =
|
|
||||||
sh_t32_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset)),
|
|
||||||
*((uint16_t *)(target_addr + target_addr_offset + 2)));
|
|
||||||
rinfo->inst_lens[idx++] = 0;
|
|
||||||
} else
|
|
||||||
rinfo->inst_lens[idx++] =
|
|
||||||
sh_t16_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset)));
|
|
||||||
|
|
||||||
target_addr_offset += inst_len;
|
|
||||||
pc += inst_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rinfo->start_addr = target_addr;
|
|
||||||
rinfo->end_addr = target_addr + rewrite_len;
|
|
||||||
rinfo->buf = (uint16_t *)self->enter_addr;
|
|
||||||
rinfo->buf_offset = 0;
|
|
||||||
rinfo->inst_lens_cnt = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_inst_hook_thumb_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
|
|
||||||
uintptr_t *orig_addr2, size_t *rewrite_len) {
|
|
||||||
// backup original instructions (length: 4 or 8 or 10)
|
|
||||||
memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
|
|
||||||
|
|
||||||
// package the information passed to rewrite
|
|
||||||
sh_txx_rewrite_info_t rinfo;
|
|
||||||
sh_inst_get_thumb_rewrite_info(self, target_addr, &rinfo);
|
|
||||||
|
|
||||||
// backup and rewrite original instructions
|
|
||||||
uintptr_t target_addr_offset = 0;
|
|
||||||
uintptr_t pc = target_addr + 4;
|
|
||||||
*rewrite_len = 0;
|
|
||||||
while (*rewrite_len < self->backup_len) {
|
|
||||||
// IT block
|
|
||||||
sh_t16_it_t it;
|
|
||||||
if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) {
|
|
||||||
*rewrite_len += (2 + it.insts_len);
|
|
||||||
|
|
||||||
// save space holder point of IT-else B instruction
|
|
||||||
uintptr_t enter_inst_else_p = self->enter_addr + rinfo.buf_offset;
|
|
||||||
rinfo.buf_offset += 2; // B<c> <label>
|
|
||||||
rinfo.buf_offset += 2; // NOP
|
|
||||||
|
|
||||||
// rewrite IT block
|
|
||||||
size_t enter_inst_else_len = 4; // B<c> + NOP + B + NOP
|
|
||||||
size_t enter_inst_then_len = 0; // B + NOP
|
|
||||||
uintptr_t enter_inst_then_p = 0;
|
|
||||||
for (size_t i = 0, j = 0; i < it.insts_cnt; i++) {
|
|
||||||
if (i == it.insts_else_cnt) {
|
|
||||||
// save space holder point of IT-then (for B instruction)
|
|
||||||
enter_inst_then_p = self->enter_addr + rinfo.buf_offset;
|
|
||||||
rinfo.buf_offset += 2; // B <label>
|
|
||||||
rinfo.buf_offset += 2; // NOP
|
|
||||||
|
|
||||||
// fill IT-else B instruction
|
|
||||||
sh_t16_rewrite_it_else((uint16_t *)enter_inst_else_p, (uint16_t)enter_inst_else_len, &it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rewrite instructions in IT block
|
|
||||||
bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j])));
|
|
||||||
size_t len;
|
|
||||||
if (is_thumb32)
|
|
||||||
len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j],
|
|
||||||
it.insts[j + 1], it.pcs[i], &rinfo);
|
|
||||||
else
|
|
||||||
len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j], it.pcs[i],
|
|
||||||
&rinfo);
|
|
||||||
if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
|
|
||||||
rinfo.buf_offset += len;
|
|
||||||
j += (is_thumb32 ? 2 : 1);
|
|
||||||
|
|
||||||
// save the total offset for ELSE/THEN in enter
|
|
||||||
if (i < it.insts_else_cnt)
|
|
||||||
enter_inst_else_len += len;
|
|
||||||
else
|
|
||||||
enter_inst_then_len += len;
|
|
||||||
|
|
||||||
if (i == it.insts_cnt - 1) {
|
|
||||||
// fill IT-then B instruction
|
|
||||||
sh_t16_rewrite_it_then((uint16_t *)enter_inst_then_p, (uint16_t)enter_inst_then_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target_addr_offset += (2 + it.insts_len);
|
|
||||||
pc += (2 + it.insts_len);
|
|
||||||
}
|
|
||||||
// not IT block
|
|
||||||
else {
|
|
||||||
bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset);
|
|
||||||
size_t inst_len = (is_thumb32 ? 4 : 2);
|
|
||||||
*rewrite_len += inst_len;
|
|
||||||
|
|
||||||
// rewrite original instructions (fill in enter)
|
|
||||||
SH_LOG_INFO("thumb rewrite: offset %zu, pc %" PRIxPTR, rinfo.buf_offset, pc);
|
|
||||||
size_t len;
|
|
||||||
if (is_thumb32)
|
|
||||||
len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset),
|
|
||||||
*((uint16_t *)(target_addr + target_addr_offset)),
|
|
||||||
*((uint16_t *)(target_addr + target_addr_offset + 2)), pc, &rinfo);
|
|
||||||
else
|
|
||||||
len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset),
|
|
||||||
*((uint16_t *)(target_addr + target_addr_offset)), pc, &rinfo);
|
|
||||||
if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
|
|
||||||
rinfo.buf_offset += len;
|
|
||||||
|
|
||||||
target_addr_offset += inst_len;
|
|
||||||
pc += inst_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SH_LOG_INFO("thumb rewrite: len %zu to %zu", *rewrite_len, rinfo.buf_offset);
|
|
||||||
|
|
||||||
// absolute jump back to remaining original instructions (fill in enter)
|
|
||||||
rinfo.buf_offset += sh_t32_absolute_jump((uint16_t *)(self->enter_addr + rinfo.buf_offset), true,
|
|
||||||
SH_UTIL_SET_BIT0(target_addr + *rewrite_len));
|
|
||||||
sh_util_clear_cache(self->enter_addr, rinfo.buf_offset);
|
|
||||||
|
|
||||||
// save original function address
|
|
||||||
if (NULL != orig_addr) __atomic_store_n(orig_addr, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST);
|
|
||||||
if (NULL != orig_addr2) __atomic_store_n(orig_addr2, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
|
|
||||||
static bool sh_inst_thumb_is_long_enough(uintptr_t target_addr, size_t overwrite_len, xdl_info_t *dlinfo) {
|
|
||||||
if (overwrite_len <= dlinfo->dli_ssize) return true;
|
|
||||||
|
|
||||||
// check align-4 in the end of symbol
|
|
||||||
if ((overwrite_len == dlinfo->dli_ssize + 2) && ((target_addr + dlinfo->dli_ssize) % 4 == 2)) {
|
|
||||||
uintptr_t sym_end = target_addr + dlinfo->dli_ssize;
|
|
||||||
if (0 != sh_util_mprotect(sym_end, 2, PROT_READ | PROT_WRITE | PROT_EXEC)) return false;
|
|
||||||
|
|
||||||
// should be zero-ed
|
|
||||||
if (0 != *((uint16_t *)sym_end)) return false;
|
|
||||||
|
|
||||||
// should not belong to any symbol
|
|
||||||
void *dlcache = NULL;
|
|
||||||
xdl_info_t dlinfo2;
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
memset(&dlinfo2, 0, sizeof(dlinfo2));
|
|
||||||
SH_LOG_WARN("thumb detect tail aligned: crashed");
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
xdl_addr_clean(&dlcache);
|
|
||||||
if (NULL != dlinfo2.dli_sname) return false;
|
|
||||||
|
|
||||||
// trust here is useless alignment data
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
|
|
||||||
// B T4: [-16M, +16M - 2]
|
|
||||||
#define SH_INST_T32_B_RANGE_LOW (16777216)
|
|
||||||
#define SH_INST_T32_B_RANGE_HIGH (16777214)
|
|
||||||
|
|
||||||
static int sh_inst_hook_thumb_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
|
|
||||||
uintptr_t pc = target_addr + 4;
|
|
||||||
self->backup_len = 4;
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
|
|
||||||
if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo))
|
|
||||||
return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
#else
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// alloc an exit for absolute jump
|
|
||||||
sh_t32_absolute_jump((uint16_t *)self->exit, true, new_addr);
|
|
||||||
if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
|
|
||||||
sizeof(self->exit), SH_INST_T32_B_RANGE_LOW, SH_INST_T32_B_RANGE_HIGH)))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
size_t rewrite_len = 0;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) goto err;
|
|
||||||
|
|
||||||
// relative jump to the exit by overwriting the head of original function
|
|
||||||
sh_t32_relative_jump((uint16_t *)self->trampo, self->exit_addr, pc);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
|
|
||||||
|
|
||||||
SH_LOG_INFO("thumb: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
|
|
||||||
" -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
|
|
||||||
target_addr, self->exit_addr, new_addr, self->enter_addr,
|
|
||||||
SH_UTIL_SET_BIT0(target_addr + rewrite_len));
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
|
|
||||||
self->exit_addr = 0; // this is a flag for with-exit or without-exit
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int sh_inst_hook_thumb_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
|
|
||||||
bool is_align4 = (0 == (target_addr % 4));
|
|
||||||
self->backup_len = (is_align4 ? 8 : 10);
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
|
|
||||||
if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo))
|
|
||||||
return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
#else
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
return SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
size_t rewrite_len = 0;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) return r;
|
|
||||||
|
|
||||||
// absolute jump to new function address by overwriting the head of original function
|
|
||||||
sh_t32_absolute_jump((uint16_t *)self->trampo, is_align4, new_addr);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
|
|
||||||
|
|
||||||
SH_LOG_INFO("thumb: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
|
|
||||||
" -> remaining %" PRIxPTR,
|
|
||||||
target_addr, new_addr, self->enter_addr, SH_UTIL_SET_BIT0(target_addr + rewrite_len));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_inst_hook_arm_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
|
|
||||||
uintptr_t *orig_addr2) {
|
|
||||||
// backup original instructions (length: 4 or 8)
|
|
||||||
memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
|
|
||||||
|
|
||||||
// package the information passed to rewrite
|
|
||||||
sh_a32_rewrite_info_t rinfo;
|
|
||||||
rinfo.overwrite_start_addr = target_addr;
|
|
||||||
rinfo.overwrite_end_addr = target_addr + self->backup_len;
|
|
||||||
rinfo.rewrite_buf = (uint32_t *)self->enter_addr;
|
|
||||||
rinfo.rewrite_buf_offset = 0;
|
|
||||||
rinfo.rewrite_inst_lens_cnt = self->backup_len / 4;
|
|
||||||
for (uintptr_t i = 0; i < self->backup_len; i += 4)
|
|
||||||
rinfo.rewrite_inst_lens[i / 4] = sh_a32_get_rewrite_inst_len(*((uint32_t *)(target_addr + i)));
|
|
||||||
|
|
||||||
// rewrite original instructions (fill in enter)
|
|
||||||
uintptr_t pc = target_addr + 8;
|
|
||||||
for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) {
|
|
||||||
size_t offset = sh_a32_rewrite((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset),
|
|
||||||
*((uint32_t *)(target_addr + i)), pc, &rinfo);
|
|
||||||
if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
|
|
||||||
rinfo.rewrite_buf_offset += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// absolute jump back to remaining original instructions (fill in enter)
|
|
||||||
rinfo.rewrite_buf_offset += sh_a32_absolute_jump((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset),
|
|
||||||
target_addr + self->backup_len);
|
|
||||||
sh_util_clear_cache(self->enter_addr, rinfo.rewrite_buf_offset);
|
|
||||||
|
|
||||||
// save original function address
|
|
||||||
if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST);
|
|
||||||
if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
|
|
||||||
// B A1: [-32M, +32M - 4]
|
|
||||||
#define SH_INST_A32_B_RANGE_LOW (33554432)
|
|
||||||
#define SH_INST_A32_B_RANGE_HIGH (33554428)
|
|
||||||
|
|
||||||
static int sh_inst_hook_arm_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
uintptr_t pc = target_addr + 8;
|
|
||||||
self->backup_len = 4;
|
|
||||||
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
|
|
||||||
// alloc an exit for absolute jump
|
|
||||||
sh_a32_absolute_jump(self->exit, new_addr);
|
|
||||||
if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
|
|
||||||
sizeof(self->exit), SH_INST_A32_B_RANGE_LOW, SH_INST_A32_B_RANGE_HIGH)))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) goto err;
|
|
||||||
|
|
||||||
// relative jump to the exit by overwriting the head of original function
|
|
||||||
sh_a32_relative_jump(self->trampo, self->exit_addr, pc);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
|
|
||||||
|
|
||||||
SH_LOG_INFO("a32: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
|
|
||||||
" -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
|
|
||||||
target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
|
|
||||||
self->exit_addr = 0; // this is a flag for with-exit or without-exit
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int sh_inst_hook_arm_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
self->backup_len = 8;
|
|
||||||
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
return SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) return r;
|
|
||||||
|
|
||||||
// absolute jump to new function address by overwriting the head of original function
|
|
||||||
sh_a32_absolute_jump(self->trampo, new_addr);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
|
|
||||||
|
|
||||||
SH_LOG_INFO("a32: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
|
|
||||||
" -> remaining %" PRIxPTR,
|
|
||||||
target_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
self->enter_addr = sh_enter_alloc();
|
|
||||||
if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER;
|
|
||||||
|
|
||||||
int r;
|
|
||||||
if (SH_UTIL_IS_THUMB(target_addr)) {
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
if (0 == (r = sh_inst_hook_thumb_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
|
|
||||||
return r;
|
|
||||||
#endif
|
|
||||||
if (0 ==
|
|
||||||
(r = sh_inst_hook_thumb_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
|
|
||||||
return r;
|
|
||||||
} else {
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
if (0 == (r = sh_inst_hook_arm_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
|
|
||||||
return r;
|
|
||||||
#endif
|
|
||||||
if (0 == (r = sh_inst_hook_arm_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hook failed
|
|
||||||
if (NULL != orig_addr) *orig_addr = 0;
|
|
||||||
if (NULL != orig_addr2) *orig_addr2 = 0;
|
|
||||||
sh_enter_free(self->enter_addr);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) {
|
|
||||||
int r;
|
|
||||||
bool is_thumb = SH_UTIL_IS_THUMB(target_addr);
|
|
||||||
|
|
||||||
if (is_thumb) target_addr = SH_UTIL_CLEAR_BIT0(target_addr);
|
|
||||||
|
|
||||||
// restore the instructions at the target address
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = memcmp((void *)target_addr, self->trampo, self->backup_len);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH;
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r;
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
|
|
||||||
// free memory space for exit
|
|
||||||
if (0 != self->exit_addr)
|
|
||||||
if (0 !=
|
|
||||||
(r = sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit))))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// free memory space for enter
|
|
||||||
sh_enter_free(self->enter_addr);
|
|
||||||
|
|
||||||
SH_LOG_INFO("%s: unhook OK. target %" PRIxPTR, is_thumb ? "thumb" : "a32", target_addr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t trampo[4]; // align 16 // length == backup_len
|
|
||||||
uint8_t backup[16]; // align 16
|
|
||||||
uint16_t backup_len; // == 4 or 8 or 10
|
|
||||||
uint16_t exit_type;
|
|
||||||
uintptr_t exit_addr;
|
|
||||||
uint32_t exit[2];
|
|
||||||
uintptr_t enter_addr;
|
|
||||||
} sh_inst_t;
|
|
||||||
|
|
||||||
int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, uintptr_t *orig_addr2);
|
|
||||||
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);
|
|
@ -1,284 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_t16.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
|
|
||||||
// https://developer.arm.com/documentation/ddi0406/latest
|
|
||||||
// https://developer.arm.com/documentation/ddi0597/latest
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IGNORED = 0,
|
|
||||||
IT_T1,
|
|
||||||
B_T1,
|
|
||||||
B_T2,
|
|
||||||
BX_T1,
|
|
||||||
ADD_REG_T2,
|
|
||||||
MOV_REG_T1,
|
|
||||||
ADR_T1,
|
|
||||||
LDR_LIT_T1,
|
|
||||||
CBZ_T1,
|
|
||||||
CBNZ_T1
|
|
||||||
} sh_t16_type_t;
|
|
||||||
|
|
||||||
static sh_t16_type_t sh_t16_get_type(uint16_t inst) {
|
|
||||||
if (((inst & 0xFF00u) == 0xBF00) && ((inst & 0x000Fu) != 0x0000) && ((inst & 0x00F0u) != 0x00F0))
|
|
||||||
return IT_T1;
|
|
||||||
else if (((inst & 0xF000u) == 0xD000) && ((inst & 0x0F00u) != 0x0F00) && ((inst & 0x0F00u) != 0x0E00))
|
|
||||||
return B_T1;
|
|
||||||
else if ((inst & 0xF800u) == 0xE000)
|
|
||||||
return B_T2;
|
|
||||||
else if ((inst & 0xFFF8u) == 0x4778)
|
|
||||||
return BX_T1;
|
|
||||||
else if (((inst & 0xFF78u) == 0x4478) && ((inst & 0x0087u) != 0x0085))
|
|
||||||
return ADD_REG_T2;
|
|
||||||
else if ((inst & 0xFF78u) == 0x4678)
|
|
||||||
return MOV_REG_T1;
|
|
||||||
else if ((inst & 0xF800u) == 0xA000)
|
|
||||||
return ADR_T1;
|
|
||||||
else if ((inst & 0xF800u) == 0x4800)
|
|
||||||
return LDR_LIT_T1;
|
|
||||||
else if ((inst & 0xFD00u) == 0xB100)
|
|
||||||
return CBZ_T1;
|
|
||||||
else if ((inst & 0xFD00u) == 0xB900)
|
|
||||||
return CBNZ_T1;
|
|
||||||
else
|
|
||||||
return IGNORED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t16_get_rewrite_inst_len(uint16_t inst) {
|
|
||||||
static uint8_t map[] = {
|
|
||||||
4, // IGNORED
|
|
||||||
0, // IT_T1
|
|
||||||
12, // B_T1
|
|
||||||
8, // B_T2
|
|
||||||
8, // BX_T1
|
|
||||||
16, // ADD_REG_T2
|
|
||||||
12, // MOV_REG_T1
|
|
||||||
8, // ADR_T1
|
|
||||||
12, // LDR_LIT_T1
|
|
||||||
12, // CBZ_T1
|
|
||||||
12 // CBNZ_T1
|
|
||||||
};
|
|
||||||
|
|
||||||
return (size_t)(map[sh_t16_get_type(inst)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_b(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_t16_type_t type,
|
|
||||||
sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t addr;
|
|
||||||
if (type == B_T1) {
|
|
||||||
uint32_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
|
|
||||||
addr = pc + SH_UTIL_SIGN_EXTEND_32(imm8 << 1u, 9u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(addr); // thumb -> thumb
|
|
||||||
} else if (type == B_T2) {
|
|
||||||
uint32_t imm11 = SH_UTIL_GET_BITS_16(inst, 10, 0);
|
|
||||||
addr = pc + SH_UTIL_SIGN_EXTEND_32(imm11 << 1u, 12u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(addr); // thumb -> thumb
|
|
||||||
} else {
|
|
||||||
// type == BX_T1
|
|
||||||
// BX PC
|
|
||||||
// PC must be even, and the "BX PC" instruction must be at a 4-byte aligned address,
|
|
||||||
// so the instruction set must be exchanged from "thumb" to "arm".
|
|
||||||
addr = pc; // thumb -> arm
|
|
||||||
}
|
|
||||||
addr = sh_txx_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
if (type == B_T1) {
|
|
||||||
buf[idx++] = inst & 0xFF00u; // B<c> #0
|
|
||||||
buf[idx++] = 0xE003; // B PC, #6
|
|
||||||
}
|
|
||||||
buf[idx++] = 0xF8DF; // LDR.W PC, [PC]
|
|
||||||
buf[idx++] = 0xF000; // ...
|
|
||||||
buf[idx++] = addr & 0xFFFFu;
|
|
||||||
buf[idx++] = addr >> 16u;
|
|
||||||
return idx * 2; // 8 or 12
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_add(uint16_t *buf, uint16_t inst, uintptr_t pc) {
|
|
||||||
// ADD<c> <Rdn>, PC
|
|
||||||
uint16_t dn = SH_UTIL_GET_BIT_16(inst, 7);
|
|
||||||
uint16_t rdn = SH_UTIL_GET_BITS_16(inst, 2, 0);
|
|
||||||
uint16_t rd = (uint16_t)(dn << 3u) | rdn;
|
|
||||||
uint16_t rx = (rd == 0) ? 1 : 0; // r0 - r1
|
|
||||||
|
|
||||||
buf[0] = (uint16_t)(0xB400u | (1u << rx)); // PUSH {Rx}
|
|
||||||
buf[1] = 0x4802u | (uint16_t)(rx << 8u); // LDR Rx, [PC, #8]
|
|
||||||
buf[2] = (inst & 0xFF87u) | (uint16_t)(rx << 3u); // ADD Rd, Rx
|
|
||||||
buf[3] = (uint16_t)(0xBC00u | (1u << rx)); // POP {Rx}
|
|
||||||
buf[4] = 0xE002; // B #4
|
|
||||||
buf[5] = 0xBF00;
|
|
||||||
buf[6] = pc & 0xFFFFu;
|
|
||||||
buf[7] = pc >> 16u;
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_mov(uint16_t *buf, uint16_t inst, uintptr_t pc) {
|
|
||||||
// MOV<c> <Rd>, PC
|
|
||||||
uint16_t D = SH_UTIL_GET_BIT_16(inst, 7);
|
|
||||||
uint16_t rd = SH_UTIL_GET_BITS_16(inst, 2, 0);
|
|
||||||
uint16_t d = (uint16_t)(D << 3u) | rd; // r0 - r15
|
|
||||||
|
|
||||||
buf[0] = 0xF8DF; // LDR.W Rd, [PC, #4]
|
|
||||||
buf[1] = (uint16_t)(d << 12u) + 4u; // ...
|
|
||||||
buf[2] = 0xE002; // B #4
|
|
||||||
buf[3] = 0xBF00; // NOP
|
|
||||||
buf[4] = pc & 0xFFFFu;
|
|
||||||
buf[5] = pc >> 16u;
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_adr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
// ADR<c> <Rd>, <label>
|
|
||||||
uint16_t rd = SH_UTIL_GET_BITS_16(inst, 10, 8); // r0 - r7
|
|
||||||
uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
|
|
||||||
uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u);
|
|
||||||
if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0x4800u | (uint16_t)(rd << 8u); // LDR Rd, [PC]
|
|
||||||
buf[1] = 0xE001; // B #2
|
|
||||||
buf[2] = addr & 0xFFFFu;
|
|
||||||
buf[3] = addr >> 16u;
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_ldr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
// LDR<c> <Rt>, <label>
|
|
||||||
uint16_t rt = SH_UTIL_GET_BITS_16(inst, 10, 8); // r0 - r7
|
|
||||||
uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0);
|
|
||||||
uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u);
|
|
||||||
if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0x4800u | (uint16_t)(rt << 8u); // LDR Rt, [PC]
|
|
||||||
buf[1] = 0xE001; // B #2
|
|
||||||
buf[2] = addr & 0xFFFFu;
|
|
||||||
buf[3] = addr >> 16u;
|
|
||||||
buf[4] = 0x6800u | (uint16_t)(rt << 3u) | rt; // LDR Rt, [Rt]
|
|
||||||
buf[5] = 0xBF00; // NOP
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_rewrite_cb(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
// CB{N}Z <Rn>, <label>
|
|
||||||
uint16_t i = SH_UTIL_GET_BIT_16(inst, 9);
|
|
||||||
uint16_t imm5 = SH_UTIL_GET_BITS_16(inst, 7, 3);
|
|
||||||
uint32_t imm32 = (uint32_t)(i << 6u) | (uint32_t)(imm5 << 1u);
|
|
||||||
uint32_t addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb
|
|
||||||
addr = sh_txx_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
buf[0] = inst & 0xFD07u; // CB(N)Z Rn, #0
|
|
||||||
buf[1] = 0xE003; // B PC, #6
|
|
||||||
buf[2] = 0xF8DF; // LDR.W PC, [PC]
|
|
||||||
buf[3] = 0xF000; // ...
|
|
||||||
buf[4] = addr & 0xFFFFu;
|
|
||||||
buf[5] = addr >> 16u;
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
sh_t16_type_t type = sh_t16_get_type(inst);
|
|
||||||
SH_LOG_INFO("t16 rewrite: type %d, inst %" PRIx16, type, inst);
|
|
||||||
|
|
||||||
if (type == B_T1 || type == B_T2 || type == BX_T1)
|
|
||||||
return sh_t16_rewrite_b(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == ADD_REG_T2)
|
|
||||||
return sh_t16_rewrite_add(buf, inst, pc);
|
|
||||||
else if (type == MOV_REG_T1)
|
|
||||||
return sh_t16_rewrite_mov(buf, inst, pc);
|
|
||||||
else if (type == ADR_T1)
|
|
||||||
return sh_t16_rewrite_adr(buf, inst, pc, rinfo);
|
|
||||||
else if (type == LDR_LIT_T1)
|
|
||||||
return sh_t16_rewrite_ldr(buf, inst, pc, rinfo);
|
|
||||||
else if (type == CBZ_T1 || type == CBNZ_T1)
|
|
||||||
return sh_t16_rewrite_cb(buf, inst, pc, rinfo);
|
|
||||||
else {
|
|
||||||
// IGNORED
|
|
||||||
buf[0] = inst;
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t16_get_it_insts_count(uint16_t inst) {
|
|
||||||
if ((inst & 0x1u) != 0) return 4;
|
|
||||||
if ((inst & 0x2u) != 0) return 3;
|
|
||||||
if ((inst & 0x4u) != 0) return 2;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc) {
|
|
||||||
if (IT_T1 != sh_t16_get_type(inst)) return false;
|
|
||||||
SH_LOG_INFO("t16 rewrite: type IT, inst %" PRIx16, inst);
|
|
||||||
|
|
||||||
// address of the first inst in the IT block, skip the IT inst itself (2 bytes)
|
|
||||||
uintptr_t target_addr = pc - 4 + 2;
|
|
||||||
|
|
||||||
it->firstcond = (uint8_t)(inst >> 4u);
|
|
||||||
uint8_t firstcond_0 = it->firstcond & 1u;
|
|
||||||
|
|
||||||
memset(it, 0, sizeof(sh_t16_it_t));
|
|
||||||
it->insts_cnt = sh_t16_get_it_insts_count(inst);
|
|
||||||
|
|
||||||
size_t insts_idx = 0, pcs_idx = 0;
|
|
||||||
for (int parse_else = 1; parse_else >= 0; parse_else--) // round 0: parse ELSE, round 1: THEN
|
|
||||||
{
|
|
||||||
uintptr_t target_offset = 0;
|
|
||||||
for (size_t i = 0; i < it->insts_cnt; i++) {
|
|
||||||
bool is_thumb32 = sh_util_is_thumb32(target_addr + target_offset);
|
|
||||||
uint8_t mask_x = (uint8_t)(inst >> (uint16_t)(4 - i)) & 1u;
|
|
||||||
|
|
||||||
if ((parse_else && mask_x != firstcond_0) || // parse ELSE or
|
|
||||||
(!parse_else && mask_x == firstcond_0)) // parse THEN
|
|
||||||
{
|
|
||||||
it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset));
|
|
||||||
if (is_thumb32) it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset + 2));
|
|
||||||
|
|
||||||
it->pcs[pcs_idx++] = target_addr + target_offset + 4;
|
|
||||||
if (parse_else) it->insts_else_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_offset += (is_thumb32 ? 4 : 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it->insts_len = insts_idx * 2;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it) {
|
|
||||||
buf[0] = 0xD000u | (uint16_t)(it->firstcond << 8u) | (uint16_t)(imm9 >> 1u); // B<c> <label>
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12) {
|
|
||||||
buf[0] = 0xE000u | (uint16_t)(imm12 >> 1u); // B <label>
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_txx.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t insts[8];
|
|
||||||
size_t insts_len; // 2 - 16 (bytes)
|
|
||||||
size_t insts_cnt; // 1 - 4
|
|
||||||
size_t insts_else_cnt; // 0 - 3
|
|
||||||
uintptr_t pcs[4];
|
|
||||||
uint8_t firstcond;
|
|
||||||
uint8_t padding[3];
|
|
||||||
} sh_t16_it_t;
|
|
||||||
|
|
||||||
bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc);
|
|
||||||
void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it);
|
|
||||||
void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12);
|
|
||||||
|
|
||||||
size_t sh_t16_get_rewrite_inst_len(uint16_t inst);
|
|
||||||
size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo);
|
|
@ -1,408 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_t32.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
|
|
||||||
// https://developer.arm.com/documentation/ddi0406/latest
|
|
||||||
// https://developer.arm.com/documentation/ddi0597/latest
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IGNORED = 0,
|
|
||||||
B_T3,
|
|
||||||
B_T4,
|
|
||||||
BL_IMM_T1,
|
|
||||||
BLX_IMM_T2,
|
|
||||||
ADR_T2,
|
|
||||||
ADR_T3,
|
|
||||||
LDR_LIT_T2,
|
|
||||||
LDR_LIT_PC_T2,
|
|
||||||
LDRB_LIT_T1,
|
|
||||||
LDRD_LIT_T1,
|
|
||||||
LDRH_LIT_T1,
|
|
||||||
LDRSB_LIT_T1,
|
|
||||||
LDRSH_LIT_T1,
|
|
||||||
PLD_LIT_T1,
|
|
||||||
PLI_LIT_T3,
|
|
||||||
TBB_T1,
|
|
||||||
TBH_T1,
|
|
||||||
VLDR_LIT_T1
|
|
||||||
} sh_t32_type_t;
|
|
||||||
|
|
||||||
static sh_t32_type_t sh_t32_get_type(uint32_t inst) {
|
|
||||||
if (((inst & 0xF800D000) == 0xF0008000) && ((inst & 0x03800000u) != 0x03800000u))
|
|
||||||
return B_T3;
|
|
||||||
else if ((inst & 0xF800D000) == 0xF0009000)
|
|
||||||
return B_T4;
|
|
||||||
else if ((inst & 0xF800D000) == 0xF000D000)
|
|
||||||
return BL_IMM_T1;
|
|
||||||
else if ((inst & 0xF800D000) == 0xF000C000)
|
|
||||||
return BLX_IMM_T2;
|
|
||||||
else if ((inst & 0xFBFF8000) == 0xF2AF0000)
|
|
||||||
return ADR_T2;
|
|
||||||
else if ((inst & 0xFBFF8000) == 0xF20F0000)
|
|
||||||
return ADR_T3;
|
|
||||||
else if ((inst & 0xFF7F0000) == 0xF85F0000)
|
|
||||||
return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_T2 : LDR_LIT_T2;
|
|
||||||
else if (((inst & 0xFF7F0000) == 0xF81F0000) && ((inst & 0xF000u) != 0xF000u))
|
|
||||||
return LDRB_LIT_T1;
|
|
||||||
else if ((inst & 0xFF7F0000) == 0xE95F0000)
|
|
||||||
return LDRD_LIT_T1;
|
|
||||||
else if (((inst & 0xFF7F0000) == 0xF83F0000) && ((inst & 0xF000u) != 0xF000u))
|
|
||||||
return LDRH_LIT_T1;
|
|
||||||
else if (((inst & 0xFF7F0000) == 0xF91F0000) && ((inst & 0xF000u) != 0xF000u))
|
|
||||||
return LDRSB_LIT_T1;
|
|
||||||
else if (((inst & 0xFF7F0000) == 0xF93F0000) && ((inst & 0xF000u) != 0xF000u))
|
|
||||||
return LDRSH_LIT_T1;
|
|
||||||
else if ((inst & 0xFF7FF000) == 0xF81FF000)
|
|
||||||
return PLD_LIT_T1;
|
|
||||||
else if ((inst & 0xFF7FF000) == 0xF91FF000)
|
|
||||||
return PLI_LIT_T3;
|
|
||||||
else if ((inst & 0xFFF0FFF0) == 0xE8D0F000)
|
|
||||||
return TBB_T1;
|
|
||||||
else if ((inst & 0xFFF0FFF0) == 0xE8D0F010)
|
|
||||||
return TBH_T1;
|
|
||||||
else if ((inst & 0xFF3F0C00) == 0xED1F0800)
|
|
||||||
return VLDR_LIT_T1;
|
|
||||||
else
|
|
||||||
return IGNORED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst) {
|
|
||||||
static uint8_t map[] = {
|
|
||||||
4, // IGNORED
|
|
||||||
12, // B_T3
|
|
||||||
8, // B_T4
|
|
||||||
12, // BL_IMM_T1
|
|
||||||
12, // BLX_IMM_T2
|
|
||||||
12, // ADR_T2
|
|
||||||
12, // ADR_T3
|
|
||||||
16, // LDR_LIT_T2
|
|
||||||
24, // LDR_LIT_PC_T2
|
|
||||||
16, // LDRB_LIT_T1
|
|
||||||
16, // LDRD_LIT_T1
|
|
||||||
16, // LDRH_LIT_T1
|
|
||||||
16, // LDRSB_LIT_T1
|
|
||||||
16, // LDRSH_LIT_T1
|
|
||||||
20, // PLD_LIT_T1
|
|
||||||
20, // PLI_LIT_T3
|
|
||||||
32, // TBB_T1
|
|
||||||
32, // TBH_T1
|
|
||||||
24 // VLDR_LIT_T1
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst;
|
|
||||||
return (size_t)(map[sh_t32_get_type(inst)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_b(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t j1 = SH_UTIL_GET_BIT_16(low_inst, 13);
|
|
||||||
uint32_t j2 = SH_UTIL_GET_BIT_16(low_inst, 11);
|
|
||||||
uint32_t s = SH_UTIL_GET_BIT_16(high_inst, 10);
|
|
||||||
uint32_t i1 = !(j1 ^ s);
|
|
||||||
uint32_t i2 = !(j2 ^ s);
|
|
||||||
|
|
||||||
uint32_t addr;
|
|
||||||
if (type == B_T3) {
|
|
||||||
uint32_t x =
|
|
||||||
(s << 20u) | (j2 << 19u) | (j1 << 18u) | ((high_inst & 0x3Fu) << 12u) | ((low_inst & 0x7FFu) << 1u);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 21u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb
|
|
||||||
} else if (type == B_T4) {
|
|
||||||
uint32_t x =
|
|
||||||
(s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb
|
|
||||||
} else if (type == BL_IMM_T1) {
|
|
||||||
uint32_t x =
|
|
||||||
(s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
|
|
||||||
addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb
|
|
||||||
} else // type == BLX_IMM_T2
|
|
||||||
{
|
|
||||||
uint32_t x =
|
|
||||||
(s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FEu) << 1u);
|
|
||||||
uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u);
|
|
||||||
// In BL and BLX instructions, only when the target instruction set is "arm",
|
|
||||||
// you need to do 4-byte alignment for PC.
|
|
||||||
addr = SH_UTIL_ALIGN_4(pc) + imm32; // thumb -> arm, align4
|
|
||||||
}
|
|
||||||
addr = sh_txx_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
if (type == B_T3) {
|
|
||||||
uint32_t cond = SH_UTIL_GET_BITS_16(high_inst, 9, 6);
|
|
||||||
buf[idx++] = 0xD000u | (uint16_t)(cond << 8u); // B<c> #0
|
|
||||||
buf[idx++] = 0xE003; // B #6
|
|
||||||
} else if (type == BL_IMM_T1 || type == BLX_IMM_T2) {
|
|
||||||
buf[idx++] = 0xF20F; // ADD LR, PC, #9
|
|
||||||
buf[idx++] = 0x0E09; // ...
|
|
||||||
}
|
|
||||||
buf[idx++] = 0xF8DF; // LDR.W PC, [PC]
|
|
||||||
buf[idx++] = 0xF000; // ...
|
|
||||||
buf[idx++] = addr & 0xFFFFu;
|
|
||||||
buf[idx++] = addr >> 16u;
|
|
||||||
return idx * 2; // 8 or 12
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_adr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 11, 8); // r0 - r14
|
|
||||||
uint32_t i = SH_UTIL_GET_BIT_16(high_inst, 10);
|
|
||||||
uint32_t imm3 = SH_UTIL_GET_BITS_16(low_inst, 14, 12);
|
|
||||||
uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
|
|
||||||
uint32_t imm32 = (i << 11u) | (imm3 << 8u) | imm8;
|
|
||||||
uint32_t addr = (type == ADR_T2 ? (SH_UTIL_ALIGN_4(pc) - imm32) : (SH_UTIL_ALIGN_4(pc) + imm32));
|
|
||||||
if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0xF8DF; // LDR.W Rt, [PC, #4]
|
|
||||||
buf[1] = (uint16_t)(rt << 12u) + 4u; // ...
|
|
||||||
buf[2] = 0xE002; // B #4
|
|
||||||
buf[3] = 0xBF00; // NOP
|
|
||||||
buf[4] = addr & 0xFFFFu;
|
|
||||||
buf[5] = addr >> 16u;
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_ldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
|
|
||||||
uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 15, 12); // r0 - r15
|
|
||||||
uint32_t rt2 = 0; // r0 - r15
|
|
||||||
uint32_t addr;
|
|
||||||
|
|
||||||
if (type == LDRD_LIT_T1) {
|
|
||||||
rt2 = SH_UTIL_GET_BITS_16(low_inst, 11, 8);
|
|
||||||
uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
|
|
||||||
addr = (u ? SH_UTIL_ALIGN_4(pc) + (imm8 << 2u) : SH_UTIL_ALIGN_4(pc) - (imm8 << 2u));
|
|
||||||
} else {
|
|
||||||
uint32_t imm12 = (uint32_t)SH_UTIL_GET_BITS_16(low_inst, 11, 0);
|
|
||||||
addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12);
|
|
||||||
}
|
|
||||||
if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
if (type == LDR_LIT_PC_T2 && rt == 0xF) // Rt == PC
|
|
||||||
{
|
|
||||||
buf[0] = 0xB403; // PUSH {R0, R1}
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
buf[2] = 0xF8DF; // LDR.W R0, [PC, #4]
|
|
||||||
buf[3] = 0x0004; // ...
|
|
||||||
buf[4] = 0xE002; // B #4
|
|
||||||
buf[5] = 0xBF00; // NOP
|
|
||||||
buf[6] = addr & 0xFFFFu; //
|
|
||||||
buf[7] = addr >> 16u; //
|
|
||||||
buf[8] = 0xF8D0; // LDR.W R0, [R0]
|
|
||||||
buf[9] = 0x0000; // ...
|
|
||||||
buf[10] = 0x9001; // STR R0, [SP, #4]
|
|
||||||
buf[11] = 0xBD01; // POP {R0, PC}
|
|
||||||
return 24;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0xF8DF; // LDR.W Rt, [PC, #4]
|
|
||||||
buf[1] = (uint16_t)(rt << 12u) | 4u; // ...
|
|
||||||
buf[2] = 0xE002; // B #4
|
|
||||||
buf[3] = 0xBF00; // NOP
|
|
||||||
buf[4] = addr & 0xFFFFu;
|
|
||||||
buf[5] = addr >> 16u;
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wswitch"
|
|
||||||
switch (type) {
|
|
||||||
case LDR_LIT_T2:
|
|
||||||
buf[6] = (uint16_t)(0xF8D0 + rt); // LDR.W Rt, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u); // ...
|
|
||||||
break;
|
|
||||||
case LDRB_LIT_T1:
|
|
||||||
buf[6] = (uint16_t)(0xF890 + rt); // LDRB.W Rt, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u); // ...
|
|
||||||
break;
|
|
||||||
case LDRD_LIT_T1:
|
|
||||||
buf[6] = (uint16_t)(0xE9D0 + rt); // LDRD Rt, Rt2, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u) + (uint16_t)(rt2 << 8u); // ...
|
|
||||||
break;
|
|
||||||
case LDRH_LIT_T1:
|
|
||||||
buf[6] = (uint16_t)(0xF8B0 + rt); // LDRH.W Rt, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u); // ...
|
|
||||||
break;
|
|
||||||
case LDRSB_LIT_T1:
|
|
||||||
buf[6] = (uint16_t)(0xF990 + rt); // LDRSB.W Rt, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u); // ...
|
|
||||||
break;
|
|
||||||
case LDRSH_LIT_T1:
|
|
||||||
buf[6] = (uint16_t)(0xF9B0 + rt); // LDRSH.W Rt, [Rt]
|
|
||||||
buf[7] = (uint16_t)(rt << 12u); // ...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_pl(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
|
|
||||||
uint32_t imm12 = SH_UTIL_GET_BITS_16(low_inst, 11, 0);
|
|
||||||
uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12);
|
|
||||||
addr = sh_txx_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
buf[0] = 0xB401; // PUSH {R0}
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
buf[2] = 0xF8DF; // LDR.W R0, [PC, #8]
|
|
||||||
buf[3] = 0x0008; // ...
|
|
||||||
if (type == PLD_LIT_T1) {
|
|
||||||
buf[4] = 0xF890; // PLD [R0]
|
|
||||||
buf[5] = 0xF000; // ...
|
|
||||||
} else {
|
|
||||||
buf[4] = 0xF990; // PLI [R0]
|
|
||||||
buf[5] = 0xF000; // ...
|
|
||||||
}
|
|
||||||
buf[6] = 0xBC01; // POP {R0}
|
|
||||||
buf[7] = 0xE001; // B #2
|
|
||||||
buf[8] = addr & 0xFFFFu;
|
|
||||||
buf[9] = addr >> 16u;
|
|
||||||
return 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_tb(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
// If TBB/TBH is not the last instruction that needs to be rewritten,
|
|
||||||
// the rewriting can NOT be completed.
|
|
||||||
uintptr_t target_addr = SH_UTIL_CLEAR_BIT0(pc - 4);
|
|
||||||
if (target_addr + 4 != rinfo->end_addr) return 0; // rewrite failed
|
|
||||||
|
|
||||||
uint32_t rn = SH_UTIL_GET_BITS_16(high_inst, 3, 0);
|
|
||||||
uint32_t rm = SH_UTIL_GET_BITS_16(low_inst, 3, 0);
|
|
||||||
uint32_t rx, ry; // r0 - r7
|
|
||||||
for (rx = 7;; --rx)
|
|
||||||
if (rx != rn && rx != rm) break;
|
|
||||||
for (ry = 7;; --ry)
|
|
||||||
if (ry != rn && ry != rm && ry != rx) break;
|
|
||||||
|
|
||||||
buf[0] = (uint16_t)(0xB500u | (1u << rx) | (1u << ry)); // PUSH {Rx, Ry, LR}
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
buf[2] = 0xF8DF; // LDR.W Rx, [PC, #20]
|
|
||||||
buf[3] = (uint16_t)(rx << 12u) | 20u; // ...
|
|
||||||
if (type == TBB_T1) {
|
|
||||||
buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn)); // ADD.W Ry, Rx|Rn, Rm
|
|
||||||
buf[5] = (uint16_t)(0x0000u | (ry << 8u) | rm); // ...
|
|
||||||
buf[6] = (uint16_t)(0x7800u | (ry << 3u) | ry); // LDRB Ry, [Ry]
|
|
||||||
buf[7] = 0xBF00; // NOP
|
|
||||||
} else {
|
|
||||||
buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn)); // ADD.W Ry, Rx|Rn, Rm, LSL #1
|
|
||||||
buf[5] = (uint16_t)(0x0040u | (ry << 8u) | rm); // ...
|
|
||||||
buf[6] = (uint16_t)(0x8800u | (ry << 3u) | ry); // LDRH Ry, [Ry]
|
|
||||||
buf[7] = 0xBF00; // NOP
|
|
||||||
}
|
|
||||||
buf[8] = (uint16_t)(0xEB00u | rx); // ADD Rx, Rx, Ry, LSL #1
|
|
||||||
buf[9] = (uint16_t)(0x0040u | (rx << 8u) | ry); // ...
|
|
||||||
buf[10] = (uint16_t)(0x3001u | (rx << 8u)); // ADD Rx, #1
|
|
||||||
buf[11] = (uint16_t)(0x9002u | (rx << 8u)); // STR Rx, [SP, #8]
|
|
||||||
buf[12] = (uint16_t)(0xBD00u | (1u << rx) | (1u << ry)); // POP {Rx, Ry, PC}
|
|
||||||
buf[13] = 0xBF00; // NOP
|
|
||||||
buf[14] = pc & 0xFFFFu;
|
|
||||||
buf[15] = pc >> 16u;
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_t32_rewrite_vldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7);
|
|
||||||
uint32_t D = SH_UTIL_GET_BIT_16(high_inst, 6);
|
|
||||||
uint32_t vd = SH_UTIL_GET_BITS_16(low_inst, 15, 12);
|
|
||||||
uint32_t size = SH_UTIL_GET_BITS_16(low_inst, 9, 8);
|
|
||||||
uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0);
|
|
||||||
uint32_t esize = (8u << size);
|
|
||||||
uint32_t imm32 = (esize == 16 ? imm8 << 1u : imm8 << 2u);
|
|
||||||
uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm32 : SH_UTIL_ALIGN_4(pc) - imm32);
|
|
||||||
if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0xB401; // PUSH {R0}
|
|
||||||
buf[1] = 0xBF00; // NOP
|
|
||||||
buf[2] = 0xF8DF; // LDR.W R0, [PC, #4]
|
|
||||||
buf[3] = 0x0004; // ...
|
|
||||||
buf[4] = 0xE002; // B #4
|
|
||||||
buf[5] = 0xBF00; // NOP
|
|
||||||
buf[6] = addr & 0xFFFFu; //
|
|
||||||
buf[7] = addr >> 16u; //
|
|
||||||
buf[8] = (uint16_t)(0xED90u | D << 6u); // VLDR Sd|Dd, [R0]
|
|
||||||
buf[9] = (uint16_t)(0x800u | vd << 12u | size << 8u); // ...
|
|
||||||
buf[10] = 0xBC01; // POP {R0}
|
|
||||||
buf[11] = 0xBF00; // NOP
|
|
||||||
return 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst;
|
|
||||||
sh_t32_type_t type = sh_t32_get_type(inst);
|
|
||||||
SH_LOG_INFO("t32 rewrite: type %d, high inst %" PRIx16 ", low inst %" PRIx16, type, high_inst, low_inst);
|
|
||||||
|
|
||||||
if (type == B_T3 || type == B_T4 || type == BL_IMM_T1 || type == BLX_IMM_T2)
|
|
||||||
return sh_t32_rewrite_b(buf, high_inst, low_inst, pc, type, rinfo);
|
|
||||||
else if (type == ADR_T2 || type == ADR_T3)
|
|
||||||
return sh_t32_rewrite_adr(buf, high_inst, low_inst, pc, type, rinfo);
|
|
||||||
else if (type == LDR_LIT_T2 || type == LDR_LIT_PC_T2 || type == LDRB_LIT_T1 || type == LDRD_LIT_T1 ||
|
|
||||||
type == LDRH_LIT_T1 || type == LDRSB_LIT_T1 || type == LDRSH_LIT_T1)
|
|
||||||
return sh_t32_rewrite_ldr(buf, high_inst, low_inst, pc, type, rinfo);
|
|
||||||
else if (type == PLD_LIT_T1 || type == PLI_LIT_T3)
|
|
||||||
return sh_t32_rewrite_pl(buf, high_inst, low_inst, pc, type, rinfo);
|
|
||||||
else if (type == TBB_T1 || type == TBH_T1)
|
|
||||||
return sh_t32_rewrite_tb(buf, high_inst, low_inst, pc, type, rinfo);
|
|
||||||
else if (type == VLDR_LIT_T1)
|
|
||||||
return sh_t32_rewrite_vldr(buf, high_inst, low_inst, pc, rinfo);
|
|
||||||
else {
|
|
||||||
// IGNORED
|
|
||||||
buf[0] = high_inst;
|
|
||||||
buf[1] = low_inst;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr) {
|
|
||||||
size_t i = 0;
|
|
||||||
if (!is_align4) buf[i++] = 0xBF00; // NOP
|
|
||||||
buf[i++] = 0xF8DF; // LDR.W PC, [PC]
|
|
||||||
buf[i++] = 0xF000; // ...
|
|
||||||
buf[i++] = addr & 0xFFFFu;
|
|
||||||
buf[i++] = addr >> 16u;
|
|
||||||
return i * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc) {
|
|
||||||
uint32_t imm32 = addr - pc;
|
|
||||||
uint32_t s = SH_UTIL_GET_BIT_32(imm32, 24);
|
|
||||||
uint32_t i1 = SH_UTIL_GET_BIT_32(imm32, 23);
|
|
||||||
uint32_t i2 = SH_UTIL_GET_BIT_32(imm32, 22);
|
|
||||||
uint32_t imm10 = SH_UTIL_GET_BITS_32(imm32, 21, 12);
|
|
||||||
uint32_t imm11 = SH_UTIL_GET_BITS_32(imm32, 11, 1);
|
|
||||||
uint32_t j1 = (!i1) ^ s;
|
|
||||||
uint32_t j2 = (!i2) ^ s;
|
|
||||||
|
|
||||||
buf[0] = (uint16_t)(0xF000u | (s << 10u) | imm10);
|
|
||||||
buf[1] = (uint16_t)(0x9000u | (j1 << 13u) | (j2 << 11u) | imm11);
|
|
||||||
return 4;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_txx.h"
|
|
||||||
|
|
||||||
size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst);
|
|
||||||
size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc,
|
|
||||||
sh_txx_rewrite_info_t *rinfo);
|
|
||||||
|
|
||||||
size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr);
|
|
||||||
size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc);
|
|
@ -1,60 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_txx.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
|
|
||||||
bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
return (rinfo->start_addr <= addr && addr < rinfo->end_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) {
|
|
||||||
bool is_thumb = SH_UTIL_IS_THUMB(addr);
|
|
||||||
|
|
||||||
if (is_thumb) addr = SH_UTIL_CLEAR_BIT0(addr);
|
|
||||||
|
|
||||||
if (rinfo->start_addr <= addr && addr < rinfo->end_addr) {
|
|
||||||
uintptr_t cursor_addr = rinfo->start_addr;
|
|
||||||
size_t offset = 0;
|
|
||||||
for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) {
|
|
||||||
if (cursor_addr >= addr) break;
|
|
||||||
cursor_addr += 2;
|
|
||||||
offset += rinfo->inst_lens[i];
|
|
||||||
}
|
|
||||||
uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset;
|
|
||||||
if (is_thumb) fixed_addr = SH_UTIL_SET_BIT0(fixed_addr);
|
|
||||||
|
|
||||||
SH_LOG_INFO("txx rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
|
|
||||||
return fixed_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_thumb) addr = SH_UTIL_SET_BIT0(addr);
|
|
||||||
return addr;
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t start_addr;
|
|
||||||
uintptr_t end_addr;
|
|
||||||
uint16_t *buf;
|
|
||||||
size_t buf_offset;
|
|
||||||
size_t inst_lens[13]; // 26 / 2 = 13
|
|
||||||
size_t inst_lens_cnt;
|
|
||||||
} sh_txx_rewrite_info_t;
|
|
||||||
|
|
||||||
bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo);
|
|
||||||
uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo);
|
|
@ -1,310 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_a64.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sh_util.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
|
|
||||||
// https://developer.arm.com/documentation/ddi0487/latest
|
|
||||||
// https://developer.arm.com/documentation/ddi0602/latest
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IGNORED = 0,
|
|
||||||
B,
|
|
||||||
B_COND,
|
|
||||||
BL,
|
|
||||||
ADR,
|
|
||||||
ADRP,
|
|
||||||
LDR_LIT_32,
|
|
||||||
LDR_LIT_64,
|
|
||||||
LDRSW_LIT,
|
|
||||||
PRFM_LIT,
|
|
||||||
LDR_SIMD_LIT_32,
|
|
||||||
LDR_SIMD_LIT_64,
|
|
||||||
LDR_SIMD_LIT_128,
|
|
||||||
CBZ,
|
|
||||||
CBNZ,
|
|
||||||
TBZ,
|
|
||||||
TBNZ
|
|
||||||
} sh_a64_type_t;
|
|
||||||
|
|
||||||
static sh_a64_type_t sh_a64_get_type(uint32_t inst) {
|
|
||||||
if ((inst & 0xFC000000) == 0x14000000)
|
|
||||||
return B;
|
|
||||||
else if ((inst & 0xFF000010) == 0x54000000)
|
|
||||||
return B_COND;
|
|
||||||
else if ((inst & 0xFC000000) == 0x94000000)
|
|
||||||
return BL;
|
|
||||||
else if ((inst & 0x9F000000) == 0x10000000)
|
|
||||||
return ADR;
|
|
||||||
else if ((inst & 0x9F000000) == 0x90000000)
|
|
||||||
return ADRP;
|
|
||||||
else if ((inst & 0xFF000000) == 0x18000000)
|
|
||||||
return LDR_LIT_32;
|
|
||||||
else if ((inst & 0xFF000000) == 0x58000000)
|
|
||||||
return LDR_LIT_64;
|
|
||||||
else if ((inst & 0xFF000000) == 0x98000000)
|
|
||||||
return LDRSW_LIT;
|
|
||||||
else if ((inst & 0xFF000000) == 0xD8000000)
|
|
||||||
return PRFM_LIT;
|
|
||||||
else if ((inst & 0xFF000000) == 0x1C000000)
|
|
||||||
return LDR_SIMD_LIT_32;
|
|
||||||
else if ((inst & 0xFF000000) == 0x5C000000)
|
|
||||||
return LDR_SIMD_LIT_64;
|
|
||||||
else if ((inst & 0xFF000000) == 0x9C000000)
|
|
||||||
return LDR_SIMD_LIT_128;
|
|
||||||
else if ((inst & 0x7F000000u) == 0x34000000)
|
|
||||||
return CBZ;
|
|
||||||
else if ((inst & 0x7F000000u) == 0x35000000)
|
|
||||||
return CBNZ;
|
|
||||||
else if ((inst & 0x7F000000u) == 0x36000000)
|
|
||||||
return TBZ;
|
|
||||||
else if ((inst & 0x7F000000u) == 0x37000000)
|
|
||||||
return TBNZ;
|
|
||||||
else
|
|
||||||
return IGNORED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a64_get_rewrite_inst_len(uint32_t inst) {
|
|
||||||
static uint8_t map[] = {
|
|
||||||
4, // IGNORED
|
|
||||||
20, // B
|
|
||||||
28, // B_COND
|
|
||||||
20, // BL
|
|
||||||
16, // ADR
|
|
||||||
16, // ADRP
|
|
||||||
20, // LDR_LIT_32
|
|
||||||
20, // LDR_LIT_64
|
|
||||||
20, // LDRSW_LIT
|
|
||||||
28, // PRFM_LIT
|
|
||||||
28, // LDR_SIMD_LIT_32
|
|
||||||
28, // LDR_SIMD_LIT_64
|
|
||||||
28, // LDR_SIMD_LIT_128
|
|
||||||
24, // CBZ
|
|
||||||
24, // CBNZ
|
|
||||||
24, // TBZ
|
|
||||||
24 // TBNZ
|
|
||||||
};
|
|
||||||
|
|
||||||
return (size_t)(map[sh_a64_get_type(inst)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_a64_is_addr_need_fix(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
return (rinfo->start_addr <= addr && addr < rinfo->end_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t sh_a64_fix_addr(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
if (rinfo->start_addr <= addr && addr < rinfo->end_addr) {
|
|
||||||
uintptr_t cursor_addr = rinfo->start_addr;
|
|
||||||
size_t offset = 0;
|
|
||||||
for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) {
|
|
||||||
if (cursor_addr >= addr) break;
|
|
||||||
cursor_addr += 4;
|
|
||||||
offset += rinfo->inst_lens[i];
|
|
||||||
}
|
|
||||||
uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset;
|
|
||||||
SH_LOG_INFO("a64 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
|
|
||||||
return fixed_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a64_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
|
|
||||||
sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
uint64_t imm64;
|
|
||||||
if (type == B_COND) {
|
|
||||||
uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
|
|
||||||
imm64 = SH_UTIL_SIGN_EXTEND_64(imm19 << 2u, 21u);
|
|
||||||
} else {
|
|
||||||
uint64_t imm26 = SH_UTIL_GET_BITS_32(inst, 25, 0);
|
|
||||||
imm64 = SH_UTIL_SIGN_EXTEND_64(imm26 << 2u, 28u);
|
|
||||||
}
|
|
||||||
uint64_t addr = pc + imm64;
|
|
||||||
addr = sh_a64_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
if (type == B_COND) {
|
|
||||||
buf[idx++] = (inst & 0xFF00001F) | 0x40u; // B.<cond> #8
|
|
||||||
buf[idx++] = 0x14000006; // B #24
|
|
||||||
}
|
|
||||||
buf[idx++] = 0x58000051; // LDR X17, #8
|
|
||||||
buf[idx++] = 0x14000003; // B #12
|
|
||||||
buf[idx++] = addr & 0xFFFFFFFF;
|
|
||||||
buf[idx++] = addr >> 32u;
|
|
||||||
if (type == BL)
|
|
||||||
buf[idx++] = 0xD63F0220; // BLR X17
|
|
||||||
else
|
|
||||||
buf[idx++] = 0xD61F0220; // BR X17
|
|
||||||
return idx * 4; // 20 or 28
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a64_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
|
|
||||||
sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t xd = SH_UTIL_GET_BITS_32(inst, 4, 0);
|
|
||||||
uint64_t immlo = SH_UTIL_GET_BITS_32(inst, 30, 29);
|
|
||||||
uint64_t immhi = SH_UTIL_GET_BITS_32(inst, 23, 5);
|
|
||||||
uint64_t addr;
|
|
||||||
if (type == ADR)
|
|
||||||
addr = pc + SH_UTIL_SIGN_EXTEND_64((immhi << 2u) | immlo, 21u);
|
|
||||||
else // ADRP
|
|
||||||
addr = (pc & 0xFFFFFFFFFFFFF000) + SH_UTIL_SIGN_EXTEND_64((immhi << 14u) | (immlo << 12u), 33u);
|
|
||||||
if (sh_a64_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
|
|
||||||
|
|
||||||
buf[0] = 0x58000040u | xd; // LDR Xd, #8
|
|
||||||
buf[1] = 0x14000003; // B #12
|
|
||||||
buf[2] = addr & 0xFFFFFFFF;
|
|
||||||
buf[3] = addr >> 32u;
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a64_rewrite_ldr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type,
|
|
||||||
sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
uint32_t rt = SH_UTIL_GET_BITS_32(inst, 4, 0);
|
|
||||||
uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
|
|
||||||
uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u);
|
|
||||||
uint64_t addr = pc + offset;
|
|
||||||
|
|
||||||
if (sh_a64_is_addr_need_fix(addr, rinfo)) {
|
|
||||||
if (type != PRFM_LIT) return 0; // rewrite failed
|
|
||||||
addr = sh_a64_fix_addr(addr, rinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT) {
|
|
||||||
buf[0] = 0x58000060u | rt; // LDR Xt, #12
|
|
||||||
if (type == LDR_LIT_32)
|
|
||||||
buf[1] = 0xB9400000 | rt | (rt << 5u); // LDR Wt, [Xt]
|
|
||||||
else if (type == LDR_LIT_64)
|
|
||||||
buf[1] = 0xF9400000 | rt | (rt << 5u); // LDR Xt, [Xt]
|
|
||||||
else
|
|
||||||
// LDRSW_LIT
|
|
||||||
buf[1] = 0xB9800000 | rt | (rt << 5u); // LDRSW Xt, [Xt]
|
|
||||||
buf[2] = 0x14000003; // B #12
|
|
||||||
buf[3] = addr & 0xFFFFFFFF;
|
|
||||||
buf[4] = addr >> 32u;
|
|
||||||
return 20;
|
|
||||||
} else {
|
|
||||||
buf[0] = 0xA93F47F0; // STP X16, X17, [SP, -0x10]
|
|
||||||
buf[1] = 0x58000091; // LDR X17, #16
|
|
||||||
if (type == PRFM_LIT)
|
|
||||||
buf[2] = 0xF9800220 | rt; // PRFM Rt, [X17]
|
|
||||||
else if (type == LDR_SIMD_LIT_32)
|
|
||||||
buf[2] = 0xBD400220 | rt; // LDR St, [X17]
|
|
||||||
else if (type == LDR_SIMD_LIT_64)
|
|
||||||
buf[2] = 0xFD400220 | rt; // LDR Dt, [X17]
|
|
||||||
else
|
|
||||||
// LDR_SIMD_LIT_128
|
|
||||||
buf[2] = 0x3DC00220u | rt; // LDR Qt, [X17]
|
|
||||||
buf[3] = 0xF85F83F1; // LDR X17, [SP, -0x8]
|
|
||||||
buf[4] = 0x14000003; // B #12
|
|
||||||
buf[5] = addr & 0xFFFFFFFF;
|
|
||||||
buf[6] = addr >> 32u;
|
|
||||||
return 28;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a64_rewrite_cb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5);
|
|
||||||
uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u);
|
|
||||||
uint64_t addr = pc + offset;
|
|
||||||
addr = sh_a64_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
buf[0] = (inst & 0xFF00001F) | 0x40u; // CB(N)Z Rt, #8
|
|
||||||
buf[1] = 0x14000005; // B #20
|
|
||||||
buf[2] = 0x58000051; // LDR X17, #8
|
|
||||||
buf[3] = 0xd61f0220; // BR X17
|
|
||||||
buf[4] = addr & 0xFFFFFFFF;
|
|
||||||
buf[5] = addr >> 32u;
|
|
||||||
return 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_a64_rewrite_tb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
uint64_t imm14 = SH_UTIL_GET_BITS_32(inst, 18, 5);
|
|
||||||
uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm14 << 2u), 16u);
|
|
||||||
uint64_t addr = pc + offset;
|
|
||||||
addr = sh_a64_fix_addr(addr, rinfo);
|
|
||||||
|
|
||||||
buf[0] = (inst & 0xFFF8001F) | 0x40u; // TB(N)Z Rt, #<imm>, #8
|
|
||||||
buf[1] = 0x14000005; // B #20
|
|
||||||
buf[2] = 0x58000051; // LDR X17, #8
|
|
||||||
buf[3] = 0xd61f0220; // BR X17
|
|
||||||
buf[4] = addr & 0xFFFFFFFF;
|
|
||||||
buf[5] = addr >> 32u;
|
|
||||||
return 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) {
|
|
||||||
sh_a64_type_t type = sh_a64_get_type(inst);
|
|
||||||
SH_LOG_INFO("a64 rewrite: type %d, inst %" PRIx32, type, inst);
|
|
||||||
|
|
||||||
if (type == B || type == B_COND || type == BL)
|
|
||||||
return sh_a64_rewrite_b(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == ADR || type == ADRP)
|
|
||||||
return sh_a64_rewrite_adr(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT || type == PRFM_LIT ||
|
|
||||||
type == LDR_SIMD_LIT_32 || type == LDR_SIMD_LIT_64 || type == LDR_SIMD_LIT_128)
|
|
||||||
return sh_a64_rewrite_ldr(buf, inst, pc, type, rinfo);
|
|
||||||
else if (type == CBZ || type == CBNZ)
|
|
||||||
return sh_a64_rewrite_cb(buf, inst, pc, rinfo);
|
|
||||||
else if (type == TBZ || type == TBNZ)
|
|
||||||
return sh_a64_rewrite_tb(buf, inst, pc, rinfo);
|
|
||||||
else {
|
|
||||||
// IGNORED
|
|
||||||
buf[0] = inst;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr) {
|
|
||||||
buf[0] = 0x58000051; // LDR X17, #8
|
|
||||||
buf[1] = 0xd61f0220; // BR X17
|
|
||||||
buf[2] = addr & 0xFFFFFFFF;
|
|
||||||
buf[3] = addr >> 32u;
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use RET instead of BR to bypass arm64 BTI.
|
|
||||||
//
|
|
||||||
// ref:
|
|
||||||
// https://developer.arm.com/documentation/102433/0100/Jump-oriented-programming
|
|
||||||
// https://developer.arm.com/documentation/ddi0602/2023-06/Base-Instructions/BTI--Branch-Target-Identification-?lang=en
|
|
||||||
// https://github.com/torvalds/linux/commit/8ef8f360cf30be12382f89ff48a57fbbd9b31c14
|
|
||||||
// https://android-review.googlesource.com/c/platform/bionic/+/1242754
|
|
||||||
// https://developer.android.com/ndk/guides/abis#armv9_enabling_pac_and_bti_for_cc
|
|
||||||
// https://developer.arm.com/documentation/100067/0612/armclang-Command-line-Options/-mbranch-protection
|
|
||||||
size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr) {
|
|
||||||
buf[0] = 0x58000051; // LDR X17, #8
|
|
||||||
buf[1] = 0xd65f0220; // RET X17
|
|
||||||
buf[2] = addr & 0xFFFFFFFF;
|
|
||||||
buf[3] = addr >> 32u;
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) {
|
|
||||||
buf[0] = 0x14000000u | (((addr - pc) & 0x0FFFFFFFu) >> 2u); // B <label>
|
|
||||||
return 4;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_inst.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t start_addr;
|
|
||||||
uintptr_t end_addr;
|
|
||||||
uint32_t *buf;
|
|
||||||
size_t buf_offset;
|
|
||||||
size_t inst_lens[4];
|
|
||||||
size_t inst_lens_cnt;
|
|
||||||
} sh_a64_rewrite_info_t;
|
|
||||||
|
|
||||||
size_t sh_a64_get_rewrite_inst_len(uint32_t inst);
|
|
||||||
size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo);
|
|
||||||
|
|
||||||
size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr);
|
|
||||||
size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr);
|
|
||||||
size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc);
|
|
@ -1,203 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_inst.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "sh_a64.h"
|
|
||||||
#include "sh_config.h"
|
|
||||||
#include "sh_enter.h"
|
|
||||||
#include "sh_exit.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
static int sh_inst_hook_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr,
|
|
||||||
uintptr_t *orig_addr2) {
|
|
||||||
// backup original instructions (length: 4 or 16)
|
|
||||||
memcpy((void *)(self->backup), (void *)target_addr, self->backup_len);
|
|
||||||
|
|
||||||
// package the information passed to rewrite
|
|
||||||
sh_a64_rewrite_info_t rinfo;
|
|
||||||
rinfo.start_addr = target_addr;
|
|
||||||
rinfo.end_addr = target_addr + self->backup_len;
|
|
||||||
rinfo.buf = (uint32_t *)self->enter_addr;
|
|
||||||
rinfo.buf_offset = 0;
|
|
||||||
rinfo.inst_lens_cnt = self->backup_len / 4;
|
|
||||||
for (uintptr_t i = 0; i < self->backup_len; i += 4)
|
|
||||||
rinfo.inst_lens[i / 4] = sh_a64_get_rewrite_inst_len(*((uint32_t *)(target_addr + i)));
|
|
||||||
|
|
||||||
// rewrite original instructions (fill in enter)
|
|
||||||
uintptr_t pc = target_addr;
|
|
||||||
for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) {
|
|
||||||
size_t offset = sh_a64_rewrite((uint32_t *)(self->enter_addr + rinfo.buf_offset),
|
|
||||||
*((uint32_t *)(target_addr + i)), pc, &rinfo);
|
|
||||||
if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED;
|
|
||||||
rinfo.buf_offset += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// absolute jump back to remaining original instructions (fill in enter)
|
|
||||||
rinfo.buf_offset += sh_a64_absolute_jump_with_ret((uint32_t *)(self->enter_addr + rinfo.buf_offset),
|
|
||||||
target_addr + self->backup_len);
|
|
||||||
sh_util_clear_cache(self->enter_addr, rinfo.buf_offset);
|
|
||||||
|
|
||||||
// save original function address
|
|
||||||
if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST);
|
|
||||||
if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
|
|
||||||
// B: [-128M, +128M - 4]
|
|
||||||
#define SH_INST_A64_B_RANGE_LOW (134217728)
|
|
||||||
#define SH_INST_A64_B_RANGE_HIGH (134217724)
|
|
||||||
|
|
||||||
static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
uintptr_t pc = target_addr;
|
|
||||||
self->backup_len = 4;
|
|
||||||
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
|
|
||||||
// alloc an exit for absolute jump
|
|
||||||
sh_a64_absolute_jump_with_br(self->exit, new_addr);
|
|
||||||
if (0 !=
|
|
||||||
(r = sh_exit_alloc(&self->exit_addr, (uint16_t *)&self->exit_type, pc, dlinfo, (uint8_t *)(self->exit),
|
|
||||||
sizeof(self->exit), SH_INST_A64_B_RANGE_LOW, SH_INST_A64_B_RANGE_HIGH)))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) goto err;
|
|
||||||
|
|
||||||
// relative jump to the exit by overwriting the head of original function
|
|
||||||
sh_a64_relative_jump(self->trampo, self->exit_addr, pc);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err;
|
|
||||||
|
|
||||||
SH_LOG_INFO("a64: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR
|
|
||||||
" -> enter %" PRIxPTR " -> remaining %" PRIxPTR,
|
|
||||||
target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit));
|
|
||||||
self->exit_addr = 0; // this is a flag for with-exit or without-exit
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int sh_inst_hook_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo,
|
|
||||||
uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
int r;
|
|
||||||
self->backup_len = 16;
|
|
||||||
|
|
||||||
if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
|
|
||||||
// rewrite
|
|
||||||
if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
return SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) return r;
|
|
||||||
|
|
||||||
// absolute jump to new function address by overwriting the head of original function
|
|
||||||
sh_a64_absolute_jump_with_br(self->trampo, new_addr);
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r;
|
|
||||||
|
|
||||||
SH_LOG_INFO("a64: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR
|
|
||||||
" -> remaining %" PRIxPTR,
|
|
||||||
target_addr, new_addr, self->enter_addr, target_addr + self->backup_len);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, uintptr_t *orig_addr2) {
|
|
||||||
self->enter_addr = sh_enter_alloc();
|
|
||||||
if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER;
|
|
||||||
|
|
||||||
int r;
|
|
||||||
#ifdef SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
if (0 == (r = sh_inst_hook_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) return r;
|
|
||||||
#endif
|
|
||||||
if (0 == (r = sh_inst_hook_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// hook failed
|
|
||||||
if (NULL != orig_addr) *orig_addr = 0;
|
|
||||||
if (NULL != orig_addr2) *orig_addr2 = 0;
|
|
||||||
sh_enter_free(self->enter_addr);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
// restore the instructions at the target address
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = memcmp((void *)target_addr, self->trampo, self->backup_len);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH;
|
|
||||||
if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r;
|
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
||||||
|
|
||||||
// free memory space for exit
|
|
||||||
if (0 != self->exit_addr)
|
|
||||||
if (0 != (r = sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit),
|
|
||||||
sizeof(self->exit))))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// free memory space for enter
|
|
||||||
sh_enter_free(self->enter_addr);
|
|
||||||
|
|
||||||
SH_LOG_INFO("a64: unhook OK. target %" PRIxPTR, target_addr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t trampo[4]; // align 16 // length == backup_len
|
|
||||||
uint8_t backup[16]; // align 16
|
|
||||||
uint32_t backup_len; // == 4 or 16
|
|
||||||
uint32_t exit_type;
|
|
||||||
uintptr_t exit_addr;
|
|
||||||
uint32_t exit[4];
|
|
||||||
uintptr_t enter_addr;
|
|
||||||
} sh_inst_t;
|
|
||||||
|
|
||||||
int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, uintptr_t *orig_addr2);
|
|
||||||
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);
|
|
@ -1,291 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
// version 1.0.4
|
|
||||||
|
|
||||||
#include "bytesig.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define BYTESIG_DEBUG 0
|
|
||||||
|
|
||||||
#if BYTESIG_DEBUG
|
|
||||||
#include <android/log.h>
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
|
||||||
#define BYTESIG_LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "bytesig_tag", fmt, ##__VA_ARGS__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#else
|
|
||||||
#define BYTESIG_LOG(fmt, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
BYTESIG_STATUS_UNAVAILABLE = 0,
|
|
||||||
BYTESIG_STATUS_SIG32 = 1, // use sigset_t
|
|
||||||
BYTESIG_STATUS_SIG64 = 2 // use sigset64_t
|
|
||||||
} bytesig_status_t;
|
|
||||||
static bytesig_status_t bytesig_status = BYTESIG_STATUS_UNAVAILABLE;
|
|
||||||
|
|
||||||
extern __attribute((weak)) int sigfillset64(sigset64_t *);
|
|
||||||
extern __attribute((weak)) int sigemptyset64(sigset64_t *);
|
|
||||||
extern __attribute((weak)) int sigaddset64(sigset64_t *, int);
|
|
||||||
extern __attribute((weak)) int sigismember64(const sigset64_t *, int);
|
|
||||||
|
|
||||||
typedef int (*bytesig_sigaction64_t)(int, const struct sigaction64 *, struct sigaction64 *);
|
|
||||||
typedef int (*bytesig_sigaction_t)(int, const struct sigaction *, struct sigaction *);
|
|
||||||
typedef int (*bytesig_sigprocmask64_t)(int, const sigset64_t *, sigset64_t *);
|
|
||||||
typedef int (*bytesig_sigprocmask_t)(int, const sigset_t *, sigset_t *);
|
|
||||||
|
|
||||||
static void *bytesig_sigaction; // point to libc's sigaction64() or libc's sigaction()
|
|
||||||
static void *bytesig_sigprocmask; // point to libc's sigprocmask() or libc's sigprocmask64()
|
|
||||||
|
|
||||||
__attribute__((constructor)) static void bytesig_ctor(void) {
|
|
||||||
void *libc = dlopen("libc.so", RTLD_LOCAL);
|
|
||||||
if (__predict_false(NULL == libc)) return;
|
|
||||||
|
|
||||||
if (__predict_true(NULL != sigfillset64 && NULL != sigemptyset64 && NULL != sigaddset64 &&
|
|
||||||
NULL != sigismember64)) {
|
|
||||||
if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction64")) &&
|
|
||||||
NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask64")))) {
|
|
||||||
bytesig_status = BYTESIG_STATUS_SIG64;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction")) &&
|
|
||||||
NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask")))) {
|
|
||||||
bytesig_status = BYTESIG_STATUS_SIG32;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
dlclose(libc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BYTESIG_PROTECTED_THREADS_MAX 256
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct {
|
|
||||||
pid_t tids[BYTESIG_PROTECTED_THREADS_MAX];
|
|
||||||
sigjmp_buf *jbufs[BYTESIG_PROTECTED_THREADS_MAX];
|
|
||||||
union {
|
|
||||||
struct sigaction64 prev_action64;
|
|
||||||
struct sigaction prev_action;
|
|
||||||
};
|
|
||||||
} bytesig_signal_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// array index is signal number, corresponds to signals 1 to 31, except 9 and 19
|
|
||||||
static bytesig_signal_t *bytesig_signal_array[__SIGRTMIN];
|
|
||||||
|
|
||||||
static void bytesig_sigorset64(sigset64_t *dest, sigset64_t *left, sigset64_t *right) {
|
|
||||||
sigemptyset64(dest);
|
|
||||||
for (size_t i = 1; i < sizeof(sigset64_t) * CHAR_BIT; i++)
|
|
||||||
if (sigismember64(left, (int)i) == 1 || sigismember64(right, (int)i) == 1) sigaddset64(dest, (int)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bytesig_sigorset(sigset_t *dest, sigset_t *left, sigset_t *right) {
|
|
||||||
sigemptyset(dest);
|
|
||||||
for (size_t i = 1; i < sizeof(sigset_t) * CHAR_BIT; i++)
|
|
||||||
if (sigismember(left, (int)i) == 1 || sigismember(right, (int)i) == 1) sigaddset(dest, (int)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) static void bytesig_handler_internal(int signum, siginfo_t *siginfo,
|
|
||||||
void *context) {
|
|
||||||
bytesig_signal_t *sig = bytesig_signal_array[signum];
|
|
||||||
|
|
||||||
// check protect info & do siglongjmp
|
|
||||||
pid_t tid = gettid();
|
|
||||||
if (__predict_false(0 == tid)) tid = (pid_t)syscall(SYS_gettid);
|
|
||||||
for (size_t i = 0; i < BYTESIG_PROTECTED_THREADS_MAX; i++) {
|
|
||||||
if (__predict_false(tid == __atomic_load_n(&(sig->tids[i]), __ATOMIC_RELAXED))) {
|
|
||||||
BYTESIG_LOG("siglongjmp signal %d (code %d) at %zu", signum, siginfo->si_code, i);
|
|
||||||
|
|
||||||
unsigned int ret_signum = (((unsigned int)signum & 0xFFU) << 16U);
|
|
||||||
unsigned int ret_code = 0U;
|
|
||||||
if (siginfo->si_code > 0)
|
|
||||||
ret_code = (((unsigned int)(siginfo->si_code) & 0xFFU) << 8U);
|
|
||||||
else if (siginfo->si_code < 0)
|
|
||||||
ret_code = (unsigned int)(-(siginfo->si_code)) & 0xFFU;
|
|
||||||
int ret_val = (int)(ret_signum | ret_code);
|
|
||||||
|
|
||||||
siglongjmp(*(__atomic_load_n(&(sig->jbufs[i]), __ATOMIC_RELAXED)), ret_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SET_THREAD_SIGNAL_MASK(suffix) \
|
|
||||||
do { \
|
|
||||||
sigset##suffix##_t prev_mask; \
|
|
||||||
bytesig_sigorset##suffix(&prev_mask, &(((ucontext_t *)context)->uc_sigmask##suffix), \
|
|
||||||
&(sig->prev_action##suffix.sa_mask)); \
|
|
||||||
if (0 == ((unsigned int)(sig->prev_action##suffix.sa_flags) & (unsigned int)SA_NODEFER)) \
|
|
||||||
sigaddset##suffix(&prev_mask, signum); \
|
|
||||||
sigaddset##suffix(&prev_mask, SIGPIPE); \
|
|
||||||
sigaddset##suffix(&prev_mask, SIGUSR1); \
|
|
||||||
sigaddset##suffix(&prev_mask, SIGQUIT); \
|
|
||||||
((bytesig_sigprocmask##suffix##_t)bytesig_sigprocmask)(SIG_SETMASK, &prev_mask, NULL); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// set thread signal mask
|
|
||||||
if (BYTESIG_STATUS_SIG64 == bytesig_status)
|
|
||||||
SET_THREAD_SIGNAL_MASK(64);
|
|
||||||
else
|
|
||||||
SET_THREAD_SIGNAL_MASK();
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://llvm.org/docs/CodeGenerator.html#tail-call-optimization
|
|
||||||
// https://clang.llvm.org/docs/AttributeReference.html#disable-tail-calls
|
|
||||||
//__attribute__((disable_tail_calls))
|
|
||||||
static void bytesig_handler(int signum, siginfo_t *siginfo, void *context) {
|
|
||||||
bytesig_handler_internal(signum, siginfo, context);
|
|
||||||
|
|
||||||
#define CALL_PREVIOUS_SIGNAL_HANDLER(suffix) \
|
|
||||||
do { \
|
|
||||||
if (__predict_true(sig->prev_action##suffix.sa_flags & SA_SIGINFO)) \
|
|
||||||
sig->prev_action##suffix.sa_sigaction(signum, siginfo, context); \
|
|
||||||
else if (SIG_DFL != sig->prev_action##suffix.sa_handler && \
|
|
||||||
SIG_IGN != sig->prev_action##suffix.sa_handler) \
|
|
||||||
sig->prev_action##suffix.sa_handler(signum); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// call previous signal handler
|
|
||||||
bytesig_signal_t *sig = bytesig_signal_array[signum];
|
|
||||||
if (BYTESIG_STATUS_SIG64 == bytesig_status)
|
|
||||||
CALL_PREVIOUS_SIGNAL_HANDLER(64);
|
|
||||||
else
|
|
||||||
CALL_PREVIOUS_SIGNAL_HANDLER();
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesig_init(int signum) {
|
|
||||||
if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
|
|
||||||
return -1;
|
|
||||||
if (__predict_false(BYTESIG_STATUS_UNAVAILABLE == bytesig_status)) return -1;
|
|
||||||
if (__predict_false(NULL != bytesig_signal_array[signum])) return -1;
|
|
||||||
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
int ret = -1;
|
|
||||||
if (__predict_false(NULL != bytesig_signal_array[signum])) goto end;
|
|
||||||
|
|
||||||
bytesig_signal_t *sig = calloc(1, sizeof(bytesig_signal_t));
|
|
||||||
if (__predict_false(NULL == sig)) goto end;
|
|
||||||
|
|
||||||
#define SA_EXPOSE_TAGBITS 0x00000800
|
|
||||||
|
|
||||||
#define REGISTER_SIGNAL_HANDLER(suffix) \
|
|
||||||
do { \
|
|
||||||
struct sigaction##suffix act; \
|
|
||||||
memset(&act, 0, sizeof(struct sigaction##suffix)); \
|
|
||||||
sigfillset##suffix(&act.sa_mask); \
|
|
||||||
act.sa_sigaction = bytesig_handler; \
|
|
||||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART | SA_EXPOSE_TAGBITS; \
|
|
||||||
if (__predict_false( \
|
|
||||||
0 != \
|
|
||||||
((bytesig_sigaction##suffix##_t)bytesig_sigaction)(signum, &act, &sig->prev_action##suffix))) { \
|
|
||||||
free(sig); \
|
|
||||||
goto end; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// register the signal handler, we start off with all signals blocked
|
|
||||||
if (BYTESIG_STATUS_SIG64 == bytesig_status)
|
|
||||||
REGISTER_SIGNAL_HANDLER(64);
|
|
||||||
else
|
|
||||||
REGISTER_SIGNAL_HANDLER();
|
|
||||||
|
|
||||||
bytesig_signal_array[signum] = sig;
|
|
||||||
ret = 0; // OK
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt) {
|
|
||||||
for (size_t i = 0; i < signums_cnt; i++) {
|
|
||||||
int signum = signums[i];
|
|
||||||
if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bytesig_signal_t *sig = bytesig_signal_array[signum];
|
|
||||||
if (__predict_false(NULL == sig)) continue;
|
|
||||||
|
|
||||||
// check repeated thread
|
|
||||||
bool repeated = false;
|
|
||||||
for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) {
|
|
||||||
if (__predict_false(tid == sig->tids[j])) {
|
|
||||||
repeated = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (__predict_false(repeated)) continue;
|
|
||||||
|
|
||||||
// save thread-ID and jump buffer
|
|
||||||
size_t j = 0;
|
|
||||||
while (1) {
|
|
||||||
if (0 == sig->tids[j]) {
|
|
||||||
pid_t expected = 0;
|
|
||||||
if (__atomic_compare_exchange_n(&sig->tids[j], &expected, tid, false, __ATOMIC_ACQUIRE,
|
|
||||||
__ATOMIC_RELAXED)) {
|
|
||||||
sig->jbufs[j] = jbuf;
|
|
||||||
BYTESIG_LOG("protect_start signal %d at %zu", signum, j);
|
|
||||||
break; // finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
j++;
|
|
||||||
if (__predict_false(BYTESIG_PROTECTED_THREADS_MAX == j)) j = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt) {
|
|
||||||
for (size_t i = 0; i < signums_cnt; i++) {
|
|
||||||
int signum = signums[i];
|
|
||||||
if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bytesig_signal_t *sig = bytesig_signal_array[signum];
|
|
||||||
if (__predict_false(NULL == sig)) continue;
|
|
||||||
|
|
||||||
// free thread-ID and jump buffer
|
|
||||||
for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) {
|
|
||||||
if (tid == sig->tids[j]) {
|
|
||||||
sig->jbufs[j] = NULL;
|
|
||||||
__atomic_store_n(&sig->tids[j], 0, __ATOMIC_RELEASE);
|
|
||||||
BYTESIG_LOG("protect_end signal %d at %zu", signum, j);
|
|
||||||
break; // finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
// version 1.0.4
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #include "bytesig.h"
|
|
||||||
*
|
|
||||||
* void init_once(void)
|
|
||||||
* {
|
|
||||||
* bytesig_init(SIGSEGV);
|
|
||||||
* bytesig_init(SIGBUS);
|
|
||||||
* bytesig_init(SIGILL);
|
|
||||||
* bytesig_init(SIGABRT);
|
|
||||||
* // ......
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* void unstable_func(void)
|
|
||||||
* {
|
|
||||||
* int *p = NULL;
|
|
||||||
*
|
|
||||||
* //
|
|
||||||
* // usage 1
|
|
||||||
* //
|
|
||||||
* BYTESIG_TRY(SIGSEGV, SIGBUS)
|
|
||||||
* {
|
|
||||||
* *p = 1;
|
|
||||||
* }
|
|
||||||
* BYTESIG_CATCH(signum, code)
|
|
||||||
* {
|
|
||||||
* LOG("signum %d (code %d)", signum, code);
|
|
||||||
* }
|
|
||||||
* BYTESIG_EXIT
|
|
||||||
*
|
|
||||||
* //
|
|
||||||
* // usage 2
|
|
||||||
* //
|
|
||||||
* BYTESIG_TRY(SIGSEGV, SIGBUS)
|
|
||||||
* {
|
|
||||||
* *p = 2;
|
|
||||||
* }
|
|
||||||
* BYTESIG_CATCH(signum)
|
|
||||||
* {
|
|
||||||
* LOG("signum %d", signum);
|
|
||||||
* }
|
|
||||||
* BYTESIG_EXIT
|
|
||||||
*
|
|
||||||
* //
|
|
||||||
* // usage 3
|
|
||||||
* //
|
|
||||||
* BYTESIG_TRY(SIGILL)
|
|
||||||
* {
|
|
||||||
* func_maybe_illed();
|
|
||||||
* }
|
|
||||||
* BYTESIG_CATCH()
|
|
||||||
* {
|
|
||||||
* do_something();
|
|
||||||
* }
|
|
||||||
* BYTESIG_EXIT
|
|
||||||
*
|
|
||||||
* //
|
|
||||||
* // usage 4
|
|
||||||
* //
|
|
||||||
* BYTESIG_TRY(SIGABRT)
|
|
||||||
* {
|
|
||||||
* func_maybe_aborted();
|
|
||||||
* }
|
|
||||||
* BYTESIG_EXIT
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BYTEDANCE_BYTESIG
|
|
||||||
#define BYTEDANCE_BYTESIG
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define BYTESIG_TRY(...) \
|
|
||||||
do { \
|
|
||||||
pid_t _bytesig_tid_ = gettid(); \
|
|
||||||
if (0 == _bytesig_tid_) _bytesig_tid_ = (pid_t)syscall(SYS_gettid); \
|
|
||||||
sigjmp_buf _bytesig_jbuf_; \
|
|
||||||
int _bytesig_sigs_[] = {__VA_ARGS__}; \
|
|
||||||
bytesig_protect(_bytesig_tid_, &_bytesig_jbuf_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
|
|
||||||
int _bytesig_protected_ = 1; \
|
|
||||||
int _bytesig_ex_ = sigsetjmp(_bytesig_jbuf_, 1); \
|
|
||||||
if (0 == _bytesig_ex_) {
|
|
||||||
#define BYTESIG_CATCH_2(signum_, code_) \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
|
|
||||||
_bytesig_protected_ = 0; \
|
|
||||||
int signum_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF0000U) >> 16U); \
|
|
||||||
int code_ = 0; \
|
|
||||||
if (((unsigned int)_bytesig_ex_ & 0xFF00U) > 0) \
|
|
||||||
code_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF00U) >> 8U); \
|
|
||||||
else if (((unsigned int)_bytesig_ex_ & 0xFFU) > 0) \
|
|
||||||
code_ = -((int)((unsigned int)_bytesig_ex_ & 0xFFU)); \
|
|
||||||
(void)signum_; \
|
|
||||||
(void)code_;
|
|
||||||
|
|
||||||
#define BYTESIG_CATCH_1(signum_) BYTESIG_CATCH_2(signum_, _bytesig_code_)
|
|
||||||
#define BYTESIG_CATCH_0() BYTESIG_CATCH_1(_bytesig_signum_)
|
|
||||||
|
|
||||||
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
|
|
||||||
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
|
|
||||||
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))
|
|
||||||
#define NO_ARG_EXPANDER() , , BYTESIG_CATCH_0
|
|
||||||
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__())
|
|
||||||
|
|
||||||
#define BYTESIG_CATCH(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
|
|
||||||
|
|
||||||
#define BYTESIG_EXIT \
|
|
||||||
} \
|
|
||||||
if (1 == _bytesig_protected_) \
|
|
||||||
bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
|
|
||||||
} \
|
|
||||||
while (0) \
|
|
||||||
;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int bytesig_init(int signum);
|
|
||||||
|
|
||||||
void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt);
|
|
||||||
void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,55 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Global debugging.
|
|
||||||
//
|
|
||||||
// Note that in some cases these logs themselves can cause a crash.
|
|
||||||
//
|
|
||||||
//#define SH_CONFIG_DEBUG
|
|
||||||
|
|
||||||
// Operation record of hook and unhook.
|
|
||||||
//
|
|
||||||
// Disabling it can reduce hook/unhook latency, memory footprint and file size.
|
|
||||||
//
|
|
||||||
#define SH_CONFIG_OPERATION_RECORDS
|
|
||||||
|
|
||||||
// Crash signal protection.
|
|
||||||
//
|
|
||||||
// Do not disable it in a production environment.
|
|
||||||
//
|
|
||||||
#define SH_CONFIG_SIG_PROT
|
|
||||||
|
|
||||||
// When hooking, try to find an exit for the relative jump for the dynamic library.
|
|
||||||
//
|
|
||||||
// Do not disable it in a production environment.
|
|
||||||
//
|
|
||||||
#define SH_CONFIG_TRY_WITH_EXIT
|
|
||||||
|
|
||||||
// When hooking the function of the thumb instruction, if the function is too short,
|
|
||||||
// try to use the gap aligned at the end of the function.
|
|
||||||
//
|
|
||||||
// Do not disable it in a production environment.
|
|
||||||
//
|
|
||||||
#define SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED
|
|
@ -1,107 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_errno.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
static int sh_errno_global = SHADOWHOOK_ERRNO_UNINIT;
|
|
||||||
static pthread_key_t sh_errno_tls_key;
|
|
||||||
|
|
||||||
int sh_errno_init(void) {
|
|
||||||
if (__predict_false(0 != pthread_key_create(&sh_errno_tls_key, NULL))) {
|
|
||||||
sh_errno_global = SHADOWHOOK_ERRNO_INIT_ERRNO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sh_errno_global = SHADOWHOOK_ERRNO_OK;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_errno_reset(void) {
|
|
||||||
sh_errno_set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_errno_set(int error_number) {
|
|
||||||
if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO) return;
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"
|
|
||||||
pthread_setspecific(sh_errno_tls_key, (void *)error_number);
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_errno_get(void) {
|
|
||||||
if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO)
|
|
||||||
return sh_errno_global;
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wvoid-pointer-to-int-cast"
|
|
||||||
return (int)(pthread_getspecific(sh_errno_tls_key));
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *sh_errno_to_errmsg(int error_number) {
|
|
||||||
static const char *msg[] = {/* 0 */ "OK",
|
|
||||||
/* 1 */ "Pending task",
|
|
||||||
/* 2 */ "Not initialized",
|
|
||||||
/* 3 */ "Invalid argument",
|
|
||||||
/* 4 */ "Out of memory",
|
|
||||||
/* 5 */ "MProtect failed",
|
|
||||||
/* 6 */ "Write to arbitrary address crashed",
|
|
||||||
/* 7 */ "Init errno mod failed",
|
|
||||||
/* 8 */ "Init bytesig SIGSEGV mod failed",
|
|
||||||
/* 9 */ "Init bytesig SIGBUS mod failed",
|
|
||||||
/* 10 */ "Init enter mod failed",
|
|
||||||
/* 11 */ "Init safe mod failed",
|
|
||||||
/* 12 */ "Init linker mod failed",
|
|
||||||
/* 13 */ "Init hub mod failed",
|
|
||||||
/* 14 */ "Create hub failed",
|
|
||||||
/* 15 */ "Monitor dlopen failed",
|
|
||||||
/* 16 */ "Create monitor thread failed",
|
|
||||||
/* 17 */ "Open ELF crashed",
|
|
||||||
/* 18 */ "Find symbol in ELF failed",
|
|
||||||
/* 19 */ "Find symbol in ELF crashed",
|
|
||||||
/* 20 */ "Duplicate hook",
|
|
||||||
/* 21 */ "Dladdr crashed",
|
|
||||||
/* 22 */ "Find dlinfo failed",
|
|
||||||
/* 23 */ "Symbol size too small",
|
|
||||||
/* 24 */ "Alloc enter failed",
|
|
||||||
/* 25 */ "Instruction rewrite crashed",
|
|
||||||
/* 26 */ "Instruction rewrite failed",
|
|
||||||
/* 27 */ "Switch not found",
|
|
||||||
/* 28 */ "Verify original instruction crashed",
|
|
||||||
/* 29 */ "Verify original instruction failed",
|
|
||||||
/* 30 */ "Exit instruction mismatch",
|
|
||||||
/* 31 */ "Free exit crashed",
|
|
||||||
/* 32 */ "Unhook on an error status task",
|
|
||||||
/* 33 */ "Unhook on an unfinished task",
|
|
||||||
/* 34 */ "ELF with an unsupported architecture",
|
|
||||||
/* 35 */ "Linker with an unsupported architecture"};
|
|
||||||
|
|
||||||
if (error_number < 0 || error_number >= (int)(sizeof(msg) / sizeof(msg[0]))) return "Unknown error number";
|
|
||||||
|
|
||||||
return msg[error_number];
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "sh_log.h"
|
|
||||||
|
|
||||||
#define SH_ERRNO_SET_RET_ERRNUM(errnum) SH_ERRNO_SET_RET((errnum), (errnum))
|
|
||||||
#define SH_ERRNO_SET_RET_FAIL(errnum) SH_ERRNO_SET_RET((errnum), -1)
|
|
||||||
#define SH_ERRNO_SET_RET_NULL(errnum) SH_ERRNO_SET_RET((errnum), NULL)
|
|
||||||
#define SH_ERRNO_SET_RET(errnum, ret) \
|
|
||||||
do { \
|
|
||||||
sh_errno_set((errnum)); \
|
|
||||||
return (ret); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int sh_errno_init(void);
|
|
||||||
void sh_errno_reset(void);
|
|
||||||
void sh_errno_set(int error_number);
|
|
||||||
int sh_errno_get(void);
|
|
||||||
const char *sh_errno_to_errmsg(int error_number);
|
|
@ -1,53 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "sh_config.h"
|
|
||||||
|
|
||||||
android_LogPriority sh_log_priority =
|
|
||||||
#ifdef SH_CONFIG_DEBUG
|
|
||||||
ANDROID_LOG_INFO
|
|
||||||
#else
|
|
||||||
ANDROID_LOG_SILENT
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
bool sh_log_get_debuggable(void) {
|
|
||||||
return sh_log_priority <= ANDROID_LOG_INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_log_set_debuggable(bool debuggable) {
|
|
||||||
#ifdef SH_CONFIG_DEBUG
|
|
||||||
(void)debuggable;
|
|
||||||
sh_log_priority = ANDROID_LOG_INFO;
|
|
||||||
#else
|
|
||||||
if (__predict_false(debuggable))
|
|
||||||
sh_log_priority = ANDROID_LOG_INFO;
|
|
||||||
else
|
|
||||||
sh_log_priority = ANDROID_LOG_SILENT;
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
extern android_LogPriority sh_log_priority;
|
|
||||||
|
|
||||||
#define SH_LOG_TAG "shadowhook_tag"
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
|
||||||
|
|
||||||
// Notice:
|
|
||||||
// We don't use ANDROID_LOG_DEBUG, because some Android devices will filter out ANDROID_LOG_DEBUG.
|
|
||||||
#ifdef SH_CONFIG_DEBUG
|
|
||||||
#define SH_LOG_DEBUG(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (sh_log_priority <= ANDROID_LOG_INFO) \
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define SH_LOG_DEBUG(fmt, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SH_LOG_INFO(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (__predict_false(sh_log_priority <= ANDROID_LOG_INFO)) \
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
#define SH_LOG_WARN(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (__predict_false(sh_log_priority <= ANDROID_LOG_WARN)) \
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
#define SH_LOG_ERROR(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (__predict_false(sh_log_priority <= ANDROID_LOG_ERROR)) \
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
#define SH_LOG_ALWAYS_SHOW(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
bool sh_log_get_debuggable(void);
|
|
||||||
void sh_log_set_debuggable(bool debuggable);
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sh_config.h"
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_SIG_PROT
|
|
||||||
|
|
||||||
#include "bytesig.h"
|
|
||||||
#define SH_SIG_TRY BYTESIG_TRY
|
|
||||||
#define SH_SIG_CATCH BYTESIG_CATCH
|
|
||||||
#define SH_SIG_EXIT BYTESIG_EXIT
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define SH_SIG_TRY(...) \
|
|
||||||
do { \
|
|
||||||
if (0 == 0) {
|
|
||||||
#define SH_SIG_CATCH(...) \
|
|
||||||
} \
|
|
||||||
else {
|
|
||||||
#define SH_SIG_EXIT \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
while (0) \
|
|
||||||
;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,173 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_trampo.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
|
|
||||||
#define SH_TRAMPO_ALIGN 4
|
|
||||||
|
|
||||||
void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size,
|
|
||||||
time_t delay_sec) {
|
|
||||||
SLIST_INIT(&mem_mgr->pages);
|
|
||||||
pthread_mutex_init(&mem_mgr->pages_lock, NULL);
|
|
||||||
mem_mgr->page_name = page_name;
|
|
||||||
mem_mgr->trampo_size = SH_UTIL_ALIGN_END(trampo_size, SH_TRAMPO_ALIGN);
|
|
||||||
mem_mgr->delay_sec = delay_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t range_low,
|
|
||||||
uintptr_t range_high) {
|
|
||||||
uintptr_t mem = 0;
|
|
||||||
uintptr_t new_ptr;
|
|
||||||
uintptr_t new_ptr_prctl = (uintptr_t)MAP_FAILED;
|
|
||||||
size_t trampo_page_size = sh_util_get_page_size();
|
|
||||||
size_t count = trampo_page_size / mem_mgr->trampo_size;
|
|
||||||
|
|
||||||
if (range_low > hint) range_low = hint;
|
|
||||||
if (range_high > UINTPTR_MAX - hint) range_high = UINTPTR_MAX - hint;
|
|
||||||
|
|
||||||
struct timeval now;
|
|
||||||
if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&mem_mgr->pages_lock);
|
|
||||||
|
|
||||||
// try to find an unused mem
|
|
||||||
sh_trampo_page_t *page;
|
|
||||||
SLIST_FOREACH(page, &mem_mgr->pages, link) {
|
|
||||||
// check hit range
|
|
||||||
uintptr_t page_trampo_start = page->ptr;
|
|
||||||
uintptr_t page_trampo_end = page->ptr + trampo_page_size - mem_mgr->trampo_size;
|
|
||||||
if (hint > 0 && ((page_trampo_end < hint - range_low) || (hint + range_high < page_trampo_start)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < count; i++) {
|
|
||||||
size_t flags_idx = i / 32;
|
|
||||||
uint32_t mask = (uint32_t)1 << (i % 32);
|
|
||||||
if (0 == (page->flags[flags_idx] & mask)) // check flag
|
|
||||||
{
|
|
||||||
// check timestamp
|
|
||||||
if (mem_mgr->delay_sec > 0 &&
|
|
||||||
(now.tv_sec <= page->timestamps[i] || now.tv_sec - page->timestamps[i] <= mem_mgr->delay_sec))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// check hit range
|
|
||||||
uintptr_t cur = page->ptr + (mem_mgr->trampo_size * i);
|
|
||||||
if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue;
|
|
||||||
|
|
||||||
// OK
|
|
||||||
page->flags[flags_idx] |= mask;
|
|
||||||
mem = cur;
|
|
||||||
memset((void *)mem, 0, mem_mgr->trampo_size);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// alloc a new mem page
|
|
||||||
new_ptr = (uintptr_t)(mmap(hint > 0 ? (void *)(hint - range_low) : NULL, trampo_page_size,
|
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
|
||||||
if ((uintptr_t)MAP_FAILED == new_ptr) goto err;
|
|
||||||
new_ptr_prctl = new_ptr;
|
|
||||||
|
|
||||||
// check hit range
|
|
||||||
if (hint > 0 && ((hint - range_low >= new_ptr + trampo_page_size - mem_mgr->trampo_size) ||
|
|
||||||
(hint + range_high < new_ptr)))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
// create a new trampo-page info
|
|
||||||
if (NULL == (page = calloc(1, sizeof(sh_trampo_page_t)))) goto err;
|
|
||||||
memset((void *)new_ptr, 0, trampo_page_size);
|
|
||||||
page->ptr = new_ptr;
|
|
||||||
new_ptr = (uintptr_t)MAP_FAILED;
|
|
||||||
if (NULL == (page->flags = calloc(1, SH_UTIL_ALIGN_END(count, 32) / 8))) goto err;
|
|
||||||
page->timestamps = NULL;
|
|
||||||
if (mem_mgr->delay_sec > 0) {
|
|
||||||
if (NULL == (page->timestamps = calloc(1, count * sizeof(time_t)))) goto err;
|
|
||||||
}
|
|
||||||
SLIST_INSERT_HEAD(&mem_mgr->pages, page, link);
|
|
||||||
|
|
||||||
// alloc mem from the new mem page
|
|
||||||
for (uintptr_t i = 0; i < count; i++) {
|
|
||||||
size_t flags_idx = i / 32;
|
|
||||||
uint32_t mask = (uint32_t)1 << (i % 32);
|
|
||||||
|
|
||||||
// check hit range
|
|
||||||
uintptr_t find = page->ptr + (mem_mgr->trampo_size * i);
|
|
||||||
if (hint > 0 && ((find < hint - range_low) || (hint + range_high < find))) continue;
|
|
||||||
|
|
||||||
// OK
|
|
||||||
page->flags[flags_idx] |= mask;
|
|
||||||
mem = find;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (0 == mem) abort();
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_mutex_unlock(&mem_mgr->pages_lock);
|
|
||||||
if ((uintptr_t)MAP_FAILED != new_ptr_prctl)
|
|
||||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_ptr_prctl, trampo_page_size, mem_mgr->page_name);
|
|
||||||
return mem;
|
|
||||||
|
|
||||||
err:
|
|
||||||
pthread_mutex_unlock(&mem_mgr->pages_lock);
|
|
||||||
if (NULL != page) {
|
|
||||||
if (0 != page->ptr) munmap((void *)page->ptr, trampo_page_size);
|
|
||||||
if (NULL != page->flags) free(page->flags);
|
|
||||||
if (NULL != page->timestamps) free(page->timestamps);
|
|
||||||
free(page);
|
|
||||||
}
|
|
||||||
if ((uintptr_t)MAP_FAILED != new_ptr) munmap((void *)new_ptr, trampo_page_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem) {
|
|
||||||
struct timeval now;
|
|
||||||
if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&mem_mgr->pages_lock);
|
|
||||||
|
|
||||||
size_t trampo_page_size = sh_util_get_page_size();
|
|
||||||
sh_trampo_page_t *page;
|
|
||||||
SLIST_FOREACH(page, &mem_mgr->pages, link) {
|
|
||||||
if (page->ptr <= mem && mem < page->ptr + trampo_page_size) {
|
|
||||||
uintptr_t i = (mem - page->ptr) / mem_mgr->trampo_size;
|
|
||||||
size_t flags_idx = i / 32;
|
|
||||||
uint32_t mask = (uint32_t)1 << (i % 32);
|
|
||||||
if (mem_mgr->delay_sec > 0) page->timestamps[i] = now.tv_sec;
|
|
||||||
page->flags[flags_idx] &= ~mask;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&mem_mgr->pages_lock);
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
|
|
||||||
typedef struct sh_trampo_page {
|
|
||||||
uintptr_t ptr;
|
|
||||||
uint32_t *flags;
|
|
||||||
time_t *timestamps;
|
|
||||||
SLIST_ENTRY(sh_trampo_page, ) link;
|
|
||||||
} sh_trampo_page_t;
|
|
||||||
typedef SLIST_HEAD(sh_trampo_page_list, sh_trampo_page, ) sh_trampo_page_list_t;
|
|
||||||
|
|
||||||
typedef struct sh_trampo_mgr {
|
|
||||||
sh_trampo_page_list_t pages;
|
|
||||||
pthread_mutex_t pages_lock;
|
|
||||||
const char *page_name;
|
|
||||||
size_t trampo_size;
|
|
||||||
time_t delay_sec;
|
|
||||||
} sh_trampo_mgr_t;
|
|
||||||
|
|
||||||
void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size,
|
|
||||||
time_t delay_sec);
|
|
||||||
|
|
||||||
uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t low_offset,
|
|
||||||
uintptr_t high_offset);
|
|
||||||
void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem);
|
|
@ -1,556 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_util.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
static size_t sh_util_page_size = 0;
|
|
||||||
|
|
||||||
__attribute__((constructor)) static void sh_util_ctor(void) {
|
|
||||||
sh_util_page_size = (size_t)getpagesize();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_util_get_page_size(void) {
|
|
||||||
return sh_util_page_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_util_page_start(uintptr_t x) {
|
|
||||||
return x & ~(sh_util_page_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_util_page_end(uintptr_t x) {
|
|
||||||
return sh_util_page_start(x + sh_util_page_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_util_mprotect(uintptr_t addr, size_t len, int prot) {
|
|
||||||
uintptr_t start = sh_util_page_start(addr);
|
|
||||||
uintptr_t end = sh_util_page_end(addr + len - 1);
|
|
||||||
|
|
||||||
return mprotect((void *)start, end - start, prot);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_util_clear_cache(uintptr_t addr, size_t len) {
|
|
||||||
__builtin___clear_cache((char *)addr, (char *)(addr + len));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sh_util_is_thumb32(uintptr_t target_addr) {
|
|
||||||
uint16_t opcode = *((uint16_t *)target_addr);
|
|
||||||
int tmp = opcode >> 11u;
|
|
||||||
return (tmp == 0x1d) || (tmp == 0x1e) || (tmp == 0x1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t sh_util_ror(uint32_t val, uint32_t n, uint32_t shift) {
|
|
||||||
uint32_t m = shift % n;
|
|
||||||
return (val >> m) | (val << (n - m));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t sh_util_arm_expand_imm(uint32_t opcode) {
|
|
||||||
uint32_t imm = SH_UTIL_GET_BITS_32(opcode, 7, 0);
|
|
||||||
uint32_t amt = 2 * SH_UTIL_GET_BITS_32(opcode, 11, 8);
|
|
||||||
|
|
||||||
return amt == 0 ? imm : sh_util_ror(imm, 32, amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len) {
|
|
||||||
if (0 != sh_util_mprotect(target_addr, inst_len, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
return SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
if ((4 == inst_len) && (0 == target_addr % 4))
|
|
||||||
__atomic_store_n((uint32_t *)target_addr, *((uint32_t *)inst), __ATOMIC_SEQ_CST);
|
|
||||||
else if ((8 == inst_len) && (0 == target_addr % 8))
|
|
||||||
__atomic_store_n((uint64_t *)target_addr, *((uint64_t *)inst), __ATOMIC_SEQ_CST);
|
|
||||||
#ifdef __LP64__
|
|
||||||
else if ((16 == inst_len) && (0 == target_addr % 16))
|
|
||||||
__atomic_store_n((__int128 *)target_addr, *((__int128 *)inst), __ATOMIC_SEQ_CST);
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
memcpy((void *)target_addr, inst, inst_len);
|
|
||||||
|
|
||||||
sh_util_clear_cache(target_addr, inst_len);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
return SHADOWHOOK_ERRNO_WRITE_CRASH;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
|
|
||||||
return 0; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_util_starts_with(const char *str, const char *start) {
|
|
||||||
while (*str && *str == *start) {
|
|
||||||
str++;
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '\0' == *start;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_util_get_api_level_from_build_prop(void) {
|
|
||||||
char buf[128];
|
|
||||||
int api_level = -1;
|
|
||||||
|
|
||||||
FILE *fp = fopen("/system/build.prop", "r");
|
|
||||||
if (__predict_false(NULL == fp)) goto end;
|
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
|
||||||
if (__predict_false(sh_util_starts_with(buf, "ro.build.version.sdk="))) {
|
|
||||||
api_level = atoi(buf + 21);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
end:
|
|
||||||
return (api_level > 0) ? api_level : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_util_get_api_level(void) {
|
|
||||||
static int xdl_util_api_level = -1;
|
|
||||||
|
|
||||||
if (__predict_false(xdl_util_api_level < 0)) {
|
|
||||||
int api_level = android_get_device_api_level();
|
|
||||||
if (__predict_false(api_level < 0))
|
|
||||||
api_level = sh_util_get_api_level_from_build_prop(); // compatible with unusual models
|
|
||||||
if (__predict_false(api_level < __ANDROID_API_J__)) api_level = __ANDROID_API_J__;
|
|
||||||
|
|
||||||
__atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST);
|
|
||||||
}
|
|
||||||
|
|
||||||
return xdl_util_api_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_util_write(int fd, const char *buf, size_t buf_len) {
|
|
||||||
if (fd < 0) return -1;
|
|
||||||
|
|
||||||
const char *ptr = buf;
|
|
||||||
size_t nleft = buf_len;
|
|
||||||
|
|
||||||
while (nleft > 0) {
|
|
||||||
errno = 0;
|
|
||||||
ssize_t nwritten = write(fd, ptr, nleft);
|
|
||||||
if (nwritten <= 0) {
|
|
||||||
if (nwritten < 0 && errno == EINTR)
|
|
||||||
nwritten = 0; // call write() again
|
|
||||||
else
|
|
||||||
return -1; // error
|
|
||||||
}
|
|
||||||
nleft -= (size_t)nwritten;
|
|
||||||
ptr += nwritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nonzero if YEAR is a leap year (every 4 years,
|
|
||||||
except every 100th isn't, and every 400th is). */
|
|
||||||
#define SH_UTIL_ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
|
|
||||||
|
|
||||||
#define SH_UTIL_SECS_PER_HOUR (60 * 60)
|
|
||||||
#define SH_UTIL_SECS_PER_DAY (SH_UTIL_SECS_PER_HOUR * 24)
|
|
||||||
#define SH_UTIL_DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
|
|
||||||
#define SH_UTIL_LEAPS_THRU_END_OF(y) (SH_UTIL_DIV(y, 4) - SH_UTIL_DIV(y, 100) + SH_UTIL_DIV(y, 400))
|
|
||||||
|
|
||||||
static const unsigned short int sh_util_mon_yday[2][13] = {
|
|
||||||
/* Normal years. */
|
|
||||||
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
|
|
||||||
/* Leap years. */
|
|
||||||
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
|
|
||||||
|
|
||||||
/* Compute the `struct tm' representation of *T,
|
|
||||||
offset GMTOFF seconds east of UTC,
|
|
||||||
and store year, yday, mon, mday, wday, hour, min, sec into *RESULT.
|
|
||||||
Return RESULT if successful. */
|
|
||||||
struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result) {
|
|
||||||
time_t days, rem, y;
|
|
||||||
const unsigned short int *ip;
|
|
||||||
|
|
||||||
if (NULL == result) return NULL;
|
|
||||||
|
|
||||||
result->tm_gmtoff = gmtoff;
|
|
||||||
|
|
||||||
days = ((*timep) / SH_UTIL_SECS_PER_DAY);
|
|
||||||
rem = ((*timep) % SH_UTIL_SECS_PER_DAY);
|
|
||||||
rem += gmtoff;
|
|
||||||
while (rem < 0) {
|
|
||||||
rem += SH_UTIL_SECS_PER_DAY;
|
|
||||||
--days;
|
|
||||||
}
|
|
||||||
while (rem >= SH_UTIL_SECS_PER_DAY) {
|
|
||||||
rem -= SH_UTIL_SECS_PER_DAY;
|
|
||||||
++days;
|
|
||||||
}
|
|
||||||
result->tm_hour = (int)(rem / SH_UTIL_SECS_PER_HOUR);
|
|
||||||
rem %= SH_UTIL_SECS_PER_HOUR;
|
|
||||||
result->tm_min = (int)(rem / 60L);
|
|
||||||
result->tm_sec = (int)(rem % 60L);
|
|
||||||
/* January 1, 1970 was a Thursday. */
|
|
||||||
result->tm_wday = (int)(4 + days) % 7;
|
|
||||||
if (result->tm_wday < 0) result->tm_wday += 7;
|
|
||||||
y = 1970;
|
|
||||||
|
|
||||||
while (days < 0 || days >= (SH_UTIL_ISLEAP(y) ? 366 : 365)) {
|
|
||||||
/* Guess a corrected year, assuming 365 days per year. */
|
|
||||||
time_t yg = y + days / 365 - (days % 365 < 0);
|
|
||||||
|
|
||||||
/* Adjust DAYS and Y to match the guessed year. */
|
|
||||||
days -= ((yg - y) * 365 + SH_UTIL_LEAPS_THRU_END_OF(yg - 1) - SH_UTIL_LEAPS_THRU_END_OF(y - 1));
|
|
||||||
|
|
||||||
y = yg;
|
|
||||||
}
|
|
||||||
result->tm_year = (int)(y - 1900);
|
|
||||||
if (result->tm_year != y - 1900) {
|
|
||||||
/* The year cannot be represented due to overflow. */
|
|
||||||
errno = EOVERFLOW;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
result->tm_yday = (int)days;
|
|
||||||
ip = sh_util_mon_yday[SH_UTIL_ISLEAP(y)];
|
|
||||||
for (y = 11; days < (long int)ip[y]; --y) continue;
|
|
||||||
days -= ip[y];
|
|
||||||
result->tm_mon = (int)y;
|
|
||||||
result->tm_mday = (int)(days + 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned sh_util_parse_decimal(const char *format, int *ppos) {
|
|
||||||
const char *p = format + *ppos;
|
|
||||||
unsigned result = 0;
|
|
||||||
for (;;) {
|
|
||||||
int ch = *p;
|
|
||||||
unsigned d = (unsigned)(ch - '0');
|
|
||||||
if (d >= 10U) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
result = result * 10 + d;
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
*ppos = (int)(p - format);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, int base, int caps) {
|
|
||||||
char *p = buf;
|
|
||||||
char *end = buf + buf_size - 1;
|
|
||||||
|
|
||||||
// Generate digit string in reverse order.
|
|
||||||
while (value) {
|
|
||||||
unsigned d = (unsigned)(value % (uint64_t)base);
|
|
||||||
value /= (uint64_t)base;
|
|
||||||
if (p != end) {
|
|
||||||
char ch;
|
|
||||||
if (d < 10) {
|
|
||||||
ch = '0' + (char)d;
|
|
||||||
} else {
|
|
||||||
ch = (caps ? 'A' : 'a') + (char)(d - 10);
|
|
||||||
}
|
|
||||||
*p++ = ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case for 0.
|
|
||||||
if (p == buf) {
|
|
||||||
if (p != end) {
|
|
||||||
*p++ = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
// Reverse digit string in-place.
|
|
||||||
size_t length = (size_t)(p - buf);
|
|
||||||
for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
|
|
||||||
char ch = buf[i];
|
|
||||||
buf[i] = buf[j];
|
|
||||||
buf[j] = ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_util_format_integer(char *buf, size_t buf_size, uint64_t value, char conversion) {
|
|
||||||
// Decode the conversion specifier.
|
|
||||||
int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
|
|
||||||
int base = 10;
|
|
||||||
if (conversion == 'x' || conversion == 'X') {
|
|
||||||
base = 16;
|
|
||||||
} else if (conversion == 'o') {
|
|
||||||
base = 8;
|
|
||||||
}
|
|
||||||
int caps = (conversion == 'X');
|
|
||||||
if (is_signed && (int64_t)(value) < 0) {
|
|
||||||
buf[0] = '-';
|
|
||||||
buf += 1;
|
|
||||||
buf_size -= 1;
|
|
||||||
value = (uint64_t)(-(int64_t)(value));
|
|
||||||
}
|
|
||||||
sh_util_format_unsigned(buf, buf_size, value, base, caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// format stream
|
|
||||||
typedef struct {
|
|
||||||
size_t total;
|
|
||||||
char *pos;
|
|
||||||
size_t avail;
|
|
||||||
} sh_util_stream_t;
|
|
||||||
|
|
||||||
static void sh_util_stream_init(sh_util_stream_t *self, char *buffer, size_t buffer_size) {
|
|
||||||
self->total = 0;
|
|
||||||
self->pos = buffer;
|
|
||||||
self->avail = buffer_size;
|
|
||||||
|
|
||||||
if (self->avail > 0) self->pos[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_util_stream_total(sh_util_stream_t *self) {
|
|
||||||
return self->total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_util_stream_send(sh_util_stream_t *self, const char *data, int len) {
|
|
||||||
if (len < 0) {
|
|
||||||
len = (int)strlen(data);
|
|
||||||
}
|
|
||||||
self->total += (size_t)len;
|
|
||||||
|
|
||||||
if (self->avail <= 1) {
|
|
||||||
// no space to put anything else
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((size_t)len >= self->avail) {
|
|
||||||
len = (int)(self->avail - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(self->pos, data, (size_t)len);
|
|
||||||
self->pos += len;
|
|
||||||
self->pos[0] = '\0';
|
|
||||||
self->avail -= (size_t)len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_util_stream_send_repeat(sh_util_stream_t *self, char ch, int count) {
|
|
||||||
char pad[8];
|
|
||||||
memset(pad, ch, sizeof(pad));
|
|
||||||
|
|
||||||
const int pad_size = (int)(sizeof(pad));
|
|
||||||
while (count > 0) {
|
|
||||||
int avail = count;
|
|
||||||
if (avail > pad_size) {
|
|
||||||
avail = pad_size;
|
|
||||||
}
|
|
||||||
sh_util_stream_send(self, pad, avail);
|
|
||||||
count -= avail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, va_list args) {
|
|
||||||
int nn = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int mm;
|
|
||||||
int padZero = 0;
|
|
||||||
int padLeft = 0;
|
|
||||||
char sign = '\0';
|
|
||||||
int width = -1;
|
|
||||||
int prec = -1;
|
|
||||||
size_t bytelen = sizeof(int);
|
|
||||||
int slen;
|
|
||||||
char buffer[32]; // temporary buffer used to format numbers
|
|
||||||
char c;
|
|
||||||
|
|
||||||
// first, find all characters that are not 0 or '%', then send them to the output directly
|
|
||||||
mm = nn;
|
|
||||||
do {
|
|
||||||
c = format[mm];
|
|
||||||
if (c == '\0' || c == '%') break;
|
|
||||||
mm++;
|
|
||||||
} while (1);
|
|
||||||
if (mm > nn) {
|
|
||||||
sh_util_stream_send(self, format + nn, mm - nn);
|
|
||||||
nn = mm;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is this it ? then exit
|
|
||||||
if (c == '\0') break;
|
|
||||||
|
|
||||||
// nope, we are at a '%' modifier
|
|
||||||
nn++; // skip it
|
|
||||||
|
|
||||||
// parse flags
|
|
||||||
for (;;) {
|
|
||||||
c = format[nn++];
|
|
||||||
if (c == '\0') {
|
|
||||||
// single trailing '%' ?
|
|
||||||
c = '%';
|
|
||||||
sh_util_stream_send(self, &c, 1);
|
|
||||||
return;
|
|
||||||
} else if (c == '0') {
|
|
||||||
padZero = 1;
|
|
||||||
continue;
|
|
||||||
} else if (c == '-') {
|
|
||||||
padLeft = 1;
|
|
||||||
continue;
|
|
||||||
} else if (c == ' ' || c == '+') {
|
|
||||||
sign = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse field width
|
|
||||||
if ((c >= '0' && c <= '9')) {
|
|
||||||
nn--;
|
|
||||||
width = (int)(sh_util_parse_decimal(format, &nn));
|
|
||||||
c = format[nn++];
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse precision
|
|
||||||
if (c == '.') {
|
|
||||||
prec = (int)(sh_util_parse_decimal(format, &nn));
|
|
||||||
c = format[nn++];
|
|
||||||
}
|
|
||||||
|
|
||||||
// length modifier
|
|
||||||
switch (c) {
|
|
||||||
case 'h':
|
|
||||||
bytelen = sizeof(short);
|
|
||||||
if (format[nn] == 'h') {
|
|
||||||
bytelen = sizeof(char);
|
|
||||||
nn += 1;
|
|
||||||
}
|
|
||||||
c = format[nn++];
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
bytelen = sizeof(long);
|
|
||||||
if (format[nn] == 'l') {
|
|
||||||
bytelen = sizeof(long long);
|
|
||||||
nn += 1;
|
|
||||||
}
|
|
||||||
c = format[nn++];
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
bytelen = sizeof(size_t);
|
|
||||||
c = format[nn++];
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
bytelen = sizeof(ptrdiff_t);
|
|
||||||
c = format[nn++];
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
|
|
||||||
// conversion specifier
|
|
||||||
const char *str = buffer;
|
|
||||||
if (c == 's') {
|
|
||||||
// string
|
|
||||||
str = va_arg(args, const char *);
|
|
||||||
if (str == NULL) {
|
|
||||||
str = "(null)";
|
|
||||||
}
|
|
||||||
} else if (c == 'c') {
|
|
||||||
// character
|
|
||||||
// NOTE: char is promoted to int when passed through the stack
|
|
||||||
buffer[0] = (char)(va_arg(args, int));
|
|
||||||
buffer[1] = '\0';
|
|
||||||
} else if (c == 'p') {
|
|
||||||
uint64_t value = (uintptr_t)(va_arg(args, void *));
|
|
||||||
buffer[0] = '0';
|
|
||||||
buffer[1] = 'x';
|
|
||||||
sh_util_format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
|
|
||||||
} else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
|
|
||||||
// integers - first read value from stack
|
|
||||||
uint64_t value;
|
|
||||||
int is_signed = (c == 'd' || c == 'i' || c == 'o');
|
|
||||||
// NOTE: int8_t and int16_t are promoted to int when passed through the stack
|
|
||||||
switch (bytelen) {
|
|
||||||
case 1:
|
|
||||||
value = (uint8_t)(va_arg(args, int));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
value = (uint16_t)(va_arg(args, int));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
value = va_arg(args, uint32_t);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
value = va_arg(args, uint64_t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return; // should not happen
|
|
||||||
}
|
|
||||||
// sign extension, if needed
|
|
||||||
if (is_signed) {
|
|
||||||
int shift = (int)(64 - 8 * bytelen);
|
|
||||||
value = (uint64_t)(((int64_t)(value << shift)) >> shift);
|
|
||||||
}
|
|
||||||
// format the number properly into our buffer
|
|
||||||
sh_util_format_integer(buffer, sizeof(buffer), value, c);
|
|
||||||
} else if (c == '%') {
|
|
||||||
buffer[0] = '%';
|
|
||||||
buffer[1] = '\0';
|
|
||||||
} else {
|
|
||||||
//__assert(__FILE__, __LINE__, "conversion specifier unsupported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are here, 'str' points to the content that must be outputted.
|
|
||||||
// handle padding and alignment now
|
|
||||||
slen = (int)strlen(str);
|
|
||||||
if (sign != '\0' || prec != -1) {
|
|
||||||
//__assert(__FILE__, __LINE__, "sign/precision unsupported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (slen < width && !padLeft) {
|
|
||||||
char padChar = padZero ? '0' : ' ';
|
|
||||||
sh_util_stream_send_repeat(self, padChar, width - slen);
|
|
||||||
}
|
|
||||||
sh_util_stream_send(self, str, slen);
|
|
||||||
if (slen < width && padLeft) {
|
|
||||||
char padChar = padZero ? '0' : ' ';
|
|
||||||
sh_util_stream_send_repeat(self, padChar, width - slen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args) {
|
|
||||||
sh_util_stream_t stream;
|
|
||||||
sh_util_stream_init(&stream, buffer, buffer_size);
|
|
||||||
sh_util_stream_vformat(&stream, format, args);
|
|
||||||
return sh_util_stream_total(&stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
size_t buffer_len = sh_util_vsnprintf(buffer, buffer_size, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return buffer_len;
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <android/api-level.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
|
||||||
#ifndef __ANDROID_API_U__
|
|
||||||
#define __ANDROID_API_U__ 34
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
#define SH_UTIL_ALIGN_START(x, align) ((uintptr_t)(x) & ~((uintptr_t)(align)-1))
|
|
||||||
#define SH_UTIL_ALIGN_END(x, align) (((uintptr_t)(x) + (uintptr_t)(align)-1) & ~((uintptr_t)(align)-1))
|
|
||||||
|
|
||||||
#define SH_UTIL_IS_THUMB(addr) ((addr)&1u)
|
|
||||||
#define SH_UTIL_CLEAR_BIT0(addr) ((addr)&0xFFFFFFFE)
|
|
||||||
#define SH_UTIL_SET_BIT0(addr) ((addr) | 1u)
|
|
||||||
|
|
||||||
#define SH_UTIL_ALIGN_4(pc) ((pc)&0xFFFFFFFC)
|
|
||||||
#define SH_UTIL_SIGN_EXTEND_32(n, len) \
|
|
||||||
((SH_UTIL_GET_BIT_32(n, len - 1) > 0) ? ((n) | (0xFFFFFFFF << (len))) : n)
|
|
||||||
#define SH_UTIL_SIGN_EXTEND_64(n, len) \
|
|
||||||
((SH_UTIL_GET_BIT_64(n, len - 1) > 0) ? ((n) | (0xFFFFFFFFFFFFFFFF << (len))) : n)
|
|
||||||
|
|
||||||
#define SH_UTIL_GET_BIT_16(n, idx) ((uint16_t)((n) << (15u - (idx))) >> 15u)
|
|
||||||
#define SH_UTIL_GET_BITS_16(n, high, low) ((uint16_t)((n) << (15u - (high))) >> (15u - (high) + (low)))
|
|
||||||
#define SH_UTIL_GET_BIT_32(n, idx) ((uint32_t)((n) << (31u - (idx))) >> 31u)
|
|
||||||
#define SH_UTIL_GET_BITS_32(n, high, low) ((uint32_t)((n) << (31u - (high))) >> (31u - (high) + (low)))
|
|
||||||
#define SH_UTIL_GET_BIT_64(n, idx) ((uint64_t)((n) << (63u - (idx))) >> 63u)
|
|
||||||
#define SH_UTIL_GET_BITS_64(n, high, low) ((uint64_t)((n) << (63u - (high))) >> (63u - (high) + (low)))
|
|
||||||
|
|
||||||
#define SH_UTIL_TEMP_FAILURE_RETRY(exp) \
|
|
||||||
({ \
|
|
||||||
__typeof__(exp) _rc; \
|
|
||||||
do { \
|
|
||||||
errno = 0; \
|
|
||||||
_rc = (exp); \
|
|
||||||
} while (_rc == -1 && errno == EINTR); \
|
|
||||||
_rc; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define SH_UTIL_MAX(a, b) \
|
|
||||||
({ \
|
|
||||||
__typeof__(a) _a = (a); \
|
|
||||||
__typeof__(b) _b = (b); \
|
|
||||||
_a > _b ? _a : _b; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define SH_UTIL_MIN(a, b) \
|
|
||||||
({ \
|
|
||||||
__typeof__(a) _a = (a); \
|
|
||||||
__typeof__(b) _b = (b); \
|
|
||||||
_a < _b ? _a : _b; \
|
|
||||||
})
|
|
||||||
|
|
||||||
size_t sh_util_get_page_size(void);
|
|
||||||
uintptr_t sh_util_page_start(uintptr_t x);
|
|
||||||
uintptr_t sh_util_page_end(uintptr_t x);
|
|
||||||
|
|
||||||
int sh_util_mprotect(uintptr_t addr, size_t len, int prot);
|
|
||||||
void sh_util_clear_cache(uintptr_t addr, size_t len);
|
|
||||||
|
|
||||||
bool sh_util_is_thumb32(uintptr_t target_addr);
|
|
||||||
|
|
||||||
uint32_t sh_util_arm_expand_imm(uint32_t opcode);
|
|
||||||
|
|
||||||
int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len);
|
|
||||||
|
|
||||||
int sh_util_get_api_level(void);
|
|
||||||
|
|
||||||
int sh_util_write(int fd, const char *buf, size_t buf_len);
|
|
||||||
|
|
||||||
struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result);
|
|
||||||
|
|
||||||
size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args);
|
|
||||||
size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...);
|
|
@ -1,191 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// shadowhook is now credited to all its contributors.
|
|
||||||
//
|
|
||||||
// Before shadowhook was open sourced in February 2022, it was developed by
|
|
||||||
// the following developers from ByteDance's App Health Team:
|
|
||||||
//
|
|
||||||
// Kelun Cai (caikelun@bytedance.com)
|
|
||||||
// Pengying Xu (xupengying@bytedance.com)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BYTEDANCE_SHADOWHOOK_H
|
|
||||||
#define BYTEDANCE_SHADOWHOOK_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define SHADOWHOOK_VERSION "1.0.10"
|
|
||||||
|
|
||||||
#define SHADOWHOOK_ERRNO_OK 0
|
|
||||||
#define SHADOWHOOK_ERRNO_PENDING 1
|
|
||||||
#define SHADOWHOOK_ERRNO_UNINIT 2
|
|
||||||
#define SHADOWHOOK_ERRNO_INVALID_ARG 3
|
|
||||||
#define SHADOWHOOK_ERRNO_OOM 4
|
|
||||||
#define SHADOWHOOK_ERRNO_MPROT 5
|
|
||||||
#define SHADOWHOOK_ERRNO_WRITE_CRASH 6
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_ERRNO 7
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_SIGSEGV 8
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_SIGBUS 9
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_ENTER 10
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_SAFE 11
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_LINKER 12
|
|
||||||
#define SHADOWHOOK_ERRNO_INIT_HUB 13
|
|
||||||
#define SHADOWHOOK_ERRNO_HUB_CREAT 14
|
|
||||||
#define SHADOWHOOK_ERRNO_MONITOR_DLOPEN 15
|
|
||||||
#define SHADOWHOOK_ERRNO_MONITOR_THREAD 16
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH 17
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DLSYM 18
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH 19
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DUP 20
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH 21
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_DLINFO 22
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_SYMSZ 23
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_ENTER 24
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH 25
|
|
||||||
#define SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED 26
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND 27
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH 28
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH 29
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH 30
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH 31
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR 32
|
|
||||||
#define SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED 33
|
|
||||||
#define SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH 34
|
|
||||||
#define SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH 35
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *shadowhook_get_version(void);
|
|
||||||
|
|
||||||
// init
|
|
||||||
typedef enum {
|
|
||||||
SHADOWHOOK_MODE_SHARED = 0, // a function can be hooked multiple times
|
|
||||||
SHADOWHOOK_MODE_UNIQUE = 1 // a function can only be hooked once, and hooking again will report an error
|
|
||||||
} shadowhook_mode_t;
|
|
||||||
int shadowhook_init(shadowhook_mode_t mode, bool debuggable);
|
|
||||||
int shadowhook_get_init_errno(void);
|
|
||||||
|
|
||||||
// get and set attributes
|
|
||||||
#define SHADOWHOOK_IS_SHARED_MODE (SHADOWHOOK_MODE_SHARED == shadowhook_get_mode())
|
|
||||||
#define SHADOWHOOK_IS_UNIQUE_MODE (SHADOWHOOK_MODE_UNIQUE == shadowhook_get_mode())
|
|
||||||
shadowhook_mode_t shadowhook_get_mode(void);
|
|
||||||
bool shadowhook_get_debuggable(void);
|
|
||||||
void shadowhook_set_debuggable(bool debuggable);
|
|
||||||
bool shadowhook_get_recordable(void);
|
|
||||||
void shadowhook_set_recordable(bool recordable);
|
|
||||||
|
|
||||||
// get error-number and error message
|
|
||||||
int shadowhook_get_errno(void);
|
|
||||||
const char *shadowhook_to_errmsg(int error_number);
|
|
||||||
|
|
||||||
// hook and unhook
|
|
||||||
typedef void (*shadowhook_hooked_t)(int error_number, const char *lib_name, const char *sym_name,
|
|
||||||
void *sym_addr, void *new_addr, void *orig_addr, void *arg);
|
|
||||||
void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr);
|
|
||||||
void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr);
|
|
||||||
void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr);
|
|
||||||
void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr,
|
|
||||||
void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg);
|
|
||||||
int shadowhook_unhook(void *stub);
|
|
||||||
|
|
||||||
// get operation records
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_ALL 0x3FF // 0b1111111111
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_TIMESTAMP (1 << 0)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_OP (1 << 2)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_LIB_NAME (1 << 3)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_SYM_NAME (1 << 4)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_SYM_ADDR (1 << 5)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_NEW_ADDR (1 << 6)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_BACKUP_LEN (1 << 7)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_ERRNO (1 << 8)
|
|
||||||
#define SHADOWHOOK_RECORD_ITEM_STUB (1 << 9)
|
|
||||||
char *shadowhook_get_records(uint32_t item_flags);
|
|
||||||
void shadowhook_dump_records(int fd, uint32_t item_flags);
|
|
||||||
|
|
||||||
// helper functions for "get symbol-address from library-name and symbol-name"
|
|
||||||
void *shadowhook_dlopen(const char *lib_name);
|
|
||||||
void shadowhook_dlclose(void *handle);
|
|
||||||
void *shadowhook_dlsym(void *handle, const char *sym_name);
|
|
||||||
void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name);
|
|
||||||
void *shadowhook_dlsym_symtab(void *handle, const char *sym_name);
|
|
||||||
|
|
||||||
// for internal use
|
|
||||||
void *shadowhook_get_prev_func(void *func);
|
|
||||||
void shadowhook_pop_stack(void *return_address);
|
|
||||||
void shadowhook_allow_reentrant(void *return_address);
|
|
||||||
void shadowhook_disallow_reentrant(void *return_address);
|
|
||||||
void *shadowhook_get_return_address(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// call previous function in proxy-function
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define SHADOWHOOK_CALL_PREV(func, ...) \
|
|
||||||
((decltype(&(func)))shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SHADOWHOOK_CALL_PREV(func, func_sig, ...) \
|
|
||||||
((func_sig)shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
// pop stack in proxy-function (for C/C++)
|
|
||||||
#define SHADOWHOOK_POP_STACK() \
|
|
||||||
do { \
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(__builtin_return_address(0)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// pop stack in proxy-function (for C++ only)
|
|
||||||
#ifdef __cplusplus
|
|
||||||
class ShadowhookStackScope {
|
|
||||||
public:
|
|
||||||
ShadowhookStackScope(void *return_address) : return_address_(return_address) {}
|
|
||||||
~ShadowhookStackScope() {
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(return_address_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *return_address_;
|
|
||||||
};
|
|
||||||
#define SHADOWHOOK_STACK_SCOPE() ShadowhookStackScope shadowhook_stack_scope_obj(__builtin_return_address(0))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allow reentrant of the current proxy-function
|
|
||||||
#define SHADOWHOOK_ALLOW_REENTRANT() \
|
|
||||||
do { \
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_allow_reentrant(__builtin_return_address(0)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// disallow reentrant of the current proxy-function
|
|
||||||
#define SHADOWHOOK_DISALLOW_REENTRANT() \
|
|
||||||
do { \
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_disallow_reentrant(__builtin_return_address(0)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// get return address in proxy-function
|
|
||||||
#define SHADOWHOOK_RETURN_ADDRESS() \
|
|
||||||
((void *)(SHADOWHOOK_IS_SHARED_MODE ? shadowhook_get_return_address() : __builtin_return_address(0)))
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,47 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_enter.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "sh_trampo.h"
|
|
||||||
|
|
||||||
#define SH_ENTER_PAGE_NAME "shadowhook-enter"
|
|
||||||
#define SH_ENTER_SZ 256
|
|
||||||
#define SH_ENTER_DELAY_SEC 10
|
|
||||||
|
|
||||||
static sh_trampo_mgr_t sh_enter_trampo_mgr;
|
|
||||||
|
|
||||||
int sh_enter_init(void) {
|
|
||||||
sh_trampo_init_mgr(&sh_enter_trampo_mgr, SH_ENTER_PAGE_NAME, SH_ENTER_SZ, SH_ENTER_DELAY_SEC);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_enter_alloc(void) {
|
|
||||||
return sh_trampo_alloc(&sh_enter_trampo_mgr, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_enter_free(uintptr_t enter) {
|
|
||||||
sh_trampo_free(&sh_enter_trampo_mgr, enter);
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int sh_enter_init(void);
|
|
||||||
|
|
||||||
uintptr_t sh_enter_alloc(void);
|
|
||||||
void sh_enter_free(uintptr_t enter);
|
|
@ -1,420 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_exit.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "sh_config.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_trampo.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#define SH_EXIT_PAGE_NAME "shadowhook-exit"
|
|
||||||
#if defined(__arm__)
|
|
||||||
#define SH_EXIT_SZ 8
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#define SH_EXIT_SZ 16
|
|
||||||
#endif
|
|
||||||
#define SH_EXIT_DELAY_SEC 2
|
|
||||||
#define SH_EXIT_GAPS_CAP 16
|
|
||||||
|
|
||||||
// Used to identify whether the ELF gap has been zero-filled,
|
|
||||||
// and also serves as a guard instruction.
|
|
||||||
#if defined(__arm__)
|
|
||||||
#define SH_EXIT_CLEAR_FLAG 0xE1200070BE00BE00 // BKPT #0(thumb) + BKPT #0(thumb) + BKPT #0(arm)
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#define SH_EXIT_CLEAR_FLAG 0xD4200000D4200000 // BRK #0 + BRK #0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SH_EXIT_TYPE_OUT_LIBRARY 0
|
|
||||||
#define SH_EXIT_TYPE_IN_LIBRARY 1
|
|
||||||
|
|
||||||
extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
|
|
||||||
|
|
||||||
static pthread_mutex_t sh_exit_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static sh_trampo_mgr_t sh_exit_trampo_mgr;
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t load_bias;
|
|
||||||
const ElfW(Phdr) *dlpi_phdr;
|
|
||||||
ElfW(Half) dlpi_phnum;
|
|
||||||
} sh_exit_elfinfo_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
static sh_exit_elfinfo_t sh_exit_app_process_info;
|
|
||||||
static sh_exit_elfinfo_t sh_exit_linker_info;
|
|
||||||
static sh_exit_elfinfo_t sh_exit_vdso_info; // vdso may not exist
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-statement-expression"
|
|
||||||
|
|
||||||
static void sh_exit_init_elfinfo(unsigned long type, sh_exit_elfinfo_t *info) {
|
|
||||||
if (__predict_false(NULL == getauxval)) goto err;
|
|
||||||
|
|
||||||
uintptr_t val = (uintptr_t)getauxval(type);
|
|
||||||
if (__predict_false(0 == val)) goto err;
|
|
||||||
|
|
||||||
// get base
|
|
||||||
uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val);
|
|
||||||
if (__predict_false(0 != memcmp((void *)base, ELFMAG, SELFMAG))) goto err;
|
|
||||||
|
|
||||||
// ELF info
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
|
|
||||||
const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
|
|
||||||
ElfW(Half) dlpi_phnum = ehdr->e_phnum;
|
|
||||||
|
|
||||||
// get load_bias
|
|
||||||
uintptr_t min_vaddr = UINTPTR_MAX;
|
|
||||||
for (size_t i = 0; i < dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type) {
|
|
||||||
if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (__predict_false(UINTPTR_MAX == min_vaddr || base < min_vaddr)) goto err;
|
|
||||||
uintptr_t load_bias = base - min_vaddr;
|
|
||||||
|
|
||||||
info->load_bias = load_bias;
|
|
||||||
info->dlpi_phdr = dlpi_phdr;
|
|
||||||
info->dlpi_phnum = dlpi_phnum;
|
|
||||||
return;
|
|
||||||
|
|
||||||
err:
|
|
||||||
info->load_bias = 0;
|
|
||||||
info->dlpi_phdr = NULL;
|
|
||||||
info->dlpi_phnum = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_exit_init(void) {
|
|
||||||
// init for out-library mode
|
|
||||||
sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC);
|
|
||||||
|
|
||||||
// init for in-library mode
|
|
||||||
sh_exit_init_elfinfo(AT_PHDR, &sh_exit_app_process_info);
|
|
||||||
sh_exit_init_elfinfo(AT_BASE, &sh_exit_linker_info);
|
|
||||||
sh_exit_init_elfinfo(AT_SYSINFO_EHDR, &sh_exit_vdso_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// out-library mode:
|
|
||||||
//
|
|
||||||
// We store the shellcode for exit in mmaped memory near the PC.
|
|
||||||
//
|
|
||||||
|
|
||||||
static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
|
|
||||||
size_t exit_len, size_t range_low, size_t range_high) {
|
|
||||||
(void)dlinfo;
|
|
||||||
|
|
||||||
uintptr_t addr = sh_trampo_alloc(&sh_exit_trampo_mgr, pc, range_low, range_high);
|
|
||||||
if (0 == addr) return -1;
|
|
||||||
|
|
||||||
memcpy((void *)addr, exit, exit_len);
|
|
||||||
sh_util_clear_cache(addr, exit_len);
|
|
||||||
*exit_addr = addr;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_exit_free_out_library(uintptr_t exit_addr) {
|
|
||||||
sh_trampo_free(&sh_exit_trampo_mgr, exit_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// in-library mode:
|
|
||||||
//
|
|
||||||
// We store the shellcode for exit in the memory gaps in the ELF.
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t start;
|
|
||||||
uintptr_t end;
|
|
||||||
bool need_fill_zero;
|
|
||||||
bool readable;
|
|
||||||
} sh_exit_gap_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
static size_t sh_exit_get_gaps(xdl_info_t *dlinfo, sh_exit_gap_t *gaps, size_t gaps_cap,
|
|
||||||
bool elf_loaded_by_kernel) {
|
|
||||||
size_t gaps_used = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) {
|
|
||||||
// current LOAD segment
|
|
||||||
const ElfW(Phdr) *cur_phdr = &(dlinfo->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD != cur_phdr->p_type) continue;
|
|
||||||
|
|
||||||
// next LOAD segment
|
|
||||||
const ElfW(Phdr) *next_phdr = NULL;
|
|
||||||
if (!elf_loaded_by_kernel) {
|
|
||||||
for (size_t j = i + 1; j < dlinfo->dlpi_phnum; j++) {
|
|
||||||
if (PT_LOAD == dlinfo->dlpi_phdr[j].p_type) {
|
|
||||||
next_phdr = &(dlinfo->dlpi_phdr[j]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t cur_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_memsz;
|
|
||||||
uintptr_t cur_page_end = sh_util_page_end(cur_end);
|
|
||||||
uintptr_t cur_file_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_filesz;
|
|
||||||
uintptr_t cur_file_page_end = sh_util_page_end(cur_file_end);
|
|
||||||
uintptr_t next_page_start =
|
|
||||||
(NULL == next_phdr ? cur_page_end
|
|
||||||
: sh_util_page_start((uintptr_t)dlinfo->dli_fbase + next_phdr->p_vaddr));
|
|
||||||
|
|
||||||
sh_exit_gap_t gap = {0, 0, false, false};
|
|
||||||
if (cur_phdr->p_flags & PF_X) {
|
|
||||||
// From: last PF_X page's unused memory tail space.
|
|
||||||
// To: next page start.
|
|
||||||
gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10);
|
|
||||||
gap.end = next_page_start;
|
|
||||||
gap.need_fill_zero = true;
|
|
||||||
gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start);
|
|
||||||
} else if (cur_page_end > cur_file_page_end) {
|
|
||||||
// From: last .bss page(which must NOT be file backend)'s unused memory tail space.
|
|
||||||
// To: next page start.
|
|
||||||
gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10);
|
|
||||||
gap.end = next_page_start;
|
|
||||||
gap.need_fill_zero = false;
|
|
||||||
gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start);
|
|
||||||
} else if (next_page_start > cur_page_end) {
|
|
||||||
// Entire unused memory pages.
|
|
||||||
gap.start = cur_page_end;
|
|
||||||
gap.end = next_page_start;
|
|
||||||
gap.need_fill_zero = true;
|
|
||||||
gap.readable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((gap.need_fill_zero && gap.end > gap.start + 0x10) || (!gap.need_fill_zero && gap.end > gap.start)) {
|
|
||||||
SH_LOG_INFO("exit: gap, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR " - %" PRIxPTR
|
|
||||||
"), NFZ %d, READABLE %d",
|
|
||||||
gap.start, gap.end, (uintptr_t)dlinfo->dli_fbase, gap.start - (uintptr_t)dlinfo->dli_fbase,
|
|
||||||
gap.end - (uintptr_t)dlinfo->dli_fbase, gap.need_fill_zero ? 1 : 0, gap.readable ? 1 : 0);
|
|
||||||
gaps[gaps_used].start = gap.start;
|
|
||||||
gaps[gaps_used].end = gap.end;
|
|
||||||
gaps[gaps_used].need_fill_zero = gap.need_fill_zero;
|
|
||||||
gaps[gaps_used].readable = gap.readable;
|
|
||||||
gaps_used++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gaps_used >= gaps_cap) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gaps_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_exit_is_in_elf_range(uintptr_t pc, sh_exit_elfinfo_t *info) {
|
|
||||||
if (pc < info->load_bias) return false;
|
|
||||||
|
|
||||||
uintptr_t vaddr = pc - info->load_bias;
|
|
||||||
for (size_t i = 0; i < info->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD != phdr->p_type) continue;
|
|
||||||
|
|
||||||
if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_exit_is_elf_loaded_by_kernel(uintptr_t pc) {
|
|
||||||
if (NULL == sh_exit_app_process_info.dlpi_phdr) return true;
|
|
||||||
if (sh_exit_is_in_elf_range(pc, &sh_exit_app_process_info)) return true;
|
|
||||||
|
|
||||||
if (NULL == sh_exit_linker_info.dlpi_phdr) return true;
|
|
||||||
if (sh_exit_is_in_elf_range(pc, &sh_exit_linker_info)) return true;
|
|
||||||
|
|
||||||
// vdso may not exist
|
|
||||||
if (NULL != sh_exit_vdso_info.dlpi_phdr)
|
|
||||||
if (sh_exit_is_in_elf_range(pc, &sh_exit_vdso_info)) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_exit_is_zero(uintptr_t buf, size_t buf_len) {
|
|
||||||
for (uintptr_t i = buf; i < buf + buf_len; i += sizeof(uintptr_t))
|
|
||||||
if (*((uintptr_t *)i) != 0) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_exit_fill_zero(uintptr_t start, uintptr_t end, bool readable, xdl_info_t *dlinfo) {
|
|
||||||
size_t size = end - start;
|
|
||||||
bool set_prot_rwx = false;
|
|
||||||
|
|
||||||
if (!readable) {
|
|
||||||
if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
|
|
||||||
set_prot_rwx = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*((uint64_t *)start) != SH_EXIT_CLEAR_FLAG) {
|
|
||||||
SH_LOG_INFO("exit: gap fill zero, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR
|
|
||||||
" - %" PRIxPTR "), READABLE %d",
|
|
||||||
start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase,
|
|
||||||
end - (uintptr_t)dlinfo->dli_fbase, readable ? 1 : 0);
|
|
||||||
if (!set_prot_rwx)
|
|
||||||
if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
|
|
||||||
memset((void *)start, 0, size);
|
|
||||||
*((uint64_t *)(start)) = SH_EXIT_CLEAR_FLAG;
|
|
||||||
sh_util_clear_cache(start, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_exit_try_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
|
|
||||||
size_t exit_len, size_t range_low, size_t range_high, uintptr_t start,
|
|
||||||
uintptr_t end) {
|
|
||||||
if (pc >= range_low) start = SH_UTIL_MAX(start, pc - range_low);
|
|
||||||
start = SH_UTIL_ALIGN_END(start, exit_len);
|
|
||||||
|
|
||||||
if (range_high <= UINTPTR_MAX - pc) end = SH_UTIL_MIN(end - exit_len, pc + range_high);
|
|
||||||
end = SH_UTIL_ALIGN_START(end, exit_len);
|
|
||||||
|
|
||||||
if (end < start) return -1;
|
|
||||||
SH_LOG_INFO("exit: gap resize, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR
|
|
||||||
" - %" PRIxPTR ")",
|
|
||||||
start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase,
|
|
||||||
end - (uintptr_t)dlinfo->dli_fbase);
|
|
||||||
|
|
||||||
for (uintptr_t cur = start; cur <= end; cur += exit_len) {
|
|
||||||
// check if the current space has been used
|
|
||||||
if (!sh_exit_is_zero(cur, exit_len)) continue;
|
|
||||||
|
|
||||||
// write shellcode to the current location
|
|
||||||
if (0 != sh_util_mprotect(cur, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
|
|
||||||
memcpy((void *)cur, exit, exit_len);
|
|
||||||
sh_util_clear_cache(cur, exit_len);
|
|
||||||
*exit_addr = cur;
|
|
||||||
SH_LOG_INFO("exit: in-library alloc, at %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR "), len %zu",
|
|
||||||
cur, (uintptr_t)dlinfo->dli_fbase, cur - (uintptr_t)dlinfo->dli_fbase, exit_len);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
|
|
||||||
size_t exit_len, size_t range_low, size_t range_high) {
|
|
||||||
int r = -1;
|
|
||||||
*exit_addr = 0;
|
|
||||||
|
|
||||||
bool elf_loaded_by_kernel = sh_exit_is_elf_loaded_by_kernel(pc);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sh_exit_lock);
|
|
||||||
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
sh_exit_gap_t gaps[SH_EXIT_GAPS_CAP];
|
|
||||||
size_t gaps_used = sh_exit_get_gaps(dlinfo, gaps, SH_EXIT_GAPS_CAP, elf_loaded_by_kernel);
|
|
||||||
for (size_t i = 0; i < gaps_used; i++) {
|
|
||||||
// fill zero
|
|
||||||
if (gaps[i].need_fill_zero) {
|
|
||||||
if (0 != sh_exit_fill_zero(gaps[i].start, gaps[i].end, gaps[i].readable, dlinfo)) return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 == (r = sh_exit_try_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high,
|
|
||||||
gaps[i].start, gaps[i].end)))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
r = -1;
|
|
||||||
*exit_addr = 0;
|
|
||||||
SH_LOG_WARN("exit: alloc crashed");
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&sh_exit_lock);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_exit_free_in_library(uintptr_t exit_addr, uint8_t *exit, size_t exit_len) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sh_exit_lock);
|
|
||||||
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
if (0 != memcmp((void *)exit_addr, exit, exit_len)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (0 != sh_util_mprotect((uintptr_t)exit_addr, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_MPROT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
memset((void *)exit_addr, 0, exit_len);
|
|
||||||
sh_util_clear_cache((uintptr_t)exit_addr, exit_len);
|
|
||||||
r = 0;
|
|
||||||
err:;
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH;
|
|
||||||
SH_LOG_WARN("exit: free crashed");
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&sh_exit_lock);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
|
|
||||||
size_t exit_len, size_t range_low, size_t range_high) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
// (1) try out-library mode first. Because ELF gaps are a valuable non-renewable resource.
|
|
||||||
*exit_type = SH_EXIT_TYPE_OUT_LIBRARY;
|
|
||||||
r = sh_exit_alloc_out_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high);
|
|
||||||
if (0 == r) goto ok;
|
|
||||||
|
|
||||||
// (2) try in-library mode.
|
|
||||||
*exit_type = SH_EXIT_TYPE_IN_LIBRARY;
|
|
||||||
r = sh_exit_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high);
|
|
||||||
if (0 == r) goto ok;
|
|
||||||
|
|
||||||
return r;
|
|
||||||
|
|
||||||
ok:
|
|
||||||
SH_LOG_INFO("exit: alloc %s library, exit %" PRIxPTR ", pc %" PRIxPTR ", distance %" PRIxPTR
|
|
||||||
", range [-%zx, %zx]",
|
|
||||||
(*exit_type == SH_EXIT_TYPE_OUT_LIBRARY ? "out" : "in"), *exit_addr, pc,
|
|
||||||
(pc > *exit_addr ? pc - *exit_addr : *exit_addr - pc), range_low, range_high);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len) {
|
|
||||||
if (SH_EXIT_TYPE_OUT_LIBRARY == exit_type) {
|
|
||||||
(void)exit, (void)exit_len;
|
|
||||||
sh_exit_free_out_library(exit_addr);
|
|
||||||
return 0;
|
|
||||||
} else
|
|
||||||
return sh_exit_free_in_library(exit_addr, exit, exit_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
@ -1,34 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
void sh_exit_init(void);
|
|
||||||
|
|
||||||
int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit,
|
|
||||||
size_t exit_len, size_t range_low, size_t range_high);
|
|
||||||
int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len);
|
|
@ -1,538 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_hub.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_safe.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_trampo.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "tree.h"
|
|
||||||
|
|
||||||
#define SH_HUB_TRAMPO_PAGE_NAME "shadowhook-hub-trampo"
|
|
||||||
#define SH_HUB_TRAMPO_DELAY_SEC 5
|
|
||||||
#define SH_HUB_STACK_NAME "shadowhook-hub-stack"
|
|
||||||
#define SH_HUB_STACK_SIZE 4096 // 4K is enough
|
|
||||||
#define SH_HUB_STACK_FRAME_MAX 127 // keep sizeof(sh_hub_stack_t) < 4K
|
|
||||||
#define SH_HUB_THREAD_MAX 1024
|
|
||||||
#define SH_HUB_DELAY_SEC 10
|
|
||||||
|
|
||||||
#define SH_HUB_FRAME_FLAG_NONE ((uintptr_t)0)
|
|
||||||
#define SH_HUB_FRAME_FLAG_ALLOW_REENTRANT ((uintptr_t)(1 << 0))
|
|
||||||
|
|
||||||
// proxy for each hook-task in the same target-address
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct sh_hub_proxy {
|
|
||||||
void *func;
|
|
||||||
bool enabled;
|
|
||||||
SLIST_ENTRY(sh_hub_proxy, ) link;
|
|
||||||
} sh_hub_proxy_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// proxy list for each hub
|
|
||||||
typedef SLIST_HEAD(sh_hub_proxy_list, sh_hub_proxy, ) sh_hub_proxy_list_t;
|
|
||||||
|
|
||||||
// frame in the stack
|
|
||||||
typedef struct {
|
|
||||||
sh_hub_proxy_list_t proxies;
|
|
||||||
uintptr_t orig_addr;
|
|
||||||
void *return_address;
|
|
||||||
uintptr_t flags;
|
|
||||||
} sh_hub_frame_t;
|
|
||||||
|
|
||||||
// stack for each thread
|
|
||||||
typedef struct {
|
|
||||||
size_t frames_cnt;
|
|
||||||
sh_hub_frame_t frames[SH_HUB_STACK_FRAME_MAX];
|
|
||||||
} sh_hub_stack_t;
|
|
||||||
|
|
||||||
// hub for each target-address
|
|
||||||
struct sh_hub {
|
|
||||||
sh_hub_proxy_list_t proxies;
|
|
||||||
pthread_mutex_t proxies_lock;
|
|
||||||
uintptr_t orig_addr;
|
|
||||||
uintptr_t trampo;
|
|
||||||
time_t destroy_ts;
|
|
||||||
LIST_ENTRY(sh_hub, ) link;
|
|
||||||
};
|
|
||||||
|
|
||||||
// hub list for delayed-destroy staging
|
|
||||||
typedef LIST_HEAD(sh_hub_list, sh_hub, ) sh_hub_list_t;
|
|
||||||
|
|
||||||
// global data for hub delayed-destroy staging
|
|
||||||
static sh_hub_list_t sh_hub_delayed_destroy;
|
|
||||||
static pthread_mutex_t sh_hub_delayed_destroy_lock;
|
|
||||||
|
|
||||||
// global data for trampo
|
|
||||||
static sh_trampo_mgr_t sh_hub_trampo_mgr;
|
|
||||||
|
|
||||||
// global data for stack
|
|
||||||
static pthread_key_t sh_hub_stack_tls_key;
|
|
||||||
static sh_hub_stack_t *sh_hub_stack_cache;
|
|
||||||
static uint8_t *sh_hub_stack_cache_used;
|
|
||||||
|
|
||||||
// hub trampoline template
|
|
||||||
extern void *sh_hub_trampo_template_data __attribute__((visibility("hidden")));
|
|
||||||
__attribute__((naked)) static void sh_hub_trampo_template(void) {
|
|
||||||
#if defined(__arm__)
|
|
||||||
__asm__(
|
|
||||||
// Save parameter registers, LR
|
|
||||||
"push { r0 - r3, lr } \n"
|
|
||||||
|
|
||||||
// Call sh_hub_push_stack()
|
|
||||||
"ldr r0, hub_ptr \n"
|
|
||||||
"mov r1, lr \n"
|
|
||||||
"ldr ip, push_stack \n"
|
|
||||||
"blx ip \n"
|
|
||||||
|
|
||||||
// Save the hook function's address to IP register
|
|
||||||
"mov ip, r0 \n"
|
|
||||||
|
|
||||||
// Restore parameter registers, LR
|
|
||||||
"pop { r0 - r3, lr } \n"
|
|
||||||
|
|
||||||
// Call hook function
|
|
||||||
"bx ip \n"
|
|
||||||
|
|
||||||
"sh_hub_trampo_template_data:"
|
|
||||||
".global sh_hub_trampo_template_data;"
|
|
||||||
"push_stack:"
|
|
||||||
".word 0;"
|
|
||||||
"hub_ptr:"
|
|
||||||
".word 0;");
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
__asm__(
|
|
||||||
// Save parameter registers, XR(X8), LR
|
|
||||||
"stp x0, x1, [sp, #-0xd0]! \n"
|
|
||||||
"stp x2, x3, [sp, #0x10] \n"
|
|
||||||
"stp x4, x5, [sp, #0x20] \n"
|
|
||||||
"stp x6, x7, [sp, #0x30] \n"
|
|
||||||
"stp x8, lr, [sp, #0x40] \n"
|
|
||||||
"stp q0, q1, [sp, #0x50] \n"
|
|
||||||
"stp q2, q3, [sp, #0x70] \n"
|
|
||||||
"stp q4, q5, [sp, #0x90] \n"
|
|
||||||
"stp q6, q7, [sp, #0xb0] \n"
|
|
||||||
|
|
||||||
// Call sh_hub_push_stack()
|
|
||||||
"ldr x0, hub_ptr \n"
|
|
||||||
"mov x1, lr \n"
|
|
||||||
"ldr x16, push_stack \n"
|
|
||||||
"blr x16 \n"
|
|
||||||
|
|
||||||
// Save the hook function's address to IP register
|
|
||||||
"mov x16, x0 \n"
|
|
||||||
|
|
||||||
// Restore parameter registers, XR(X8), LR
|
|
||||||
"ldp q6, q7, [sp, #0xb0] \n"
|
|
||||||
"ldp q4, q5, [sp, #0x90] \n"
|
|
||||||
"ldp q2, q3, [sp, #0x70] \n"
|
|
||||||
"ldp q0, q1, [sp, #0x50] \n"
|
|
||||||
"ldp x8, lr, [sp, #0x40] \n"
|
|
||||||
"ldp x6, x7, [sp, #0x30] \n"
|
|
||||||
"ldp x4, x5, [sp, #0x20] \n"
|
|
||||||
"ldp x2, x3, [sp, #0x10] \n"
|
|
||||||
"ldp x0, x1, [sp], #0xd0 \n"
|
|
||||||
|
|
||||||
// Call hook function
|
|
||||||
"br x16 \n"
|
|
||||||
|
|
||||||
"sh_hub_trampo_template_data:"
|
|
||||||
".global sh_hub_trampo_template_data;"
|
|
||||||
"push_stack:"
|
|
||||||
".quad 0;"
|
|
||||||
"hub_ptr:"
|
|
||||||
".quad 0;");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *sh_hub_trampo_template_start(void) {
|
|
||||||
#if defined(__arm__) && defined(__thumb__)
|
|
||||||
return (void *)((uintptr_t)&sh_hub_trampo_template - 1);
|
|
||||||
#else
|
|
||||||
return (void *)&sh_hub_trampo_template;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static sh_hub_stack_t *sh_hub_stack_create(void) {
|
|
||||||
// get stack from global cache
|
|
||||||
for (size_t i = 0; i < SH_HUB_THREAD_MAX; i++) {
|
|
||||||
uint8_t *used = &(sh_hub_stack_cache_used[i]);
|
|
||||||
if (0 == *used) {
|
|
||||||
uint8_t expected = 0;
|
|
||||||
if (__atomic_compare_exchange_n(used, &expected, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
|
|
||||||
sh_hub_stack_t *stack = &(sh_hub_stack_cache[i]);
|
|
||||||
stack->frames_cnt = 0;
|
|
||||||
SH_LOG_DEBUG("hub: get stack from global cache[%zu] %p", i, (void *)stack);
|
|
||||||
return stack; // OK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new stack by mmap
|
|
||||||
int prot = PROT_READ | PROT_WRITE;
|
|
||||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
|
||||||
void *buf = sh_safe_mmap(NULL, SH_HUB_STACK_SIZE, prot, flags, -1, 0);
|
|
||||||
if (MAP_FAILED == buf) return NULL; // failed
|
|
||||||
sh_safe_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)buf, SH_HUB_STACK_SIZE,
|
|
||||||
(unsigned long)SH_HUB_STACK_NAME);
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)buf;
|
|
||||||
stack->frames_cnt = 0;
|
|
||||||
return stack; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_hub_stack_destroy(void *buf) {
|
|
||||||
if (NULL == buf) return;
|
|
||||||
|
|
||||||
if ((uintptr_t)sh_hub_stack_cache <= (uintptr_t)buf &&
|
|
||||||
(uintptr_t)buf < ((uintptr_t)sh_hub_stack_cache + SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t))) {
|
|
||||||
// return stack to global cache
|
|
||||||
size_t i = ((uintptr_t)buf - (uintptr_t)sh_hub_stack_cache) / sizeof(sh_hub_stack_t);
|
|
||||||
uint8_t *used = &(sh_hub_stack_cache_used[i]);
|
|
||||||
if (1 != *used) abort();
|
|
||||||
__atomic_store_n(used, 0, __ATOMIC_RELEASE);
|
|
||||||
SH_LOG_DEBUG("hub: return stack to global cache[%zu] %p", i, buf);
|
|
||||||
} else {
|
|
||||||
// munmap stack
|
|
||||||
munmap(buf, SH_HUB_STACK_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_hub_init(void) {
|
|
||||||
LIST_INIT(&sh_hub_delayed_destroy);
|
|
||||||
pthread_mutex_init(&sh_hub_delayed_destroy_lock, NULL);
|
|
||||||
|
|
||||||
// init TLS key
|
|
||||||
if (__predict_false(0 != pthread_key_create(&sh_hub_stack_tls_key, sh_hub_stack_destroy))) return -1;
|
|
||||||
|
|
||||||
// init hub's stack cache
|
|
||||||
if (__predict_false(NULL == (sh_hub_stack_cache = malloc(SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t)))))
|
|
||||||
return -1;
|
|
||||||
if (__predict_false(NULL == (sh_hub_stack_cache_used = calloc(SH_HUB_THREAD_MAX, sizeof(uint8_t)))))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// init hub's trampoline manager
|
|
||||||
size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start());
|
|
||||||
size_t data_size = sizeof(void *) + sizeof(void *);
|
|
||||||
sh_trampo_init_mgr(&sh_hub_trampo_mgr, SH_HUB_TRAMPO_PAGE_NAME, code_size + data_size,
|
|
||||||
SH_HUB_TRAMPO_DELAY_SEC);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *sh_hub_push_stack(sh_hub_t *self, void *return_address) {
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
|
|
||||||
|
|
||||||
// create stack, only once
|
|
||||||
if (__predict_false(NULL == stack)) {
|
|
||||||
if (__predict_false(NULL == (stack = sh_hub_stack_create()))) goto end;
|
|
||||||
sh_safe_pthread_setspecific(sh_hub_stack_tls_key, (void *)stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether a recursive call occurred
|
|
||||||
bool recursive = false;
|
|
||||||
for (size_t i = stack->frames_cnt; i > 0; i--) {
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[i - 1];
|
|
||||||
if (0 == (frame->flags & SH_HUB_FRAME_FLAG_ALLOW_REENTRANT) && (frame->orig_addr == self->orig_addr)) {
|
|
||||||
// recursive call found
|
|
||||||
recursive = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// find and return the first enabled proxy's function in the proxy-list
|
|
||||||
// (does not include the original function)
|
|
||||||
if (!recursive) {
|
|
||||||
sh_hub_proxy_t *proxy;
|
|
||||||
SLIST_FOREACH(proxy, &self->proxies, link) {
|
|
||||||
if (proxy->enabled) {
|
|
||||||
// push a new frame for the current proxy
|
|
||||||
if (stack->frames_cnt >= SH_HUB_STACK_FRAME_MAX) goto end;
|
|
||||||
stack->frames_cnt++;
|
|
||||||
SH_LOG_DEBUG("hub: frames_cnt++ = %zu", stack->frames_cnt);
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
|
|
||||||
frame->proxies = self->proxies;
|
|
||||||
frame->orig_addr = self->orig_addr;
|
|
||||||
frame->return_address = return_address;
|
|
||||||
frame->flags = SH_HUB_FRAME_FLAG_NONE;
|
|
||||||
|
|
||||||
// return the first enabled proxy's function
|
|
||||||
SH_LOG_DEBUG("hub: push_stack() return first enabled proxy %p", proxy->func);
|
|
||||||
return proxy->func;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not found enabled proxy in the proxy-list, or recursive call found,
|
|
||||||
// just return the original-function
|
|
||||||
end:
|
|
||||||
SH_LOG_DEBUG("hub: push_stack() return orig_addr %p", (void *)self->orig_addr);
|
|
||||||
return (void *)self->orig_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_hub_pop_stack(void *return_address) {
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
|
|
||||||
if (0 == stack->frames_cnt) return;
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
|
|
||||||
|
|
||||||
// only the first proxy will actually execute pop-stack()
|
|
||||||
if (frame->return_address == return_address) {
|
|
||||||
stack->frames_cnt--;
|
|
||||||
SH_LOG_DEBUG("hub: frames_cnt-- = %zu", stack->frames_cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo) {
|
|
||||||
size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start());
|
|
||||||
size_t data_size = sizeof(void *) + sizeof(void *);
|
|
||||||
|
|
||||||
sh_hub_t *self = malloc(sizeof(sh_hub_t));
|
|
||||||
if (NULL == self) return NULL;
|
|
||||||
SLIST_INIT(&self->proxies);
|
|
||||||
pthread_mutex_init(&self->proxies_lock, NULL);
|
|
||||||
self->orig_addr = 0;
|
|
||||||
|
|
||||||
// alloc memory for trampoline
|
|
||||||
if (0 == (self->trampo = sh_trampo_alloc(&sh_hub_trampo_mgr, 0, 0, 0))) {
|
|
||||||
free(self);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill in code
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
memcpy((void *)self->trampo, sh_hub_trampo_template_start(), code_size);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
|
|
||||||
free(self);
|
|
||||||
SH_LOG_WARN("hub: fill in code crashed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
|
|
||||||
// file in data
|
|
||||||
void **data = (void **)(self->trampo + code_size);
|
|
||||||
*data++ = (void *)sh_hub_push_stack;
|
|
||||||
*data = (void *)self;
|
|
||||||
|
|
||||||
// clear CPU cache
|
|
||||||
sh_util_clear_cache(self->trampo, code_size + data_size);
|
|
||||||
|
|
||||||
#if defined(__arm__) && defined(__thumb__)
|
|
||||||
*trampo = self->trampo + 1;
|
|
||||||
#else
|
|
||||||
*trampo = self->trampo;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SH_LOG_INFO("hub: create trampo for target_addr %" PRIxPTR " at %" PRIxPTR ", size %zu + %zu = %zu",
|
|
||||||
target_addr, *trampo, code_size, data_size, code_size + data_size);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_hub_destroy_inner(sh_hub_t *self) {
|
|
||||||
pthread_mutex_destroy(&self->proxies_lock);
|
|
||||||
|
|
||||||
if (0 != self->trampo) sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
|
|
||||||
|
|
||||||
while (!SLIST_EMPTY(&self->proxies)) {
|
|
||||||
sh_hub_proxy_t *proxy = SLIST_FIRST(&self->proxies);
|
|
||||||
SLIST_REMOVE_HEAD(&self->proxies, link);
|
|
||||||
free(proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_hub_destroy(sh_hub_t *self, bool with_delay) {
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) {
|
|
||||||
struct timeval now;
|
|
||||||
gettimeofday(&now, NULL);
|
|
||||||
|
|
||||||
if (!LIST_EMPTY(&sh_hub_delayed_destroy)) {
|
|
||||||
pthread_mutex_lock(&sh_hub_delayed_destroy_lock);
|
|
||||||
sh_hub_t *hub, *hub_tmp;
|
|
||||||
LIST_FOREACH_SAFE(hub, &sh_hub_delayed_destroy, link, hub_tmp)
|
|
||||||
if (now.tv_sec - hub->destroy_ts > SH_HUB_DELAY_SEC) {
|
|
||||||
LIST_REMOVE(hub, link);
|
|
||||||
sh_hub_destroy_inner(hub);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&sh_hub_delayed_destroy_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (with_delay) {
|
|
||||||
self->destroy_ts = now.tv_sec;
|
|
||||||
sh_trampo_free(&sh_hub_trampo_mgr, self->trampo);
|
|
||||||
self->trampo = 0;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sh_hub_delayed_destroy_lock);
|
|
||||||
LIST_INSERT_HEAD(&sh_hub_delayed_destroy, self, link);
|
|
||||||
pthread_mutex_unlock(&sh_hub_delayed_destroy_lock);
|
|
||||||
} else
|
|
||||||
sh_hub_destroy_inner(self);
|
|
||||||
} else
|
|
||||||
sh_hub_destroy_inner(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t sh_hub_get_orig_addr(sh_hub_t *self) {
|
|
||||||
return self->orig_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self) {
|
|
||||||
return &self->orig_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func) {
|
|
||||||
int r = SHADOWHOOK_ERRNO_OK;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&self->proxies_lock);
|
|
||||||
|
|
||||||
// check repeated funcion
|
|
||||||
sh_hub_proxy_t *proxy;
|
|
||||||
SLIST_FOREACH(proxy, &self->proxies, link) {
|
|
||||||
if (proxy->enabled && proxy->func == (void *)func) {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_DUP;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to re-enable an exists item
|
|
||||||
SLIST_FOREACH(proxy, &self->proxies, link) {
|
|
||||||
if (proxy->func == (void *)func) {
|
|
||||||
if (!proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, true, __ATOMIC_SEQ_CST);
|
|
||||||
|
|
||||||
SH_LOG_INFO("hub: add(re-enable) func %" PRIxPTR, func);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new item
|
|
||||||
if (NULL == (proxy = malloc(sizeof(sh_hub_proxy_t)))) {
|
|
||||||
r = SHADOWHOOK_ERRNO_OOM;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
proxy->func = (void *)func;
|
|
||||||
proxy->enabled = true;
|
|
||||||
|
|
||||||
// insert to the head of the proxy-list
|
|
||||||
// equivalent to: SLIST_INSERT_HEAD(&self->proxies, proxy, link);
|
|
||||||
// but: __ATOMIC_RELEASE ensures readers see only fully-constructed item
|
|
||||||
SLIST_NEXT(proxy, link) = SLIST_FIRST(&self->proxies);
|
|
||||||
__atomic_store_n((uintptr_t *)(&SLIST_FIRST(&self->proxies)), (uintptr_t)proxy, __ATOMIC_RELEASE);
|
|
||||||
SH_LOG_INFO("hub: add(new) func %" PRIxPTR, func);
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_mutex_unlock(&self->proxies_lock);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy) {
|
|
||||||
*have_enabled_proxy = false;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&self->proxies_lock);
|
|
||||||
|
|
||||||
sh_hub_proxy_t *proxy;
|
|
||||||
bool deleted = false;
|
|
||||||
SLIST_FOREACH(proxy, &self->proxies, link) {
|
|
||||||
if (proxy->func == (void *)func) {
|
|
||||||
if (proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, false, __ATOMIC_SEQ_CST);
|
|
||||||
|
|
||||||
deleted = true;
|
|
||||||
SH_LOG_INFO("hub: del func %" PRIxPTR, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxy->enabled && !*have_enabled_proxy) *have_enabled_proxy = true;
|
|
||||||
|
|
||||||
if (deleted && *have_enabled_proxy) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&self->proxies_lock);
|
|
||||||
|
|
||||||
return deleted ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sh_hub_frame_t *sh_hub_get_current_frame(void *return_address) {
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
|
|
||||||
if (0 == stack->frames_cnt) return NULL;
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
|
|
||||||
return frame->return_address == return_address ? frame : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_hub_allow_reentrant(void *return_address) {
|
|
||||||
sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address);
|
|
||||||
if (NULL != frame) {
|
|
||||||
frame->flags |= SH_HUB_FRAME_FLAG_ALLOW_REENTRANT;
|
|
||||||
SH_LOG_DEBUG("hub: allow reentrant frame %p", return_address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_hub_disallow_reentrant(void *return_address) {
|
|
||||||
sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address);
|
|
||||||
if (NULL != frame) {
|
|
||||||
frame->flags &= ~SH_HUB_FRAME_FLAG_ALLOW_REENTRANT;
|
|
||||||
SH_LOG_DEBUG("hub: disallow reentrant frame %p", return_address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *sh_hub_get_prev_func(void *func) {
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
|
|
||||||
if (0 == stack->frames_cnt) sh_safe_abort(); // called in a non-hook status?
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
|
|
||||||
|
|
||||||
// find and return the next enabled hook-function in the hook-chain
|
|
||||||
bool found = false;
|
|
||||||
sh_hub_proxy_t *proxy;
|
|
||||||
SLIST_FOREACH(proxy, &(frame->proxies), link) {
|
|
||||||
if (!found) {
|
|
||||||
if (proxy->func == func) found = true;
|
|
||||||
} else {
|
|
||||||
if (proxy->enabled) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NULL != proxy) {
|
|
||||||
SH_LOG_DEBUG("hub: get_prev_func() return next enabled proxy %p", proxy->func);
|
|
||||||
return proxy->func;
|
|
||||||
}
|
|
||||||
|
|
||||||
SH_LOG_DEBUG("hub: get_prev_func() return orig_addr %p", (void *)frame->orig_addr);
|
|
||||||
// did not find, return the original-function
|
|
||||||
return (void *)frame->orig_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *sh_hub_get_return_address(void) {
|
|
||||||
sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key);
|
|
||||||
if (0 == stack->frames_cnt) sh_safe_abort(); // called in a non-hook status?
|
|
||||||
sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1];
|
|
||||||
|
|
||||||
return frame->return_address;
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int sh_hub_init(void);
|
|
||||||
|
|
||||||
typedef struct sh_hub sh_hub_t;
|
|
||||||
|
|
||||||
sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo);
|
|
||||||
void sh_hub_destroy(sh_hub_t *self, bool with_delay);
|
|
||||||
|
|
||||||
uintptr_t sh_hub_get_orig_addr(sh_hub_t *self);
|
|
||||||
uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self);
|
|
||||||
|
|
||||||
int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func);
|
|
||||||
int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy);
|
|
||||||
|
|
||||||
void *sh_hub_get_prev_func(void *func);
|
|
||||||
void sh_hub_pop_stack(void *return_address);
|
|
||||||
void sh_hub_allow_reentrant(void *return_address);
|
|
||||||
void sh_hub_disallow_reentrant(void *return_address);
|
|
||||||
void *sh_hub_get_return_address(void);
|
|
@ -1,139 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <jni.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
#define SH_JNI_VERSION JNI_VERSION_1_6
|
|
||||||
#define SH_JNI_CLASS_NAME "com/bytedance/shadowhook/ShadowHook"
|
|
||||||
|
|
||||||
static jstring sh_jni_get_version(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)thiz;
|
|
||||||
return (*env)->NewStringUTF(env, shadowhook_get_version());
|
|
||||||
}
|
|
||||||
|
|
||||||
static jint sh_jni_init(JNIEnv *env, jobject thiz, jint mode, jboolean debuggable) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
return shadowhook_init(0 == mode ? SHADOWHOOK_MODE_SHARED : SHADOWHOOK_MODE_UNIQUE, (bool)debuggable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jint sh_jni_get_init_errno(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
return shadowhook_get_init_errno();
|
|
||||||
}
|
|
||||||
|
|
||||||
static jint sh_jni_get_mode(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
return SHADOWHOOK_MODE_SHARED == shadowhook_get_mode() ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jboolean sh_jni_get_debuggable(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
return shadowhook_get_debuggable();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_jni_set_debuggable(JNIEnv *env, jobject thiz, jboolean debuggable) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
shadowhook_set_debuggable((bool)debuggable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jboolean sh_jni_get_recordable(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
return shadowhook_get_recordable();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_jni_set_recordable(JNIEnv *env, jobject thiz, jboolean recordable) {
|
|
||||||
(void)env, (void)thiz;
|
|
||||||
|
|
||||||
shadowhook_set_recordable((bool)recordable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jstring sh_jni_to_errmsg(JNIEnv *env, jobject thiz, jint error_number) {
|
|
||||||
(void)thiz;
|
|
||||||
|
|
||||||
return (*env)->NewStringUTF(env, shadowhook_to_errmsg(error_number));
|
|
||||||
}
|
|
||||||
|
|
||||||
static jstring sh_jni_get_records(JNIEnv *env, jobject thiz, jint item_flags) {
|
|
||||||
(void)thiz;
|
|
||||||
|
|
||||||
char *str = shadowhook_get_records((uint32_t)item_flags);
|
|
||||||
if (NULL == str) return NULL;
|
|
||||||
|
|
||||||
jstring jstr = (*env)->NewStringUTF(env, str);
|
|
||||||
free(str);
|
|
||||||
return jstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jstring sh_jni_get_arch(JNIEnv *env, jobject thiz) {
|
|
||||||
(void)thiz;
|
|
||||||
|
|
||||||
#if defined(__arm__)
|
|
||||||
char *arch = "arm";
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
char *arch = "arm64";
|
|
||||||
#else
|
|
||||||
char *arch = "unsupported";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (*env)->NewStringUTF(env, arch);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|
||||||
(void)reserved;
|
|
||||||
|
|
||||||
if (__predict_false(NULL == vm)) return JNI_ERR;
|
|
||||||
|
|
||||||
JNIEnv *env;
|
|
||||||
if (__predict_false(JNI_OK != (*vm)->GetEnv(vm, (void **)&env, SH_JNI_VERSION))) return JNI_ERR;
|
|
||||||
if (__predict_false(NULL == env || NULL == *env)) return JNI_ERR;
|
|
||||||
|
|
||||||
jclass cls;
|
|
||||||
if (__predict_false(NULL == (cls = (*env)->FindClass(env, SH_JNI_CLASS_NAME)))) return JNI_ERR;
|
|
||||||
|
|
||||||
JNINativeMethod m[] = {{"nativeGetVersion", "()Ljava/lang/String;", (void *)sh_jni_get_version},
|
|
||||||
{"nativeInit", "(IZ)I", (void *)sh_jni_init},
|
|
||||||
{"nativeGetInitErrno", "()I", (void *)sh_jni_get_init_errno},
|
|
||||||
{"nativeGetMode", "()I", (void *)sh_jni_get_mode},
|
|
||||||
{"nativeGetDebuggable", "()Z", (void *)sh_jni_get_debuggable},
|
|
||||||
{"nativeSetDebuggable", "(Z)V", (void *)sh_jni_set_debuggable},
|
|
||||||
{"nativeGetRecordable", "()Z", (void *)sh_jni_get_recordable},
|
|
||||||
{"nativeSetRecordable", "(Z)V", (void *)sh_jni_set_recordable},
|
|
||||||
{"nativeToErrmsg", "(I)Ljava/lang/String;", (void *)sh_jni_to_errmsg},
|
|
||||||
{"nativeGetRecords", "(I)Ljava/lang/String;", (void *)sh_jni_get_records},
|
|
||||||
{"nativeGetArch", "()Ljava/lang/String;", (void *)sh_jni_get_arch}};
|
|
||||||
if (__predict_false(0 != (*env)->RegisterNatives(env, cls, m, sizeof(m) / sizeof(m[0])))) return JNI_ERR;
|
|
||||||
|
|
||||||
return SH_JNI_VERSION;
|
|
||||||
}
|
|
@ -1,416 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_linker.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_recorder.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_switch.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
#define SH_LINKER_BASENAME "linker"
|
|
||||||
#else
|
|
||||||
#define SH_LINKER_BASENAME "linker64"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SH_LINKER_SYM_G_DL_MUTEX "__dl__ZL10g_dl_mutex"
|
|
||||||
#define SH_LINKER_SYM_G_DL_MUTEX_U_QPR2 "__dl_g_dl_mutex"
|
|
||||||
#define SH_LINKER_SYM_DO_DLOPEN_L "__dl__Z9do_dlopenPKciPK17android_dlextinfo"
|
|
||||||
#define SH_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv"
|
|
||||||
#define SH_LINKER_SYM_DO_DLOPEN_O "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv"
|
|
||||||
|
|
||||||
static bool sh_linker_dlopen_hooked = false;
|
|
||||||
|
|
||||||
static sh_linker_post_dlopen_t sh_linker_post_dlopen;
|
|
||||||
static void *sh_linker_post_dlopen_arg;
|
|
||||||
|
|
||||||
static pthread_mutex_t *sh_linker_g_dl_mutex;
|
|
||||||
static uintptr_t sh_linker_dlopen_addr; // save address of dlopen(==4.x) or do_dlopen(>=5.0)
|
|
||||||
static xdl_info_t sh_linker_dlopen_dlinfo;
|
|
||||||
|
|
||||||
#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
static uintptr_t sh_linker_dlfcn[6];
|
|
||||||
static const char *sh_linker_dlfcn_name[6] = {"dlopen", "dlerror", "dlsym",
|
|
||||||
"dladdr", "dlclose", "dl_unwind_find_exidx"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__attribute__((constructor)) static void sh_linker_ctor(void) {
|
|
||||||
sh_linker_dlopen_addr = (uintptr_t)dlopen;
|
|
||||||
#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
sh_linker_dlfcn[0] = (uintptr_t)dlopen;
|
|
||||||
sh_linker_dlfcn[1] = (uintptr_t)dlerror;
|
|
||||||
sh_linker_dlfcn[2] = (uintptr_t)dlsym;
|
|
||||||
sh_linker_dlfcn[3] = (uintptr_t)dladdr;
|
|
||||||
sh_linker_dlfcn[4] = (uintptr_t)dlclose;
|
|
||||||
sh_linker_dlfcn[5] = (uintptr_t)dl_unwind_find_exidx;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *sh_linker_get_base_addr(xdl_info_t *dlinfo) {
|
|
||||||
uintptr_t vaddr_min = UINTPTR_MAX;
|
|
||||||
for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(dlinfo->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type && vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UINTPTR_MAX == vaddr_min)
|
|
||||||
return dlinfo->dli_fbase; // should not happen
|
|
||||||
else
|
|
||||||
return (void *)((uintptr_t)dlinfo->dli_fbase + sh_util_page_start(vaddr_min));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sh_linker_check_arch(xdl_info_t *dlinfo) {
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)sh_linker_get_base_addr(dlinfo);
|
|
||||||
|
|
||||||
#if defined(__LP64__)
|
|
||||||
#define SH_LINKER_ELF_CLASS ELFCLASS64
|
|
||||||
#define SH_LINKER_ELF_MACHINE EM_AARCH64
|
|
||||||
#else
|
|
||||||
#define SH_LINKER_ELF_CLASS ELFCLASS32
|
|
||||||
#define SH_LINKER_ELF_MACHINE EM_ARM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return false;
|
|
||||||
if (SH_LINKER_ELF_CLASS != ehdr->e_ident[EI_CLASS]) return false;
|
|
||||||
if (SH_LINKER_ELF_MACHINE != ehdr->e_machine) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_linker_init(void) {
|
|
||||||
memset(&sh_linker_dlopen_dlinfo, 0, sizeof(sh_linker_dlopen_dlinfo));
|
|
||||||
|
|
||||||
int api_level = sh_util_get_api_level();
|
|
||||||
if (__predict_true(api_level >= __ANDROID_API_L__)) {
|
|
||||||
sh_linker_dlopen_addr = 0;
|
|
||||||
|
|
||||||
void *handle = xdl_open(SH_LINKER_BASENAME, XDL_DEFAULT);
|
|
||||||
if (__predict_false(NULL == handle)) return -1;
|
|
||||||
xdl_info(handle, XDL_DI_DLINFO, (void *)&sh_linker_dlopen_dlinfo);
|
|
||||||
sh_linker_dlopen_dlinfo.dli_fname = SH_LINKER_BASENAME;
|
|
||||||
|
|
||||||
// get g_dl_mutex
|
|
||||||
if (api_level > __ANDROID_API_U__) {
|
|
||||||
sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX_U_QPR2, NULL));
|
|
||||||
} else {
|
|
||||||
sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX, NULL));
|
|
||||||
if (NULL == sh_linker_g_dl_mutex && api_level == __ANDROID_API_U__)
|
|
||||||
sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX_U_QPR2, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get do_dlopen
|
|
||||||
if (api_level >= __ANDROID_API_O__)
|
|
||||||
sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_O;
|
|
||||||
else if (api_level >= __ANDROID_API_N__)
|
|
||||||
sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_N;
|
|
||||||
else
|
|
||||||
sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_L;
|
|
||||||
sh_linker_dlopen_dlinfo.dli_saddr =
|
|
||||||
xdl_dsym(handle, sh_linker_dlopen_dlinfo.dli_sname, &(sh_linker_dlopen_dlinfo.dli_ssize));
|
|
||||||
sh_linker_dlopen_addr = (uintptr_t)sh_linker_dlopen_dlinfo.dli_saddr;
|
|
||||||
|
|
||||||
xdl_close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0 != sh_linker_dlopen_addr && (NULL != sh_linker_g_dl_mutex || api_level < __ANDROID_API_L__)) ? 0
|
|
||||||
: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *sh_linker_match_dlfcn(uintptr_t target_addr) {
|
|
||||||
#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
if (sh_util_get_api_level() < __ANDROID_API_L__)
|
|
||||||
for (size_t i = 0; i < sizeof(sh_linker_dlfcn) / sizeof(sh_linker_dlfcn[0]); i++)
|
|
||||||
if (sh_linker_dlfcn[i] == target_addr) return sh_linker_dlfcn_name[i];
|
|
||||||
#else
|
|
||||||
(void)target_addr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr) {
|
|
||||||
return SHADOWHOOK_IS_UNIQUE_MODE && !sh_linker_dlopen_hooked && target_addr == sh_linker_dlopen_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void *(*sh_linker_proxy_dlopen_t)(const char *, int);
|
|
||||||
static sh_linker_proxy_dlopen_t sh_linker_orig_dlopen;
|
|
||||||
static void *sh_linker_proxy_dlopen(const char *filename, int flag) {
|
|
||||||
void *handle;
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE)
|
|
||||||
handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_dlopen, sh_linker_proxy_dlopen_t, filename, flag);
|
|
||||||
else
|
|
||||||
handle = sh_linker_orig_dlopen(filename, flag);
|
|
||||||
|
|
||||||
if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
|
|
||||||
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void *(*sh_linker_proxy_do_dlopen_l_t)(const char *, int, const void *);
|
|
||||||
static sh_linker_proxy_do_dlopen_l_t sh_linker_orig_do_dlopen_l;
|
|
||||||
static void *sh_linker_proxy_do_dlopen_l(const char *name, int flags, const void *extinfo) {
|
|
||||||
void *handle;
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE)
|
|
||||||
handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_l, sh_linker_proxy_do_dlopen_l_t, name, flags,
|
|
||||||
extinfo);
|
|
||||||
else
|
|
||||||
handle = sh_linker_orig_do_dlopen_l(name, flags, extinfo);
|
|
||||||
|
|
||||||
if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
|
|
||||||
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void *(*sh_linker_proxy_do_dlopen_n_t)(const char *, int, const void *, void *);
|
|
||||||
static sh_linker_proxy_do_dlopen_n_t sh_linker_orig_do_dlopen_n;
|
|
||||||
static void *sh_linker_proxy_do_dlopen_n(const char *name, int flags, const void *extinfo,
|
|
||||||
void *caller_addr) {
|
|
||||||
void *handle;
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE)
|
|
||||||
handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_n, sh_linker_proxy_do_dlopen_n_t, name, flags,
|
|
||||||
extinfo, caller_addr);
|
|
||||||
else
|
|
||||||
handle = sh_linker_orig_do_dlopen_n(name, flags, extinfo, caller_addr);
|
|
||||||
|
|
||||||
if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
|
|
||||||
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK();
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg) {
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static int result = SHADOWHOOK_ERRNO_MONITOR_DLOPEN;
|
|
||||||
|
|
||||||
if (sh_linker_dlopen_hooked) return result;
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (sh_linker_dlopen_hooked) goto end;
|
|
||||||
|
|
||||||
// try hooking-dlopen only once
|
|
||||||
sh_linker_dlopen_hooked = true;
|
|
||||||
|
|
||||||
// do init for SHARED mode
|
|
||||||
if (SHADOWHOOK_IS_SHARED_MODE)
|
|
||||||
if (0 != sh_linker_init()) goto end;
|
|
||||||
|
|
||||||
// save post callback ptr before hooking
|
|
||||||
sh_linker_post_dlopen = post_dlopen;
|
|
||||||
sh_linker_post_dlopen_arg = post_dlopen_arg;
|
|
||||||
|
|
||||||
// hook for dlopen() or do_dlopen()
|
|
||||||
int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *) =
|
|
||||||
SHADOWHOOK_IS_SHARED_MODE ? sh_switch_hook : sh_switch_hook_invisible;
|
|
||||||
int api_level = sh_util_get_api_level();
|
|
||||||
size_t backup_len = 0;
|
|
||||||
int r;
|
|
||||||
if (api_level < __ANDROID_API_L__) {
|
|
||||||
// get & check dlinfo
|
|
||||||
r = sh_linker_get_dlinfo_by_addr((void *)sh_linker_dlopen_addr, &sh_linker_dlopen_dlinfo, NULL, 0, NULL,
|
|
||||||
0, false);
|
|
||||||
if (SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH == r) result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH;
|
|
||||||
if (0 != r) goto end;
|
|
||||||
|
|
||||||
// hook
|
|
||||||
r = hook(sh_linker_dlopen_addr, (uintptr_t)sh_linker_proxy_dlopen, (uintptr_t *)&sh_linker_orig_dlopen,
|
|
||||||
&backup_len, &sh_linker_dlopen_dlinfo);
|
|
||||||
|
|
||||||
// record
|
|
||||||
sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME, "dlopen",
|
|
||||||
(uintptr_t)sh_linker_proxy_dlopen, backup_len, UINTPTR_MAX,
|
|
||||||
(uintptr_t)(__builtin_return_address(0)));
|
|
||||||
|
|
||||||
if (0 != r) goto end;
|
|
||||||
} else {
|
|
||||||
// check dlinfo
|
|
||||||
if (!sh_linker_check_arch(&sh_linker_dlopen_dlinfo)) {
|
|
||||||
result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t proxy;
|
|
||||||
uintptr_t *orig;
|
|
||||||
if (api_level >= __ANDROID_API_N__) {
|
|
||||||
proxy = (uintptr_t)sh_linker_proxy_do_dlopen_n;
|
|
||||||
orig = (uintptr_t *)&sh_linker_orig_do_dlopen_n;
|
|
||||||
} else {
|
|
||||||
proxy = (uintptr_t)sh_linker_proxy_do_dlopen_l;
|
|
||||||
orig = (uintptr_t *)&sh_linker_orig_do_dlopen_l;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hook
|
|
||||||
pthread_mutex_lock(sh_linker_g_dl_mutex);
|
|
||||||
r = hook(sh_linker_dlopen_addr, proxy, orig, &backup_len, &sh_linker_dlopen_dlinfo);
|
|
||||||
pthread_mutex_unlock(sh_linker_g_dl_mutex);
|
|
||||||
|
|
||||||
// record
|
|
||||||
sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME,
|
|
||||||
sh_linker_dlopen_dlinfo.dli_sname, proxy, backup_len, UINTPTR_MAX,
|
|
||||||
(uintptr_t)(__builtin_return_address(0)));
|
|
||||||
|
|
||||||
if (0 != r) goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
result = 0;
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
SH_LOG_INFO("linker: hook dlopen %s, return: %d", 0 == result ? "OK" : "FAILED", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
|
|
||||||
char *sym_name, size_t sym_name_sz, bool ignore_symbol_check) {
|
|
||||||
// dladdr()
|
|
||||||
bool crashed = false;
|
|
||||||
void *dlcache = NULL;
|
|
||||||
int r = 0;
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
r = xdl_addr4(addr, dlinfo, &dlcache, ignore_symbol_check ? XDL_NON_SYM : XDL_DEFAULT);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
r = xdl_addr4(addr, dlinfo, &dlcache, ignore_symbol_check ? XDL_NON_SYM : XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
crashed = true;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
SH_LOG_INFO("task: get dlinfo by target addr: target_addr %p, sym_name %s, sym_sz %zu, load_bias %" PRIxPTR
|
|
||||||
", pathname %s",
|
|
||||||
addr, NULL == dlinfo->dli_sname ? "(NULL)" : dlinfo->dli_sname, dlinfo->dli_ssize,
|
|
||||||
(uintptr_t)dlinfo->dli_fbase, NULL == dlinfo->dli_fname ? "(NULL)" : dlinfo->dli_fname);
|
|
||||||
|
|
||||||
// check error
|
|
||||||
if (crashed) {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (0 == r || NULL == dlinfo->dli_fname) {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_DLINFO;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (!sh_linker_check_arch(dlinfo)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == dlinfo->dli_sname) {
|
|
||||||
if (ignore_symbol_check) {
|
|
||||||
dlinfo->dli_saddr = addr;
|
|
||||||
dlinfo->dli_sname = "unknown";
|
|
||||||
dlinfo->dli_ssize = 1024; // big enough
|
|
||||||
} else {
|
|
||||||
const char *matched_dlfcn_name = NULL;
|
|
||||||
if (NULL == (matched_dlfcn_name = sh_linker_match_dlfcn((uintptr_t)addr))) {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_DLINFO;
|
|
||||||
goto end;
|
|
||||||
} else {
|
|
||||||
dlinfo->dli_saddr = addr;
|
|
||||||
dlinfo->dli_sname = matched_dlfcn_name;
|
|
||||||
dlinfo->dli_ssize = 4; // safe length, only relative jumps are allowed
|
|
||||||
SH_LOG_INFO("task: match dlfcn, target_addr %p, sym_name %s", addr, matched_dlfcn_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (0 == dlinfo->dli_ssize) {
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_SYMSZ;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL != lib_name) strlcpy(lib_name, dlinfo->dli_fname, lib_name_sz);
|
|
||||||
if (NULL != sym_name) strlcpy(sym_name, dlinfo->dli_sname, sym_name_sz);
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
end:
|
|
||||||
xdl_addr_clean(&dlcache);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo,
|
|
||||||
char *real_lib_name, size_t real_lib_name_sz) {
|
|
||||||
// open library
|
|
||||||
bool crashed = false;
|
|
||||||
void *handle = NULL;
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
handle = xdl_open(lib_name, XDL_DEFAULT);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
handle = xdl_open(lib_name, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
crashed = true;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH;
|
|
||||||
if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING;
|
|
||||||
|
|
||||||
// get dlinfo
|
|
||||||
xdl_info(handle, XDL_DI_DLINFO, (void *)dlinfo);
|
|
||||||
|
|
||||||
// check error
|
|
||||||
if (!sh_linker_check_arch(dlinfo)) {
|
|
||||||
xdl_close(handle);
|
|
||||||
return SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup symbol address
|
|
||||||
crashed = false;
|
|
||||||
void *addr = NULL;
|
|
||||||
size_t sym_size = 0;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
// do xdl_sym() or xdl_dsym() in an dlclosed-ELF will cause a crash
|
|
||||||
addr = xdl_sym(handle, sym_name, &sym_size);
|
|
||||||
if (NULL == addr) addr = xdl_dsym(handle, sym_name, &sym_size);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
crashed = true;
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
|
|
||||||
// close library
|
|
||||||
xdl_close(handle);
|
|
||||||
|
|
||||||
if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH;
|
|
||||||
if (NULL == addr) return SHADOWHOOK_ERRNO_HOOK_DLSYM;
|
|
||||||
|
|
||||||
dlinfo->dli_fname = lib_name;
|
|
||||||
dlinfo->dli_sname = sym_name;
|
|
||||||
dlinfo->dli_saddr = addr;
|
|
||||||
dlinfo->dli_ssize = sym_size;
|
|
||||||
if (NULL != real_lib_name) strlcpy(real_lib_name, dlinfo->dli_fname, real_lib_name_sz);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
int sh_linker_init(void);
|
|
||||||
|
|
||||||
const char *sh_linker_match_dlfcn(uintptr_t target_addr);
|
|
||||||
bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr);
|
|
||||||
|
|
||||||
typedef void (*sh_linker_post_dlopen_t)(void *arg);
|
|
||||||
int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg);
|
|
||||||
|
|
||||||
int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
|
|
||||||
char *sym_name, size_t sym_name_sz, bool ignore_symbol_check);
|
|
||||||
int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo,
|
|
||||||
char *real_lib_name, size_t real_lib_name_sz);
|
|
@ -1,517 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_recorder.h"
|
|
||||||
|
|
||||||
#include "sh_config.h"
|
|
||||||
|
|
||||||
#ifdef SH_CONFIG_OPERATION_RECORDS
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#define SH_RECORDER_OP_HOOK_SYM_ADDR 0
|
|
||||||
#define SH_RECORDER_OP_HOOK_SYM_NAME 1
|
|
||||||
#define SH_RECORDER_OP_UNHOOK 2
|
|
||||||
|
|
||||||
#define SH_RECORDER_LIB_NAME_MAX 512
|
|
||||||
#define SH_RECORDER_SYM_NAME_MAX 1024
|
|
||||||
|
|
||||||
#define SH_RECORDER_STRINGS_BUF_EXPAND_STEP (1024 * 16)
|
|
||||||
#define SH_RECORDER_STRINGS_BUF_MAX (1024 * 128)
|
|
||||||
#define SH_RECORDER_RECORDS_BUF_EXPAND_STEP (1024 * 32)
|
|
||||||
#define SH_RECORDER_RECORDS_BUF_MAX (1024 * 384)
|
|
||||||
#define SH_RECORDER_OUTPUT_BUF_EXPAND_STEP (1024 * 128)
|
|
||||||
#define SH_RECORDER_OUTPUT_BUF_MAX (1024 * 1024)
|
|
||||||
|
|
||||||
static bool sh_recorder_recordable = false;
|
|
||||||
|
|
||||||
bool sh_recorder_get_recordable(void) {
|
|
||||||
return sh_recorder_recordable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_recorder_set_recordable(bool recordable) {
|
|
||||||
sh_recorder_recordable = recordable;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void *ptr;
|
|
||||||
size_t cap;
|
|
||||||
size_t sz;
|
|
||||||
pthread_mutex_t lock;
|
|
||||||
} sh_recorder_buf_t;
|
|
||||||
|
|
||||||
static int sh_recorder_buf_append(sh_recorder_buf_t *buf, size_t step, size_t max, const void *header,
|
|
||||||
size_t header_sz, const void *body, size_t body_sz) {
|
|
||||||
size_t needs = (header_sz + (NULL != body ? body_sz : 0));
|
|
||||||
if (needs > step) return -1;
|
|
||||||
|
|
||||||
if (buf->cap - buf->sz < needs) {
|
|
||||||
size_t new_cap = buf->cap + step;
|
|
||||||
if (new_cap > max) return -1;
|
|
||||||
void *new_ptr = realloc(buf->ptr, new_cap);
|
|
||||||
if (NULL == new_ptr) return -1;
|
|
||||||
buf->ptr = new_ptr;
|
|
||||||
buf->cap = new_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((void *)((uintptr_t)buf->ptr + buf->sz), header, header_sz);
|
|
||||||
if (NULL != body) memcpy((void *)((uintptr_t)buf->ptr + buf->sz + header_sz), body, body_sz);
|
|
||||||
buf->sz += needs;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_recorder_buf_free(sh_recorder_buf_t *buf) {
|
|
||||||
if (NULL != buf->ptr) {
|
|
||||||
free(buf->ptr);
|
|
||||||
buf->ptr = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static sh_recorder_buf_t sh_recorder_strings = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
|
|
||||||
static sh_recorder_buf_t sh_recorder_records = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
|
|
||||||
static bool sh_recorder_error = false;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t str_len; // body length, in order to speed up the search
|
|
||||||
} __attribute__((packed)) sh_recorder_str_header_t;
|
|
||||||
// +body: string, including the terminating null byte ('\0')
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint64_t op : 8;
|
|
||||||
uint64_t error_number : 8;
|
|
||||||
uint64_t ts_ms : 48;
|
|
||||||
uintptr_t stub;
|
|
||||||
uint16_t caller_lib_name_idx;
|
|
||||||
uint8_t backup_len;
|
|
||||||
uint16_t lib_name_idx;
|
|
||||||
uint16_t sym_name_idx;
|
|
||||||
uintptr_t sym_addr;
|
|
||||||
uintptr_t new_addr;
|
|
||||||
} __attribute__((packed)) sh_recorder_record_hook_header_t;
|
|
||||||
// no body
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint64_t op : 8;
|
|
||||||
uint64_t error_number : 8;
|
|
||||||
uint64_t ts_ms : 48;
|
|
||||||
uintptr_t stub;
|
|
||||||
uint16_t caller_lib_name_idx;
|
|
||||||
} __attribute__((packed)) sh_recorder_record_unhook_header_t;
|
|
||||||
// no body
|
|
||||||
|
|
||||||
static int sh_recorder_add_str(const char *str, size_t str_len, uint16_t *str_idx) {
|
|
||||||
uint16_t idx = 0;
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sh_recorder_strings.lock);
|
|
||||||
|
|
||||||
// find in existing strings
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < sh_recorder_strings.sz) {
|
|
||||||
sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
|
|
||||||
if (header->str_len == str_len) {
|
|
||||||
void *tmp = (void *)((uintptr_t)sh_recorder_strings.ptr + i + sizeof(header->str_len));
|
|
||||||
if (0 == memcmp(tmp, str, str_len)) {
|
|
||||||
*str_idx = idx;
|
|
||||||
ok = true;
|
|
||||||
break; // OK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1);
|
|
||||||
idx++;
|
|
||||||
if (idx == UINT16_MAX) break; // failed
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert a new string
|
|
||||||
if (!ok && idx < UINT16_MAX) {
|
|
||||||
// append new string
|
|
||||||
sh_recorder_str_header_t header = {(uint16_t)str_len};
|
|
||||||
if (0 == sh_recorder_buf_append(&sh_recorder_strings, SH_RECORDER_STRINGS_BUF_EXPAND_STEP,
|
|
||||||
SH_RECORDER_STRINGS_BUF_MAX, &header, sizeof(header), str, str_len + 1)) {
|
|
||||||
*str_idx = idx;
|
|
||||||
ok = true; // OK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&sh_recorder_strings.lock);
|
|
||||||
|
|
||||||
return ok ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *sh_recorder_find_str(uint16_t idx) {
|
|
||||||
uint16_t cur_idx = 0;
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < sh_recorder_strings.sz && cur_idx < idx) {
|
|
||||||
sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
|
|
||||||
i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1);
|
|
||||||
cur_idx++;
|
|
||||||
}
|
|
||||||
if (cur_idx != idx) return "error";
|
|
||||||
|
|
||||||
sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i);
|
|
||||||
return (char *)((uintptr_t)header + sizeof(sh_recorder_str_header_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static long sh_recorder_tz = LONG_MAX;
|
|
||||||
|
|
||||||
static uint64_t sh_recorder_get_timestamp_ms(void) {
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
|
|
||||||
if (LONG_MAX == sh_recorder_tz) {
|
|
||||||
// localtime_r() will call getenv() without lock protection,
|
|
||||||
// and will crash when encountering concurrent setenv() calls.
|
|
||||||
// We really encountered.
|
|
||||||
sh_recorder_tz = 0;
|
|
||||||
// struct tm tm;
|
|
||||||
// if (NULL != localtime_r((time_t *)(&(tv.tv_sec)), &tm)) sh_recorder_tz = tm.tm_gmtoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t sh_recorder_format_timestamp_ms(uint64_t ts_ms, char *buf, size_t buf_len) {
|
|
||||||
time_t sec = (time_t)(ts_ms / 1000);
|
|
||||||
time_t msec = (time_t)(ts_ms % 1000);
|
|
||||||
|
|
||||||
struct tm tm;
|
|
||||||
sh_util_localtime_r(&sec, sh_recorder_tz, &tm);
|
|
||||||
|
|
||||||
return sh_util_snprintf(buf, buf_len, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%c%02ld:%02ld,",
|
|
||||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
|
|
||||||
msec, sh_recorder_tz < 0 ? '-' : '+', labs(sh_recorder_tz / 3600),
|
|
||||||
labs(sh_recorder_tz % 3600));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *sh_recorder_get_base_name(const char *lib_name) {
|
|
||||||
const char *p = strrchr(lib_name, '/');
|
|
||||||
if (NULL != p && '\0' != *(p + 1))
|
|
||||||
return p + 1;
|
|
||||||
else
|
|
||||||
return lib_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_recorder_get_base_name_by_addr_iterator(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
(void)size;
|
|
||||||
|
|
||||||
uintptr_t *pkg = (uintptr_t *)arg;
|
|
||||||
uintptr_t addr = *pkg++;
|
|
||||||
char *base_name = (char *)*pkg++;
|
|
||||||
size_t base_name_sz = (size_t)*pkg;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < info->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD != phdr->p_type) continue;
|
|
||||||
if (addr < (uintptr_t)(info->dlpi_addr + phdr->p_vaddr) ||
|
|
||||||
addr >= (uintptr_t)(info->dlpi_addr + phdr->p_vaddr + phdr->p_memsz))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// get lib_name from path_name
|
|
||||||
const char *p;
|
|
||||||
if (NULL == info->dlpi_name || '\0' == info->dlpi_name[0])
|
|
||||||
p = "unknown";
|
|
||||||
else {
|
|
||||||
p = strrchr(info->dlpi_name, '/');
|
|
||||||
if (NULL == p || '\0' == *(p + 1))
|
|
||||||
p = info->dlpi_name;
|
|
||||||
else
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(base_name, p, base_name_sz);
|
|
||||||
return 1; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // continue
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_recorder_get_base_name_by_addr(uintptr_t addr, char *base_name, size_t base_name_sz) {
|
|
||||||
base_name[0] = '\0';
|
|
||||||
uintptr_t pkg[3] = {addr, (uintptr_t)base_name, (uintptr_t)base_name_sz};
|
|
||||||
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('\0' == base_name[0]) strlcpy(base_name, "unknown", base_name_sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
|
|
||||||
const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
|
|
||||||
uintptr_t caller_addr) {
|
|
||||||
if (!sh_recorder_recordable) return 0;
|
|
||||||
if (sh_recorder_error) return -1;
|
|
||||||
|
|
||||||
// lib_name
|
|
||||||
if (NULL == lib_name) return -1;
|
|
||||||
lib_name = sh_recorder_get_base_name(lib_name);
|
|
||||||
size_t lib_name_len = strlen(lib_name);
|
|
||||||
if (0 == lib_name_len || lib_name_len > SH_RECORDER_LIB_NAME_MAX) return -1;
|
|
||||||
|
|
||||||
// sym_name
|
|
||||||
if (NULL == sym_name) return -1;
|
|
||||||
size_t sym_name_len = strlen(sym_name);
|
|
||||||
if (0 == sym_name_len || sym_name_len > SH_RECORDER_SYM_NAME_MAX) return -1;
|
|
||||||
|
|
||||||
// caller_lib_name
|
|
||||||
char caller_lib_name[SH_RECORDER_LIB_NAME_MAX];
|
|
||||||
sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name));
|
|
||||||
size_t caller_lib_name_len = strlen(caller_lib_name);
|
|
||||||
|
|
||||||
// add strings to strings-pool
|
|
||||||
uint16_t lib_name_idx, sym_name_idx, caller_lib_name_idx;
|
|
||||||
if (0 != sh_recorder_add_str(lib_name, lib_name_len, &lib_name_idx)) goto err;
|
|
||||||
if (0 != sh_recorder_add_str(sym_name, sym_name_len, &sym_name_idx)) goto err;
|
|
||||||
if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err;
|
|
||||||
|
|
||||||
// append new hook record
|
|
||||||
sh_recorder_record_hook_header_t header = {
|
|
||||||
is_hook_sym_addr ? SH_RECORDER_OP_HOOK_SYM_ADDR : SH_RECORDER_OP_HOOK_SYM_NAME,
|
|
||||||
(uint8_t)error_number,
|
|
||||||
sh_recorder_get_timestamp_ms(),
|
|
||||||
stub,
|
|
||||||
caller_lib_name_idx,
|
|
||||||
(uint8_t)backup_len,
|
|
||||||
lib_name_idx,
|
|
||||||
sym_name_idx,
|
|
||||||
sym_addr,
|
|
||||||
new_addr};
|
|
||||||
pthread_mutex_lock(&sh_recorder_records.lock);
|
|
||||||
int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP,
|
|
||||||
SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0);
|
|
||||||
pthread_mutex_unlock(&sh_recorder_records.lock);
|
|
||||||
if (0 != r) goto err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sh_recorder_error = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) {
|
|
||||||
if (!sh_recorder_recordable) return 0;
|
|
||||||
if (sh_recorder_error) return -1;
|
|
||||||
|
|
||||||
char caller_lib_name[SH_RECORDER_LIB_NAME_MAX];
|
|
||||||
sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name));
|
|
||||||
size_t caller_lib_name_len = strlen(caller_lib_name);
|
|
||||||
|
|
||||||
uint16_t caller_lib_name_idx;
|
|
||||||
if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err;
|
|
||||||
|
|
||||||
sh_recorder_record_unhook_header_t header = {SH_RECORDER_OP_UNHOOK, (uint8_t)error_number,
|
|
||||||
sh_recorder_get_timestamp_ms(), stub, caller_lib_name_idx};
|
|
||||||
pthread_mutex_lock(&sh_recorder_records.lock);
|
|
||||||
int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP,
|
|
||||||
SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0);
|
|
||||||
pthread_mutex_unlock(&sh_recorder_records.lock);
|
|
||||||
if (0 != r) goto err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sh_recorder_error = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *sh_recorder_get_op_name(uint8_t op) {
|
|
||||||
switch (op) {
|
|
||||||
case SH_RECORDER_OP_HOOK_SYM_ADDR:
|
|
||||||
return "hook_sym_addr";
|
|
||||||
case SH_RECORDER_OP_HOOK_SYM_NAME:
|
|
||||||
return "hook_sym_name";
|
|
||||||
case SH_RECORDER_OP_UNHOOK:
|
|
||||||
return "unhook";
|
|
||||||
default:
|
|
||||||
return "error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_recorder_output(char **str, int fd, uint32_t item_flags) {
|
|
||||||
if (NULL == sh_recorder_records.ptr || 0 == sh_recorder_records.sz) return;
|
|
||||||
|
|
||||||
sh_recorder_buf_t output = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER};
|
|
||||||
|
|
||||||
pthread_mutex_lock(&sh_recorder_records.lock);
|
|
||||||
pthread_mutex_lock(&sh_recorder_strings.lock);
|
|
||||||
|
|
||||||
char line[SH_RECORDER_LIB_NAME_MAX * 2 + SH_RECORDER_SYM_NAME_MAX + 256];
|
|
||||||
size_t line_sz;
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < sh_recorder_records.sz) {
|
|
||||||
line_sz = 0;
|
|
||||||
sh_recorder_record_hook_header_t *header =
|
|
||||||
(sh_recorder_record_hook_header_t *)((uintptr_t)sh_recorder_records.ptr + i);
|
|
||||||
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP)
|
|
||||||
line_sz += sh_recorder_format_timestamp_ms(header->ts_ms, line + line_sz, sizeof(line) - line_sz);
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
|
|
||||||
sh_recorder_find_str(header->caller_lib_name_idx));
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_OP)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
|
|
||||||
sh_recorder_get_op_name(header->op));
|
|
||||||
if ((item_flags & SHADOWHOOK_RECORD_ITEM_LIB_NAME) && header->op != SH_RECORDER_OP_UNHOOK)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
|
|
||||||
sh_recorder_find_str(header->lib_name_idx));
|
|
||||||
if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_NAME) && header->op != SH_RECORDER_OP_UNHOOK)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,",
|
|
||||||
sh_recorder_find_str(header->sym_name_idx));
|
|
||||||
if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_ADDR) && header->op != SH_RECORDER_OP_UNHOOK)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->sym_addr);
|
|
||||||
if ((item_flags & SHADOWHOOK_RECORD_ITEM_NEW_ADDR) && header->op != SH_RECORDER_OP_UNHOOK)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->new_addr);
|
|
||||||
if ((item_flags & SHADOWHOOK_RECORD_ITEM_BACKUP_LEN) && header->op != SH_RECORDER_OP_UNHOOK)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->backup_len);
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_ERRNO)
|
|
||||||
line_sz +=
|
|
||||||
sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->error_number);
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_STUB)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->stub);
|
|
||||||
line[line_sz - 1] = '\n';
|
|
||||||
|
|
||||||
if (NULL != str) {
|
|
||||||
// append to string
|
|
||||||
if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
|
|
||||||
line, line_sz, NULL, 0)) {
|
|
||||||
sh_recorder_buf_free(&output);
|
|
||||||
break; // failed
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// write to FD
|
|
||||||
if (0 != sh_util_write(fd, line, line_sz)) break; // failed
|
|
||||||
}
|
|
||||||
|
|
||||||
i += (SH_RECORDER_OP_UNHOOK == header->op ? sizeof(sh_recorder_record_unhook_header_t)
|
|
||||||
: sizeof(sh_recorder_record_hook_header_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&sh_recorder_strings.lock);
|
|
||||||
pthread_mutex_unlock(&sh_recorder_records.lock);
|
|
||||||
|
|
||||||
// error message
|
|
||||||
if (sh_recorder_error) {
|
|
||||||
line_sz = 0;
|
|
||||||
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "9999-99-99T00:00:00.000+00:00,");
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
|
|
||||||
if (item_flags & SHADOWHOOK_RECORD_ITEM_OP)
|
|
||||||
line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
|
|
||||||
|
|
||||||
if (0 == line_sz) line_sz = sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,");
|
|
||||||
|
|
||||||
line[line_sz - 1] = '\n';
|
|
||||||
|
|
||||||
if (NULL != str) {
|
|
||||||
// append to string
|
|
||||||
if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
|
|
||||||
line, line_sz, NULL, 0)) {
|
|
||||||
sh_recorder_buf_free(&output);
|
|
||||||
return; // failed
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// write to FD
|
|
||||||
if (0 != sh_util_write(fd, line, line_sz)) return; // failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return string
|
|
||||||
if (NULL != str) {
|
|
||||||
if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX,
|
|
||||||
"", 1, NULL, 0)) {
|
|
||||||
sh_recorder_buf_free(&output);
|
|
||||||
return; // failed
|
|
||||||
}
|
|
||||||
*str = output.ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *sh_recorder_get(uint32_t item_flags) {
|
|
||||||
if (!sh_recorder_recordable) return NULL;
|
|
||||||
if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return NULL;
|
|
||||||
|
|
||||||
char *str = NULL;
|
|
||||||
sh_recorder_output(&str, -1, item_flags);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_recorder_dump(int fd, uint32_t item_flags) {
|
|
||||||
if (!sh_recorder_recordable) return;
|
|
||||||
if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return;
|
|
||||||
if (fd < 0) return;
|
|
||||||
sh_recorder_output(NULL, fd, item_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
bool sh_recorder_get_recordable(void) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_recorder_set_recordable(bool recordable) {
|
|
||||||
(void)recordable;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
|
|
||||||
const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
|
|
||||||
uintptr_t caller_addr) {
|
|
||||||
(void)error_number, (void)is_hook_sym_addr, (void)sym_addr, (void)lib_name, (void)sym_name, (void)new_addr,
|
|
||||||
(void)backup_len, (void)stub, (void)caller_addr;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) {
|
|
||||||
(void)error_number, (void)stub, (void)caller_addr;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *sh_recorder_get(uint32_t item_flags) {
|
|
||||||
(void)item_flags;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_recorder_dump(int fd, uint32_t item_flags) {
|
|
||||||
(void)fd, (void)item_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
bool sh_recorder_get_recordable(void);
|
|
||||||
void sh_recorder_set_recordable(bool recordable);
|
|
||||||
|
|
||||||
int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name,
|
|
||||||
const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub,
|
|
||||||
uintptr_t caller_addr);
|
|
||||||
int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr);
|
|
||||||
|
|
||||||
char *sh_recorder_get(uint32_t item_flags);
|
|
||||||
void sh_recorder_dump(int fd, uint32_t item_flags);
|
|
@ -1,133 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_safe.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
|
||||||
#pragma clang diagnostic ignored "-Wvariadic-macros"
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
|
||||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
|
||||||
#pragma clang diagnostic ignored "-Wpacked"
|
|
||||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
|
||||||
#include "linux_syscall_support.h"
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
#define SH_SAFE_IDX_PTHREAD_GETSPECIFIC 0
|
|
||||||
#define SH_SAFE_IDX_PTHREAD_SETSPECIFIC 1
|
|
||||||
#define SH_SAFE_IDX_ABORT 2
|
|
||||||
|
|
||||||
#define SH_SAFE_IDX_SZ 3
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t target_addr;
|
|
||||||
uintptr_t orig_addr;
|
|
||||||
} sh_safe_addr_t;
|
|
||||||
static sh_safe_addr_t sh_safe_addrs[SH_SAFE_IDX_SZ];
|
|
||||||
static int sh_safe_api_level;
|
|
||||||
|
|
||||||
static int sh_safe_init_func(void *handle, const char *symbol, size_t idx) {
|
|
||||||
sh_safe_addrs[idx].target_addr = (uintptr_t)(xdl_sym(handle, symbol, NULL));
|
|
||||||
if (__predict_false(0 == sh_safe_addrs[idx].target_addr)) return -1;
|
|
||||||
sh_safe_addrs[idx].orig_addr = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_safe_init(void) {
|
|
||||||
sh_safe_api_level = sh_util_get_api_level();
|
|
||||||
|
|
||||||
void *handle = xdl_open("libc.so", XDL_DEFAULT);
|
|
||||||
if (NULL == handle) return -1;
|
|
||||||
|
|
||||||
int r = -1;
|
|
||||||
if (__predict_false(0 != sh_safe_init_func(handle, "pthread_getspecific", SH_SAFE_IDX_PTHREAD_GETSPECIFIC)))
|
|
||||||
goto end;
|
|
||||||
if (__predict_false(0 != sh_safe_init_func(handle, "pthread_setspecific", SH_SAFE_IDX_PTHREAD_SETSPECIFIC)))
|
|
||||||
goto end;
|
|
||||||
if (__predict_false(0 != sh_safe_init_func(handle, "abort", SH_SAFE_IDX_ABORT))) goto end;
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
end:
|
|
||||||
xdl_close(handle);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr) {
|
|
||||||
for (size_t i = 0; i < SH_SAFE_IDX_SZ; i++) {
|
|
||||||
if (sh_safe_addrs[i].target_addr == target_addr) {
|
|
||||||
return &sh_safe_addrs[i].orig_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t sh_safe_get_orig_addr(size_t idx) {
|
|
||||||
sh_safe_addr_t *addr = &sh_safe_addrs[idx];
|
|
||||||
return 0 != addr->orig_addr ? addr->orig_addr : addr->target_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *sh_safe_pthread_getspecific(pthread_key_t key) {
|
|
||||||
uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_GETSPECIFIC);
|
|
||||||
return ((void *(*)(pthread_key_t))addr)(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_safe_pthread_setspecific(pthread_key_t key, const void *value) {
|
|
||||||
if (sh_safe_api_level >= __ANDROID_API_M__) {
|
|
||||||
uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_SETSPECIFIC);
|
|
||||||
return ((int (*)(pthread_key_t, const void *))addr)(key, value);
|
|
||||||
} else {
|
|
||||||
// Before Android M, pthread_setspecific() will call pthread_mutex_lock() and
|
|
||||||
// pthread_mutex_unlock(). So if we use pthread_setspecific() in hub's trampo,
|
|
||||||
// we will NOT be able to hook pthread_mutex_lock() and pthread_mutex_unlock().
|
|
||||||
void **tls;
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
__asm__("mrs %0, tpidr_el0" : "=r"(tls));
|
|
||||||
#elif defined(__arm__)
|
|
||||||
__asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(tls));
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wcast-qual"
|
|
||||||
tls[key] = (void *)value;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_safe_abort(void) {
|
|
||||||
uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_ABORT);
|
|
||||||
((void (*)(void))addr)();
|
|
||||||
}
|
|
||||||
|
|
||||||
void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
|
||||||
return sys_mmap(addr, length, prot, flags, fd, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
|
||||||
unsigned long arg5) {
|
|
||||||
return sys_prctl(option, arg2, arg3, arg4, arg5);
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
int sh_safe_init(void);
|
|
||||||
uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr);
|
|
||||||
|
|
||||||
void *sh_safe_pthread_getspecific(pthread_key_t key);
|
|
||||||
int sh_safe_pthread_setspecific(pthread_key_t key, const void *value);
|
|
||||||
void sh_safe_abort(void);
|
|
||||||
|
|
||||||
void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
|
||||||
int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
|
@ -1,343 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_switch.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "sh_config.h"
|
|
||||||
#include "sh_errno.h"
|
|
||||||
#include "sh_hub.h"
|
|
||||||
#include "sh_inst.h"
|
|
||||||
#include "sh_linker.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_safe.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "tree.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
// switch
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct sh_switch {
|
|
||||||
sh_inst_t inst; // align 16
|
|
||||||
uintptr_t target_addr;
|
|
||||||
sh_hub_t *hub;
|
|
||||||
RB_ENTRY(sh_switch) link;
|
|
||||||
} sh_switch_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// switch tree
|
|
||||||
static __inline__ int sh_switch_cmp(sh_switch_t *a, sh_switch_t *b) {
|
|
||||||
if (a->target_addr == b->target_addr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return a->target_addr > b->target_addr ? 1 : -1;
|
|
||||||
}
|
|
||||||
typedef RB_HEAD(sh_switch_tree, sh_switch) sh_switch_tree_t;
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wunused-function"
|
|
||||||
RB_GENERATE_STATIC(sh_switch_tree, sh_switch, link, sh_switch_cmp)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// switch tree object
|
|
||||||
static sh_switch_tree_t sh_switches = RB_INITIALIZER(&sh_switches);
|
|
||||||
static pthread_rwlock_t sh_switches_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
||||||
|
|
||||||
static sh_switch_t *sh_switch_find(uintptr_t target_addr) {
|
|
||||||
sh_switch_t key = {.target_addr = target_addr};
|
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&sh_switches_lock);
|
|
||||||
sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock);
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_create(sh_switch_t **self, uintptr_t target_addr, uintptr_t *hub_trampo) {
|
|
||||||
*self = memalign(16, sizeof(sh_switch_t));
|
|
||||||
if (NULL == *self) return SHADOWHOOK_ERRNO_OOM;
|
|
||||||
|
|
||||||
memset(&(*self)->inst, 0, sizeof((*self)->inst));
|
|
||||||
(*self)->target_addr = target_addr;
|
|
||||||
(*self)->hub = NULL;
|
|
||||||
|
|
||||||
if (NULL != hub_trampo) {
|
|
||||||
if (NULL == ((*self)->hub = sh_hub_create(target_addr, hub_trampo))) {
|
|
||||||
free(*self);
|
|
||||||
return SHADOWHOOK_ERRNO_HUB_CREAT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_switch_destroy(sh_switch_t *self, bool hub_with_delay) {
|
|
||||||
if (NULL != self->hub) sh_hub_destroy(self->hub, hub_with_delay);
|
|
||||||
free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_switch_dump_enter(sh_switch_t *self) {
|
|
||||||
#ifdef SH_CONFIG_DEBUG
|
|
||||||
char buf[1024];
|
|
||||||
size_t len = 0;
|
|
||||||
size_t zero = 0;
|
|
||||||
for (size_t i = 0; i < 128; i++) {
|
|
||||||
uint16_t inst = ((uint16_t *)(self->inst.enter_addr))[i];
|
|
||||||
zero = (0 == inst ? zero + 1 : 0);
|
|
||||||
if (zero > 4) break;
|
|
||||||
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%04" PRIx16 " ", inst);
|
|
||||||
}
|
|
||||||
SH_LOG_DEBUG("switch: enter < %s>", buf);
|
|
||||||
#else
|
|
||||||
(void)self;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_hook_unique(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
size_t *backup_len, xdl_info_t *dlinfo) {
|
|
||||||
sh_switch_t *self = sh_switch_find(target_addr);
|
|
||||||
if (NULL != self) return SHADOWHOOK_ERRNO_HOOK_DUP;
|
|
||||||
|
|
||||||
// alloc new switch
|
|
||||||
int r;
|
|
||||||
if (0 != (r = sh_switch_create(&self, target_addr, NULL))) return r;
|
|
||||||
|
|
||||||
sh_switch_t *useless = NULL;
|
|
||||||
pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start
|
|
||||||
|
|
||||||
// insert new switch to switch-tree
|
|
||||||
if (NULL != RB_INSERT(sh_switch_tree, &sh_switches, self)) {
|
|
||||||
useless = self;
|
|
||||||
r = SHADOWHOOK_ERRNO_HOOK_DUP;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do hook
|
|
||||||
if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, NULL))) {
|
|
||||||
RB_REMOVE(sh_switch_tree, &sh_switches, self);
|
|
||||||
useless = self;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
*backup_len = self->inst.backup_len;
|
|
||||||
sh_switch_dump_enter(self);
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end
|
|
||||||
if (NULL != useless) sh_switch_destroy(useless, false);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_hook_shared(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
size_t *backup_len, xdl_info_t *dlinfo) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&sh_switches_lock); // SYNC(read) - start
|
|
||||||
sh_switch_t key = {.target_addr = target_addr};
|
|
||||||
sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
|
|
||||||
if (NULL != self) // already exists
|
|
||||||
{
|
|
||||||
// add an new proxy to hub
|
|
||||||
if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub);
|
|
||||||
r = sh_hub_add_proxy(self->hub, new_addr);
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC(read) - end
|
|
||||||
|
|
||||||
*backup_len = self->inst.backup_len;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC(read) - end
|
|
||||||
|
|
||||||
// first hook for this target_addr
|
|
||||||
|
|
||||||
// alloc new switch
|
|
||||||
uintptr_t hub_trampo;
|
|
||||||
if (0 != (r = sh_switch_create(&self, target_addr, &hub_trampo))) return r;
|
|
||||||
|
|
||||||
sh_switch_t *useless = NULL;
|
|
||||||
pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start
|
|
||||||
|
|
||||||
// insert new switch to switch-tree
|
|
||||||
sh_switch_t *exists;
|
|
||||||
if (NULL != (exists = RB_INSERT(sh_switch_tree, &sh_switches, self))) {
|
|
||||||
// already exists
|
|
||||||
useless = self;
|
|
||||||
if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(exists->hub);
|
|
||||||
r = sh_hub_add_proxy(exists->hub, new_addr);
|
|
||||||
*backup_len = exists->inst.backup_len;
|
|
||||||
} else {
|
|
||||||
// do hook
|
|
||||||
uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr);
|
|
||||||
if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, hub_trampo,
|
|
||||||
sh_hub_get_orig_addr_addr(self->hub), safe_orig_addr_addr))) {
|
|
||||||
RB_REMOVE(sh_switch_tree, &sh_switches, self);
|
|
||||||
useless = self;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
*backup_len = self->inst.backup_len;
|
|
||||||
sh_switch_dump_enter(self);
|
|
||||||
|
|
||||||
// return original-address
|
|
||||||
if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub);
|
|
||||||
|
|
||||||
// add proxy to hub
|
|
||||||
if (0 != (r = sh_hub_add_proxy(self->hub, new_addr))) {
|
|
||||||
sh_inst_unhook(&self->inst, target_addr);
|
|
||||||
*backup_len = 0;
|
|
||||||
RB_REMOVE(sh_switch_tree, &sh_switches, self);
|
|
||||||
useless = self;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end
|
|
||||||
if (NULL != useless) sh_switch_destroy(useless, false);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
|
|
||||||
xdl_info_t *dlinfo) {
|
|
||||||
int r;
|
|
||||||
if (SHADOWHOOK_IS_UNIQUE_MODE)
|
|
||||||
r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo);
|
|
||||||
else
|
|
||||||
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
|
|
||||||
|
|
||||||
if (0 == r)
|
|
||||||
SH_LOG_INFO("switch: hook in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
|
|
||||||
SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_hook_unique_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
size_t *backup_len, xdl_info_t *dlinfo) {
|
|
||||||
pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start
|
|
||||||
|
|
||||||
// do hook
|
|
||||||
sh_inst_t inst;
|
|
||||||
int r = sh_inst_hook(&inst, target_addr, dlinfo, new_addr, orig_addr, NULL);
|
|
||||||
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end
|
|
||||||
|
|
||||||
*backup_len = inst.backup_len;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
size_t *backup_len, xdl_info_t *dlinfo) {
|
|
||||||
int r;
|
|
||||||
if (SHADOWHOOK_IS_UNIQUE_MODE)
|
|
||||||
r = sh_switch_hook_unique_invisible(target_addr, new_addr, orig_addr, backup_len, dlinfo);
|
|
||||||
else
|
|
||||||
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
|
|
||||||
|
|
||||||
if (0 == r)
|
|
||||||
SH_LOG_INFO("switch: hook(invisible) in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
|
|
||||||
SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_unhook_unique(uintptr_t target_addr) {
|
|
||||||
int r;
|
|
||||||
sh_switch_t *useless = NULL;
|
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start
|
|
||||||
|
|
||||||
sh_switch_t key = {.target_addr = target_addr};
|
|
||||||
sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
|
|
||||||
if (NULL == self) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
r = sh_inst_unhook(&self->inst, target_addr);
|
|
||||||
RB_REMOVE(sh_switch_tree, &sh_switches, self);
|
|
||||||
useless = self;
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end
|
|
||||||
if (NULL != useless) sh_switch_destroy(useless, false);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_switch_unhook_shared(uintptr_t target_addr, uintptr_t new_addr) {
|
|
||||||
int r;
|
|
||||||
sh_switch_t *useless = NULL;
|
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start
|
|
||||||
|
|
||||||
sh_switch_t key = {.target_addr = target_addr};
|
|
||||||
sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key);
|
|
||||||
if (NULL == self) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete proxy in hub
|
|
||||||
bool have_enabled_proxy;
|
|
||||||
if (0 != sh_hub_del_proxy(self->hub, new_addr, &have_enabled_proxy)) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
// unhook inst, remove current switch from switch-tree
|
|
||||||
if (!have_enabled_proxy) {
|
|
||||||
r = sh_inst_unhook(&self->inst, target_addr);
|
|
||||||
|
|
||||||
uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr);
|
|
||||||
if (NULL != safe_orig_addr_addr) __atomic_store_n(safe_orig_addr_addr, 0, __ATOMIC_SEQ_CST);
|
|
||||||
|
|
||||||
RB_REMOVE(sh_switch_tree, &sh_switches, self);
|
|
||||||
useless = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end
|
|
||||||
if (NULL != useless) sh_switch_destroy(useless, true);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr) {
|
|
||||||
int r;
|
|
||||||
if (SHADOWHOOK_IS_UNIQUE_MODE) {
|
|
||||||
r = sh_switch_unhook_unique(target_addr);
|
|
||||||
if (0 == r) SH_LOG_INFO("switch: unhook in UNIQUE mode OK: target_addr %" PRIxPTR, target_addr);
|
|
||||||
} else {
|
|
||||||
r = sh_switch_unhook_shared(target_addr, new_addr);
|
|
||||||
if (0 == r)
|
|
||||||
SH_LOG_INFO("switch: unhook in SHARED mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
|
|
||||||
target_addr, new_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
|
|
||||||
xdl_info_t *dlinfo);
|
|
||||||
int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr);
|
|
||||||
|
|
||||||
int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
size_t *backup_len, xdl_info_t *dlinfo);
|
|
@ -1,333 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "sh_task.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/eventfd.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
#include "sh_config.h"
|
|
||||||
#include "sh_errno.h"
|
|
||||||
#include "sh_linker.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_recorder.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_switch.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "shadowhook.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#define SH_TASK_THREAD_NAME "shadowhook-task"
|
|
||||||
|
|
||||||
// task
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
struct sh_task {
|
|
||||||
char *lib_name;
|
|
||||||
char *sym_name;
|
|
||||||
uintptr_t target_addr;
|
|
||||||
uintptr_t new_addr;
|
|
||||||
uintptr_t *orig_addr;
|
|
||||||
shadowhook_hooked_t hooked;
|
|
||||||
void *hooked_arg;
|
|
||||||
uintptr_t caller_addr;
|
|
||||||
bool finished;
|
|
||||||
bool error;
|
|
||||||
bool ignore_symbol_check;
|
|
||||||
TAILQ_ENTRY(sh_task, ) link;
|
|
||||||
};
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// task queue
|
|
||||||
typedef TAILQ_HEAD(sh_task_queue, sh_task, ) sh_task_queue_t;
|
|
||||||
|
|
||||||
// task queue object
|
|
||||||
static sh_task_queue_t sh_tasks = TAILQ_HEAD_INITIALIZER(sh_tasks);
|
|
||||||
static pthread_rwlock_t sh_tasks_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
||||||
static int sh_tasks_unfinished_cnt = 0;
|
|
||||||
|
|
||||||
static int sh_task_eventfd;
|
|
||||||
static int thread_started_result = SHADOWHOOK_ERRNO_MONITOR_THREAD;
|
|
||||||
|
|
||||||
sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
bool ignore_symbol_check, uintptr_t caller_addr) {
|
|
||||||
sh_task_t *self = malloc(sizeof(sh_task_t));
|
|
||||||
if (NULL == self) return NULL;
|
|
||||||
self->lib_name = NULL;
|
|
||||||
self->sym_name = NULL;
|
|
||||||
self->target_addr = target_addr;
|
|
||||||
self->new_addr = new_addr;
|
|
||||||
self->orig_addr = orig_addr;
|
|
||||||
self->hooked = NULL;
|
|
||||||
self->hooked_arg = NULL;
|
|
||||||
self->caller_addr = caller_addr;
|
|
||||||
self->finished = false;
|
|
||||||
self->error = false;
|
|
||||||
self->ignore_symbol_check = ignore_symbol_check;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
|
|
||||||
uintptr_t caller_addr) {
|
|
||||||
sh_task_t *self = malloc(sizeof(sh_task_t));
|
|
||||||
if (NULL == self) return NULL;
|
|
||||||
|
|
||||||
if (NULL == (self->lib_name = strdup(lib_name))) goto err;
|
|
||||||
if (NULL == (self->sym_name = strdup(sym_name))) goto err;
|
|
||||||
self->target_addr = 0;
|
|
||||||
self->new_addr = new_addr;
|
|
||||||
self->orig_addr = orig_addr;
|
|
||||||
self->hooked = hooked;
|
|
||||||
self->hooked_arg = hooked_arg;
|
|
||||||
self->caller_addr = caller_addr;
|
|
||||||
self->finished = false;
|
|
||||||
self->error = false;
|
|
||||||
self->ignore_symbol_check = false;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
|
|
||||||
err:
|
|
||||||
if (NULL != self->lib_name) free(self->lib_name);
|
|
||||||
if (NULL != self->sym_name) free(self->sym_name);
|
|
||||||
free(self);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sh_task_destroy(sh_task_t *self) {
|
|
||||||
if (NULL != self->lib_name) free(self->lib_name);
|
|
||||||
if (NULL != self->sym_name) free(self->sym_name);
|
|
||||||
free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_task_do_callback(sh_task_t *self, int error_number) {
|
|
||||||
if (NULL != self->hooked)
|
|
||||||
self->hooked(error_number, self->lib_name, self->sym_name, (void *)self->target_addr,
|
|
||||||
(void *)self->new_addr, self->orig_addr, self->hooked_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_task_hook_pending(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
(void)size, (void)arg;
|
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&sh_tasks_lock);
|
|
||||||
|
|
||||||
sh_task_t *task;
|
|
||||||
TAILQ_FOREACH(task, &sh_tasks, link) {
|
|
||||||
if (task->finished) continue;
|
|
||||||
if ('/' == info->dlpi_name[0] && NULL == strstr(info->dlpi_name, task->lib_name)) continue;
|
|
||||||
if ('/' != info->dlpi_name[0] && NULL == strstr(task->lib_name, info->dlpi_name)) continue;
|
|
||||||
|
|
||||||
xdl_info_t dlinfo;
|
|
||||||
char real_lib_name[512];
|
|
||||||
int r = sh_linker_get_dlinfo_by_sym_name(task->lib_name, task->sym_name, &dlinfo, real_lib_name,
|
|
||||||
sizeof(real_lib_name));
|
|
||||||
task->target_addr = (uintptr_t)dlinfo.dli_saddr;
|
|
||||||
if (SHADOWHOOK_ERRNO_PENDING != r) {
|
|
||||||
size_t backup_len = 0;
|
|
||||||
if (0 == r) {
|
|
||||||
r = sh_switch_hook(task->target_addr, task->new_addr, task->orig_addr, &backup_len, &dlinfo);
|
|
||||||
if (0 != r) task->error = true;
|
|
||||||
} else {
|
|
||||||
strlcpy(real_lib_name, task->lib_name, sizeof(real_lib_name));
|
|
||||||
task->error = true;
|
|
||||||
}
|
|
||||||
sh_recorder_add_hook(r, false, task->target_addr, real_lib_name, task->sym_name, task->new_addr,
|
|
||||||
backup_len, (uintptr_t)task, task->caller_addr);
|
|
||||||
task->finished = true;
|
|
||||||
sh_task_do_callback(task, r);
|
|
||||||
if (0 == __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST)) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_rwlock_unlock(&sh_tasks_lock);
|
|
||||||
|
|
||||||
return __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-statement-expression"
|
|
||||||
|
|
||||||
static void sh_task_post_dlopen_callback(void *arg) {
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
if (0 == thread_started_result && __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0) {
|
|
||||||
uint64_t ev_val = 1;
|
|
||||||
SH_UTIL_TEMP_FAILURE_RETRY(write(sh_task_eventfd, &ev_val, sizeof(ev_val)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__noreturn static void *sh_task_thread_func(void *arg) {
|
|
||||||
(void)arg;
|
|
||||||
pthread_t thread = pthread_self();
|
|
||||||
pthread_setname_np(thread, SH_TASK_THREAD_NAME);
|
|
||||||
pthread_detach(thread);
|
|
||||||
|
|
||||||
struct pollfd ev = {.fd = sh_task_eventfd, .events = POLLIN, .revents = 0};
|
|
||||||
while (1) {
|
|
||||||
int n = SH_UTIL_TEMP_FAILURE_RETRY(poll(&ev, 1, -1));
|
|
||||||
if (n < 0) {
|
|
||||||
sleep(1);
|
|
||||||
continue;
|
|
||||||
} else if (n > 0) {
|
|
||||||
uint64_t ev_val;
|
|
||||||
SH_UTIL_TEMP_FAILURE_RETRY(read(sh_task_eventfd, &ev_val, sizeof(ev_val)));
|
|
||||||
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
SH_LOG_WARN("task: dliterate crashed");
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
static int sh_task_start_monitor(bool start_thread) {
|
|
||||||
static bool thread_started = false;
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
pthread_t thread;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
// hook linker dlopen()
|
|
||||||
if (0 != (r = sh_linker_hook_dlopen(sh_task_post_dlopen_callback, NULL))) return r;
|
|
||||||
|
|
||||||
if (!start_thread) return 0;
|
|
||||||
|
|
||||||
// start thread
|
|
||||||
if (thread_started) return thread_started_result;
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (thread_started) goto end;
|
|
||||||
|
|
||||||
if (0 > (sh_task_eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC))) goto end;
|
|
||||||
if (0 != pthread_create(&thread, NULL, &sh_task_thread_func, NULL)) goto end;
|
|
||||||
|
|
||||||
// OK
|
|
||||||
thread_started_result = 0;
|
|
||||||
|
|
||||||
end:
|
|
||||||
thread_started = true;
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
SH_LOG_INFO("task: start monitor %s, return: %d", 0 == thread_started_result ? "OK" : "FAILED",
|
|
||||||
thread_started_result);
|
|
||||||
return thread_started_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_task_hook(sh_task_t *self) {
|
|
||||||
int r;
|
|
||||||
bool is_hook_sym_addr = true;
|
|
||||||
char real_lib_name[512] = "unknown";
|
|
||||||
char real_sym_name[1024] = "unknown";
|
|
||||||
size_t backup_len = 0;
|
|
||||||
|
|
||||||
// find target-address by library-name and symbol-name
|
|
||||||
xdl_info_t dlinfo;
|
|
||||||
memset(&dlinfo, 0, sizeof(xdl_info_t));
|
|
||||||
if (0 == self->target_addr) {
|
|
||||||
is_hook_sym_addr = false;
|
|
||||||
strlcpy(real_lib_name, self->lib_name, sizeof(real_lib_name));
|
|
||||||
strlcpy(real_sym_name, self->sym_name, sizeof(real_sym_name));
|
|
||||||
r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name,
|
|
||||||
sizeof(real_lib_name));
|
|
||||||
if (SHADOWHOOK_ERRNO_PENDING == r) {
|
|
||||||
// we need to start monitor linker dlopen for handle the pending task
|
|
||||||
if (0 != (r = sh_task_start_monitor(true))) goto end;
|
|
||||||
r = SHADOWHOOK_ERRNO_PENDING;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (0 != r) goto end; // error
|
|
||||||
self->target_addr = (uintptr_t)dlinfo.dli_saddr; // OK
|
|
||||||
} else {
|
|
||||||
r = sh_linker_get_dlinfo_by_addr((void *)self->target_addr, &dlinfo, real_lib_name, sizeof(real_lib_name),
|
|
||||||
real_sym_name, sizeof(real_sym_name), self->ignore_symbol_check);
|
|
||||||
if (0 != r) goto end; // error
|
|
||||||
}
|
|
||||||
|
|
||||||
// In UNIQUE mode, if external users are hooking the linker dlopen() or do_dlopen(),
|
|
||||||
// we MUST hook this method with invisible for ourself first.
|
|
||||||
if (sh_linker_need_to_hook_dlopen(self->target_addr)) {
|
|
||||||
SH_LOG_INFO("task: hook dlopen/do_dlopen internal. target-address %" PRIxPTR, self->target_addr);
|
|
||||||
if (0 != (r = sh_task_start_monitor(false))) goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hook by target-address
|
|
||||||
r = sh_switch_hook(self->target_addr, self->new_addr, self->orig_addr, &backup_len, &dlinfo);
|
|
||||||
self->finished = true;
|
|
||||||
|
|
||||||
end:
|
|
||||||
if (0 == r || SHADOWHOOK_ERRNO_PENDING == r) // "PENDING" is NOT an error
|
|
||||||
{
|
|
||||||
pthread_rwlock_wrlock(&sh_tasks_lock);
|
|
||||||
TAILQ_INSERT_TAIL(&sh_tasks, self, link);
|
|
||||||
if (!self->finished) __atomic_add_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST);
|
|
||||||
pthread_rwlock_unlock(&sh_tasks_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// record
|
|
||||||
sh_recorder_add_hook(r, is_hook_sym_addr, self->target_addr, real_lib_name, real_sym_name, self->new_addr,
|
|
||||||
backup_len, (uintptr_t)self, self->caller_addr);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr) {
|
|
||||||
pthread_rwlock_wrlock(&sh_tasks_lock);
|
|
||||||
TAILQ_REMOVE(&sh_tasks, self, link);
|
|
||||||
if (!self->finished) __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST);
|
|
||||||
pthread_rwlock_unlock(&sh_tasks_lock);
|
|
||||||
|
|
||||||
// check task status
|
|
||||||
int r;
|
|
||||||
if (self->error) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (!self->finished) {
|
|
||||||
r = SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do unhook
|
|
||||||
r = sh_switch_unhook(self->target_addr, self->new_addr);
|
|
||||||
|
|
||||||
end:
|
|
||||||
// record
|
|
||||||
sh_recorder_add_unhook(r, (uintptr_t)self, caller_addr);
|
|
||||||
return r;
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
typedef struct sh_task sh_task_t;
|
|
||||||
|
|
||||||
sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
|
|
||||||
bool ignore_symbol_check, uintptr_t caller_addr);
|
|
||||||
sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr,
|
|
||||||
uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
|
|
||||||
uintptr_t caller_addr);
|
|
||||||
void sh_task_destroy(sh_task_t *self);
|
|
||||||
|
|
||||||
int sh_task_hook(sh_task_t *self);
|
|
||||||
int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr);
|
|
@ -1,328 +0,0 @@
|
|||||||
// Copyright (c) 2021-2024 ByteDance Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
|
|
||||||
|
|
||||||
#include "shadowhook.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "bytesig.h"
|
|
||||||
#include "sh_enter.h"
|
|
||||||
#include "sh_errno.h"
|
|
||||||
#include "sh_exit.h"
|
|
||||||
#include "sh_hub.h"
|
|
||||||
#include "sh_linker.h"
|
|
||||||
#include "sh_log.h"
|
|
||||||
#include "sh_recorder.h"
|
|
||||||
#include "sh_safe.h"
|
|
||||||
#include "sh_sig.h"
|
|
||||||
#include "sh_switch.h"
|
|
||||||
#include "sh_task.h"
|
|
||||||
#include "sh_util.h"
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#define GOTO_ERR(errnum) \
|
|
||||||
do { \
|
|
||||||
r = errnum; \
|
|
||||||
goto err; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static int shadowhook_init_errno = SHADOWHOOK_ERRNO_UNINIT;
|
|
||||||
static shadowhook_mode_t shadowhook_mode = SHADOWHOOK_MODE_SHARED;
|
|
||||||
|
|
||||||
const char *shadowhook_get_version(void) {
|
|
||||||
return "shadowhook version " SHADOWHOOK_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
int shadowhook_init(shadowhook_mode_t mode, bool debuggable) {
|
|
||||||
bool do_init = false;
|
|
||||||
|
|
||||||
if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) {
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) {
|
|
||||||
do_init = true;
|
|
||||||
shadowhook_mode = mode;
|
|
||||||
sh_log_set_debuggable(debuggable);
|
|
||||||
|
|
||||||
#define GOTO_END(errnum) \
|
|
||||||
do { \
|
|
||||||
shadowhook_init_errno = errnum; \
|
|
||||||
goto end; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
if (__predict_false(0 != sh_errno_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ERRNO);
|
|
||||||
if (__predict_false(0 != bytesig_init(SIGSEGV))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGSEGV);
|
|
||||||
if (__predict_false(0 != bytesig_init(SIGBUS))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGBUS);
|
|
||||||
if (__predict_false(0 != sh_enter_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ENTER);
|
|
||||||
sh_exit_init();
|
|
||||||
if (SHADOWHOOK_MODE_SHARED == shadowhook_mode) {
|
|
||||||
if (__predict_false(0 != sh_safe_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_SAFE);
|
|
||||||
if (__predict_false(0 != sh_hub_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_HUB);
|
|
||||||
} else {
|
|
||||||
if (__predict_false(0 != sh_linker_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_LINKER);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef GOTO_END
|
|
||||||
|
|
||||||
shadowhook_init_errno = SHADOWHOOK_ERRNO_OK;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
SH_LOG_ALWAYS_SHOW("%s: shadowhook init(mode: %s, debuggable: %s), return: %d, real-init: %s",
|
|
||||||
shadowhook_get_version(), SHADOWHOOK_MODE_SHARED == mode ? "SHARED" : "UNIQUE",
|
|
||||||
debuggable ? "true" : "false", shadowhook_init_errno, do_init ? "yes" : "no");
|
|
||||||
SH_ERRNO_SET_RET_ERRNUM(shadowhook_init_errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
int shadowhook_get_init_errno(void) {
|
|
||||||
return shadowhook_init_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowhook_mode_t shadowhook_get_mode(void) {
|
|
||||||
return shadowhook_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shadowhook_get_debuggable(void) {
|
|
||||||
return sh_log_get_debuggable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_set_debuggable(bool debuggable) {
|
|
||||||
sh_log_set_debuggable(debuggable);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shadowhook_get_recordable(void) {
|
|
||||||
return sh_recorder_get_recordable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_set_recordable(bool recordable) {
|
|
||||||
sh_recorder_set_recordable(recordable);
|
|
||||||
}
|
|
||||||
|
|
||||||
int shadowhook_get_errno(void) {
|
|
||||||
return sh_errno_get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *shadowhook_to_errmsg(int error_number) {
|
|
||||||
return sh_errno_to_errmsg(error_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *shadowhook_hook_addr_impl(void *sym_addr, void *new_addr, void **orig_addr,
|
|
||||||
bool ignore_symbol_check, uintptr_t caller_addr) {
|
|
||||||
SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) ...", ignore_symbol_check ? "func" : "sym", sym_addr,
|
|
||||||
new_addr);
|
|
||||||
sh_errno_reset();
|
|
||||||
|
|
||||||
int r;
|
|
||||||
if (NULL == sym_addr || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
|
|
||||||
if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
|
|
||||||
|
|
||||||
// create task
|
|
||||||
sh_task_t *task =
|
|
||||||
sh_task_create_by_target_addr((uintptr_t)sym_addr, (uintptr_t)new_addr, (uintptr_t *)orig_addr,
|
|
||||||
ignore_symbol_check, (uintptr_t)caller_addr);
|
|
||||||
if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM);
|
|
||||||
|
|
||||||
// do hook
|
|
||||||
r = sh_task_hook(task);
|
|
||||||
if (0 != r) {
|
|
||||||
sh_task_destroy(task);
|
|
||||||
GOTO_ERR(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) OK. return: %p", ignore_symbol_check ? "func" : "sym",
|
|
||||||
sym_addr, new_addr, (void *)task);
|
|
||||||
SH_ERRNO_SET_RET(SHADOWHOOK_ERRNO_OK, (void *)task);
|
|
||||||
|
|
||||||
err:
|
|
||||||
SH_LOG_ERROR("shadowhook: hook_%s_addr(%p, %p) FAILED. %d - %s", ignore_symbol_check ? "func" : "sym",
|
|
||||||
sym_addr, new_addr, r, sh_errno_to_errmsg(r));
|
|
||||||
SH_ERRNO_SET_RET_NULL(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr) {
|
|
||||||
const void *caller_addr = __builtin_return_address(0);
|
|
||||||
return shadowhook_hook_addr_impl(func_addr, new_addr, orig_addr, true, (uintptr_t)caller_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr) {
|
|
||||||
const void *caller_addr = __builtin_return_address(0);
|
|
||||||
return shadowhook_hook_addr_impl(sym_addr, new_addr, orig_addr, false, (uintptr_t)caller_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *shadowhook_hook_sym_name_impl(const char *lib_name, const char *sym_name, void *new_addr,
|
|
||||||
void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg,
|
|
||||||
uintptr_t caller_addr) {
|
|
||||||
SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) ...", lib_name, sym_name, new_addr);
|
|
||||||
sh_errno_reset();
|
|
||||||
|
|
||||||
int r;
|
|
||||||
if (NULL == lib_name || NULL == sym_name || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
|
|
||||||
if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
|
|
||||||
|
|
||||||
// create task
|
|
||||||
sh_task_t *task =
|
|
||||||
sh_task_create_by_sym_name(lib_name, sym_name, (uintptr_t)new_addr, (uintptr_t *)orig_addr, hooked,
|
|
||||||
hooked_arg, (uintptr_t)caller_addr);
|
|
||||||
if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM);
|
|
||||||
|
|
||||||
// do hook
|
|
||||||
r = sh_task_hook(task);
|
|
||||||
if (0 != r && SHADOWHOOK_ERRNO_PENDING != r) {
|
|
||||||
sh_task_destroy(task);
|
|
||||||
GOTO_ERR(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) OK. return: %p. %d - %s", lib_name, sym_name, new_addr,
|
|
||||||
(void *)task, r, sh_errno_to_errmsg(r));
|
|
||||||
SH_ERRNO_SET_RET(r, (void *)task);
|
|
||||||
|
|
||||||
err:
|
|
||||||
SH_LOG_ERROR("shadowhook: hook_sym_name(%s, %s, %p) FAILED. %d - %s", lib_name, sym_name, new_addr, r,
|
|
||||||
sh_errno_to_errmsg(r));
|
|
||||||
SH_ERRNO_SET_RET_NULL(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr) {
|
|
||||||
const void *caller_addr = __builtin_return_address(0);
|
|
||||||
return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, NULL, NULL,
|
|
||||||
(uintptr_t)caller_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr,
|
|
||||||
void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg) {
|
|
||||||
const void *caller_addr = __builtin_return_address(0);
|
|
||||||
return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, hooked, hooked_arg,
|
|
||||||
(uintptr_t)caller_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int shadowhook_unhook(void *stub) {
|
|
||||||
const void *caller_addr = __builtin_return_address(0);
|
|
||||||
SH_LOG_INFO("shadowhook: unhook(%p) ...", stub);
|
|
||||||
sh_errno_reset();
|
|
||||||
|
|
||||||
int r;
|
|
||||||
if (NULL == stub) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG);
|
|
||||||
if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno);
|
|
||||||
|
|
||||||
sh_task_t *task = (sh_task_t *)stub;
|
|
||||||
r = sh_task_unhook(task, (uintptr_t)caller_addr);
|
|
||||||
sh_task_destroy(task);
|
|
||||||
if (0 != r) GOTO_ERR(r);
|
|
||||||
|
|
||||||
// OK
|
|
||||||
SH_LOG_INFO("shadowhook: unhook(%p) OK", stub);
|
|
||||||
SH_ERRNO_SET_RET_ERRNUM(SHADOWHOOK_ERRNO_OK);
|
|
||||||
|
|
||||||
err:
|
|
||||||
SH_LOG_ERROR("shadowhook: unhook(%p) FAILED. %d - %s", stub, r, sh_errno_to_errmsg(r));
|
|
||||||
SH_ERRNO_SET_RET_FAIL(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *shadowhook_get_records(uint32_t item_flags) {
|
|
||||||
return sh_recorder_get(item_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_dump_records(int fd, uint32_t item_flags) {
|
|
||||||
sh_recorder_dump(fd, item_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_dlopen(const char *lib_name) {
|
|
||||||
void *handle = NULL;
|
|
||||||
if (sh_util_get_api_level() >= __ANDROID_API_L__) {
|
|
||||||
handle = xdl_open(lib_name, XDL_DEFAULT);
|
|
||||||
} else {
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
handle = xdl_open(lib_name, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
SH_LOG_WARN("shadowhook: dlopen crashed - %s", lib_name);
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
}
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_dlclose(void *handle) {
|
|
||||||
xdl_close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_dlsym(void *handle, const char *sym_name) {
|
|
||||||
void *addr = shadowhook_dlsym_dynsym(handle, sym_name);
|
|
||||||
if (NULL == addr) addr = shadowhook_dlsym_symtab(handle, sym_name);
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name) {
|
|
||||||
void *addr = NULL;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
addr = xdl_sym(handle, sym_name, NULL);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
SH_LOG_WARN("shadowhook: dlsym_dynsym crashed - %p, %s", handle, sym_name);
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_dlsym_symtab(void *handle, const char *sym_name) {
|
|
||||||
void *addr = NULL;
|
|
||||||
SH_SIG_TRY(SIGSEGV, SIGBUS) {
|
|
||||||
addr = xdl_dsym(handle, sym_name, NULL);
|
|
||||||
}
|
|
||||||
SH_SIG_CATCH() {
|
|
||||||
SH_LOG_WARN("shadowhook: dlsym_symtab crashed - %p, %s", handle, sym_name);
|
|
||||||
}
|
|
||||||
SH_SIG_EXIT
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_get_prev_func(void *func) {
|
|
||||||
if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
|
|
||||||
return sh_hub_get_prev_func(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_pop_stack(void *return_address) {
|
|
||||||
if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
|
|
||||||
sh_hub_pop_stack(return_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_allow_reentrant(void *return_address) {
|
|
||||||
if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
|
|
||||||
sh_hub_allow_reentrant(return_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void shadowhook_disallow_reentrant(void *return_address) {
|
|
||||||
if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
|
|
||||||
sh_hub_disallow_reentrant(return_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shadowhook_get_return_address(void) {
|
|
||||||
if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort();
|
|
||||||
return sh_hub_get_return_address();
|
|
||||||
}
|
|
551
app/src/main/cpp/shadowhook/third_party/bsd/queue.h
vendored
551
app/src/main/cpp/shadowhook/third_party/bsd/queue.h
vendored
@ -1,551 +0,0 @@
|
|||||||
/*-
|
|
||||||
* Copyright (c) 1991, 1993
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the University nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef QUEUE_H
|
|
||||||
#define QUEUE_H
|
|
||||||
|
|
||||||
/* #include <sys/cdefs.h> */
|
|
||||||
#define QUEUE_CONTAINER_OF(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file defines four types of data structures: singly-linked lists,
|
|
||||||
* singly-linked tail queues, lists and tail queues.
|
|
||||||
*
|
|
||||||
* A singly-linked list is headed by a single forward pointer. The elements
|
|
||||||
* are singly linked for minimum space and pointer manipulation overhead at
|
|
||||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
|
||||||
* added to the list after an existing element or at the head of the list.
|
|
||||||
* Elements being removed from the head of the list should use the explicit
|
|
||||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
|
||||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
|
||||||
* for applications with large datasets and few or no removals or for
|
|
||||||
* implementing a LIFO queue.
|
|
||||||
*
|
|
||||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
|
||||||
* head of the list and the other to the tail of the list. The elements are
|
|
||||||
* singly linked for minimum space and pointer manipulation overhead at the
|
|
||||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
|
||||||
* to the list after an existing element, at the head of the list, or at the
|
|
||||||
* end of the list. Elements being removed from the head of the tail queue
|
|
||||||
* should use the explicit macro for this purpose for optimum efficiency.
|
|
||||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
|
||||||
* Singly-linked tail queues are ideal for applications with large datasets
|
|
||||||
* and few or no removals or for implementing a FIFO queue.
|
|
||||||
*
|
|
||||||
* A list is headed by a single forward pointer (or an array of forward
|
|
||||||
* pointers for a hash table header). The elements are doubly linked
|
|
||||||
* so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before
|
|
||||||
* or after an existing element or at the head of the list. A list
|
|
||||||
* may be traversed in either direction.
|
|
||||||
*
|
|
||||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are doubly
|
|
||||||
* linked so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before or
|
|
||||||
* after an existing element, at the head of the list, or at the end of
|
|
||||||
* the list. A tail queue may be traversed in either direction.
|
|
||||||
*
|
|
||||||
* For details on the use of these macros, see the queue(3) manual page.
|
|
||||||
*
|
|
||||||
* SLIST LIST STAILQ TAILQ
|
|
||||||
* _HEAD + + + +
|
|
||||||
* _HEAD_INITIALIZER + + + +
|
|
||||||
* _ENTRY + + + +
|
|
||||||
* _INIT + + + +
|
|
||||||
* _EMPTY + + + +
|
|
||||||
* _FIRST + + + +
|
|
||||||
* _NEXT + + + +
|
|
||||||
* _PREV - + - +
|
|
||||||
* _LAST - - + +
|
|
||||||
* _FOREACH + + + +
|
|
||||||
* _FOREACH_FROM + + + +
|
|
||||||
* _FOREACH_SAFE + + + +
|
|
||||||
* _FOREACH_FROM_SAFE + + + +
|
|
||||||
* _FOREACH_REVERSE - - - +
|
|
||||||
* _FOREACH_REVERSE_FROM - - - +
|
|
||||||
* _FOREACH_REVERSE_SAFE - - - +
|
|
||||||
* _FOREACH_REVERSE_FROM_SAFE - - - +
|
|
||||||
* _INSERT_HEAD + + + +
|
|
||||||
* _INSERT_BEFORE - + - +
|
|
||||||
* _INSERT_AFTER + + + +
|
|
||||||
* _INSERT_TAIL - - + +
|
|
||||||
* _CONCAT - - + +
|
|
||||||
* _REMOVE_AFTER + - + -
|
|
||||||
* _REMOVE_HEAD + - + -
|
|
||||||
* _REMOVE + + + +
|
|
||||||
* _SWAP + + + +
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List declarations.
|
|
||||||
*/
|
|
||||||
#define SLIST_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
struct type *qual slh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SLIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SLIST_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
struct type *qual sle_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List functions.
|
|
||||||
*/
|
|
||||||
#define SLIST_INIT(head) do { \
|
|
||||||
SLIST_FIRST((head)) = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
|
||||||
|
|
||||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
|
||||||
|
|
||||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = SLIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_FROM(var, head, field) \
|
|
||||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
|
||||||
(var); \
|
|
||||||
(var) = SLIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
|
||||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
|
||||||
SLIST_FIRST((head)) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
|
||||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
|
||||||
SLIST_NEXT((slistelm), field) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
|
||||||
SLIST_NEXT(elm, field) = \
|
|
||||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
|
||||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
|
||||||
if (SLIST_FIRST((head)) == (elm)) { \
|
|
||||||
SLIST_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = SLIST_FIRST((head)); \
|
|
||||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
|
||||||
curelm = SLIST_NEXT(curelm, field); \
|
|
||||||
SLIST_REMOVE_AFTER(curelm, field); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_SWAP(head1, head2, type) do { \
|
|
||||||
struct type *swap_first = SLIST_FIRST(head1); \
|
|
||||||
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
|
|
||||||
SLIST_FIRST(head2) = swap_first; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List declarations.
|
|
||||||
*/
|
|
||||||
#define LIST_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
struct type *qual lh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define LIST_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
struct type *qual le_next; /* next element */ \
|
|
||||||
struct type *qual *le_prev; /* address of previous next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List functions.
|
|
||||||
*/
|
|
||||||
#define LIST_INIT(head) do { \
|
|
||||||
LIST_FIRST((head)) = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
|
||||||
|
|
||||||
#define LIST_FIRST(head) ((head)->lh_first)
|
|
||||||
|
|
||||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
|
||||||
|
|
||||||
#define LIST_PREV(elm, head, type, field) \
|
|
||||||
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
|
|
||||||
QUEUE_CONTAINER_OF((elm)->field.le_prev, struct type, field.le_next))
|
|
||||||
|
|
||||||
#define LIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = LIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_FROM(var, head, field) \
|
|
||||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
|
||||||
(var); \
|
|
||||||
(var) = LIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
|
||||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
|
||||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
|
|
||||||
LIST_FIRST((head)) = (elm); \
|
|
||||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
|
||||||
LIST_NEXT((elm), field) = (listelm); \
|
|
||||||
*(listelm)->field.le_prev = (elm); \
|
|
||||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
|
||||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
|
|
||||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
|
||||||
&LIST_NEXT((elm), field); \
|
|
||||||
LIST_NEXT((listelm), field) = (elm); \
|
|
||||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_REMOVE(elm, field) do { \
|
|
||||||
if (LIST_NEXT((elm), field) != NULL) \
|
|
||||||
LIST_NEXT((elm), field)->field.le_prev = \
|
|
||||||
(elm)->field.le_prev; \
|
|
||||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
|
||||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
|
||||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
|
||||||
LIST_FIRST((head2)) = swap_tmp; \
|
|
||||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
|
||||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
|
||||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
|
||||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define STAILQ_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
struct type *qual stqh_first;/* first element */ \
|
|
||||||
struct type *qual *stqh_last;/* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).stqh_first }
|
|
||||||
|
|
||||||
#define STAILQ_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
struct type *qual stqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue functions.
|
|
||||||
*/
|
|
||||||
#define STAILQ_INIT(head) do { \
|
|
||||||
STAILQ_FIRST((head)) = NULL; \
|
|
||||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
|
||||||
|
|
||||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
|
||||||
|
|
||||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
|
||||||
|
|
||||||
#define STAILQ_LAST(head, type, field) \
|
|
||||||
(STAILQ_EMPTY((head)) ? NULL : \
|
|
||||||
QUEUE_CONTAINER_OF((head)->stqh_last, struct type, field.stqe_next))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH(var, head, field) \
|
|
||||||
for((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = STAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_FROM(var, head, field) \
|
|
||||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
|
||||||
(var); \
|
|
||||||
(var) = STAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
|
||||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
STAILQ_FIRST((head)) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
STAILQ_NEXT((elm), field) = NULL; \
|
|
||||||
*(head)->stqh_last = (elm); \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!STAILQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT(elm, field) = \
|
|
||||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if ((STAILQ_FIRST((head)) = \
|
|
||||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
|
||||||
STAILQ_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = STAILQ_FIRST((head)); \
|
|
||||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
|
||||||
curelm = STAILQ_NEXT(curelm, field); \
|
|
||||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
|
||||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
|
||||||
struct type **swap_last = (head1)->stqh_last; \
|
|
||||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_FIRST(head2) = swap_first; \
|
|
||||||
(head2)->stqh_last = swap_last; \
|
|
||||||
if (STAILQ_EMPTY(head1)) \
|
|
||||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
|
||||||
if (STAILQ_EMPTY(head2)) \
|
|
||||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define TAILQ_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
struct type *qual tqh_first; /* first element */ \
|
|
||||||
struct type *qual *tqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).tqh_first }
|
|
||||||
|
|
||||||
#define TAILQ_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
struct type *qual tqe_next; /* next element */ \
|
|
||||||
struct type *qual *tqe_prev; /* address of previous next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue functions.
|
|
||||||
*/
|
|
||||||
#define TAILQ_INIT(head) do { \
|
|
||||||
TAILQ_FIRST((head)) = NULL; \
|
|
||||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
|
||||||
|
|
||||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
|
||||||
|
|
||||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
|
||||||
|
|
||||||
#define TAILQ_PREV(elm, headname, field) \
|
|
||||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
|
||||||
|
|
||||||
#define TAILQ_LAST(head, headname) \
|
|
||||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = TAILQ_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_FROM(var, head, field) \
|
|
||||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = TAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
|
||||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_PREV((var), headname, field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
|
|
||||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_PREV((var), headname, field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
|
|
||||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
|
||||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
|
||||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
|
||||||
&TAILQ_NEXT((elm), field); \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
TAILQ_FIRST((head)) = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
|
||||||
TAILQ_NEXT((elm), field) = (listelm); \
|
|
||||||
*(listelm)->field.tqe_prev = (elm); \
|
|
||||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
|
|
||||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
|
||||||
&TAILQ_NEXT((elm), field); \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
TAILQ_NEXT((listelm), field) = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
TAILQ_NEXT((elm), field) = NULL; \
|
|
||||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
|
||||||
*(head)->tqh_last = (elm); \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
|
||||||
if (!TAILQ_EMPTY(head2)) { \
|
|
||||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
|
||||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
TAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
|
||||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
|
||||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
|
||||||
(elm)->field.tqe_prev; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
|
||||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
|
||||||
struct type *swap_first = (head1)->tqh_first; \
|
|
||||||
struct type **swap_last = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_first = (head2)->tqh_first; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
(head2)->tqh_first = swap_first; \
|
|
||||||
(head2)->tqh_last = swap_last; \
|
|
||||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
|
||||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
|
||||||
else \
|
|
||||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
|
||||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
|
||||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
|
||||||
else \
|
|
||||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#endif
|
|
759
app/src/main/cpp/shadowhook/third_party/bsd/tree.h
vendored
759
app/src/main/cpp/shadowhook/third_party/bsd/tree.h
vendored
@ -1,759 +0,0 @@
|
|||||||
/*-
|
|
||||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TREE_H
|
|
||||||
#define TREE_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file defines data structures for different types of trees:
|
|
||||||
* splay trees and red-black trees.
|
|
||||||
*
|
|
||||||
* A splay tree is a self-organizing data structure. Every operation
|
|
||||||
* on the tree causes a splay to happen. The splay moves the requested
|
|
||||||
* node to the root of the tree and partly rebalances it.
|
|
||||||
*
|
|
||||||
* This has the benefit that request locality causes faster lookups as
|
|
||||||
* the requested nodes move to the top of the tree. On the other hand,
|
|
||||||
* every lookup causes memory writes.
|
|
||||||
*
|
|
||||||
* The Balance Theorem bounds the total access time for m operations
|
|
||||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
|
||||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
|
||||||
*
|
|
||||||
* A red-black tree is a binary search tree with the node color as an
|
|
||||||
* extra attribute. It fulfills a set of conditions:
|
|
||||||
* - every search path from the root to a leaf consists of the
|
|
||||||
* same number of black nodes,
|
|
||||||
* - each red node (except for the root) has a black parent,
|
|
||||||
* - each leaf node is black.
|
|
||||||
*
|
|
||||||
* Every operation on a red-black tree is bounded as O(lg n).
|
|
||||||
* The maximum height of a red-black tree is 2lg (n+1).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define SPLAY_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *sph_root; /* root of the tree */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_INITIALIZER(root) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SPLAY_INIT(root) do { \
|
|
||||||
(root)->sph_root = NULL; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *spe_left; /* left element */ \
|
|
||||||
struct type *spe_right; /* right element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
|
||||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
|
||||||
#define SPLAY_ROOT(head) (head)->sph_root
|
|
||||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
|
||||||
|
|
||||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
|
||||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
|
||||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
|
||||||
(head)->sph_root = tmp; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
|
||||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
|
||||||
(head)->sph_root = tmp; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
|
||||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
|
||||||
tmp = (head)->sph_root; \
|
|
||||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
|
||||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
|
||||||
tmp = (head)->sph_root; \
|
|
||||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
|
||||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
/* Generates prototypes and inline functions */
|
|
||||||
|
|
||||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
|
||||||
void name##_SPLAY(struct name *, struct type *); \
|
|
||||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
|
||||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
|
||||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
|
||||||
\
|
|
||||||
/* Finds the node with the same key as elm */ \
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (SPLAY_EMPTY(head)) \
|
|
||||||
return(NULL); \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
|
||||||
return (head->sph_root); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
|
||||||
elm = SPLAY_RIGHT(elm, field); \
|
|
||||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
|
||||||
elm = SPLAY_LEFT(elm, field); \
|
|
||||||
} \
|
|
||||||
} else \
|
|
||||||
elm = NULL; \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
|
||||||
{ \
|
|
||||||
name##_SPLAY_MINMAX(head, val); \
|
|
||||||
return (SPLAY_ROOT(head)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main splay operation.
|
|
||||||
* Moves node close to the key of elm to top
|
|
||||||
*/
|
|
||||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
|
||||||
struct type * \
|
|
||||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (SPLAY_EMPTY(head)) { \
|
|
||||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
|
||||||
} else { \
|
|
||||||
int __comp; \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
__comp = (cmp)(elm, (head)->sph_root); \
|
|
||||||
if(__comp < 0) { \
|
|
||||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
|
||||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
|
||||||
} else \
|
|
||||||
return ((head)->sph_root); \
|
|
||||||
} \
|
|
||||||
(head)->sph_root = (elm); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
struct type * \
|
|
||||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *__tmp; \
|
|
||||||
if (SPLAY_EMPTY(head)) \
|
|
||||||
return (NULL); \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
|
||||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
} else { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
void \
|
|
||||||
name##_SPLAY(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type __node, *__left, *__right, *__tmp; \
|
|
||||||
int __comp; \
|
|
||||||
\
|
|
||||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
|
||||||
__left = __right = &__node; \
|
|
||||||
\
|
|
||||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
|
||||||
if (__comp < 0) { \
|
|
||||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if ((cmp)(elm, __tmp) < 0){ \
|
|
||||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKLEFT(head, __right, field); \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if ((cmp)(elm, __tmp) > 0){ \
|
|
||||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
|
||||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKRIGHT(head, __left, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Splay with either the minimum or the maximum element \
|
|
||||||
* Used to find minimum or maximum element in tree. \
|
|
||||||
*/ \
|
|
||||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
|
||||||
{ \
|
|
||||||
struct type __node, *__left, *__right, *__tmp; \
|
|
||||||
\
|
|
||||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
|
||||||
__left = __right = &__node; \
|
|
||||||
\
|
|
||||||
while (1) { \
|
|
||||||
if (__comp < 0) { \
|
|
||||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if (__comp < 0){ \
|
|
||||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKLEFT(head, __right, field); \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if (__comp > 0) { \
|
|
||||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
|
||||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKRIGHT(head, __left, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_NEGINF -1
|
|
||||||
#define SPLAY_INF 1
|
|
||||||
|
|
||||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
|
||||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
|
||||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
|
||||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
|
||||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
|
||||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
|
||||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
|
||||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
|
||||||
|
|
||||||
#define SPLAY_FOREACH(x, name, head) \
|
|
||||||
for ((x) = SPLAY_MIN(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = SPLAY_NEXT(name, head, x))
|
|
||||||
|
|
||||||
/* Macros that define a red-black tree */
|
|
||||||
#define RB_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *rbh_root; /* root of the tree */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_INITIALIZER(root) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define RB_INIT(root) do { \
|
|
||||||
(root)->rbh_root = NULL; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define RB_BLACK 0
|
|
||||||
#define RB_RED 1
|
|
||||||
#define RB_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *rbe_left; /* left element */ \
|
|
||||||
struct type *rbe_right; /* right element */ \
|
|
||||||
struct type *rbe_parent; /* parent element */ \
|
|
||||||
int rbe_color; /* node color */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
|
||||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
|
||||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
|
||||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
|
||||||
#define RB_ROOT(head) (head)->rbh_root
|
|
||||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
|
||||||
|
|
||||||
#define RB_SET(elm, parent, field) do { \
|
|
||||||
RB_PARENT(elm, field) = parent; \
|
|
||||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
|
||||||
RB_COLOR(elm, field) = RB_RED; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
|
||||||
RB_COLOR(black, field) = RB_BLACK; \
|
|
||||||
RB_COLOR(red, field) = RB_RED; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#ifndef RB_AUGMENT
|
|
||||||
#define RB_AUGMENT(x) do {} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
|
||||||
(tmp) = RB_RIGHT(elm, field); \
|
|
||||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
|
||||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
|
||||||
} \
|
|
||||||
RB_AUGMENT(elm); \
|
|
||||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
|
||||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
|
||||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
} else \
|
|
||||||
(head)->rbh_root = (tmp); \
|
|
||||||
RB_LEFT(tmp, field) = (elm); \
|
|
||||||
RB_PARENT(elm, field) = (tmp); \
|
|
||||||
RB_AUGMENT(tmp); \
|
|
||||||
if ((RB_PARENT(tmp, field))) \
|
|
||||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
|
||||||
(tmp) = RB_LEFT(elm, field); \
|
|
||||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
|
||||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
|
||||||
} \
|
|
||||||
RB_AUGMENT(elm); \
|
|
||||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
|
||||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
|
||||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
} else \
|
|
||||||
(head)->rbh_root = (tmp); \
|
|
||||||
RB_RIGHT(tmp, field) = (elm); \
|
|
||||||
RB_PARENT(elm, field) = (tmp); \
|
|
||||||
RB_AUGMENT(tmp); \
|
|
||||||
if ((RB_PARENT(tmp, field))) \
|
|
||||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
/* Generates prototypes and inline functions */
|
|
||||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
|
||||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static)
|
|
||||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
|
||||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
|
||||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
|
||||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_NEXT(struct type *); \
|
|
||||||
attr struct type *name##_RB_PREV(struct type *); \
|
|
||||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
|
||||||
\
|
|
||||||
|
|
||||||
/* Main rb operation.
|
|
||||||
* Moves node close to the key of elm to top
|
|
||||||
*/
|
|
||||||
#define RB_GENERATE(name, type, field, cmp) \
|
|
||||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
|
||||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
|
||||||
RB_GENERATE_INTERNAL(name, type, field, cmp, static)
|
|
||||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
|
||||||
attr void \
|
|
||||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *parent, *gparent, *tmp; \
|
|
||||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
|
||||||
RB_COLOR(parent, field) == RB_RED) { \
|
|
||||||
gparent = RB_PARENT(parent, field); \
|
|
||||||
if (parent == RB_LEFT(gparent, field)) { \
|
|
||||||
tmp = RB_RIGHT(gparent, field); \
|
|
||||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field);\
|
|
||||||
elm = gparent; \
|
|
||||||
continue; \
|
|
||||||
} \
|
|
||||||
if (RB_RIGHT(parent, field) == elm) { \
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
|
||||||
tmp = parent; \
|
|
||||||
parent = elm; \
|
|
||||||
elm = tmp; \
|
|
||||||
} \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
|
||||||
} else { \
|
|
||||||
tmp = RB_LEFT(gparent, field); \
|
|
||||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field);\
|
|
||||||
elm = gparent; \
|
|
||||||
continue; \
|
|
||||||
} \
|
|
||||||
if (RB_LEFT(parent, field) == elm) { \
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
|
||||||
tmp = parent; \
|
|
||||||
parent = elm; \
|
|
||||||
elm = tmp; \
|
|
||||||
} \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr void \
|
|
||||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp; \
|
|
||||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
|
||||||
elm != RB_ROOT(head)) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) { \
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_SET_BLACKRED(tmp, parent, field); \
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
} \
|
|
||||||
if ((RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
|
||||||
(RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
elm = parent; \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
|
||||||
struct type *oleft; \
|
|
||||||
if ((oleft = RB_LEFT(tmp, field)) \
|
|
||||||
!= NULL) \
|
|
||||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
} \
|
|
||||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
|
||||||
RB_COLOR(parent, field) = RB_BLACK; \
|
|
||||||
if (RB_RIGHT(tmp, field)) \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
|
||||||
elm = RB_ROOT(head); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_SET_BLACKRED(tmp, parent, field); \
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
} \
|
|
||||||
if ((RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
|
||||||
(RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
elm = parent; \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
|
||||||
struct type *oright; \
|
|
||||||
if ((oright = RB_RIGHT(tmp, field)) \
|
|
||||||
!= NULL) \
|
|
||||||
RB_COLOR(oright, field) = RB_BLACK;\
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
} \
|
|
||||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
|
||||||
RB_COLOR(parent, field) = RB_BLACK; \
|
|
||||||
if (RB_LEFT(tmp, field)) \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
|
||||||
elm = RB_ROOT(head); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
if (elm) \
|
|
||||||
RB_COLOR(elm, field) = RB_BLACK; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *child, *parent, *old = elm; \
|
|
||||||
int color; \
|
|
||||||
if (RB_LEFT(elm, field) == NULL) \
|
|
||||||
child = RB_RIGHT(elm, field); \
|
|
||||||
else if (RB_RIGHT(elm, field) == NULL) \
|
|
||||||
child = RB_LEFT(elm, field); \
|
|
||||||
else { \
|
|
||||||
struct type *left; \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
|
||||||
elm = left; \
|
|
||||||
child = RB_RIGHT(elm, field); \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
color = RB_COLOR(elm, field); \
|
|
||||||
if (child) \
|
|
||||||
RB_PARENT(child, field) = parent; \
|
|
||||||
if (parent) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) \
|
|
||||||
RB_LEFT(parent, field) = child; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = child; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = child; \
|
|
||||||
if (RB_PARENT(elm, field) == old) \
|
|
||||||
parent = elm; \
|
|
||||||
(elm)->field = (old)->field; \
|
|
||||||
if (RB_PARENT(old, field)) { \
|
|
||||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
|
||||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
|
||||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = elm; \
|
|
||||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
|
||||||
if (RB_RIGHT(old, field)) \
|
|
||||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
|
||||||
if (parent) { \
|
|
||||||
left = parent; \
|
|
||||||
do { \
|
|
||||||
RB_AUGMENT(left); \
|
|
||||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
|
||||||
} \
|
|
||||||
goto color; \
|
|
||||||
} \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
color = RB_COLOR(elm, field); \
|
|
||||||
if (child) \
|
|
||||||
RB_PARENT(child, field) = parent; \
|
|
||||||
if (parent) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) \
|
|
||||||
RB_LEFT(parent, field) = child; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = child; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = child; \
|
|
||||||
color: \
|
|
||||||
if (color == RB_BLACK) \
|
|
||||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
|
||||||
return (old); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Inserts a node into the RB tree */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp; \
|
|
||||||
struct type *parent = NULL; \
|
|
||||||
int comp = 0; \
|
|
||||||
tmp = RB_ROOT(head); \
|
|
||||||
while (tmp) { \
|
|
||||||
parent = tmp; \
|
|
||||||
comp = (cmp)(elm, parent); \
|
|
||||||
if (comp < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
RB_SET(elm, parent, field); \
|
|
||||||
if (parent != NULL) { \
|
|
||||||
if (comp < 0) \
|
|
||||||
RB_LEFT(parent, field) = elm; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = elm; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = elm; \
|
|
||||||
name##_RB_INSERT_COLOR(head, elm); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Finds the node with the same key as elm */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
int comp; \
|
|
||||||
while (tmp) { \
|
|
||||||
comp = cmp(elm, tmp); \
|
|
||||||
if (comp < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Finds the first node greater than or equal to the search key */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
struct type *res = NULL; \
|
|
||||||
int comp; \
|
|
||||||
while (tmp) { \
|
|
||||||
comp = cmp(elm, tmp); \
|
|
||||||
if (comp < 0) { \
|
|
||||||
res = tmp; \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
} \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
return (res); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* ARGSUSED */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_NEXT(struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (RB_RIGHT(elm, field)) { \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
while (RB_LEFT(elm, field)) \
|
|
||||||
elm = RB_LEFT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
else { \
|
|
||||||
while (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* ARGSUSED */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_PREV(struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (RB_LEFT(elm, field)) { \
|
|
||||||
elm = RB_LEFT(elm, field); \
|
|
||||||
while (RB_RIGHT(elm, field)) \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
else { \
|
|
||||||
while (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_MINMAX(struct name *head, int val) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
struct type *parent = NULL; \
|
|
||||||
while (tmp) { \
|
|
||||||
parent = tmp; \
|
|
||||||
if (val < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
} \
|
|
||||||
return (parent); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_NEGINF -1
|
|
||||||
#define RB_INF 1
|
|
||||||
|
|
||||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
|
||||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
|
||||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
|
||||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
|
||||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
|
||||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
|
||||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
|
||||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
|
||||||
|
|
||||||
#define RB_FOREACH(x, name, head) \
|
|
||||||
for ((x) = RB_MIN(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = name##_RB_NEXT(x))
|
|
||||||
|
|
||||||
#define RB_FOREACH_FROM(x, name, y) \
|
|
||||||
for ((x) = (y); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
|
||||||
for ((x) = RB_MIN(name, head); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
|
||||||
for ((x) = RB_MAX(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = name##_RB_PREV(x))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
|
||||||
for ((x) = (y); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
|
||||||
for ((x) = RB_MAX(name, head); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||||||
Copyright (c) 2005-2011, Google Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
|
|
||||||
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.
|
|
989
app/src/main/cpp/shadowhook/third_party/xdl/xdl.c
vendored
989
app/src/main/cpp/shadowhook/third_party/xdl/xdl.c
vendored
@ -1,989 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
|
|
||||||
#include <android/api-level.h>
|
|
||||||
#include <elf.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <link.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/auxv.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "xdl_iterate.h"
|
|
||||||
#include "xdl_linker.h"
|
|
||||||
#include "xdl_lzma.h"
|
|
||||||
#include "xdl_util.h"
|
|
||||||
|
|
||||||
#ifndef STT_GNU_IFUNC
|
|
||||||
#define STT_GNU_IFUNC 10
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
#define XDL_LIB_PATH "/system/lib"
|
|
||||||
#else
|
|
||||||
#define XDL_LIB_PATH "/system/lib64"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx))
|
|
||||||
#define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \
|
|
||||||
(SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE))
|
|
||||||
|
|
||||||
extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
|
|
||||||
typedef struct xdl {
|
|
||||||
char *pathname;
|
|
||||||
uintptr_t load_bias;
|
|
||||||
const ElfW(Phdr) *dlpi_phdr;
|
|
||||||
ElfW(Half) dlpi_phnum;
|
|
||||||
|
|
||||||
struct xdl *next; // to next xdl obj for cache in xdl_addr()
|
|
||||||
void *linker_handle; // hold handle returned by xdl_linker_force_dlopen()
|
|
||||||
|
|
||||||
//
|
|
||||||
// (1) for searching symbols from .dynsym
|
|
||||||
//
|
|
||||||
|
|
||||||
bool dynsym_try_load;
|
|
||||||
ElfW(Sym) *dynsym; // .dynsym
|
|
||||||
const char *dynstr; // .dynstr
|
|
||||||
|
|
||||||
// .hash (SYSV hash for .dynstr)
|
|
||||||
struct {
|
|
||||||
const uint32_t *buckets;
|
|
||||||
uint32_t buckets_cnt;
|
|
||||||
const uint32_t *chains;
|
|
||||||
uint32_t chains_cnt;
|
|
||||||
} sysv_hash;
|
|
||||||
|
|
||||||
// .gnu.hash (GNU hash for .dynstr)
|
|
||||||
struct {
|
|
||||||
const uint32_t *buckets;
|
|
||||||
uint32_t buckets_cnt;
|
|
||||||
const uint32_t *chains;
|
|
||||||
uint32_t symoffset;
|
|
||||||
const ElfW(Addr) *bloom;
|
|
||||||
uint32_t bloom_cnt;
|
|
||||||
uint32_t bloom_shift;
|
|
||||||
} gnu_hash;
|
|
||||||
|
|
||||||
//
|
|
||||||
// (2) for searching symbols from .symtab
|
|
||||||
//
|
|
||||||
|
|
||||||
bool symtab_try_load;
|
|
||||||
uintptr_t base;
|
|
||||||
|
|
||||||
ElfW(Sym) *symtab; // .symtab
|
|
||||||
size_t symtab_cnt;
|
|
||||||
char *strtab; // .strtab
|
|
||||||
size_t strtab_sz;
|
|
||||||
} xdl_t;
|
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr);
|
|
||||||
static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr);
|
|
||||||
|
|
||||||
// load from memory
|
|
||||||
static int xdl_dynsym_load(xdl_t *self) {
|
|
||||||
// find the dynamic segment
|
|
||||||
ElfW(Dyn) *dynamic = NULL;
|
|
||||||
for (size_t i = 0; i < self->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
|
|
||||||
if (PT_DYNAMIC == phdr->p_type) {
|
|
||||||
dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NULL == dynamic) return -1;
|
|
||||||
|
|
||||||
// iterate the dynamic segment
|
|
||||||
for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) {
|
|
||||||
switch (entry->d_tag) {
|
|
||||||
case DT_SYMTAB: //.dynsym
|
|
||||||
self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr);
|
|
||||||
break;
|
|
||||||
case DT_STRTAB: //.dynstr
|
|
||||||
self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr);
|
|
||||||
break;
|
|
||||||
case DT_HASH: //.hash
|
|
||||||
self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
|
|
||||||
self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
|
|
||||||
self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]);
|
|
||||||
self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]);
|
|
||||||
break;
|
|
||||||
case DT_GNU_HASH: //.gnu.hash
|
|
||||||
self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
|
|
||||||
self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
|
|
||||||
self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2];
|
|
||||||
self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3];
|
|
||||||
self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16);
|
|
||||||
self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt]));
|
|
||||||
self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt]));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == self->dynsym || NULL == self->dynstr ||
|
|
||||||
(0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) {
|
|
||||||
self->dynsym = NULL;
|
|
||||||
self->dynstr = NULL;
|
|
||||||
self->sysv_hash.buckets_cnt = 0;
|
|
||||||
self->gnu_hash.buckets_cnt = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) {
|
|
||||||
if (0 == data_len) return NULL;
|
|
||||||
if (data_offset >= file_sz) return NULL;
|
|
||||||
if (data_offset + data_len > file_sz) return NULL;
|
|
||||||
|
|
||||||
if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL;
|
|
||||||
|
|
||||||
void *data = malloc(data_len);
|
|
||||||
if (NULL == data) return NULL;
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgnu-statement-expression"
|
|
||||||
if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len)))
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
{
|
|
||||||
free(data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) {
|
|
||||||
return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
|
|
||||||
if (0 == data_len) return NULL;
|
|
||||||
if (data_offset >= mem_sz) return NULL;
|
|
||||||
if (data_offset + data_len > mem_sz) return NULL;
|
|
||||||
|
|
||||||
void *data = malloc(data_len);
|
|
||||||
if (NULL == data) return NULL;
|
|
||||||
|
|
||||||
memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
|
|
||||||
return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
|
|
||||||
if (0 == data_len) return NULL;
|
|
||||||
if (data_offset >= mem_sz) return NULL;
|
|
||||||
if (data_offset + data_len > mem_sz) return NULL;
|
|
||||||
|
|
||||||
return (void *)((uintptr_t)mem + data_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
|
|
||||||
return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load from disk and memory
|
|
||||||
static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz,
|
|
||||||
ElfW(Shdr) *shdr_debugdata) {
|
|
||||||
void *debugdata = NULL;
|
|
||||||
ElfW(Shdr) *shdrs = NULL;
|
|
||||||
int r = -1;
|
|
||||||
|
|
||||||
// get zipped .gnu_debugdata
|
|
||||||
uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata);
|
|
||||||
if (NULL == debugdata_zip) return -1;
|
|
||||||
|
|
||||||
// get unzipped .gnu_debugdata
|
|
||||||
size_t debugdata_sz;
|
|
||||||
if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz))
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
// get ELF header
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata;
|
|
||||||
if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
|
|
||||||
|
|
||||||
// get section headers
|
|
||||||
shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff,
|
|
||||||
ehdr->e_shentsize * ehdr->e_shnum);
|
|
||||||
if (NULL == shdrs) goto end;
|
|
||||||
|
|
||||||
// get .shstrtab
|
|
||||||
if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
|
|
||||||
char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx);
|
|
||||||
if (NULL == shstrtab) goto end;
|
|
||||||
|
|
||||||
// find .symtab & .strtab
|
|
||||||
for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
|
|
||||||
char *shdr_name = shstrtab + shdr->sh_name;
|
|
||||||
|
|
||||||
if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
|
|
||||||
// get & check associated .strtab section
|
|
||||||
if (shdr->sh_link >= ehdr->e_shnum) continue;
|
|
||||||
ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
|
|
||||||
if (SHT_STRTAB != shdr_strtab->sh_type) continue;
|
|
||||||
|
|
||||||
// get .symtab & .strtab
|
|
||||||
ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr);
|
|
||||||
if (NULL == symtab) continue;
|
|
||||||
char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab);
|
|
||||||
if (NULL == strtab) {
|
|
||||||
free(symtab);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
self->symtab = symtab;
|
|
||||||
self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
|
|
||||||
self->strtab = strtab;
|
|
||||||
self->strtab_sz = shdr_strtab->sh_size;
|
|
||||||
r = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
free(debugdata_zip);
|
|
||||||
if (NULL != debugdata) free(debugdata);
|
|
||||||
if (NULL != shdrs) free(shdrs);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load from disk and memory
|
|
||||||
static int xdl_symtab_load(xdl_t *self) {
|
|
||||||
if ('[' == self->pathname[0]) return -1;
|
|
||||||
|
|
||||||
int r = -1;
|
|
||||||
ElfW(Shdr) *shdrs = NULL;
|
|
||||||
char *shstrtab = NULL;
|
|
||||||
|
|
||||||
// get base address
|
|
||||||
uintptr_t vaddr_min = UINTPTR_MAX;
|
|
||||||
for (size_t i = 0; i < self->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type) {
|
|
||||||
if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (UINTPTR_MAX == vaddr_min) return -1;
|
|
||||||
self->base = self->load_bias + vaddr_min;
|
|
||||||
|
|
||||||
// open file
|
|
||||||
int flags = O_RDONLY | O_CLOEXEC;
|
|
||||||
int file_fd;
|
|
||||||
if ('/' == self->pathname[0]) {
|
|
||||||
file_fd = open(self->pathname, flags);
|
|
||||||
} else {
|
|
||||||
char full_pathname[1024];
|
|
||||||
// try the fast method
|
|
||||||
snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname);
|
|
||||||
file_fd = open(full_pathname, flags);
|
|
||||||
if (file_fd < 0) {
|
|
||||||
// try the slow method
|
|
||||||
if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1;
|
|
||||||
file_fd = open(full_pathname, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (file_fd < 0) return -1;
|
|
||||||
struct stat st;
|
|
||||||
if (0 != fstat(file_fd, &st)) goto end;
|
|
||||||
size_t file_sz = (size_t)st.st_size;
|
|
||||||
|
|
||||||
// get ELF header
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base;
|
|
||||||
if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
|
|
||||||
|
|
||||||
// get section headers
|
|
||||||
shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff,
|
|
||||||
ehdr->e_shentsize * ehdr->e_shnum);
|
|
||||||
if (NULL == shdrs) goto end;
|
|
||||||
|
|
||||||
// get .shstrtab
|
|
||||||
if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
|
|
||||||
shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx);
|
|
||||||
if (NULL == shstrtab) goto end;
|
|
||||||
|
|
||||||
// find .symtab & .strtab
|
|
||||||
for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
|
|
||||||
char *shdr_name = shstrtab + shdr->sh_name;
|
|
||||||
|
|
||||||
if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
|
|
||||||
// get & check associated .strtab section
|
|
||||||
if (shdr->sh_link >= ehdr->e_shnum) continue;
|
|
||||||
ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
|
|
||||||
if (SHT_STRTAB != shdr_strtab->sh_type) continue;
|
|
||||||
|
|
||||||
// get .symtab & .strtab
|
|
||||||
ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr);
|
|
||||||
if (NULL == symtab) continue;
|
|
||||||
char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab);
|
|
||||||
if (NULL == strtab) {
|
|
||||||
free(symtab);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
self->symtab = symtab;
|
|
||||||
self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
|
|
||||||
self->strtab = strtab;
|
|
||||||
self->strtab_sz = shdr_strtab->sh_size;
|
|
||||||
r = 0;
|
|
||||||
break;
|
|
||||||
} else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) {
|
|
||||||
if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) {
|
|
||||||
// OK
|
|
||||||
r = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
close(file_fd);
|
|
||||||
if (NULL != shdrs) free(shdrs);
|
|
||||||
if (NULL != shstrtab) free(shstrtab);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) {
|
|
||||||
if (NULL == getauxval) return NULL; // API level < 18
|
|
||||||
|
|
||||||
uintptr_t val = (uintptr_t)getauxval(type);
|
|
||||||
if (0 == val) return NULL;
|
|
||||||
|
|
||||||
// get base
|
|
||||||
uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val);
|
|
||||||
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL;
|
|
||||||
|
|
||||||
// ELF info
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
|
|
||||||
const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
|
|
||||||
ElfW(Half) dlpi_phnum = ehdr->e_phnum;
|
|
||||||
|
|
||||||
// get bias
|
|
||||||
uintptr_t min_vaddr = UINTPTR_MAX;
|
|
||||||
for (size_t i = 0; i < dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type) {
|
|
||||||
if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL;
|
|
||||||
uintptr_t load_bias = base - min_vaddr;
|
|
||||||
|
|
||||||
// create xDL object
|
|
||||||
xdl_t *self;
|
|
||||||
if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL;
|
|
||||||
if (NULL == (self->pathname = strdup(pathname))) {
|
|
||||||
free(self);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
self->load_bias = load_bias;
|
|
||||||
self->dlpi_phdr = dlpi_phdr;
|
|
||||||
self->dlpi_phnum = dlpi_phnum;
|
|
||||||
self->dynsym_try_load = false;
|
|
||||||
self->symtab_try_load = false;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
(void)size;
|
|
||||||
|
|
||||||
uintptr_t *pkg = (uintptr_t *)arg;
|
|
||||||
xdl_t **self = (xdl_t **)*pkg++;
|
|
||||||
const char *filename = (const char *)*pkg;
|
|
||||||
|
|
||||||
// check load_bias
|
|
||||||
if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0;
|
|
||||||
|
|
||||||
// check pathname
|
|
||||||
if ('[' == filename[0]) {
|
|
||||||
if (0 != strcmp(info->dlpi_name, filename)) return 0;
|
|
||||||
} else if ('/' == filename[0]) {
|
|
||||||
if ('/' == info->dlpi_name[0]) {
|
|
||||||
if (0 != strcmp(info->dlpi_name, filename)) return 0;
|
|
||||||
} else {
|
|
||||||
if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ('/' == info->dlpi_name[0]) {
|
|
||||||
if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0;
|
|
||||||
} else {
|
|
||||||
if (0 != strcmp(info->dlpi_name, filename)) return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// found the target ELF
|
|
||||||
if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // return failed
|
|
||||||
if (NULL == ((*self)->pathname = strdup((const char *)info->dlpi_name))) {
|
|
||||||
free(*self);
|
|
||||||
*self = NULL;
|
|
||||||
return 1; // return failed
|
|
||||||
}
|
|
||||||
(*self)->load_bias = info->dlpi_addr;
|
|
||||||
(*self)->dlpi_phdr = info->dlpi_phdr;
|
|
||||||
(*self)->dlpi_phnum = info->dlpi_phnum;
|
|
||||||
(*self)->dynsym_try_load = false;
|
|
||||||
(*self)->symtab_try_load = false;
|
|
||||||
return 1; // return OK
|
|
||||||
}
|
|
||||||
|
|
||||||
static xdl_t *xdl_find(const char *filename) {
|
|
||||||
// from auxv (linker, vDSO)
|
|
||||||
xdl_t *self = NULL;
|
|
||||||
if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME))
|
|
||||||
self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME);
|
|
||||||
else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME))
|
|
||||||
self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME);
|
|
||||||
|
|
||||||
// from auxv (app_process)
|
|
||||||
const char *basename, *pathname;
|
|
||||||
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
if (xdl_util_get_api_level() < __ANDROID_API_L__) {
|
|
||||||
basename = XDL_UTIL_APP_PROCESS_BASENAME_K;
|
|
||||||
pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
basename = XDL_UTIL_APP_PROCESS_BASENAME;
|
|
||||||
pathname = XDL_UTIL_APP_PROCESS_PATHNAME;
|
|
||||||
}
|
|
||||||
if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname);
|
|
||||||
|
|
||||||
if (NULL != self) return self;
|
|
||||||
|
|
||||||
// from dl_iterate_phdr
|
|
||||||
uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename};
|
|
||||||
xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_open_always_force(const char *filename) {
|
|
||||||
// always force dlopen()
|
|
||||||
void *linker_handle = xdl_linker_force_dlopen(filename);
|
|
||||||
if (NULL == linker_handle) return NULL;
|
|
||||||
|
|
||||||
// find
|
|
||||||
xdl_t *self = xdl_find(filename);
|
|
||||||
if (NULL == self)
|
|
||||||
dlclose(linker_handle);
|
|
||||||
else
|
|
||||||
self->linker_handle = linker_handle;
|
|
||||||
|
|
||||||
return (void *)self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_open_try_force(const char *filename) {
|
|
||||||
// find
|
|
||||||
xdl_t *self = xdl_find(filename);
|
|
||||||
if (NULL != self) return (void *)self;
|
|
||||||
|
|
||||||
// try force dlopen()
|
|
||||||
void *linker_handle = xdl_linker_force_dlopen(filename);
|
|
||||||
if (NULL == linker_handle) return NULL;
|
|
||||||
|
|
||||||
// find again
|
|
||||||
self = xdl_find(filename);
|
|
||||||
if (NULL == self)
|
|
||||||
dlclose(linker_handle);
|
|
||||||
else
|
|
||||||
self->linker_handle = linker_handle;
|
|
||||||
|
|
||||||
return (void *)self;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *xdl_open(const char *filename, int flags) {
|
|
||||||
if (NULL == filename) return NULL;
|
|
||||||
|
|
||||||
if (flags & XDL_ALWAYS_FORCE_LOAD)
|
|
||||||
return xdl_open_always_force(filename);
|
|
||||||
else if (flags & XDL_TRY_FORCE_LOAD)
|
|
||||||
return xdl_open_try_force(filename);
|
|
||||||
else
|
|
||||||
return xdl_find(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *xdl_close(void *handle) {
|
|
||||||
if (NULL == handle) return NULL;
|
|
||||||
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
if (NULL != self->pathname) free(self->pathname);
|
|
||||||
if (NULL != self->symtab) free(self->symtab);
|
|
||||||
if (NULL != self->strtab) free(self->strtab);
|
|
||||||
|
|
||||||
void *linker_handle = self->linker_handle;
|
|
||||||
free(self);
|
|
||||||
return linker_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t xdl_sysv_hash(const uint8_t *name) {
|
|
||||||
uint32_t h = 0, g;
|
|
||||||
|
|
||||||
while (*name) {
|
|
||||||
h = (h << 4) + *name++;
|
|
||||||
g = h & 0xf0000000;
|
|
||||||
h ^= g;
|
|
||||||
h ^= g >> 24;
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t xdl_gnu_hash(const uint8_t *name) {
|
|
||||||
uint32_t h = 5381;
|
|
||||||
|
|
||||||
while (*name) {
|
|
||||||
h += (h << 5) + *name++;
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) {
|
|
||||||
uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name);
|
|
||||||
|
|
||||||
for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i;
|
|
||||||
i = self->sysv_hash.chains[i]) {
|
|
||||||
ElfW(Sym) *sym = self->dynsym + i;
|
|
||||||
if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue;
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) {
|
|
||||||
uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name);
|
|
||||||
|
|
||||||
static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;
|
|
||||||
size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt];
|
|
||||||
size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) |
|
|
||||||
(size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits);
|
|
||||||
|
|
||||||
// if at least one bit is not set, this symbol is surely missing
|
|
||||||
if ((word & mask) != mask) return NULL;
|
|
||||||
|
|
||||||
// ignore STN_UNDEF
|
|
||||||
uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt];
|
|
||||||
if (i < self->gnu_hash.symoffset) return NULL;
|
|
||||||
|
|
||||||
// loop through the chain
|
|
||||||
while (1) {
|
|
||||||
ElfW(Sym) *sym = self->dynsym + i;
|
|
||||||
uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset];
|
|
||||||
|
|
||||||
if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) {
|
|
||||||
if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) {
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// chain ends with an element with the lowest bit set to 1
|
|
||||||
if (sym_hash & (uint32_t)1) break;
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned long size; /** Set to sizeof(__ifunc_arg_t). */
|
|
||||||
unsigned long hwcap; /** Set to getauxval(AT_HWCAP). */
|
|
||||||
unsigned long hwcap2; /** Set to getauxval(AT_HWCAP2). */
|
|
||||||
} xdl_ifunc_arg_t;
|
|
||||||
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
#define XDL_IFUNC_ARG_HWCAP (1ULL << 62)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void *xdl_resolve_symbol_address(xdl_t *self, ElfW(Sym) *sym, size_t *symbol_size) {
|
|
||||||
if (ELF_ST_TYPE(sym->st_info) == STT_TLS) {
|
|
||||||
return NULL;
|
|
||||||
} else if (ELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) {
|
|
||||||
void *sym_addr = (void *)(self->load_bias + sym->st_value);
|
|
||||||
void *real_sym_addr = NULL;
|
|
||||||
if (xdl_util_get_api_level() < __ANDROID_API_R__) {
|
|
||||||
// Android [4.x, 10]
|
|
||||||
typedef void *(*ifunc_resolver_t)(void);
|
|
||||||
real_sym_addr = ((ifunc_resolver_t)sym_addr)();
|
|
||||||
} else {
|
|
||||||
// Android [11, ...)
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
if (NULL == getauxval) return NULL;
|
|
||||||
typedef void *(*ifunc_resolver_t)(uint64_t, xdl_ifunc_arg_t *);
|
|
||||||
static xdl_ifunc_arg_t arg;
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
arg.size = sizeof(xdl_ifunc_arg_t);
|
|
||||||
arg.hwcap = getauxval(AT_HWCAP);
|
|
||||||
arg.hwcap2 = getauxval(AT_HWCAP2);
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
real_sym_addr = ((ifunc_resolver_t)sym_addr)(arg.hwcap | XDL_IFUNC_ARG_HWCAP, &arg);
|
|
||||||
#elif defined(__arm__)
|
|
||||||
if (NULL == getauxval) return NULL;
|
|
||||||
typedef void *(*ifunc_resolver_t)(unsigned long);
|
|
||||||
static unsigned long hwcap;
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
hwcap = getauxval(AT_HWCAP);
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
real_sym_addr = ((ifunc_resolver_t)sym_addr)(hwcap);
|
|
||||||
#else
|
|
||||||
typedef void *(*ifunc_resolver_t)(void);
|
|
||||||
real_sym_addr = ((ifunc_resolver_t)sym_addr)();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (NULL != symbol_size && NULL != real_sym_addr) {
|
|
||||||
ElfW(Sym) *real_sym = xdl_sym_by_addr(self, real_sym_addr);
|
|
||||||
if (NULL == real_sym) real_sym = xdl_dsym_by_addr(self, real_sym_addr);
|
|
||||||
if (NULL != real_sym) *symbol_size = real_sym->st_size;
|
|
||||||
}
|
|
||||||
return real_sym_addr;
|
|
||||||
} else {
|
|
||||||
if (NULL != symbol_size) *symbol_size = sym->st_size;
|
|
||||||
return (void *)(self->load_bias + sym->st_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) {
|
|
||||||
if (NULL == handle || NULL == symbol) return NULL;
|
|
||||||
if (NULL != symbol_size) *symbol_size = 0;
|
|
||||||
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
|
|
||||||
// load .dynsym only once
|
|
||||||
if (!self->dynsym_try_load) {
|
|
||||||
self->dynsym_try_load = true;
|
|
||||||
if (0 != xdl_dynsym_load(self)) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find symbol
|
|
||||||
if (NULL == self->dynsym) return NULL;
|
|
||||||
ElfW(Sym) *sym = NULL;
|
|
||||||
if (self->gnu_hash.buckets_cnt > 0) {
|
|
||||||
// use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
|
|
||||||
sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol);
|
|
||||||
}
|
|
||||||
if (NULL == sym && self->sysv_hash.buckets_cnt > 0) {
|
|
||||||
// use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
|
|
||||||
sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol);
|
|
||||||
}
|
|
||||||
if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL;
|
|
||||||
|
|
||||||
return xdl_resolve_symbol_address(self, sym, symbol_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
/*
|
|
||||||
* For internal symbols in .symtab, LLVM may add some suffixes (for example for thinLTO).
|
|
||||||
* The format of the suffix is: ".xxxx.[hash]". LLVM may add multiple suffixes at once.
|
|
||||||
* The symbol name after removing these all suffixes is called canonical name.
|
|
||||||
*
|
|
||||||
* Because the hash part in the suffix may change when recompiled, so here we only match
|
|
||||||
* the canonical name.
|
|
||||||
*
|
|
||||||
* IN ADDITION: According to C/C++ syntax, it is illegal for a function name to contain
|
|
||||||
* dot character('.'), either in the middle or at the end.
|
|
||||||
*
|
|
||||||
* samples:
|
|
||||||
*
|
|
||||||
* symbol name in .symtab lookup is match
|
|
||||||
* ---------------------- ---------------- --------
|
|
||||||
* abcd abc N
|
|
||||||
* abcd abcde N
|
|
||||||
* abcd abcd Y
|
|
||||||
* abcd.llvm.10190306339727611508 abc N
|
|
||||||
* abcd.llvm.10190306339727611508 abcd Y
|
|
||||||
* abcd.llvm.10190306339727611508 abcd. N
|
|
||||||
* abcd.llvm.10190306339727611508 abcd.llvm Y
|
|
||||||
* abcd.llvm.10190306339727611508 abcd.llvm. N
|
|
||||||
* abcd.__uniq.513291356003753 abcd.__uniq.51329 N
|
|
||||||
* abcd.__uniq.513291356003753 abcd.__uniq.513291356003753 Y
|
|
||||||
*/
|
|
||||||
// clang-format on
|
|
||||||
static inline bool xdl_dsym_is_match(const char *str, const char *sym, size_t sym_len) {
|
|
||||||
size_t str_len = strlen(str);
|
|
||||||
if (0 == str_len) return false;
|
|
||||||
|
|
||||||
if (str_len < sym_len) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
bool sym_len_match = (0 == memcmp(str, sym, sym_len));
|
|
||||||
if (str_len == sym_len)
|
|
||||||
return sym_len_match;
|
|
||||||
else // str_len > sym_len
|
|
||||||
return sym_len_match && (str[sym_len] == '.' || str[sym_len] == '$');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) {
|
|
||||||
if (NULL == handle || NULL == symbol) return NULL;
|
|
||||||
if (NULL != symbol_size) *symbol_size = 0;
|
|
||||||
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
|
|
||||||
// load .symtab only once
|
|
||||||
if (!self->symtab_try_load) {
|
|
||||||
self->symtab_try_load = true;
|
|
||||||
if (0 != xdl_symtab_load(self)) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find symbol
|
|
||||||
if (NULL == self->symtab) return NULL;
|
|
||||||
size_t symbol_len = strlen(symbol);
|
|
||||||
for (size_t i = 0; i < self->symtab_cnt; i++) {
|
|
||||||
ElfW(Sym) *sym = self->symtab + i;
|
|
||||||
|
|
||||||
if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue;
|
|
||||||
// if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue;
|
|
||||||
if (!xdl_dsym_is_match(self->strtab + sym->st_name, symbol, symbol_len)) continue;
|
|
||||||
|
|
||||||
if (NULL != symbol_size) *symbol_size = sym->st_size;
|
|
||||||
return (void *)(self->load_bias + sym->st_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum,
|
|
||||||
uintptr_t addr) {
|
|
||||||
if (addr < load_bias) return false;
|
|
||||||
|
|
||||||
uintptr_t vaddr = addr - load_bias;
|
|
||||||
for (size_t i = 0; i < dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD != phdr->p_type) continue;
|
|
||||||
|
|
||||||
if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
(void)size;
|
|
||||||
|
|
||||||
uintptr_t *pkg = (uintptr_t *)arg;
|
|
||||||
xdl_t **self = (xdl_t **)*pkg++;
|
|
||||||
uintptr_t addr = *pkg;
|
|
||||||
|
|
||||||
if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue
|
|
||||||
|
|
||||||
if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) {
|
|
||||||
// found the target ELF
|
|
||||||
if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // failed
|
|
||||||
if (NULL == ((*self)->pathname = strdup((const char *)info->dlpi_name))) {
|
|
||||||
free(*self);
|
|
||||||
*self = NULL;
|
|
||||||
return 1; // failed
|
|
||||||
}
|
|
||||||
(*self)->load_bias = info->dlpi_addr;
|
|
||||||
(*self)->dlpi_phdr = info->dlpi_phdr;
|
|
||||||
(*self)->dlpi_phnum = info->dlpi_phnum;
|
|
||||||
(*self)->dynsym_try_load = false;
|
|
||||||
(*self)->symtab_try_load = false;
|
|
||||||
return 1; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // continue
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_open_by_addr(void *addr) {
|
|
||||||
if (NULL == addr) return NULL;
|
|
||||||
|
|
||||||
xdl_t *self = NULL;
|
|
||||||
uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr};
|
|
||||||
xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT);
|
|
||||||
|
|
||||||
return (void *)self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) {
|
|
||||||
if (is_symtab) {
|
|
||||||
if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) false;
|
|
||||||
} else {
|
|
||||||
if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ELF_ST_TYPE(sym->st_info) != STT_TLS && offset >= sym->st_value &&
|
|
||||||
offset < sym->st_value + sym->st_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) {
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
|
|
||||||
// load .dynsym only once
|
|
||||||
if (!self->dynsym_try_load) {
|
|
||||||
self->dynsym_try_load = true;
|
|
||||||
if (0 != xdl_dynsym_load(self)) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find symbol
|
|
||||||
if (NULL == self->dynsym) return NULL;
|
|
||||||
uintptr_t offset = (uintptr_t)addr - self->load_bias;
|
|
||||||
if (self->gnu_hash.buckets_cnt > 0) {
|
|
||||||
const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset;
|
|
||||||
for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) {
|
|
||||||
uint32_t n = self->gnu_hash.buckets[i];
|
|
||||||
if (n < self->gnu_hash.symoffset) continue;
|
|
||||||
do {
|
|
||||||
ElfW(Sym) *sym = self->dynsym + n;
|
|
||||||
if (xdl_sym_is_match(sym, offset, false)) return sym;
|
|
||||||
} while ((chains_all[n++] & 1) == 0);
|
|
||||||
}
|
|
||||||
} else if (self->sysv_hash.chains_cnt > 0) {
|
|
||||||
for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) {
|
|
||||||
ElfW(Sym) *sym = self->dynsym + i;
|
|
||||||
if (xdl_sym_is_match(sym, offset, false)) return sym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) {
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
|
|
||||||
// load .symtab only once
|
|
||||||
if (!self->symtab_try_load) {
|
|
||||||
self->symtab_try_load = true;
|
|
||||||
if (0 != xdl_symtab_load(self)) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find symbol
|
|
||||||
if (NULL == self->symtab) return NULL;
|
|
||||||
uintptr_t offset = (uintptr_t)addr - self->load_bias;
|
|
||||||
for (size_t i = 0; i < self->symtab_cnt; i++) {
|
|
||||||
ElfW(Sym) *sym = self->symtab + i;
|
|
||||||
if (xdl_sym_is_match(sym, offset, true)) return sym;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_addr(void *addr, xdl_info_t *info, void **cache) {
|
|
||||||
return xdl_addr4(addr, info, cache, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags) {
|
|
||||||
if (NULL == addr || NULL == info || NULL == cache) return 0;
|
|
||||||
|
|
||||||
memset(info, 0, sizeof(Dl_info));
|
|
||||||
|
|
||||||
// find handle from cache
|
|
||||||
xdl_t *handle = NULL;
|
|
||||||
for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next)
|
|
||||||
if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break;
|
|
||||||
|
|
||||||
// create new handle, save handle to cache
|
|
||||||
if (NULL == handle) {
|
|
||||||
handle = (xdl_t *)xdl_open_by_addr(addr);
|
|
||||||
if (NULL == handle) return 0;
|
|
||||||
handle->next = *(xdl_t **)cache;
|
|
||||||
*(xdl_t **)cache = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum
|
|
||||||
info->dli_fbase = (void *)handle->load_bias;
|
|
||||||
info->dli_fname = handle->pathname;
|
|
||||||
info->dli_sname = NULL;
|
|
||||||
info->dli_saddr = 0;
|
|
||||||
info->dli_ssize = 0;
|
|
||||||
info->dlpi_phdr = handle->dlpi_phdr;
|
|
||||||
info->dlpi_phnum = (size_t)handle->dlpi_phnum;
|
|
||||||
|
|
||||||
// keep looking for: symbol name, symbol offset, symbol size
|
|
||||||
if (!(flags & XDL_NON_SYM)) {
|
|
||||||
ElfW(Sym) *sym;
|
|
||||||
if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) {
|
|
||||||
info->dli_sname = handle->dynstr + sym->st_name;
|
|
||||||
info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
|
|
||||||
info->dli_ssize = sym->st_size;
|
|
||||||
} else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) {
|
|
||||||
info->dli_sname = handle->strtab + sym->st_name;
|
|
||||||
info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
|
|
||||||
info->dli_ssize = sym->st_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void xdl_addr_clean(void **cache) {
|
|
||||||
if (NULL == cache) return;
|
|
||||||
|
|
||||||
xdl_t *handle = *((xdl_t **)cache);
|
|
||||||
while (NULL != handle) {
|
|
||||||
xdl_t *tmp = handle;
|
|
||||||
handle = handle->next;
|
|
||||||
xdl_close(tmp);
|
|
||||||
}
|
|
||||||
*cache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) {
|
|
||||||
if (NULL == callback) return 0;
|
|
||||||
|
|
||||||
return xdl_iterate_phdr_impl(callback, data, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_info(void *handle, int request, void *info) {
|
|
||||||
if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1;
|
|
||||||
|
|
||||||
xdl_t *self = (xdl_t *)handle;
|
|
||||||
xdl_info_t *dlinfo = (xdl_info_t *)info;
|
|
||||||
|
|
||||||
dlinfo->dli_fbase = (void *)self->load_bias;
|
|
||||||
dlinfo->dli_fname = self->pathname;
|
|
||||||
dlinfo->dli_sname = NULL;
|
|
||||||
dlinfo->dli_saddr = 0;
|
|
||||||
dlinfo->dli_ssize = 0;
|
|
||||||
dlinfo->dlpi_phdr = self->dlpi_phdr;
|
|
||||||
dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum;
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
//
|
|
||||||
// xDL version: 2.2.0
|
|
||||||
//
|
|
||||||
// xDL is an enhanced implementation of the Android DL series functions.
|
|
||||||
// For more information, documentation, and the latest version please check:
|
|
||||||
// https://github.com/hexhacking/xDL
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef IO_GITHUB_HEXHACKING_XDL
|
|
||||||
#define IO_GITHUB_HEXHACKING_XDL
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <link.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// same as Dl_info:
|
|
||||||
const char *dli_fname; // Pathname of shared object that contains address.
|
|
||||||
void *dli_fbase; // Address at which shared object is loaded.
|
|
||||||
const char *dli_sname; // Name of nearest symbol with address lower than addr.
|
|
||||||
void *dli_saddr; // Exact address of symbol named in dli_sname.
|
|
||||||
// added by xDL:
|
|
||||||
size_t dli_ssize; // Symbol size of nearest symbol with address lower than addr.
|
|
||||||
const ElfW(Phdr) *dlpi_phdr; // Pointer to array of ELF program headers for this object.
|
|
||||||
size_t dlpi_phnum; // Number of items in dlpi_phdr.
|
|
||||||
} xdl_info_t;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Default value for flags in xdl_open(), xdl_addr4(), and xdl_iterate_phdr().
|
|
||||||
//
|
|
||||||
#define XDL_DEFAULT 0x00
|
|
||||||
|
|
||||||
//
|
|
||||||
// Enhanced dlopen() / dlclose() / dlsym().
|
|
||||||
//
|
|
||||||
#define XDL_TRY_FORCE_LOAD 0x01
|
|
||||||
#define XDL_ALWAYS_FORCE_LOAD 0x02
|
|
||||||
void *xdl_open(const char *filename, int flags);
|
|
||||||
void *xdl_close(void *handle);
|
|
||||||
void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size);
|
|
||||||
void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Enhanced dladdr().
|
|
||||||
//
|
|
||||||
#define XDL_NON_SYM 0x01
|
|
||||||
int xdl_addr(void *addr, xdl_info_t *info, void **cache);
|
|
||||||
int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags);
|
|
||||||
void xdl_addr_clean(void **cache);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Enhanced dl_iterate_phdr().
|
|
||||||
//
|
|
||||||
#define XDL_FULL_PATHNAME 0x01
|
|
||||||
int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Custom dlinfo().
|
|
||||||
//
|
|
||||||
#define XDL_DI_DLINFO 1 // type of info: xdl_info_t
|
|
||||||
int xdl_info(void *handle, int request, void *info);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,297 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
#include "xdl_iterate.h"
|
|
||||||
|
|
||||||
#include <android/api-level.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <elf.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <link.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/auxv.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
#include "xdl_linker.h"
|
|
||||||
#include "xdl_util.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* =========================================================================================================
|
|
||||||
* API-LEVEL ANDROID-VERSION SOLUTION
|
|
||||||
* =========================================================================================================
|
|
||||||
* 16 4.1 /proc/self/maps
|
|
||||||
* 17 4.2 /proc/self/maps
|
|
||||||
* 18 4.3 /proc/self/maps
|
|
||||||
* 19 4.4 /proc/self/maps
|
|
||||||
* 20 4.4W /proc/self/maps
|
|
||||||
* ---------------------------------------------------------------------------------------------------------
|
|
||||||
* 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
|
|
||||||
* 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
|
|
||||||
* ---------------------------------------------------------------------------------------------------------
|
|
||||||
* 23 >= 6.0 dl_iterate_phdr() + linker/linker64 from getauxval(3)
|
|
||||||
* =========================================================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *);
|
|
||||||
extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
|
|
||||||
|
|
||||||
static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) {
|
|
||||||
uintptr_t min_vaddr = UINTPTR_MAX;
|
|
||||||
for (size_t i = 0; i < info->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type) {
|
|
||||||
if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return min_vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_iterate_open_or_rewind_maps(FILE **maps) {
|
|
||||||
if (NULL == *maps) {
|
|
||||||
*maps = fopen("/proc/self/maps", "r");
|
|
||||||
if (NULL == *maps) return -1;
|
|
||||||
} else
|
|
||||||
rewind(*maps);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) {
|
|
||||||
// open or rewind maps-file
|
|
||||||
if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1; // failed
|
|
||||||
|
|
||||||
char line[1024];
|
|
||||||
while (fgets(line, sizeof(line), *maps)) {
|
|
||||||
// check base address
|
|
||||||
uintptr_t start, end;
|
|
||||||
if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue;
|
|
||||||
if (base < start) break; // failed
|
|
||||||
if (base >= end) continue;
|
|
||||||
|
|
||||||
// get pathname
|
|
||||||
char *pathname = strchr(line, '/');
|
|
||||||
if (NULL == pathname) break; // failed
|
|
||||||
xdl_util_trim_ending(pathname);
|
|
||||||
|
|
||||||
// found it
|
|
||||||
strlcpy(buf, pathname, buf_len);
|
|
||||||
return 0; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1; // failed
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
uintptr_t *pkg = (uintptr_t *)arg;
|
|
||||||
xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++;
|
|
||||||
void *cb_arg = (void *)*pkg++;
|
|
||||||
FILE **maps = (FILE **)*pkg++;
|
|
||||||
uintptr_t linker_load_bias = *pkg++;
|
|
||||||
int flags = (int)*pkg;
|
|
||||||
|
|
||||||
// ignore invalid ELF
|
|
||||||
if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0;
|
|
||||||
|
|
||||||
// ignore linker if we have returned it already
|
|
||||||
if (linker_load_bias == info->dlpi_addr) return 0;
|
|
||||||
|
|
||||||
struct dl_phdr_info info_fixed;
|
|
||||||
info_fixed.dlpi_addr = info->dlpi_addr;
|
|
||||||
info_fixed.dlpi_name = info->dlpi_name;
|
|
||||||
info_fixed.dlpi_phdr = info->dlpi_phdr;
|
|
||||||
info_fixed.dlpi_phnum = info->dlpi_phnum;
|
|
||||||
info = &info_fixed;
|
|
||||||
|
|
||||||
// fix dlpi_phdr & dlpi_phnum (from memory)
|
|
||||||
if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) {
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr;
|
|
||||||
info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff);
|
|
||||||
info->dlpi_phnum = ehdr->e_phnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix dlpi_name (from /proc/self/maps)
|
|
||||||
if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) {
|
|
||||||
// get base address
|
|
||||||
uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info);
|
|
||||||
if (UINTPTR_MAX == min_vaddr) return 0; // ignore this ELF
|
|
||||||
uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr);
|
|
||||||
|
|
||||||
char buf[1024];
|
|
||||||
if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0; // ignore this ELF
|
|
||||||
|
|
||||||
info->dlpi_name = (const char *)buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// callback
|
|
||||||
return cb(info, size, cb_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t xdl_iterate_get_linker_base(void) {
|
|
||||||
if (NULL == getauxval) return 0;
|
|
||||||
|
|
||||||
uintptr_t base = (uintptr_t)getauxval(AT_BASE);
|
|
||||||
if (0 == base) return 0;
|
|
||||||
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0;
|
|
||||||
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base,
|
|
||||||
const char *pathname, uintptr_t *load_bias) {
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
|
|
||||||
|
|
||||||
struct dl_phdr_info info;
|
|
||||||
info.dlpi_name = pathname;
|
|
||||||
info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
|
|
||||||
info.dlpi_phnum = ehdr->e_phnum;
|
|
||||||
|
|
||||||
// get load bias
|
|
||||||
uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info);
|
|
||||||
if (UINTPTR_MAX == min_vaddr) return 0; // ignore invalid ELF
|
|
||||||
info.dlpi_addr = (ElfW(Addr))(base - min_vaddr);
|
|
||||||
if (NULL != load_bias) *load_bias = info.dlpi_addr;
|
|
||||||
|
|
||||||
return cb(&info, sizeof(struct dl_phdr_info), cb_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
|
|
||||||
if (NULL == dl_iterate_phdr) return 0;
|
|
||||||
|
|
||||||
int api_level = xdl_util_get_api_level();
|
|
||||||
FILE *maps = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
// dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27).
|
|
||||||
// Here we always try to get linker base address from auxv.
|
|
||||||
uintptr_t linker_load_bias = 0;
|
|
||||||
uintptr_t linker_base = xdl_iterate_get_linker_base();
|
|
||||||
if (0 != linker_base) {
|
|
||||||
if (0 !=
|
|
||||||
(r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias)))
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for other ELF
|
|
||||||
uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags};
|
|
||||||
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock();
|
|
||||||
r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg);
|
|
||||||
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock();
|
|
||||||
|
|
||||||
if (NULL != maps) fclose(maps);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) {
|
|
||||||
FILE *maps = fopen("/proc/self/maps", "r");
|
|
||||||
if (NULL == maps) return 0;
|
|
||||||
|
|
||||||
int r = 0;
|
|
||||||
char buf1[1024], buf2[1024];
|
|
||||||
char *line = buf1;
|
|
||||||
uintptr_t prev_base = 0;
|
|
||||||
bool try_next_line = false;
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(buf1), maps)) {
|
|
||||||
// Try to find an ELF which loaded by linker.
|
|
||||||
uintptr_t base, offset;
|
|
||||||
char exec;
|
|
||||||
if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset))
|
|
||||||
goto clean;
|
|
||||||
|
|
||||||
if ('-' == exec && 0 == offset) {
|
|
||||||
// r--p
|
|
||||||
prev_base = base;
|
|
||||||
line = (line == buf1 ? buf2 : buf1);
|
|
||||||
try_next_line = true;
|
|
||||||
continue;
|
|
||||||
} else if (exec == 'x') {
|
|
||||||
// r-xp
|
|
||||||
char *pathname = NULL;
|
|
||||||
if (try_next_line && 0 != offset) {
|
|
||||||
char *prev = (line == buf1 ? buf2 : buf1);
|
|
||||||
char *prev_pathname = strchr(prev, '/');
|
|
||||||
if (NULL == prev_pathname) goto clean;
|
|
||||||
|
|
||||||
pathname = strchr(line, '/');
|
|
||||||
if (NULL == pathname) goto clean;
|
|
||||||
|
|
||||||
xdl_util_trim_ending(prev_pathname);
|
|
||||||
xdl_util_trim_ending(pathname);
|
|
||||||
if (0 != strcmp(prev_pathname, pathname)) goto clean;
|
|
||||||
|
|
||||||
// we found the line with r-xp in the next line
|
|
||||||
base = prev_base;
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 != offset) goto clean;
|
|
||||||
|
|
||||||
// get pathname
|
|
||||||
if (NULL == pathname) {
|
|
||||||
pathname = strchr(line, '/');
|
|
||||||
if (NULL == pathname) goto clean;
|
|
||||||
xdl_util_trim_ending(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean;
|
|
||||||
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
|
|
||||||
struct dl_phdr_info info;
|
|
||||||
info.dlpi_name = pathname;
|
|
||||||
info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
|
|
||||||
info.dlpi_phnum = ehdr->e_phnum;
|
|
||||||
|
|
||||||
// callback
|
|
||||||
if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
|
||||||
try_next_line = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(maps);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
|
|
||||||
// iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86)
|
|
||||||
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
|
|
||||||
if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// iterate by dl_iterate_phdr()
|
|
||||||
return xdl_iterate_by_linker(cb, cb_arg, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) {
|
|
||||||
FILE *maps = NULL;
|
|
||||||
int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps);
|
|
||||||
if (NULL != maps) fclose(maps);
|
|
||||||
return r;
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
#ifndef IO_GITHUB_HEXHACKING_XDL_ITERATE
|
|
||||||
#define IO_GITHUB_HEXHACKING_XDL_ITERATE
|
|
||||||
|
|
||||||
#include <link.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg);
|
|
||||||
int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags);
|
|
||||||
|
|
||||||
int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,234 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2021-02-21.
|
|
||||||
|
|
||||||
#include "xdl_linker.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
#include "xdl_iterate.h"
|
|
||||||
#include "xdl_util.h"
|
|
||||||
|
|
||||||
#define XDL_LINKER_SYM_MUTEX "__dl__ZL10g_dl_mutex"
|
|
||||||
#define XDL_LINKER_SYM_DLOPEN_EXT_N "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv"
|
|
||||||
#define XDL_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv"
|
|
||||||
#define XDL_LINKER_SYM_DLOPEN_O "__dl__Z8__dlopenPKciPKv"
|
|
||||||
#define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen"
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
#define LIB "lib"
|
|
||||||
#else
|
|
||||||
#define LIB "lib64"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *);
|
|
||||||
typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *);
|
|
||||||
|
|
||||||
static pthread_mutex_t *xdl_linker_mutex = NULL;
|
|
||||||
static void *xdl_linker_dlopen = NULL;
|
|
||||||
|
|
||||||
typedef enum { MATCH_PREFIX, MATCH_SUFFIX } xdl_linker_match_type_t;
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
typedef struct {
|
|
||||||
xdl_linker_match_type_t type;
|
|
||||||
const char *value;
|
|
||||||
} xdl_linker_match_t;
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void *addr;
|
|
||||||
xdl_linker_match_t *matches;
|
|
||||||
size_t matches_cursor;
|
|
||||||
} xdl_linker_caller_t;
|
|
||||||
|
|
||||||
// https://source.android.com/docs/core/architecture/vndk/linker-namespace
|
|
||||||
// The following rules are loose and incomplete, you can add more according to your needs.
|
|
||||||
static xdl_linker_match_t xdl_linker_match_default[] = {{MATCH_SUFFIX, "/libc.so"}};
|
|
||||||
static xdl_linker_match_t xdl_linker_match_art[] = {{MATCH_SUFFIX, "/libart.so"}};
|
|
||||||
static xdl_linker_match_t xdl_linker_match_sphal[] = {{MATCH_PREFIX, "/vendor/" LIB "/egl/"},
|
|
||||||
{MATCH_PREFIX, "/vendor/" LIB "/hw/"},
|
|
||||||
{MATCH_PREFIX, "/vendor/" LIB "/"},
|
|
||||||
{MATCH_PREFIX, "/odm/" LIB "/"}};
|
|
||||||
static xdl_linker_match_t xdl_linker_match_vndk[] = {{MATCH_PREFIX, "/apex/com.android.vndk.v"},
|
|
||||||
{MATCH_PREFIX, "/vendor/" LIB "/vndk-sp/"},
|
|
||||||
{MATCH_PREFIX, "/odm/" LIB "/vndk-sp/"}};
|
|
||||||
static xdl_linker_caller_t xdl_linker_callers[] = {
|
|
||||||
{NULL, xdl_linker_match_default, sizeof(xdl_linker_match_default) / sizeof(xdl_linker_match_t)},
|
|
||||||
{NULL, xdl_linker_match_art, sizeof(xdl_linker_match_art) / sizeof(xdl_linker_match_t)},
|
|
||||||
{NULL, xdl_linker_match_sphal, sizeof(xdl_linker_match_sphal) / sizeof(xdl_linker_match_t)},
|
|
||||||
{NULL, xdl_linker_match_vndk, sizeof(xdl_linker_match_vndk) / sizeof(xdl_linker_match_t)}};
|
|
||||||
|
|
||||||
static void xdl_linker_init_symbols_impl(void) {
|
|
||||||
// find linker from: /proc/self/maps (API level < 18) or getauxval (API level >= 18)
|
|
||||||
void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT);
|
|
||||||
if (NULL == handle) return;
|
|
||||||
|
|
||||||
int api_level = xdl_util_get_api_level();
|
|
||||||
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) {
|
|
||||||
// == Android 5.x
|
|
||||||
xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
|
|
||||||
} else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
|
|
||||||
// == Android 7.x
|
|
||||||
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL);
|
|
||||||
if (NULL == xdl_linker_dlopen) {
|
|
||||||
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL);
|
|
||||||
xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
|
|
||||||
}
|
|
||||||
} else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) {
|
|
||||||
// == Android 8.x
|
|
||||||
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL);
|
|
||||||
} else if (api_level >= __ANDROID_API_P__) {
|
|
||||||
// >= Android 9.0
|
|
||||||
xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
xdl_close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdl_linker_init_symbols(void) {
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static bool inited = false;
|
|
||||||
if (!inited) {
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (!inited) {
|
|
||||||
xdl_linker_init_symbols_impl();
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void xdl_linker_lock(void) {
|
|
||||||
xdl_linker_init_symbols();
|
|
||||||
|
|
||||||
if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void xdl_linker_unlock(void) {
|
|
||||||
if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) {
|
|
||||||
for (size_t i = 0; i < info->dlpi_phnum; i++) {
|
|
||||||
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
|
|
||||||
if (PT_LOAD == phdr->p_type) {
|
|
||||||
return (void *)(info->dlpi_addr + phdr->p_vaddr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdl_linker_save_caller_addr(struct dl_phdr_info *info, xdl_linker_caller_t *caller,
|
|
||||||
size_t cursor) {
|
|
||||||
void *addr = xdl_linker_get_caller_addr(info);
|
|
||||||
if (NULL != addr) {
|
|
||||||
caller->addr = addr;
|
|
||||||
caller->matches_cursor = cursor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) {
|
|
||||||
(void)size, (void)arg;
|
|
||||||
if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue
|
|
||||||
|
|
||||||
int ret = 1; // OK
|
|
||||||
for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
|
|
||||||
xdl_linker_caller_t *caller = &xdl_linker_callers[i];
|
|
||||||
for (size_t j = 0; j < caller->matches_cursor; j++) {
|
|
||||||
xdl_linker_match_t *match = &caller->matches[j];
|
|
||||||
switch (match->type) {
|
|
||||||
case MATCH_PREFIX:
|
|
||||||
if (xdl_util_starts_with(info->dlpi_name, match->value)) {
|
|
||||||
xdl_linker_save_caller_addr(info, caller, j);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MATCH_SUFFIX:
|
|
||||||
if (xdl_util_ends_with(info->dlpi_name, match->value)) {
|
|
||||||
xdl_linker_save_caller_addr(info, caller, j);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NULL == caller->addr || 0 != caller->matches_cursor) ret = 0; // continue
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdl_linker_init_caller_addr_impl(void) {
|
|
||||||
xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, NULL, XDL_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdl_linker_init_caller_addr(void) {
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static bool inited = false;
|
|
||||||
if (!inited) {
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (!inited) {
|
|
||||||
xdl_linker_init_caller_addr_impl();
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *xdl_linker_force_dlopen(const char *filename) {
|
|
||||||
int api_level = xdl_util_get_api_level();
|
|
||||||
|
|
||||||
if (api_level <= __ANDROID_API_M__) {
|
|
||||||
// <= Android 6.0
|
|
||||||
return dlopen(filename, RTLD_NOW);
|
|
||||||
} else {
|
|
||||||
xdl_linker_init_symbols();
|
|
||||||
if (NULL == xdl_linker_dlopen) return NULL;
|
|
||||||
xdl_linker_init_caller_addr();
|
|
||||||
|
|
||||||
void *handle = NULL;
|
|
||||||
if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
|
|
||||||
// == Android 7.x
|
|
||||||
xdl_linker_lock();
|
|
||||||
for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
|
|
||||||
xdl_linker_caller_t *caller = &xdl_linker_callers[i];
|
|
||||||
if (NULL != caller->addr) {
|
|
||||||
handle = ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, caller->addr);
|
|
||||||
if (NULL != handle) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xdl_linker_unlock();
|
|
||||||
} else {
|
|
||||||
// >= Android 8.0
|
|
||||||
for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) {
|
|
||||||
xdl_linker_caller_t *caller = &xdl_linker_callers[i];
|
|
||||||
if (NULL != caller->addr) {
|
|
||||||
handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, caller->addr);
|
|
||||||
if (NULL != handle) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2021-02-21.
|
|
||||||
|
|
||||||
#ifndef IO_GITHUB_HEXHACKING_XDL_LINKER
|
|
||||||
#define IO_GITHUB_HEXHACKING_XDL_LINKER
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void xdl_linker_lock(void);
|
|
||||||
void xdl_linker_unlock(void);
|
|
||||||
|
|
||||||
void *xdl_linker_force_dlopen(const char *filename);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,187 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-11-08.
|
|
||||||
|
|
||||||
#include "xdl_lzma.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "xdl.h"
|
|
||||||
#include "xdl_util.h"
|
|
||||||
|
|
||||||
// LZMA library pathname & symbol names
|
|
||||||
#ifndef __LP64__
|
|
||||||
#define XDL_LZMA_PATHNAME "/system/lib/liblzma.so"
|
|
||||||
#else
|
|
||||||
#define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so"
|
|
||||||
#endif
|
|
||||||
#define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable"
|
|
||||||
#define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable"
|
|
||||||
#define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct"
|
|
||||||
#define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished"
|
|
||||||
#define XDL_LZMA_SYM_FREE "XzUnpacker_Free"
|
|
||||||
#define XDL_LZMA_SYM_CODE "XzUnpacker_Code"
|
|
||||||
|
|
||||||
// LZMA data type definition
|
|
||||||
#define SZ_OK 0
|
|
||||||
typedef struct ISzAlloc ISzAlloc;
|
|
||||||
typedef const ISzAlloc *ISzAllocPtr;
|
|
||||||
struct ISzAlloc {
|
|
||||||
void *(*Alloc)(ISzAllocPtr p, size_t size);
|
|
||||||
void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
|
|
||||||
};
|
|
||||||
typedef enum {
|
|
||||||
CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */
|
|
||||||
CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
|
|
||||||
CODER_STATUS_NOT_FINISHED, /* stream was not finished */
|
|
||||||
CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */
|
|
||||||
} ECoderStatus;
|
|
||||||
typedef enum {
|
|
||||||
CODER_FINISH_ANY, /* finish at any point */
|
|
||||||
CODER_FINISH_END /* block must be finished at the end */
|
|
||||||
} ECoderFinishMode;
|
|
||||||
|
|
||||||
// LZMA function type definition
|
|
||||||
typedef void (*xdl_lzma_crcgen_t)(void);
|
|
||||||
typedef void (*xdl_lzma_crc64gen_t)(void);
|
|
||||||
typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr);
|
|
||||||
typedef int (*xdl_lzma_isfinished_t)(const void *);
|
|
||||||
typedef void (*xdl_lzma_free_t)(void *);
|
|
||||||
typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode,
|
|
||||||
ECoderStatus *);
|
|
||||||
typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int,
|
|
||||||
ECoderFinishMode, ECoderStatus *);
|
|
||||||
|
|
||||||
// LZMA function pointor
|
|
||||||
static xdl_lzma_construct_t xdl_lzma_construct = NULL;
|
|
||||||
static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL;
|
|
||||||
static xdl_lzma_free_t xdl_lzma_free = NULL;
|
|
||||||
static void *xdl_lzma_code = NULL;
|
|
||||||
|
|
||||||
// LZMA init
|
|
||||||
static void xdl_lzma_init(void) {
|
|
||||||
void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD);
|
|
||||||
if (NULL == lzma) return;
|
|
||||||
|
|
||||||
xdl_lzma_crcgen_t crcgen = NULL;
|
|
||||||
xdl_lzma_crc64gen_t crc64gen = NULL;
|
|
||||||
if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end;
|
|
||||||
if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end;
|
|
||||||
if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL)))
|
|
||||||
goto end;
|
|
||||||
if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL)))
|
|
||||||
goto end;
|
|
||||||
if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end;
|
|
||||||
if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end;
|
|
||||||
crcgen();
|
|
||||||
crc64gen();
|
|
||||||
|
|
||||||
end:
|
|
||||||
xdl_close(lzma);
|
|
||||||
}
|
|
||||||
|
|
||||||
// LZMA internal alloc / free
|
|
||||||
static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) {
|
|
||||||
(void)p;
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) {
|
|
||||||
(void)p;
|
|
||||||
free(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) {
|
|
||||||
size_t src_offset = 0;
|
|
||||||
size_t dst_offset = 0;
|
|
||||||
size_t src_remaining;
|
|
||||||
size_t dst_remaining;
|
|
||||||
ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free};
|
|
||||||
long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned
|
|
||||||
ECoderStatus status;
|
|
||||||
int api_level = xdl_util_get_api_level();
|
|
||||||
|
|
||||||
// init and check
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static bool inited = false;
|
|
||||||
if (!inited) {
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
if (!inited) {
|
|
||||||
xdl_lzma_init();
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
if (NULL == xdl_lzma_code) return -1;
|
|
||||||
|
|
||||||
xdl_lzma_construct(&state, &alloc);
|
|
||||||
|
|
||||||
*dst_size = 2 * src_size;
|
|
||||||
*dst = NULL;
|
|
||||||
do {
|
|
||||||
*dst_size *= 2;
|
|
||||||
if (NULL == (*dst = realloc(*dst, *dst_size))) {
|
|
||||||
xdl_lzma_free(&state);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
src_remaining = src_size - src_offset;
|
|
||||||
dst_remaining = *dst_size - dst_offset;
|
|
||||||
|
|
||||||
int result;
|
|
||||||
if (api_level >= __ANDROID_API_Q__) {
|
|
||||||
xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code;
|
|
||||||
result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1,
|
|
||||||
CODER_FINISH_ANY, &status);
|
|
||||||
} else {
|
|
||||||
xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code;
|
|
||||||
result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining,
|
|
||||||
CODER_FINISH_ANY, &status);
|
|
||||||
}
|
|
||||||
if (SZ_OK != result) {
|
|
||||||
free(*dst);
|
|
||||||
xdl_lzma_free(&state);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
src_offset += src_remaining;
|
|
||||||
dst_offset += dst_remaining;
|
|
||||||
} while (status == CODER_STATUS_NOT_FINISHED);
|
|
||||||
|
|
||||||
xdl_lzma_free(&state);
|
|
||||||
|
|
||||||
if (!xdl_lzma_isfinished(&state)) {
|
|
||||||
free(*dst);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst_size = dst_offset;
|
|
||||||
*dst = realloc(*dst, *dst_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-11-08.
|
|
||||||
|
|
||||||
#ifndef IO_GITHUB_HEXHACKING_XDL_LZMA
|
|
||||||
#define IO_GITHUB_HEXHACKING_XDL_LZMA
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
#include "xdl_util.h"
|
|
||||||
|
|
||||||
#include <android/api-level.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
bool xdl_util_starts_with(const char *str, const char *start) {
|
|
||||||
while (*str && *str == *start) {
|
|
||||||
str++;
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '\0' == *start;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool xdl_util_ends_with(const char *str, const char *ending) {
|
|
||||||
size_t str_len = strlen(str);
|
|
||||||
size_t ending_len = strlen(ending);
|
|
||||||
|
|
||||||
if (ending_len > str_len) return false;
|
|
||||||
|
|
||||||
return 0 == strcmp(str + (str_len - ending_len), ending);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t xdl_util_trim_ending(char *start) {
|
|
||||||
char *end = start + strlen(start);
|
|
||||||
while (start < end && isspace((int)(*(end - 1)))) {
|
|
||||||
end--;
|
|
||||||
*end = '\0';
|
|
||||||
}
|
|
||||||
return (size_t)(end - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xdl_util_get_api_level_from_build_prop(void) {
|
|
||||||
char buf[128];
|
|
||||||
int api_level = -1;
|
|
||||||
|
|
||||||
FILE *fp = fopen("/system/build.prop", "r");
|
|
||||||
if (NULL == fp) goto end;
|
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
|
||||||
if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) {
|
|
||||||
api_level = atoi(buf + 21);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
end:
|
|
||||||
return (api_level > 0) ? api_level : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdl_util_get_api_level(void) {
|
|
||||||
static int xdl_util_api_level = -1;
|
|
||||||
|
|
||||||
if (xdl_util_api_level < 0) {
|
|
||||||
int api_level = android_get_device_api_level();
|
|
||||||
if (api_level < 0)
|
|
||||||
api_level = xdl_util_get_api_level_from_build_prop(); // compatible with unusual models
|
|
||||||
if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__;
|
|
||||||
|
|
||||||
__atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST);
|
|
||||||
}
|
|
||||||
|
|
||||||
return xdl_util_api_level;
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
// Copyright (c) 2020-2024 HexHacking Team
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Created by caikelun on 2020-10-04.
|
|
||||||
|
|
||||||
#ifndef IO_GITHUB_HEXHACKING_XDL_UTIL
|
|
||||||
#define IO_GITHUB_HEXHACKING_XDL_UTIL
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
#define XDL_UTIL_LINKER_BASENAME "linker"
|
|
||||||
#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_BASENAME "app_process32"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process"
|
|
||||||
#else
|
|
||||||
#define XDL_UTIL_LINKER_BASENAME "linker64"
|
|
||||||
#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_BASENAME "app_process64"
|
|
||||||
#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64"
|
|
||||||
#endif
|
|
||||||
#define XDL_UTIL_VDSO_BASENAME "[vdso]"
|
|
||||||
|
|
||||||
#define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \
|
|
||||||
({ \
|
|
||||||
__typeof__(exp) _rc; \
|
|
||||||
do { \
|
|
||||||
errno = 0; \
|
|
||||||
_rc = (exp); \
|
|
||||||
} while (_rc == -1 && errno == EINTR); \
|
|
||||||
_rc; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool xdl_util_starts_with(const char *str, const char *start);
|
|
||||||
bool xdl_util_ends_with(const char *str, const char *ending);
|
|
||||||
|
|
||||||
size_t xdl_util_trim_ending(char *start);
|
|
||||||
|
|
||||||
int xdl_util_get_api_level(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user