/* | |||||
\todo: | |||||
What if there's a loop in the function which loops back to an overwritten instruction? | |||||
start: | |||||
jmp hook_function | |||||
bla | |||||
jXX start+3 | |||||
*/ | |||||
// todo: on weird jump: stop the disassembling there or treat as normal instruction? | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <Windows.h> | |||||
#include <stddef.h> | |||||
#ifdef WINDOWS | |||||
#include <windows.h> | |||||
#else | |||||
#include <sys/mman.h> | |||||
#include <string.h> | |||||
#endif | |||||
#include "udis86.h" | #include "udis86.h" | ||||
#include "list.h" | #include "list.h" | ||||
#include "hook.h" | #include "hook.h" | ||||
#include "misc.h" | |||||
#define MINIMUM_REQUIRED_FUNCTION_LENGTH_SHORT_HOOK 5 | #define MINIMUM_REQUIRED_FUNCTION_LENGTH_SHORT_HOOK 5 | ||||
#define MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK 16 | #define MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK 16 | ||||
return (int)*trampoline; | return (int)*trampoline; | ||||
printf("Needed for trampoline %p: %d (Found %d)\n", *trampoline, trampolineSizeNeeded, functionLength); | printf("Needed for trampoline %p: %d (Found %d)\n", *trampoline, trampolineSizeNeeded, functionLength); | ||||
#ifdef WINDOWS | |||||
FlushInstructionCache(GetCurrentProcess(), function, needed); | |||||
#endif | |||||
return SUCCESS; | return SUCCESS; | ||||
} | } | ||||
*trampolineSize = 0; | *trampolineSize = 0; | ||||
if(!(trampoline = VirtualAlloc(NULL, PAGE_BOUNDARY, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))) | |||||
if(!(trampoline = alloc_rwx(PAGE_BOUNDARY))) | |||||
return (void*)CANT_ALLOC; | return (void*)CANT_ALLOC; | ||||
printf("trampoline @ %p\n", trampoline); | printf("trampoline @ %p\n", trampoline); | ||||
// Check for XXX [rip + ?] | // Check for XXX [rip + ?] | ||||
const struct ud_operand* op = NULL; | const struct ud_operand* op = NULL; | ||||
for(int i = 0; op = ud_insn_opr(&ud, i); i++) | |||||
for(int i = 0; ; i++, op = ud_insn_opr(&ud, i)) | |||||
{ | { | ||||
if((op->type & UD_OP_REG) == UD_OP_REG) | if((op->type & UD_OP_REG) == UD_OP_REG) | ||||
{ | { | ||||
const struct ud_operand* op = NULL; | const struct ud_operand* op = NULL; | ||||
int32_t ret = 0; | int32_t ret = 0; | ||||
for(int i = 0; op = ud_insn_opr(ud, i); i++) | |||||
for(int i = 0; ; i++, op = ud_insn_opr(ud, i)) | |||||
{ | { | ||||
// Only the RIP offset is interesting | // Only the RIP offset is interesting | ||||
if((op->type & UD_OP_REG) != UD_OP_REG || op->base != UD_R_RIP) | if((op->type & UD_OP_REG) != UD_OP_REG || op->base != UD_R_RIP) | ||||
static size_t get_jump_offset(ud_t* ud, size_t offsetOfInstr) | static size_t get_jump_offset(ud_t* ud, size_t offsetOfInstr) | ||||
{ | { | ||||
const struct ud_operand* op = NULL; | const struct ud_operand* op = NULL; | ||||
for(int i = 0; op = ud_insn_opr(ud, i); i++) | |||||
for(int i = 0; ; i++, op = ud_insn_opr(ud, i)) | |||||
{ | { | ||||
if(op->type == UD_OP_JIMM) | if(op->type == UD_OP_JIMM) | ||||
{ | { | ||||
return sizeof(jmpMemTemplate)+sizeof(toWhere); | return sizeof(jmpMemTemplate)+sizeof(toWhere); | ||||
} | } | ||||
//! fixme: | |||||
static bool loops_into_overwritten_code(void* function) | |||||
{ | |||||
return false; | |||||
} |
/** | /** | ||||
\brief | \brief | ||||
\param functionLength Length of the function you want to hook. If the length isn't known, pass 0 and the library will try to figure it out | |||||
\param functionLength Length of the function you want to hook. | |||||
If the length isn't known, pass 0 and the library will try to figure it out | |||||
*/ | */ | ||||
int hook(void* function, size_t functionLength, void* replacement, void* trampoline); | int hook(void* function, size_t functionLength, void* replacement, void* trampoline); | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <Windows.h> | |||||
#include "misc.h" | #include "misc.h" | ||||
#include "hook.h" | #include "hook.h" | ||||
original(3, 1, 1, 1, 1); | original(3, 1, 1, 1, 1); | ||||
//original(5, 1, 1, 1, 1); | //original(5, 1, 1, 1, 1); | ||||
#if WINDOWS | |||||
VirtualFree(original, 0, MEM_RELEASE); | VirtualFree(original, 0, MEM_RELEASE); | ||||
#endif | |||||
(void)getc(stdin); | (void)getc(stdin); | ||||
} | } | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdbool.h> | |||||
#include <string.h> | |||||
#ifdef WINDOWS | |||||
#include <windows.h> | |||||
#else | |||||
#include <sys/mman.h> | |||||
#endif | |||||
#include "udis86.h" | #include "udis86.h" | ||||
#include "misc.h" | #include "misc.h" | ||||
void* alloc_rwx(size_t size) { | |||||
#ifdef WINDOWS | |||||
return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWIRTE); | |||||
#else | |||||
return mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |||||
#endif | |||||
} | |||||
bool set_rwx(void* addr, size_t size) { | |||||
#ifdef WINDOWS | |||||
DWORD tmp; | |||||
return VirtualProtect(addr, size, PAGE_EXECUTE_READWIRTE, &tmp) == TRUE; | |||||
#else | |||||
return mprotect(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC) == 0; | |||||
#endif | |||||
} | |||||
void disassemble_func(void* function, size_t numberOfInstr) | void disassemble_func(void* function, size_t numberOfInstr) | ||||
{ | { | ||||
unsigned char* p = function; | unsigned char* p = function; | ||||
ud_t ud; | ud_t ud; | ||||
ud_init(&ud); | ud_init(&ud); | ||||
ud_set_input_buffer(&ud, function, (size_t)function | (0x1000 - 1)); // Can't read further than page boundary - there be dragons | |||||
ud_set_input_buffer(&ud, function, | |||||
(size_t)function | (0x1000 - 1) // Can't read further than page boundary - there be dragons | |||||
); | |||||
ud_set_pc(&ud, (uint64_t)function); | ud_set_pc(&ud, (uint64_t)function); | ||||
ud_set_mode(&ud, 64); | ud_set_mode(&ud, 64); | ||||
ud_set_syntax(&ud, UD_SYN_INTEL); | ud_set_syntax(&ud, UD_SYN_INTEL); | ||||
printf("%p %s\n", ud_insn_off(&ud), ud_insn_asm(&ud)); | printf("%p %s\n", ud_insn_off(&ud), ud_insn_asm(&ud)); | ||||
// ugly, ugly hack todo: make it work | |||||
/* jmp qword [rip] is used by the hooking engine | |||||
* x64 relative jumps don't exists so this way is used: | |||||
* jmp qword [rip] | |||||
* address where to tjump to | |||||
* other code | |||||
* Disassembling the address fucks up the dissassembling | |||||
* todo: Follow all jump targets? | |||||
*/ | |||||
if(strcmp(ud_insn_asm(&ud), "jmp qword [rip]") == 0) | if(strcmp(ud_insn_asm(&ud), "jmp qword [rip]") == 0) | ||||
p += 8; | p += 8; | ||||
} | } | ||||
} | |||||
} |
#define MISC_H | #define MISC_H | ||||
void disassemble_func(void* func, size_t numberOfInstr); | void disassemble_func(void* func, size_t numberOfInstr); | ||||
bool set_rwx(void* addr, size_t size); | |||||
#endif MISC_H | |||||
void* alloc_rwx(size_t size); | |||||
#endif MISC_H |