Skip to content

Commit

Permalink
More fixes for 64 bit
Browse files Browse the repository at this point in the history
  • Loading branch information
rafradek committed Jun 8, 2024
1 parent 1e4f3f1 commit 84067b7
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 323 deletions.
1 change: 1 addition & 0 deletions AMBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ sourceFiles += [

'src/mem/alloc.cpp',
'src/mem/detour.cpp',
'src/mem/func_copy.cpp',
'src/mem/extract.cpp',
'src/mem/hook.cpp',
'src/mem/patch.cpp',
Expand Down
13 changes: 12 additions & 1 deletion src/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ template<class C, typename RET, typename... PARAMS> using MemberPtrTypeConst = R
template<class C, typename RET, typename... PARAMS> using MemberPtrTypeVa = RET (C::*)(PARAMS..., ...);
template<class C, typename RET, typename... PARAMS> using MemberPtrTypeVaConst = RET (C::*)(PARAMS..., ...) const;

#if defined __GNUC__ && !defined __clang__ && !defined PLATFORM_64BITS
template<class C, typename RET, typename... PARAMS> using MemberPtrTypeRegcall = RET (C::*)(PARAMS...) __gcc_regcall;
template<class C, typename RET, typename... PARAMS> using MemberPtrTypeConstRegcall = RET (C::*)(PARAMS...) const __gcc_regcall;
#endif

#if defined __clang__

#error TODO
Expand Down Expand Up @@ -183,7 +188,13 @@ inline void *GetAddrOfMemberFunc(MemberPtrTypeVaConst<C, RET, PARAMS...> ptr)
{
return GetAddrOfMemberFunc(reinterpret_cast<MemberPtrType<C, RET, PARAMS...>>(ptr));
}

#if defined __GNUC__ && !defined __clang__ && !defined PLATFORM_64BITS
template<class C, typename RET, typename... PARAMS>
inline void *GetAddrOfMemberFunc(MemberPtrTypeRegcall<C, RET, PARAMS...> ptr)
{
return GetAddrOfMemberFunc(reinterpret_cast<MemberPtrType<C, RET, PARAMS...>>(ptr));
}
#endif

template<class C, typename RET, typename... PARAMS>
int GetVIdxOfMemberFunc(MemberPtrType<C, RET, PARAMS...> ptr)
Expand Down
296 changes: 5 additions & 291 deletions src/mem/detour.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "mem/protect.h"
#include "mem/opcode.h"
#include "mem/wrapper.h"
#include "mem/func_copy.h"
#include "util/backtrace.h"
#include "util/demangle.h"
#include "util/misc.h"
Expand All @@ -14,8 +15,8 @@
#include <regex>


#if !(defined(__i386) || defined(_M_IX86) || defined(__x86_64__) )
#error Architecture must be IA32
#if !(PLATFORM_64BITS)
#error Architecture must be IA32/64
#endif


Expand All @@ -29,243 +30,6 @@
#include "util/trace.h"


/* get number of instruction operands */
static unsigned int UD86_num_operands(struct ud *ud)
{
for (unsigned int i = 0; i < 4; ++i) {
if (ud_insn_opr(ud, i) == nullptr) return i;
}
return 4;
}

CON_COMMAND(sig_test_udis, "") {
std::vector<uint8_t> bytes;
int value;

for (int i = 1; i < args.ArgC(); i++) {
value = strtol( args[i], nullptr, 16);
bytes.push_back(value);
}

if (bytes.empty()) return;

ud_t ud;
ud_init(&ud);
#ifdef PLATFORM_64BITS
ud_set_mode(&ud, 64);
#else
ud_set_mode(&ud, 32);
#endif
ud_set_pc(&ud, (uint64_t)bytes.data());
ud_set_input_buffer(&ud, bytes.data(), 0x100);

int len = ud_decode(&ud);
if (len == 0) {
Msg("Error decoding\n");
}

auto mnemonic = ud_insn_mnemonic(&ud);
Msg("Instr %s %d | length %d offset %d ptr %p opr count %d\n", ud_insn_asm(&ud), ud_insn_mnemonic(&ud), ud_insn_len(&ud), ud_insn_off(&ud), ud_insn_ptr(&ud), UD86_num_operands(&ud));
for (int i = 0; i < UD86_num_operands(&ud); i++) {
const auto *op0 = ud_insn_opr(&ud, i);
Msg("op %d type %d size %d lval %llx base %d index %d access %d\n", i, op0->type, op0->size, op0->lval.uqword, op0->base, op0->index, op0->access);
}
}

/* fix [rip + disp32] operands*/
static bool UD86_insn_fix_disp32(struct ud *ud, const uint8_t *func = nullptr, uint8_t *dest = nullptr)
{
auto mnemonic = ud_insn_mnemonic(ud);
if (ud_insn_opr(ud, 0) == nullptr) return false;

//Msg("Instr %s %d | length %d offset %d ptr %p opr count %d\n", ud_insn_asm(ud), ud_insn_mnemonic(ud), ud_insn_len(ud), ud_insn_off(ud), ud_insn_ptr(ud), UD86_num_operands(ud));
//if (ud_insn_len(ud) != 5) return false;
//if (UD86_num_operands(ud) != 1) return false;

int32_t writeOffset = -1;
uint64_t dispValue = -1ULL;
int memArgs = 0;
int immArgSize = 0;

for (unsigned int i = 0; i < UD86_num_operands(ud); i++) {
auto op = ud_insn_opr(ud, i);
if (op->type == UD_OP_MEM && op->base == UD_R_RIP) {
dispValue = op->lval.uqword;
memArgs++;
}
if (op->type == UD_OP_IMM) {
immArgSize = op->size / 8;
}
}
if (immArgSize > 0) {
Msg("Instr %s %d | length %d offset %d ptr %p opr count %d\n", ud_insn_asm(ud), ud_insn_mnemonic(ud), ud_insn_len(ud), ud_insn_off(ud), ud_insn_ptr(ud), UD86_num_operands(ud));
for (int i = 0; i < UD86_num_operands(ud); i++) {
const auto *op0 = ud_insn_opr(ud, i);
Msg("op %d type %d size %d lval %llx base %d index %d access %d\n", i, op0->type, op0->size, op0->lval.uqword, op0->base, op0->index, op0->access);
}
}
writeOffset = ud_insn_len(ud) - 4 - immArgSize;

if (memArgs != 1 || writeOffset == -1 || dispValue == -1ULL || (dispValue & 0xFFFFFFFF) != dispValue) return false;


if (dest == nullptr || func == nullptr) return true;

int32_t diff = (intptr_t)(dest) - (intptr_t)func;

memcpy(dest, (uint8_t *)ud_insn_off(ud), ud_insn_len(ud));

*(int32_t *)(dest + writeOffset) -= diff;
Msg("Prev bytes: %d Post bytes: %d offset %d\n", *(int32_t *)(ud_insn_off(ud)+writeOffset) + ud_insn_off(ud), *(int32_t *)(dest+writeOffset) + dest, diff);

return true;
}

/* fix instruction: '(jmp|call) <rel_imm32>' */
static bool UD86_insn_fix_jmpcall_rel_imm32(struct ud *ud, size_t &newLength, const uint8_t *func = nullptr, uint8_t *dest = nullptr)
{
auto mnemonic = ud_insn_mnemonic(ud);
// if (mnemonic != UD_Ijmp && mnemonic != UD_Icall ) return false;

// if (ud_insn_len(ud) != 5) return false;
if (UD86_num_operands(ud) != 1) return false;

newLength = ud_insn_len(ud);

const auto *op0 = ud_insn_opr(ud, 0);
if (op0->type != UD_OP_JIMM) return false;

if (func == nullptr || dest == nullptr) return true;

memcpy(dest, (uint8_t *)ud_insn_off(ud), ud_insn_len(ud));

if (op0->size != 32) {
// Convert jumps from 8 bit to 32 bit address
if (mnemonic == UD_Ijmp) {
dest[0] = OPCODE_JMP_REL_IMM32;
dest[2] = 0;
dest[3] = 0;
dest[4] = 0;

newLength = 5;
}
else {
dest[5] = 0;
dest[4] = 0;
dest[3] = 0;
dest[2] = dest[1];
dest[1] = (dest[0] & 0x0F) | 0x80;
dest[0] = OPCODE_JCC_REL_IMM32;
newLength = 6;
}
}
int32_t writeOffset = newLength - 4;

int32_t diff = (intptr_t)(dest + (newLength - ud_insn_len(ud))) - (intptr_t)func;

*(int32_t *)(dest + writeOffset) -= diff;

return true;
}

/* detect instruction: 'call <rel_imm32>' */
static bool UD86_insn_is_call_rel_imm32(struct ud *ud, const uint8_t **call_target = nullptr)
{
auto mnemonic = ud_insn_mnemonic(ud);
if (mnemonic != UD_Icall) return false;

if (ud_insn_len(ud) != 5) return false;
if (UD86_num_operands(ud) != 1) return false;

const auto *op0 = ud_insn_opr(ud, 0);
if (op0->type != UD_OP_JIMM) return false;
if (op0->size != 32) return false;

/* optional parameter: write out the call destination address */
if (call_target != nullptr) {
*call_target = (const uint8_t *)(ud_insn_off(ud) + ud_insn_len(ud) + op0->lval.sdword);
}

return true;
}

/* detect instruction: 'mov e[acdb]x,[esp]' */
static bool UD86_insn_is_mov_r32_rtnval(struct ud *ud, Reg *dest_reg = nullptr)
{
auto mnemonic = ud_insn_mnemonic(ud);
if (mnemonic != UD_Imov) return false;

if (ud_insn_len(ud) != 3) return false;
if (UD86_num_operands(ud) != 2) return false;

const auto *op0 = ud_insn_opr(ud, 0);
if (op0->type != UD_OP_REG) return false;
if (op0->size != 32) return false;

Reg reg;
switch (op0->base) {
case UD_R_EAX: reg = REG_AX; break;
case UD_R_ECX: reg = REG_CX; break;
case UD_R_EDX: reg = REG_DX; break;
case UD_R_EBX: reg = REG_BX; break;
default: return false;
}

const auto *op1 = ud_insn_opr(ud, 1);
if (op1->type != UD_OP_MEM) return false;
if (op1->size != 32) return false;
if (op1->base != UD_R_ESP) return false;
if (op1->index != UD_NONE) return false;
if (op1->scale != UD_NONE) return false;
if (op1->offset != 0) return false;

/* optional parameter: write out the first operand base register */
if (dest_reg != nullptr) {
*dest_reg = reg;
}

return true;
}

/* detect instruction: 'ret' */
static bool UD86_insn_is_ret(struct ud *ud)
{
auto mnemonic = ud_insn_mnemonic(ud);
if (mnemonic != UD_Iret) return false;

if (ud_insn_len(ud) != 1) return false;
if (UD86_num_operands(ud) != 0) return false;

return true;
}


/* detect whether an instruction is a call to __i686.get_pc_thunk.(ax|cx|dx|bx) */
static bool UD86_insn_is_call_to_get_pc_thunk(struct ud *ud, Reg *dest_reg = nullptr)
{
const uint8_t *call_target;
if (!UD86_insn_is_call_rel_imm32(ud, &call_target)) return false;

ud_t ux;
ud_init(&ux);
#ifdef PLATFORM_64BITS
ud_set_mode(&ux, 64);
#else
ud_set_mode(&ux, 32);
#endif
ud_set_pc(&ux, (uint64_t)call_target);
ud_set_input_buffer(&ux, call_target, 0x100);

if (ud_decode(&ux) == 0) return false;
if (!UD86_insn_is_mov_r32_rtnval(&ux, dest_reg)) return false;

if (ud_decode(&ux) == 0) return false;
if (!UD86_insn_is_ret(&ux)) return false;

return true;
}


/* analogous to asm.c copy_bytes() when dest == nullptr */
static size_t Trampoline_CalcNumBytesToCopy(size_t len_min, const uint8_t *func)
{
Expand All @@ -291,52 +55,6 @@ static size_t Trampoline_CalcNumBytesToCopy(size_t len_min, const uint8_t *func)
return len_actual;
}

/* analogous to asm.c copy_bytes() when dest != nullptr */
static size_t Trampoline_CopyAndFixUpFuncBytes(size_t len_min, const uint8_t *func, uint8_t *trampoline)
{
uint8_t *dest = trampoline;

ud_t ud;
ud_init(&ud);
#ifdef PLATFORM_64BITS
ud_set_mode(&ud, 64);
#else
ud_set_mode(&ud, 32);
#endif
ud_set_pc(&ud, (uint64_t)func);
ud_set_input_buffer(&ud, func, 0x100);

size_t len_actual = 0;
while (len_actual < len_min) {
size_t len_decoded = ud_decode(&ud);
assert(len_decoded != 0);

// They typically determine end of function
if (ud_insn_mnemonic(&ud) == UD_Inop || ud_insn_mnemonic(&ud) == UD_Iint3) break;

/* detect calls to __i686.get_pc_thunk.(ax|cx|dx|bx);
* convert them into direct-register-load operations */
Reg reg;
if (UD86_insn_is_call_to_get_pc_thunk(&ud, &reg)) {
uint32_t pc_value = (ud_insn_off(&ud) + ud_insn_len(&ud) + (trampoline - func));
MovRegImm32(dest, reg, pc_value).Write();
} else {
/* fixup jmp and call relative offsets */
if (UD86_insn_fix_jmpcall_rel_imm32(&ud, len_decoded, func + len_actual, dest)) {
}
else if (UD86_insn_fix_disp32(&ud, func + len_actual, dest)) {

} else {
memcpy(dest, (uint8_t *)ud_insn_off(&ud), len_decoded);
}
}

len_actual += len_decoded;
dest += len_decoded;
}
return len_actual;
}

static bool Jump_ShouldUseRelativeJump(intptr_t from, intptr_t target)
{
#ifndef PLATFORM_64BITS
Expand All @@ -349,7 +67,6 @@ static bool Jump_ShouldUseRelativeJump(intptr_t from, intptr_t target)
static size_t Jump_CalculateSize(intptr_t from, intptr_t target)
{
auto size = Jump_ShouldUseRelativeJump(from, target) ? JmpRelImm32::Size() : JmpIndirectMem32::Size() + sizeof(intptr_t);
Msg("calculated size %d\n", size);
return size;
}

Expand All @@ -361,7 +78,6 @@ static void Jump_WriteJump(uint8_t *from, uintptr_t target, size_t padSize)
jmp.WritePadded(padSize);
else
jmp.Write();
Msg("WriteRelative\n");
}
else {
auto pointerAddress = (uintptr_t)from + JmpIndirectMem32::Size();
Expand All @@ -371,7 +87,6 @@ static void Jump_WriteJump(uint8_t *from, uintptr_t target, size_t padSize)
else
jmp.Write();

Msg("WriteAbsolute %x\n", (uint8_t) ModRM{ RM_DISP32, OP_FF_JMP_RM32, MOD_INDIRECT });
*(uintptr_t *)pointerAddress = target;
}
}
Expand Down Expand Up @@ -896,14 +611,13 @@ void CDetouredFunc::CreateTrampoline()
size_t len_trampoline;
{
MemProtModifier_RX_RWX(this->m_pTrampoline, len_trampoline_alloc);
len_trampoline = Trampoline_CopyAndFixUpFuncBytes(len_prologue, this->m_pFunc, this->m_pTrampoline);
Msg("len trampoline %zu alloc %zu jumps %zu prologue %zu\n", len_trampoline, len_trampoline_alloc, jumpInTrampolineSize, len_prologue);
len_trampoline = CopyAndFixUpFuncBytes(len_prologue, this->m_pFunc, this->m_pTrampoline);
Msg("trampoline addr %p len trampoline %zu alloc %zu jumps %zu prologue %zu\n", this->m_pTrampoline, len_trampoline, len_trampoline_alloc, jumpInTrampolineSize, len_prologue);
TRACE_MSG("len_trampoline = %zu\n", len_trampoline);

assert(len_trampoline >= len_prologue && len_trampoline + jumpInTrampolineSize <= len_trampoline_alloc);
Jump_WriteJump(this->m_pTrampoline + len_trampoline, (uintptr_t)this->m_pFunc + len_prologue, 0);
}

assert(this->m_TrampolineCheck.empty());
this->m_TrampolineCheck.resize(len_trampoline + jumpInTrampolineSize);
memcpy(this->m_TrampolineCheck.data(), this->m_pTrampoline, len_trampoline + jumpInTrampolineSize);
Expand Down
Loading

0 comments on commit 84067b7

Please sign in to comment.