mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-08-06 02:22:34 -05:00
The function signature was already defined in the header but the implementation in the module missing
250 lines
6.6 KiB
C
250 lines
6.6 KiB
C
#include <windows.h>
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "hook/pe.h"
|
|
#include "hook/peb.h"
|
|
#include "hook/table.h"
|
|
|
|
static const char apiset_prefix[] = "api-ms-win-core-";
|
|
static const size_t apiset_prefix_len = sizeof(apiset_prefix) - 1;
|
|
|
|
static void hook_table_apply_to_all(
|
|
const char *depname, const struct hook_symbol *syms, size_t nsyms);
|
|
|
|
static void hook_table_revert_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 void hook_table_revert_to_iid(
|
|
HMODULE target,
|
|
const pe_iid_t *iid,
|
|
const struct hook_symbol *syms,
|
|
size_t nsyms);
|
|
|
|
static bool hook_table_match_module(
|
|
HMODULE target, const char *iid_name, const char *depname);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static void hook_table_revert_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_revert(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 (hook_table_match_module(target, iid_name, depname)) {
|
|
hook_table_apply_to_iid(target, iid, syms, nsyms);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hook_table_revert(
|
|
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_revert_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 (hook_table_match_module(target, iid_name, depname)) {
|
|
hook_table_revert_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 == NULL) {
|
|
*sym->link = *iate.ppointer;
|
|
}
|
|
|
|
pe_patch(iate.ppointer, &sym->patch, sizeof(sym->patch));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hook_table_revert_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)) {
|
|
// Only revert-able if the original pointer was stored
|
|
// previously
|
|
if (sym->link != NULL && *sym->link != NULL) {
|
|
pe_patch(iate.ppointer, sym->link, sizeof(*sym->link));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool hook_table_match_module(
|
|
HMODULE target, const char *iid_name, const char *depname)
|
|
{
|
|
HMODULE kernel32;
|
|
int result;
|
|
|
|
/* OK, first do a straightforward match on the imported DLL name versus
|
|
the hook table DLL name. If it succeeds then we're done. */
|
|
|
|
result = _stricmp(iid_name, depname);
|
|
|
|
if (result == 0) {
|
|
return true;
|
|
}
|
|
|
|
/* If it failed then we have to check if this hook table targets kernel32.
|
|
We have to do some special processing around API sets in that case, so
|
|
stop here if kernel32 is not the subject of this hook table. */
|
|
|
|
if (_stricmp(depname, "kernel32.dll") != 0) {
|
|
return false;
|
|
}
|
|
|
|
/* There isn't really any good test for whether a DLL import wants a
|
|
concrete DLL or an abstract DLL providing a particular Windows API-set,
|
|
so we use a hacky check against the prefix. If the imported DLL name
|
|
looks like an apiset then we'll allow kernel32 hook tables to apply. */
|
|
|
|
result = _strnicmp(iid_name, apiset_prefix, apiset_prefix_len);
|
|
|
|
if (result != 0) {
|
|
return false;
|
|
}
|
|
|
|
/* ... EXCEPT for the case where we're hooking all DLLs loaded into the
|
|
process and we are currently examining kernel32 itself. In that case
|
|
there's some weird reference loops issues I don't entirely understand
|
|
right now. To avoid those, we just don't apply kernel32 hook tables to
|
|
kernel32 itself. */
|
|
|
|
kernel32 = GetModuleHandleW(L"kernel32.dll");
|
|
|
|
if (target == kernel32) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|