detour_attach, except for internal_detour_copy_instruction
This commit is contained in:
parent
f5a3491203
commit
4963fbc33c
4 changed files with 597 additions and 28 deletions
|
|
@ -7,12 +7,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct detour_align
|
||||
{
|
||||
uint8_t offset_target;
|
||||
uint8_t offset_trampoline;
|
||||
} detour_align;
|
||||
|
||||
typedef struct detour_trampoline
|
||||
{
|
||||
// An ARM64 instruction is 4 bytes long.
|
||||
|
|
@ -54,21 +48,25 @@ typedef struct detour_trampoline
|
|||
|
||||
static_assert(sizeof(detour_trampoline) == 192);
|
||||
|
||||
typedef uint32_t detours_arm64_opcode_t;
|
||||
enum {
|
||||
DETOUR_SIZE_OF_JMP = 12
|
||||
};
|
||||
|
||||
static inline detours_arm64_opcode_t fetch_opcode(const uint8_t* code)
|
||||
typedef uint32_t detour_arm64_opcode_t;
|
||||
|
||||
static inline detour_arm64_opcode_t fetch_opcode(const uint8_t* code)
|
||||
{
|
||||
return *(detours_arm64_opcode_t*)code;
|
||||
return *(detour_arm64_opcode_t*)code;
|
||||
}
|
||||
|
||||
static inline void write_opcode(uint8_t** int_out_code, const detours_arm64_opcode_t opcode)
|
||||
static inline void write_opcode(uint8_t** int_out_code, const detour_arm64_opcode_t opcode)
|
||||
{
|
||||
uint8_t* code = *int_out_code;
|
||||
*(detours_arm64_opcode_t*)code = opcode;
|
||||
*int_out_code += sizeof(detours_arm64_opcode_t);
|
||||
*(detour_arm64_opcode_t*)code = opcode;
|
||||
*int_out_code += sizeof(detour_arm64_opcode_t);
|
||||
}
|
||||
|
||||
struct detours_arm64_indirect_jmp {
|
||||
struct detour_arm64_indirect_jmp {
|
||||
struct {
|
||||
uint32_t Rd : 5;
|
||||
uint32_t immhi : 19;
|
||||
|
|
@ -91,7 +89,7 @@ struct detours_arm64_indirect_jmp {
|
|||
uint32_t br;
|
||||
};
|
||||
|
||||
union detours_arm64_indirect_imm {
|
||||
union detour_arm64_indirect_imm {
|
||||
struct {
|
||||
uint64_t pad : 12;
|
||||
uint64_t adrp_immlo : 2;
|
||||
|
|
@ -107,12 +105,12 @@ static inline uint8_t* internal_detour_gen_jmp_indirect(uint8_t* code, const uin
|
|||
// ldr x17, [x17, jmpval]
|
||||
// br x17
|
||||
|
||||
union detours_arm64_indirect_imm jmp_ind_addr;
|
||||
union detour_arm64_indirect_imm jmp_ind_addr;
|
||||
|
||||
jmp_ind_addr.value = (((uint64_t)jump_val) & 0xFFFFFFFFFFFFF000) -
|
||||
(((uint64_t)code) & 0xFFFFFFFFFFFFF000);
|
||||
|
||||
struct detours_arm64_indirect_jmp* ind_jmp = (struct detours_arm64_indirect_jmp*)code;
|
||||
struct detour_arm64_indirect_jmp* ind_jmp = (struct detour_arm64_indirect_jmp*)code;
|
||||
code = (uint8_t*)(ind_jmp + 1);
|
||||
|
||||
ind_jmp->ardp.Rd = 17;
|
||||
|
|
@ -135,6 +133,20 @@ static inline uint8_t* internal_detour_gen_jmp_indirect(uint8_t* code, const uin
|
|||
return code;
|
||||
}
|
||||
|
||||
static inline uint8_t* internal_detour_gen_jmp_immediate(uint8_t* code, uint8_t** inout_code_limit, uint8_t* jmp_val)
|
||||
{
|
||||
*inout_code_limit = *inout_code_limit - 8;
|
||||
uint8_t* literal = *inout_code_limit;
|
||||
|
||||
*(uint8_t**)literal = jmp_val;
|
||||
const int32_t delta = (int32_t)(literal - code);
|
||||
|
||||
write_opcode(&code, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n]
|
||||
write_opcode(&code, 0xd61f0000 | (17 << 5)); // BR X17
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static inline uint8_t* internal_detour_gen_brk(uint8_t* code, const uint8_t* limit)
|
||||
{
|
||||
while (code < limit) {
|
||||
|
|
@ -143,4 +155,81 @@ static inline uint8_t* internal_detour_gen_brk(uint8_t* code, const uint8_t* lim
|
|||
return code;
|
||||
}
|
||||
|
||||
static inline void internal_detour_find_jmp_bounds(uint8_t* code, detour_trampoline** out_lower, detour_trampoline** out_upper)
|
||||
{
|
||||
// The encoding used by detour_gen_jmp_indirect actually enables a
|
||||
// displacement of +/- 4GiB. In the future, this could be changed to
|
||||
// reflect that. For now, just reuse the x86 logic which is plenty.
|
||||
|
||||
const uintptr_t lo = internal_detour_2gb_below((uintptr_t)code);
|
||||
const uintptr_t hi = internal_detour_2gb_above((uintptr_t)code);
|
||||
DETOUR_TRACE(("[%p..%p..%p]\n", (void*)lo, (void*)code, (void*)hi));
|
||||
|
||||
*out_lower = (detour_trampoline*)lo;
|
||||
*out_upper = (detour_trampoline*)hi;
|
||||
}
|
||||
|
||||
static inline bool internal_detour_is_code_os_patched(const uint8_t* code)
|
||||
{
|
||||
// Identify whether the provided code pointer is a OS patch jump.
|
||||
// We can do this by checking if a branch (b <imm26>) is present, and if so,
|
||||
// it must be jumping to an HPAT page containing ldr <reg> [PC+PAGE_SIZE-4], br <reg>.
|
||||
const uint32_t opcode = fetch_opcode(code);
|
||||
|
||||
if ((opcode & 0xfc000000) != 0x14000000) {
|
||||
return false;
|
||||
}
|
||||
// The branch must be jumping forward if it's going into the HPAT.
|
||||
// Check that the sign bit is cleared.
|
||||
if ((opcode & 0x2000000) != 0) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t delta = (uint32_t)((opcode & 0x1FFFFFF) * 4);
|
||||
const uint8_t* branch_target = code + delta;
|
||||
|
||||
// Now inspect the opcodes of the code we jumped to in order to determine if it's HPAT.
|
||||
const uint32_t hpat_opcode1 = fetch_opcode(branch_target);
|
||||
const uint32_t hpat_opcode2 = fetch_opcode(branch_target + 4);
|
||||
|
||||
if (hpat_opcode1 != 0x58008010) { // ldr <reg> [PC+PAGE_SIZE]
|
||||
return false;
|
||||
}
|
||||
if (hpat_opcode2 != 0xd61f0200) { // br <reg>
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool internal_detour_does_code_end_function(const uint8_t* code)
|
||||
{
|
||||
const uint32_t opcode = fetch_opcode(code);
|
||||
// When the OS has patched a function entry point, it will incorrectly
|
||||
// appear as though the function is just a single branch instruction.
|
||||
if (internal_detour_is_code_os_patched(code)) {
|
||||
return false;
|
||||
}
|
||||
if ((opcode & 0xffbffc1f) == 0xd61f0000 || // ret/br <reg>
|
||||
(opcode & 0xfc000000) == 0x14000000) { // b <imm26>
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline uint32_t internal_detour_is_code_filler(const uint8_t* code)
|
||||
{
|
||||
if (*(uint32_t *)code == 0xd503201f) { // nop.
|
||||
return 4;
|
||||
}
|
||||
if (*(uint32_t *)code == 0x00000000) { // zero-filled padding.
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint8_t* internal_detour_skip_jmp(uint8_t* code)
|
||||
{
|
||||
// nothing special implemented
|
||||
return code;
|
||||
}
|
||||
|
||||
#endif //MACH_DETOURS_ARM64_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue