Skip to content

Instantly share code, notes, and snippets.

@Barakat
Last active December 8, 2019 04:06
Show Gist options
  • Save Barakat/6c2445653e7490a2698bbc16a41d96c4 to your computer and use it in GitHub Desktop.
Save Barakat/6c2445653e7490a2698bbc16a41d96c4 to your computer and use it in GitHub Desktop.
SSDT hook implementation
#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