diff --git a/hook/meson.build b/hook/meson.build index f7d61d8..985599b 100644 --- a/hook/meson.build +++ b/hook/meson.build @@ -7,6 +7,8 @@ hook_lib = static_library( 'pe.h', 'peb.c', 'peb.h', + 'table.c', + 'table.h', ], ) diff --git a/hook/table.c b/hook/table.c new file mode 100644 index 0000000..aa561be --- /dev/null +++ b/hook/table.c @@ -0,0 +1,120 @@ +#include + +#include +#include +#include + +#include "hook/pe.h" +#include "hook/peb.h" +#include "hook/table.h" + +static void hook_table_apply_to_all( + const char *depname, + const struct hook_symbol *syms, + size_t nsyms); + +static void hook_table_apply_to_iid( + HMODULE target, + const pe_iid_t *iid, + const struct hook_symbol *syms, + size_t nsyms); + +static bool hook_table_match_proc( + const struct pe_iat_entry *iate, + const struct hook_symbol *sym); + +static void hook_table_apply_to_all( + const char *depname, + const struct hook_symbol *syms, + size_t nsyms) +{ + const peb_dll_t *dll; + HMODULE pe; + + for ( dll = peb_dll_get_first() ; + dll != NULL ; + dll = peb_dll_get_next(dll)) { + pe = peb_dll_get_base(dll); + + if (pe == NULL) { + continue; /* ?? Happens sometimes. */ + } + + hook_table_apply(pe, depname, syms, nsyms); + } +} + +void hook_table_apply( + HMODULE target, + const char *depname, + const struct hook_symbol *syms, + size_t nsyms) +{ + const pe_iid_t *iid; + const char *iid_name; + + assert(depname != NULL); + assert(syms != NULL || nsyms == 0); + + if (target == NULL) { + /* Call out, which will then call us back repeatedly. Awkward, but + viewed from the outside it's good for usability. */ + + hook_table_apply_to_all(depname, syms, nsyms); + } else { + for ( iid = pe_iid_get_first(target) ; + iid != NULL ; + iid = pe_iid_get_next(target, iid)) { + iid_name = pe_iid_get_name(target, iid); + + if (_stricmp(iid_name, depname) == 0) { + hook_table_apply_to_iid(target, iid, syms, nsyms); + } + } + } +} + +static void hook_table_apply_to_iid( + HMODULE target, + const pe_iid_t *iid, + const struct hook_symbol *syms, + size_t nsyms) +{ + struct pe_iat_entry iate; + size_t i; + size_t j; + const struct hook_symbol *sym; + + i = 0; + + while (pe_iid_get_iat_entry(target, iid, i++, &iate) == S_OK) { + for (j = 0 ; j < nsyms ; j++) { + sym = &syms[j]; + + if (hook_table_match_proc(&iate, sym)) { + if (sym->link != NULL) { + *sym->link = *iate.ppointer; + } + + pe_patch(iate.ppointer, &sym->patch, sizeof(sym->patch)); + } + } + } +} + +static bool hook_table_match_proc( + const struct pe_iat_entry *iate, + const struct hook_symbol *sym) +{ + if ( sym->name != NULL && + iate->name != NULL && + strcmp(sym->name, iate->name) == 0) { + return true; + } + + if (sym->ordinal != 0 && sym->ordinal == iate->ordinal) { + return true; + } + + return false; +} diff --git a/hook/table.h b/hook/table.h new file mode 100644 index 0000000..f4028cc --- /dev/null +++ b/hook/table.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include + +struct hook_symbol { + const char *name; + uint16_t ordinal; + void *patch; + void **link; +}; + +void hook_table_apply( + HMODULE target, + const char *depname, + const struct hook_symbol *syms, + size_t nsyms); + +void hook_table_revert( + HMODULE target, + const char *depname, + const struct hook_symbol *syms, + size_t nsyms);