diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a0867a..41926ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ add_library(mach_detours SHARED src/mach_detours.c src/arm64/detours_arm64.h - src/arm64/detours_arm64_disasm.cpp + src/arm64/detours_arm64_disasm.c ) target_include_directories(mach_detours PUBLIC include diff --git a/src/arm64/detours_arm64_disasm.c b/src/arm64/detours_arm64_disasm.c new file mode 100644 index 0000000..83d3e8d --- /dev/null +++ b/src/arm64/detours_arm64_disasm.c @@ -0,0 +1,586 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Lysann Tranvouez. All rights reserved. + +#include "detours_disasm.h" + +#include + +// ReSharper disable CppDFAConstantParameter + +#define c_LR 30 // The register number for the Link Register +#define c_SP 31 // The register number for the Stack Pointer +#define c_NOP 0xd503201f // A nop instruction +#define c_BREAK (0xd4200000 | (0xf000 << 5)) // A break instruction + +// +// Problematic instructions: +// +// ADR 0ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x10000000 (l = low, h = high, d = Rd) +// ADRP 1ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x90000000 (l = low, h = high, d = Rd) +// +// B.cond 01010100 iiiiiiii iiiiiiii iii0cccc & 0xff000010 == 0x54000000 (i = delta = SignExtend(imm19:00, 64), c = cond) +// +// B 000101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x14000000 (i = delta = SignExtend(imm26:00, 64)) +// BL 100101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x94000000 (i = delta = SignExtend(imm26:00, 64)) +// +// CBNZ z0110101 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x35000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// CBZ z0110100 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x34000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// +// LDR Wt 00011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x18000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Xt 01011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x58000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDRSW 10011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x98000000 (i = SignExtend(imm19:00, 64), t = Rt) +// PRFM 11011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xd8000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR St 00011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x1c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Dt 01011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x5c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Qt 10011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x9c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xdc000000 (i = SignExtend(imm19:00, 64), t = Rt) +// +// TBNZ z0110111 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x37000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// TBZ z0110110 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x36000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// + +union detour_arm64_disasm_add_imm12 +{ + uint32_t assembled; + struct + { + uint32_t rd : 5; // Destination register + uint32_t rn : 5; // Source register + uint32_t imm12 : 12; // 12-bit immediate + uint32_t shift : 2; // shift (must be 0 or 1) + uint32_t opcode1 : 7; // Must be 0010001 == 0x11 + uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +}; +static uint32_t assemble_add_imm12_impl(uint32_t size, const uint32_t rd, const uint32_t rn, const uint32_t imm, const uint32_t shift) +{ + union detour_arm64_disasm_add_imm12 temp; + temp.s.rd = rd; + temp.s.rn = rn; + temp.s.imm12 = imm & 0xfff; + temp.s.shift = shift; + temp.s.opcode1 = 0x11; + temp.s.size = size; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} +static uint32_t assemble_add_imm12_32(const uint32_t rd, const uint32_t rn, const uint32_t imm, const uint32_t shift) { return assemble_add_imm12_impl(0, rd, rn, imm, shift); } +static uint32_t assemble_add_imm12_64(const uint32_t rd, const uint32_t rn, const uint32_t imm, const uint32_t shift) { return assemble_add_imm12_impl(1, rd, rn, imm, shift); } + +union detour_arm64_disasm_adr19 +{ + uint32_t assembled; + struct + { + uint32_t rd : 5; // Destination register + uint32_t imm19 : 19; // 19-bit upper immediate + uint32_t opcode1 : 5; // Must be 10000 == 0x10 + uint32_t imm2 : 2; // 2-bit lower immediate + uint32_t type : 1; // 0 = ADR, 1 = ADRP + } s; +}; +static inline union detour_arm64_disasm_adr19 decode_adr19(const uint32_t instruction) { union detour_arm64_disasm_adr19 result; result.assembled = instruction; return result; } +static inline int32_t adr19_get_imm(const union detour_arm64_disasm_adr19 val) +{ + const uint32_t Imm = (val.s.imm19 << 2) | val.s.imm2; + return (int32_t)(Imm << 11) >> 11; +} +static uint32_t assemble_adr19_impl(const uint32_t type, const uint32_t rd, const int32_t delta) +{ + union detour_arm64_disasm_adr19 temp; + temp.s.rd = rd; + temp.s.imm19 = (delta >> 2) & 0x7ffff; + temp.s.opcode1 = 0x10; + temp.s.imm2 = delta & 3; + temp.s.type = type; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} +static uint32_t assemble_adr19(const uint32_t rd, const int32_t delta) { return assemble_adr19_impl(0, rd, delta); } +static uint32_t assemble_adrp19(const uint32_t rd, const int32_t delta) { return assemble_adr19_impl(1, rd, delta); } + +union detour_arm64_disasm_bcc19 +{ + uint32_t assembled; + struct + { + uint32_t condition : 4; // Condition + uint32_t opcode1 : 1; // Must be 0 + uint32_t imm19 : 19; // 19-bit immediate + uint32_t opcode2 : 8; // Must be 01010100 == 0x54 + } s; +}; +static inline union detour_arm64_disasm_bcc19 decode_bcc19(const uint32_t instruction) { union detour_arm64_disasm_bcc19 result; result.assembled = instruction; return result; } +static int32_t bcc19_get_imm(const union detour_arm64_disasm_bcc19 val) { return (int32_t)(val.s.imm19 << 13) >> 11; } +static uint32_t assemble_bcc19(const uint32_t condition, const int32_t delta) +{ + union detour_arm64_disasm_bcc19 temp; + temp.s.condition = condition; + temp.s.opcode1 = 0; + temp.s.imm19 = delta >> 2; + temp.s.opcode2 = 0x54; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} + +union detour_arm64_disasm_branch26 +{ + uint32_t assembled; + struct + { + uint32_t imm26 : 26; // 26-bit immediate + uint32_t opcode1 : 5; // Must be 00101 == 0x5 + uint32_t link : 1; // 0 = B, 1 = BL + } s; +}; +static inline union detour_arm64_disasm_branch26 decode_branch26(const uint32_t instruction) { union detour_arm64_disasm_branch26 result; result.assembled = instruction; return result; } +static int32_t branch26_get_imm(const union detour_arm64_disasm_branch26 val) { return (int32_t)(val.s.imm26 << 6) >> 4; } +static uint32_t assemble_branch26_impl(const uint32_t link, const int32_t delta) +{ + union detour_arm64_disasm_branch26 temp; + temp.s.imm26 = delta >> 2; + temp.s.opcode1 = 0x5; + temp.s.link = link; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} +static uint32_t assemble_b26(int32_t delta) { return assemble_branch26_impl(0, delta); } +static uint32_t assemble_bl26(int32_t delta) { return assemble_branch26_impl(1, delta); } + +union detour_arm64_disasm_br +{ + uint32_t assembled; + struct + { + uint32_t opcode1 : 5; // Must be 00000 == 0 + uint32_t rn : 5; // Register number + uint32_t opcode2 : 22; // Must be 1101011000011111000000 == 0x3587c0 for Br + // 0x358fc0 for Brl + } s; +}; +static uint32_t assemble_br_impl(const uint32_t rn, const bool link) +{ + union detour_arm64_disasm_br temp; + temp.s.opcode1 = 0; + temp.s.rn = rn; + temp.s.opcode2 = 0x3587c0; + if (link) { + // ReSharper disable once CppDFANotInitializedField + temp.assembled |= 0x00200000; + } + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} +static uint32_t assemble_br(const uint32_t rn) { return assemble_br_impl(rn, false); } +static uint32_t assemble_brl(const uint32_t rn) { return assemble_br_impl(rn, true); } + +union detour_arm64_disasm_cbz19 +{ + uint32_t assembled; + struct + { + uint32_t rt : 5; // Register to test + uint32_t imm19 : 19; // 19-bit immediate + uint32_t nz : 1; // 0 = CBZ, 1 = CBNZ + uint32_t opcode1 : 6; // Must be 011010 == 0x1a + uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +}; +static inline union detour_arm64_disasm_cbz19 decode_cbz19(const uint32_t instruction) { union detour_arm64_disasm_cbz19 result; result.assembled = instruction; return result; } +static int32_t cbz19_get_imm(const union detour_arm64_disasm_cbz19 val) { return (int32_t)(val.s.imm19 << 13) >> 11; } +static uint32_t assemble_cbz19(const uint32_t size, const uint32_t nz, const uint32_t rt, const int32_t delta) +{ + union detour_arm64_disasm_cbz19 temp; + temp.s.rt = rt; + temp.s.imm19 = delta >> 2; + temp.s.nz = nz; + temp.s.opcode1 = 0x1a; + temp.s.size = size; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} + +union detour_arm64_disasm_ldr_lit19 +{ + uint32_t assembled; + struct + { + uint32_t rt : 5; // Destination register + uint32_t imm19 : 19; // 19-bit immediate + uint32_t opcode1 : 2; // Must be 0 + uint32_t fp_neon : 1; // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt + uint32_t opcode2 : 3; // Must be 011 = 3 + uint32_t size : 2; // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid + } s; +}; +static inline union detour_arm64_disasm_ldr_lit19 decode_ldr_lit19(const uint32_t instruction) { union detour_arm64_disasm_ldr_lit19 result; result.assembled = instruction; return result; } +static int32_t ldr_lit19_get_imm(const union detour_arm64_disasm_ldr_lit19 val) { return (int32_t)(val.s.imm19 << 13) >> 11; } +static uint32_t assemble_ldr_lit19(const uint32_t size, const uint32_t fpneon, const uint32_t rt, const int32_t delta) +{ + union detour_arm64_disasm_ldr_lit19 temp; + temp.s.rt = rt; + temp.s.imm19 = delta >> 2; + temp.s.opcode1 = 0; + temp.s.fp_neon = fpneon; + temp.s.opcode2 = 3; + temp.s.size = size; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} + +union detour_arm64_disasm_ldr_fp_neon_imm9 +{ + uint32_t assembled; + struct + { + uint32_t rt : 5; // Destination register + uint32_t rn : 5; // Base register + uint32_t imm12 : 12; // 12-bit immediate + uint32_t opcode1 : 1; // Must be 1 == 1 + uint32_t opc : 1; // Part of size + uint32_t opcode2 : 6; // Must be 111101 == 0x3d + uint32_t size : 2; // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit) + } s; +}; +static uint32_t assemble_ldr_fp_neon_imm9(const uint32_t size, const uint32_t rt, const uint32_t rn, const uint32_t imm) +{ + union detour_arm64_disasm_ldr_fp_neon_imm9 temp; + temp.s.rt = rt; + temp.s.rn = rn; + temp.s.imm12 = imm; + temp.s.opcode1 = 1; + temp.s.opc = size >> 2; + temp.s.opcode2 = 0x3d; + temp.s.size = size & 3; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} + +union detour_arm64_disasm_mov16 +{ + uint32_t assembled; + struct + { + uint32_t rd : 5; // Destination register + uint32_t imm16 : 16; // Immediate + uint32_t shift : 2; // Shift amount (0=0, 1=16, 2=32, 3=48) + uint32_t opcode : 6; // Must be 100101 == 0x25 + uint32_t type : 2; // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK + uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +}; +static uint32_t assemble_mov16_impl(const uint32_t size, const uint32_t type, const uint32_t rd, const uint32_t imm, const uint32_t shift) +{ + union detour_arm64_disasm_mov16 temp; + temp.s.rd = rd; + temp.s.imm16 = imm; + temp.s.shift = shift; + temp.s.opcode = 0x25; + temp.s.type = type; + temp.s.size = size; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} +static uint32_t assemble_movn32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(0, 0, rd, imm, shift); } +static uint32_t assemble_movn64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(1, 0, rd, imm, shift); } +static uint32_t assemble_movz32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(0, 2, rd, imm, shift); } +static uint32_t assemble_movz64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(1, 2, rd, imm, shift); } +static uint32_t assemble_movk32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(0, 3, rd, imm, shift); } +static uint32_t assemble_movk64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble_mov16_impl(1, 3, rd, imm, shift); } + +union detour_arm64_disasm_tbz14 +{ + uint32_t assembled; + struct + { + uint32_t rt : 5; // Register to test + uint32_t imm14 : 14; // 14-bit immediate + uint32_t bit : 5; // 5-bit index + uint32_t nz : 1; // 0 = TBZ, 1 = TBNZ + uint32_t opcode1 : 6; // Must be 011011 == 0x1b + uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +}; +static inline union detour_arm64_disasm_tbz14 decode_tbz14(const uint32_t instruction) { union detour_arm64_disasm_tbz14 result; result.assembled = instruction; return result; } +static int32_t tbz14_get_imm(const union detour_arm64_disasm_tbz14 val) { return (int32_t)(val.s.imm14 << 18) >> 16; } +static uint32_t assemble_tbz14(const uint32_t size, const uint32_t nz, const uint32_t rt, const uint32_t bit, const int32_t delta) +{ + union detour_arm64_disasm_tbz14 temp; + temp.s.rt = rt; + temp.s.imm14 = delta >> 2; + temp.s.bit = bit; + temp.s.nz = nz; + temp.s.opcode1 = 0x1b; + temp.s.size = size; + // ReSharper disable once CppDFANotInitializedField + return temp.assembled; +} + +static uint32_t get_instruction(const uint8_t* source) +{ + return ((uint32_t*)source)[0]; +} + +static uint8_t emit_instruction(uint32_t** dst_inst, const uint32_t instruction) +{ + *(*dst_inst)++ = instruction; + return sizeof(uint32_t); +} + +static uint8_t pure_copy32(const uint8_t* source, uint8_t* dest) +{ + *(uint32_t *)dest = *(const uint32_t*)source; + return sizeof(uint32_t); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// + +static uint8_t emit_mov_immediate(uint32_t** dst_inst, const uint8_t rd, const uint64_t immediate) +{ + uint32_t piece[4]; + piece[3] = (uint32_t)((immediate >> 48) & 0xffff); + piece[2] = (uint32_t)((immediate >> 32) & 0xffff); + piece[1] = (uint32_t)((immediate >> 16) & 0xffff); + piece[0] = (uint32_t)((immediate >> 0) & 0xffff); + int count = 0; + + // special case: MOVN with 32-bit dest + if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff) { + emit_instruction(dst_inst, assemble_movn32(rd, piece[0] ^ 0xffff, 0)); + count++; + } + // MOVN/MOVZ with 64-bit dest + else { + const int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000); + const int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff); + const uint32_t default_piece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000; + bool first = true; + for (int piece_num = 3; piece_num >= 0; piece_num--) { + uint32_t cur_piece = piece[piece_num]; + if (cur_piece != default_piece || (piece_num == 0 && first)) { + count++; + if (first) { + if (default_piece == 0xffff) { + emit_instruction(dst_inst, assemble_movn64(rd, cur_piece ^ 0xffff, piece_num)); + } else { + emit_instruction(dst_inst, assemble_movz64(rd, cur_piece, piece_num)); + } + first = false; + } else { + emit_instruction(dst_inst, assemble_movk64(rd, cur_piece, piece_num)); + } + } + } + } + return (uint8_t)(count * sizeof(uint32_t)); +} + +static uint8_t copy_adr(const uint8_t* source, uint8_t* dest, const uint32_t instruction) +{ + const union detour_arm64_disasm_adr19 decoded = decode_adr19(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + // ADR case + if (decoded.s.type == 0) { + const uint8_t* target = source + adr19_get_imm(decoded); + const int64_t delta = target - dest; + const int64_t delta_page = ((ptrdiff_t)target >> 12) - ((ptrdiff_t)dest >> 12); + + // output as ADR + if (delta >= -(1 << 20) && delta < (1 << 20)) { + emit_instruction(&dst_inst, assemble_adr19(decoded.s.rd, (int32_t)delta)); + } + // output as ADRP; ADD + else if (delta_page >= -(1 << 20) && (delta_page < (1 << 20))) { + emit_instruction(&dst_inst, assemble_adrp19(decoded.s.rd, (int32_t)delta_page)); + emit_instruction(&dst_inst, assemble_add_imm12_32(decoded.s.rd, decoded.s.rd, ((ptrdiff_t)target) & 0xfff, 0)); + } + // output as immediate move + else { + emit_mov_immediate(&dst_inst, decoded.s.rd, (ptrdiff_t)target); + } + } + // ADRP case + else { + uint8_t* target = (uint8_t*)((((ptrdiff_t)source >> 12) + adr19_get_imm(decoded)) << 12); + int64_t delta_page = ((ptrdiff_t)target >> 12) - ((ptrdiff_t)dest >> 12); + + // output as ADRP + if (delta_page >= -(1 << 20) && (delta_page < (1 << 20))) { + emit_instruction(&dst_inst, assemble_adrp19(decoded.s.rd, (int32_t)delta_page)); + } + + // output as immediate move + else { + emit_mov_immediate(&dst_inst, decoded.s.rd, (ptrdiff_t)target); + } + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +static uint8_t copy_bcc(const uint8_t* source, uint8_t* dest, const uint32_t instruction) +{ + const union detour_arm64_disasm_bcc19 decoded = decode_bcc19(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + const uint8_t* target = source + bcc19_get_imm(decoded); + const int64_t delta = target - dest; + const int64_t delta4 = target - (dest + 4); + + // output as BCC + if (delta >= -(1 << 20) && delta < (1 << 20)) { + emit_instruction(&dst_inst, assemble_bcc19(decoded.s.condition, (int32_t)delta)); + } + // output as BCC ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) { + emit_instruction(&dst_inst, assemble_bcc19(decoded.s.condition ^ 1, 8)); + emit_instruction(&dst_inst, assemble_b26((int32_t)delta4)); + } + // output as MOV x17, Target; BCC ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else { + emit_mov_immediate(&dst_inst, 17, (ptrdiff_t)target); + emit_instruction(&dst_inst, assemble_bcc19(decoded.s.condition ^ 1, 8)); + emit_instruction(&dst_inst, assemble_br(17)); + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +uint8_t copy_b_or_bl(const uint8_t* source, uint8_t* dest, const uint32_t instruction, const bool link) +{ + const union detour_arm64_disasm_branch26 decoded = decode_branch26(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + const uint8_t* target = source + branch26_get_imm(decoded); + const int64_t delta = target - dest; + + // output as B or BRL + if (delta >= -(1 << 27) && (delta < (1 << 27))) { + emit_instruction(&dst_inst, assemble_branch26_impl(link, (int32_t)delta)); + } + // output as MOV x17, Target; BR or BRL x17 (BIG assumption that x17 isn't being used for anything!!) + else { + emit_mov_immediate(&dst_inst, 17, (ptrdiff_t)target); + emit_instruction(&dst_inst, assemble_br_impl(17, link)); + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +uint8_t copy_cbz(const uint8_t* source, uint8_t* dest, const uint32_t instruction) +{ + const union detour_arm64_disasm_cbz19 decoded = decode_cbz19(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + const uint8_t* target = source + cbz19_get_imm(decoded); + const int64_t delta = target - dest; + const int64_t delta4 = target - (dest + 4); + + // output as CBZ/NZ + if (delta >= -(1 << 20) && delta < (1 << 20)) { + emit_instruction(&dst_inst, assemble_cbz19(decoded.s.size, decoded.s.nz, decoded.s.rt, (int32_t)delta)); + } + // output as CBNZ/Z ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) { + emit_instruction(&dst_inst, assemble_cbz19(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, 8)); + emit_instruction(&dst_inst, assemble_b26((int32_t)delta4)); + } + // output as MOV x17, Target; CBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else { + emit_mov_immediate(&dst_inst, 17, (ptrdiff_t)target); + emit_instruction(&dst_inst, assemble_cbz19(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, 8)); + emit_instruction(&dst_inst, assemble_br(17)); + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +uint8_t copy_tbz(const uint8_t* source, uint8_t* dest, const uint32_t instruction) +{ + const union detour_arm64_disasm_tbz14 decoded = decode_tbz14(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + const uint8_t* target = source + tbz14_get_imm(decoded); + const int64_t delta = target - dest; + const int64_t delta4 = target - (dest + 4); + + // output as TBZ/NZ + if (delta >= -(1 << 13) && delta < (1 << 13)) { + emit_instruction(&dst_inst, assemble_tbz14(decoded.s.size, decoded.s.nz, decoded.s.rt, decoded.s.bit, (int32_t)delta)); + } + // output as TBNZ/Z ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) { + emit_instruction(&dst_inst, assemble_tbz14(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, decoded.s.bit, 8)); + emit_instruction(&dst_inst, assemble_b26((int32_t)delta4)); + } + // output as MOV x17, Target; TBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else { + emit_mov_immediate(&dst_inst, 17, (ptrdiff_t)target); + emit_instruction(&dst_inst, assemble_tbz14(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, decoded.s.bit, 8)); + emit_instruction(&dst_inst, assemble_br(17)); + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +uint8_t copy_ldr_literal(const uint8_t* source, uint8_t* dest, const uint32_t instruction) +{ + const union detour_arm64_disasm_ldr_lit19 decoded = decode_ldr_lit19(instruction); + uint32_t* dst_inst = (uint32_t*)(dest); + + const uint8_t* target = source + ldr_lit19_get_imm(decoded); + const int64_t delta = target - dest; + + // output as LDR + if (delta >= -(1 << 21) && delta < (1 << 21)) { + emit_instruction(&dst_inst, assemble_ldr_lit19(decoded.s.size, decoded.s.fp_neon, decoded.s.rt, (int32_t)delta)); + } + // output as move immediate + else if (decoded.s.fp_neon == 0) { + uint64_t value = 0; + switch (decoded.s.size) + { + case 0: value = *(uint32_t*)target; break; + case 1: value = *(uint64_t*)target; break; + case 2: value = *(int32_t*)target; break; + } + emit_mov_immediate(&dst_inst, decoded.s.rt, value); + } + // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!) + else { + emit_mov_immediate(&dst_inst, 17, (ptrdiff_t)target); + emit_instruction(&dst_inst, assemble_ldr_fp_neon_imm9(2 + decoded.s.size, decoded.s.rt, 17, 0)); + } + + return (uint8_t)((uint8_t*)dst_inst - dest); +} + +const uint8_t* internal_detour_copy_instruction(uint8_t* dst, const uint8_t* src, uint32_t* out_extra_len) +{ + const uint32_t instruction = get_instruction(src); + + uint32_t copied_size; + if ((instruction & 0x1f000000) == 0x10000000) { + copied_size = copy_adr(src, dst, instruction); + } else if ((instruction & 0xff000010) == 0x54000000) { + copied_size = copy_bcc(src, dst, instruction); + } else if ((instruction & 0x7c000000) == 0x14000000) { + copied_size = copy_b_or_bl(src, dst, instruction, (instruction & 0x80000000) != 0); + } else if ((instruction & 0x7e000000) == 0x34000000) { + copied_size = copy_cbz(src, dst, instruction); + } else if ((instruction & 0x7e000000) == 0x36000000) { + copied_size = copy_tbz(src, dst, instruction); + } else if ((instruction & 0x3b000000) == 0x18000000) { + copied_size = copy_ldr_literal(src, dst, instruction); + } else { + copied_size = pure_copy32(src, dst); + } + + if (out_extra_len) { + *out_extra_len = copied_size - sizeof(uint32_t); + } + + return src + 4; +} diff --git a/src/arm64/detours_arm64_disasm.cpp b/src/arm64/detours_arm64_disasm.cpp deleted file mode 100644 index e14d83e..0000000 --- a/src/arm64/detours_arm64_disasm.cpp +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. - -#include "detours_disasm.h" - -#include - -#define c_LR 30 // The register number for the Link Register -#define c_SP 31 // The register number for the Stack Pointer -#define c_NOP 0xd503201f // A nop instruction -#define c_BREAK (0xd4200000 | (0xf000 << 5)) // A break instruction - -// -// Problematic instructions: -// -// ADR 0ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x10000000 (l = low, h = high, d = Rd) -// ADRP 1ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x90000000 (l = low, h = high, d = Rd) -// -// B.cond 01010100 iiiiiiii iiiiiiii iii0cccc & 0xff000010 == 0x54000000 (i = delta = SignExtend(imm19:00, 64), c = cond) -// -// B 000101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x14000000 (i = delta = SignExtend(imm26:00, 64)) -// BL 100101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x94000000 (i = delta = SignExtend(imm26:00, 64)) -// -// CBNZ z0110101 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x35000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) -// CBZ z0110100 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x34000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) -// -// LDR Wt 00011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x18000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDR Xt 01011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x58000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDRSW 10011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x98000000 (i = SignExtend(imm19:00, 64), t = Rt) -// PRFM 11011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xd8000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDR St 00011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x1c000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDR Dt 01011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x5c000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDR Qt 10011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x9c000000 (i = SignExtend(imm19:00, 64), t = Rt) -// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xdc000000 (i = SignExtend(imm19:00, 64), t = Rt) -// -// TBNZ z0110111 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x37000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) -// TBZ z0110110 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x36000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) -// - -union detour_arm64_disasm_add_imm12 -{ - uint32_t assembled; - struct - { - uint32_t rd : 5; // Destination register - uint32_t rn : 5; // Source register - uint32_t imm12 : 12; // 12-bit immediate - uint32_t shift : 2; // shift (must be 0 or 1) - uint32_t opcode1 : 7; // Must be 0010001 == 0x11 - uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit - } s; - static uint32_t assemble(uint32_t size, uint32_t rd, uint32_t rn, uint32_t imm, uint32_t shift) - { - detour_arm64_disasm_add_imm12 temp; - temp.s.rd = rd; - temp.s.rn = rn; - temp.s.imm12 = imm & 0xfff; - temp.s.shift = shift; - temp.s.opcode1 = 0x11; - temp.s.size = size; - return temp.assembled; - } - static uint32_t assemble_add_32(const uint32_t rd, const uint32_t rn, const uint32_t imm, const uint32_t shift) { return assemble(0, rd, rn, imm, shift); } - static uint32_t assemble_add_64(const uint32_t rd, const uint32_t rn, const uint32_t imm, const uint32_t shift) { return assemble(1, rd, rn, imm, shift); } -}; - -union detour_arm64_disasm_adr19 -{ - uint32_t assembled; - struct - { - uint32_t rd : 5; // Destination register - uint32_t imm19 : 19; // 19-bit upper immediate - uint32_t opcode1 : 5; // Must be 10000 == 0x10 - uint32_t imm2 : 2; // 2-bit lower immediate - uint32_t type : 1; // 0 = ADR, 1 = ADRP - } s; - inline int32_t imm() const { uint32_t Imm = (s.imm19 << 2) | s.imm2; return (int32_t)(Imm << 11) >> 11; } - static uint32_t assemble(uint32_t type, uint32_t rd, int32_t delta) - { - detour_arm64_disasm_adr19 temp; - temp.s.rd = rd; - temp.s.imm19 = (delta >> 2) & 0x7ffff; - temp.s.opcode1 = 0x10; - temp.s.imm2 = delta & 3; - temp.s.type = type; - return temp.assembled; - } - static uint32_t assemble_adr(uint32_t rd, int32_t delta) { return assemble(0, rd, delta); } - static uint32_t assemble_adrp(uint32_t rd, int32_t delta) { return assemble(1, rd, delta); } -}; - -union detour_arm64_disasm_bcc19 -{ - uint32_t assembled; - struct - { - uint32_t condition : 4; // Condition - uint32_t opcode1 : 1; // Must be 0 - uint32_t imm19 : 19; // 19-bit immediate - uint32_t opcode2 : 8; // Must be 01010100 == 0x54 - } s; - inline int32_t imm() const { return (int32_t)(s.imm19 << 13) >> 11; } - static uint32_t assemble_bcc(uint32_t condition, int32_t delta) - { - detour_arm64_disasm_bcc19 temp; - temp.s.condition = condition; - temp.s.opcode1 = 0; - temp.s.imm19 = delta >> 2; - temp.s.opcode2 = 0x54; - return temp.assembled; - } -}; - -union detour_arm64_disasm_branch26 -{ - uint32_t assembled; - struct - { - uint32_t imm26 : 26; // 26-bit immediate - uint32_t opcode1 : 5; // Must be 00101 == 0x5 - uint32_t link : 1; // 0 = B, 1 = BL - } s; - inline int32_t imm() const { return (int32_t)(s.imm26 << 6) >> 4; } - static uint32_t assemble(uint32_t link, int32_t delta) - { - detour_arm64_disasm_branch26 temp; - temp.s.imm26 = delta >> 2; - temp.s.opcode1 = 0x5; - temp.s.link = link; - return temp.assembled; - } - static uint32_t assemble_b(int32_t delta) { return assemble(0, delta); } - static uint32_t assemble_bl(int32_t delta) { return assemble(1, delta); } -}; - -union detour_arm64_disasm_br -{ - uint32_t assembled; - struct - { - uint32_t opcode1 : 5; // Must be 00000 == 0 - uint32_t rn : 5; // Register number - uint32_t opcode2 : 22; // Must be 1101011000011111000000 == 0x3587c0 for Br - // 0x358fc0 for Brl - } s; - static uint32_t assemble(uint32_t rn, bool link) - { - detour_arm64_disasm_br temp; - temp.s.opcode1 = 0; - temp.s.rn = rn; - temp.s.opcode2 = 0x3587c0; - if (link) - temp.assembled |= 0x00200000; - return temp.assembled; - } - static uint32_t assemble_br(uint32_t rn) - { - return assemble(rn, false); - } - static uint32_t assemble_brl(uint32_t rn) - { - return assemble(rn, true); - } -}; - -union detour_arm64_disasm_cbz19 -{ - uint32_t assembled; - struct - { - uint32_t rt : 5; // Register to test - uint32_t imm19 : 19; // 19-bit immediate - uint32_t nz : 1; // 0 = CBZ, 1 = CBNZ - uint32_t opcode1 : 6; // Must be 011010 == 0x1a - uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit - } s; - inline int32_t imm() const { return (int32_t)(s.imm19 << 13) >> 11; } - static uint32_t assemble(uint32_t size, uint32_t nz, uint32_t rt, int32_t delta) - { - detour_arm64_disasm_cbz19 temp; - temp.s.rt = rt; - temp.s.imm19 = delta >> 2; - temp.s.nz = nz; - temp.s.opcode1 = 0x1a; - temp.s.size = size; - return temp.assembled; - } -}; - -union detour_arm64_disasm_ldr_lit19 -{ - uint32_t assembled; - struct - { - uint32_t rt : 5; // Destination register - uint32_t imm19 : 19; // 19-bit immediate - uint32_t opcode1 : 2; // Must be 0 - uint32_t fp_neon : 1; // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt - uint32_t opcode2 : 3; // Must be 011 = 3 - uint32_t size : 2; // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid - } s; - inline int32_t imm() const { return (int32_t)(s.imm19 << 13) >> 11; } - static uint32_t assemble(uint32_t size, uint32_t fpneon, uint32_t rt, int32_t delta) - { - detour_arm64_disasm_ldr_lit19 temp; - temp.s.rt = rt; - temp.s.imm19 = delta >> 2; - temp.s.opcode1 = 0; - temp.s.fp_neon = fpneon; - temp.s.opcode2 = 3; - temp.s.size = size; - return temp.assembled; - } -}; - -union detour_arm64_disasm_ldr_fp_neon_imm9 -{ - uint32_t assembled; - struct - { - uint32_t rt : 5; // Destination register - uint32_t rn : 5; // Base register - uint32_t imm12 : 12; // 12-bit immediate - uint32_t opcode1 : 1; // Must be 1 == 1 - uint32_t opc : 1; // Part of size - uint32_t opcode2 : 6; // Must be 111101 == 0x3d - uint32_t size : 2; // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit) - } s; - static uint32_t assemble(uint32_t size, uint32_t rt, uint32_t rn, uint32_t imm) - { - detour_arm64_disasm_ldr_fp_neon_imm9 temp; - temp.s.rt = rt; - temp.s.rn = rn; - temp.s.imm12 = imm; - temp.s.opcode1 = 1; - temp.s.opc = size >> 2; - temp.s.opcode2 = 0x3d; - temp.s.size = size & 3; - return temp.assembled; - } -}; - -union detour_arm64_disasm_mov16 -{ - uint32_t assembled; - struct - { - uint32_t rd : 5; // Destination register - uint32_t imm16 : 16; // Immediate - uint32_t shift : 2; // Shift amount (0=0, 1=16, 2=32, 3=48) - uint32_t opcode : 6; // Must be 100101 == 0x25 - uint32_t type : 2; // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK - uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit - } s; - static uint32_t assemble(uint32_t size, uint32_t type, uint32_t rd, uint32_t imm, uint32_t shift) - { - detour_arm64_disasm_mov16 temp; - temp.s.rd = rd; - temp.s.imm16 = imm; - temp.s.shift = shift; - temp.s.opcode = 0x25; - temp.s.type = type; - temp.s.size = size; - return temp.assembled; - } - static uint32_t assemble_movn32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(0, 0, rd, imm, shift); } - static uint32_t assemble_movn64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(1, 0, rd, imm, shift); } - static uint32_t assemble_movz32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(0, 2, rd, imm, shift); } - static uint32_t assemble_movz64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(1, 2, rd, imm, shift); } - static uint32_t assemble_movk32(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(0, 3, rd, imm, shift); } - static uint32_t assemble_movk64(uint32_t rd, uint32_t imm, uint32_t shift) { return assemble(1, 3, rd, imm, shift); } -}; - -union detour_arm64_disasm_tbz14 -{ - uint32_t assembled; - struct - { - uint32_t rt : 5; // Register to test - uint32_t imm14 : 14; // 14-bit immediate - uint32_t bit : 5; // 5-bit index - uint32_t nz : 1; // 0 = TBZ, 1 = TBNZ - uint32_t opcode1 : 6; // Must be 011011 == 0x1b - uint32_t size : 1; // 0 = 32-bit, 1 = 64-bit - } s; - inline int32_t imm() const { return (int32_t)(s.imm14 << 18) >> 16; } - static uint32_t assemble(uint32_t size, uint32_t nz, uint32_t rt, uint32_t bit, int32_t delta) - { - detour_arm64_disasm_tbz14 temp; - temp.s.rt = rt; - temp.s.imm14 = delta >> 2; - temp.s.bit = bit; - temp.s.nz = nz; - temp.s.opcode1 = 0x1b; - temp.s.size = size; - return temp.assembled; - } -}; - -static uint32_t get_instruction(uint8_t* source) -{ - return ((uint32_t*)source)[0]; -} - -static uint8_t emit_instruction(uint32_t*& dst_inst, uint32_t instruction) -{ - *dst_inst++ = instruction; - return sizeof(uint32_t); -}; - -uint8_t pure_copy32(uint8_t* source, uint8_t* dest) -{ - *(uint32_t *)dest = *(uint32_t*)source; - return sizeof(uint32_t); -} - -/////////////////////////////////////////////////////////// Disassembler Code. -// - -uint8_t emit_mov_immediate(uint32_t*& dst_inst, uint8_t rd, uint64_t immediate) -{ - uint32_t piece[4]; - piece[3] = (uint32_t)((immediate >> 48) & 0xffff); - piece[2] = (uint32_t)((immediate >> 32) & 0xffff); - piece[1] = (uint32_t)((immediate >> 16) & 0xffff); - piece[0] = (uint32_t)((immediate >> 0) & 0xffff); - int count = 0; - - // special case: MOVN with 32-bit dest - if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff) - { - emit_instruction(dst_inst, detour_arm64_disasm_mov16::assemble_movn32(rd, piece[0] ^ 0xffff, 0)); - count++; - } - - // MOVN/MOVZ with 64-bit dest - else - { - int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000); - int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff); - uint32_t default_piece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000; - bool first = true; - for (int piece_num = 3; piece_num >= 0; piece_num--) - { - uint32_t cur_piece = piece[piece_num]; - if (cur_piece != default_piece || (piece_num == 0 && first)) - { - count++; - if (first) - { - if (default_piece == 0xffff) - { - emit_instruction(dst_inst, detour_arm64_disasm_mov16::assemble_movn64(rd, cur_piece ^ 0xffff, piece_num)); - } - else - { - emit_instruction(dst_inst, detour_arm64_disasm_mov16::assemble_movz64(rd, cur_piece, piece_num)); - } - first = false; - } - else - { - emit_instruction(dst_inst, detour_arm64_disasm_mov16::assemble_movk64(rd, cur_piece, piece_num)); - } - } - } - } - return (uint8_t)(count * sizeof(uint32_t)); -} - -uint8_t copy_adr(uint8_t* source, uint8_t* dest, uint32_t instruction) -{ - detour_arm64_disasm_adr19& decoded = (detour_arm64_disasm_adr19&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - // ADR case - if (decoded.s.type == 0) - { - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - int64_t delta_page = ((std::ptrdiff_t)target >> 12) - ((std::ptrdiff_t)dest >> 12); - - // output as ADR - if (delta >= -(1 << 20) && delta < (1 << 20)) - { - emit_instruction(dst_inst, detour_arm64_disasm_adr19::assemble_adr(decoded.s.rd, (int32_t)delta)); - } - - // output as ADRP; ADD - else if (delta_page >= -(1 << 20) && (delta_page < (1 << 20))) - { - emit_instruction(dst_inst, detour_arm64_disasm_adr19::assemble_adrp(decoded.s.rd, (int32_t)delta_page)); - emit_instruction(dst_inst, detour_arm64_disasm_add_imm12::assemble_add_32(decoded.s.rd, decoded.s.rd, ((std::ptrdiff_t)target) & 0xfff, 0)); - } - - // output as immediate move - else - { - emit_mov_immediate(dst_inst, decoded.s.rd, (std::ptrdiff_t)target); - } - } - - // ADRP case - else - { - uint8_t* target = (uint8_t*)((((std::ptrdiff_t)source >> 12) + decoded.imm()) << 12); - int64_t delta_page = ((std::ptrdiff_t)target >> 12) - ((std::ptrdiff_t)dest >> 12); - - // output as ADRP - if (delta_page >= -(1 << 20) && (delta_page < (1 << 20))) - { - emit_instruction(dst_inst, detour_arm64_disasm_adr19::assemble_adrp(decoded.s.rd, (int32_t)delta_page)); - } - - // output as immediate move - else - { - emit_mov_immediate(dst_inst, decoded.s.rd, (std::ptrdiff_t)target); - } - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t copy_bcc(uint8_t* source, uint8_t* dest, uint32_t instruction) -{ - detour_arm64_disasm_bcc19& decoded = (detour_arm64_disasm_bcc19&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - int64_t delta4 = target - (dest + 4); - - // output as BCC - if (delta >= -(1 << 20) && delta < (1 << 20)) - { - emit_instruction(dst_inst, detour_arm64_disasm_bcc19::assemble_bcc(decoded.s.condition, (int32_t)delta)); - } - - // output as BCC ; B - else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) - { - emit_instruction(dst_inst, detour_arm64_disasm_bcc19::assemble_bcc(decoded.s.condition ^ 1, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_branch26::assemble_b((int32_t)delta4)); - } - - // output as MOV x17, Target; BCC ; BR x17 (BIG assumption that x17 isn't being used for anything!!) - else - { - emit_mov_immediate(dst_inst, 17, (std::ptrdiff_t)target); - emit_instruction(dst_inst, detour_arm64_disasm_bcc19::assemble_bcc(decoded.s.condition ^ 1, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_br::assemble_br(17)); - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t copy_b_or_bl(uint8_t* source, uint8_t* dest, uint32_t instruction, bool link) -{ - detour_arm64_disasm_branch26& decoded = (detour_arm64_disasm_branch26&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - - // output as B or BRL - if (delta >= -(1 << 27) && (delta < (1 << 27))) - { - emit_instruction(dst_inst, detour_arm64_disasm_branch26::assemble(link, (int32_t)delta)); - } - - // output as MOV x17, Target; BR or BRL x17 (BIG assumption that x17 isn't being used for anything!!) - else - { - emit_mov_immediate(dst_inst, 17, (std::ptrdiff_t)target); - emit_instruction(dst_inst, detour_arm64_disasm_br::assemble(17, link)); - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t copy_cbz(uint8_t* source, uint8_t* dest, uint32_t instruction) -{ - detour_arm64_disasm_cbz19& decoded = (detour_arm64_disasm_cbz19&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - int64_t delta4 = target - (dest + 4); - - // output as CBZ/NZ - if (delta >= -(1 << 20) && delta < (1 << 20)) - { - emit_instruction(dst_inst, detour_arm64_disasm_cbz19::assemble(decoded.s.size, decoded.s.nz, decoded.s.rt, (int32_t)delta)); - } - - // output as CBNZ/Z ; B - else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) - { - emit_instruction(dst_inst, detour_arm64_disasm_cbz19::assemble(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_branch26::assemble_b((int32_t)delta4)); - } - - // output as MOV x17, Target; CBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) - else - { - emit_mov_immediate(dst_inst, 17, (std::ptrdiff_t)target); - emit_instruction(dst_inst, detour_arm64_disasm_cbz19::assemble(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_br::assemble_br(17)); - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t copy_tbz(uint8_t* source, uint8_t* dest, uint32_t instruction) -{ - detour_arm64_disasm_tbz14& decoded = (detour_arm64_disasm_tbz14&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - int64_t delta4 = target - (dest + 4); - - // output as TBZ/NZ - if (delta >= -(1 << 13) && delta < (1 << 13)) - { - emit_instruction(dst_inst, detour_arm64_disasm_tbz14::assemble(decoded.s.size, decoded.s.nz, decoded.s.rt, decoded.s.bit, (int32_t)delta)); - } - - // output as TBNZ/Z ; B - else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) - { - emit_instruction(dst_inst, detour_arm64_disasm_tbz14::assemble(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, decoded.s.bit, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_branch26::assemble_b((int32_t)delta4)); - } - - // output as MOV x17, Target; TBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) - else - { - emit_mov_immediate(dst_inst, 17, (std::ptrdiff_t)target); - emit_instruction(dst_inst, detour_arm64_disasm_tbz14::assemble(decoded.s.size, decoded.s.nz ^ 1, decoded.s.rt, decoded.s.bit, 8)); - emit_instruction(dst_inst, detour_arm64_disasm_br::assemble_br(17)); - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t copy_ldr_literal(uint8_t* source, uint8_t* dest, uint32_t instruction) -{ - detour_arm64_disasm_ldr_lit19& decoded = (detour_arm64_disasm_ldr_lit19&)(instruction); - uint32_t* dst_inst = (uint32_t*)(dest); - - uint8_t* target = source + decoded.imm(); - int64_t delta = target - dest; - - // output as LDR - if (delta >= -(1 << 21) && delta < (1 << 21)) - { - emit_instruction(dst_inst, detour_arm64_disasm_ldr_lit19::assemble(decoded.s.size, decoded.s.fp_neon, decoded.s.rt, (int32_t)delta)); - } - - // output as move immediate - else if (decoded.s.fp_neon == 0) - { - uint64_t value = 0; - switch (decoded.s.size) - { - case 0: value = *(uint32_t*)target; break; - case 1: value = *(uint64_t*)target; break; - case 2: value = *(int32_t*)target; break; - } - emit_mov_immediate(dst_inst, decoded.s.rt, value); - } - - // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!) - else - { - emit_mov_immediate(dst_inst, 17, (std::ptrdiff_t)target); - emit_instruction(dst_inst, detour_arm64_disasm_ldr_fp_neon_imm9::assemble(2 + decoded.s.size, decoded.s.rt, 17, 0)); - } - - return (uint8_t)((uint8_t*)dst_inst - dest); -} - -uint8_t* internal_detour_copy_instruction(uint8_t* dst, uint8_t* src, uint32_t* out_extra_len) -{ - const uint32_t instruction = get_instruction(src); - - uint32_t copied_size; - if ((instruction & 0x1f000000) == 0x10000000) { - copied_size = copy_adr(src, dst, instruction); - } else if ((instruction & 0xff000010) == 0x54000000) { - copied_size = copy_bcc(src, dst, instruction); - } else if ((instruction & 0x7c000000) == 0x14000000) { - copied_size = copy_b_or_bl(src, dst, instruction, (instruction & 0x80000000) != 0); - } else if ((instruction & 0x7e000000) == 0x34000000) { - copied_size = copy_cbz(src, dst, instruction); - } else if ((instruction & 0x7e000000) == 0x36000000) { - copied_size = copy_tbz(src, dst, instruction); - } else if ((instruction & 0x3b000000) == 0x18000000) { - copied_size = copy_ldr_literal(src, dst, instruction); - } else { - copied_size = pure_copy32(src, dst); - } - - if (out_extra_len) { - *out_extra_len = copied_size - sizeof(uint32_t); - } - - return src + 4; -} diff --git a/src/detours_disasm.h b/src/detours_disasm.h index a9646a0..3c26cb9 100644 --- a/src/detours_disasm.h +++ b/src/detours_disasm.h @@ -5,16 +5,8 @@ #ifndef MACH_DETOURS_DISASM_H #define MACH_DETOURS_DISASM_H -#ifdef __cplusplus -extern "C" { -#endif - #include -uint8_t* internal_detour_copy_instruction(uint8_t* dst, uint8_t* src, uint32_t* out_extra_len); - -#ifdef __cplusplus -} -#endif +const uint8_t* internal_detour_copy_instruction(uint8_t* dst, const uint8_t* src, uint32_t* out_extra_len); #endif //MACH_DETOURS_DISASM_H diff --git a/src/mach_detours.c b/src/mach_detours.c index 366b415..6c2ba17 100644 --- a/src/mach_detours.c +++ b/src/mach_detours.c @@ -758,7 +758,7 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour DETOUR_TRACE(("detours: trampoline=%p, detour=%p\n", trampoline, detour)); // Determine the number of movable target instructions. - uint8_t* src = target; + const uint8_t* src = target; uint8_t* trampoline_code = trampoline->code; uint8_t* trampoline_code_limit = trampoline_code + sizeof(trampoline->code); uint32_t offset_target = 0;