102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
hook_registry.c File Reference
#include "core_internal.h"
#include <dttr_log.h>
#include <dttr_sigscan.h>
#include <Zydis/Zydis.h>
#include <khash.h>
#include <kvec.h>
#include <psapi.h>
#include <xxhash.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Go to the source code of this file.

Data Structures

struct  DTTR_Core_Hook
struct  hook_chain
struct  trampoline_insn

Macros

#define DTTR_HOOK_PATCH_SIZE   5u
#define DTTR_HOOK_MIN_PROLOGUE   5u
#define DTTR_HOOK_MAX_PROLOGUE   64u
#define DTTR_HOOK_MAX_INSN   32u

Typedefs

typedef struct hook_chain hook_chain

Enumerations

enum  hook_record_kind { DTTR_HOOK_RECORD_PATCH = 0 , DTTR_HOOK_RECORD_FUNCTION = 1 }

Functions

uintptr_t DTTR_Core_HookSigscan (HMODULE mod, const char *sig, const char *mask)
DTTR_Result dttr_core_hook_last_error ()
void dttr_core_hook_set_last_error (DTTR_Status status, const char *message)
static void hook_error_clear ()
static bool decoder_init ()
static bool is_readable_page_protect (DWORD protect)
static bool copy_memory_checked (uintptr_t addr, uint8_t *out, size_t size)
static void log_prologue_bytes (uintptr_t site, const uint8_t *bytes, size_t size)
static bool decode_prologue (uintptr_t addr, int requested_size, trampoline_insn *insns, size_t *out_insn_count, size_t *out_prologue_size, uint8_t *out_prologue_bytes, size_t out_prologue_bytes_cap)
static bool trampoline_relocate (uint8_t *trampoline, uintptr_t site, const trampoline_insn *insns, size_t insn_count)
static khash_t (sigscan_cache)
uintptr_t DTTR_Core_HookCachedSigscan (HMODULE mod, const char *sig, const char *mask)
typedef kvec_t (DTTR_Core_Hook *)
static void hook_chain_destroy (hook_chain *chain)
static bool check_overlap (uintptr_t addr, size_t size)
static DTTR_Core_Hookhook_create (const char *op, uintptr_t addr, size_t size)
static size_t hook_find_index (DTTR_Core_Hook *hook)
static hook_chainhook_find_function_chain (uintptr_t addr)
static void flush_patched_range (const char *op, uintptr_t addr, size_t size)
static bool write_bytes (const char *op, uintptr_t addr, const uint8_t *bytes, size_t size)
static bool build_rel32_jump (uintptr_t site, void *target, uint8_t out[DTTR_HOOK_PATCH_SIZE])
static bool write_function_jump (uintptr_t site, void *target)
static bool hook_thunk_set_target (uint8_t *thunk, void *target)
static voidfunction_link_next_target (const DTTR_Core_Hook *hook)
static DTTR_Core_Hookfunction_link_create (hook_chain *chain, void *detour, void *next_target)
static bool function_chain_push_head (hook_chain *chain, DTTR_Core_Hook *hook, void **out_original)
static DTTR_Core_Hookfunction_chain_append (hook_chain *chain, void *handler, void **out_original)
static bool function_link_detach (DTTR_Core_Hook *hook)
static bool hook_detach_index (size_t index)
DTTR_Core_HookDTTR_Core_HookAttachFunction (uintptr_t addr, int prologue_size, void *handler, void **out_original)
DTTR_Core_HookDTTR_Core_HookAttachPointer (uintptr_t addr, void *new_value, void **out_original)
DTTR_Core_HookDTTR_Core_HookPatchBytes (uintptr_t addr, const uint8_t *bytes, size_t size)
bool DTTR_Core_HookDetachChecked (DTTR_Core_Hook *hook)
void DTTR_Core_HookDetach (DTTR_Core_Hook *hook)
bool DTTR_Core_HookIsActive (DTTR_Core_Hook *hook)
bool DTTR_Core_HookDetachOwnerChecked (void *owner)
voidDTTR_Core_HookSetOwner (void *owner)
void DTTR_Core_HookDetachOwner (void *owner)
static void cleanup_sigscan_cache ()
bool DTTR_Core_HookCleanupAllChecked ()
void DTTR_Core_HookCleanupAll ()
 Detach all remaining hooks and free the sigscan cache.

Variables

static ZydisDecoder decoder
static bool decoder_initialized = false
static DTTR_Result hook_last_error = {DTTR_OK, "ok"}

Macro Definition Documentation

◆ DTTR_HOOK_MAX_INSN

#define DTTR_HOOK_MAX_INSN   32u

Definition at line 72 of file hook_registry.c.

Referenced by decode_prologue(), and DTTR_Core_HookAttachFunction().

◆ DTTR_HOOK_MAX_PROLOGUE

#define DTTR_HOOK_MAX_PROLOGUE   64u

◆ DTTR_HOOK_MIN_PROLOGUE

#define DTTR_HOOK_MIN_PROLOGUE   5u

Definition at line 70 of file hook_registry.c.

Referenced by decode_prologue().

◆ DTTR_HOOK_PATCH_SIZE

#define DTTR_HOOK_PATCH_SIZE   5u

Typedef Documentation

◆ hook_chain

typedef struct hook_chain hook_chain

Definition at line 22 of file hook_registry.c.

Enumeration Type Documentation

◆ hook_record_kind

Enumerator
DTTR_HOOK_RECORD_PATCH 
DTTR_HOOK_RECORD_FUNCTION 

Definition at line 17 of file hook_registry.c.

Function Documentation

◆ build_rel32_jump()

bool build_rel32_jump ( uintptr_t site,
void * target,
uint8_t out[DTTR_HOOK_PATCH_SIZE] )
static

Definition at line 626 of file hook_registry.c.

References DTTR_HOOK_PATCH_SIZE, and target.

Referenced by write_function_jump().

◆ check_overlap()

bool check_overlap ( uintptr_t addr,
size_t size )
static

Definition at line 540 of file hook_registry.c.

References DTTR_LOG_WARN, h, and size.

Referenced by DTTR_Core_HookAttachFunction(), and DTTR_Core_HookPatchBytes().

◆ cleanup_sigscan_cache()

void cleanup_sigscan_cache ( )
static

Definition at line 1115 of file hook_registry.c.

References NULL.

Referenced by DTTR_Core_HookCleanupAllChecked().

◆ copy_memory_checked()

bool copy_memory_checked ( uintptr_t addr,
uint8_t * out,
size_t size )
static

Definition at line 136 of file hook_registry.c.

References DTTR_LOG_ERROR, is_readable_page_protect(), and size.

Referenced by decode_prologue().

◆ decode_prologue()

bool decode_prologue ( uintptr_t addr,
int requested_size,
trampoline_insn * insns,
size_t * out_insn_count,
size_t * out_prologue_size,
uint8_t * out_prologue_bytes,
size_t out_prologue_bytes_cap )
static

◆ decoder_init()

bool decoder_init ( )
static

Definition at line 97 of file hook_registry.c.

References decoder, decoder_initialized, and DTTR_LOG_ERROR.

Referenced by decode_prologue().

◆ dttr_core_hook_last_error()

DTTR_Result dttr_core_hook_last_error ( )

Definition at line 85 of file hook_registry.c.

References hook_last_error.

Referenced by DTTR_Core_HookFunction().

◆ dttr_core_hook_set_last_error()

void dttr_core_hook_set_last_error ( DTTR_Status status,
const char * message )

Definition at line 89 of file hook_registry.c.

References dttr_core_result(), and hook_last_error.

Referenced by DTTR_Core_HookAttachFunction(), and hook_error_clear().

◆ DTTR_Core_HookAttachFunction()

DTTR_Core_Hook * DTTR_Core_HookAttachFunction ( uintptr_t addr,
int prologue_size,
void * handler,
void ** out_original )

Install a JMP hook and optionally return the trampoline.

Parameters
addrFunction entry or instruction site to hook.
prologue_sizeMinimum prologue bytes for the hook injection site, or 0 for automatic sizing.
handlerReplacement function to call.
out_originalOptional output receiving the original trampoline.
Returns
Hook handle on success, or NULL on failure.

Definition at line 861 of file hook_registry.c.

References hook_chain::addr, check_overlap(), decode_prologue(), dttr_core_hook_set_last_error(), DTTR_ERR_HOOK_CHAIN_UNSUPPORTED, DTTR_HOOK_MAX_INSN, DTTR_HOOK_MAX_PROLOGUE, DTTR_HOOK_PATCH_SIZE, DTTR_LOG_DEBUG, DTTR_LOG_ERROR, DTTR_LOG_WARN, free, function_chain_append(), function_chain_push_head(), function_link_create(), hook_chain_destroy(), hook_error_clear(), hook_find_function_chain(), NULL, hook_chain::original, hook_chain::patch_size, hook_chain::prologue_size, hook_chain::trampoline, and trampoline_relocate().

Referenced by test_function_hooks_chain_and_detach().

◆ DTTR_Core_HookAttachPointer()

DTTR_Core_Hook * DTTR_Core_HookAttachPointer ( uintptr_t addr,
void * new_value,
void ** out_original )

Patch a pointer or IAT slot and optionally return the previous value.

Parameters
addrAddress of the pointer slot to replace.
new_valueReplacement pointer value.
out_originalOptional output receiving the previous pointer value.
Returns
Hook handle on success, or NULL on failure.

Definition at line 1010 of file hook_registry.c.

References DTTR_Core_HookPatchBytes(), DTTR_LOG_ERROR, and NULL.

Referenced by test_pointer_hook_detach_restores_original().

◆ DTTR_Core_HookCachedSigscan()

uintptr_t DTTR_Core_HookCachedSigscan ( HMODULE mod,
const char * sig,
const char * mask )

Scan a module with the runtime signature cache.

Parameters
modModule to scan.
sigRaw byte signature buffer.
maskMask string where implementation-defined wildcard characters skip bytes.
Returns
Matching address, or 0 when not found.

Definition at line 467 of file hook_registry.c.

References DTTR_Core_HookSigscan().

◆ DTTR_Core_HookCleanupAll()

◆ DTTR_Core_HookCleanupAllChecked()

bool DTTR_Core_HookCleanupAllChecked ( )

Detach all remaining hooks, free the sigscan cache, and report restore failures.

Returns
true when every active hook was detached.

Definition at line 1122 of file hook_registry.c.

References cleanup_sigscan_cache(), hook_detach_index(), and NULL.

Referenced by DTTR_Core_HookCleanupAll().

◆ DTTR_Core_HookDetach()

void DTTR_Core_HookDetach ( DTTR_Core_Hook * hook)

Detach one hook or patch.

Parameters
hookHook or patch handle returned by this runtime, or NULL.

Definition at line 1078 of file hook_registry.c.

References DTTR_Core_HookDetachChecked().

Referenced by test_function_hooks_chain_and_detach(), test_overlapping_byte_patches_are_rejected(), test_patch_bytes_detach_restores_original(), and test_pointer_hook_detach_restores_original().

◆ DTTR_Core_HookDetachChecked()

bool DTTR_Core_HookDetachChecked ( DTTR_Core_Hook * hook)

Detach one hook or patch and report restore failure.

Parameters
hookHook or patch handle returned by this runtime, or NULL.
Returns
true when the hook is detached, already stale, or NULL.

Definition at line 1063 of file hook_registry.c.

References DTTR_LOG_DEBUG, hook_detach_index(), and hook_find_index().

Referenced by DTTR_Core_HookDetach(), DTTR_Core_Unhook(), DTTR_Core_Unpatch(), and patch_group_detach_checked().

◆ DTTR_Core_HookDetachOwner()

void DTTR_Core_HookDetachOwner ( void * owner)

Detach hooks tagged with the given owner.

Parameters
ownerOwner pointer previously active when hooks or byte patches were created.

Definition at line 1111 of file hook_registry.c.

References DTTR_Core_HookDetachOwnerChecked().

Referenced by test_owner_detach_only_detaches_matching_owner().

◆ DTTR_Core_HookDetachOwnerChecked()

bool DTTR_Core_HookDetachOwnerChecked ( void * owner)

Detach hooks tagged with the given owner and report restore failures.

Parameters
ownerOwner pointer previously active when hooks or byte patches were created.
Returns
true when every owned hook was detached or no owner was supplied.

Definition at line 1086 of file hook_registry.c.

References hook_detach_index().

Referenced by DTTR_Core_HookDetachOwner(), and unload_mod().

◆ DTTR_Core_HookIsActive()

bool DTTR_Core_HookIsActive ( DTTR_Core_Hook * hook)

Report whether a hook handle is still registered with the runtime.

Parameters
hookHook or patch handle returned by this runtime, or NULL.
Returns
true while the hook is still active.

Definition at line 1082 of file hook_registry.c.

References hook_find_index().

Referenced by dttr_pcdogs_hook_is_active().

◆ DTTR_Core_HookPatchBytes()

DTTR_Core_Hook * DTTR_Core_HookPatchBytes ( uintptr_t addr,
const uint8_t * bytes,
size_t size )

Patch bytes and keep the originals for detach.

Parameters
addrAddress to patch.
bytesReplacement bytes to write.
sizeNumber of replacement bytes.
Returns
Patch handle on success, or NULL on failure.

Definition at line 1029 of file hook_registry.c.

References check_overlap(), DTTR_LOG_ERROR, hook_create(), NULL, DTTR_Core_Hook::original, size, and write_bytes().

Referenced by DTTR_Core_HookAttachPointer(), install_win_main_hook(), test_cleanup_all_restores_hooks_and_allows_reuse(), test_overlapping_byte_patches_are_rejected(), test_owner_detach_only_detaches_matching_owner(), and test_patch_bytes_detach_restores_original().

◆ DTTR_Core_HookSetOwner()

void * DTTR_Core_HookSetOwner ( void * owner)

Set the owner tag for subsequent hooks and return the previous owner.

Parameters
ownerOpaque owner pointer assigned to hooks or byte patches created after this call.

The owner tag is process-global runtime state. Save the returned owner and restore it before returning from a callback if you set it manually.

Returns
Previous owner pointer.

Definition at line 1104 of file hook_registry.c.

Referenced by init_mod(), and test_owner_detach_only_detaches_matching_owner().

◆ DTTR_Core_HookSigscan()

uintptr_t DTTR_Core_HookSigscan ( HMODULE mod,
const char * sig,
const char * mask )

Scan a module for the first matching signature.

Parameters
modModule to scan.
sigRaw byte signature buffer.
maskMask string where implementation-defined wildcard characters skip bytes.
Returns
Matching address, or 0 when not found.

Definition at line 48 of file hook_registry.c.

References DTTR_Sigscan_Bytes(), and size.

Referenced by DTTR_Core_AOBFindInModule(), DTTR_Core_HookCachedSigscan(), and install_win_main_hook().

◆ flush_patched_range()

void flush_patched_range ( const char * op,
uintptr_t addr,
size_t size )
static

Definition at line 607 of file hook_registry.c.

References DTTR_LOG_WARN, and size.

Referenced by hook_thunk_set_target(), and write_bytes().

◆ function_chain_append()

DTTR_Core_Hook * function_chain_append ( hook_chain * chain,
void * handler,
void ** out_original )
static

◆ function_chain_push_head()

◆ function_link_create()

◆ function_link_detach()

◆ function_link_next_target()

void * function_link_next_target ( const DTTR_Core_Hook * hook)
static

◆ hook_chain_destroy()

void hook_chain_destroy ( hook_chain * chain)
static

◆ hook_create()

DTTR_Core_Hook * hook_create ( const char * op,
uintptr_t addr,
size_t size )
static

◆ hook_detach_index()

◆ hook_error_clear()

void hook_error_clear ( )
static

Definition at line 93 of file hook_registry.c.

References dttr_core_hook_set_last_error(), and DTTR_OK.

Referenced by DTTR_Core_HookAttachFunction().

◆ hook_find_function_chain()

hook_chain * hook_find_function_chain ( uintptr_t addr)
static

◆ hook_find_index()

size_t hook_find_index ( DTTR_Core_Hook * hook)
static

Definition at line 584 of file hook_registry.c.

Referenced by DTTR_Core_HookDetachChecked(), and DTTR_Core_HookIsActive().

◆ hook_thunk_set_target()

bool hook_thunk_set_target ( uint8_t * thunk,
void * target )
static

Definition at line 657 of file hook_registry.c.

References flush_patched_range(), and target.

Referenced by function_link_create(), and function_link_detach().

◆ is_readable_page_protect()

bool is_readable_page_protect ( DWORD protect)
static

Definition at line 120 of file hook_registry.c.

References DWORD.

Referenced by copy_memory_checked().

◆ khash_t()

khash_t ( sigscan_cache )
static

Definition at line 453 of file hook_registry.c.

References khash_t(), NULL, and state.

◆ kvec_t()

◆ log_prologue_bytes()

void log_prologue_bytes ( uintptr_t site,
const uint8_t * bytes,
size_t size )
static

Definition at line 184 of file hook_registry.c.

References DTTR_HOOK_MAX_PROLOGUE, DTTR_LOG_DEBUG, size, and w.

Referenced by decode_prologue().

◆ trampoline_relocate()

bool trampoline_relocate ( uint8_t * trampoline,
uintptr_t site,
const trampoline_insn * insns,
size_t insn_count )
static

◆ write_bytes()

bool write_bytes ( const char * op,
uintptr_t addr,
const uint8_t * bytes,
size_t size )
static

◆ write_function_jump()

bool write_function_jump ( uintptr_t site,
void * target )
static

Variable Documentation

◆ decoder

◆ decoder_initialized

bool decoder_initialized = false
static

Definition at line 82 of file hook_registry.c.

Referenced by decoder_init().

◆ hook_last_error

DTTR_Result hook_last_error = {DTTR_OK, "ok"}
static

Definition at line 83 of file hook_registry.c.

Referenced by dttr_core_hook_last_error(), and dttr_core_hook_set_last_error().