From 92ed36f499253570fa26b339ae198e376cae6585 Mon Sep 17 00:00:00 2001 From: Nemirtingas Date: Sat, 31 Aug 2019 20:47:38 +0200 Subject: [PATCH] Linux version of detour --- overlay_experimental/Linux_Detour.cpp | 637 ++++++++++++++++++++++++++ overlay_experimental/Linux_Detour.h | 32 ++ 2 files changed, 669 insertions(+) create mode 100644 overlay_experimental/Linux_Detour.cpp create mode 100644 overlay_experimental/Linux_Detour.h diff --git a/overlay_experimental/Linux_Detour.cpp b/overlay_experimental/Linux_Detour.cpp new file mode 100644 index 0000000..984e815 --- /dev/null +++ b/overlay_experimental/Linux_Detour.cpp @@ -0,0 +1,637 @@ +#include "Linux_Detour.h" + +#include +#include + +#include + +#include +#include +#include +#include + +//------------------------------------------------------------------------------// +// Helper funcs +//------------------------------------------------------------------------------// +constexpr static auto relative_jump_size = 5; +constexpr static auto relative_addr_jump_size = sizeof(int32_t); +constexpr static auto absolute_jump_size = 6; + +struct +{ + bool has_r_m; + uint8_t base_size; +} s_opcodes[256] = +{ + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 0 - 7 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 8 - 15 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 16 - 23 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 24 - 31 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 32 - 39 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 40 - 47 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 48 - 55 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 56 - 63 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 64 - 71 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 72 - 79 + // PUSH ... + {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, // 80 - 87 + // POP ... + {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, // 88 - 95 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 96 - 103 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 104 - 111 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 112 - 129 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 120 - 127 + // MOV, ADD, ... R8 <- IMM8 TEST_8 TEST XCHG_8 XCHG + {true , 3}, {true , 6}, {true , 3}, {true , 3}, {true , 2}, {true , 2}, {true , 2}, {true , 2}, // 128 - 135 + // MOV_8 MOV MOV_R8_B MOV_R32_D MOV_32_ES LEA MOV_ES_32 POP + {true , 2}, {true , 2}, {true , 2}, {true , 2}, {true , 2}, {true , 2}, {true , 2}, {false, 2}, // 136 - 143 + // NOP + {false, 1}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 144 - 151 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 152 - 159 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 160 - 167 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 168 - 175 + // MOV_AL MOV_CL MOV_DL MOV_BL MOV_AH MOV_CH MOV_DH MOV_BH + {false, 2}, {false, 2}, {false, 2}, {false, 2}, {false, 2}, {false, 2}, {false, 2}, {false, 2}, // 176 - 183 + // MOV_EAX MOV_ECX MOV_EDX MOV_EBX MOV_ESP MOV_EBP MOV_ESI MOV_EDI, + {false, 5}, {false, 5}, {false, 5}, {false, 5}, {false, 5}, {false, 5}, {false, 5}, {false, 5}, // 184 - 191 + // RETN_IMM16 RETN + {false, 0}, {false, 0}, {false, 3}, {false, 1}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 192 - 199 + // LEAVE RETF_IMM16 RETF INT INT_IMM8 INTO + {false, 0}, {false, 1}, {false, 3}, {false, 1}, {false, 1}, {false, 1}, {false, 1}, {false, 0}, // 200 - 207 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 208 - 215 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 216 - 223 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 224 - 231 + // CALL JMP LJMP SHORT_JMP + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 232 - 239 + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, // 240 - 247 + // EXTENDED + {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 0}, {false, 6}, // 248 - 255 +}; + +static constexpr auto mod_mask = 0xC0; +static constexpr auto register_addressing_mode = 0xC0; +static constexpr auto four_bytes_signed_displacement = 0x80; +static constexpr auto one_byte_signed_displacement = 0x40; + +static constexpr auto rm_mask = 0x05; +static constexpr auto displacement_only_addressing = 0x05; +static constexpr auto sib_with_no_displacement = 0x04; +static constexpr auto register_indirect_addressing_mode = 0x00; + +enum opcodes_e // Commonly used opcode in the beginning of functions +{ + PUSH_EAX = 0x50, PUSH_ECX, PUSH_EDX, PUSH_EBX, PUSH_ESP, PUSH_EBP, PUSH_ESI, PUSH_EDI, + POP_EAX , POP_ECX , POP_EDX , POP_EBX , POP_ESP , POP_EBP , POP_ESI , POP_EDI , + + R8_IMM8 = 0x80, R32_IMM32, R8_IMM8_2, R32_IMM8, + TEST_8, TEST, + XCHG_8, XCHG, + MOV_8 , MOV, + MOV_8_B, MOV_32_D, MOV_32_ES, + LEA, + MOV_ES_32, POP, + NOP, + MOV_AL = 0xB0, MOV_CL, MOV_DL, MOV_BL, MOV_AH, MOV_CH, MOV_DH, MOV_BH, + MOV_EAX, MOV_ECX, MOV_EDX, MOV_EBX, MOV_ESP, MOV_EBP, MOV_ESI, MOV_EDI, + RETN_IMM16 = 0xC2, RETN, + LEAVE = 0xC9, RETF_IMM16, RETF, INT, INT_IMM8, INTO, + CALL = 0xE8, // 5 bytes don't process + JMP, // 5 bytes don't process + LJMP, // 7 bytes, don't process + SHORT_JMP, // 2 bytes don't process + EXTENDED = 0xFF, +}; + +const char* opcode_name(uint8_t opcode) +{ +#define NAME(opcode_name) case opcode_name: return #opcode_name + switch( opcode ) + { + NAME(PUSH_EAX); NAME(PUSH_ECX); NAME(PUSH_EDX); NAME(PUSH_EBX); + NAME(PUSH_ESP); NAME(PUSH_EBP); NAME(PUSH_ESI); NAME(PUSH_EDI); + NAME(POP_EAX); NAME(POP_ECX); NAME(POP_EDX); NAME(POP_EBX); + NAME(POP_ESP); NAME(POP_EBP); NAME(POP_ESI); NAME(POP_EDI); + NAME(R8_IMM8); NAME(R32_IMM32); NAME(R8_IMM8_2); NAME(R32_IMM8); + NAME(TEST_8); NAME(TEST); NAME(XCHG_8); NAME(XCHG); NAME(MOV_8); NAME(MOV); + NAME(MOV_8_B); NAME(MOV_32_D); NAME(MOV_32_ES); + NAME(LEA); + NAME(MOV_ES_32); NAME(POP); + NAME(NOP); + NAME(MOV_AL) ; NAME(MOV_CL) ; NAME(MOV_DL) ; NAME(MOV_BL) ; NAME(MOV_AH) ; NAME(MOV_CH) ; NAME(MOV_DH) ; NAME(MOV_BH) ; + NAME(MOV_EAX); NAME(MOV_ECX); NAME(MOV_EDX); NAME(MOV_EBX); NAME(MOV_ESP); NAME(MOV_EBP); NAME(MOV_ESI); NAME(MOV_EDI); + NAME(RETN_IMM16); NAME(RETN); NAME(LEAVE); NAME(RETF_IMM16); NAME(RETF); + NAME(INT); NAME(INT_IMM8); NAME(INTO); NAME(CALL); NAME(JMP); NAME(LJMP); NAME(SHORT_JMP); + NAME(EXTENDED); + } +#undef NAME + return "no registered"; +} + +#pragma pack(push, 1) + +struct trampoline_x86_t +{ + uint8_t trampolineBytes[16+relative_jump_size]; // trampoline + original function opcodes + uint8_t hookJump[relative_jump_size]; // jump to hook addr, needed because of relative jump overflow + uint8_t nOriginalBytes; // number of original function bytes bkp + uint8_t originalBytes[16]; // original function bytes +}; + +typedef trampoline_x86_t trampoline_t; + +struct trampoline_region_t +{ + uint32_t header; + uint8_t numTrampolines; // current trampolines allocated + trampoline_t *trampolines_start; // start pointer of current region trampolines + trampoline_t *next_free_trampoline; // next free trampoline in region +}; + +#pragma pack(pop) + +struct transaction_t +{ + bool detach; + void** ppOriginalFunc; + trampoline_t *trampoline; +}; + +static std::list trampoline_regions; + +static bool transaction_started = false; +static std::list cur_transaction; + +inline size_t page_size() +{ + static size_t _page_size = sysconf(_SC_PAGESIZE); + return _page_size; +} + +inline size_t region_size() +{ + return page_size(); +} + +static uint8_t max_trampolines_in_region = region_size() / sizeof(trampoline_t); + +inline void* library_address_by_handle(void *library) +{ + return (library == nullptr ? nullptr : *reinterpret_cast(library)); +} + +inline size_t page_align(size_t size, size_t page_size) +{ + return (size+(page_size-1)) & (((size_t)-1)^(page_size-1)); +} + +inline void* page_addr(void* addr, size_t page_size) +{ + return reinterpret_cast(reinterpret_cast(addr) & (((size_t)-1)^(page_size-1))); +} + +//////////////////////////////////////////////////// +/// Tiny disasm + +bool is_opcode_terminating_function(uint8_t* pCode) +{ + switch( *pCode ) + { + case LEAVE: + case RETN: case RETN_IMM16: + case RETF: case RETF_IMM16: + case INT: case INT_IMM8: case INTO: + return true; + } + return false; +} + +int find_space_for_trampoline(uint8_t* func, int bytes_needed) +{ + if( func == nullptr ) + return -1; + + int code_len = -1; + bool search = true; + uint8_t *startCode = reinterpret_cast(func); + uint8_t *pCode = startCode; + while( search ) // Find opcodes size and try to find at least 5 bytes for our JMP + { + if( is_opcode_terminating_function(pCode) ) + break; + + if( s_opcodes[*pCode].has_r_m ) + { // MOD-REG-R/M Byte + // 7 6 5 4 3 2 1 0 - bits + //[ MOD ][ REG ][ R/M ] + switch( pCode[1] & mod_mask ) // Check MOD to know how many bytes we have after this opcode + { + case register_addressing_mode : pCode += s_opcodes[*pCode].base_size ; break;// register addressing mode [opcode] [R/M] [XX] + case four_bytes_signed_displacement: pCode += s_opcodes[*pCode].base_size+5; break;// address mode byte + 4 bytes displacement + case one_byte_signed_displacement : pCode += s_opcodes[*pCode].base_size+2; break;// address mode byte + 1 byte displacement + case 0x00: + switch( pCode[1] & rm_mask ) + { + case sib_with_no_displacement : pCode += s_opcodes[*pCode].base_size+1; break;// SIB with no displacement + case displacement_only_addressing : pCode += s_opcodes[*pCode].base_size+4; break;// 4 bytes Displacement only addressing mode + case register_indirect_addressing_mode: pCode += s_opcodes[*pCode].base_size; // Register indirect addressing mode + } + } + } + else if( s_opcodes[*pCode].base_size ) + { + pCode += s_opcodes[*pCode].base_size; + } + else + { + switch( *pCode ) + { + case CALL: case JMP: case LJMP: case SHORT_JMP: + //std::cerr << "CALL and JMP are not supported for trampolines." << std::endl; + search = false; + break; + + case EXTENDED: + //std::cerr << "IMPORT_JUMP is not handled" << std::endl; + if (pCode[1] == 0x25) // This is an imported function + { // Get the true function call + //pCode = (uint8_t*)*(pCode+2); + //startCode = pCode; + // For now disable this case + search = false; + } + else + { + search = false; + } + break; + + default: + //std::cerr << "opcode " << std::hex << (uint32_t)*pCode << " no registered" << std::endl; + search = false; + } + } + + if( (pCode - startCode) >= bytes_needed && search ) + { + search = false; + code_len = pCode-startCode; + } + } + return code_len; +} + +/////////////////////////////////////////// +// Tiny asm + +inline uint8_t* gen_immediate_addr(uint8_t* opcode_addr, uint8_t* dest) +{ + *reinterpret_cast(opcode_addr) = (dest - (opcode_addr + relative_addr_jump_size)); + return opcode_addr + relative_addr_jump_size; +} + +inline uint8_t* gen_immediate_jump(uint8_t* opcode_addr, uint8_t* dest) +{ + *opcode_addr++ = JMP; + return gen_immediate_addr(opcode_addr, dest); +} + +inline uint8_t* gen_immediate_call(uint8_t* opcode_addr, uint8_t* dest) +{ + *opcode_addr++ = CALL; + return gen_immediate_addr(opcode_addr, dest); +} + +uint8_t* relative_addr_to_absolute(int32_t rel_addr, uint8_t *code_addr) +{ + return code_addr + rel_addr + relative_jump_size; +} + +void alloc_new_trampoline_region() +{ + trampoline_region_t region; + + region.numTrampolines = 0; + // allocate new trampoline right in the middle of memory so relative jump can access any function + region.trampolines_start = reinterpret_cast(mmap((void*)std::numeric_limits::max(), // allocate the page near the half of memory addressing + region_size(), // size + PROT_EXEC|PROT_WRITE|PROT_READ, // protection + MAP_PRIVATE|MAP_32BIT|MAP_ANONYMOUS, // don't map a file but memory + -1, // fd = -1 + 0) // offset + ); + // Fill the region with 0 + std::fill(reinterpret_cast(region.trampolines_start), reinterpret_cast(region.trampolines_start)+region_size(), 0); + region.next_free_trampoline = region.trampolines_start; + // Protect trampoline region memory + mprotect((void*)region.trampolines_start, region_size(), PROT_READ|PROT_EXEC); + + trampoline_regions.push_back(region); +} + +trampoline_t* get_free_trampoline() +{ + if (!transaction_started) + return nullptr; + + trampoline_t *res = nullptr; + auto it = std::find_if(trampoline_regions.begin(), trampoline_regions.end(), [&res](trampoline_region_t ®ion){ + if( region.numTrampolines == max_trampolines_in_region ) + return false; + return true; + }); + + if( it == trampoline_regions.end() ) + { + alloc_new_trampoline_region(); + it = --trampoline_regions.end(); + } + res = it->next_free_trampoline; + + trampoline_t *next_new_trampoline = res+1; + if( it->numTrampolines != max_trampolines_in_region ) + { + while( next_new_trampoline->nOriginalBytes != 0 ) + { + ++next_new_trampoline; + } + } + else + { + next_new_trampoline = nullptr; + } + it->next_free_trampoline = next_new_trampoline; + + ++it->numTrampolines; + + return res; +} + +void clear_trampoline(trampoline_region_t& region, trampoline_t *trampoline) +{ + --region.numTrampolines; + + std::fill(reinterpret_cast(trampoline), reinterpret_cast(trampoline+1), 0); + if( region.next_free_trampoline == nullptr || region.next_free_trampoline > trampoline ) + region.next_free_trampoline = trampoline; +} + +inline bool is_page_inside_region(void *page, trampoline_region_t& region) +{ + if( page >= region.trampolines_start && page <= (region.trampolines_start+region_size()) ) + return true; + return false; +} + +//------------------------------------------------------------------------------// + +/* +#include +#include + +static pid_t tid = 0; + +struct dism_pthread_t +{ + uint32_t d0; + uint32_t d4; + uint32_t d8; + uint32_t dc; + uint32_t d10; + uint32_t d14; + uint32_t d18; + uint32_t d1c; + uint32_t d20; + uint32_t d24; + uint32_t d28; + uint32_t d2c; + uint32_t d30; + uint32_t d34; + uint32_t d38; + uint32_t d3c; + uint32_t d40; + uint32_t d44; + uint32_t d48; + uint32_t d4c; + uint32_t d50; + uint32_t d54; + uint32_t d58; + uint32_t d5c; + uint32_t d60; + uint32_t d64; + pid_t task_id; +}; +*/ + +int Linux_Detour::update_thread(pthread_t thread_id) +{ + //dism_pthread_t *dt = (dism_pthread_t*)thread_id; + + // dt->task_id == syscall(SYS_gettid); + + + return 0; +} + +int Linux_Detour::transaction_begin() +{ + if( transaction_started ) + return -1; + + transaction_started = true; + + return 0; +} + +int Linux_Detour::transaction_abort() +{ + if(!transaction_started) + return -1; + + for( auto &i : cur_transaction ) + { + trampoline_t *trampoline = i.trampoline; + void *page_start = page_addr(reinterpret_cast(trampoline), page_size()); + auto it = std::find_if(trampoline_regions.begin(), trampoline_regions.end(), [page_start](trampoline_region_t ®ion){ + if( is_page_inside_region(page_start, region) ) + return true; + return false; + }); + if( it != trampoline_regions.end() ) + { + clear_trampoline(*it, trampoline); + } + } + cur_transaction.clear(); + transaction_started = false; + + return 0; +} + +int Linux_Detour::transaction_commit() +{ + if (!transaction_started ) + return -1; + + for( auto &i : cur_transaction) + { + trampoline_t *trampoline = i.trampoline; + void **ppOriginalFunc = i.ppOriginalFunc; + + int res; + + if( i.detach ) + { + void* trampoline_page = page_addr(reinterpret_cast(trampoline), page_size()); + + *ppOriginalFunc = (void*)(relative_addr_to_absolute(*reinterpret_cast(trampoline->trampolineBytes+trampoline->nOriginalBytes+1), + trampoline->trampolineBytes)); + + void* originalFunctionPage = page_addr(*ppOriginalFunc, page_size()); + + // Allow write on the original func + res = mprotect(originalFunctionPage, page_size()*2, PROT_READ|PROT_WRITE|PROT_EXEC); + + // Write the original opcodes + std::copy(trampoline->originalBytes, trampoline->originalBytes+trampoline->nOriginalBytes, + reinterpret_cast(*ppOriginalFunc)); + + // Remove write permission + res = mprotect(originalFunctionPage, page_size()*2, PROT_READ|PROT_EXEC); + + // Allow write on trampoline page + res = mprotect(trampoline_page, page_size()*2, PROT_READ|PROT_WRITE|PROT_EXEC); + + clear_trampoline(*reinterpret_cast(trampoline_page), trampoline); + + // Remove write permission + res = mprotect(trampoline_page, page_size()*2, PROT_READ|PROT_EXEC); + } + else + { + void* originalFunctionPage = page_addr(*ppOriginalFunc, page_size()); + + // Allow write on the original func + res = mprotect(originalFunctionPage, page_size()*2, PROT_READ|PROT_WRITE|PROT_EXEC); + + // Write the jump to trampoline + gen_immediate_jump(reinterpret_cast(*ppOriginalFunc), trampoline->hookJump); + + // Remove write permission + res = mprotect(originalFunctionPage, page_size()*2, PROT_READ|PROT_EXEC); + + *ppOriginalFunc = (void*)(trampoline->trampolineBytes); + } + } + cur_transaction.clear(); + transaction_started = false; + + return 0; +} + +int Linux_Detour::unhook_func(void** ppOriginalFunc, void* _hook) +{ + if( !transaction_started ) + return -EPERM; + + if( ppOriginalFunc == nullptr || _hook == nullptr || *ppOriginalFunc == nullptr ) + return -EINVAL; + + trampoline_t *trampoline = reinterpret_cast(*ppOriginalFunc); + void *page_start = page_addr(reinterpret_cast(trampoline), page_size()); + auto it = std::find_if(trampoline_regions.begin(), trampoline_regions.end(), [page_start](trampoline_region_t ®ion){ + if( is_page_inside_region(page_start, region) ) + return true; + return false; + }); + if( it != trampoline_regions.end() ) + { + cur_transaction.push_back({true, ppOriginalFunc, trampoline}); + } + + return -EINVAL; +} + +int Linux_Detour::hook_func(void** ppOriginalFunc, void* _hook) +{ + if( !transaction_started ) + return -EPERM; + + if( ppOriginalFunc == nullptr || _hook == nullptr || *ppOriginalFunc == nullptr ) + return -EINVAL; + + uint8_t* hook = reinterpret_cast(_hook); + uint8_t* pOriginalFunc = reinterpret_cast(*ppOriginalFunc); + int code_len = find_space_for_trampoline(pOriginalFunc, relative_jump_size); + + if( code_len < relative_jump_size ) + return -ENOSPC; + + // Allocate the trampoline, try to put it right in the middle of the mem, so a relative jump can access any function in the app (+/-2GB) + // + // Our hook is a 5 bytes JMP (1 bytes for opcode, 4 for RELATIVE jump) + // /!\ TODO: Add checks on JMP overflow + + trampoline_t *trampoline = get_free_trampoline(); + uint8_t *pTrampolineCode = trampoline->trampolineBytes; + + void* trampoline_page = page_addr(reinterpret_cast(trampoline), page_size()); + + // Enable write to the trampoline region + mprotect(trampoline_page, page_size()*2, PROT_READ|PROT_WRITE|PROT_EXEC); + // Create relative jmp to hook + gen_immediate_jump(trampoline->hookJump, hook); + // Copy original opcodes + trampoline->nOriginalBytes = code_len; + std::copy((uint8_t*)pOriginalFunc, ((uint8_t*)pOriginalFunc)+code_len, trampoline->originalBytes); + std::copy((uint8_t*)pOriginalFunc, ((uint8_t*)pOriginalFunc)+code_len, pTrampolineCode); + pTrampolineCode += code_len; + // Create the relative jmp to original (function + backed up opcodes) + pTrampolineCode = gen_immediate_jump(pTrampolineCode, pOriginalFunc+code_len); + pTrampolineCode += relative_addr_jump_size; + + // Disable trampoline region write + mprotect(trampoline_page, page_size()*2, PROT_READ|PROT_EXEC); + + cur_transaction.push_back({false, ppOriginalFunc, trampoline}); + + return 0; +} + +/* ------ DOCUMENTATION ------ +http://www.c-jump.com/CIS77/CPU/x86/lecture.html <- some help to understand [MOD][REG][R/M] (see paragraph #6) +http://shell-storm.org/online/Online-Assembler-and-Disassembler <- online assembler +http://ref.x86asm.net/coder32.html <- opcodes reference + +X86 + +push ebx : 0x53 +sub esp ?? : 0x83 0xEC 0x?? +call ????????: 0xE8 0x?? 0x?? 0x?? 0x?? + + +// relative jmp: ???????? = dst_addr - curr_addr - 5 +jmp ???????? : 0xe9 0x?? 0x?? 0x?? 0x?? +destination = 0x8dba8 +jmp location: 0x91995 - opcodes: e9 0e c2 ff ff +0e c2 ff ff = 0x8dba8 - 0x91995 - 5 + +// short jmp: ?? = dst_addr - curr_addr - 2 +jmp short ??: 0xeb 0x?? +destination = 0x91964 +jmp location: 0x9198f - opcodes: 0xeb 0xd3 +d3 = 0x91964 - 0x9198f - 2 + +X64 + +TODO: + Hint: make relative jump to near (+/-2Gb) code : 5 bytes + load absolute addr into R11 and call a jmp on register R11 : 13 bytes + Or + Reuse x86 relative jmp method + +Example: +mov r11, 0x0123456789abcdef -> 0x49 0xbb 0xef 0xcd 0xab 0x89 0x67 0x45 0x23 0x01 +jmp r11 -> 0x41 0xff 0xe3 + + +*/ diff --git a/overlay_experimental/Linux_Detour.h b/overlay_experimental/Linux_Detour.h new file mode 100644 index 0000000..731d426 --- /dev/null +++ b/overlay_experimental/Linux_Detour.h @@ -0,0 +1,32 @@ +#ifndef LINUX_DETOUR_H +#define LINUX_DETOUR_H + +#include +#include +#include +#include + +class Linux_Detour +{ + public: + static int update_thread(pthread_t thread_id); + static int transaction_begin(); + static int transaction_abort(); + static int transaction_commit(); + static int hook_func(void** ppOriginalFunc, void* _hook); + static int unhook_func(void** ppOriginalFunc, void* _hook); + + private: + static int hook_func_abs(void** ppOriginalFunc, void* _hook); + static int hook_func_rel(void** ppOriginalFunc, void* _hook); + + Linux_Detour() = delete; + Linux_Detour(Linux_Detour const&) = delete; + Linux_Detour(Linux_Detour &&) = delete; + Linux_Detour& operator=(Linux_Detour const&) = delete; + Linux_Detour& operator=(Linux_Detour &&) = delete; +}; + +extern "C" void *elf_hook(char const *library_filename, void const *library_address, char const *function_name, void const *substitution_address); + +#endif // LINUX_DETOUR_H