mirror of
https://github.com/zer0condition/GoodmansKernel/
synced 2026-08-19 01:14:35 +00:00
169 lines
6.1 KiB
C
169 lines
6.1 KiB
C
/* infinity_hook.c - InfinityHook as a wasm guest.
|
|
* original technique: github.com/everdox/InfinityHook (MIT). */
|
|
|
|
#include "../guest_sdk/gvm.h"
|
|
|
|
GVM_MANIFEST(GVM_CAP_READ_KMEM | GVM_CAP_WRITE_KMEM | GVM_CAP_INTROSPECT |
|
|
GVM_CAP_CALLBACKS | GVM_CAP_HOSTCALL);
|
|
|
|
static u64 g_nt_base = 0;
|
|
static u32 g_nt_size = 0;
|
|
static u64 g_getclock_slot = 0;
|
|
static u64 g_orig_getclock = 0;
|
|
|
|
// walk pages backwards from any VA inside nt.exe until we find the MZ+PE
|
|
// header pair. classic module-base recovery.
|
|
static u64 find_nt_base(u64 va_in_nt)
|
|
{
|
|
u64 p = va_in_nt & ~0xFFFULL;
|
|
for (u32 i = 0; i < 4096 && p >= 0x1000; i++, p -= 0x1000) {
|
|
if (!MmIsAddressValid((PVOID)p)) continue;
|
|
if ((gvm_read_u32(p) & 0xFFFF) != 0x5A4D) continue;
|
|
u32 pe_off = gvm_read_u32(p + 0x3C);
|
|
if (!pe_off || pe_off > 0x1000) continue;
|
|
if (gvm_read_u32(p + pe_off) != 0x00004550) continue;
|
|
return p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// scan nt's .data section for the classic InfinityHook signature that
|
|
// marks the start of EtwpDebuggerData: 2C 08 04 38
|
|
static u64 find_etwp_debugger_data(u64 nt)
|
|
{
|
|
u32 pe_off = gvm_read_u32(nt + 0x3C);
|
|
u16 nsec = (u16)(gvm_read_u32(nt + pe_off + 6) & 0xFFFF);
|
|
u16 opt_sz = (u16)(gvm_read_u32(nt + pe_off + 20) & 0xFFFF);
|
|
u64 sec = nt + pe_off + 24 + opt_sz;
|
|
|
|
for (u16 i = 0; i < nsec; i++, sec += 40) {
|
|
if (gvm_read_u32(sec) != 0x61746164) continue; // '.dat'
|
|
u32 v_sz = gvm_read_u32(sec + 8);
|
|
u32 v_rva = gvm_read_u32(sec + 12);
|
|
u64 base = nt + v_rva;
|
|
u64 end = base + v_sz;
|
|
for (u64 q = base; q + 8 < end; q += 4) {
|
|
if (!MmIsAddressValid((PVOID)q)) continue;
|
|
if (gvm_read_u8(q + 0) == 0x2C &&
|
|
gvm_read_u8(q + 1) == 0x08 &&
|
|
gvm_read_u8(q + 2) == 0x04 &&
|
|
gvm_read_u8(q + 3) == 0x38)
|
|
return q;
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// EtwpDebuggerData -> silo -> kernel WMI_LOGGER_CONTEXT -> GetCpuClock slot.
|
|
// GetCpuClock offset inside the context varies by Windows build (0x28 on
|
|
// most modern Win10/11, 0x18/0x30 on older). we try each and accept the
|
|
// one whose current value points into nt (unhooked baseline).
|
|
static u64 locate_getclock_slot(u64 nt, u32 nt_size, u64* out_orig)
|
|
{
|
|
u64 etwp = find_etwp_debugger_data(nt);
|
|
if (!etwp) return 0;
|
|
|
|
u64 silo = gvm_read_u64(etwp + 0x10);
|
|
if (!silo || !MmIsAddressValid((PVOID)silo)) return 0;
|
|
|
|
u64 ctx = gvm_read_u64(silo + 2 * 8); // silo[2] = kernel logger context
|
|
if (!ctx || !MmIsAddressValid((PVOID)ctx)) return 0;
|
|
|
|
static const u32 candidates[] = { 0x28, 0x18, 0x30 };
|
|
for (u32 i = 0; i < 3; i++) {
|
|
u64 slot = ctx + candidates[i];
|
|
if (!MmIsAddressValid((PVOID)slot)) continue;
|
|
u64 v = gvm_read_u64(slot);
|
|
if (v >= nt && v < nt + nt_size) {
|
|
*out_orig = v;
|
|
return slot;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// install: locate everything, atomically swap in the driver's trampoline.
|
|
GVM_EXPORT(start)
|
|
u64 start(u64 sample_rate)
|
|
{
|
|
if (g_getclock_slot) { gvm_print("[ih] already installed"); return 0; }
|
|
|
|
PVOID any = MmGetSystemRoutineAddress((PVOID)0); // arg unused for demo
|
|
// real resolve: build a UNICODE_STRING for a known nt export, pass it in
|
|
u64 ustr = host_make_unistr((u32)(unsigned long)L"KeBugCheckEx",
|
|
sizeof(L"KeBugCheckEx") - sizeof(u16));
|
|
any = MmGetSystemRoutineAddress((PVOID)ustr);
|
|
host_free_unistr(ustr);
|
|
if (!any) { gvm_print("[ih] MmGetSystemRoutineAddress failed"); return 1; }
|
|
|
|
g_nt_base = find_nt_base((u64)any);
|
|
if (!g_nt_base) { gvm_print("[ih] nt base not found"); return 2; }
|
|
u32 pe_off = gvm_read_u32(g_nt_base + 0x3C);
|
|
g_nt_size = gvm_read_u32(g_nt_base + pe_off + 24 + 56); // SizeOfImage
|
|
|
|
g_getclock_slot = locate_getclock_slot(g_nt_base, g_nt_size, &g_orig_getclock);
|
|
if (!g_getclock_slot) {
|
|
gvm_print("[ih] GetCpuClock slot not located (build offsets may differ)");
|
|
return 3;
|
|
}
|
|
|
|
// hand the trampoline our nt bounds so it filters non-syscall callers
|
|
gvm_ih_configure((u32)sample_rate, g_nt_base, g_nt_size);
|
|
|
|
// atomic pointer swap. aligned 8-byte write on x64 is single-copy atomic
|
|
u64 tramp = gvm_ih_trampoline();
|
|
if (!tramp) { gvm_print("[ih] trampoline addr = 0"); return 4; }
|
|
gvm_write_u64(g_getclock_slot, tramp);
|
|
|
|
char line[128], t[24]; u32 n = 0;
|
|
n += gvm_strcpy(line + n, "[ih] hook installed. nt=0x");
|
|
gvm_hex64(g_nt_base, t); n += gvm_strcpy(line + n, t);
|
|
n += gvm_strcpy(line + n, " slot=0x");
|
|
gvm_hex64(g_getclock_slot, t); n += gvm_strcpy(line + n, t);
|
|
n += gvm_strcpy(line + n, " orig=0x");
|
|
gvm_hex64(g_orig_getclock, t); n += gvm_strcpy(line + n, t);
|
|
n += gvm_strcpy(line + n, " tramp=0x");
|
|
gvm_hex64(tramp, t); n += gvm_strcpy(line + n, t);
|
|
gvm_print(line);
|
|
return 0;
|
|
}
|
|
|
|
// uninstall: atomically restore original, then wait for in-flight trampoline
|
|
// calls to drain before we let the module be unloaded.
|
|
GVM_EXPORT(stop)
|
|
u64 stop(void)
|
|
{
|
|
if (!g_getclock_slot) { gvm_print("[ih] not installed"); return 0; }
|
|
gvm_write_u64(g_getclock_slot, g_orig_getclock);
|
|
u64 total = gvm_ih_quiesce();
|
|
g_getclock_slot = 0;
|
|
g_orig_getclock = 0;
|
|
|
|
char line[80], t[24]; u32 n = 0;
|
|
n += gvm_strcpy(line + n, "[ih] hook removed. trampoline hits=");
|
|
gvm_dec((u32)total, t); n += gvm_strcpy(line + n, t);
|
|
gvm_print(line);
|
|
return total;
|
|
}
|
|
|
|
// dispatched by the driver's worker on every sampled syscall event.
|
|
static volatile u32 g_seen = 0;
|
|
|
|
GVM_EXPORT(on_syscall)
|
|
u64 on_syscall(u64 tid, u64 retaddr, u64 counter)
|
|
{
|
|
g_seen++;
|
|
if ((g_seen & 63) != 0) return 0; // print 1 in 64 to keep log readable
|
|
|
|
char line[128], t[24]; u32 n = 0;
|
|
n += gvm_strcpy(line + n, "[ih] tid=");
|
|
gvm_dec((u32)tid, t); n += gvm_strcpy(line + n, t);
|
|
n += gvm_strcpy(line + n, " ret=0x");
|
|
gvm_hex64(retaddr, t); n += gvm_strcpy(line + n, t);
|
|
n += gvm_strcpy(line + n, " hit=");
|
|
gvm_dec((u32)counter, t); n += gvm_strcpy(line + n, t);
|
|
gvm_print(line);
|
|
return 0;
|
|
}
|