summaryrefslogtreecommitdiff
path: root/internal_rewrite/detours.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'internal_rewrite/detours.cpp')
-rw-r--r--internal_rewrite/detours.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/internal_rewrite/detours.cpp b/internal_rewrite/detours.cpp
new file mode 100644
index 0000000..a12520c
--- /dev/null
+++ b/internal_rewrite/detours.cpp
@@ -0,0 +1,292 @@
+#include "detours.h"
+#include "hde32.h"
+
+// very MUCH comment i APPRECIATE cool
+
+memory::c_detours memory::detours;
+
+__forceinline static bool is_code_padding( uintptr_t address, size_t size )
+{
+ uint8_t* code_ptr = (uint8_t*)address;
+
+ if ( code_ptr[0] != 0x00 && code_ptr[0] != 0x90 && code_ptr[0] != 0xCC )
+ return false;
+
+ for ( int i = 1; i < size; ++i )
+ {
+ if ( code_ptr[i] != code_ptr[0] )
+ return false;
+ }
+
+ return true;
+}
+
+bool memory::c_detours::create_trampoline( detour_hook_t* hook )
+{
+ if ( !hook->allocate_trampoline( ) )
+ return false;
+
+ instructions::long_jump_t call = {
+ 0xE8,
+ 0x00000000
+ };
+
+ instructions::long_jump_t jmp = {
+ 0xE9,
+ 0x00000000
+ };
+
+ instructions::JCC_REL jcc = {
+ 0x0F, 0x80,
+ 0x00000000
+ };
+
+ uint8_t old_position = 0;
+ uint8_t new_position = 0;
+ uintptr_t jmp_dest = 0;
+ bool finished = false;
+
+ hook->short_patch = false;
+
+ const int max_instruction_boundaries = 8;
+ int instruction_boundaries = 0;
+
+ do
+ {
+ hde32s hs;
+ uint8_t copy_size;
+ uintptr_t copy_src;
+ uintptr_t old_instruction = hook->m_original + old_position;
+ uintptr_t new_instruction = hook->m_trampoline + new_position;
+
+ copy_size = hde32_disasm( (void*)old_instruction, &hs );
+ if ( hs.flags & F_ERROR )
+ return false;
+
+ copy_src = old_instruction;
+ if ( old_position >= sizeof( instructions::long_jump_t ) )
+ {
+ // the trampoline function is long enough.
+ // complete the function with the jump to the target function.
+
+ jmp.opcode = 0xE9;
+ jmp.operand = (uint32_t)( old_instruction - ( new_instruction + sizeof( instructions::long_jump_t ) ) );
+ copy_src = uintptr_t( &jmp );
+ copy_size = sizeof( jmp );
+
+ finished = true;
+ }
+ else if ( hs.opcode == 0xE8 )
+ {
+ // direct relative CALL
+ uintptr_t dest = old_instruction + hs.len + (int32_t)hs.imm.imm32;
+
+ call.opcode = 0xE8;
+ call.operand = (uint32_t)( dest - ( new_instruction + sizeof( instructions::long_jump_t ) ) );
+
+ copy_src = uintptr_t( &call );
+ copy_size = sizeof( call );
+ }
+ else if ( ( hs.opcode & 0xFD ) == 0xE9 )
+ {
+ // direct relative JMP (EB or E9)
+ uintptr_t dest = old_instruction + hs.len;
+
+ if ( hs.opcode == 0xEB ) // short jmp
+ dest += (int8_t)hs.imm.imm8;
+ else
+ dest += (int32_t)hs.imm.imm32;
+
+ // simply copy an internal jump.
+ if ( hook->m_original <= dest && dest < hook->m_original + sizeof( instructions::long_jump_t ) )
+ {
+ if ( jmp_dest < dest )
+ jmp_dest = dest;
+ }
+ else
+ {
+ jmp.operand = (uint32_t)( dest - ( new_instruction + sizeof( instructions::long_jump_t ) ) );
+
+ copy_src = uintptr_t( &jmp );
+ copy_size = sizeof( instructions::long_jump_t );
+
+ // exit the function if it is not in the branch
+ finished = ( old_instruction >= jmp_dest );
+ }
+ }
+ else if ( ( hs.opcode & 0xF0 ) == 0x70 || ( hs.opcode & 0xFC ) == 0xE0 || ( hs.opcode2 & 0xF0 ) == 0x80 )
+ {
+ // direct relative JCC
+ uintptr_t dest = old_instruction + hs.len;
+
+ if ( ( hs.opcode & 0xF0 ) == 0x70 // JCC
+ || ( hs.opcode & 0xFC ) == 0xE0 ) // LOOPNZ/LOOPZ/LOOP/JECXZ
+ dest += (int8_t)hs.imm.imm8;
+ else
+ dest += (int32_t)hs.imm.imm32;
+
+ // simply copy an internal jump.
+ if ( (uintptr_t)hook->m_original <= dest && dest < ( (uintptr_t)hook->m_original + sizeof( instructions::long_jump_t ) ) )
+ {
+ if ( jmp_dest < dest )
+ jmp_dest = dest;
+ }
+ else if ( ( hs.opcode & 0xFC ) == 0xE0 )
+ {
+ // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported.
+ return false;
+ }
+ else
+ {
+ uint8_t cond = ( ( hs.opcode != 0x0F ? hs.opcode : hs.opcode2 ) & 0x0F );
+
+ jcc.opcode1 = 0x80 | cond;
+ jcc.operand = (uint32_t)( dest - ( new_instruction + sizeof( jcc ) ) );
+
+ copy_src = uintptr_t( &jcc );
+ copy_size = sizeof( jcc );
+ }
+ }
+ else if ( ( hs.opcode & 0xFE ) == 0xC2 )
+ {
+ // RET (C2 or C3)
+
+ // complete the function if not in a branch.
+ finished = ( old_instruction >= jmp_dest );
+ }
+
+ // can't alter the instruction length in a branch.
+ if ( old_instruction < jmp_dest && copy_size != hs.len )
+ return false;
+
+ // trampoline function is too large.
+ if ( new_position + copy_size > MEMORY_SLOT_SIZE )
+ return false;
+
+ // trampoline function has too many instructions.
+ if ( instruction_boundaries >= max_instruction_boundaries )
+ return false;
+
+ instruction_boundaries++;
+
+ memcpy( (void*)( hook->m_trampoline + new_position ), (void*)copy_src, copy_size );
+
+ new_position += copy_size;
+ old_position += hs.len;
+ }
+ while ( !finished );
+
+ // can we do a regular 5 byte jmp patch?
+ if ( old_position < sizeof( instructions::long_jump_t ) && !is_code_padding( (uintptr_t)hook->m_original + old_position, sizeof( instructions::long_jump_t ) - old_position ) )
+ {
+ // can we put 2 byte jmp patch?
+ if ( old_position < sizeof( instructions::short_jump_t ) && !is_code_padding( (uintptr_t)hook->m_original + old_position, sizeof( instructions::short_jump_t ) - old_position ) )
+ return false;
+
+ // can we put a 5 byte jmp patch above it?
+ if ( !is_code_padding( (uintptr_t)hook->m_original - sizeof( instructions::long_jump_t ), sizeof( instructions::long_jump_t ) ) )
+ return false;
+
+ hook->short_patch = true;
+ }
+
+ return true;
+}
+
+bool memory::c_detours::set_hook( detour_hook_t* hook, bool enable )
+{
+ // hook is already in place silly
+ if ( hook->enabled == enable )
+ return true;
+
+ if ( enable )
+ {
+ if ( hook->short_patch )
+ {
+ uint8_t* target = (uint8_t*)( hook->m_original - sizeof( instructions::long_jump_t ) );
+
+ // prepare patch
+ instructions::short_patch_t jmp_patch;
+ jmp_patch.jmp.opcode = 0xE9;
+ jmp_patch.jmp.operand = (uint32_t)( hook->m_custom - hook->m_original - sizeof( instructions::long_jump_t ) );
+
+ jmp_patch.jmp_short.opcode = 0xEB;
+ jmp_patch.jmp_short.operand = (uint8_t)( 0 - sizeof( instructions::short_patch_t ) );
+
+ // overwrite protection
+ DWORD original_protection;
+ VirtualProtect( target, sizeof( instructions::short_patch_t ), PAGE_EXECUTE_READWRITE, &original_protection );
+
+ // place hook
+ memcpy( target, &jmp_patch, sizeof( instructions::short_patch_t ) );
+
+ // restore protection
+ VirtualProtect( target, sizeof( instructions::short_patch_t ), original_protection, &original_protection );
+
+ // let cpu know we altered code
+ FlushInstructionCache( GetCurrentProcess( ), target, sizeof( instructions::short_patch_t ) );
+ }
+ else
+ {
+ // prepare patch
+ instructions::long_jump_t jmp_patch;
+ jmp_patch.opcode = 0xE9;
+ jmp_patch.operand = (uint32_t)( hook->m_custom - hook->m_original - sizeof( instructions::long_jump_t ) );
+
+ // overwrite protection
+ DWORD original_protection;
+ VirtualProtect( (void*)hook->m_original, sizeof( instructions::long_jump_t ), PAGE_EXECUTE_READWRITE, &original_protection );
+
+ // place hook
+ memcpy( (void*)hook->m_original, &jmp_patch, sizeof( instructions::long_jump_t ) );
+
+ // restore protection
+ VirtualProtect( (void*)hook->m_original, sizeof( instructions::long_jump_t ), original_protection, &original_protection );
+
+ // let cpu know we altered code
+ FlushInstructionCache( GetCurrentProcess( ), (void*)hook->m_original, sizeof( instructions::long_jump_t ) );
+ }
+
+
+ }
+ else
+ {
+ if ( hook->short_patch )
+ {
+ uint8_t* target = (uint8_t*)( hook->m_original - sizeof( instructions::long_jump_t ) );
+
+ // overwrite protection
+ DWORD original_protection;
+ VirtualProtect( target, sizeof( instructions::short_patch_t ), PAGE_EXECUTE_READWRITE, &original_protection );
+
+ // restore backup
+ memcpy( target, hook->m_original_bytes, sizeof( instructions::short_patch_t ) );
+
+ // restore protection
+ VirtualProtect( target, sizeof( instructions::short_patch_t ), original_protection, &original_protection );
+
+ // let cpu know we altered code
+ FlushInstructionCache( GetCurrentProcess( ), target, sizeof( instructions::short_patch_t ) );
+ }
+ else
+ {
+ uint8_t* target = (uint8_t*)hook->m_original;
+
+ // overwrite protection
+ DWORD original_protection;
+ VirtualProtect( target, sizeof( instructions::long_jump_t ), PAGE_EXECUTE_READWRITE, &original_protection );
+
+ // restore backup
+ memcpy( target, hook->m_original_bytes, sizeof( instructions::long_jump_t ) );
+
+ // restore protection
+ VirtualProtect( target, sizeof( instructions::long_jump_t ), original_protection, &original_protection );
+
+ // let cpu know we altered code
+ FlushInstructionCache( GetCurrentProcess( ), target, sizeof( instructions::long_jump_t ) );
+ }
+ }
+
+ hook->enabled = enable;
+ return true;
+} \ No newline at end of file