Last active
December 8, 2019 04:06
-
-
Save Barakat/6c2445653e7490a2698bbc16a41d96c4 to your computer and use it in GitHub Desktop.
SSDT hook implementation
This file contains hidden or 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
#include <wdm.h> | |
#ifndef _X86_ | |
#error "Only x86 is supported" | |
#endif | |
// | |
// 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)) | |
// | |
// Atomically swap the hook and the function pointer, and store the original function pointer | |
// | |
#define INSTALL_SERVICE_HOOK(Service) (Service ## Original = InterlockedExchangePointer((PVOID)&WritableServiceTable[GET_SERVICE_INDEX(Service)], Service ## Hook)) | |
// | |
// Restore the original function | |
// | |
#define REMOVE_SERVICE_HOOK(Service) (InterlockedExchangePointer((PVOID)&WritableServiceTable[GET_SERVICE_INDEX(Service)], Service ## Original)) | |
// | |
// The definition of the first fields in the service table descriptor. We only care about the table | |
// base address and the limit which holds the number of system calls in the table | |
// | |
typedef struct _KSERVICE_TABLE_DESCRIPTOR | |
{ | |
PULONG_PTR Base; | |
PULONG Count; | |
ULONG Limit; | |
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; | |
// | |
// Let us hook ZwCreateFile/NtCreateFile | |
// | |
typedef | |
NTSTATUS | |
(NTAPI* PZW_CREATE_FILE)(_Out_ PHANDLE FileHandle, | |
_In_ ACCESS_MASK DesiredAccess, | |
_In_ POBJECT_ATTRIBUTES ObjectAttributes, | |
_Out_ PIO_STATUS_BLOCK IoStatusBlock, | |
_In_opt_ PLARGE_INTEGER AllocationSize, | |
_In_ ULONG FileAttributes, | |
_In_ ULONG ShareAccess, | |
_In_ ULONG CreateDisposition, | |
_In_ ULONG CreateOptions, | |
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer, | |
_In_ ULONG EaLength); | |
// | |
// We will create an MDL for the table and keep it around until the driver is unloaded | |
// | |
static PMDL WritableServiceTableMdl; | |
// | |
// Original address of ZwCreateFile | |
// | |
static PZW_CREATE_FILE ZwCreateFileOriginal; | |
// | |
// Simple hook to log calls to ZwCreateFile | |
// | |
static | |
NTSTATUS | |
NTAPI | |
ZwCreateFileHook(_Out_ PHANDLE FileHandle, | |
_In_ ACCESS_MASK DesiredAccess, | |
_In_ POBJECT_ATTRIBUTES ObjectAttributes, | |
_Out_ PIO_STATUS_BLOCK IoStatusBlock, | |
_In_opt_ PLARGE_INTEGER AllocationSize, | |
_In_ ULONG FileAttributes, | |
_In_ ULONG ShareAccess, | |
_In_ ULONG CreateDisposition, | |
_In_ ULONG CreateOptions, | |
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer, | |
_In_ ULONG EaLength) | |
{ | |
DbgPrint("ZwCreateFileHook\n"); | |
// | |
// Call the original function | |
// | |
return ZwCreateFileOriginal(FileHandle, | |
DesiredAccess, | |
ObjectAttributes, | |
IoStatusBlock, | |
AllocationSize, | |
FileAttributes, | |
ShareAccess, | |
CreateDisposition, | |
CreateOptions, | |
EaBuffer, | |
EaLength); | |
} | |
// | |
// Restore the function and free the MDL when the driver is unloaded | |
// | |
static | |
VOID | |
_Function_class_(DRIVER_UNLOAD) | |
DriverUnload(PDRIVER_OBJECT DriverObject) | |
{ | |
UNREFERENCED_PARAMETER(DriverObject); | |
// | |
// Remap the service table at high priority, the system will crush if the hooked function points | |
// to code in an non-existing driver | |
// | |
const PULONG_PTR WritableServiceTable = MmMapLockedPagesSpecifyCache(WritableServiceTableMdl, | |
KernelMode, | |
MmCached, | |
NULL, | |
FALSE, | |
HighPagePriority); | |
if (WritableServiceTable != NULL) | |
{ | |
const NTSTATUS Status = MmProtectMdlSystemAddress(WritableServiceTableMdl, PAGE_READWRITE); | |
if (NT_SUCCESS(Status)) | |
{ | |
// | |
// Restore the function | |
// | |
#pragma warning(push) | |
#pragma warning(disable: 4152) | |
REMOVE_SERVICE_HOOK(ZwCreateFile); | |
#pragma warning(pop) | |
} | |
MmUnmapLockedPages(WritableServiceTable, WritableServiceTableMdl); | |
} | |
IoFreeMdl(WritableServiceTableMdl); | |
} | |
// | |
// Driver entry point | |
// | |
NTSTATUS | |
_Function_class_(DRIVER_INITIALIZE) | |
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) | |
{ | |
NTSTATUS Status = STATUS_UNSUCCESSFUL; | |
UNREFERENCED_PARAMETER(RegistryPath); | |
DriverObject->DriverUnload = DriverUnload; | |
// | |
// Resolve the address of KeServiceDescriptorTable, it is exported by ntoskrnl.exe | |
// | |
UNICODE_STRING Unicode; | |
RtlInitUnicodeString(&Unicode, L"KeServiceDescriptorTable"); | |
const PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable = MmGetSystemRoutineAddress(&Unicode); | |
if (KeServiceDescriptorTable != NULL) | |
{ | |
// | |
// Allocate an MDL, build it and keep it around | |
// | |
WritableServiceTableMdl = IoAllocateMdl(KeServiceDescriptorTable->Base, | |
KeServiceDescriptorTable->Limit * sizeof(*KeServiceDescriptorTable->Base | |
), | |
FALSE, | |
FALSE, | |
NULL); | |
if (WritableServiceTableMdl != NULL) | |
{ | |
MmBuildMdlForNonPagedPool(WritableServiceTableMdl); | |
// | |
// Map the Service table to a writable memory | |
// | |
const PULONG_PTR WritableServiceTable = MmMapLockedPagesSpecifyCache(WritableServiceTableMdl, | |
KernelMode, | |
MmCached, | |
NULL, | |
FALSE, | |
NormalPagePriority); | |
if (WritableServiceTable != NULL) | |
{ | |
// | |
// Make the memory writable | |
// | |
Status = MmProtectMdlSystemAddress(WritableServiceTableMdl, PAGE_READWRITE); | |
// | |
// Install the service hook | |
// | |
if (NT_SUCCESS(Status)) | |
{ | |
#pragma warning(push) | |
#pragma warning(disable: 4152) | |
INSTALL_SERVICE_HOOK(ZwCreateFile); | |
#pragma warning(pop) | |
} | |
MmUnmapLockedPages(WritableServiceTable, WritableServiceTableMdl); | |
} | |
} | |
} | |
return Status; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment