Last active
October 8, 2023 05:43
-
-
Save Barakat/7e01524fbf2a55031fb2f2d4edf1d8bd to your computer and use it in GitHub Desktop.
sysenter/KiFastCallEntry/IA32_SYSENTER_EIP hooking driver for Windows x86
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// sysenter/KiFastCallEntry/IA32_SYSENTER_EIP hooking driver | |
// | |
// Barakat Soror (https://twitter.com/barakatsoror) | |
// | |
#include <wdm.h> | |
#include <intrin.h> | |
#ifndef _X86_ | |
#error "Only x86 is supported" | |
#endif | |
// | |
// This is the MSR address of the value that will be loaded in EIP when sysenter is requested. Windows | |
// stores a pointer to KiFastCallEntry. This MSR has a "Core" scope. Meaning each "physical core" will have | |
// its own value. | |
// | |
// https://www.felixcloutier.com/x86/sysenter | |
// https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-1350.html | |
// | |
#define IA32_SYSENTER_EIP 0x176 | |
// | |
// If you disassemble any service, you will see that it moves the service index to EAX in the first | |
// instruction. The index is right after the first byte of the MOV opcode. This behavior looks stable | |
// and is used by Sysinternals Procmon. | |
// | |
// | |
// 0: kd > u ZwCreateFile L4 | |
// ntdll!NtCreateFile: | |
// 770655c8 b842000000 mov eax, 42h | |
// 770655cd ba0003fe7f mov edx, offset SharedUserData!SystemCallStub(7ffe0300) | |
// 770655d2 ff12 call dword ptr[edx] | |
// 770655d4 c22c00 ret 2Ch | |
// | |
// Another method is linearly search the table for the function instead. But the previous method does the job. | |
// | |
#define GET_SERVICE_INDEX(Service) *((PULONG_PTR) ((PCHAR)(Service) + 1)) | |
// | |
// Target system call we want to hook | |
// | |
ULONG_PTR TargetSystemCallIndex; | |
// | |
// Value of the original MSR value | |
// | |
ULONG_PTR KiFastCallEntryReal; | |
// | |
// These events are just to ensure that the hook will be used and stopped being used on all cores at | |
// the same time | |
// | |
static KEVENT EnterKiFastCallEntryHookEvent; | |
static KEVENT ExitKiFastCallEntryHookEvent; | |
// | |
// This is a stub code written in assembly to store/restore CPU registers, set selector registers, then | |
// it will call the real KiFastCallEntryHook | |
// | |
extern VOID KiFastCallEntryHookStub(VOID); | |
// | |
// Our real hook function. It will always be running at PASSIVE_LEVEL | |
// | |
VOID | |
NTAPI | |
KiFastCallEntryHook(ULONG_PTR SystemCallIndex, ULONG_PTR SavedStackPointer) | |
{ | |
if (KeReadStateEvent(&EnterKiFastCallEntryHookEvent) != 0 && KeReadStateEvent(&ExitKiFastCallEntryHookEvent) == 0) | |
{ | |
if (SystemCallIndex == TargetSystemCallIndex) | |
{ | |
DbgPrint("[!] KiFastCallEntryHook (eax = %p, edx = %p)\n", SystemCallIndex, SavedStackPointer); | |
} | |
} | |
} | |
// | |
// Device unload procedure | |
// | |
static | |
VOID | |
_Function_class_(DRIVER_UNLOAD) | |
DriverUnload(PDRIVER_OBJECT DriverObject) | |
{ | |
UNREFERENCED_PARAMETER(DriverObject); | |
DbgPrint("[!] DRIVER UNLOAD\n"); | |
// | |
// Signal hook disabling | |
// | |
KeSetEvent(&ExitKiFastCallEntryHookEvent, 0, FALSE); | |
// | |
// Remove all hooks and restore the real sysenter handlers | |
// | |
KAFFINITY ActiveProcessors = KeQueryActiveProcessors(); | |
KAFFINITY Affinity; | |
for (Affinity = 1; ActiveProcessors != 0; Affinity <<= 1, ActiveProcessors >>= 1) | |
{ | |
if (ActiveProcessors & 1) | |
{ | |
KeSetSystemAffinityThread(Affinity); | |
DbgPrint("[!] UNHOOKING A LOGICAL PROCESSOR\n"); | |
__writemsr(IA32_SYSENTER_EIP, KiFastCallEntryReal); | |
} | |
} | |
// | |
// Revert the thread to its original affinity | |
// | |
KeRevertToUserAffinityThread(); | |
} | |
// | |
// Driver entry point | |
// | |
NTSTATUS | |
NTAPI | |
_Function_class_(DRIVER_INITIALIZE) | |
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) | |
{ | |
UNREFERENCED_PARAMETER(RegistryPath); | |
DriverObject->DriverUnload = DriverUnload; | |
DbgPrint("[!] DRIVER LOAD\n"); | |
// | |
// Store the real sysenter handler value of any core | |
// | |
KiFastCallEntryReal = (ULONG_PTR)__readmsr(IA32_SYSENTER_EIP); | |
// | |
// Read the service index | |
// | |
TargetSystemCallIndex = GET_SERVICE_INDEX(ZwCreateFile); | |
// | |
// Create notification events | |
// | |
KeInitializeEvent(&EnterKiFastCallEntryHookEvent, NotificationEvent, FALSE); | |
KeInitializeEvent(&ExitKiFastCallEntryHookEvent, NotificationEvent, FALSE); | |
// | |
// We will pin the thread on each logical core and update its IA32_SYSENTER_EIP | |
// | |
KAFFINITY ActiveProcessors = KeQueryActiveProcessors(); | |
KAFFINITY Affinity; | |
for (Affinity = 1; ActiveProcessors != 0; Affinity <<= 1, ActiveProcessors >>= 1) | |
{ | |
if (ActiveProcessors & 1) | |
{ | |
KeSetSystemAffinityThread(Affinity); | |
DbgPrint("[!] HOOKING A LOGICAL PROCESSOR\n"); | |
__writemsr(IA32_SYSENTER_EIP, (ULONG_PTR)&KiFastCallEntryHookStub); | |
} | |
} | |
// | |
// Revert the thread to its original affinity | |
// | |
KeRevertToUserAffinityThread(); | |
// | |
// Enable the hook | |
// | |
KeSetEvent(&EnterKiFastCallEntryHookEvent, 0, FALSE); | |
return STATUS_SUCCESS; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; | |
; sysenter/KiFastCallEntry/IA32_SYSENTER_EIP hooking driver | |
; | |
; Barakat Soror (https://twitter.com/barakatsoror) | |
; | |
.model flat, stdcall | |
KiFastCallEntryHook proto near stdcall, | |
SystemCallIndex:dword, | |
SavedStackPointer:dword | |
extern KiFastCallEntryReal: dword | |
public KiFastCallEntryHookStub | |
.code | |
KiFastCallEntryHookStub proc | |
; At this point, the cs and ss segment selectors have kernel-mode selectors | |
; (IA32_SYSENTER_CS, IA32_SYSENTER_CS + 8), and the stack pointer (ESP) points | |
; to valid kernel stack (IA32_SYSENTER_ESP). We need to store registers so we | |
; can restore their contents after calling our hook, and fix the remaining selectors | |
; because they are still using user-mode selectors | |
; Store general registers and EFLAGS on the stack | |
pushad | |
pushfd | |
push fs | |
push ds | |
push es | |
; The segement selectors (fs, ds, es) will be holding user-mode selectors | |
; so we need to set them to the kernel-mode selectors (fs = 0x30, ds = es = 0x23) | |
push cx | |
; Load 0x30 into fs segment selector | |
mov cx, 30h | |
mov fs, cx | |
; Load 0x23 into ds and es segment selectors | |
mov cx, 23h | |
mov ds, cx | |
mov es, cx | |
pop cx | |
; Call our hook and pass the system call index (eax) and the user stack pointer (edx) | |
invoke KiFastCallEntryHook, eax, edx | |
; Restore register values | |
pop es | |
pop ds | |
pop fs | |
popfd | |
popad | |
; Jump to the real sysenter handler | |
jmp [KiFastCallEntryReal] | |
KiFastCallEntryHookStub endp | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment