From 581ee8f8efedd41bc4fa7728133e90fb6407fd2d Mon Sep 17 00:00:00 2001 From: wj32 Date: Thu, 8 Apr 2010 04:09:49 +0000 Subject: [PATCH] copied KProcessHacker to ProcessHacker2 directory git-svn-id: svn://svn.code.sf.net/p/processhacker/code@3040 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- 2.x/trunk/KProcessHacker/HACKING.txt | 67 + .../KProcessHacker/amd64/kprocesshacker.sys | Bin 0 -> 60416 bytes 2.x/trunk/KProcessHacker/auto.cmd | 7 + 2.x/trunk/KProcessHacker/autoreload.cmd | 2 + 2.x/trunk/KProcessHacker/handle.c | 355 +++ 2.x/trunk/KProcessHacker/hook.c | 408 +++ .../KProcessHacker/i386/kprocesshacker.sys | Bin 0 -> 59904 bytes 2.x/trunk/KProcessHacker/include/debug.h | 35 + 2.x/trunk/KProcessHacker/include/ex.h | 262 ++ 2.x/trunk/KProcessHacker/include/handle.h | 78 + 2.x/trunk/KProcessHacker/include/handlep.h | 144 + 2.x/trunk/KProcessHacker/include/hook.h | 108 + 2.x/trunk/KProcessHacker/include/io.h | 34 + 2.x/trunk/KProcessHacker/include/ke.h | 95 + 2.x/trunk/KProcessHacker/include/kph.h | 506 ++++ .../KProcessHacker/include/kprocesshacker.h | 170 ++ 2.x/trunk/KProcessHacker/include/mm.h | 37 + 2.x/trunk/KProcessHacker/include/ob.h | 168 ++ 2.x/trunk/KProcessHacker/include/protect.h | 95 + 2.x/trunk/KProcessHacker/include/ps.h | 151 + 2.x/trunk/KProcessHacker/include/ref.h | 113 + 2.x/trunk/KProcessHacker/include/refp.h | 137 + 2.x/trunk/KProcessHacker/include/se.h | 55 + 2.x/trunk/KProcessHacker/include/sync.h | 320 ++ 2.x/trunk/KProcessHacker/include/sysservice.h | 279 ++ .../KProcessHacker/include/sysservicedata.h | 174 ++ .../KProcessHacker/include/sysservicep.h | 468 +++ 2.x/trunk/KProcessHacker/include/test.h | 30 + 2.x/trunk/KProcessHacker/include/trace.h | 188 ++ 2.x/trunk/KProcessHacker/include/types.h | 7 + 2.x/trunk/KProcessHacker/include/util.h | 133 + 2.x/trunk/KProcessHacker/include/version.h | 241 ++ 2.x/trunk/KProcessHacker/include/zw.h | 68 + 2.x/trunk/KProcessHacker/io.c | 265 ++ 2.x/trunk/KProcessHacker/kph.c | 414 +++ 2.x/trunk/KProcessHacker/kprocesshacker.c | 2609 +++++++++++++++++ 2.x/trunk/KProcessHacker/makefile | 1 + 2.x/trunk/KProcessHacker/mm.c | 703 +++++ 2.x/trunk/KProcessHacker/ob.c | 872 ++++++ 2.x/trunk/KProcessHacker/protect.c | 457 +++ 2.x/trunk/KProcessHacker/ps.c | 1221 ++++++++ 2.x/trunk/KProcessHacker/ref.c | 574 ++++ 2.x/trunk/KProcessHacker/resource.rc | 53 + 2.x/trunk/KProcessHacker/se.c | 102 + 2.x/trunk/KProcessHacker/sources | 29 + 2.x/trunk/KProcessHacker/sync.c | 312 ++ 2.x/trunk/KProcessHacker/sysservice.c | 2140 ++++++++++++++ 2.x/trunk/KProcessHacker/sysservicedata.c | 513 ++++ 2.x/trunk/KProcessHacker/test.c | 71 + 2.x/trunk/KProcessHacker/trace.c | 344 +++ 2.x/trunk/KProcessHacker/util.c | 115 + 2.x/trunk/KProcessHacker/version.c | 611 ++++ 52 files changed, 16341 insertions(+) create mode 100644 2.x/trunk/KProcessHacker/HACKING.txt create mode 100644 2.x/trunk/KProcessHacker/amd64/kprocesshacker.sys create mode 100644 2.x/trunk/KProcessHacker/auto.cmd create mode 100644 2.x/trunk/KProcessHacker/autoreload.cmd create mode 100644 2.x/trunk/KProcessHacker/handle.c create mode 100644 2.x/trunk/KProcessHacker/hook.c create mode 100644 2.x/trunk/KProcessHacker/i386/kprocesshacker.sys create mode 100644 2.x/trunk/KProcessHacker/include/debug.h create mode 100644 2.x/trunk/KProcessHacker/include/ex.h create mode 100644 2.x/trunk/KProcessHacker/include/handle.h create mode 100644 2.x/trunk/KProcessHacker/include/handlep.h create mode 100644 2.x/trunk/KProcessHacker/include/hook.h create mode 100644 2.x/trunk/KProcessHacker/include/io.h create mode 100644 2.x/trunk/KProcessHacker/include/ke.h create mode 100644 2.x/trunk/KProcessHacker/include/kph.h create mode 100644 2.x/trunk/KProcessHacker/include/kprocesshacker.h create mode 100644 2.x/trunk/KProcessHacker/include/mm.h create mode 100644 2.x/trunk/KProcessHacker/include/ob.h create mode 100644 2.x/trunk/KProcessHacker/include/protect.h create mode 100644 2.x/trunk/KProcessHacker/include/ps.h create mode 100644 2.x/trunk/KProcessHacker/include/ref.h create mode 100644 2.x/trunk/KProcessHacker/include/refp.h create mode 100644 2.x/trunk/KProcessHacker/include/se.h create mode 100644 2.x/trunk/KProcessHacker/include/sync.h create mode 100644 2.x/trunk/KProcessHacker/include/sysservice.h create mode 100644 2.x/trunk/KProcessHacker/include/sysservicedata.h create mode 100644 2.x/trunk/KProcessHacker/include/sysservicep.h create mode 100644 2.x/trunk/KProcessHacker/include/test.h create mode 100644 2.x/trunk/KProcessHacker/include/trace.h create mode 100644 2.x/trunk/KProcessHacker/include/types.h create mode 100644 2.x/trunk/KProcessHacker/include/util.h create mode 100644 2.x/trunk/KProcessHacker/include/version.h create mode 100644 2.x/trunk/KProcessHacker/include/zw.h create mode 100644 2.x/trunk/KProcessHacker/io.c create mode 100644 2.x/trunk/KProcessHacker/kph.c create mode 100644 2.x/trunk/KProcessHacker/kprocesshacker.c create mode 100644 2.x/trunk/KProcessHacker/makefile create mode 100644 2.x/trunk/KProcessHacker/mm.c create mode 100644 2.x/trunk/KProcessHacker/ob.c create mode 100644 2.x/trunk/KProcessHacker/protect.c create mode 100644 2.x/trunk/KProcessHacker/ps.c create mode 100644 2.x/trunk/KProcessHacker/ref.c create mode 100644 2.x/trunk/KProcessHacker/resource.rc create mode 100644 2.x/trunk/KProcessHacker/se.c create mode 100644 2.x/trunk/KProcessHacker/sources create mode 100644 2.x/trunk/KProcessHacker/sync.c create mode 100644 2.x/trunk/KProcessHacker/sysservice.c create mode 100644 2.x/trunk/KProcessHacker/sysservicedata.c create mode 100644 2.x/trunk/KProcessHacker/test.c create mode 100644 2.x/trunk/KProcessHacker/trace.c create mode 100644 2.x/trunk/KProcessHacker/util.c create mode 100644 2.x/trunk/KProcessHacker/version.c diff --git a/2.x/trunk/KProcessHacker/HACKING.txt b/2.x/trunk/KProcessHacker/HACKING.txt new file mode 100644 index 000000000..00c104cfa --- /dev/null +++ b/2.x/trunk/KProcessHacker/HACKING.txt @@ -0,0 +1,67 @@ +==== KProcessHacker ==== + +== IMPORTANT == +KProcessHacker has been developed from either reverse engineering of +the Windows kernel or ReactOS code (http://www.reactos.org). The +following files contain "ported" ReactOS code (with modifications): + + * mm.c + * MiDoMappedCopy + * MiDoPoolCopy (added smarter buffer management) + * MiGetExceptionInfo + * ps.c + * KphOpenProcess + * KphOpenThread + * se.c + * KphOpenProcessTokenEx + +== CODE STRUCTURE == + * handle.c + - Contains handle table code. + * hook.c + - Contains hooking code. Currently you may hook any kernel-mode + function and object type open procedures. + * io.c + - Contains I/O-related code, such as device and driver functions. + * kph.c + - Contains support routines. + * kprocesshacker.c + - Contains interfacing code, mainly consisting of the I/O control + handler. + * mm.c + - Contains memory-related code, such as reading and writing. + * ob.c + - Contains object-related code, such as handle duplication. + * protect.c + - Contains process protection code. Process protection is + achieved by hooking ObOpenObjectByPointer and some object type + OpenProcedures. + * ps.c + - Contains process- and thread-related code, such as opening and + terminating. + * ref.c + - Contains the KPH object manager. + * se.c + - Contains security-related code. Only function there is + KphOpenProcessTokenEx. + * sync.c + - Various synchronization functions. + * sysservice.c + - System service logging. + * trace.c + - Stack trace code. + * version.c + - Contains Windows-version-specific data. + +== POOL TAGS == +PhAB: System service logging argument block. sysservice.h +PhCH: Client handle table. kprocesshacker.h +PhCt: System service logging argument capture temporary buffer. sysservicep.h +PhCU: Captured Unicode string. kph.h +PhEB: System service logging event block. sysservice.h +PhOb: Object manager object. refp.h +PhPC: Pool-based virtual memory copying. mm.h +PhPr: Protection entry. protect.h +PhSc: System service call entry. sysservicedata.h +PhSD: Processor lock DPC storage. sync.h +PhSt: Stack back trace. ps.h diff --git a/2.x/trunk/KProcessHacker/amd64/kprocesshacker.sys b/2.x/trunk/KProcessHacker/amd64/kprocesshacker.sys new file mode 100644 index 0000000000000000000000000000000000000000..9c86f56245b18c3450696c7f82774df722f9ba56 GIT binary patch literal 60416 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. + */ + +#include "include/handle.h" +#include "include/handlep.h" + +NTSTATUS KphpAllocateHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __out PKPH_HANDLE_TABLE_ENTRY *Entry + ); + +NTSTATUS KphpFreeHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __in PKPH_HANDLE_TABLE_ENTRY Entry + ); + +/* KphCreateHandleTable + * + * Creates a handle table. + * + * HandleTable: A variable which receives a pointer to the handle table. + * MaximumHandles: The maximum number of handles that can be created. + * SizeOfEntry: The size of each handle table entry. This value must be + * divisible by 4. + * Tag: The tag to use when allocating handle table resources. + */ +NTSTATUS KphCreateHandleTable( + __out PKPH_HANDLE_TABLE *HandleTable, + __in ULONG MaximumHandles, + __in ULONG SizeOfEntry, + __in ULONG Tag + ) +{ + PKPH_HANDLE_TABLE handleTable; + + /* Each handle entry must be at least the size of our + * handle table entry definition. + */ + if (SizeOfEntry < sizeof(KPH_HANDLE_TABLE_ENTRY)) + return STATUS_INVALID_PARAMETER_3; + /* Handle entries must be 4-byte aligned. */ + if (SizeOfEntry % 4 != 0) + return STATUS_INVALID_PARAMETER_3; + + /* Allocate storage for the handle table structure. */ + handleTable = ExAllocatePoolWithTag( + PagedPool, + sizeof(KPH_HANDLE_TABLE), + Tag + ); + + if (!handleTable) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Allocate storage for the handle table itself. */ + handleTable->Table = ExAllocatePoolWithTag( + PagedPool, + MaximumHandles * SizeOfEntry, + Tag + ); + + if (!handleTable->Table) + { + ExFreePoolWithTag(handleTable, Tag); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Initialize the rest of the table descriptor. */ + handleTable->Tag = Tag; + handleTable->SizeOfEntry = SizeOfEntry; + handleTable->NextHandle = (HANDLE)0; + handleTable->FreeHandle = NULL; + ExInitializeFastMutex(&handleTable->Mutex); + handleTable->TableSize = MaximumHandles * SizeOfEntry; + + /* Zero the handle table. */ + memset(handleTable->Table, 0, handleTable->TableSize); + + /* Pass the pointer to the handle table back. */ + *HandleTable = handleTable; + + return STATUS_SUCCESS; +} + +/* KphFreeHandleTable + * + * Frees all handle table resources. + */ +VOID KphFreeHandleTable( + __in PKPH_HANDLE_TABLE HandleTable + ) +{ + ULONG i; + ULONG tag; + + /* Free all handle values. */ + for (i = 0; i < HandleTable->TableSize / HandleTable->SizeOfEntry; i++) + { + KphCloseHandle(HandleTable, KphHandleFromIndex(i)); + } + + /* Save the handle table tag first. */ + tag = HandleTable->Tag; + /* Free the table. */ + ExFreePoolWithTag(HandleTable->Table, tag); + /* Free the descriptor. */ + ExFreePoolWithTag(HandleTable, tag); +} + +/* KphCloseHandle + * + * Closes a handle, dereferencing the referenced object. + */ +NTSTATUS KphCloseHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + PVOID object; + + if (!KphValidHandle(HandleTable, Handle, &entry)) + return STATUS_INVALID_HANDLE; + + /* Save a pointer to the object referenced by the handle. */ + object = entry->Object; + /* Free the handle. */ + status = KphpFreeHandleEntry(HandleTable, entry); + + if (!NT_SUCCESS(status)) + return status; + + /* Dereference the object. */ + KphDereferenceObject(object); + + return status; +} + +/* KphCreateHandle + * + * Creates a handle and references an object. + */ +NTSTATUS KphCreateHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in PVOID Object, + __out PHANDLE Handle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + + /* Allocate a handle. */ + status = KphpAllocateHandleEntry(HandleTable, &entry); + + if (!NT_SUCCESS(status)) + return status; + + /* Reference and set the object in the entry. */ + KphReferenceObject(Object); + entry->Object = Object; + + /* Pass the handle back. */ + *Handle = KphGetHandleEntry(entry); + + return status; +} + +/* KphReferenceObjectByHandle + * + * References an object from a handle. + */ +NTSTATUS KphReferenceObjectByHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + + if (!KphValidHandle(HandleTable, Handle, &entry)) + return STATUS_INVALID_HANDLE; + + /* Lock the entry. */ + if (!KphLockAllocatedHandleEntry(entry)) + return STATUS_INVALID_HANDLE; + + /* Check the type of object if the caller requested us + * to do that. + */ + if (ObjectType) + { + if (KphGetObjectType(entry->Object) != ObjectType) + { + /* Bad type. */ + KphUnlockHandleEntry(entry); + + return STATUS_OBJECT_TYPE_MISMATCH; + } + } + + /* Reference and pass the object back. */ + KphReferenceObject(entry->Object); + *Object = entry->Object; + + KphUnlockHandleEntry(entry); + + return status; +} + +/* KphValidHandle + * + * Checks if a handle is valid. + */ +BOOLEAN KphValidHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry + ) +{ + PKPH_HANDLE_TABLE_ENTRY entry; + BOOLEAN valid; + + entry = KphEntryFromHandle(HandleTable, Handle); + valid = + ((ULONG_PTR)entry >= (ULONG_PTR)HandleTable->Table) && + ((ULONG_PTR)entry + HandleTable->SizeOfEntry <= + (ULONG_PTR)HandleTable->Table + HandleTable->TableSize); + + if (valid) + *Entry = entry; + + return valid; +} + +/* KphpAllocateHandleEntry + * + * Allocates a handle table entry. + */ +NTSTATUS KphpAllocateHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __out PKPH_HANDLE_TABLE_ENTRY *Entry + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry = NULL; + + /* Prevent others from modifying the handle table. */ + ExAcquireFastMutex(&HandleTable->Mutex); + + /* Check the free list first. If we have a free entry, + * claim it and update the free list. Otherwise, create + * a new entry from the NextHandle value. + */ + if (HandleTable->FreeHandle) + { + /* We have a free entry. Update the free list. */ + entry = HandleTable->FreeHandle; + /* The next free entry goes into FreeHandle. */ + HandleTable->FreeHandle = KphGetNextFreeEntry(entry); + } + else + { + /* No free handles. We have to initialize a new one + * based on the NextHandle value. + */ + /* Make sure we don't go past the end of the table. */ + if ( + KphIndexFromHandle(HandleTable->NextHandle) * + HandleTable->SizeOfEntry <= + HandleTable->TableSize + ) + { + /* Get a pointer to the entry from the handle. */ + entry = KphEntryFromHandle(HandleTable, HandleTable->NextHandle); + /* Increment the next handle value. */ + HandleTable->NextHandle = KphIncrementHandle(HandleTable->NextHandle); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) + { + /* Set the entry's handle value. */ + entry->Handle = KphHandleFromEntry(HandleTable, entry); + KphSetAllocatedEntry(entry); + + *Entry = entry; + } + + ExReleaseFastMutex(&HandleTable->Mutex); + + return status; +} + +/* KphpFreeHandleEntry + * + * Frees a handle table entry. + */ +NTSTATUS KphpFreeHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __in PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + ExAcquireFastMutex(&HandleTable->Mutex); + + /* Lock the entry. */ + if (!KphLockAllocatedHandleEntry(Entry)) + { + /* Someone else has already freed the entry (or it was never allocated). */ + ExReleaseFastMutex(&HandleTable->Mutex); + return STATUS_INVALID_HANDLE; + } + + /* Mark the entry as unallocated. */ + KphClearAllocatedEntry(Entry); + + /* Add the entry to the free list. */ + KphSetNextFreeEntry(Entry, HandleTable->FreeHandle); + HandleTable->FreeHandle = Entry; + + /* Zero the entry (except for the Value). */ + memset(&Entry->Object, 0, HandleTable->SizeOfEntry - sizeof(ULONG_PTR)); + + /* Unlock the entry. */ + KphUnlockHandleEntry(Entry); + + ExReleaseFastMutex(&HandleTable->Mutex); + + return STATUS_SUCCESS; +} diff --git a/2.x/trunk/KProcessHacker/hook.c b/2.x/trunk/KProcessHacker/hook.c new file mode 100644 index 000000000..3f775db5d --- /dev/null +++ b/2.x/trunk/KProcessHacker/hook.c @@ -0,0 +1,408 @@ +/* + * Process Hacker Driver - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/hook.h" +#include "include/sync.h" + +static KPH_PROCESSOR_LOCK HookProcessorLock; + +/* KphHookInit + * + * Initializes the hooking module. + */ +NTSTATUS KphHookInit() +{ + KphInitializeProcessorLock(&HookProcessorLock); + + return STATUS_SUCCESS; +} + +/* KphInitializeHook + * + * Initializes a hook structure. + */ +VOID KphInitializeHook( + __out PKPH_HOOK Hook, + __in PVOID Function, + __in PVOID Target + ) +{ + memset(Hook, 0, sizeof(KPH_HOOK)); + Hook->Function = Function; + Hook->Target = Target; +} + +/* KphHook + * + * Hooks a kernel-mode function. + * WARNING: DO NOT HOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphHook( + __inout PKPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PUCHAR function; + + status = KphpCreateMappedMdl( + Hook->Function, + 5, + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + function = (PUCHAR)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Note that this is completely safe even though we are at + * DISPATCH_LEVEL because we are using the mapped MDL. + */ + /* Copy the original five bytes (for unhooking). */ + memcpy(Hook->Bytes, function, 10); + /* Hook the function by writing a jump instruction. */ + Hook->Hooked = TRUE; + /* jmp Target */ + *function = 0xe9; + *(PULONG_PTR)(function + 1) = (ULONG_PTR)Hook->Target - (ULONG_PTR)Hook->Function - 5; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + dprintf("KphHook: Could not acquire processor lock!\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphUnhook + * + * Unhooks a kernel-mode function. + * WARNING: DO NOT UNHOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphUnhook( + __inout PKPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + + if (!Hook->Hooked) + return STATUS_UNSUCCESSFUL; + + status = KphpCreateMappedMdl( + Hook->Function, + 5, + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Unpatch the function. */ + memcpy(mappedMdl.Address, Hook->Bytes, 5); + Hook->Hooked = FALSE; + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + dprintf("KphUnhook: Could not acquire processor lock!\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphObOpenCall + * + * Calls the original open procedure for an object type. + * + * AccessMode: If this argument is unavailable, specify KernelMode. + */ +NTSTATUS NTAPI KphObOpenCall( + __in PKPH_OB_OPEN_HOOK ObOpenHook, + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + /* If there wasn't any original open procedure, exit. */ + if (!ObOpenHook->Function) + return STATUS_SUCCESS; + + if (WindowsVersion == WINDOWS_XP) + { + return ((OB_OPEN_METHOD_51)ObOpenHook->Function)( + OpenReason, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + return ((OB_OPEN_METHOD_60)ObOpenHook->Function)( + OpenReason, + AccessMode, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} + +/* KphInitializeObOpenHook + * + * Initializes a hook structure. + */ +VOID KphInitializeObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook, + __in POBJECT_TYPE ObjectType, + __in PVOID Target51, + __in PVOID Target60 + ) +{ + memset(ObOpenHook, 0, sizeof(KPH_OB_OPEN_HOOK)); + ObOpenHook->ObjectType = ObjectType; + ObOpenHook->Target51 = Target51; + ObOpenHook->Target60 = Target60; +} + +/* KphObOpenHook + * + * Hooks the open procedure for an object type. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PVOID *openProcedure; + + status = KphpCreateMappedMdl( + KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure), + sizeof(PVOID), + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + openProcedure = (PVOID *)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Save the original open procedure pointer. */ + ObOpenHook->Function = *openProcedure; + + /* Choose the correct target open procedure and hook. */ + if (WindowsVersion == WINDOWS_XP) + { + if (ObOpenHook->Target51) + *openProcedure = ObOpenHook->Target51; + else + status = STATUS_INVALID_PARAMETER; + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + if (ObOpenHook->Target60) + *openProcedure = ObOpenHook->Target60; + else + status = STATUS_INVALID_PARAMETER; + } + else + { + status = STATUS_NOT_SUPPORTED; + } + + ObOpenHook->Hooked = TRUE; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphObOpenUnhook + * + * Unhooks the open procedure for an object type. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphObOpenUnhook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PVOID *openProcedure; + + if (!ObOpenHook->Hooked) + return STATUS_UNSUCCESSFUL; + + status = KphpCreateMappedMdl( + KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure), + sizeof(PVOID), + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + openProcedure = (PVOID *)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Restore the original open procedure pointer. */ + *openProcedure = ObOpenHook->Function; + ObOpenHook->Hooked = FALSE; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphpCreateMappedMdl + * + * Creates and maps a MDL. + * + * Thread safety: Full + * IRQL: Any + */ +NTSTATUS KphpCreateMappedMdl( + __in PVOID Address, + __in ULONG Length, + __out PMAPPED_MDL MappedMdl + ) +{ + PMDL mdl; + + MappedMdl->Mdl = NULL; + MappedMdl->Address = NULL; + + mdl = IoAllocateMdl(Address, Length, FALSE, FALSE, NULL); + + if (mdl == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + MmBuildMdlForNonPagedPool(mdl); + mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA; + MappedMdl->Address = MmMapLockedPagesSpecifyCache( + mdl, + KernelMode, + MmNonCached, + NULL, + FALSE, + HighPagePriority + ); + MappedMdl->Mdl = mdl; + + if (!MappedMdl->Address) + { + KphpFreeMappedMdl(MappedMdl); + return STATUS_INSUFFICIENT_RESOURCES; + } + + return STATUS_SUCCESS; +} + +/* KphpFreeMappedMdl + * + * Unmaps and frees a MDL. + * + * Thread safety: Full + * IRQL: Any + */ +VOID KphpFreeMappedMdl( + __in PMAPPED_MDL MappedMdl + ) +{ + if (MappedMdl->Mdl != NULL) + { + if (MappedMdl->Address != NULL) + { + MmUnmapLockedPages(MappedMdl->Address, MappedMdl->Mdl); + MappedMdl->Address = NULL; + } + + IoFreeMdl(MappedMdl->Mdl); + MappedMdl->Mdl = NULL; + } +} diff --git a/2.x/trunk/KProcessHacker/i386/kprocesshacker.sys b/2.x/trunk/KProcessHacker/i386/kprocesshacker.sys new file mode 100644 index 0000000000000000000000000000000000000000..247c5f1fadc484b875919d405262a114680ecb8d GIT binary patch literal 59904 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 literal 0 HcmV?d00001 diff --git a/2.x/trunk/KProcessHacker/include/debug.h b/2.x/trunk/KProcessHacker/include/debug.h new file mode 100644 index 000000000..0a140616b --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/debug.h @@ -0,0 +1,35 @@ +/* + * Process Hacker Driver - + * debug definitions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifdef DBG +#define dprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__) +#else +#define dprintf +#endif + +#define dfprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__) +#define dwprintf DbgPrint + +#endif diff --git a/2.x/trunk/KProcessHacker/include/ex.h b/2.x/trunk/KProcessHacker/include/ex.h new file mode 100644 index 000000000..bfd06ccbe --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/ex.h @@ -0,0 +1,262 @@ +/* + * Process Hacker Driver - + * executive + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _EX_H +#define _EX_H + +#include "types.h" + +/* HACK - version.c dependency */ +#define WINDOWS_XP 51 +#define WINDOWS_SERVER_2003 52 +#define WINDOWS_VISTA 60 +#define WINDOWS_7 61 + +extern ULONG WindowsVersion; + +/* Handles */ + +struct _HANDLE_TABLE; +struct _HANDLE_TABLE_ENTRY; + +typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)( + struct _HANDLE_TABLE_ENTRY *HandleTableEntry, + HANDLE Handle, + PVOID Context + ); + +BOOLEAN NTAPI ExEnumHandleTable( + __in struct _HANDLE_TABLE *HandleTable, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ); + +/* Push Locks */ + +/* Definition for Windows 2003 and above. This means we + * MUST use the slow path on Windows XP. + */ +typedef struct _EXI_PUSH_LOCK +{ + union + { + struct + { + ULONG_PTR Locked : 1; + ULONG_PTR Waiting : 1; + ULONG_PTR Waking : 1; + ULONG_PTR MultipleShared : 1; + ULONG_PTR Shared : sizeof(ULONG_PTR) * 8 - 4; /* ULONG_PTR bits minus 4 */ + }; + ULONG_PTR Value; + PVOID Ptr; + }; +} EXI_PUSH_LOCK, *PEXI_PUSH_LOCK; + +#define EX_PUSH_LOCK_LOCK_SHIFT 0 +#define EX_PUSH_LOCK_LOCK ((ULONG_PTR)0x1) +/* Indicates chained waiters */ +#define EX_PUSH_LOCK_WAITING ((ULONG_PTR)0x2) +/* Traversing the list */ +#define EX_PUSH_LOCK_WAKING ((ULONG_PTR)0x4) +/* Multiple owners + waiters */ +#define EX_PUSH_LOCK_MULTIPLE_SHARED ((ULONG_PTR)0x8) + +#define EX_PUSH_LOCK_SHARE_INC ((ULONG_PTR)0x10) +#define EX_PUSH_LOCK_PTR_BITS ((ULONG_PTR)0xf) + +NTKERNELAPI VOID FASTCALL ExfAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfReleasePushLock( + __inout PEX_PUSH_LOCK PushLock + ); + +/* The below functions are only exported on Vista and higher. */ + +NTKERNELAPI VOID FASTCALL ExfReleasePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfReleasePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI BOOLEAN FASTCALL ExfTryAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfTryToWakePushLock( + __inout PEX_PUSH_LOCK PushLock + ); + +/* Wrapper functions */ + +/* ExInitializePushLock + * + * Initializes a push lock. + */ +FORCEINLINE VOID ExInitializePushLock( + __out PEX_PUSH_LOCK PushLock + ) +{ + *PushLock = 0; +} + +/* ExAcquirePushLockExclusive + * + * Acquires a push lock in exclusive mode. + */ +FORCEINLINE VOID ExAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path - acquire push lock, no function call. */ + if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT)) + { + /* Slow path - call the function. */ + ExfAcquirePushLockExclusive(PushLock); + } +} + +/* ExAcquirePushLockShared + * + * Acquires a push lock in shared mode. + */ +FORCEINLINE VOID ExAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path - acquire push lock which is not held at all, no function call. */ + if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedCompareExchangePointer( + (PVOID)PushLock, + (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK), + 0 + ) != 0) + { + /* Slow path - call the function. */ + ExfAcquirePushLockShared(PushLock); + } +} + +/* ExReleasePushLock + * + * Releases a push lock (for both types). + */ +FORCEINLINE VOID ExReleasePushLock( + __inout PEX_PUSH_LOCK PushLock + ) +{ + EXI_PUSH_LOCK oldValue, newValue; + + oldValue.Value = *PushLock; + + /* If we are the last to release in shared mode or we + * are releasing in exclusive mode, we simply set + * the value to 0. + */ + + if (oldValue.Shared > 1) + { + /* One less shared holder. */ + newValue.Value = oldValue.Value - EX_PUSH_LOCK_SHARE_INC; + } + else + { + newValue.Value = 0; + } + + /* If we have chained waiters, we can't release the + * push lock using the fast path since they need to + * be unblocked. + */ + if ( + WindowsVersion < WINDOWS_SERVER_2003 || + oldValue.Waiting || + InterlockedCompareExchangePointer( + (PVOID)PushLock, + newValue.Ptr, + oldValue.Ptr + ) != oldValue.Ptr + ) + { + /* Slow path - call the function. */ + ExfReleasePushLock(PushLock); + } +} + +#ifndef NEVER_DEFINED +/* ExTryAcquirePushLockExclusive + * + * Attempts to acquire a push lock in exclusive mode. + * + * Return value: TRUE if the push lock was acquired, FALSE if + * the push lock was already acquired in exclusive mode. + */ +FORCEINLINE BOOLEAN ExTryAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ) +{ + if (!InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/* ExTryAcquirePushLockShared + * + * Attempts to acquire a push lock in shared mode. + * + * Return value: TRUE if the push lock was acquired, FALSE if + * the push lock was already acquired in exclusive mode. + */ +FORCEINLINE BOOLEAN ExTryAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path with the push lock not held at all. */ + if (InterlockedCompareExchangePointer( + (PVOID)PushLock, + (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK), + 0 + ) != 0) + { + return ExfTryAcquirePushLockShared(PushLock); + } + else + { + return TRUE; + } +} +#endif + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/handle.h b/2.x/trunk/KProcessHacker/include/handle.h new file mode 100644 index 000000000..710205b3e --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/handle.h @@ -0,0 +1,78 @@ +/* + * Process Hacker Driver - + * handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HANDLE_H +#define _HANDLE_H + +#include "kph.h" +#include "ref.h" + +struct _KPH_HANDLE_TABLE; +typedef struct _KPH_HANDLE_TABLE *PKPH_HANDLE_TABLE; + +typedef struct _KPH_HANDLE_TABLE_ENTRY +{ + union + { + HANDLE Handle; + ULONG_PTR Value; + struct _KPH_HANDLE_TABLE_ENTRY *NextFree; + }; + PVOID Object; +} KPH_HANDLE_TABLE_ENTRY, *PKPH_HANDLE_TABLE_ENTRY; + +NTSTATUS KphCreateHandleTable( + __out PKPH_HANDLE_TABLE *HandleTable, + __in ULONG MaximumHandles, + __in ULONG SizeOfEntry, + __in ULONG Tag + ); + +VOID KphFreeHandleTable( + __in PKPH_HANDLE_TABLE HandleTable + ); + +NTSTATUS KphCloseHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle + ); + +NTSTATUS KphCreateHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in PVOID Object, + __out PHANDLE Handle + ); + +NTSTATUS KphReferenceObjectByHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ); + +BOOLEAN KphValidHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/handlep.h b/2.x/trunk/KProcessHacker/include/handlep.h new file mode 100644 index 000000000..a6cd445ee --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/handlep.h @@ -0,0 +1,144 @@ +/* + * Process Hacker Driver - + * handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HANDLEP_H +#define _HANDLEP_H + +#define _HANDLE_PRIVATE +#include "handle.h" +#include "sync.h" + +#define KPH_HANDLE_INCREMENT 4 +#define KPH_HANDLE_LOCKED 0x1 +#define KPH_HANDLE_LOCKED_SHIFT 0 +#define KPH_HANDLE_ALLOCATED 0x2 +#define KPH_HANDLE_FLAGS 0x3 + +#define KphGetFlagsEntry(Entry) ((Entry)->Value & KPH_HANDLE_FLAGS) +#define KphGetHandleEntry(Entry) ((HANDLE)((Entry)->Value & ~KPH_HANDLE_FLAGS)) +#define KphIncrementHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) + KPH_HANDLE_INCREMENT)) + +#define KphIsAllocatedEntry(Entry) ((Entry)->Value & KPH_HANDLE_ALLOCATED) +#define KphClearAllocatedEntry(Entry) ((Entry)->Value &= ~KPH_HANDLE_ALLOCATED) +#define KphSetAllocatedEntry(Entry) ((Entry)->Value |= KPH_HANDLE_ALLOCATED) + +#define KphGetNextFreeEntry(Entry) ((PKPH_HANDLE_TABLE_ENTRY)((Entry)->Value & ~KPH_HANDLE_FLAGS)) +#define KphSetNextFreeEntry(Entry, NextFree) ((Entry)->Value = ((ULONG_PTR)(NextFree) | KphGetFlagsEntry(Entry))) + +#define KphHandleFromIndex(Index) ((HANDLE)((ULONG_PTR)(Index) * KPH_HANDLE_INCREMENT)) +#define KphHandleFromIndexEx(Index, Flags) ((HANDLE)(((Index) * KPH_HANDLE_INCREMENT) | (Flags))) +#define KphIndexFromHandle(Handle) (((ULONG_PTR)(Handle) & ~KPH_HANDLE_FLAGS) / KPH_HANDLE_INCREMENT) + +#define KphEntryFromHandle(HandleTable, Handle) KphEntryFromIndex((HandleTable), KphIndexFromHandle(Handle)) +#define KphEntryFromIndex(HandleTable, Index) \ + ((PKPH_HANDLE_TABLE_ENTRY)((ULONG_PTR)(HandleTable)->Table + (Index) * (HandleTable)->SizeOfEntry)) +#define KphHandleFromEntry(HandleTable, Entry) KphHandleFromIndex(KphIndexFromEntry((HandleTable), (Entry))) +#define KphHandleFromEntryEx(HandleTable, Entry, Flags) \ + KphHandleFromIndexEx(KphIndexFromEntry((HandleTable), (Entry)), (Flags)) +#define KphIndexFromEntry(HandleTable, Entry) \ + (((ULONG_PTR)(Entry) - (ULONG_PTR)(HandleTable)->Table) / (HandleTable)->SizeOfEntry) + +typedef struct _KPH_HANDLE_TABLE +{ + /* The pool tag used for this descriptor and the table itself. */ + ULONG Tag; + /* The size of each handle table entry. */ + ULONG SizeOfEntry; + /* The next handle value to use. */ + HANDLE NextHandle; + /* The free list of handle table entries. */ + struct _KPH_HANDLE_TABLE_ENTRY *FreeHandle; + + /* A fast mutex guarding writes to the handle table. */ + FAST_MUTEX Mutex; + /* The size of the table, in bytes. */ + ULONG TableSize; + /* The actual handle table. */ + PVOID Table; +} KPH_HANDLE_TABLE, *PKPH_HANDLE_TABLE; + +FORCEINLINE BOOLEAN KphLockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +FORCEINLINE VOID KphUnlockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +/* KphLockHandle + * + * Locks a handle table entry for exclusive access. Do not + * modify the lowest bit of the entry's value while you + * hold the lock. + * + * Return value: TRUE if the entry is allocated, otherwise FALSE. + */ +FORCEINLINE BOOLEAN KphLockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + /* Acquire the spinlock. */ + KphAcquireBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT); + + /* Return whether the entry is allocated. */ + return !!(Entry->Value & KPH_HANDLE_ALLOCATED); +} + +/* KphLockAllocatedHandle + * + * Locks a handle table entry for exclusive access. Do not + * modify the lowest bit of the entry's value while you + * hold the lock. + * The function will not lock the handle if it is unallocated. + * + * Return value: TRUE if the entry was locked, otherwise FALSE. + */ +FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + if (!KphLockHandleEntry(Entry)) + { + KphUnlockHandleEntry(Entry); + return FALSE; + } + + return TRUE; +} + +/* KphUnlockHandle + * + * Unlocks a handle table entry. + */ +FORCEINLINE VOID KphUnlockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + /* Unlock the spinlock. */ + KphReleaseBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT); +} + +#endif diff --git a/2.x/trunk/KProcessHacker/include/hook.h b/2.x/trunk/KProcessHacker/include/hook.h new file mode 100644 index 000000000..12ec3d3f9 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/hook.h @@ -0,0 +1,108 @@ +/* + * Process Hacker Driver - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HOOK_H +#define _HOOK_H + +#include "kph.h" +#include "ob.h" + +#define KPH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \ + __declspec(naked) Name(Arguments) \ + { \ + __asm lea eax, Hook \ + __asm mov eax, [eax+KPH_HOOK.Function] \ + __asm add eax, 5 \ + __asm push ebp \ + __asm mov ebp, esp \ + __asm jmp eax \ + } \ + +typedef struct _KPH_HOOK +{ + /* The address of the hooked function. + Should NOT be a function that is callable above PASSIVE_LEVEL. */ + PVOID Function; + /* The address of the new function. */ + PVOID Target; + /* Whether the function is hooked. */ + BOOLEAN Hooked; + /* The original first 10 bytes. */ + CHAR Bytes[10]; +} KPH_HOOK, *PKPH_HOOK; + +typedef struct _KPH_OB_OPEN_HOOK +{ + /* The object type that is being hooked. */ + POBJECT_TYPE ObjectType; + /* The original open procedure. */ + PVOID Function; + /* The new open procedure for NT 5.1 (XP). */ + OB_OPEN_METHOD_51 Target51; + /* The new open procedure for NT 6.1 and above (Vista, 7 or higher). */ + OB_OPEN_METHOD_60 Target60; + /* Whether the open procedure is hooked. */ + BOOLEAN Hooked; +} KPH_OB_OPEN_HOOK, *PKPH_OB_OPEN_HOOK; + +NTSTATUS KphHookInit(); + +VOID KphInitializeHook( + __out PKPH_HOOK Hook, + __in PVOID Function, + __in PVOID Target + ); + +NTSTATUS KphHook( + __inout PKPH_HOOK Hook + ); + +NTSTATUS KphUnhook( + __inout PKPH_HOOK Hook + ); + +NTSTATUS NTAPI KphObOpenCall( + __in PKPH_OB_OPEN_HOOK ObOpenHook, + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +VOID KphInitializeObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook, + __in POBJECT_TYPE ObjectType, + __in PVOID Target51, + __in PVOID Target60 + ); + +NTSTATUS KphObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ); + +NTSTATUS KphObOpenUnhook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/io.h b/2.x/trunk/KProcessHacker/include/io.h new file mode 100644 index 000000000..bc98fb151 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/io.h @@ -0,0 +1,34 @@ +/* + * Process Hacker Driver - + * I/O manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _IO_H +#define _IO_H + +#include "types.h" + +extern POBJECT_TYPE *IoAdapterObjectType; +extern POBJECT_TYPE *IoControllerObjectType; +extern POBJECT_TYPE *IoDeviceHandlerObjectType; /* not used anymore */ +extern POBJECT_TYPE *IoDeviceObjectType; +extern POBJECT_TYPE *IoDriverObjectType; + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/ke.h b/2.x/trunk/KProcessHacker/include/ke.h new file mode 100644 index 000000000..741c1b00b --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/ke.h @@ -0,0 +1,95 @@ +/* + * Process Hacker Driver - + * kernel + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KE_H +#define _KE_H + +#include "types.h" + +/* APCs */ + +typedef enum _KAPC_ENVIRONMENT +{ + OriginalApcEnvironment, + AttachedApcEnvironment, + CurrentApcEnvironment, + InsertApcEnvironment +} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT; + +typedef VOID (NTAPI *PKKERNEL_ROUTINE)( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)( + PKAPC Apc + ); + +typedef VOID (NTAPI *PKNORMAL_ROUTINE)( + PVOID NormalContext, + PVOID SystemArgument1, + PVOID SystemArgument2 + ); + +NTKERNELAPI VOID NTAPI KeInitializeApc( + PKAPC Apc, + PKTHREAD Thread, + KAPC_ENVIRONMENT Environment, + PKKERNEL_ROUTINE KernelRoutine, + PKRUNDOWN_ROUTINE RundownRoutine, + PKNORMAL_ROUTINE NormalRoutine, + KPROCESSOR_MODE ProcessorMode, + PVOID NormalContext + ); + +NTKERNELAPI BOOLEAN NTAPI KeInsertQueueApc( + PRKAPC Apc, + PVOID SystemArgument1, + PVOID SystemArgument2, + KPRIORITY Increment + ); + +/* System services */ + +/* Exported by ntoskrnl as KeServiceDescriptorTable. */ +typedef struct _KSERVICE_TABLE_DESCRIPTOR +{ + /* A pointer to an array of ULONG_PTRs - addresses of + * system services. + */ + PULONG_PTR Base; + /* A pointer to an array of ULONGs which contain counters for + * the system services. + */ + PULONG Count; + /* The number of system services. */ + ULONG Limit; + /* A pointer to an array of UCHARs which contain + * the number of arguments (in bytes) for each system service. + */ + PUCHAR Number; +} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/kph.h b/2.x/trunk/KProcessHacker/include/kph.h new file mode 100644 index 000000000..99c7a8e8d --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/kph.h @@ -0,0 +1,506 @@ +/* + * Process Hacker Driver - + * custom APIs + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KPH_H +#define _KPH_H + +#include "types.h" +#include "debug.h" +#include "ref.h" +#include "version.h" + +#include "ke.h" +#include "mm.h" +#include "ps.h" +#include "trace.h" +#include "zw.h" + +#define MAX_UINTEGER(Bits) ((1 << (Bits)) - 1) +#define BITS_UCHAR 8 +#define MAX_UCHAR MAX_UINTEGER(BITS_UCHAR) +#define BITS_USHORT 16 +#define MAX_USHORT MAX_UINTEGER(BITS_USHORT) +#define BITS_ULONG 32 +#define MAX_ULONG MAX_UINTEGER(BITS_ULONG) + +#define SYSTEM_PROCESS_ID ((HANDLE)4) +#define KERNEL_HANDLE_BIT ((ULONG_PTR)1 << (sizeof(HANDLE) * 8 - 1)) +#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0) +#define MakeKernelHandle(Handle) ((ULONG_PTR)(Handle) |= KERNEL_HANDLE_BIT) + +#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) + +#define GET_BIT(Integer, Bit) (((Integer) >> (Bit)) & 0x1) +#define SET_BIT(Integer, Bit) ((Integer) |= 1 << (Bit)) +#define CLEAR_BIT(Integer, Bit) ((Integer) &= ~(1 << (Bit))) + +#define KPH_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000) +#define KPH_REL_TIMEOUT_IN_SEC(Time) (Time * -1 * KPH_TIMEOUT_TO_SEC) + +#define TAG_CAPTURED_UNICODE_STRING ('UChP') + +#ifdef EXT +#undef EXT +#endif + +#ifdef _KPH_PRIVATE +#define EXT +#define EQNULL = NULL +#else +#define EXT extern +#define EQNULL +#endif + +EXT POBJECT_TYPE *ObDirectoryObjectType EQNULL; +EXT POBJECT_TYPE *ObTypeObjectType EQNULL; + +EXT PKSERVICE_TABLE_DESCRIPTOR __KeServiceDescriptorTable EQNULL; +EXT PVOID __KiFastCallEntry EQNULL; +EXT _NtClose __NtClose EQNULL; +EXT _ObGetObjectType ObGetObjectType EQNULL; +EXT _PsGetProcessJob PsGetProcessJob EQNULL; +EXT _PsResumeProcess PsResumeProcess EQNULL; +EXT _PsSuspendProcess PsSuspendProcess EQNULL; +EXT _PsTerminateProcess __PsTerminateProcess EQNULL; +EXT PVOID __PspTerminateThreadByPointer EQNULL; +EXT _NtClose __ZwClose EQNULL; + +/* Driver information */ + +typedef enum _DRIVER_INFORMATION_CLASS +{ + DriverBasicInformation, + DriverNameInformation, + DriverServiceKeyNameInformation, + MaxDriverInfoClass +} DRIVER_INFORMATION_CLASS; + +typedef struct _DRIVER_BASIC_INFORMATION +{ + ULONG Flags; + PVOID DriverStart; + ULONG DriverSize; +} DRIVER_BASIC_INFORMATION, *PDRIVER_BASIC_INFORMATION; + +typedef struct _KPH_ATTACH_STATE +{ + BOOLEAN Attached; + PEPROCESS Process; + KAPC_STATE ApcState; +} KPH_ATTACH_STATE, *PKPH_ATTACH_STATE; + +typedef struct _MAPPED_MDL +{ + PMDL Mdl; + PVOID Address; +} MAPPED_MDL, *PMAPPED_MDL; + +typedef struct _PROCESS_HANDLE +{ + HANDLE Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; + ULONG HandleAttributes; +} PROCESS_HANDLE, *PPROCESS_HANDLE; + +typedef struct _PROCESS_HANDLE_INFORMATION +{ + ULONG HandleCount; + PROCESS_HANDLE Handles[1]; +} PROCESS_HANDLE_INFORMATION, *PPROCESS_HANDLE_INFORMATION; + +/* Support routines */ + +NTSTATUS KphNtInit(); + +PVOID GetSystemRoutineAddress( + WCHAR *Name + ); + +VOID KphAttachProcess( + __in PEPROCESS Process, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphAttachProcessHandle( + __in HANDLE ProcessHandle, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphAttachProcessId( + __in HANDLE ProcessId, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphCaptureUnicodeString( + __in PUNICODE_STRING UnicodeString, + __out PUNICODE_STRING CapturedUnicodeString + ); + +VOID KphDetachProcess( + __in PKPH_ATTACH_STATE AttachState + ); + +VOID KphFreeCapturedUnicodeString( + __in PUNICODE_STRING CapturedUnicodeString + ); + +VOID KphProbeForReadUnicodeString( + __in PUNICODE_STRING UnicodeString + ); + +VOID KphProbeSystemAddressRange( + __in PVOID BaseAddress, + __in ULONG Length + ); + +NTSTATUS OpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in HANDLE ProcessId + ); + +NTSTATUS SetProcessToken( + __in HANDLE sourcePid, + __in HANDLE targetPid + ); + +/* KProcessHacker */ + +BOOLEAN KphAcquireProcessRundownProtection( + __in PEPROCESS Process + ); + +NTSTATUS KphAssignImpersonationToken( + __in HANDLE ThreadHandle, + __in HANDLE TokenHandle + ); + +NTSTATUS KphCaptureStackBackTraceThread( + __in HANDLE ThreadHandle, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphDangerousTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphDuplicateObject( + __in HANDLE SourceProcessHandle, + __in HANDLE SourceHandle, + __in_opt HANDLE TargetProcessHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ); + +BOOLEAN KphEnumProcessHandleTable( + __in PEPROCESS Process, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ); + +NTSTATUS KphGetContextThread( + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ); + +POBJECT_TYPE KphGetObjectTypeNt( + __in PVOID Object + ); + +HANDLE KphGetProcessId( + __in HANDLE ProcessHandle + ); + +HANDLE KphGetThreadId( + __in HANDLE ThreadHandle, + __out_opt PHANDLE ProcessId + ); + +NTSTATUS KphGetThreadWin32Thread( + __in HANDLE ThreadHandle, + __out PVOID *Win32Thread, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenDirectoryObject( + __out PHANDLE DirectoryObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenDriver( + __out PHANDLE DriverHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenNamedObject( + __out PHANDLE ObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcessJob( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE JobHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcessTokenEx( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG ObjectAttributes, + __out PHANDLE TokenHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenThread( + __out PHANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenThreadProcess( + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE ProcessHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenType( + __out PHANDLE TypeHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphQueryInformationDriver( + __in HANDLE DriverHandle, + __in DRIVER_INFORMATION_CLASS DriverInformationClass, + __out_bcount_opt(DriverInformationLength) PVOID DriverInformation, + __in ULONG DriverInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphQueryNameFileObject( + __in PFILE_OBJECT FileObject, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ); + +NTSTATUS KphQueryNameObject( + __in PVOID Object, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ); + +NTSTATUS KphQueryProcessHandles( + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +VOID KphReleaseProcessRundownProtection( + __in PEPROCESS Process + ); + +NTSTATUS KphResumeProcess( + __in HANDLE ProcessHandle + ); + +NTSTATUS KphSetContextThread( + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSetHandleGrantedAccess( + __in PEPROCESS Process, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ); + +NTSTATUS KphSuspendProcess( + __in HANDLE ProcessHandle + ); + +NTSTATUS KphTerminateProcess( + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphUnsafeReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphWriteVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +/* MM */ + +NTSTATUS MiDoMappedCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +NTSTATUS MiDoPoolCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +ULONG MiGetExceptionInfo( + __in PEXCEPTION_POINTERS ExceptionInfo, + __out PBOOLEAN HaveBadAddress, + __out PULONG_PTR BadAddress + ); + +NTSTATUS MmCopyVirtualMemory( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +/* KProcessHacker private */ + +NTSTATUS KphpCaptureStackBackTraceThread( + __in PETHREAD Thread, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphpCreateMappedMdl( + __in PVOID Address, + __in ULONG Length, + __out PMAPPED_MDL MappedMdl + ); + +VOID KphpFreeMappedMdl( + __in PMAPPED_MDL MappedMdl + ); + +/* OB */ + +NTSTATUS ObDuplicateObject( + __in PEPROCESS SourceProcess, + __in_opt PEPROCESS TargetProcess, + __in HANDLE SourceHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ); + +PHANDLE_TABLE ObReferenceProcessHandleTable( + __in PEPROCESS Process + ); + +VOID ObDereferenceProcessHandleTable( + __in PEPROCESS Process + ); + +/* PS */ + +NTSTATUS PsTerminateProcess( + __in PEPROCESS Process, + __in NTSTATUS ExitStatus + ); + +NTSTATUS PspTerminateThreadByPointer( + __in PETHREAD Thread, + __in NTSTATUS ExitStatus + ); + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/kprocesshacker.h b/2.x/trunk/KProcessHacker/include/kprocesshacker.h new file mode 100644 index 000000000..0df5a7326 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/kprocesshacker.h @@ -0,0 +1,170 @@ +/* + * Process Hacker Driver - + * main header file + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef KPROCESSHACKER_H +#define KPROCESSHACKER_H + +#include "include/kph.h" +#include "include/handle.h" +#include "include/ref.h" +#include "include/sync.h" + +/* KPH Configuration */ + +//#define KPH_REQUIRE_DEBUG_PRIVILEGE + +/* Device */ + +#define KPH_DEVICE_TYPE (0x9999) +#define KPH_DEVICE_NAME (L"\\Device\\KProcessHacker") +#define KPH_DEVICE_DOS_NAME (L"\\DosDevices\\KProcessHacker") + +/* Features */ + +#define KPHF_PSTERMINATEPROCESS 0x1 +#define KPHF_PSPTERMINATETHREADBPYPOINTER 0x2 + +/* Control Codes */ + +#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define KPH_CLOSEHANDLE KPH_CTL_CODE(0) +#define KPH_SSQUERYCLIENTENTRY KPH_CTL_CODE(1) +#define KPH_RESERVED1 KPH_CTL_CODE(2) +#define KPH_OPENPROCESS KPH_CTL_CODE(3) +#define KPH_OPENTHREAD KPH_CTL_CODE(4) +#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(5) +#define KPH_GETPROCESSPROTECTED KPH_CTL_CODE(6) +#define KPH_SETPROCESSPROTECTED KPH_CTL_CODE(7) +#define KPH_TERMINATEPROCESS KPH_CTL_CODE(8) +#define KPH_SUSPENDPROCESS KPH_CTL_CODE(9) +#define KPH_RESUMEPROCESS KPH_CTL_CODE(10) +#define KPH_READVIRTUALMEMORY KPH_CTL_CODE(11) +#define KPH_WRITEVIRTUALMEMORY KPH_CTL_CODE(12) +#define KPH_SETPROCESSTOKEN KPH_CTL_CODE(13) +#define KPH_GETTHREADSTARTADDRESS KPH_CTL_CODE(14) +#define KPH_SETHANDLEATTRIBUTES KPH_CTL_CODE(15) +#define KPH_GETHANDLEOBJECTNAME KPH_CTL_CODE(16) +#define KPH_OPENPROCESSJOB KPH_CTL_CODE(17) +#define KPH_GETCONTEXTTHREAD KPH_CTL_CODE(18) +#define KPH_SETCONTEXTTHREAD KPH_CTL_CODE(19) +#define KPH_GETTHREADWIN32THREAD KPH_CTL_CODE(20) +#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(21) +#define KPH_ZWQUERYOBJECT KPH_CTL_CODE(22) +#define KPH_GETPROCESSID KPH_CTL_CODE(23) +#define KPH_GETTHREADID KPH_CTL_CODE(24) +#define KPH_TERMINATETHREAD KPH_CTL_CODE(25) +#define KPH_GETFEATURES KPH_CTL_CODE(26) +#define KPH_SETHANDLEGRANTEDACCESS KPH_CTL_CODE(27) +#define KPH_ASSIGNIMPERSONATIONTOKEN KPH_CTL_CODE(28) +#define KPH_PROTECTADD KPH_CTL_CODE(29) +#define KPH_PROTECTREMOVE KPH_CTL_CODE(30) +#define KPH_PROTECTQUERY KPH_CTL_CODE(31) +#define KPH_UNSAFEREADVIRTUALMEMORY KPH_CTL_CODE(32) +#define KPH_SETEXECUTEOPTIONS KPH_CTL_CODE(33) +#define KPH_QUERYPROCESSHANDLES KPH_CTL_CODE(34) +#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(35) +#define KPH_CAPTURESTACKBACKTRACETHREAD KPH_CTL_CODE(36) +#define KPH_DANGEROUSTERMINATETHREAD KPH_CTL_CODE(37) +#define KPH_OPENTYPE KPH_CTL_CODE(38) +#define KPH_OPENDRIVER KPH_CTL_CODE(39) +#define KPH_QUERYINFORMATIONDRIVER KPH_CTL_CODE(40) +#define KPH_OPENDIRECTORYOBJECT KPH_CTL_CODE(41) +#define KPH_SSREF KPH_CTL_CODE(42) +#define KPH_SSUNREF KPH_CTL_CODE(43) +#define KPH_SSCREATECLIENTENTRY KPH_CTL_CODE(44) +#define KPH_SSCREATERULESETENTRY KPH_CTL_CODE(45) +#define KPH_SSREMOVERULE KPH_CTL_CODE(46) +#define KPH_SSADDPROCESSIDRULE KPH_CTL_CODE(47) +#define KPH_SSADDTHREADIDRULE KPH_CTL_CODE(48) +#define KPH_SSADDPREVIOUSMODERULE KPH_CTL_CODE(49) +#define KPH_SSADDNUMBERRULE KPH_CTL_CODE(50) +#define KPH_SSENABLECLIENTENTRY KPH_CTL_CODE(51) +#define KPH_OPENNAMEDOBJECT KPH_CTL_CODE(52) +#define KPH_QUERYINFORMATIONPROCESS KPH_CTL_CODE(53) +#define KPH_QUERYINFORMATIONTHREAD KPH_CTL_CODE(54) +#define KPH_SETINFORMATIONPROCESS KPH_CTL_CODE(55) +#define KPH_SETINFORMATIONTHREAD KPH_CTL_CODE(56) + +/* Standard Driver Routines */ + +NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); +VOID DriverUnload(PDRIVER_OBJECT DriverObject); +NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp); + +/* Clients */ + +#define TAG_CLIENT_HANDLETABLE ('HChP') +#define KPH_CLIENT_SSMAXCOUNT 1000 +#define KPH_CLIENT_MAXHANDLES 100 + +typedef struct _KPH_CLIENT_ENTRY +{ + LIST_ENTRY ClientListEntry; + HANDLE ProcessId; + PKPH_HANDLE_TABLE HandleTable; + + KPH_GUARDED_LOCK SsLock; + /* The number of times the client has "started" the system service logger. */ + LONG SsStartCount; +} KPH_CLIENT_ENTRY, *PKPH_CLIENT_ENTRY; + +/* Functions */ + +VOID SsRef(LONG count); +VOID SsUnref(LONG count); + +VOID NTAPI ClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +PKPH_CLIENT_ENTRY CreateClientEntry( + __in HANDLE ProcessId + ); + +PKPH_CLIENT_ENTRY ReferenceClientEntry( + __in_opt HANDLE ProcessId + ); + +NTSTATUS CloseClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle + ); + +NTSTATUS CreateClientHandle( + __in_opt HANDLE ProcessId, + __in PVOID Object, + __out PHANDLE Handle + ); + +NTSTATUS ReferenceClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle, + __in PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ); + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/mm.h b/2.x/trunk/KProcessHacker/include/mm.h new file mode 100644 index 000000000..07206dc0a --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/mm.h @@ -0,0 +1,37 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _MM_H +#define _MM_H + +#define MI_MAX_TRANSFER_SIZE (0x10000) +#define MI_COPY_STACK_SIZE (0x200) +#define MI_MAPPED_COPY_PAGES (14) +#define MM_POOL_COPY_THRESHOLD (0x1ff) +#define TAG_POOL_COPY ('CPhP') + +#define MEM_EXECUTE_OPTION_DISABLE 0x1 +#define MEM_EXECUTE_OPTION_ENABLE 0x2 +#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4 +#define MEM_EXECUTE_OPTION_PERMANENT 0x8 + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/ob.h b/2.x/trunk/KProcessHacker/include/ob.h new file mode 100644 index 000000000..783629c12 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/ob.h @@ -0,0 +1,168 @@ +/* + * Process Hacker Driver - + * object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _OB_H +#define _OB_H + +#include "types.h" +#include "ex.h" + +#define OBJECT_TO_OBJECT_HEADER(o) \ + CONTAINING_RECORD((o), OBJECT_HEADER, Body) + +#define OBJ_PROTECT_CLOSE 0x00000001L +#define OBJ_INHERIT 0x00000002L +#define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L +#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE) + +#define ObpDecodeGrantedAccess(Access) \ + ((Access) & ~ObpAccessProtectCloseBit) +#define ObpDecodeObject(Object) \ + ((PVOID)((ULONG_PTR)(Object) & ~OBJ_HANDLE_ATTRIBUTES)) +#define ObpGetHandleAttributes(HandleTableEntry) \ + (((HandleTableEntry)->GrantedAccess & ObpAccessProtectCloseBit) ? \ + (((HandleTableEntry)->Value & OBJ_HANDLE_ATTRIBUTES) | OBJ_PROTECT_CLOSE) : \ + ((HandleTableEntry)->Value & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE))) + +/* FUNCTION DEFS */ + +struct _OBJECT_HANDLE_FLAG_INFORMATION; +typedef struct _OBJECT_TYPE_INITIALIZER OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; + +NTSTATUS NTAPI ObCreateObjectType( + __in PUNICODE_STRING TypeName, + __in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer, + __in PSECURITY_DESCRIPTOR SecurityDescriptor, + __out_opt POBJECT_TYPE *ObjectType + ); + +NTSTATUS NTAPI ObOpenObjectByName( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE PreviousMode, + __in_opt PACCESS_STATE AccessState, + __in_opt ACCESS_MASK DesiredAccess, + __in PVOID ParseContext, + __out PHANDLE Handle + ); + +NTSTATUS NTAPI ObSetHandleAttributes( + __in HANDLE Handle, + __in struct _OBJECT_HANDLE_FLAG_INFORMATION *HandleFlags, + __in KPROCESSOR_MODE PreviousMode + ); + +/* FUNCTION TYPEDEFS */ + +/* Seven+ */ +typedef POBJECT_TYPE (NTAPI *_ObGetObjectType)( + __in PVOID Object + ); + +enum _OB_OPEN_REASON; + +typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_51)( + enum _OB_OPEN_REASON OpenReason, + PEPROCESS Process, + PVOID Object, + ACCESS_MASK GrantedAccess, + ULONG HandleCount + ); + +typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_60)( + enum _OB_OPEN_REASON OpenReason, + KPROCESSOR_MODE AccessMode, + PEPROCESS Process, + PVOID Object, + ACCESS_MASK GrantedAccess, + ULONG HandleCount + ); + +/* ENUMS */ +typedef enum _OB_OPEN_REASON +{ + ObCreateHandle, + ObOpenHandle, + ObDuplicateHandle, + ObInheritHandle, + ObMaxOpenReason +} OB_OPEN_REASON, *POB_OPEN_REASON; + +/* STRUCTS */ + +typedef struct _OBP_QUERY_PROCESS_HANDLES_DATA +{ + PVOID Buffer; + ULONG BufferLength; + ULONG CurrentIndex; + NTSTATUS Status; +} OBP_QUERY_PROCESS_HANDLES_DATA, *POBP_QUERY_PROCESS_HANDLES_DATA; + +typedef struct _OBP_SET_HANDLE_GRANTED_ACCESS_DATA +{ + HANDLE Handle; + ACCESS_MASK GrantedAccess; +} OBP_SET_HANDLE_GRANTED_ACCESS_DATA, *POBP_SET_HANDLE_GRANTED_ACCESS_DATA; + +typedef struct _OBJECT_HANDLE_FLAG_INFORMATION +{ + BOOLEAN Inherit; + BOOLEAN ProtectFromClose; +} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; + +typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +typedef struct _OBJECT_HEADER +{ + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + POBJECT_TYPE Type; + UCHAR NameInfoOffset; + UCHAR HandleInfoOffset; + UCHAR QuotaInfoOffset; + UCHAR Flags; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +typedef struct _HANDLE_TABLE_ENTRY +{ + union + { + PVOID Object; + ULONG Value; + }; + ULONG GrantedAccess; +} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; + +typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE; + +#endif diff --git a/2.x/trunk/KProcessHacker/include/protect.h b/2.x/trunk/KProcessHacker/include/protect.h new file mode 100644 index 000000000..b714ab1b0 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/protect.h @@ -0,0 +1,95 @@ +/* + * Process Hacker Driver - + * process protection + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PROTECT_H +#define _PROTECT_H + +#include "hook.h" + +#define TAG_PROTECTION_ENTRY ('rPhP') + +#define OBOPENOBJECTBYPOINTER_ARGS \ + PVOID Object, \ + ULONG HandleAttributes, \ + PACCESS_STATE PassedAccessState, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_TYPE ObjectType, \ + KPROCESSOR_MODE AccessMode, \ + PHANDLE Handle + +typedef struct _KPH_PROCESS_ENTRY +{ + LIST_ENTRY ListEntry; + PEPROCESS Process; + PEPROCESS CreatorProcess; + HANDLE Tag; + LOGICAL AllowKernelMode; + ACCESS_MASK ProcessAllowMask; + ACCESS_MASK ThreadAllowMask; +} KPH_PROCESS_ENTRY, *PKPH_PROCESS_ENTRY; + +NTSTATUS NTAPI KphNewObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS); +NTSTATUS NTAPI KphOldObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS); + +NTSTATUS NTAPI KphNewOpenProcedure51( + __in OB_OPEN_REASON OpenReason, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +NTSTATUS NTAPI KphNewOpenProcedure60( + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +NTSTATUS KphProtectInit(); +NTSTATUS KphProtectDeinit(); + +PKPH_PROCESS_ENTRY KphProtectAddEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __in LOGICAL AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ); + +PKPH_PROCESS_ENTRY KphProtectFindEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy + ); + +BOOLEAN KphProtectRemoveByProcess( + __in PEPROCESS Process + ); + +ULONG KphProtectRemoveByTag( + __in HANDLE Tag + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/ps.h b/2.x/trunk/KProcessHacker/include/ps.h new file mode 100644 index 000000000..62b7309f2 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/ps.h @@ -0,0 +1,151 @@ +/* + * Process Hacker Driver - + * processes and threads + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PS_H +#define _PS_H + +#include "types.h" +#include "ex.h" +#include "mm.h" +#include "ob.h" +#include "se.h" + +#define TAG_CAPTURE_STACK_BACKTRACE ('tShP') + +#define PROCESS_TERMINATE (0x0001) +#define PROCESS_CREATE_THREAD (0x0002) +#define PROCESS_SET_SESSIONID (0x0004) +#define PROCESS_VM_OPERATION (0x0008) +#define PROCESS_VM_READ (0x0010) +#define PROCESS_VM_WRITE (0x0020) +#define PROCESS_DUP_HANDLE (0x0040) +#define PROCESS_CREATE_PROCESS (0x0080) +#define PROCESS_SET_QUOTA (0x0100) +#define PROCESS_SET_INFORMATION (0x0200) +#define PROCESS_QUERY_INFORMATION (0x0400) +#define PROCESS_SUSPEND_RESUME (0x0800) +#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) +#ifndef PROCESS_ALL_ACCESS +#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff) +#endif + +#define THREAD_TERMINATE (0x0001) +#define THREAD_SUSPEND_RESUME (0x0002) +#define THREAD_ALERT (0x0004) +#define THREAD_GET_CONTEXT (0x0008) +#define THREAD_SET_CONTEXT (0x0010) +#define THREAD_SET_INFORMATION (0x0020) +#define THREAD_QUERY_INFORMATION (0x0040) +#define THREAD_SET_THREAD_TOKEN (0x0080) +#define THREAD_IMPERSONATE (0x0100) +#define THREAD_DIRECT_IMPERSONATION (0x0200) +#ifndef THREAD_ALL_ACCESS +#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff) +#endif + +#define JOB_OBJECT_ASSIGN_PROCESS (0x0001) +#define JOB_OBJECT_SET_ATTRIBUTES (0x0002) +#define JOB_OBJECT_QUERY (0x0004) +#define JOB_OBJECT_TERMINATE (0x0008) +#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010) +#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1f) + +extern POBJECT_TYPE *PsJobType; + +typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT +{ + BOOLEAN Local; + KAPC Apc; + KEVENT CompletedEvent; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + ULONG CapturedFrames; + ULONG BackTraceHash; +} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT; + +typedef struct _EXIT_THREAD_CONTEXT +{ + KAPC Apc; + KEVENT CompletedEvent; + NTSTATUS ExitStatus; +} EXIT_THREAD_CONTEXT, *PEXIT_THREAD_CONTEXT; + +/* FUNCTION DEFS */ + +NTSTATUS NTAPI PsGetContextThread( + __in PETHREAD Thread, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE PreviousMode + ); + +BOOLEAN NTAPI PsGetProcessExitProcessCalled( + __in PEPROCESS Process + ); + +PVOID NTAPI PsGetThreadWin32Thread( + __in PETHREAD Thread + ); + +NTSTATUS NTAPI PsLookupProcessThreadByCid( + __in PCLIENT_ID ClientId, + __out_opt PEPROCESS *Process, + __out PETHREAD *Thread + ); + +NTSTATUS NTAPI PsSetContextThread( + __in PETHREAD Thread, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE PreviousMode + ); + +/* FUNCTION TYPEDEFS */ + +typedef PVOID (NTAPI *_PsGetProcessJob)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsResumeProcess)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsSuspendProcess)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsTerminateProcess)( + PEPROCESS Process, + NTSTATUS ExitStatus + ); + +typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer51)( + PETHREAD Thread, + NTSTATUS ExitStatus + ); + +typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer52)( + PETHREAD Thread, + NTSTATUS ExitStatus, + BOOLEAN DirectTerminate + ); + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/ref.h b/2.x/trunk/KProcessHacker/include/ref.h new file mode 100644 index 000000000..03f9fdb9e --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/ref.h @@ -0,0 +1,113 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _REF_H +#define _REF_H + +#include "kph.h" + +/* Object flags */ +#define KPHOBJ_RAISE_ON_FAIL 0x00000001 +#define KPHOBJ_PAGED_POOL 0x00000002 +#define KPHOBJ_NONPAGED_POOL 0x00000004 +#define KPHOBJ_VALID_FLAGS 0x00000007 + +/* Object type flags */ +#define KPHOBJTYPE_PASSIVE_LEVEL_DELETE 0x00000001 +#define KPHOBJTYPE_VALID_FLAGS 0x00000001 + +/* Object type callbacks */ + +/* PKPH_TYPE_DELETE_PROCEDURE + * + * The delete procedure for an object type, called when + * an object of the type is being freed. + * + * Object: A pointer to the object being freed. + * Flags: The flags specified when the object was created. + * + * IRQL: = PASSIVE_LEVEL if the require passive level flag was + * specified for the object type, otherwise <= APC_LEVEL. + */ +typedef VOID (NTAPI *PKPH_TYPE_DELETE_PROCEDURE)( + __in PVOID Object, + __in ULONG Flags + ); + +struct _KPH_OBJECT_TYPE; +typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE; + +#ifndef _REF_PRIVATE +extern PKPH_OBJECT_TYPE KphObjectTypeObject; +#endif + +NTSTATUS KphRefInit(); + +NTSTATUS KphRefDeinit(); + +NTSTATUS KphCreateObject( + __out PVOID *Object, + __in SIZE_T ObjectSize, + __in ULONG Flags, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __in_opt LONG AdditionalReferences + ); + +NTSTATUS KphCreateObjectType( + __out PKPH_OBJECT_TYPE *ObjectType, + __in POOL_TYPE DefaultPoolType, + __in ULONG Flags, + __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ); + +BOOLEAN KphDereferenceObject( + __in PVOID Object + ); + +BOOLEAN KphDereferenceObjectDeferDelete( + __in PVOID Object + ); + +LONG KphDereferenceObjectEx( + __in PVOID Object, + __in LONG RefCount, + __in BOOLEAN DeferDelete + ); + +PKPH_OBJECT_TYPE KphGetObjectType( + __in PVOID Object + ); + +VOID KphReferenceObject( + __in PVOID Object + ); + +LONG KphReferenceObjectEx( + __in PVOID Object, + __in LONG RefCount + ); + +BOOLEAN KphReferenceObjectSafe( + __in PVOID Object + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/refp.h b/2.x/trunk/KProcessHacker/include/refp.h new file mode 100644 index 000000000..d31fa034e --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/refp.h @@ -0,0 +1,137 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _REFP_H +#define _REFP_H + +#define _REF_PRIVATE +#include "ref.h" +#include "sync.h" + +#define TAG_KPHOBJ ('bOhP') + +#define KphObjectToObjectHeader(Object) ((PKPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)(Object), KPH_OBJECT_HEADER, Body)) +#define KphObjectHeaderToObject(ObjectHeader) (&((PKPH_OBJECT_HEADER)(ObjectHeader))->Body) +#define KphpAddObjectHeaderSize(Size) ((Size) + FIELD_OFFSET(KPH_OBJECT_HEADER, Body)) + +typedef struct _KPH_OBJECT_HEADER *PKPH_OBJECT_HEADER; +typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE; + +typedef struct _KPH_OBJECT_HEADER +{ + /* The reference count of the object. */ + LONG RefCount; + /* The flags that were used to create the object. */ + ULONG Flags; + union + { + /* The size of the object, excluding the header. */ + SIZE_T Size; + /* A pointer to the object header of the next object to free. */ + PKPH_OBJECT_HEADER NextToFree; + }; + /* The type of the object. */ + PKPH_OBJECT_TYPE Type; + /* A linked list entry for an optional object manager object list. + * For example, this may be used to free all objects when the + * driver exits. + */ + LIST_ENTRY GlobalObjectListEntry; + + /* The body of the object. For use by the KphObject(Header)ToObject(Header) macros. */ + QUAD Body; +} KPH_OBJECT_HEADER, *PKPH_OBJECT_HEADER; + +typedef struct _KPH_OBJECT_TYPE +{ + /* The default pool type for objects of this type, used when the + * pool type is not specified when an object is created. */ + POOL_TYPE DefaultPoolType; + /* The flags that were used to create the object type. */ + ULONG Flags; + /* An optional procedure called when objects of this type are freed. */ + PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure; + + /* The total number of objects of this type that are alive. */ + ULONG NumberOfObjects; +} KPH_OBJECT_TYPE, *PKPH_OBJECT_TYPE; + +/* KphpInterlockedIncrementSafe + * + * Increments a reference count, but will never increment + * from 0 to 1. + */ +FORCEINLINE BOOLEAN KphpInterlockedIncrementSafe( + __inout PLONG RefCount + ) +{ + LONG refCount; + + /* Here we will attempt to increment the reference count, + * making sure that it is not 0. + */ + + while (TRUE) + { + refCount = *RefCount; + + /* Check if the reference count is 0. If it is, the + * object is being or about to be deleted. + */ + if (refCount == 0) + return FALSE; + + /* Try to increment the reference count. */ + if (InterlockedCompareExchange( + RefCount, + refCount + 1, + refCount + ) == refCount) + { + /* Success. */ + return TRUE; + } + + /* Someone else changed the reference count before we did. + * Go back and try again. + */ + } +} + +PKPH_OBJECT_HEADER KphpAllocateObject( + __in SIZE_T ObjectSize, + __in POOL_TYPE PoolType + ); + +VOID KphpDeferDeleteObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ); + +VOID KphpDeferDeleteObjectRoutine( + __in PVOID Parameter + ); + +VOID KphpFreeObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/se.h b/2.x/trunk/KProcessHacker/include/se.h new file mode 100644 index 000000000..947d59f5f --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/se.h @@ -0,0 +1,55 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SE_H +#define _SE_H + +#include "types.h" + +extern POBJECT_TYPE *SeTokenObjectType; + +/* Was 0x38 on Vista, appears to be 0xc8 on 7. */ +#define AUX_ACCESS_DATA_SIZE (0xc8) + +typedef PVOID PAUX_ACCESS_DATA; + +/* FUNCTION DEFS */ + +NTKERNELAPI NTSTATUS NTAPI SeCreateAccessState( + PACCESS_STATE AccessState, + PAUX_ACCESS_DATA AuxData, + ACCESS_MASK DesiredAccess, + PGENERIC_MAPPING Mapping + ); + +NTKERNELAPI VOID NTAPI SeDeleteAccessState( + PACCESS_STATE AccessState + ); + +/* STRUCTS */ + +typedef struct _SE_AUDIT_PROCESS_CREATION_INFO +{ + POBJECT_NAME_INFORMATION ImageFileName; +} SE_AUDIT_PROCESS_CREATION_INFO, *PSE_AUDIT_PROCESS_CREATION_INFO; + +#endif diff --git a/2.x/trunk/KProcessHacker/include/sync.h b/2.x/trunk/KProcessHacker/include/sync.h new file mode 100644 index 000000000..e344ea75e --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/sync.h @@ -0,0 +1,320 @@ +/* + * Process Hacker Driver - + * synchronization code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYNC_H +#define _SYNC_H + +#include "kph.h" +#include "ex.h" + +/* General synchronization macros */ + +/* KphEqualSpin + * + * Spins until the first value is equal to the second + * value. + */ +FORCEINLINE VOID KphSpinUntilEqual( + __inout PLONG Value, + __in LONG Value2 + ) +{ + while (InterlockedCompareExchange( + Value, + Value2, + Value2 + ) != Value2) + YieldProcessor(); +} + +/* KphNotEqualSpin + * + * Spins until the first value is not equal to the second + * value. + */ +FORCEINLINE VOID KphSpinUntilNotEqual( + __inout PLONG Value, + __in LONG Value2 + ) +{ + while (InterlockedCompareExchange( + Value, + Value2, + Value2 + ) == Value2) + YieldProcessor(); +} + +/* Spin Locks */ + +/* KphAcquireBitSpinLock + * + * Uses the specified bit as a spinlock and acquires the + * lock in the given value. + */ +FORCEINLINE VOID KphAcquireBitSpinLock( + __inout PLONG Value, + __in LONG Bit + ) +{ + while (InterlockedBitTestAndSet(Value, Bit)) + YieldProcessor(); +} + +/* KphReleaseBitSpinLock + * + * Uses the specified bit as a spinlock and releases the + * lock in the given value. + */ +FORCEINLINE VOID KphReleaseBitSpinLock( + __inout PLONG Value, + __in LONG Bit + ) +{ + InterlockedBitTestAndReset(Value, Bit); +} + +/* Guarded Locks */ +/* Guarded locks are small spinlocks. Code within + * synchronized regions run at APC_LEVEL. They also contain + * a signal which can used to implement rundown routines. + */ + +#define KPH_GUARDED_LOCK_ACTIVE 0x80000000 +#define KPH_GUARDED_LOCK_ACTIVE_SHIFT 31 +#define KPH_GUARDED_LOCK_SIGNALED 0x40000000 +#define KPH_GUARDED_LOCK_SIGNALED_SHIFT 30 +#define KPH_GUARDED_LOCK_FLAGS 0xc0000000 + +typedef struct _KPH_GUARDED_LOCK +{ + LONG Value; +} KPH_GUARDED_LOCK, *PKPH_GUARDED_LOCK; + +#define KphAcquireGuardedLock KphfAcquireGuardedLock +VOID FASTCALL KphfAcquireGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ); + +#define KphReleaseGuardedLock KphfReleaseGuardedLock +VOID FASTCALL KphfReleaseGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ); + +/* KphInitializeGuardedLock + * + * Initializes a guarded lock. + * + * IRQL: Any + */ +FORCEINLINE VOID KphInitializeGuardedLock( + __out PKPH_GUARDED_LOCK Lock, + __in BOOLEAN Signaled + ) +{ + Lock->Value = 0; + + if (Signaled) + Lock->Value |= KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphClearGuardedLock + * + * Clears the signal state of a guarded lock, assuming + * that the current thread has acquired it. + * + * IRQL: Any + */ +FORCEINLINE VOID KphClearGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + Lock->Value &= ~KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphSignalGuardedLock + * + * Signals a guarded lock. + * + * IRQL: Any + */ +FORCEINLINE VOID KphSignalGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + Lock->Value |= KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphSignaledGuardedLock + * + * Determines whether a guarded lock is signaled. + * + * IRQL: Any + */ +FORCEINLINE BOOLEAN KphSignaledGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + return !!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED); +} + +/* KphAcquireAndClearGuardedLock + * + * Acquires a guarded lock, clear its signal, and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +FORCEINLINE VOID KphAcquireAndClearGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + KphClearGuardedLock(Lock); +} + +/* KphAcquireAndSignalGuardedLock + * + * Acquires a guarded lock, signals it, and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +FORCEINLINE VOID KphAcquireAndSignalGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + KphSignalGuardedLock(Lock); +} + +/* KphAcquireNonSignaledGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL, + * making sure the lock is not signaled. If it is, the + * lock is not acquired. + * + * Return value: whether the lock was acquired. + * IRQL: <= APC_LEVEL + */ +FORCEINLINE BOOLEAN KphAcquireNonSignaledGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + + if (Lock->Value & KPH_GUARDED_LOCK_SIGNALED) + { + KphReleaseGuardedLock(Lock); + return FALSE; + } + + return TRUE; +} + +/* KphAcquireSignaledGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL, + * making sure the lock is signaled. If it is not, the + * lock is not acquired. + * + * Return value: whether the lock was acquired. + * IRQL: <= APC_LEVEL + */ +FORCEINLINE BOOLEAN KphAcquireSignaledGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + + if (!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED)) + { + KphReleaseGuardedLock(Lock); + return FALSE; + } + + return TRUE; +} + +/* KphReleaseAndClearGuardedLock + * + * Releases a guarded lock, clears its signal, and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +FORCEINLINE VOID KphReleaseAndClearGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphClearGuardedLock(Lock); + KphReleaseGuardedLock(Lock); +} + +/* KphReleaseAndSignalGuardedLock + * + * Releases a guarded lock, signals it, and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +FORCEINLINE VOID KphReleaseAndSignalGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphSignalGuardedLock(Lock); + KphReleaseGuardedLock(Lock); +} + +/* Processor Locks */ +/* Processor locks prevent code from executing on all other + * processors. Code within synchronized regions run at + * DISPATCH_LEVEL. + */ + +#define TAG_SYNC_DPC ('DShP') + +typedef struct _KPH_PROCESSOR_LOCK +{ + /* Synchronizes access to the processor lock. */ + KPH_GUARDED_LOCK Lock; + /* Storage allocated for DPCs. */ + PKDPC Dpcs; + /* The number of currently acquired processors. */ + LONG AcquiredProcessors; + /* The signal for acquired processors to be released. */ + LONG ReleaseSignal; + /* The old IRQL. */ + KIRQL OldIrql; + /* Whether the processor lock has been acquired. */ + BOOLEAN Acquired; +} KPH_PROCESSOR_LOCK, *PKPH_PROCESSOR_LOCK; + +BOOLEAN KphAcquireProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ); + +VOID KphInitializeProcessorLock( + __out PKPH_PROCESSOR_LOCK ProcessorLock + ); + +VOID KphReleaseProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/sysservice.h b/2.x/trunk/KProcessHacker/include/sysservice.h new file mode 100644 index 000000000..787847c33 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/sysservice.h @@ -0,0 +1,279 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICE_H +#define _SYSSERVICE_H + +#include "kph.h" +#include "sysservicedata.h" + +/* Define opaque object types */ + +struct _KPHSS_CLIENT_ENTRY; +typedef struct _KPHSS_CLIENT_ENTRY *PKPHSS_CLIENT_ENTRY; +struct _KPHSS_RULESET_ENTRY; +typedef struct _KPHSS_RULESET_ENTRY *PKPHSS_RULESET_ENTRY; +struct _KPHSS_RULE_ENTRY; +typedef struct _KPHSS_RULE_ENTRY *PKPHSS_RULE_ENTRY; + +/* Information types */ + +typedef struct _KPHSS_CLIENT_INFORMATION +{ + HANDLE ProcessId; + PVOID BufferBase; + ULONG BufferSize; + + ULONG NumberOfBlocksWritten; + ULONG NumberOfBlocksDropped; +} KPHSS_CLIENT_INFORMATION, *PKPHSS_CLIENT_INFORMATION; + +/* Object types */ + +#ifndef _SYSSERVICE_PRIVATE +extern PKPH_OBJECT_TYPE KphSsClientEntryType; +extern PKPH_OBJECT_TYPE KphSsRuleSetEntryType; +extern PKPH_OBJECT_TYPE KphSsRuleEntryType; +#endif + +/* Ruleset types */ + +typedef enum _KPHSS_RULESET_ACTION +{ + LogRuleSetAction, + MaxRuleSetAction +} KPHSS_RULESET_ACTION; + +/* Rule types */ + +typedef enum _KPHSS_FILTER_TYPE +{ + IncludeFilterType, + ExcludeFilterType, + MaxFilterType +} KPHSS_FILTER_TYPE; + +typedef enum _KPHSS_RULE_TYPE +{ + ProcessIdRuleType = 0, + ThreadIdRuleType, + PreviousModeRuleType, + NumberRuleType, + MaxRuleType +} KPHSS_RULE_TYPE; + +/* Block types */ + +#define KPHSS_BLOCK_SUCCESS(Status) (NT_SUCCESS(Status) && (Status) != STATUS_TIMEOUT) + +typedef enum _KPHSS_BLOCK_TYPE +{ + ResetBlockType, + EventBlockType, + ArgumentBlockType, + ProcessBlockType, + ModuleBlockType +} KPHSS_BLOCK_TYPE; + +typedef struct _KPHSS_BLOCK_HEADER +{ + USHORT Size; /* a.k.a. NextEntryOffset */ + USHORT Type; +} KPHSS_BLOCK_HEADER, *PKPHSS_BLOCK_HEADER; + +typedef struct _KPHSS_RESET_BLOCK +{ + KPHSS_BLOCK_HEADER Header; +} KPHSS_RESET_BLOCK, *PKPHSS_RESET_BLOCK; + +#define TAG_EVENT_BLOCK ('BEhP') + +#define KPHSS_EVENT_PROBE_ARGUMENTS_FAILED 0x00000001 +#define KPHSS_EVENT_COPY_ARGUMENTS_FAILED 0x00000002 +#define KPHSS_EVENT_KERNEL_MODE 0x00000004 +#define KPHSS_EVENT_USER_MODE 0x00000008 + +typedef struct _KPHSS_EVENT_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + USHORT Flags; + LARGE_INTEGER Time; + CLIENT_ID ClientId; + + /* The system service number. */ + ULONG Number; + /* The number of ULONG arguments to the system service. */ + USHORT NumberOfArguments; + USHORT ArgumentsOffset; /* ULONG[] */ + + /* The number of PVOIDs in the trace. */ + USHORT TraceCount; + USHORT TraceOffset; /* PVOID[] */ +} KPHSS_EVENT_BLOCK, *PKPHSS_EVENT_BLOCK; + +/* Argument Blocks + * + * These blocks provide additional information about + * arguments. + */ + +#define TAG_ARGUMENT_BLOCK ('BAhP') + +#define KPHSS_ARGUMENT_BLOCK_OVERHEAD \ + FIELD_OFFSET(KPHSS_ARGUMENT_BLOCK, Normal) +#define KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize) \ + (KPHSS_ARGUMENT_BLOCK_OVERHEAD + (InnerSize)) + +typedef struct _KPHSS_ARGUMENT_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + UCHAR Index; + UCHAR Type; /* KPHSS_ARGUMENT_TYPE */ + + union + { + ULONG Normal; + + LARGE_INTEGER Simple; + KPHSS_HANDLE Handle; + KPHSS_STRING String; + KPHSS_WSTRING WString; + KPHSS_ANSI_STRING AnsiString; + KPHSS_UNICODE_STRING UnicodeString; + KPHSS_OBJECT_ATTRIBUTES ObjectAttributes; + CLIENT_ID ClientId; + CONTEXT Context; + KPHSS_INITIAL_TEB InitialTeb; + GUID Guid; + KPHSS_BYTES Bytes; + }; +} KPHSS_ARGUMENT_BLOCK, *PKPHSS_ARGUMENT_BLOCK; + +/* Process Blocks + * + * These blocks notify the client of a new process. + */ + +#define TAG_PROCESS_BLOCK ('BPhP') + +typedef struct _KPHSS_PROCESS_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + + HANDLE ProcessId; + USHORT NameOffset; /* KPHSS_WSTRING */ + USHORT ImageFileNameOffset; /* KPHSS_WSTRING */ +} KPHSS_PROCESS_BLOCK, *PKPHSS_PROCESS_BLOCK; + +/* Module Blocks + * + * These blocks provide information about modules + * loaded by a process. + */ + +#define TAG_MODULE_BLOCK ('BMhP') + +typedef struct _KPHSS_MODULE_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + + HANDLE ProcessId; + PVOID ModuleBase; + ULONG ModuleSize; + USHORT FileNameOffset; /* KPHSS_WSTRING */ +} KPHSS_MODULE_BLOCK, *PKPHSS_MODULE_BLOCK; + +/* Functions */ + +NTSTATUS KphSsLogInit(); +NTSTATUS KphSsLogDeinit(); +NTSTATUS KphSsLogStart(); +NTSTATUS KphSsLogStop(); + +NTSTATUS KphSsCreateClientEntry( + __out PKPHSS_CLIENT_ENTRY *ClientEntry, + __in HANDLE ProcessHandle, + __in HANDLE ReadSemaphoreHandle, + __in HANDLE WriteSemaphoreHandle, + __in PVOID BufferBase, + __in ULONG BufferSize, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSsEnableClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in BOOLEAN Enable + ); + +NTSTATUS KphSsQueryClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation, + __in ULONG ClientInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action + ); + +HANDLE KphSsGetHandleRule( + __in PKPHSS_RULE_ENTRY RuleEntry + ); + +NTSTATUS KphSsRemoveRule( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in HANDLE RuleEntryHandle + ); + +NTSTATUS KphSsAddProcessIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ProcessId + ); + +NTSTATUS KphSsAddThreadIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ThreadId + ); + +NTSTATUS KphSsAddPreviousModeRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphSsAddNumberRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in ULONG Number + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/sysservicedata.h b/2.x/trunk/KProcessHacker/include/sysservicedata.h new file mode 100644 index 000000000..a2553c33c --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/sysservicedata.h @@ -0,0 +1,174 @@ +/* + * Process Hacker Driver - + * system service logging (data) + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICEDATA_H +#define _SYSSERVICEDATA_H + +#include "kph.h" + +#define TAG_CALL_ENTRY ('cShP') + +typedef enum _KPHSS_ARGUMENT_TYPE +{ + /* Having argument info for out variables is very rare + * because usually the caller does not fill in anything + * in the variable. In some cases, however, the caller + * does specify a length (usually Length, or MaximumLength). + * + * Note that with the exception of a few types such as + * HANDLE, all types listed here are POINTER TYPES + * (although a handle is the size of a pointer). This + * is because non-pointer arguments are already recorded + * in the event block. + */ + + /* Anything passed by value */ + NormalArgument = 0, + + /* PBOOLEAN */ + Int8Argument, + /* P(U)SHORT */ + Int16Argument, + /* P(U)LONG */ + Int32Argument, + /* P(U)LARGE_INTEGER */ + Int64Argument, + /* HANDLE */ + /* Only object manager handles, no fake handles. */ + HandleArgument, + /* PSTR */ + StringArgument, + /* PWSTR */ + WStringArgument, + /* PANSI_STRING */ + AnsiStringArgument, + /* PUNICODE_STRING */ + UnicodeStringArgument, + /* POBJECT_ATTRIBUTES */ + ObjectAttributesArgument, + /* PCLIENT_ID */ + ClientIdArgument, + /* PCONTEXT */ + ContextArgument, + /* PINITIAL_TEB */ + InitialTebArgument, + /* PGUID */ + GuidArgument, + /* PVOID */ + BytesArgument +} KPHSS_ARGUMENT_TYPE; + +typedef struct _KPHSS_HANDLE +{ + CLIENT_ID ClientId; + USHORT TypeNameOffset; /* KPHSS_WSTRING */ + USHORT NameOffset; /* KPHSS_WSTRING */ +} KPHSS_HANDLE, *PKPHSS_HANDLE; + +typedef struct _KPHSS_STRING +{ + USHORT Length; + CHAR Buffer[1]; +} KPHSS_STRING, *PKPHSS_STRING; + +typedef struct _KPHSS_WSTRING +{ + USHORT Length; + WCHAR Buffer[1]; +} KPHSS_WSTRING, *PKPHSS_WSTRING; + +typedef struct _KPHSS_ANSI_STRING +{ + USHORT Length; + USHORT MaximumLength; + PSTR Pointer; + CHAR Buffer[1]; +} KPHSS_ANSI_STRING, *PKPHSS_ANSI_STRING; + +typedef struct _KPHSS_UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Pointer; + WCHAR Buffer[1]; +} KPHSS_UNICODE_STRING, *PKPHSS_UNICODE_STRING; + +typedef struct _KPHSS_OBJECT_ATTRIBUTES +{ + union + { + OBJECT_ATTRIBUTES ObjectAttributes; + struct + { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; + }; + }; + + USHORT RootDirectoryOffset; /* KPHSS_HANDLE */ + USHORT ObjectNameOffset; /* KPHSS_UNICODE_STRING */ +} KPHSS_OBJECT_ATTRIBUTES, *PKPHSS_OBJECT_ATTRIBUTES; + +typedef struct _KPHSS_INITIAL_TEB +{ + struct + { + PVOID OldStackBase; + PVOID OldStackLimit; + } OldInitialTeb; + PVOID StackBase; + PVOID StackLimit; + PVOID StackAllocationBase; +} KPHSS_INITIAL_TEB, *PKPHSS_INITIAL_TEB; + +typedef struct _KPHSS_BYTES +{ + USHORT Length; + CHAR Buffer[1]; +} KPHSS_BYTES, *PKPHSS_BYTES; + +#ifndef _SYSSERVICEDATA_PRIVATE +extern RTL_GENERIC_TABLE KphSsCallTable; +#endif + +#define KPHSS_MAXIMUM_ARGUMENT_BLOCKS 20 + +typedef struct _KPHSS_CALL_ENTRY +{ + PULONG Number; + PSTR Name; + ULONG NumberOfArguments; + KPHSS_ARGUMENT_TYPE Arguments[KPHSS_MAXIMUM_ARGUMENT_BLOCKS]; +} KPHSS_CALL_ENTRY, *PKPHSS_CALL_ENTRY; + +VOID KphSsDataInit(); +VOID KphSsDataDeinit(); + +PKPHSS_CALL_ENTRY KphSsLookupCallEntry( + __in ULONG Number + ); + +#endif \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/include/sysservicep.h b/2.x/trunk/KProcessHacker/include/sysservicep.h new file mode 100644 index 000000000..5b7e0c133 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/sysservicep.h @@ -0,0 +1,468 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICEP_H +#define _SYSSERVICEP_H + +#define _SYSSERVICE_PRIVATE +#include "sysservice.h" +#include "ex.h" +#include "ref.h" + +/* PKPHPSS_KIFASTCALLENTRYPROC + * + * Represents a function called by KphpSsNewKiFastCallEntry. + */ +typedef VOID (NTAPI *PKPHPSS_KIFASTCALLENTRYPROC)( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ); + +/* Client entries + * + * Client entries describe a process and a circular buffer which + * receives logging events. + */ + +typedef struct _KPHSS_CLIENT_ENTRY +{ + PEPROCESS Process; + BOOLEAN Enabled; + + /* Buffer */ + PKSEMAPHORE ReadSemaphore; + PKSEMAPHORE WriteSemaphore; + FAST_MUTEX BufferMutex; + PVOID BufferBase; + ULONG BufferSize; + ULONG BufferCursor; + + /* Statistics */ + ULONG NumberOfBlocksWritten; /* excludes reset blocks */ + ULONG NumberOfBlocksDropped; +} KPHSS_CLIENT_ENTRY, *PKPHSS_CLIENT_ENTRY; + +/* Rulesets + * + * Rulesets contain a list of rules and an action to take if a + * system service matches the set of rules. + */ + +#define KPHSS_RULESET_ENTRY(ListEntry) \ + CONTAINING_RECORD((ListEntry), KPHSS_RULESET_ENTRY, RuleSetListEntry) +#define KPHSS_RULESET_ENTRY_LIMIT 10 +#define KPHSS_RULE_HANDLE_INCREMENT 4 + +typedef struct _KPHSS_RULESET_ENTRY +{ + LIST_ENTRY RuleSetListEntry; + /* The client is referenced. */ + PKPHSS_CLIENT_ENTRY Client; + + KPHSS_RULESET_ACTION Action; + KPHSS_FILTER_TYPE DefaultFilterType; + + ULONG NextRuleHandle; + EX_PUSH_LOCK RuleListPushLock; + /* A list of rules. Each rule is referenced when stored. */ + LIST_ENTRY RuleListHead; +} KPHSS_RULESET_ENTRY, *PKPHSS_RULESET_ENTRY; + +/* Rules */ + +#define KPHSS_RULE_ENTRY(ListEntry) \ + CONTAINING_RECORD((ListEntry), KPHSS_RULE_ENTRY, RuleListEntry) + +typedef struct _KPHSS_RULE_ENTRY +{ + BOOLEAN Initialized; + HANDLE Handle; + LIST_ENTRY RuleListEntry; + + KPHSS_FILTER_TYPE FilterType; + KPHSS_RULE_TYPE RuleType; + + union + { + struct + { + HANDLE ProcessId; + } ProcessIdRule; + struct + { + HANDLE ThreadId; + } ThreadIdRule; + struct + { + KPROCESSOR_MODE PreviousMode; + } PreviousModeRule; + struct + { + ULONG Number; + } NumberRule; + }; +} KPHSS_RULE_ENTRY, *PKPHSS_RULE_ENTRY; + +typedef enum _KPHSS_SEQUENCE_MODE +{ + NoSequence, + StartSequence, + InSequence, + EndSequence +} KPHSS_SEQUENCE_MODE; + +#define TAG_CAPTURE_TEMP_BUFFER ('tChP') +#define CAPTURE_HANDLE_BUFFER_SIZE 0x400 +#define CAPTURE_UNICODE_STRING_MAX_SIZE 0x400 +#define CAPTURE_BYTES_MAX_SIZE 0x400 + +/* Functions */ + +VOID NTAPI KphpSsClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +NTSTATUS KphpSsAddRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPHSS_RULE_TYPE RuleType + ); + +NTSTATUS KphpSsCreateEventBlock( + __out PKPHSS_EVENT_BLOCK *EventBlock, + __in PKTHREAD Thread, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments + ); + +VOID KphpSsFreeEventBlock( + __in PKPHSS_EVENT_BLOCK EventBlock + ); + +NTSTATUS KphpSsCaptureSimpleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in KPHSS_ARGUMENT_TYPE Type, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureHandleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in HANDLE Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureUnicodeStringArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PUNICODE_STRING Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureObjectAttributesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in POBJECT_ATTRIBUTES Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureClientIdArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PCLIENT_ID Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureBytesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in ULONG Length, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index, + __in_opt KPHSS_ARGUMENT_TYPE Type, + __in_opt PVOID Context + ); + +PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock( + __in ULONG InnerSize, + __in KPHSS_ARGUMENT_TYPE Type + ); + +VOID KphpSsFreeArgumentBlock( + __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock + ); + +NTSTATUS KphpSsWriteBlock( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode + ); + +VOID NTAPI KphpSsLogSystemServiceCall( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ); + +VOID NTAPI KphpSsNewKiFastCallEntry(); + +/* KphpSsMatchRuleSetEntry + * + * Determines if a ruleset is relevant to an event. + * + * Note: This function is inlined for performance reasons. + */ +BOOLEAN FORCEINLINE KphpSsMatchRuleSetEntry( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread, + __in KPROCESSOR_MODE PreviousMode + ) +{ + PLIST_ENTRY currentListEntry; + ULONG i; + BOOLEAN ruleTypeUsedArray[MaxRuleType]; + BOOLEAN ruleTypeIncludeArray[MaxRuleType]; + BOOLEAN ruleTypeExcludeArray[MaxRuleType]; + BOOLEAN ruleTypeFailedArray[MaxRuleType]; + BOOLEAN isRuleSetMatch; + + /* Due to the lack of proper boolean expression support, + * we are going to have these rules: + * + * * Each rule type has four arrays. The standard + * filtering rules apply to each rule type, + * except that on an include we increment the value + * in the include array and on an exclude we + * increment the value in the exclude array. On a + * failed include we increment the value in the + * failed array. + * * When we're done matching the rules, we'll look + * at the default filter type. If it's Include, + * we assume the ruleset matches. If it's Exclude, + * we assume the ruleset fails. + * * We will go through each rule type and look at + * the two arrays. See the code for further + * information. + */ + + /* Initialize the arrays. */ + for (i = 0; i < MaxRuleType; i++) + { + ruleTypeUsedArray[i] = FALSE; + ruleTypeIncludeArray[i] = FALSE; + ruleTypeExcludeArray[i] = FALSE; + ruleTypeFailedArray[i] = FALSE; + } + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&RuleSetEntry->RuleListPushLock); + + currentListEntry = RuleSetEntry->RuleListHead.Flink; + + while (currentListEntry != &RuleSetEntry->RuleListHead) + { + PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry); + BOOLEAN isRuleMatch = FALSE; + + /* Check if the rule is initialized, and if + * the rule type has already been failed - + * Exclude filter types take precedence. + */ + if ( + !ruleEntry->Initialized || + ruleTypeExcludeArray[ruleEntry->RuleType] + ) + { + currentListEntry = currentListEntry->Flink; + continue; + } + + /* Attempt to match the rule. All rule types are + * considered in this one function. + */ + switch (ruleEntry->RuleType) + { + case ProcessIdRuleType: + if (PsGetProcessId(IoThreadToProcess(Thread)) == + ruleEntry->ProcessIdRule.ProcessId) + isRuleMatch = TRUE; + break; + case ThreadIdRuleType: + if (PsGetThreadId(Thread) == ruleEntry->ThreadIdRule.ThreadId) + isRuleMatch = TRUE; + break; + case PreviousModeRuleType: + if (PreviousMode == ruleEntry->PreviousModeRule.PreviousMode) + isRuleMatch = TRUE; + break; + case NumberRuleType: + if (Number == ruleEntry->NumberRule.Number) + isRuleMatch = TRUE; + break; + } + + /* Now that we have attempted to match the rule, we + * must look at the rule filter type to determine + * what to do. + */ + if (isRuleMatch) + { + if (ruleEntry->FilterType == IncludeFilterType) + { + ruleTypeIncludeArray[ruleEntry->RuleType] = TRUE; + } + else if (ruleEntry->FilterType == ExcludeFilterType) + { + ruleTypeExcludeArray[ruleEntry->RuleType] = TRUE; + } + } + else + { + if (ruleEntry->FilterType == IncludeFilterType) + { + ruleTypeFailedArray[ruleEntry->RuleType] = TRUE; + } + } + + /* Declare that we have used the rule type. */ + ruleTypeUsedArray[ruleEntry->RuleType] = TRUE; + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + /* Look at the default filter type. If it's Include, + * we assume the ruleset matches. Otherwise, we + * assume it fails. + */ + if (RuleSetEntry->DefaultFilterType == IncludeFilterType) + { + isRuleSetMatch = TRUE; + } + else if (RuleSetEntry->DefaultFilterType == ExcludeFilterType) + { + isRuleSetMatch = FALSE; + } + + /* Go through the rule type match/failed arrays. */ + + for (i = 0; i < MaxRuleType; i++) + { + /* Make sure this rule type has been used. */ + if (!ruleTypeUsedArray[i]) + continue; + + /* The ordering of these if statements are + * extremely important. The order of precedence + * is: exclude, include, failed include. Failed include + * doesn't apply if we're using the Include default + * filter type, though. + */ + if (ruleTypeExcludeArray[i]) + { + isRuleSetMatch = FALSE; + break; + } + else if (ruleTypeIncludeArray[i]) + { + isRuleSetMatch = TRUE; + } + else if ( + ruleTypeFailedArray[i] && + RuleSetEntry->DefaultFilterType != IncludeFilterType + ) + { + isRuleSetMatch = FALSE; + break; + } + } + + return isRuleSetMatch; +} + +/* KphpSsProcessSpecificArguments + * + * Creates argument blocks for specific system calls. + * + * Note: This function is inlined for performance reasons. + */ +VOID FORCEINLINE KphpSsProcessSpecificArguments( + __in PKPHSS_ARGUMENT_BLOCK *ArgumentBlocks, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status; + + /* For safety reasons, system call no. 0 is not supported. */ + if (Number == 0) + return; + + /* Wrap in SEH because we will be accessing the arguments. */ + + __try + { + if (Number == SsNtDeviceIoControlFile) + { + /* Create an argument block for the input buffer. */ + if (!NT_SUCCESS(KphpSsCreateArgumentBlock( + &ArgumentBlocks[6], + Number, + Arguments[6], + 6, + BytesArgument, + (PVOID)Arguments[7] + ))) + ArgumentBlocks[6] = NULL; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // Nothing + } +} + +#endif diff --git a/2.x/trunk/KProcessHacker/include/test.h b/2.x/trunk/KProcessHacker/include/test.h new file mode 100644 index 000000000..49dc96095 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/test.h @@ -0,0 +1,30 @@ +/* + * Process Hacker Driver - + * testing code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _TEST_H +#define _TEST_H + +#include "kph.h" + +VOID KphTestPushLock(); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/trace.h b/2.x/trunk/KProcessHacker/include/trace.h new file mode 100644 index 000000000..6b707ede5 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/trace.h @@ -0,0 +1,188 @@ +/* + * Process Hacker Driver - + * stack tracing + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _TRACE_H +#define _TRACE_H + +#include "types.h" + +/* Stack Tracing */ + +/* Sensible limit that may or may not correspond to the actual Windows value. */ +#define MAX_STACK_DEPTH 64 + +#define RTL_WALK_USER_MODE_STACK 0x00000001 +#define RTL_WALK_VALID_FLAGS 0x00000001 + +/* RtlWalkFrameChain + * + * Walks an EBP chain and fills out an array of addresses. + * + * Return value: the number of frames found. + */ +NTSYSAPI ULONG NTAPI RtlWalkFrameChain( + __out PVOID *Callers, + __in ULONG Count, + __in ULONG Flags + ); + +/* Trace Database */ + +#define RTL_TRACE_IN_USER_MODE 0x00000001 +#define RTL_TRACE_IN_KERNEL_MODE 0x00000002 +#define RTL_TRACE_USE_NONPAGED_POOL 0x00000004 +#define RTL_TRACE_USE_PAGED_POOL 0x00000008 + +typedef struct _RTL_TRACE_BLOCK +{ + ULONG Magic; + ULONG Count; /* Reference count */ + ULONG Size; /* Size, in PVOIDs, of the trace */ + + SIZE_T UserCount; + SIZE_T UserSize; + PVOID UserContext; + + struct _RTL_TRACE_BLOCK *Next; + PVOID *Trace; +} RTL_TRACE_BLOCK, *PRTL_TRACE_BLOCK; + +typedef struct _RTL_TRACE_DATABASE *PRTL_TRACE_DATABASE; + +/* Enumeration context. */ +typedef struct _RTL_TRACE_ENUMERATE +{ + PRTL_TRACE_DATABASE Database; + ULONG Index; + PRTL_TRACE_BLOCK Block; +} RTL_TRACE_ENUMERATE, *PRTL_TRACE_ENUMERATE; + +typedef ULONG (*RTL_TRACE_HASH_FUNCTION)( + ULONG Count, + PVOID *Trace + ); + +PRTL_TRACE_DATABASE RtlTraceDatabaseCreate( + __in ULONG Buckets, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, /* optional in user-mode */ + __in ULONG Tag, /* optional in user-mode */ + __in_opt RTL_TRACE_HASH_FUNCTION HashFunction + ); + +BOOLEAN RtlTraceDatabaseDestroy( + __in PRTL_TRACE_DATABASE Database + ); + +BOOLEAN RtlTraceDatabaseValidate( + __in PRTL_TRACE_DATABASE Database + ); + +BOOLEAN RtlTraceDatabaseAdd( + __in PRTL_TRACE_DATABASE Database, + __in ULONG Count, + __in PVOID *Trace, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +/* RtlTraceDatabaseEnumerate + * + * Enumerates the trace blocks in the specified trace database. + * + * Database: The trace database to process. + * Enumerate: A context structure for the enumeration. Zero the + * structure if you are using it for the first time. + * TraceBlock: The trace block that was found by the function. + * + * Return value: TRUE if a trace block was found, FALSE if there + * are no more trace blocks. + */ +BOOLEAN RtlTraceDatabaseEnumerate( + __in PRTL_TRACE_DATABASE Database, + __inout PRTL_TRACE_ENUMERATE Enumerate, + __out PRTL_TRACE_BLOCK *TraceBlock + ); + +BOOLEAN RtlTraceDatabaseFind( + __in PRTL_TRACE_DATABASE Database, + __in ULONG Count, + __in PVOID *Trace, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +/* Note: locking/unlocking is only needed when trace blocks are modified. + * It is not needed for adding/enumerating/finding. */ +VOID RtlTraceDatabaseLock( + __in PRTL_TRACE_DATABASE Database + ); + +VOID RtlTraceDatabaseUnlock( + __in PRTL_TRACE_DATABASE Database + ); + +/* KPH trace interface */ + +typedef enum _KPH_CAPTURE_AND_ADD_STACK_TYPE +{ + KphCaptureAndAddKModeStack, + KphCaptureAndAddUModeStack, + KphCaptureAndAddBothStacks, + KphCaptureAndAddMaximum +} KPH_CAPTURE_AND_ADD_STACK_TYPE, *PKPH_CAPTURE_AND_ADD_STACK_TYPE; + +typedef struct _KPH_TRACE_DATABASE +{ + PRTL_TRACE_DATABASE Database; +} KPH_TRACE_DATABASE, *PKPH_TRACE_DATABASE; + +typedef struct _KPH_TRACEDB_INFORMATION +{ + ULONG NextEntryOffset; + ULONG Count; + ULONG TraceSize; + PVOID Trace[1]; +} KPH_TRACEDB_INFORMATION, *PKPH_TRACEDB_INFORMATION; + +NTSTATUS KphTraceDatabaseInitialization(); + +BOOLEAN KphCaptureAndAddStack( + __in PKPH_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +ULONG KphCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __in_opt ULONG Flags, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG BackTraceHash + ); + +NTSTATUS KphCreateTraceDatabase( + __out PKPH_TRACE_DATABASE *Database, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, + __in ULONG Tag + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/include/types.h b/2.x/trunk/KProcessHacker/include/types.h new file mode 100644 index 000000000..e1ca291c1 --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/types.h @@ -0,0 +1,7 @@ +#ifndef _TYPES_H +#define _TYPES_H + +#include +#include "version.h" + +#endif diff --git a/2.x/trunk/KProcessHacker/include/util.h b/2.x/trunk/KProcessHacker/include/util.h new file mode 100644 index 000000000..b62b0d22e --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/util.h @@ -0,0 +1,133 @@ +/* + * Process Hacker Driver - + * utility functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include "kph.h" + +/* Streams + * + * Streams are small buffer management structures. They + * automatically raise an exception if the buffer is overrun. + */ + +typedef struct _KPH_STREAM +{ + PVOID Buffer; + ULONG Length; + ULONG Position; +} KPH_STREAM, *PKPH_STREAM; + +typedef enum _KPH_STREAM_ORIGIN +{ + StartOrigin, + CurrentOrigin, + EndOrigin +} KPH_STREAM_ORIGIN; + +VOID KphInitializeStream( + __out PKPH_STREAM Stream, + __in PVOID Buffer, + __in ULONG Length + ); + +ULONG KphWriteDataStream( + __inout PKPH_STREAM Stream, + __in PVOID Data, + __in ULONG Length + ); + +/* KphCheckStreamPosition + * + * Checks a stream position and raises an exception if + * appropriate. + */ +FORCEINLINE VOID KphCheckStreamPosition( + __in PKPH_STREAM Stream, + __in ULONG Position + ) +{ + if (Position > Stream->Length) + ExRaiseStatus(STATUS_BUFFER_TOO_SMALL); +} + +/* KphPositionStream + * + * Gets the current position of the specified stream. + */ +FORCEINLINE ULONG KphPositionStream( + __in PKPH_STREAM Stream + ) +{ + return Stream->Position; +} + +/* KphWriteInt8Stream + * + * Writes a 1-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt8Stream( + __inout PKPH_STREAM Stream, + __in BOOLEAN Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(BOOLEAN)); +} + +/* KphWriteInt16Stream + * + * Writes a 2-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt16Stream( + __inout PKPH_STREAM Stream, + __in SHORT Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(SHORT)); +} + +/* KphWriteInt32Stream + * + * Writes a 4-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt32Stream( + __inout PKPH_STREAM Stream, + __in LONG Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(LONG)); +} + +/* KphWriteInt64Stream + * + * Writes a 8-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt64Stream( + __inout PKPH_STREAM Stream, + __in PLARGE_INTEGER Value + ) +{ + KphWriteDataStream(Stream, Value, sizeof(LARGE_INTEGER)); +} + +#endif diff --git a/2.x/trunk/KProcessHacker/include/version.h b/2.x/trunk/KProcessHacker/include/version.h new file mode 100644 index 000000000..00d6ba5ab --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/version.h @@ -0,0 +1,241 @@ +/* + * Process Hacker Driver - + * Windows version-specific data + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _VERSION_H +#define _VERSION_H + +#include "kph.h" + +#define WINDOWS_XP 51 +#define WINDOWS_SERVER_2003 52 +#define WINDOWS_VISTA 60 +#define WINDOWS_7 61 + +#define KVOFF(object, offset) ((PCHAR)(object) + offset) +#define SCAN_LENGTH 0x100000 +#define INIT_SCAN(scan, bytes, length, address, scanLength, displacement) \ + ( \ + ((scan).Initialized = TRUE), \ + ((scan).Bytes = (bytes)), \ + ((scan).Length = (length)), \ + ((scan).StartAddress = (address)), \ + ((scan).ScanLength = (scanLength)), \ + ((scan).Displacement = (displacement)), \ + bytes \ + ) + +typedef struct _KV_SCANPROC +{ + BOOLEAN Initialized; + PUCHAR Bytes; + ULONG Length; + ULONG_PTR StartAddress; + ULONG ScanLength; + LONG Displacement; +} KV_SCANPROC, *PKV_SCANPROC; + +NTSTATUS KvInit(); + +PVOID KvScanProc( + PKV_SCANPROC ScanProc + ); + +PVOID KvVerifyPrologue( + PVOID Address + ); + +#ifdef EXT +#undef EXT +#endif + +#ifdef _VERSION_PRIVATE +#define EXT +#define SCANNULL = { FALSE, NULL, 0, 0, 0, 0 } +#else +#define EXT extern +#define SCANNULL +#endif + +EXT ULONG WindowsVersion; +EXT RTL_OSVERSIONINFOEXW RtlWindowsVersion; +EXT ACCESS_MASK ProcessAllAccess; +EXT ACCESS_MASK ThreadAllAccess; + +/* Offsets */ +/* Structures + * Et: ETHREAD + * Ep: EPROCESS + * Ot: OBJECT_TYPE + * Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE + */ +EXT ULONG OffEtClientId; +EXT ULONG OffEtSpareByteForSs; +EXT ULONG OffEtStartAddress; +EXT ULONG OffEtWin32StartAddress; +EXT ULONG OffEpJob; +EXT ULONG OffEpObjectTable; +EXT ULONG OffEpProtectedProcessOff; +EXT ULONG OffEpProtectedProcessBit; +EXT ULONG OffEpRundownProtect; +EXT ULONG OffOhBody; +EXT ULONG OffOtName; +EXT ULONG OffOtiGenericMapping; +EXT ULONG OffOtiOpenProcedure; + +/* Functions + */ +EXT KV_SCANPROC KiFastCallEntryScan SCANNULL; +EXT KV_SCANPROC PsExitSpecialApcScan SCANNULL; +EXT KV_SCANPROC PsTerminateProcessScan SCANNULL; +EXT KV_SCANPROC PspTerminateThreadByPointerScan SCANNULL; + +/* System Call Numbers + */ +EXT ULONG SsNtAddAtom; +EXT ULONG SsNtAlertResumeThread; +EXT ULONG SsNtAlertThread; +EXT ULONG SsNtAllocateLocallyUniqueId; +EXT ULONG SsNtAllocateUserPhysicalPages; +EXT ULONG SsNtAllocateUuids; +EXT ULONG SsNtAllocateVirtualMemory; +EXT ULONG SsNtApphelpCacheControl; +EXT ULONG SsNtAreMappedFilesTheSame; +EXT ULONG SsNtAssignProcessToJobObject; +EXT ULONG SsNtCallbackReturn; +EXT ULONG SsNtCancelDeviceWakeupRequest; +EXT ULONG SsNtCancelIoFile; +EXT ULONG SsNtCancelTimer; +EXT ULONG SsNtClearEvent; +EXT ULONG SsNtClose; +EXT ULONG SsNtContinue; +EXT ULONG SsNtCreateDebugObject; +EXT ULONG SsNtCreateDirectoryObject; +EXT ULONG SsNtCreateEvent; +EXT ULONG SsNtCreateEventPair; +EXT ULONG SsNtCreateFile; +EXT ULONG SsNtCreateIoCompletion; +EXT ULONG SsNtCreateJobObject; +EXT ULONG SsNtCreateJobSet; +EXT ULONG SsNtCreateKey; +EXT ULONG SsNtCreateKeyedEvent; +EXT ULONG SsNtCreateMailslotFile; +EXT ULONG SsNtCreateMutant; +EXT ULONG SsNtCreateNamedPipeFile; +EXT ULONG SsNtCreatePagingFile; +EXT ULONG SsNtCreatePort; +EXT ULONG SsNtCreatePrivateNamespace; +EXT ULONG SsNtCreateProcess; +EXT ULONG SsNtCreateProcessEx; +EXT ULONG SsNtCreateProfile; +EXT ULONG SsNtCreateSection; +EXT ULONG SsNtCreateSemaphore; +EXT ULONG SsNtCreateSymbolicLinkObject; +EXT ULONG SsNtCreateThread; +EXT ULONG SsNtCreateTimer; +EXT ULONG SsNtCreateToken; +EXT ULONG SsNtCreateUserProcess; +EXT ULONG SsNtCreateWaitablePort; +EXT ULONG SsNtDebugActiveProcess; +EXT ULONG SsNtDebugContinue; +EXT ULONG SsNtDelayExecution; +EXT ULONG SsNtDeleteAtom; +EXT ULONG SsNtDeleteBootEntry; +EXT ULONG SsNtDeleteDriverEntry; +EXT ULONG SsNtDeleteFile; +EXT ULONG SsNtDeleteKey; +EXT ULONG SsNtDeleteObjectAuditAlarm; +EXT ULONG SsNtDeletePrivateNamespace; +EXT ULONG SsNtDeleteValueKey; +EXT ULONG SsNtDeviceIoControlFile; +EXT ULONG SsNtDisplayString; +EXT ULONG SsNtDuplicateObject; +EXT ULONG SsNtDuplicateToken; +EXT ULONG SsNtEnumerateBootEntries; +EXT ULONG SsNtEnumerateDriverEntries; +EXT ULONG SsNtEnumerateKey; +EXT ULONG SsNtEnumerateSystemEnvironmentValuesEx; +EXT ULONG SsNtEnumerateValueKey; +EXT ULONG SsNtExtendSection; +EXT ULONG SsNtFilterToken; +EXT ULONG SsNtFindAtom; +EXT ULONG SsNtFlushBuffersFile; +EXT ULONG SsNtFlushInstructionCache; +EXT ULONG SsNtFlushKey; +EXT ULONG SsNtFlushProcessWriteBuffers; +EXT ULONG SsNtFlushVirtualMemory; +EXT ULONG SsNtFlushWriteBuffer; +EXT ULONG SsNtFreeUserPhysicalPages; +EXT ULONG SsNtFreeVirtualMemory; +EXT ULONG SsNtFsControlFile; +EXT ULONG SsNtGetContextThread; +EXT ULONG SsNtGetCurrentProcessorNumber; +EXT ULONG SsNtGetDevicePowerState; +EXT ULONG SsNtGetNextProcess; +EXT ULONG SsNtGetNextThread; +EXT ULONG SsNtGetPlugPlayEvent; +EXT ULONG SsNtGetWriteWatch; +EXT ULONG SsNtImpersonateAnonymousToken; +EXT ULONG SsNtImpersonateClientOfPort; +EXT ULONG SsNtImpersonateThread; +EXT ULONG SsNtInitiatePowerAction; +EXT ULONG SsNtIsProcessInJob; +EXT ULONG SsNtIsSystemResumeAutomatic; +EXT ULONG SsNtListenPort; +EXT ULONG SsNtLoadDriver; +EXT ULONG SsNtLoadKey; +EXT ULONG SsNtLoadKey2; +EXT ULONG SsNtLockFile; +EXT ULONG SsNtLockVirtualMemory; +EXT ULONG SsNtMakePermanentObject; +EXT ULONG SsNtMakeTemporaryObject; +EXT ULONG SsNtMapUserPhysicalPages; +EXT ULONG SsNtMapUserPhysicalPagesScatter; +EXT ULONG SsNtMapViewOfSection; +EXT ULONG SsNtModifyBootEntry; +EXT ULONG SsNtModifyDriverEntry; +EXT ULONG SsNtNotifyChangeDirectoryFile; +EXT ULONG SsNtNotifyChangeKey; +EXT ULONG SsNtNotifyChangeMultipleKeys; +EXT ULONG SsNtOpenDirectoryObject; +EXT ULONG SsNtOpenEvent; +EXT ULONG SsNtOpenEventPair; +EXT ULONG SsNtOpenFile; +EXT ULONG SsNtOpenIoCompletion; +EXT ULONG SsNtOpenJobObject; +EXT ULONG SsNtOpenKey; +EXT ULONG SsNtOpenKeyedEvent; +EXT ULONG SsNtOpenMutant; +EXT ULONG SsNtOpenObjectAuditAlarm; +EXT ULONG SsNtOpenProcess; +EXT ULONG SsNtOpenProcessToken; +EXT ULONG SsNtOpenProcessTokenEx; +EXT ULONG SsNtOpenSection; +EXT ULONG SsNtOpenSemaphore; +EXT ULONG SsNtOpenSymbolicLinkObject; +EXT ULONG SsNtOpenThread; +EXT ULONG SsNtOpenThreadToken; +EXT ULONG SsNtOpenThreadTokenEx; +EXT ULONG SsNtOpenTimer; +EXT ULONG SsNtReadFile; +EXT ULONG SsNtWriteFile; + +#endif diff --git a/2.x/trunk/KProcessHacker/include/zw.h b/2.x/trunk/KProcessHacker/include/zw.h new file mode 100644 index 000000000..db943135f --- /dev/null +++ b/2.x/trunk/KProcessHacker/include/zw.h @@ -0,0 +1,68 @@ +/* + * Process Hacker Driver - + * system calls + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _ZW_H +#define _ZW_H + +#include "types.h" + +NTSTATUS NTAPI ZwOpenProcessToken( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE TokenHandle + ); + +NTSTATUS NTAPI ZwQueryInformationProcess( + __in HANDLE ProcessHandle, + __in PROCESSINFOCLASS ProcessInformationClass, + __out PVOID ProcessInformation, + __in ULONG ProcessInformationLength, + __out_opt PULONG ReturnLength + ); + +NTSTATUS NTAPI ZwQueryInformationThread( + __in HANDLE ThreadHandle, + __in PROCESSINFOCLASS ThreadInformationClass, + __out PVOID ThreadInformation, + __in ULONG ThreadInformationLength, + __out_opt PULONG ReturnLength + ); + +NTSTATUS NTAPI ZwSetInformationProcess( + __in HANDLE ProcessHandle, + __in PROCESSINFOCLASS ProcessInformationClass, + __in PVOID ProcessInformation, + __in ULONG ProcessInformationLength + ); + +/* NTSTATUS NTAPI ZwSetInformationThread( + __in HANDLE ThreadHandle, + __in THREADINFOCLASS ThreadInformationClass, + __in PVOID ThreadInformation, + __in ULONG ThreadInformationLength + ); */ + +typedef NTSTATUS (NTAPI *_NtClose)( + __in HANDLE Handle + ); + +#endif diff --git a/2.x/trunk/KProcessHacker/io.c b/2.x/trunk/KProcessHacker/io.c new file mode 100644 index 000000000..c8a55d68b --- /dev/null +++ b/2.x/trunk/KProcessHacker/io.c @@ -0,0 +1,265 @@ +/* + * Process Hacker Driver - + * I/O manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/io.h" + +VOID KphpCopyInfoUnicodeString( + __out PVOID Information, + __in PUNICODE_STRING UnicodeString + ); + +/* KphOpenDriver + * + * Opens a driver object. + */ +NTSTATUS KphOpenDriver( + __out PHANDLE DriverHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + DriverHandle, + 0, + ObjectAttributes, + *IoDriverObjectType, + AccessMode + ); +} + +/* KphQueryInformationDriver + * + * Queries information about a driver object. + */ +NTSTATUS KphQueryInformationDriver( + __in HANDLE DriverHandle, + __in DRIVER_INFORMATION_CLASS DriverInformationClass, + __out_bcount_opt(DriverInformationLength) PVOID DriverInformation, + __in ULONG DriverInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PDRIVER_OBJECT driverObject; + + if ( + DriverInformationClass < DriverBasicInformation || + DriverInformationClass >= MaxDriverInfoClass + ) + return STATUS_INVALID_INFO_CLASS; + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + if (DriverInformation) + ProbeForWrite(DriverInformation, DriverInformationLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + DriverHandle, + 0, + *IoDriverObjectType, + KernelMode, + &driverObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + __try + { + switch (DriverInformationClass) + { + /* DriverBasicInformation + * + * Basic information such as flags, driver base and driver size. + */ + case DriverBasicInformation: + { + if (DriverInformation) + { + /* Check buffer length. */ + if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION)) + { + PDRIVER_BASIC_INFORMATION basicInfo; + + basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation; + basicInfo->Flags = driverObject->Flags; + basicInfo->DriverStart = driverObject->DriverStart; + basicInfo->DriverSize = driverObject->DriverSize; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(DRIVER_BASIC_INFORMATION); + } + break; + + /* DriverNameInformation + * + * The name of the driver - e.g. \Driver\KProcessHacker. + */ + case DriverNameInformation: + { + if (DriverInformation) + { + /* Check buffer length. */ + if ( + sizeof(UNICODE_STRING) + + driverObject->DriverName.Length <= + DriverInformationLength + ) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + /* Pass the ReturnLength. */ + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length; + } + break; + + /* DriverServiceKeyNameInformation + * + * The name of the driver's service key - e.g. \REGISTRY\... + */ + case DriverServiceKeyNameInformation: + { + if (driverObject->DriverExtension) + { + if (DriverInformation) + { + if ( + sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length <= + DriverInformationLength + ) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverExtension->ServiceKeyName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length; + } + else + { + if (DriverInformation) + { + if (sizeof(UNICODE_STRING) <= DriverInformationLength) + { + /* Zero the information buffer. */ + KphpCopyInfoUnicodeString( + DriverInformation, + NULL + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING); + } + } + break; + + default: + { + status = STATUS_INVALID_INFO_CLASS; + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + ObDereferenceObject(driverObject); + + return status; +} + +/* KphpCopyInfoUnicodeString + * + * Copies a UNICODE_STRING to an information buffer. If + * the given string is NULL, the function zeros the + * destination UNICODE_STRING. + */ +VOID KphpCopyInfoUnicodeString( + __out PVOID Information, + __in PUNICODE_STRING UnicodeString + ) +{ + PUNICODE_STRING targetUnicodeString = (PUNICODE_STRING)Information; + + if (UnicodeString) + { + targetUnicodeString->Length = UnicodeString->Length; + targetUnicodeString->MaximumLength = targetUnicodeString->Length; + targetUnicodeString->Buffer = (PWSTR)((PCHAR)Information + sizeof(UNICODE_STRING)); + memcpy( + targetUnicodeString->Buffer, + UnicodeString->Buffer, + targetUnicodeString->Length + ); + } + else + { + targetUnicodeString->Length = 0; + targetUnicodeString->MaximumLength = 0; + targetUnicodeString->Buffer = NULL; + } +} diff --git a/2.x/trunk/KProcessHacker/kph.c b/2.x/trunk/KProcessHacker/kph.c new file mode 100644 index 000000000..8bc172a0f --- /dev/null +++ b/2.x/trunk/KProcessHacker/kph.c @@ -0,0 +1,414 @@ +/* + * Process Hacker Driver - + * custom APIs + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _KPH_PRIVATE +#include "include/kph.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, GetSystemRoutineAddress) +#pragma alloc_text(PAGE, KphNtInit) +#pragma alloc_text(PAGE, OpenProcess) +#pragma alloc_text(PAGE, SetProcessToken) +#endif + +POBJECT_TYPE ObpDirectoryObjectType; +POBJECT_TYPE ObpTypeObjectType; + +/* GetSystemRoutineAddress + * + * Gets the address of a function exported by ntoskrnl or hal. + */ +PVOID GetSystemRoutineAddress(WCHAR *Name) +{ + UNICODE_STRING routineName; + PVOID routineAddress = NULL; + + RtlInitUnicodeString(&routineName, Name); + + /* Wrap in SEH because MmGetSystemRoutineAddress is known to cause + some BSODs. */ + try + { + routineAddress = MmGetSystemRoutineAddress(&routineName); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + routineAddress = NULL; + } + + return routineAddress; +} + +/* KphNtInit + * + * Initializes the KProcessHacker NT component. + */ +NTSTATUS KphNtInit() +{ + NTSTATUS status = STATUS_SUCCESS; + /* Confuse those damn AVs... */ + PWCHAR keService = L"KeService"; // length 9, 18 bytes + PWCHAR descriptorTable = L"DescriptorTable"; // 15, 30 bytes + WCHAR keServiceDescriptorTable[9 + 15 + 1]; + + /* Reconstruct the string. */ + memcpy(keServiceDescriptorTable, keService, 18); + memcpy(keServiceDescriptorTable + 9, descriptorTable, 30); + keServiceDescriptorTable[9 + 15] = L'\0'; + + /* Dynamically get function pointers. */ + __KeServiceDescriptorTable = GetSystemRoutineAddress(keServiceDescriptorTable); + dfprintf("KeServiceDescriptorTable: %#x\n", __KeServiceDescriptorTable); + PsGetProcessJob = GetSystemRoutineAddress(L"PsGetProcessJob"); + dfprintf("PsGetProcessJob: %#x\n", PsGetProcessJob); + PsResumeProcess = GetSystemRoutineAddress(L"PsResumeProcess"); + dfprintf("PsResumeProcess: %#x\n", PsResumeProcess); + PsSuspendProcess = GetSystemRoutineAddress(L"PsSuspendProcess"); + dfprintf("PsSuspendProcess: %#x\n", PsSuspendProcess); + + if (WindowsVersion >= WINDOWS_7) + { + ObGetObjectType = GetSystemRoutineAddress(L"ObGetObjectType"); + dfprintf("ObGetObjectType: %#x\n", ObGetObjectType); + } + + /* Scan for functions. */ + if (KiFastCallEntryScan.Initialized) + { + __KiFastCallEntry = KvScanProc(&KiFastCallEntryScan); + dfprintf("KiFastCallEntry+x: %#x\n", __KiFastCallEntry); + } + if (PsTerminateProcessScan.Initialized) + { + __PsTerminateProcess = KvScanProc(&PsTerminateProcessScan); + dfprintf("PsTerminateProcess: %#x\n", __PsTerminateProcess); + } + if (PspTerminateThreadByPointerScan.Initialized) + { + __PspTerminateThreadByPointer = KvScanProc(&PspTerminateThreadByPointerScan); + dfprintf("PspTerminateThreadByPointer: %#x\n", __PspTerminateThreadByPointer); + } + + /* Fill in other global variables. */ + + /* Directory object type. */ + { + HANDLE rootDirectoryHandle; + PVOID rootDirectoryObject; + UNICODE_STRING rootDirectoryName; + OBJECT_ATTRIBUTES objectAttributes; + + RtlInitUnicodeString(&rootDirectoryName, L"\\"); + InitializeObjectAttributes( + &objectAttributes, + &rootDirectoryName, + OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + status = ZwOpenDirectoryObject(&rootDirectoryHandle, DIRECTORY_QUERY, &objectAttributes); + + if (!NT_SUCCESS(status)) + return status; + + status = ObReferenceObjectByHandle(rootDirectoryHandle, 0, NULL, KernelMode, &rootDirectoryObject, NULL); + ZwClose(rootDirectoryHandle); + + if (!NT_SUCCESS(status)) + return status; + + ObpDirectoryObjectType = KphGetObjectTypeNt(rootDirectoryObject); + ObDirectoryObjectType = &ObpDirectoryObjectType; + ObDereferenceObject(rootDirectoryObject); + } + + /* Type object type. */ + ObpTypeObjectType = KphGetObjectTypeNt(*PsProcessType); + ObTypeObjectType = &ObpTypeObjectType; + + return status; +} + +/* KphAttachProcess + * + * Attaches to a process represented by the specified EPROCESS. + */ +VOID KphAttachProcess( + __in PEPROCESS Process, + __out PKPH_ATTACH_STATE AttachState + ) +{ + AttachState->Attached = FALSE; + + /* Don't attach if we are already attached to the target. */ + if (Process != PsGetCurrentProcess()) + { + KeStackAttachProcess(Process, &AttachState->ApcState); + AttachState->Attached = TRUE; + AttachState->Process = Process; + } +} + +/* KphAttachProcessHandle + * + * Attaches to a process represented by the specified handle. + */ +NTSTATUS KphAttachProcessHandle( + __in HANDLE ProcessHandle, + __out PKPH_ATTACH_STATE AttachState + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + AttachState->Attached = FALSE; + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + KphAttachProcess(processObject, AttachState); + ObDereferenceObject(processObject); + + return status; +} + +/* KphAttachProcessId + * + * Attaches to a process represented by the specified process ID. + */ +NTSTATUS KphAttachProcessId( + __in HANDLE ProcessId, + __out PKPH_ATTACH_STATE AttachState + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + AttachState->Attached = FALSE; + + status = PsLookupProcessByProcessId(ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + return status; + + KphAttachProcess(processObject, AttachState); + ObDereferenceObject(processObject); + + return status; +} + +/* KphCaptureUnicodeString + * + * Captures a UNICODE_STRING. This function will not throw exceptions. + */ +NTSTATUS KphCaptureUnicodeString( + __in PUNICODE_STRING UnicodeString, + __out PUNICODE_STRING CapturedUnicodeString + ) +{ + __try + { + CapturedUnicodeString->Length = UnicodeString->Length; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + CapturedUnicodeString->MaximumLength = CapturedUnicodeString->Length; + CapturedUnicodeString->Buffer = ExAllocatePoolWithTag( + PagedPool, + CapturedUnicodeString->Length, + TAG_CAPTURED_UNICODE_STRING + ); + + if (!CapturedUnicodeString->Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + __try + { + memcpy( + CapturedUnicodeString->Buffer, + UnicodeString->Buffer, + CapturedUnicodeString->Length + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphFreeCapturedUnicodeString(CapturedUnicodeString); + return GetExceptionCode(); + } + + return STATUS_SUCCESS; +} + +/* KphDetachProcess + * + * Detaches from the currently attached process. + */ +VOID KphDetachProcess( + __in PKPH_ATTACH_STATE AttachState + ) +{ + if (AttachState->Attached) + KeUnstackDetachProcess(&AttachState->ApcState); +} + +/* KphFreeCapturedUnicodeString + * + * Frees a UNICODE_STRING captured by KphCaptureUnicodeString. + */ +VOID KphFreeCapturedUnicodeString( + __in PUNICODE_STRING CapturedUnicodeString + ) +{ + ExFreePoolWithTag( + CapturedUnicodeString->Buffer, + TAG_CAPTURED_UNICODE_STRING + ); +} + +/* KphProbeForReadUnicodeString + * + * Probes a UNICODE_STRING structure for reading. + */ +VOID KphProbeForReadUnicodeString( + __in PUNICODE_STRING UnicodeString + ) +{ + ProbeForRead(UnicodeString, sizeof(UNICODE_STRING), 1); + ProbeForRead(UnicodeString->Buffer, UnicodeString->Length, 1); +} + +/* KphProbeSystemAddressRange + * + * Probes an address range in kernel-mode memory for reading. + */ +VOID KphProbeSystemAddressRange( + __in PVOID BaseAddress, + __in ULONG Length + ) +{ + ULONG_PTR page, pageEnd; + + /* HACK HACK HACK HACK HACK HACK */ + /* Check the address range by checking each page. */ + /* Round down the base address to the page size. Note: please make sure you are + * not using a dumbass compiler which optimizes the following line by removing + * the divide and multiply. + */ + page = (ULONG_PTR)BaseAddress / PAGE_SIZE * PAGE_SIZE; + /* BaseAddress + Length - 1 is the last address we will be reading. */ + pageEnd = ((ULONG_PTR)BaseAddress + Length - 1) / PAGE_SIZE * PAGE_SIZE; + + for (; page <= pageEnd; page += PAGE_SIZE) + { + /* Check the page. */ + if (!MmIsAddressValid((PVOID)page)) + ExRaiseStatus(STATUS_ACCESS_VIOLATION); + } +} + +/* OpenProcess + * + * Opens the process with the specified PID. + */ +NTSTATUS OpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in HANDLE ProcessId + ) +{ + OBJECT_ATTRIBUTES objAttr = { 0 }; + CLIENT_ID clientId; + + objAttr.Length = sizeof(objAttr); + clientId.UniqueThread = 0; + clientId.UniqueProcess = ProcessId; + + return KphOpenProcess(ProcessHandle, DesiredAccess, &objAttr, &clientId, KernelMode); +} + +/* SetProcessToken + * + * Assigns the primary token of the target process from the + * primary token of source process. + */ +NTSTATUS SetProcessToken( + __in HANDLE sourcePid, + __in HANDLE targetPid + ) +{ + NTSTATUS status; + HANDLE source; + + if (NT_SUCCESS(status = OpenProcess(&source, PROCESS_QUERY_INFORMATION, sourcePid))) + { + HANDLE target; + + if (NT_SUCCESS(status = OpenProcess(&target, PROCESS_QUERY_INFORMATION | + PROCESS_SET_INFORMATION, targetPid))) + { + HANDLE sourceToken; + + if (NT_SUCCESS(status = KphOpenProcessTokenEx(source, TOKEN_DUPLICATE, 0, + &sourceToken, UserMode))) + { + HANDLE dupSourceToken; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + + objectAttributes.Length = sizeof(objectAttributes); + + if (NT_SUCCESS(status = ZwDuplicateToken(sourceToken, TOKEN_ASSIGN_PRIMARY, &objectAttributes, + FALSE, TokenPrimary, &dupSourceToken))) + { + PROCESS_ACCESS_TOKEN token; + + token.Token = dupSourceToken; + token.Thread = 0; + + status = ZwSetInformationProcess(target, ProcessAccessToken, &token, sizeof(token)); + } + + ZwClose(dupSourceToken); + } + + ZwClose(sourceToken); + } + + ZwClose(target); + } + + ZwClose(source); + + return status; +} diff --git a/2.x/trunk/KProcessHacker/kprocesshacker.c b/2.x/trunk/KProcessHacker/kprocesshacker.c new file mode 100644 index 000000000..5a470578f --- /dev/null +++ b/2.x/trunk/KProcessHacker/kprocesshacker.c @@ -0,0 +1,2609 @@ +/* + * Process Hacker Driver - + * main driver code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kprocesshacker.h" +#include "include/debug.h" + +#include "include/kph.h" +#include "include/protect.h" +#include "include/ps.h" +#include "include/sysservice.h" +#include "include/version.h" + +#define CHECK_IN_LENGTH \ + if (inLength < sizeof(*args)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } +#define CHECK_OUT_LENGTH \ + if (outLength < sizeof(*ret)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } +#define CHECK_IN_OUT_LENGTH \ + if (inLength < sizeof(*args) || outLength < sizeof(*ret)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } + +PDRIVER_OBJECT KphDriverObject; + +static PKPH_OBJECT_TYPE ClientEntryType; +static LIST_ENTRY ClientListHead; +static EX_PUSH_LOCK ClientListLock; + +static BOOLEAN ProtectionInitialized = FALSE; +static FAST_MUTEX ProtectionMutex; + +static ULONG SsStartCount = 0; +static FAST_MUTEX SsMutex; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, DriverUnload) +#pragma alloc_text(PAGE, KphDispatchCreate) +#pragma alloc_text(PAGE, KphDispatchClose) +#pragma alloc_text(PAGE, KphDispatchDeviceControl) +#pragma alloc_text(PAGE, KphDispatchRead) +#pragma alloc_text(PAGE, KphUnsupported) +#endif + +NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + NTSTATUS status = STATUS_SUCCESS; + int i; + PDEVICE_OBJECT deviceObject = NULL; + UNICODE_STRING deviceName, dosDeviceName; + + KphDriverObject = DriverObject; + + /* Initialize version information. */ + status = KvInit(); + + if (!NT_SUCCESS(status)) + { + if (status == STATUS_NOT_SUPPORTED) + dprintf("Your operating system is not supported by KProcessHacker\n"); + + return status; + } + + /* Initialize NT KPH. */ + status = KphNtInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize hooking. */ + status = KphHookInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the KPH object manager. */ + status = KphRefInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize system service logging. */ + status = KphSsLogInit(); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize trace databases. */ + status = KphTraceDatabaseInitialization(); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize client list structures. */ + InitializeListHead(&ClientListHead); + ExInitializePushLock(&ClientListLock); + + status = KphCreateObjectType( + &ClientEntryType, + PagedPool, + 0, + ClientEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize process protection. */ + ExInitializeFastMutex(&ProtectionMutex); + /* Initialize the system service logging mutex. */ + ExInitializeFastMutex(&SsMutex); + + RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME); + RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME); + + /* Create the KProcessHacker device. */ + status = IoCreateDevice(DriverObject, 0, &deviceName, + FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); + + /* Set up the major functions. */ + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + DriverObject->MajorFunction[i] = NULL; + + DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose; + DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_READ] = KphDispatchRead; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl; + DriverObject->DriverUnload = DriverUnload; + + deviceObject->Flags |= DO_BUFFERED_IO; + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + IoCreateSymbolicLink(&dosDeviceName, &deviceName); + + dprintf("Driver loaded\n"); + + return STATUS_SUCCESS; +} + +VOID DriverUnload(PDRIVER_OBJECT DriverObject) +{ + UNICODE_STRING dosDeviceName; + + RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME); + IoDeleteSymbolicLink(&dosDeviceName); + IoDeleteDevice(DriverObject->DeviceObject); + + ExAcquireFastMutex(&ProtectionMutex); + + if (ProtectionInitialized) + { + KphProtectDeinit(); + ProtectionInitialized = FALSE; + } + + ExReleaseFastMutex(&ProtectionMutex); + + /* Make sure system service logging is disabled. */ + if (SsStartCount > 0) + SsUnref(SsStartCount); + + /* Free system service logging structures. */ + KphSsLogDeinit(); + + /* Free all objects in the object manager. */ + KphRefDeinit(); + + dprintf("Driver unloaded\n"); +} + +NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + +#ifdef KPH_REQUIRE_DEBUG_PRIVILEGE + if (!SeSinglePrivilegeCheck(SeExports->SeDebugPrivilege, UserMode)) + { + dprintf("Client (PID %d) was refused\n", PsGetCurrentProcessId()); + Irp->IoStatus.Status = STATUS_PRIVILEGE_NOT_HELD; + + return STATUS_PRIVILEGE_NOT_HELD; + } +#endif + + /* Add a client entry. Note that we don't dereference it because + * we keep one reference for it being on the client list. + */ + if (!CreateClientEntry(NULL)) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + return STATUS_INSUFFICIENT_RESOURCES; + } + + dprintf("Client (PID %d) connected\n", PsGetCurrentProcessId()); + dprintf("Base IOCTL is 0x%08x\n", KPH_CTL_CODE(0)); + + return status; +} + +NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_CLIENT_ENTRY clientEntry; + + ExAcquireFastMutex(&ProtectionMutex); + + if (ProtectionInitialized) + { + ULONG count = KphProtectRemoveByTag(PsGetCurrentProcessId()); + dprintf("Removed %d protection entries\n", count); + } + + ExReleaseFastMutex(&ProtectionMutex); + + /* Get the current client entry and dereference it twice to remove it. */ + clientEntry = ReferenceClientEntry(NULL); + + if (clientEntry) + KphDereferenceObjectEx(clientEntry, 2, FALSE); + + dprintf("Client (PID %d) disconnected\n", PsGetCurrentProcessId()); + + return status; +} + +VOID InitProtection() +{ + ExAcquireFastMutex(&ProtectionMutex); + + if (!ProtectionInitialized) + { + if (NT_SUCCESS(KphProtectInit())) + ProtectionInitialized = TRUE; + } + + ExReleaseFastMutex(&ProtectionMutex); +} + +VOID SsRef(LONG count) +{ + LONG oldRefCount; + + ASSERT(count >= 0); + + if (count == 0) + return; + + ExAcquireFastMutex(&SsMutex); + + /* Add references. */ + oldRefCount = InterlockedExchangeAdd(&SsStartCount, count); + ASSERT(oldRefCount >= 0); + + /* Start system service logging if this was the first bunch of references. */ + if (oldRefCount == 0) + KphSsLogStart(); + + ExReleaseFastMutex(&SsMutex); +} + +VOID SsUnref(LONG count) +{ + LONG oldRefCount; + + ASSERT(count >= 0); + + if (count == 0) + return; + + ExAcquireFastMutex(&SsMutex); + + oldRefCount = InterlockedExchangeAdd(&SsStartCount, -count); + ASSERT(oldRefCount > 0); + + if (oldRefCount - count == 0) + KphSsLogStop(); + + ExReleaseFastMutex(&SsMutex); +} + +VOID NTAPI ClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPH_CLIENT_ENTRY entry = (PKPH_CLIENT_ENTRY)Object; + + /* Lower the SS start count. */ + SsUnref(entry->SsStartCount); + + /* Free the handle table. */ + KphFreeHandleTable(entry->HandleTable); + + /* Remove the entry from the client list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&ClientListLock); + RemoveEntryList(&entry->ClientListEntry); + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); +} + +PKPH_CLIENT_ENTRY CreateClientEntry( + __in_opt HANDLE ProcessId + ) +{ + PKPH_CLIENT_ENTRY entry; + PKPH_HANDLE_TABLE handleTable; + + /* If the PID wasn't specified, use the current one. */ + if (!ProcessId) + ProcessId = PsGetCurrentProcessId(); + + if (!NT_SUCCESS(KphCreateHandleTable( + &handleTable, + KPH_CLIENT_MAXHANDLES, + sizeof(KPH_HANDLE_TABLE_ENTRY), + TAG_CLIENT_HANDLETABLE + ))) + return NULL; + + if (!NT_SUCCESS(KphCreateObject( + &entry, + sizeof(KPH_CLIENT_ENTRY), + 0, + ClientEntryType, + 0 + ))) + { + KphFreeHandleTable(handleTable); + return NULL; + } + + /* Initialize the entry. */ + entry->ProcessId = ProcessId; + entry->HandleTable = handleTable; + KphInitializeGuardedLock(&entry->SsLock, FALSE); + entry->SsStartCount = 0; + + /* Insert the entry into the client list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&ClientListLock); + InsertHeadList(&ClientListHead, &entry->ClientListEntry); + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return entry; +} + +PKPH_CLIENT_ENTRY ReferenceClientEntry( + __in_opt HANDLE ProcessId + ) +{ + PLIST_ENTRY entry = ClientListHead.Flink; + + /* If the PID wasn't specified, use the current one. */ + if (!ProcessId) + ProcessId = PsGetCurrentProcessId(); + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&ClientListLock); + + /* Find the client entry. */ + while (entry != &ClientListHead) + { + PKPH_CLIENT_ENTRY clientEntry = + CONTAINING_RECORD(entry, KPH_CLIENT_ENTRY, ClientListEntry); + + if (clientEntry->ProcessId == ProcessId) + { + PKPH_CLIENT_ENTRY returnEntry = NULL; + + /* Reference and return the entry. */ + if (KphReferenceObjectSafe(clientEntry)) + { + returnEntry = clientEntry; + } + + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return returnEntry; + } + + entry = entry->Flink; + } + + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return NULL; +} + +NTSTATUS CloseClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphCloseHandle(clientEntry->HandleTable, Handle); + KphDereferenceObject(clientEntry); + + return status; +} + +NTSTATUS CreateClientHandle( + __in_opt HANDLE ProcessId, + __in PVOID Object, + __out PHANDLE Handle + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphCreateHandle(clientEntry->HandleTable, Object, Handle); + KphDereferenceObject(clientEntry); + + return status; +} + +NTSTATUS ReferenceClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle, + __in PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphReferenceObjectByHandle( + clientEntry->HandleTable, + Handle, + ObjectType, + Object + ); + KphDereferenceObject(clientEntry); + + return status; +} + +PCHAR GetIoControlName(ULONG ControlCode) +{ + switch (ControlCode) + { + case KPH_CLOSEHANDLE: + return "Client Close Handle"; + case KPH_SSQUERYCLIENTENTRY: + return "SsQueryClientEntry"; + case KPH_OPENPROCESS: + return "KphOpenProcess"; + case KPH_OPENTHREAD: + return "KphOpenThread"; + case KPH_OPENPROCESSTOKEN: + return "KphOpenProcessTokenEx"; + case KPH_GETPROCESSPROTECTED: + return "Get Process Protected"; + case KPH_SETPROCESSPROTECTED: + return "Set Process Protected"; + case KPH_TERMINATEPROCESS: + return "KphTerminateProcess"; + case KPH_SUSPENDPROCESS: + return "KphSuspendProcess"; + case KPH_RESUMEPROCESS: + return "KphResumeProcess"; + case KPH_READVIRTUALMEMORY: + return "KphReadVirtualMemory"; + case KPH_WRITEVIRTUALMEMORY: + return "KphWriteVirtualMemory"; + case KPH_SETPROCESSTOKEN: + return "Set Process Token"; + case KPH_GETTHREADSTARTADDRESS: + return "Get Thread Start Address"; + case KPH_SETHANDLEATTRIBUTES: + return "Set Handle Attributes"; + case KPH_GETHANDLEOBJECTNAME: + return "Get Handle Object Name"; + case KPH_OPENPROCESSJOB: + return "KphOpenProcessJob"; + case KPH_GETCONTEXTTHREAD: + return "KphGetContextThread"; + case KPH_SETCONTEXTTHREAD: + return "KphSetContextThread"; + case KPH_GETTHREADWIN32THREAD: + return "KphGetThreadWin32Thread"; + case KPH_DUPLICATEOBJECT: + return "KphDuplicateObject"; + case KPH_ZWQUERYOBJECT: + return "ZwQueryObject"; + case KPH_GETPROCESSID: + return "KphGetProcessId"; + case KPH_GETTHREADID: + return "KphGetThreadId"; + case KPH_TERMINATETHREAD: + return "KphTerminateThread"; + case KPH_GETFEATURES: + return "Get Features"; + case KPH_SETHANDLEGRANTEDACCESS: + return "KphSetHandleGrantedAccess"; + case KPH_ASSIGNIMPERSONATIONTOKEN: + return "KphAssignImpersonationToken"; + case KPH_PROTECTADD: + return "Add Process Protection"; + case KPH_PROTECTREMOVE: + return "Remove Process Protection"; + case KPH_PROTECTQUERY: + return "Query Process Protection"; + case KPH_UNSAFEREADVIRTUALMEMORY: + return "KphUnsafeReadVirtualMemory"; + case KPH_SETEXECUTEOPTIONS: + return "Set Execute Options"; + case KPH_QUERYPROCESSHANDLES: + return "KphQueryProcessHandles"; + case KPH_OPENTHREADPROCESS: + return "KphOpenThreadProcess"; + case KPH_CAPTURESTACKBACKTRACETHREAD: + return "KphCaptureStackBackTraceThread"; + case KPH_DANGEROUSTERMINATETHREAD: + return "KphDangerousTerminateThread"; + case KPH_OPENTYPE: + return "KphOpenType"; + case KPH_OPENDRIVER: + return "KphOpenDriver"; + case KPH_QUERYINFORMATIONDRIVER: + return "KphQueryInformationDriver"; + case KPH_OPENDIRECTORYOBJECT: + return "KphOpenDirectoryObject"; + case KPH_SSREF: + return "SsRef"; + case KPH_SSUNREF: + return "SsUnref"; + case KPH_SSCREATECLIENTENTRY: + return "SsCreateClientEntry"; + case KPH_SSCREATERULESETENTRY: + return "SsCreateRuleSetEntry"; + case KPH_SSREMOVERULE: + return "SsRemoveRule"; + case KPH_SSADDPROCESSIDRULE: + return "SsAddProcessIdRule"; + case KPH_SSADDTHREADIDRULE: + return "SsAddThreadIdRule"; + case KPH_SSADDPREVIOUSMODERULE: + return "SsAddPreviousModeRule"; + case KPH_SSADDNUMBERRULE: + return "SsAddNumberRule"; + case KPH_SSENABLECLIENTENTRY: + return "SsEnableClientEntry"; + case KPH_OPENNAMEDOBJECT: + return "KphOpenNamedObject"; + case KPH_QUERYINFORMATIONPROCESS: + return "KphQueryInformationProcess"; + case KPH_QUERYINFORMATIONTHREAD: + return "KphQueryInformationThread"; + case KPH_SETINFORMATIONPROCESS: + return "KphSetInformationProcess"; + case KPH_SETINFORMATIONTHREAD: + return "KphSetInformationThread"; + default: + return "Unknown"; + } +} + +NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION ioStackIrp = NULL; + PVOID dataBuffer; + ULONG controlCode; + ULONG inLength = 0; + ULONG outLength = 0; + ULONG retLength = 0; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + ioStackIrp = IoGetCurrentIrpStackLocation(Irp); + + if (ioStackIrp == NULL) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + dataBuffer = Irp->AssociatedIrp.SystemBuffer; + + if (dataBuffer == NULL && (inLength != 0 || outLength != 0)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto IoControlEnd; + } + + inLength = ioStackIrp->Parameters.DeviceIoControl.InputBufferLength; + outLength = ioStackIrp->Parameters.DeviceIoControl.OutputBufferLength; + controlCode = ioStackIrp->Parameters.DeviceIoControl.IoControlCode; + + dprintf("IoControl 0x%08x (%s)\n", controlCode, GetIoControlName(controlCode)); + + /* 1-byte packing for KPH input/output structures. */ + #include + + switch (controlCode) + { + /* Client Close Handle + * + * Closes a handle opened by the client. + */ + case KPH_CLOSEHANDLE: + { + struct + { + HANDLE Handle; + } *args = dataBuffer; + PKPH_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + status = CloseClientHandle(NULL, args->Handle); + } + break; + + /* SsQueryClientEntry + * + * Queries information about a client entry. + */ + case KPH_SSQUERYCLIENTENTRY: + { + struct + { + HANDLE ClientEntryHandle; + PKPHSS_CLIENT_INFORMATION ClientInformation; + ULONG ClientInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Query the client entry. */ + status = KphSsQueryClientEntry( + clientEntry, + args->ClientInformation, + args->ClientInformationLength, + args->ReturnLength, + UserMode + ); + KphDereferenceObject(clientEntry); + } + break; + + /* KphOpenProcess + * + * Opens the specified process. This call will never fail unless: + * 1. PsLookupProcessByProcessId, ObOpenObjectByPointer or some lower-level + * function is hooked, or + * 2. The process is protected. + */ + case KPH_OPENPROCESS: + { + struct + { + HANDLE ProcessId; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ProcessHandle; + } *ret = dataBuffer; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + + CHECK_IN_OUT_LENGTH; + + clientId.UniqueThread = 0; + clientId.UniqueProcess = args->ProcessId; + status = KphOpenProcess( + &ret->ProcessHandle, + args->DesiredAccess, + &objectAttributes, + &clientId, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphOpenThread + * + * Opens the specified thread. This call will never fail unless: + * 1. PsLookupProcessThreadByCid, ObOpenObjectByPointer or some lower-level + * function is hooked, or + * 2. The thread's process is protected. + */ + case KPH_OPENTHREAD: + { + struct + { + HANDLE ThreadId; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ThreadHandle; + } *ret = dataBuffer; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + + CHECK_IN_OUT_LENGTH; + + clientId.UniqueThread = args->ThreadId; + clientId.UniqueProcess = 0; + status = KphOpenThread( + &ret->ThreadHandle, + args->DesiredAccess, + &objectAttributes, + &clientId, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphOpenProcessToken + * + * Opens the specified process' token. This call will never fail unless + * a low-level function is hooked. + */ + case KPH_OPENPROCESSTOKEN: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE TokenHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenProcessTokenEx( + args->ProcessHandle, + args->DesiredAccess, + 0, + &ret->TokenHandle, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* Get Process Protected + * + * Gets whether the process is protected. + */ + case KPH_GETPROCESSPROTECTED: + { + struct + { + HANDLE ProcessId; + } *args = dataBuffer; + struct + { + BOOLEAN IsProtected; + } *ret = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_OUT_LENGTH; + + status = PsLookupProcessByProcessId(args->ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ret->IsProtected = + (CHAR)GET_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + ObDereferenceObject(processObject); + retLength = sizeof(*ret); + } + break; + + /* Set Process Protected + * + * Sets whether the process is protected. + */ + case KPH_SETPROCESSPROTECTED: + { + struct + { + HANDLE ProcessId; + BOOLEAN IsProtected; + } *args = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_LENGTH; + + status = PsLookupProcessByProcessId(args->ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (args->IsProtected) + { + SET_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + } + else + { + CLEAR_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + } + + ObDereferenceObject(processObject); + } + break; + + /* KphTerminateProcess + * + * Terminates the specified process. This call will never fail unless + * PsTerminateProcess could not be located and Zw/NtTerminateProcess + * is hooked, or an attempt was made to terminate the current process. + * In that case, the call will fail with STATUS_CANT_TERMINATE_SELF. + */ + case KPH_TERMINATEPROCESS: + { + struct + { + HANDLE ProcessHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphTerminateProcess(args->ProcessHandle, args->ExitStatus); + } + break; + + /* KphSuspendProcess + * + * Suspends the specified process. This call will fail on Windows XP + * and below. + */ + case KPH_SUSPENDPROCESS: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSuspendProcess(args->ProcessHandle); + } + break; + + /* KphResumeProcess + * + * Resumes the specified process. This call will fail on Windows XP + * and below. + */ + case KPH_RESUMEPROCESS: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphResumeProcess(args->ProcessHandle); + } + break; + + /* KphReadVirtualMemory + * + * Reads process memory. + */ + case KPH_READVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphReadVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphWriteVirtualMemory + * + * Writes to process memory. + */ + case KPH_WRITEVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphWriteVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* Set Process Token + * + * Assigns the primary token of a source process to a target process. + */ + case KPH_SETPROCESSTOKEN: + { + struct + { + HANDLE SourceProcessId; + HANDLE TargetProcessId; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = SetProcessToken(args->SourceProcessId, args->TargetProcessId); + } + break; + + /* Get Thread Start Address + * + * Gets the specified thread's start address. + */ + case KPH_GETTHREADSTARTADDRESS: + { + struct + { + HANDLE ThreadHandle; + } *args = dataBuffer; + struct + { + PVOID StartAddress; + } *ret = dataBuffer; + PETHREAD threadObject; + + CHECK_IN_OUT_LENGTH; + + status = ObReferenceObjectByHandle(args->ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, NULL); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Get the Win32StartAddress */ + if (!(ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtWin32StartAddress))) + { + /* If that failed, get the StartAddress */ + ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtStartAddress); + } + + ObDereferenceObject(threadObject); + retLength = sizeof(*ret); + } + break; + + /* Set Handle Attributes + * + * Sets handle flags in the specified process. + */ + case KPH_SETHANDLEATTRIBUTES: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + ULONG Flags; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + OBJECT_HANDLE_FLAG_INFORMATION handleFlags = { 0 }; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (args->Flags & OBJ_PROTECT_CLOSE) + handleFlags.ProtectFromClose = TRUE; + if (args->Flags & OBJ_INHERIT) + handleFlags.Inherit = TRUE; + + status = ObSetHandleAttributes(args->Handle, &handleFlags, UserMode); + KphDetachProcess(&attachState); + } + break; + + /* Get Handle Object Name + * + * Gets the name of the specified handle. The handle can be remote; in + * that case a valid process handle must be passed. Otherwise, set the + * process handle to -1 (NtCurrentProcess()). + */ + case KPH_GETHANDLEOBJECTNAME: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + PVOID object; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* See the block for KPH_ZWQUERYOBJECT for information. */ + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + status = ObReferenceObjectByHandle(args->Handle, 0, NULL, KernelMode, &object, NULL); + KphDetachProcess(&attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = KphQueryNameObject(object, (PUNICODE_STRING)dataBuffer, outLength, &retLength); + ObDereferenceObject(object); + + /* Check if the return length is greater than the length of the user buffer. + * If so, it means the user needs to provide a larger buffer. In that case, + * store the length in the Unicode string structure. + */ + if (retLength > outLength) + { + if (outLength >= sizeof(UNICODE_STRING)) + { + ((PUNICODE_STRING)dataBuffer)->Length = (USHORT)retLength; + retLength = sizeof(UNICODE_STRING); + } + } + } + break; + + /* KphOpenProcessJob + * + * Opens the job object that the process is assigned to. If the process is + * not assigned to any job object, the call will fail with STATUS_PROCESS_NOT_IN_JOB. + */ + case KPH_OPENPROCESSJOB: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE JobHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenProcessJob(args->ProcessHandle, args->DesiredAccess, &ret->JobHandle, KernelMode); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphGetContextThread + * + * Gets the context of the specified thread. + */ + case KPH_GETCONTEXTTHREAD: + { + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphGetContextThread(args->ThreadHandle, args->ThreadContext, UserMode); + } + break; + + /* KphSetContextThread + * + * Sets the context of the specified thread. + */ + case KPH_SETCONTEXTTHREAD: + { + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSetContextThread(args->ThreadHandle, args->ThreadContext, UserMode); + } + break; + + /* KphGetThreadWin32Thread + * + * Gets a pointer to the specified thread's Win32Thread structure. + */ + case KPH_GETTHREADWIN32THREAD: + { + struct + { + HANDLE ThreadHandle; + } *args = dataBuffer; + struct + { + PVOID Win32Thread; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphGetThreadWin32Thread(args->ThreadHandle, &ret->Win32Thread, KernelMode); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphDuplicateObject + * + * Duplicates the specified handle from the source process to the target process. + * Do not use this call to duplicate file handles; it may freeze indefinitely if + * the file is a named pipe. + */ + case KPH_DUPLICATEOBJECT: + { + struct + { + HANDLE SourceProcessHandle; + HANDLE SourceHandle; + HANDLE TargetProcessHandle; + PHANDLE TargetHandle; + ACCESS_MASK DesiredAccess; + ULONG HandleAttributes; + ULONG Options; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphDuplicateObject( + args->SourceProcessHandle, + args->SourceHandle, + args->TargetProcessHandle, + args->TargetHandle, + args->DesiredAccess, + args->HandleAttributes, + args->Options, + UserMode + ); + } + break; + + /* ZwQueryObject + * + * Performs ZwQueryObject in the context of another process. + */ + case KPH_ZWQUERYOBJECT: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + OBJECT_INFORMATION_CLASS ObjectInformationClass; + } *args = dataBuffer; + struct + { + NTSTATUS Status; + ULONG ReturnLength; + PVOID BufferBase; + CHAR Buffer[1]; + } *ret = dataBuffer; + NTSTATUS status2 = STATUS_SUCCESS; + KPH_ATTACH_STATE attachState; + BOOLEAN attached; + + if (inLength < sizeof(*args) || outLength < sizeof(*ret) - sizeof(CHAR)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto IoControlEnd; + } + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Are we attached to the system process? If we are, + * we must set the high bit in the handle to indicate + * that it is a kernel handle - a new check for this + * was added in Windows 7. + */ + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + status2 = ZwQueryObject( + args->Handle, + args->ObjectInformationClass, + ret->Buffer, + outLength - (sizeof(*ret) - sizeof(CHAR)), + &retLength + ); + KphDetachProcess(&attachState); + + ret->ReturnLength = retLength; + ret->BufferBase = ret->Buffer; + + if (NT_SUCCESS(status2)) + retLength += sizeof(*ret) - sizeof(CHAR); + else + retLength = sizeof(*ret) - sizeof(CHAR); + + ret->Status = status2; + } + break; + + /* KphGetProcessId + * + * Gets the process ID of a process handle in the context of another process. + */ + case KPH_GETPROCESSID: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + struct + { + HANDLE ProcessId; + } *ret = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_OUT_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + ret->ProcessId = KphGetProcessId(args->Handle); + KphDetachProcess(&attachState); + retLength = sizeof(*ret); + } + break; + + /* KphGetThreadId + * + * Gets the thread ID of a thread handle in the context of another process. + */ + case KPH_GETTHREADID: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + struct + { + HANDLE ThreadId; + HANDLE ProcessId; + } *ret = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_OUT_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + ret->ThreadId = KphGetThreadId(args->Handle, &ret->ProcessId); + KphDetachProcess(&attachState); + retLength = sizeof(*ret); + } + break; + + /* KphTerminateThread + * + * Terminates the specified thread. This call will fail if + * PspTerminateThreadByPointer could not be located or if an attempt + * was made to terminate the current thread. In that case, the call + * will return STATUS_CANT_TERMINATE_SELF. + */ + case KPH_TERMINATETHREAD: + { + struct + { + HANDLE ThreadHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphTerminateThread(args->ThreadHandle, args->ExitStatus); + } + break; + + /* Get Features + * + * Gets the features supported by the driver. + */ + case KPH_GETFEATURES: + { + struct + { + ULONG Features; + } *ret = dataBuffer; + ULONG features = 0; + + CHECK_OUT_LENGTH; + + if (__PsTerminateProcess) + features |= KPHF_PSTERMINATEPROCESS; + if (__PspTerminateThreadByPointer) + features |= KPHF_PSPTERMINATETHREADBPYPOINTER; + + ret->Features = features; + retLength = sizeof(*ret); + } + break; + + /* KphSetHandleGrantedAccess + * + * Sets the granted access for a handle. + */ + case KPH_SETHANDLEGRANTEDACCESS: + { + struct + { + HANDLE Handle; + ACCESS_MASK GrantedAccess; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSetHandleGrantedAccess( + PsGetCurrentProcess(), + args->Handle, + args->GrantedAccess + ); + } + break; + + /* KphAssignImpersonationToken + * + * Assigns an impersonation token to a thread. + */ + case KPH_ASSIGNIMPERSONATIONTOKEN: + { + struct + { + HANDLE ThreadHandle; + HANDLE TokenHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphAssignImpersonationToken(args->ThreadHandle, args->TokenHandle); + } + break; + + /* Add Process Protection */ + case KPH_PROTECTADD: + { + struct + { + HANDLE ProcessHandle; + LOGICAL AllowKernelMode; + ACCESS_MASK ProcessAllowMask; + ACCESS_MASK ThreadAllowMask; + } *args = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_LENGTH; + + /* We'll reference the process and then dereference it. That way + * we can get the address of the object - that's all we need. + */ + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + InitProtection(); + + /* Don't protect the same process twice. */ + if (KphProtectFindEntry(processObject, NULL, NULL)) + { + status = STATUS_NOT_SUPPORTED; + goto IoControlEnd; + } + + if (!KphProtectAddEntry( + processObject, + PsGetCurrentProcessId(), + args->AllowKernelMode, + args->ProcessAllowMask, + args->ThreadAllowMask + )) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + } + break; + + /* Remove Process Protection */ + case KPH_PROTECTREMOVE: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + PEPROCESS processObject; + + /* Can't remove anything if process protection hasn't been initialized - + there isn't anything to remove. */ + if (!ProtectionInitialized) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + CHECK_IN_LENGTH; + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + if (!KphProtectRemoveByProcess(processObject)) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + } + break; + + /* Query Process Protection */ + case KPH_PROTECTQUERY: + { + struct + { + HANDLE ProcessHandle; + PLOGICAL AllowKernelMode; + PACCESS_MASK ProcessAllowMask; + PACCESS_MASK ThreadAllowMask; + } *args = dataBuffer; + PEPROCESS processObject; + KPH_PROCESS_ENTRY processEntry; + + /* Can't query anything if process protection hasn't been initialized - + there isn't anything to query. */ + if (!ProtectionInitialized) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + CHECK_IN_LENGTH; + + __try + { + ProbeForWrite(args->AllowKernelMode, sizeof(LOGICAL), 1); + ProbeForWrite(args->ProcessAllowMask, sizeof(ACCESS_MASK), 1); + ProbeForWrite(args->ThreadAllowMask, sizeof(ACCESS_MASK), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + if (!KphProtectFindEntry(processObject, NULL, &processEntry)) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + + __try + { + *(args->AllowKernelMode) = processEntry.AllowKernelMode; + *(args->ProcessAllowMask) = processEntry.ProcessAllowMask; + *(args->ThreadAllowMask) = processEntry.ThreadAllowMask; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + /* KphUnsafeReadVirtualMemory + * + * Reads process memory or kernel memory. + */ + case KPH_UNSAFEREADVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphUnsafeReadVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* Set Execute Options + * + * Sets NX status for a process. + */ + case KPH_SETEXECUTEOPTIONS: + { + struct + { + HANDLE ProcessHandle; + ULONG ExecuteOptions; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = ZwSetInformationProcess( + NtCurrentProcess(), + ProcessExecuteFlags, + &args->ExecuteOptions, + sizeof(ULONG) + ); + KphDetachProcess(&attachState); + } + break; + + /* KphQueryProcessHandles + * + * Gets the handles in a process handle table. + */ + case KPH_QUERYPROCESSHANDLES: + { + struct + { + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphQueryProcessHandles( + args->ProcessHandle, + (PPROCESS_HANDLE_INFORMATION)args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphOpenThreadProcess + * + * Opens the process associated with the specified thread. + */ + case KPH_OPENTHREADPROCESS: + { + struct + { + HANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ProcessHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenThreadProcess( + args->ThreadHandle, + args->DesiredAccess, + &ret->ProcessHandle, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphCaptureStackBackTraceThread + * + * Captures a kernel stack trace for the specified thread. + */ + case KPH_CAPTURESTACKBACKTRACETHREAD: + { + struct + { + HANDLE ThreadHandle; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + PULONG CapturedFrames; + PULONG BackTraceHash; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphCaptureStackBackTraceThread( + args->ThreadHandle, + args->FramesToSkip, + args->FramesToCapture, + args->BackTrace, + args->CapturedFrames, + args->BackTraceHash, + UserMode + ); + } + break; + + /* KphDangerousTerminateThread + * + * Terminates the specified thread. This operation may cause a bugcheck. + */ + case KPH_DANGEROUSTERMINATETHREAD: + { + struct + { + HANDLE ThreadHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphDangerousTerminateThread(args->ThreadHandle, args->ExitStatus); + } + break; + + /* KphOpenType + * + * Opens a type object. + */ + case KPH_OPENTYPE: + { + struct + { + PHANDLE TypeHandle; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenType(args->TypeHandle, args->ObjectAttributes, UserMode); + } + break; + + /* KphOpenDriver + * + * Opens a driver object. + */ + case KPH_OPENDRIVER: + { + struct + { + PHANDLE DriverHandle; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenDriver(args->DriverHandle, args->ObjectAttributes, UserMode); + } + break; + + /* KphQueryInformationDriver + * + * Queries information about a driver object. + */ + case KPH_QUERYINFORMATIONDRIVER: + { + struct + { + HANDLE DriverHandle; + DRIVER_INFORMATION_CLASS DriverInformationClass; + PVOID DriverInformation; + ULONG DriverInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphQueryInformationDriver( + args->DriverHandle, + args->DriverInformationClass, + args->DriverInformation, + args->DriverInformationLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphOpenDirectoryObject + * + * Opens a directory object. + */ + case KPH_OPENDIRECTORYOBJECT: + { + struct + { + PHANDLE DirectoryObjectHandle; + ACCESS_MASK DesiredAccess; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenDirectoryObject( + args->DirectoryObjectHandle, + args->DesiredAccess, + args->ObjectAttributes, + UserMode + ); + } + break; + + /* SsRef + * + * Adds a system service logging reference. + */ + case KPH_SSREF: + { + PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL); + + if (!clientEntry) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + KphAcquireGuardedLock(&clientEntry->SsLock); + + if (clientEntry->SsStartCount < KPH_CLIENT_SSMAXCOUNT) + { + clientEntry->SsStartCount++; + SsRef(1); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + KphReleaseGuardedLock(&clientEntry->SsLock); + + KphDereferenceObject(clientEntry); + } + break; + + /* SsUnref + * + * Removes a system service logging reference. + */ + case KPH_SSUNREF: + { + PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL); + + if (!clientEntry) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + KphAcquireGuardedLock(&clientEntry->SsLock); + + if (clientEntry->SsStartCount > 0) + { + clientEntry->SsStartCount--; + SsUnref(1); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + KphReleaseGuardedLock(&clientEntry->SsLock); + + KphDereferenceObject(clientEntry); + } + break; + + /* SsCreateClientEntry + * + * Creates a system service logging client entry. + */ + case KPH_SSCREATECLIENTENTRY: + { + struct + { + HANDLE ProcessHandle; + HANDLE EventHandle; + HANDLE SemaphoreHandle; + PVOID BufferBase; + ULONG BufferSize; + } *args = dataBuffer; + struct + { + HANDLE ClientEntryHandle; + } *ret = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_OUT_LENGTH; + + status = KphSsCreateClientEntry( + &clientEntry, + args->ProcessHandle, + args->EventHandle, + args->SemaphoreHandle, + args->BufferBase, + args->BufferSize, + UserMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = CreateClientHandle(NULL, clientEntry, &ret->ClientEntryHandle); + KphDereferenceObject(clientEntry); + retLength = sizeof(*ret); + } + break; + + /* SsCreateRuleSetEntry + * + * Creates a system service logging ruleset entry. + */ + case KPH_SSCREATERULESETENTRY: + { + struct + { + HANDLE ClientEntryHandle; + KPHSS_FILTER_TYPE DefaultFilterType; + KPHSS_RULESET_ACTION Action; + } *args = dataBuffer; + struct + { + HANDLE RuleSetEntryHandle; + } *ret = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Create the ruleset entry. */ + status = KphSsCreateRuleSetEntry( + &ruleSetEntry, + clientEntry, + args->DefaultFilterType, + args->Action + ); + KphDereferenceObject(clientEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Create and return a handle to the ruleset entry. */ + status = CreateClientHandle(NULL, ruleSetEntry, &ret->RuleSetEntryHandle); + KphDereferenceObject(ruleSetEntry); + retLength = sizeof(*ret); + } + break; + + /* SsRemoveRule + * + * Removes a rule from a ruleset. + */ + case KPH_SSREMOVERULE: + { + struct + { + HANDLE RuleSetEntryHandle; + HANDLE RuleEntryHandle; + } *args = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + CHECK_IN_LENGTH; + + /* Reference the ruleset entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Remove the rule. */ + status = KphSsRemoveRule(ruleSetEntry, args->RuleEntryHandle); + KphDereferenceObject(ruleSetEntry); + } + break; + + /* SsAddProcessIdRule + * + * Adds a process ID rule to a ruleset. + */ + case KPH_SSADDPROCESSIDRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + HANDLE ProcessId; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a process ID rule. */ + status = KphSsAddProcessIdRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->ProcessId + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddThreadIdRule + * + * Adds a thread ID rule to a ruleset. + */ + case KPH_SSADDTHREADIDRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + HANDLE ThreadId; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a thread ID rule. */ + status = KphSsAddThreadIdRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->ThreadId + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddPreviousModeRule + * + * Adds a previous mode rule to a ruleset. + */ + case KPH_SSADDPREVIOUSMODERULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + KPROCESSOR_MODE PreviousMode; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a previous mode rule. */ + status = KphSsAddPreviousModeRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->PreviousMode + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddNumberRule + * + * Adds a system service number rule to a ruleset. + */ + case KPH_SSADDNUMBERRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + ULONG Number; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a number rule. */ + status = KphSsAddNumberRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->Number + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsEnableClientEntry + * + * Enables or disables a client entry. + */ + case KPH_SSENABLECLIENTENTRY: + { + struct + { + HANDLE ClientEntryHandle; + BOOLEAN Enable; + } *args = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Enable/disable the client entry. */ + status = KphSsEnableClientEntry(clientEntry, args->Enable); + KphDereferenceObject(clientEntry); + } + break; + + /* KphOpenNamedObject + * + * Opens a named object of any type. + */ + case KPH_OPENNAMEDOBJECT: + { + struct + { + PHANDLE Handle; + ACCESS_MASK DesiredAccess; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenNamedObject( + args->Handle, + args->DesiredAccess, + args->ObjectAttributes, + NULL, + UserMode + ); + } + break; + + case KPH_QUERYINFORMATIONPROCESS: + { + struct + { + HANDLE ProcessHandle; + PROCESSINFOCLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + if ( + args->ProcessInformationClass != ProcessIoPriority + ) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + __try + { + ProbeForWrite(args->ProcessInformation, args->ProcessInformationLength, 1); + + if (args->ReturnLength) + ProbeForWrite(args->ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + __try + { + status = ZwQueryInformationProcess( + args->ProcessHandle, + args->ProcessInformationClass, + args->ProcessInformation, + args->ProcessInformationLength, + args->ReturnLength + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + case KPH_QUERYINFORMATIONTHREAD: + { + struct + { + HANDLE ThreadHandle; + THREADINFOCLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + if ( + args->ThreadInformationClass != ThreadIoPriority + ) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + __try + { + ProbeForWrite(args->ThreadInformation, args->ThreadInformationLength, 1); + + if (args->ReturnLength) + ProbeForWrite(args->ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + __try + { + status = ZwQueryInformationThread( + args->ThreadHandle, + args->ThreadInformationClass, + args->ThreadInformation, + args->ThreadInformationLength, + args->ReturnLength + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + case KPH_SETINFORMATIONPROCESS: + { + struct + { + HANDLE ProcessHandle; + PROCESSINFOCLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + if ( + args->ProcessInformationClass != ProcessIoPriority + ) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + __try + { + ProbeForRead(args->ProcessInformation, args->ProcessInformationLength, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + __try + { + status = ZwSetInformationProcess( + args->ProcessHandle, + args->ProcessInformationClass, + args->ProcessInformation, + args->ProcessInformationLength + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + case KPH_SETINFORMATIONTHREAD: + { + struct + { + HANDLE ThreadHandle; + THREADINFOCLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + if ( + args->ThreadInformationClass != ThreadIoPriority + ) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + __try + { + ProbeForRead(args->ThreadInformation, args->ThreadInformationLength, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + __try + { + status = ZwSetInformationThread( + args->ThreadHandle, + args->ThreadInformationClass, + args->ThreadInformation, + args->ThreadInformationLength + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + default: + { + dprintf("Unrecognized IOCTL code 0x%08x\n", controlCode); + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + /* Restore the old packing. */ + #include + +IoControlEnd: + Irp->IoStatus.Information = retLength; + Irp->IoStatus.Status = status; + dprintf("IOCTL 0x%08x result was 0x%08x\n", controlCode, status); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION ioStackIrp = NULL; + ULONG retLength = 0; + + ioStackIrp = IoGetCurrentIrpStackLocation(Irp); + + if (ioStackIrp != NULL) + { + PCHAR readDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer; + ULONG readLength = ioStackIrp->Parameters.Read.Length; + + if (readDataBuffer != NULL) + { + dprintf("Client read %d bytes!\n", readLength); + + if (readLength == 4) + { + *(ULONG *)readDataBuffer = KPH_CTL_CODE(0); + retLength = 4; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + } + + Irp->IoStatus.Information = retLength; + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + dfprintf("Unsupported function called.\n"); + + return STATUS_NOT_SUPPORTED; +} diff --git a/2.x/trunk/KProcessHacker/makefile b/2.x/trunk/KProcessHacker/makefile new file mode 100644 index 000000000..05a507be4 --- /dev/null +++ b/2.x/trunk/KProcessHacker/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/mm.c b/2.x/trunk/KProcessHacker/mm.c new file mode 100644 index 000000000..84492ab34 --- /dev/null +++ b/2.x/trunk/KProcessHacker/mm.c @@ -0,0 +1,703 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/mm.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphReadVirtualMemory) +#pragma alloc_text(PAGE, KphUnsafeReadVirtualMemory) +#pragma alloc_text(PAGE, KphWriteVirtualMemory) +#pragma alloc_text(PAGE, MiDoMappedCopy) +#pragma alloc_text(PAGE, MiDoPoolCopy) +#pragma alloc_text(PAGE, MiGetExceptionInfo) +#pragma alloc_text(PAGE, MmCopyVirtualMemory) +#endif + +/* KphReadVirtualMemory + * + * Reads virtual memory from the specified process. + */ +NTSTATUS KphReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + ULONG returnLength = 0; + + /* Probe user input if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + /* If we actually have work to do, reference the process object and + call the internal function. */ + if (BufferLength) + { + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_READ, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = MmCopyVirtualMemory( + processObject, + BaseAddress, + PsGetCurrentProcess(), + Buffer, + BufferLength, + AccessMode, + &returnLength + ); + ObDereferenceObject(processObject); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +NTSTATUS KphUnsafeReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength = 0; + + /* Initial probing. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + ProbeForWrite(Buffer, BufferLength, 1); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Make sure we have something to copy. */ + if (BufferLength == 0) + { + __try + { + *ReturnLength = 0; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + return STATUS_SUCCESS; + } + + /* Select the appropriate copy method. */ + if (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) + { + /* Kernel memory unsafe copy. */ + + __try + { + /* Probe the address range. */ + KphProbeSystemAddressRange(BaseAddress, BufferLength); + + /* Copy the data. */ + memcpy(Buffer, BaseAddress, BufferLength); + returnLength = BufferLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + } + else + { + /* User memory safe copy. */ + status = KphReadVirtualMemory( + ProcessHandle, + BaseAddress, + Buffer, + BufferLength, + ReturnLength, + AccessMode + ); + } + + return status; +} + +/* KphWriteVirtualMemory + * + * Writes virtual memory to the specified process. + */ +NTSTATUS KphWriteVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + ULONG returnLength = 0; + + /* Probe user input if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + /* If we actually have work to do, reference the process object and + call the internal function. */ + if (BufferLength) + { + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_WRITE, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = MmCopyVirtualMemory( + PsGetCurrentProcess(), + Buffer, + processObject, + BaseAddress, + BufferLength, + AccessMode, + &returnLength + ); + ObDereferenceObject(processObject); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* MiDoMappedCopy + * + * Copies virtual memory from the source process to the target process + * using a memory mapping. + */ +NTSTATUS MiDoMappedCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; + PMDL mdl = (PMDL)mdlBuffer; + /* The mapped address. */ + PVOID mappedAddress; + /* The total size allocated (mapped pages). */ + ULONG totalSize; + /* The block size. */ + ULONG blockSize; + /* The amount still left to copy. */ + ULONG stillToCopy; + /* Attach state. */ + KPH_ATTACH_STATE attachState; + /* The current source address. */ + PVOID sourceAddress; + /* The current target address. */ + PVOID targetAddress; + /* Whether the pages have been locked. */ + BOOLEAN pagesLocked; + /* Whether we are currently copying. */ + BOOLEAN copying = FALSE; + /* Whether we are currently probing. */ + BOOLEAN probing = FALSE; + /* Whether we are currently mapping. */ + BOOLEAN mapping = FALSE; + /* Whether we have the bad address. */ + BOOLEAN haveBadAddress; + /* The bad address of the exception. */ + ULONG_PTR badAddress; + + sourceAddress = FromAddress; + targetAddress = ToAddress; + + totalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE; + + if (BufferLength <= totalSize) + totalSize = BufferLength; + + stillToCopy = BufferLength; + blockSize = totalSize; + + while (stillToCopy) + { + /* If we're at the last copy block, copy the remaining bytes instead + of the whole block size. */ + if (stillToCopy < blockSize) + blockSize = stillToCopy; + + /* Reset state. */ + mappedAddress = NULL; + pagesLocked = FALSE; + copying = FALSE; + + KphAttachProcess(FromProcess, &attachState); + + __try + { + /* Probe only if this is the first time. */ + if ((sourceAddress == FromAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForRead(sourceAddress, BufferLength, 1); + probing = FALSE; + } + + /* Initialize the MDL. */ + MmInitializeMdl(mdl, sourceAddress, blockSize); + MmProbeAndLockPages(mdl, AccessMode, IoReadAccess); + pagesLocked = TRUE; + + /* Map the pages. */ + mappedAddress = MmMapLockedPagesSpecifyCache( + mdl, + KernelMode, + MmCached, + NULL, + FALSE, + HighPagePriority + ); + + if (!mappedAddress) + { + /* Insufficient resources; exit. */ + mapping = TRUE; + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + + KphDetachProcess(&attachState); + + /* Attach to the target process and copy the mapped contents. */ + KphAttachProcess(ToProcess, &attachState); + + /* Probe only if this is the first time. */ + if ((targetAddress == ToAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForWrite(targetAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the data. */ + copying = TRUE; + memcpy(targetAddress, mappedAddress, blockSize); + } + __except (MiGetExceptionInfo( + GetExceptionInformation(), + &haveBadAddress, + &badAddress + )) + { + KphDetachProcess(&attachState); + + /* If we mapped the pages, unmap them. */ + if (mappedAddress) + MmUnmapLockedPages(mappedAddress, mdl); + + /* If we locked the pages, unlock them. */ + if (pagesLocked) + MmUnlockPages(mdl); + + /* If we failed when probing or mapping, return the error code. */ + if (probing || mapping) + return GetExceptionCode(); + + /* Otherwise, give the caller the number of bytes we copied. */ + *ReturnLength = BufferLength - stillToCopy; + + /* If we were copying, we can probably get the exact + number of bytes copied. */ + if (copying && haveBadAddress) + *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); + + return STATUS_PARTIAL_COPY; + } + + KphDetachProcess(&attachState); + MmUnmapLockedPages(mappedAddress, mdl); + MmUnlockPages(mdl); + + stillToCopy -= blockSize; + sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); + targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); + } + + *ReturnLength = BufferLength; + + return STATUS_SUCCESS; +} + +/* MiDoPoolCopy + * + * Copies virtual memory from the source process to the target process + * using either a pool allocation or a stack buffer. + */ +NTSTATUS MiDoPoolCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + /* The size of the pool-allocated buffer. */ + ULONG allocSize = MI_MAX_TRANSFER_SIZE; + /* The stack-based buffer. */ + CHAR stackBuffer[MI_COPY_STACK_SIZE]; + /* The buffer - could be from the pool or could be the stack buffer. */ + PVOID buffer = NULL; + /* The block size - should be the same as the allocated size. */ + ULONG blockSize; + /* The amount still left to copy. */ + ULONG stillToCopy; + /* Attach state. */ + KPH_ATTACH_STATE attachState; + /* The current source address. */ + PVOID sourceAddress; + /* The current target address. */ + PVOID targetAddress; + /* Whether we are currently copying. */ + BOOLEAN copying = FALSE; + /* Whether we are currently probing. */ + BOOLEAN probing = FALSE; + /* Whether we have the bad address. */ + BOOLEAN haveBadAddress; + /* The bad address of the exception. */ + ULONG_PTR badAddress; + + sourceAddress = FromAddress; + targetAddress = ToAddress; + + /* Don't allocate a buffer larger than the amount we're about to copy. */ + if (allocSize > BufferLength) + allocSize = BufferLength; + + /* If we're copying MI_COPY_STACK_SIZE bytes or less, use the stack buffer. */ + if (BufferLength <= MI_COPY_STACK_SIZE) + { + buffer = stackBuffer; + } + else + { + /* Keep on trying to allocate a buffer, halving the size each time + we fail. */ + while (TRUE) + { + buffer = ExAllocatePoolWithTag(NonPagedPool, allocSize, TAG_POOL_COPY); + + /* Stop trying if we got a buffer. */ + if (buffer) + break; + + /* Otherwise, halve the size and try again. */ + allocSize /= 2; + /* Could we use the stack buffer? */ + if (allocSize <= MI_COPY_STACK_SIZE) + { + buffer = stackBuffer; + break; + } + } + } + + stillToCopy = BufferLength; + blockSize = allocSize; + + /* Perform the copy in blocks of blockSize. */ + while (stillToCopy) + { + /* If we're at the last copy block, copy the remaining bytes instead + of the whole block size. */ + if (stillToCopy < blockSize) + blockSize = stillToCopy; + + copying = FALSE; + KphAttachProcess(FromProcess, &attachState); + + __try + { + /* Probe before reading the source contents. */ + /* Probe only if this is the first time. */ + if ((sourceAddress == FromAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForRead(sourceAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the source contents to the buffer. */ + memcpy(buffer, sourceAddress, blockSize); + KphDetachProcess(&attachState); + + /* Probe before writing. */ + KphAttachProcess(ToProcess, &attachState); + + /* Probe only if this is the first time. */ + if ((targetAddress == ToAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForWrite(targetAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the buffer contents to the destination. */ + copying = TRUE; + memcpy(targetAddress, buffer, blockSize); + } + __except (MiGetExceptionInfo( + GetExceptionInformation(), + &haveBadAddress, + &badAddress + )) + { + KphDetachProcess(&attachState); + + /* Free the allocated buffer if needed. */ + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, TAG_POOL_COPY); + + /* If we were probing an address, return the error code. */ + if (probing) + return GetExceptionCode(); + + /* Otherwise, give the caller the number of bytes we copied. */ + *ReturnLength = BufferLength - stillToCopy; + + /* If we were copying, we can probably get the exact + number of bytes copied. */ + if (copying && haveBadAddress) + *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); + + return STATUS_PARTIAL_COPY; + } + + KphDetachProcess(&attachState); + + stillToCopy -= blockSize; + sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); + targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); + } + + /* Free the buffer if it wasn't stack-allocated. */ + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, TAG_POOL_COPY); + + *ReturnLength = BufferLength; + + return STATUS_SUCCESS; +} + +ULONG MiGetExceptionInfo( + __in PEXCEPTION_POINTERS ExceptionInfo, + __out PBOOLEAN HaveBadAddress, + __out PULONG_PTR BadAddress + ) +{ + PEXCEPTION_RECORD exceptionRecord; + + *HaveBadAddress = FALSE; + exceptionRecord = ExceptionInfo->ExceptionRecord; + + if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) + { + if (exceptionRecord->NumberParameters > 1) + { + /* We have the address. */ + *HaveBadAddress = TRUE; + *BadAddress = exceptionRecord->ExceptionInformation[1]; + } + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +NTSTATUS MmCopyVirtualMemory( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processToLock = FromProcess; + + if (!BufferLength) + return STATUS_SUCCESS; + + /* If we're copying from the current process, lock the target. */ + if (processToLock == PsGetCurrentProcess()) + processToLock = ToProcess; + + /* Prevent the process from terminating. */ + if (!KphAcquireProcessRundownProtection(processToLock)) + return STATUS_PROCESS_IS_TERMINATING; + + /* If the amount we're trying to copy is over the threshold + for MiDoPoolCopy, use MiDoMappedCopy. */ + if (BufferLength > MM_POOL_COPY_THRESHOLD) + { + status = MiDoMappedCopy( + FromProcess, + FromAddress, + ToProcess, + ToAddress, + BufferLength, + AccessMode, + ReturnLength + ); + } + else + { + status = MiDoPoolCopy( + FromProcess, + FromAddress, + ToProcess, + ToAddress, + BufferLength, + AccessMode, + ReturnLength + ); + } + + /* Allow the process to terminate. */ + KphReleaseProcessRundownProtection(processToLock); + + return status; +} diff --git a/2.x/trunk/KProcessHacker/ob.c b/2.x/trunk/KProcessHacker/ob.c new file mode 100644 index 000000000..e5325b95e --- /dev/null +++ b/2.x/trunk/KProcessHacker/ob.c @@ -0,0 +1,872 @@ +/* + * Process Hacker Driver - + * object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/ob.h" + +BOOLEAN KphpQueryProcessHandlesEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_QUERY_PROCESS_HANDLES_DATA Context + ); + +BOOLEAN KphpSetHandleGrantedAccessEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphDuplicateObject) +#pragma alloc_text(PAGE, ObDuplicateObject) +#endif + +/* This attribute is now stored in the GrantedAccess field. */ +ULONG ObpAccessProtectCloseBit = 0x80000000; + +/* KphDuplicateObject + * + * Duplicates a handle from the source process to the target process. + */ +NTSTATUS KphDuplicateObject( + __in HANDLE SourceProcessHandle, + __in HANDLE SourceHandle, + __in_opt HANDLE TargetProcessHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS sourceProcess = NULL; + PEPROCESS targetProcess = NULL; + HANDLE targetHandle; + + if (TargetHandle && AccessMode != KernelMode) + { + __try + { + ProbeForWrite(TargetHandle, sizeof(HANDLE), 1); + *TargetHandle = NULL; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + status = ObReferenceObjectByHandle( + SourceProcessHandle, + PROCESS_DUP_HANDLE, + *PsProcessType, + KernelMode, + &sourceProcess, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Target handle is optional. */ + if (TargetProcessHandle) + { + status = ObReferenceObjectByHandle( + TargetProcessHandle, + PROCESS_DUP_HANDLE, + *PsProcessType, + KernelMode, + &targetProcess, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + } + + /* Fix the source handle if the source process is + * the system process. + */ + if (sourceProcess == PsInitialSystemProcess) + MakeKernelHandle(SourceHandle); + + /* Call the internal function. */ + status = ObDuplicateObject( + sourceProcess, + targetProcess, + SourceHandle, + &targetHandle, + DesiredAccess, + HandleAttributes, + Options, + AccessMode + ); + + if (TargetHandle) + { + __try + { + *TargetHandle = targetHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = STATUS_ACCESS_VIOLATION; + } + } + + ObDereferenceObject(sourceProcess); + if (targetProcess) + ObDereferenceObject(targetProcess); + + return status; +} + +/* KphEnumProcessHandleTable + * + * Enumerates the handles in the specified process' handle table. + */ +BOOLEAN KphEnumProcessHandleTable( + __in PEPROCESS Process, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ) +{ + BOOLEAN result = FALSE; + PHANDLE_TABLE handleTable = NULL; + + handleTable = ObReferenceProcessHandleTable(Process); + + if (!handleTable) + return FALSE; + + result = ExEnumHandleTable( + handleTable, + EnumHandleProcedure, + Context, + Handle + ); + ObDereferenceProcessHandleTable(Process); + + return result; +} + +/* KphGetObjectTypeNt + * + * Gets the type of an object. + */ +POBJECT_TYPE KphGetObjectTypeNt( + __in PVOID Object + ) +{ + /* XP to Vista: A pointer to the object type is + * stored in the object header. + */ + if ( + WindowsVersion >= WINDOWS_XP && + WindowsVersion <= WINDOWS_VISTA + ) + { + return OBJECT_TO_OBJECT_HEADER(Object)->Type; + } + /* Seven and above: An index to an internal object type + * table is stored in the object header. Luckily we have + * a new exported function, ObGetObjectType, to get + * the object type. + */ + else if (WindowsVersion >= WINDOWS_7) + { + return ObGetObjectType(Object); + } + else + { + return NULL; + } +} + +/* KphOpenDirectoryObject + * + * Opens a directory object. + */ +NTSTATUS KphOpenDirectoryObject( + __out PHANDLE DirectoryObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + DirectoryObjectHandle, + DesiredAccess, + ObjectAttributes, + *ObDirectoryObjectType, + AccessMode + ); +} + +/* KphOpenNamedObject + * + * Opens a named object. + */ +NTSTATUS KphOpenNamedObject( + __out PHANDLE ObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + HANDLE objectHandle; + UNICODE_STRING capturedObjectName; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + + if (!ObjectAttributes) + return STATUS_INVALID_PARAMETER; + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ObjectHandle, sizeof(HANDLE), 1); + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1); + + if (ObjectAttributes->ObjectName) + KphProbeForReadUnicodeString(ObjectAttributes->ObjectName); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + __try + { + /* Copy the object attributes structure. */ + memcpy(&objectAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Verify parameters. */ + if (!objectAttributes.ObjectName) + return STATUS_INVALID_PARAMETER; + + /* Make sure the root directory handle isn't a kernel handle if + * we're from user-mode. + */ + if (AccessMode != KernelMode && IsKernelHandle(objectAttributes.RootDirectory)) + return STATUS_INVALID_PARAMETER; + + /* Capture the ObjectName string. */ + status = KphCaptureUnicodeString( + objectAttributes.ObjectName, + &capturedObjectName + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Set the new string in the object attributes. */ + objectAttributes.ObjectName = &capturedObjectName; + /* Make sure the SecurityDescriptor and SecurityQualityOfService fields are NULL + * since we haven't probed them. + */ + objectAttributes.SecurityDescriptor = NULL; + objectAttributes.SecurityQualityOfService = NULL; + + /* Open the object. */ + status = ObOpenObjectByName( + &objectAttributes, + ObjectType, + KernelMode, + NULL, + DesiredAccess, + NULL, + &objectHandle + ); + + /* Free the captured ObjectName. */ + KphFreeCapturedUnicodeString(&capturedObjectName); + + /* Pass the handle back. */ + __try + { + *ObjectHandle = objectHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + return status; +} + +/* KphOpenType + * + * Opens a type object. + */ +NTSTATUS KphOpenType( + __out PHANDLE TypeHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + TypeHandle, + 0, + ObjectAttributes, + *ObTypeObjectType, + AccessMode + ); +} + +/* KphQueryFileObjectName + * + * Queries the name of a file object. + * + * Technique from YAPM. + */ +NTSTATUS KphQueryNameFileObject( + __in PFILE_OBJECT FileObject, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength; + PCHAR objectName; + ULONG usedLength; + ULONG subNameLength; + PFILE_OBJECT relatedFileObject; + + /* We need at least the size of UNICODE_STRING to + * continue. + */ + if (BufferLength < sizeof(UNICODE_STRING)) + { + *ReturnLength = sizeof(UNICODE_STRING); + + return STATUS_BUFFER_TOO_SMALL; + } + + /* Assume failure. */ + Buffer->Length = 0; + /* We will place the object name directly after the + * UNICODE_STRING structure in the buffer. + */ + Buffer->Buffer = (PWSTR)PTR_ADD_OFFSET(Buffer, sizeof(UNICODE_STRING)); + /* Retain a local pointer to the object name so we + * can manipulate the pointer. + */ + objectName = (PCHAR)Buffer->Buffer; + /* A variable that keeps track of how much space we + * have used. + */ + usedLength = sizeof(UNICODE_STRING); + + /* Check if the file object has an associated device + * (e.g. "\Device\NamedPipe", "\Device\Mup"). We can + * use the user-supplied buffer for this since if the + * buffer isn't big enough, we can't proceed anyway + * (we are going to use the name). + */ + if (FileObject->DeviceObject) + { + status = ObQueryNameString( + FileObject->DeviceObject, + (POBJECT_NAME_INFORMATION)Buffer, + BufferLength, + &returnLength + ); + + if (!NT_SUCCESS(status)) + { + *ReturnLength = returnLength; + + return status; + } + + /* The UNICODE_STRING in the buffer is now filled in. + * We will append to the object name later, so + * we need to fix the object name pointer by adding + * the length, in bytes, of the device name string we + * just got. + */ + objectName += Buffer->Length; + usedLength += Buffer->Length; + } + + /* Check if the file object has a file name component. If not, + * we can't do anything else, so we just return the name we + * have already. + */ + if (!FileObject->FileName.Buffer) + { + *ReturnLength = usedLength; + + return STATUS_SUCCESS; + } + + /* The file object has a name. We need to walk up the file + * object tree and append the names of the related file + * objects in reverse order. This means we need to calculate + * the total length first. + */ + + relatedFileObject = FileObject; + subNameLength = 0; + + do + { + subNameLength += relatedFileObject->FileName.Length; + + /* Avoid infinite loops. */ + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + usedLength += subNameLength; + + /* Check if we have enough space to write the whole thing. */ + if (usedLength > BufferLength) + { + *ReturnLength = usedLength; + + return STATUS_BUFFER_TOO_SMALL; + } + + /* We're ready to begin copying the names. */ + + /* Add the name length because we're copying in reverse order. */ + objectName += subNameLength; + + relatedFileObject = FileObject; + + do + { + objectName -= relatedFileObject->FileName.Length; + memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length); + + /* Avoid infinite loops. */ + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + /* Update the length. */ + Buffer->Length += (USHORT)subNameLength; + + /* Pass the return length back. */ + *ReturnLength = usedLength; + + return STATUS_SUCCESS; +} + +/* KphQueryObjectName + * + * Queries the name of an object. + */ +NTSTATUS KphQueryNameObject( + __in PVOID Object, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + POBJECT_TYPE objectType; + + objectType = KphGetObjectTypeNt(Object); + + /* Check if we are going to hang when querying the object, and use + * the special file object query function if needed. + */ + if ( + (objectType == *IoFileObjectType) && + (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters) + ) + { + status = KphQueryNameFileObject((PFILE_OBJECT)Object, Buffer, BufferLength, ReturnLength); + } + else + { + status = ObQueryNameString(Object, (POBJECT_NAME_INFORMATION)Buffer, BufferLength, ReturnLength); + } + + return status; +} + +/* KphQueryProcessHandles + * + * Queries a process handle table. + */ +NTSTATUS KphQueryProcessHandles( + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + BOOLEAN result; + PEPROCESS processObject; + OBP_QUERY_PROCESS_HANDLES_DATA context; + + /* Probe buffer contents. */ + if (AccessMode != KernelMode) + { + __try + { + if (Buffer) + ProbeForWrite(Buffer, BufferLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Reference the process object. */ + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_QUERY_INFORMATION, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the enumeration context. */ + context.Buffer = Buffer; + context.BufferLength = BufferLength; + context.CurrentIndex = 0; + context.Status = STATUS_SUCCESS; + + /* Enumerate the handles. */ + result = KphEnumProcessHandleTable( + processObject, + KphpQueryProcessHandlesEnumCallback, + &context, + NULL + ); + ObDereferenceObject(processObject); + + /* Write the number of handles (if we have a buffer). */ + if ( + Buffer && + BufferLength >= sizeof(ULONG) + ) + { + __try + { + Buffer->HandleCount = context.CurrentIndex; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Supply the return length if the caller wanted it. */ + if (ReturnLength) + { + __try + { + /* CurrentIndex should contain the number of handles, so we simply multiply it + by the size of PROCESS_HANDLE. */ + *ReturnLength = sizeof(ULONG) + context.CurrentIndex * sizeof(PROCESS_HANDLE); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + return context.Status; +} + +/* KphpQueryProcessHandlesEnumCallback + * + * The callback for KphEnumProcessHandleTable, used by + * KphQueryProcessHandles. + */ +BOOLEAN KphpQueryProcessHandlesEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_QUERY_PROCESS_HANDLES_DATA Context + ) +{ + PROCESS_HANDLE handleInfo; + PPROCESS_HANDLE_INFORMATION buffer = Context->Buffer; + ULONG i; + + handleInfo.Handle = Handle; + handleInfo.Object = ObpDecodeObject(HandleTableEntry->Object); + handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess); + handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry); + + /* Increment the index regardless of whether the information will be written; + this will allow KphQueryProcessHandles to report the correct return length. */ + i = Context->CurrentIndex++; + + /* Only write if we have a buffer and have not exceeded the buffer length. */ + if ( + buffer && + (sizeof(ULONG) + Context->CurrentIndex * sizeof(PROCESS_HANDLE)) <= Context->BufferLength + ) + { + __try + { + buffer->Handles[i] = handleInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* Report an error. */ + if (Context->Status == STATUS_SUCCESS) + Context->Status = GetExceptionCode(); + } + } + else + { + /* Report that the buffer is too small. */ + if (Context->Status == STATUS_SUCCESS) + Context->Status = STATUS_BUFFER_TOO_SMALL; + } + + return FALSE; +} + +/* KphSetHandleGrantedAccess + * + * Sets the granted access of a handle. + */ +NTSTATUS KphSetHandleGrantedAccess( + __in PEPROCESS Process, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ) +{ + BOOLEAN result; + OBP_SET_HANDLE_GRANTED_ACCESS_DATA context; + + context.Handle = Handle; + context.GrantedAccess = GrantedAccess; + + result = KphEnumProcessHandleTable( + Process, + KphpSetHandleGrantedAccessEnumCallback, + &context, + NULL + ); + + return result ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +/* KphpSetHandleGrantedAccessEnumCallback + * + * The callback for KphEnumProcessHandleTable, used by + * KphSetHandleGrantedAccess. + */ +BOOLEAN KphpSetHandleGrantedAccessEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context + ) +{ + if (Handle != Context->Handle) + return FALSE; + + HandleTableEntry->GrantedAccess = Context->GrantedAccess; + + return TRUE; +} + +/* ObDereferenceProcessHandleTable + * + * Allows the process to terminate. + */ +VOID ObDereferenceProcessHandleTable( + __in PEPROCESS Process + ) +{ + KphReleaseProcessRundownProtection(Process); +} + +/* ObDuplicateObject + * + * Duplicates a handle from the source process to the target process. + * WARNING: This does not actually duplicate a handle. It simply + * re-opens an object in another process. + */ +NTSTATUS ObDuplicateObject( + __in PEPROCESS SourceProcess, + __in_opt PEPROCESS TargetProcess, + __in HANDLE SourceHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN sourceAttached = FALSE; + BOOLEAN targetAttached = FALSE; + KAPC_STATE apcState; + PVOID object; + HANDLE objectHandle; + + /* Validate the parameters */ + if (!TargetProcess || !TargetHandle) + { + if (!(Options & DUPLICATE_CLOSE_SOURCE)) + return STATUS_INVALID_PARAMETER; + } + + /* Check if we need to attach to the source process */ + if (SourceProcess != PsGetCurrentProcess()) + { + KeStackAttachProcess(SourceProcess, &apcState); + sourceAttached = TRUE; + } + + /* If the caller wants us to close the source handle, do it now */ + if (Options & DUPLICATE_CLOSE_SOURCE) + { + status = NtClose(SourceHandle); + if (sourceAttached) + KeUnstackDetachProcess(&apcState); + + return status; + } + + /* Reference the object and detach from the source process */ + status = ObReferenceObjectByHandle( + SourceHandle, + 0, + NULL, + KernelMode, + &object, + NULL + ); + if (sourceAttached) + KeUnstackDetachProcess(&apcState); + + if (!NT_SUCCESS(status)) + return status; + + /* Check if we need to attach to the target process */ + if (TargetProcess != PsGetCurrentProcess()) + { + KeStackAttachProcess(TargetProcess, &apcState); + targetAttached = TRUE; + } + + /* Open the object and detach from the target process */ + { + POBJECT_TYPE objectType = KphGetObjectTypeNt(object); + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + if (!objectType && AccessMode != KernelMode) + { + status = STATUS_INVALID_HANDLE; + goto OpenObjectEnd; + } + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(objectType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + goto OpenObjectEnd; + + accessState.PreviouslyGrantedAccess |= 0xffffffff; /* HACK, doesn't work properly */ + accessState.RemainingDesiredAccess = 0; + + status = ObOpenObjectByPointer( + object, + HandleAttributes, + &accessState, + DesiredAccess, + objectType, + KernelMode, + &objectHandle + ); + SeDeleteAccessState(&accessState); + } + +OpenObjectEnd: + ObDereferenceObject(object); + + if (targetAttached) + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + *TargetHandle = objectHandle; + else + *TargetHandle = NULL; + + return status; +} + +/* ObReferenceProcessHandleTable + * + * Prevents the process from terminating and returns a pointer + * to its handle table. + */ +PHANDLE_TABLE ObReferenceProcessHandleTable( + __in PEPROCESS Process + ) +{ + PHANDLE_TABLE handleTable = NULL; + + if (KphAcquireProcessRundownProtection(Process)) + { + handleTable = *(PHANDLE_TABLE *)KVOFF(Process, OffEpObjectTable); + + if (!handleTable) + KphReleaseProcessRundownProtection(Process); + } + + return handleTable; +} diff --git a/2.x/trunk/KProcessHacker/protect.c b/2.x/trunk/KProcessHacker/protect.c new file mode 100644 index 000000000..406c32019 --- /dev/null +++ b/2.x/trunk/KProcessHacker/protect.c @@ -0,0 +1,457 @@ +/* + * Process Hacker Driver - + * process protection + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/protect.h" + +BOOLEAN KphpIsAccessAllowed( + __in PVOID Object, + __in KPROCESSOR_MODE AccessMode, + __in ACCESS_MASK DesiredAccess + ); + +BOOLEAN KphpIsCurrentProcessProtected(); + +VOID KphpProtectRemoveEntry( + __in PKPH_PROCESS_ENTRY Entry + ); + +/* ProtectedProcessRundownProtect + * + * Rundown protection making sure this module doesn't deinitialize before all hook targets + * have finished executing and no one is accessing the lookaside list. + */ +static EX_RUNDOWN_REF ProtectedProcessRundownProtect; +/* ProtectedProcessListHead + * + * The head of the process protection linked list. Each entry stores protection + * information for a process. + */ +static LIST_ENTRY ProtectedProcessListHead; +/* ProtectedProcessListLock + * + * The spinlock which protects all accesses to the protected process list (even + * the individual entries) + */ +static KSPIN_LOCK ProtectedProcessListLock; +/* ProtectedProcessLookasideList + * + * The lookaside list for protected process entries. + */ +static NPAGED_LOOKASIDE_LIST ProtectedProcessLookasideList; + +static KPH_OB_OPEN_HOOK ProcessOpenHook = { 0 }; +static KPH_OB_OPEN_HOOK ThreadOpenHook = { 0 }; + +/* KphProtectInit + * + * Initializes process protection. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphProtectInit() +{ + NTSTATUS status; + + /* Initialize rundown protection. */ + ExInitializeRundownProtection(&ProtectedProcessRundownProtect); + /* Initialize list structures. */ + InitializeListHead(&ProtectedProcessListHead); + KeInitializeSpinLock(&ProtectedProcessListLock); + ExInitializeNPagedLookasideList( + &ProtectedProcessLookasideList, + NULL, + NULL, + 0, + sizeof(KPH_PROCESS_ENTRY), + TAG_PROTECTION_ENTRY, + 0 + ); + + /* Hook various functions. */ + /* Hooking the open procedure calls for processes and threads allows + * us to intercept handle creation/duplication/inheritance. */ + KphInitializeObOpenHook(&ProcessOpenHook, *PsProcessType, KphNewOpenProcedure51, KphNewOpenProcedure60); + if (!NT_SUCCESS(status = KphObOpenHook(&ProcessOpenHook))) + return status; + KphInitializeObOpenHook(&ThreadOpenHook, *PsThreadType, KphNewOpenProcedure51, KphNewOpenProcedure60); + if (!NT_SUCCESS(status = KphObOpenHook(&ThreadOpenHook))) + return status; + + return STATUS_SUCCESS; +} + +/* KphProtectDeinit + * + * Removes process protection and frees associated structures. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphProtectDeinit() +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + LARGE_INTEGER waitLi; + + /* Unhook. */ + status = KphObOpenUnhook(&ProcessOpenHook); + status = KphObOpenUnhook(&ThreadOpenHook); + + /* Wait for all activity to finish. */ + ExWaitForRundownProtectionRelease(&ProtectedProcessRundownProtect); + /* Wait for a bit (some regions of hook target functions + are NOT guarded by rundown protection, e.g. + prologues and epilogues). */ + waitLi.QuadPart = KPH_REL_TIMEOUT_IN_SEC(1); + KeDelayExecutionThread(KernelMode, FALSE, &waitLi); + + /* Free all process protection entries. */ + ExDeleteNPagedLookasideList(&ProtectedProcessLookasideList); + + return status; +} + +/* KphNewOpenProcedure51 + * + * New process/thread open procedure for NT 5.1. + */ +NTSTATUS NTAPI KphNewOpenProcedure51( + __in OB_OPEN_REASON OpenReason, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + /* Simply call the 6.0 open procedure. */ + /* NOTE: GrantedAccess is always 0 on XP... */ + return KphNewOpenProcedure60( + OpenReason, + /* Assume worst case. */ + UserMode, + Process, + Object, + GrantedAccess, + HandleCount + ); +} + +/* KphNewOpenProcedure60 + * + * New process/thread open procedure for NT 6.0 and 6.1. + */ +NTSTATUS NTAPI KphNewOpenProcedure60( + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN accessAllowed = TRUE; + + /* Prevent the driver from unloading while this routine is executing. */ + if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect)) + { + /* Should never happen. */ + return STATUS_INTERNAL_ERROR; + } + + accessAllowed = KphpIsAccessAllowed( + Object, + AccessMode, + /* Assume worst case if granted access not available. */ + !GrantedAccess ? (ACCESS_MASK)-1 : GrantedAccess + ); + + if (accessAllowed) + { + POBJECT_TYPE objectType = KphGetObjectTypeNt(Object); + + /* Call the original open procedure. There shouldn't be any for Windows XP, + * while on Windows Vista and 7 it is used for implementing protected + * processes (Big Content's DRM protection, not KProcessHacker's protection). + */ + status = KphObOpenCall( + objectType == *PsProcessType ? &ProcessOpenHook : &ThreadOpenHook, + OpenReason, + AccessMode, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else + { + dprintf("KphNewOpenProcedure60: Access denied.\n"); + status = STATUS_ACCESS_DENIED; + } + + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + return status; +} + +/* KphProtectAddEntry + * + * Protects the specified process. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +PKPH_PROCESS_ENTRY KphProtectAddEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __in LOGICAL AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ) +{ + KIRQL oldIrql; + PKPH_PROCESS_ENTRY entry; + + /* Prevent the lookaside list from being freed. */ + if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect)) + return NULL; + + entry = ExAllocateFromNPagedLookasideList(&ProtectedProcessLookasideList); + /* Lookaside list no longer needed. */ + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + if (!entry) + return NULL; + + entry->Process = Process; + entry->CreatorProcess = PsGetCurrentProcess(); + entry->Tag = Tag; + entry->AllowKernelMode = AllowKernelMode; + entry->ProcessAllowMask = ProcessAllowMask; + entry->ThreadAllowMask = ThreadAllowMask; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + InsertHeadList(&ProtectedProcessListHead, &entry->ListEntry); + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return entry; +} + +/* KphProtectFindEntry + * + * Finds process protection data. + * + * Thread safety: Full/Limited. The returned pointer is not guaranteed to + * point to a valid process entry. However, the copied entry is safe to + * read. + * IRQL: <= DISPATCH_LEVEL + */ +PKPH_PROCESS_ENTRY KphProtectFindEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy + ) +{ + KIRQL oldIrql; + PLIST_ENTRY entry = ProtectedProcessListHead.Flink; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + + while (entry != &ProtectedProcessListHead) + { + PKPH_PROCESS_ENTRY processEntry = + CONTAINING_RECORD(entry, KPH_PROCESS_ENTRY, ListEntry); + + if ( + (Process != NULL && processEntry->Process == Process) || + (Tag != NULL && processEntry->Tag == Tag) + ) + { + /* Copy the entry if requested. */ + if (ProcessEntryCopy) + memcpy(ProcessEntryCopy, processEntry, sizeof(KPH_PROCESS_ENTRY)); + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return processEntry; + } + + entry = entry->Flink; + } + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return NULL; +} + +/* KphProtectRemoveByProcess + * + * Removes protection from the specified process. + * + * Thread safety: Limited. Callers must synchronize remove calls such + * as KphProtectRemoveByProcess and KphProtectRemoveByTag. + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphProtectRemoveByProcess( + __in PEPROCESS Process + ) +{ + PKPH_PROCESS_ENTRY entry = KphProtectFindEntry(Process, NULL, NULL); + + if (!entry) + return FALSE; + + KphpProtectRemoveEntry(entry); + + return TRUE; +} + +/* KphProtectRemoveByTag + * + * Removes protection from all processes with the specified tag. + * + * Thread safety: Limited. Callers must synchronize remove calls such + * as KphProtectRemoveByProcess and KphProtectRemoveByTag. + * IRQL: <= DISPATCH_LEVEL + */ +ULONG KphProtectRemoveByTag( + __in HANDLE Tag + ) +{ + KIRQL oldIrql; + ULONG count = 0; + PKPH_PROCESS_ENTRY entry; + + /* Keep removing entries until we can't find any more. */ + while (entry = KphProtectFindEntry(NULL, Tag, NULL)) + { + KphpProtectRemoveEntry(entry); + count++; + } + + return count; +} + +/* KphpIsAccessAllowed + * + * Checks if the specified access is allowed, according to process + * protection rules. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphpIsAccessAllowed( + __in PVOID Object, + __in KPROCESSOR_MODE AccessMode, + __in ACCESS_MASK DesiredAccess + ) +{ + POBJECT_TYPE objectType; + PEPROCESS processObject; + BOOLEAN isThread = FALSE; + + objectType = KphGetObjectTypeNt(Object); + /* It doesn't matter if it isn't actually a process because we won't be + dereferencing it. */ + processObject = (PEPROCESS)Object; + isThread = objectType == *PsThreadType; + + /* If this is a thread, get its parent process. */ + if (isThread) + processObject = IoThreadToProcess((PETHREAD)Object); + + if ( + processObject != PsGetCurrentProcess() && /* let the caller open its own processes/threads */ + (objectType == *PsProcessType || objectType == *PsThreadType) /* only protect processes and threads */ + ) + { + KPH_PROCESS_ENTRY processEntry; + + /* Search for and copy the corresponding process protection entry. */ + if (KphProtectFindEntry(processObject, NULL, &processEntry)) + { + ACCESS_MASK mask = + isThread ? processEntry.ThreadAllowMask : processEntry.ProcessAllowMask; + + /* The process/thread is protected. Check if the requested access is allowed. */ + if ( + /* check if kernel-mode is exempt from protection */ + !(processEntry.AllowKernelMode && AccessMode == KernelMode) && + /* allow the creator of the rule to bypass protection */ + processEntry.CreatorProcess != PsGetCurrentProcess() && + (DesiredAccess & mask) != DesiredAccess + ) + { + /* Access denied. */ + dprintf( + "%d: Access denied: 0x%08x (%s)\n", + PsGetCurrentProcessId(), + DesiredAccess, + isThread ? "Thread" : "Process" + ); + + return FALSE; + } + } + } + + return TRUE; +} + +/* KphpIsCurrentProcessProtected + * + * Determines whether the current process is protected. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphpIsCurrentProcessProtected() +{ + return KphProtectFindEntry(PsGetCurrentProcess(), NULL, NULL) != NULL; +} + +/* KphpProtectRemoveEntry + * + * Removes and frees process protection data. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +VOID KphpProtectRemoveEntry( + __in PKPH_PROCESS_ENTRY Entry + ) +{ + KIRQL oldIrql; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + RemoveEntryList(&Entry->ListEntry); + + /* Prevent the lookaside list from being destroyed. */ + ExAcquireRundownProtection(&ProtectedProcessRundownProtect); + ExFreeToNPagedLookasideList( + &ProtectedProcessLookasideList, + Entry + ); + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); +} diff --git a/2.x/trunk/KProcessHacker/ps.c b/2.x/trunk/KProcessHacker/ps.c new file mode 100644 index 000000000..c5662d7e3 --- /dev/null +++ b/2.x/trunk/KProcessHacker/ps.c @@ -0,0 +1,1221 @@ +/* + * Process Hacker Driver - + * processes and threads + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/ke.h" +#include "include/ps.h" + +VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +VOID NTAPI KphpExitSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphAssignImpersonationToken) +#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc) +#pragma alloc_text(PAGE, KphDangerousTerminateThread) +#pragma alloc_text(PAGE, KphpExitSpecialApc) +#pragma alloc_text(PAGE, KphGetContextThread) +#pragma alloc_text(PAGE, KphGetProcessId) +#pragma alloc_text(PAGE, KphGetThreadId) +#pragma alloc_text(PAGE, KphGetThreadWin32Thread) +#pragma alloc_text(PAGE, KphOpenProcess) +#pragma alloc_text(PAGE, KphOpenProcessJob) +#pragma alloc_text(PAGE, KphOpenThread) +#pragma alloc_text(PAGE, KphOpenThreadProcess) +#pragma alloc_text(PAGE, KphResumeProcess) +#pragma alloc_text(PAGE, KphSetContextThread) +#pragma alloc_text(PAGE, KphSuspendProcess) +#pragma alloc_text(PAGE, KphResumeProcess) +#pragma alloc_text(PAGE, KphTerminateProcess) +#pragma alloc_text(PAGE, KphTerminateThread) +#pragma alloc_text(PAGE, PsTerminateProcess) +#pragma alloc_text(PAGE, PspTerminateThreadByPointer) +#endif + +/* KphAcquireProcessRundownProtection + * + * Prevents the process from terminating. + */ +BOOLEAN KphAcquireProcessRundownProtection( + __in PEPROCESS Process + ) +{ + return ExAcquireRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect)); +} + +/* KphAssignImpersonationToken + * + * Assigns an impersonation token to the specified thread. + */ +NTSTATUS KphAssignImpersonationToken( + __in HANDLE ThreadHandle, + __in HANDLE TokenHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PsAssignImpersonationToken(threadObject, TokenHandle); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphCaptureStackBackTraceThread + * + * Captures a kernel-mode stack backtrace for the specified thread. + */ +NTSTATUS KphCaptureStackBackTraceThread( + __in HANDLE ThreadHandle, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + /* Reference the thread. */ + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_QUERY_INFORMATION, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Get the stack trace. */ + status = KphpCaptureStackBackTraceThread( + threadObject, + FramesToSkip, + FramesToCapture, + BackTrace, + CapturedFrames, + BackTraceHash, + AccessMode + ); + /* Dereference the thread. */ + ObDereferenceObject(threadObject); + + return status; +} + +/* KphpCaptureStackBackTraceThread + * + * Captures a kernel-mode stack backtrace for the specified thread. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphpCaptureStackBackTraceThread( + __in PETHREAD Thread, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + CAPTURE_BACKTRACE_THREAD_CONTEXT context; + ULONG backTraceSize; + PVOID *backTrace; + + backTraceSize = FramesToCapture * sizeof(PVOID); + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(BackTrace, backTraceSize, 1); + + if (CapturedFrames) + ProbeForWrite(CapturedFrames, sizeof(ULONG), 1); + if (BackTraceHash) + ProbeForWrite(BackTraceHash, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Allocate storage for the stack trace. */ + backTrace = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, backTraceSize, TAG_CAPTURE_STACK_BACKTRACE); + + if (!backTrace) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize the context structure. */ + context.FramesToSkip = FramesToSkip; + context.FramesToCapture = FramesToCapture; + context.BackTrace = backTrace; + + /* Check if we're trying to get a stack trace of the current thread. */ + if (Thread == PsGetCurrentThread()) + { + PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context; + PVOID dummy = NULL; + KIRQL oldIrql; + + context.Local = TRUE; + /* Raise the IRQL to APC_LEVEL to simulate an APC environment. */ + KeRaiseIrql(APC_LEVEL, &oldIrql); + /* Call the APC routine directly. */ + KphpCaptureStackBackTraceThreadSpecialApc( + &context.Apc, + NULL, + NULL, + &contextPtr, + &dummy + ); + /* Lower the IRQL back. */ + KeLowerIrql(oldIrql); + } + else + { + context.Local = FALSE; + /* Initialize the stack trace completed event. */ + KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); + /* Initialize the APC. */ + KeInitializeApc( + &context.Apc, + (PKTHREAD)Thread, + OriginalApcEnvironment, + KphpCaptureStackBackTraceThreadSpecialApc, + NULL, + NULL, + KernelMode, + NULL + ); + /* Queue the APC. */ + if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) + { + /* Wait for the APC to complete. */ + status = KeWaitForSingleObject( + &context.CompletedEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) + { + ASSERT(context.CapturedFrames <= FramesToCapture); + + /* Write the information. */ + __try + { + memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); + + if (CapturedFrames) + *CapturedFrames = context.CapturedFrames; + if (BackTraceHash) + *BackTraceHash = context.BackTraceHash; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + /* Free the allocated stack trace storage. */ + ExFreePoolWithTag(backTrace, TAG_CAPTURE_STACK_BACKTRACE); + + return status; +} + +/* KphpCaptureStackBackTraceThreadSpecialApc + * + * The special APC routine which captures a thread stack trace. + */ +VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ) +{ + PCAPTURE_BACKTRACE_THREAD_CONTEXT context = + (PCAPTURE_BACKTRACE_THREAD_CONTEXT)*SystemArgument1; + + /* Capture a stack trace. */ + context->CapturedFrames = KphCaptureStackBackTrace( + context->FramesToSkip, + context->FramesToCapture, + 0, + context->BackTrace, + &context->BackTraceHash + ); + + if (!context->Local) + { + /* Signal the completed event. */ + KeSetEvent(&context->CompletedEvent, 0, FALSE); + } +} + +/* KphDangerousTerminateThread + * + * Terminates the specified thread by queueing an APC. + */ +NTSTATUS KphDangerousTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + if (!__PspTerminateThreadByPointer) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_TERMINATE, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (threadObject != PsGetCurrentThread()) + { + EXIT_THREAD_CONTEXT context; + + /* Initialize the context structure. */ + context.ExitStatus = ExitStatus; + /* Initialize the completion event. */ + KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); + /* Initialize the APC. */ + KeInitializeApc( + &context.Apc, + (PKTHREAD)threadObject, + OriginalApcEnvironment, + KphpExitSpecialApc, + NULL, + NULL, + KernelMode, + NULL + ); + + /* Queue the APC. */ + if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) + { + /* Wait for the APC to initialize. */ + status = KeWaitForSingleObject( + &context.CompletedEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + ObDereferenceObject(threadObject); + } + else + { + /* Can't terminate self. */ + ObDereferenceObject(threadObject); + return STATUS_CANT_TERMINATE_SELF; + } + + return status; +} + +VOID NTAPI KphpExitSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ) +{ + PEXIT_THREAD_CONTEXT context = + (PEXIT_THREAD_CONTEXT)*SystemArgument1; + NTSTATUS exitStatus; + + /* Get the exit status. */ + exitStatus = context->ExitStatus; + /* That's the best we can do. Once we exit the current thread we can't + * signal the event, so just signal it now. */ + KeSetEvent(&context->CompletedEvent, 0, FALSE); + /* Exit the thread by calling PspTerminateThreadByPointer. */ + PspTerminateThreadByPointer(PsGetCurrentThread(), exitStatus); + /* Should never happen. */ + dfprintf( + "WARNING: Thread was not terminated by PspTerminateThreadByPointer: %d, %#x\n", + PsGetCurrentThreadId(), + PsGetCurrentThread() + ); +} + +/* KphGetContextThread + * + * Gets the context of the specified thread. + */ +NTSTATUS KphGetContextThread( + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_GET_CONTEXT, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PsGetContextThread(threadObject, ThreadContext, AccessMode); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphGetProcessId + * + * Gets the ID of the process referenced by the specified handle. + */ +HANDLE KphGetProcessId( + __in HANDLE ProcessHandle + ) +{ + PEPROCESS processObject; + HANDLE processId; + + if (!NT_SUCCESS(ObReferenceObjectByHandle(ProcessHandle, 0, + *PsProcessType, KernelMode, &processObject, NULL))) + return 0; + + processId = PsGetProcessId(processObject); + ObDereferenceObject(processObject); + + return processId; +} + +/* KphGetThreadId + * + * Gets the ID of the thread referenced by the specified handle, + * and optionally the ID of the thread's process. + */ +HANDLE KphGetThreadId( + __in HANDLE ThreadHandle, + __out_opt PHANDLE ProcessId + ) +{ + PETHREAD threadObject; + CLIENT_ID clientId; + + if (!NT_SUCCESS(ObReferenceObjectByHandle(ThreadHandle, 0, + *PsThreadType, KernelMode, &threadObject, NULL))) + return 0; + + clientId = *(PCLIENT_ID)KVOFF(threadObject, OffEtClientId); + + ObDereferenceObject(threadObject); + + if (ProcessId) + { + *ProcessId = clientId.UniqueProcess; + } + + return clientId.UniqueThread; +} + +/* KphGetThreadWin32Thread + * + * Gets a pointer to the WIN32THREAD structure of the specified thread. + */ +NTSTATUS KphGetThreadWin32Thread( + __in HANDLE ThreadHandle, + __out PVOID *Win32Thread, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + PVOID win32Thread; + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(Win32Thread, sizeof(PVOID), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + win32Thread = PsGetThreadWin32Thread(threadObject); + ObDereferenceObject(threadObject); + + __try + { + *Win32Thread = win32Thread; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + return status; +} + +/* KphOpenProcess + * + * Opens a process. + */ +NTSTATUS KphOpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ) +{ + BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL; + ULONG attributes = ObjectAttributes->Attributes; + NTSTATUS status = STATUS_SUCCESS; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + PEPROCESS processObject = NULL; + PETHREAD threadObject = NULL; + HANDLE processHandle = NULL; + + if (hasObjectName && ClientId) + return STATUS_INVALID_PARAMETER_MIX; + + /* ReactOS code cleared this bit up for me :) */ + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + /* Let's hope our client isn't a virus... */ + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ProcessAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + if (hasObjectName) + { + status = ObOpenObjectByName( + ObjectAttributes, + *PsProcessType, + AccessMode, + &accessState, + 0, + NULL, + &processHandle + ); + SeDeleteAccessState(&accessState); + } + else if (ClientId) + { + if (ClientId->UniqueThread) + { + status = PsLookupProcessThreadByCid(ClientId, &processObject, &threadObject); + } + else + { + status = PsLookupProcessByProcessId(ClientId->UniqueProcess, &processObject); + } + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + status = ObOpenObjectByPointer( + processObject, + attributes, + &accessState, + 0, + *PsProcessType, + AccessMode, + &processHandle + ); + + SeDeleteAccessState(&accessState); + ObDereferenceObject(processObject); + + if (threadObject) + ObDereferenceObject(threadObject); + } + else + { + SeDeleteAccessState(&accessState); + return STATUS_INVALID_PARAMETER_MIX; + } + + if (NT_SUCCESS(status)) + { + *ProcessHandle = processHandle; + } + + return status; +} + +/* KphOpenProcessJob + * + * Opens the specified process' job object. If the process has + * not been assigned to a job object, the function returns + * STATUS_PROCESS_NOT_IN_JOB. + */ +NTSTATUS KphOpenProcessJob( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE JobHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + PVOID jobObject; + HANDLE jobHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsJobType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= JOB_OBJECT_ALL_ACCESS; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, &processObject, 0); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + /* If we have PsGetProcessJob, use it. Otherwise, read the EPROCESS structure. */ + if (PsGetProcessJob) + { + jobObject = PsGetProcessJob(processObject); + } + else + { + jobObject = *(PVOID *)((PCHAR)processObject + OffEpJob); + } + + ObDereferenceObject(processObject); + + if (jobObject == NULL) + { + /* No such job. Output a NULL handle and exit. */ + SeDeleteAccessState(&accessState); + *JobHandle = NULL; + return STATUS_PROCESS_NOT_IN_JOB; + } + + ObReferenceObject(jobObject); + status = ObOpenObjectByPointer( + jobObject, + 0, + &accessState, + 0, + *PsJobType, + AccessMode, + &jobHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(jobObject); + + if (NT_SUCCESS(status)) + *JobHandle = jobHandle; + + return status; +} + +/* KphOpenThread + * + * Opens a thread. + */ +NTSTATUS KphOpenThread( + __out PHANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ) +{ + BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL; + ULONG attributes = ObjectAttributes->Attributes; + NTSTATUS status = STATUS_SUCCESS; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + PETHREAD threadObject = NULL; + HANDLE threadHandle = NULL; + + if (hasObjectName && ClientId) + return STATUS_INVALID_PARAMETER_MIX; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsThreadType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ThreadAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + if (hasObjectName) + { + status = ObOpenObjectByName( + ObjectAttributes, + *PsThreadType, + AccessMode, + &accessState, + 0, + NULL, + &threadHandle + ); + SeDeleteAccessState(&accessState); + } + else if (ClientId) + { + if (ClientId->UniqueProcess) + { + status = PsLookupProcessThreadByCid(ClientId, NULL, &threadObject); + } + else + { + status = PsLookupThreadByThreadId(ClientId->UniqueThread, &threadObject); + } + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + status = ObOpenObjectByPointer( + threadObject, + attributes, + &accessState, + 0, + *PsThreadType, + AccessMode, + &threadHandle + ); + + SeDeleteAccessState(&accessState); + ObDereferenceObject(threadObject); + } + else + { + SeDeleteAccessState(&accessState); + return STATUS_INVALID_PARAMETER_MIX; + } + + if (NT_SUCCESS(status)) + { + *ThreadHandle = threadHandle; + } + + return status; +} + +/* KphOpenThreadProcess + * + * Opens a thread's process. + */ +NTSTATUS KphOpenThreadProcess( + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE ProcessHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + PEPROCESS processObject; + HANDLE processHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ProcessAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle(ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, 0); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + /* Get the process object. */ + processObject = IoThreadToProcess(threadObject); + ObDereferenceObject(threadObject); + + if (processObject == NULL) + { + /* Thread does not have a process (?). */ + SeDeleteAccessState(&accessState); + *ProcessHandle = NULL; + return STATUS_UNSUCCESSFUL; + } + + ObReferenceObject(processObject); + status = ObOpenObjectByPointer( + processObject, + 0, + &accessState, + 0, + *PsProcessType, + AccessMode, + &processHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(processObject); + + if (NT_SUCCESS(status)) + *ProcessHandle = processHandle; + + return status; +} + +/* KphReleaseProcessRundownProtection + * + * Allows the process to terminate. + */ +VOID KphReleaseProcessRundownProtection( + __in PEPROCESS Process + ) +{ + ExReleaseRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect)); +} + +/* KphResumeProcess + * + * Resumes the specified process. + */ +NTSTATUS KphResumeProcess( + __in HANDLE ProcessHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + if (!PsResumeProcess) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_SUSPEND_RESUME, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsResumeProcess(processObject); + ObDereferenceObject(processObject); + + return status; +} + +/* KphSetContextThread + * + * Sets the context of the specified thread. + */ +NTSTATUS KphSetContextThread( + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_SET_CONTEXT, + *PsThreadType, + KernelMode, + &threadObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsSetContextThread(threadObject, ThreadContext, AccessMode); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphSuspendProcess + * + * Suspends the specified process. + */ +NTSTATUS KphSuspendProcess( + __in HANDLE ProcessHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + if (!PsSuspendProcess) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_SUSPEND_RESUME, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsSuspendProcess(processObject); + ObDereferenceObject(processObject); + + return status; +} + +/* KphTerminateProcess + * + * Terminates the specified process. + */ +NTSTATUS KphTerminateProcess( + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_TERMINATE, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + /* Can't terminate ourself. Get user-mode to do it. */ + if (processObject == PsGetCurrentProcess()) + { + ObDereferenceObject(processObject); + return STATUS_CANT_TERMINATE_SELF; + } + + /* If we have located PsTerminateProcess/PspTerminateProcess, + call it. */ + if (__PsTerminateProcess) + { + status = PsTerminateProcess(processObject, ExitStatus); + ObDereferenceObject(processObject); + } + else + { + /* Otherwise, we'll have to call ZwTerminateProcess - most hooks on this function + allow kernel-mode callers through. */ + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + HANDLE newProcessHandle; + + /* We have to open it again because ZwTerminateProcess only accepts kernel handles. */ + clientId.UniqueThread = 0; + clientId.UniqueProcess = PsGetProcessId(processObject); + status = KphOpenProcess(&newProcessHandle, 0x1, &objectAttributes, &clientId, KernelMode); + ObDereferenceObject(processObject); + + if (NT_SUCCESS(status)) + { + status = ZwTerminateProcess(newProcessHandle, ExitStatus); + ZwClose(newProcessHandle); + } + } + + return status; +} + +/* KphTerminateThread + * + * Terminates the specified thread. + */ +NTSTATUS KphTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_TERMINATE, + *PsThreadType, + KernelMode, + &threadObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + if (threadObject != PsGetCurrentThread()) + { + status = PspTerminateThreadByPointer(threadObject, ExitStatus); + ObDereferenceObject(threadObject); + } + else + {/* + ObDereferenceObject(threadObject); + status = PspTerminateThreadByPointer(PsGetCurrentThread(), ExitStatus); */ + /* Leads to bugs, so don't terminate self. */ + ObDereferenceObject(threadObject); + return STATUS_CANT_TERMINATE_SELF; + } + + return status; +} + +/* PsTerminateProcess + * + * Terminates the specified process. If PsTerminateProcess or + * PspTerminateProcess could not be located, the call will fail + * with STATUS_NOT_SUPPORTED. + */ +NTSTATUS PsTerminateProcess( + __in PEPROCESS Process, + __in NTSTATUS ExitStatus + ) +{ + PVOID psTerminateProcess = __PsTerminateProcess; + NTSTATUS status; + + if (!psTerminateProcess) + return STATUS_NOT_SUPPORTED; + +#ifdef _X86_ + if ( + WindowsVersion == WINDOWS_XP || + WindowsVersion == WINDOWS_SERVER_2003 + ) + { + /* PspTerminateProcess on XP and Server 2003 is stdcall. */ + __asm + { + push [ExitStatus] + push [Process] + call [psTerminateProcess] + mov [status], eax + } + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + /* PsTerminateProcess on Vista and above is thiscall. */ + __asm + { + push [ExitStatus] + mov ecx, [Process] + call [psTerminateProcess] + mov [status], eax + } + } + else + { + return STATUS_NOT_SUPPORTED; + } +#else + status = __PsTerminateProcess(Process, ExitStatus); +#endif + + return status; +} + +/* PspTerminateThreadByPointer + * + * Terminates the specified thread. If PspTerminateThreadByPointer + * could not be located, the call will fail with STATUS_NOT_SUPPORTED. + */ +NTSTATUS PspTerminateThreadByPointer( + __in PETHREAD Thread, + __in NTSTATUS ExitStatus + ) +{ + PVOID pspTerminateThreadByPointer = __PspTerminateThreadByPointer; + + if (!pspTerminateThreadByPointer) + return STATUS_NOT_SUPPORTED; + + if (WindowsVersion == WINDOWS_XP) + { + return ((_PspTerminateThreadByPointer51)pspTerminateThreadByPointer)( + Thread, + ExitStatus + ); + } + else if ( + WindowsVersion == WINDOWS_SERVER_2003 || + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + return ((_PspTerminateThreadByPointer52)pspTerminateThreadByPointer)( + Thread, + ExitStatus, + Thread == PsGetCurrentThread() + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} diff --git a/2.x/trunk/KProcessHacker/ref.c b/2.x/trunk/KProcessHacker/ref.c new file mode 100644 index 000000000..eeb0f917a --- /dev/null +++ b/2.x/trunk/KProcessHacker/ref.c @@ -0,0 +1,574 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/refp.h" + +/* A list of all objects created by the object manager. */ +LIST_ENTRY KphObjectListHead; +/* A mutex protecting global data structures. */ +FAST_MUTEX KphObjectListMutex; +/* The object type type. */ +PKPH_OBJECT_TYPE KphObjectTypeObject = NULL; + +/* Whether the object manager is destroying all objects. */ +BOOLEAN KphObjectDeinitializing = FALSE; +/* The work item for deferred object deletes. */ +WORK_QUEUE_ITEM KphObjectDeferDeleteWorkItem; +/* The next object to delete. */ +PKPH_OBJECT_HEADER KphObjectNextToFree = NULL; + +/* KphRefInit + * + * Initializes the KPH object manager. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphRefInit() +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Initialize the object list. */ + InitializeListHead(&KphObjectListHead); + /* Initialize the object list mutex. */ + ExInitializeFastMutex(&KphObjectListMutex); + + /* Initialize the deferred delete work item. */ + ExInitializeWorkItem( + &KphObjectDeferDeleteWorkItem, + KphpDeferDeleteObjectRoutine, + NULL + ); + + /* Create the fundamental object type. */ + status = KphCreateObjectType( + &KphObjectTypeObject, + NonPagedPool, + 0, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Now that the fundamental object type exists, fix it up. */ + KphObjectToObjectHeader(KphObjectTypeObject)->Type = KphObjectTypeObject; + KphObjectTypeObject->NumberOfObjects = 1; + + return status; +} + +/* KphRefDeinit + * + * Frees all objects created by the KPH object manager. + * + * IRQL: = PASSIVE_LEVEL + */ +NTSTATUS KphRefDeinit() +{ + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY currentEntry; + + KphObjectDeinitializing = TRUE; + + /* Acquire the object list mutex to make sure no one else + * modifies the list. */ + ExAcquireFastMutex(&KphObjectListMutex); + + /* Remove and free all objects in the list. */ + while ((currentEntry = RemoveHeadList(&KphObjectListHead)) != &KphObjectListHead) + { + PKPH_OBJECT_HEADER objectHeader = + CONTAINING_RECORD(currentEntry, KPH_OBJECT_HEADER, GlobalObjectListEntry); + + /* Free the object, ignoring its reference count. */ + KphpFreeObject(objectHeader); + } + + /* Release the object list mutex and restore the IRQL. */ + ExReleaseFastMutex(&KphObjectListMutex); + + return STATUS_SUCCESS; +} + +/* KphCreateObject + * + * Allocates a object. + * + * Object: A variable which receives a pointer to the newly allocated object. + * ObjectSize: The size of the object. + * Flags: A combination of flags specifying how the object is to be allocated. + * * KPHOBJ_RAISE_ON_FAIL: An exception will be raised if the object could + * not be allocated. + * * KPHOBJ_PAGED_POOL: The object will be allocated in the paged pool. If + * this flag is specified, KPHOBJ_NONPAGED_POOL cannot be specified. + * * KPHOBJ_NONPAGED_POOL: The object will be allocated in the non-paged pool. + * If this flag is specified, KPHOBJ_PAGED_POOL cannot be specified. + * ObjectType: The type of the object. + * AdditionalReferences: The number of references to add to the object. The + * object will have a reference count of 1 + AdditionalReferences. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphCreateObject( + __out PVOID *Object, + __in SIZE_T ObjectSize, + __in ULONG Flags, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __in_opt LONG AdditionalReferences + ) +{ + PKPH_OBJECT_HEADER objectHeader; + POOL_TYPE poolType; + + /* Check the flags. */ + if ((Flags & KPHOBJ_VALID_FLAGS) != Flags) /* Valid flag mask */ + return STATUS_INVALID_PARAMETER_3; + if ((Flags & KPHOBJ_PAGED_POOL) && (Flags & KPHOBJ_NONPAGED_POOL)) /* Can't be both pools */ + return STATUS_INVALID_PARAMETER_3; + /* The object type is only optional if the fundamental object type + * hasn't been created. */ + if (!ObjectType && KphObjectTypeObject) + return STATUS_INVALID_PARAMETER_4; + /* Make sure the additional reference count isn't negative. */ + if (AdditionalReferences < 0) + return STATUS_INVALID_PARAMETER_5; + + /* Figure out the pool type. If it wasn't specified in Flags, + * get the pool type from the object type. */ + if (Flags & KPHOBJ_PAGED_POOL) + poolType = PagedPool; + else if (Flags & KPHOBJ_NONPAGED_POOL) + poolType = NonPagedPool; + else if (ObjectType) /* May be null if we're creating the fundamental type */ + poolType = ObjectType->DefaultPoolType; + else + poolType = NonPagedPool; + + /* Allocate storage for the object. Note that this includes + * the object header followed by the object body. */ + objectHeader = KphpAllocateObject(ObjectSize, poolType); + + if (!objectHeader) + { + if (Flags & KPHOBJ_RAISE_ON_FAIL) + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + else + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Object type statistics. */ + if (ObjectType) + { + InterlockedIncrement(&ObjectType->NumberOfObjects); + } + + /* Initialize the object header. */ + objectHeader->RefCount = 1 + AdditionalReferences; + objectHeader->Flags = Flags; + objectHeader->Size = ObjectSize; + objectHeader->Type = ObjectType; + + /* Insert the object into the global object list. */ + ExAcquireFastMutex(&KphObjectListMutex); + InsertHeadList(&KphObjectListHead, &objectHeader->GlobalObjectListEntry); + ExReleaseFastMutex(&KphObjectListMutex); + + /* Pass a pointer to the object body back to the caller. */ + *Object = KphObjectHeaderToObject(objectHeader); + + return STATUS_SUCCESS; +} + +/* KphCreateObjectType + * + * Creates an object type. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphCreateObjectType( + __out PKPH_OBJECT_TYPE *ObjectType, + __in POOL_TYPE DefaultPoolType, + __in ULONG Flags, + __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_OBJECT_TYPE objectType; + + /* Check the flags. */ + if ((Flags & KPHOBJTYPE_VALID_FLAGS) != Flags) /* Valid flag mask */ + return STATUS_INVALID_PARAMETER_3; + + /* Create the type object. */ + status = KphCreateObject( + &objectType, + sizeof(KPH_OBJECT_TYPE), + 0, + KphObjectTypeObject, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the type object. */ + objectType->DefaultPoolType = DefaultPoolType; + objectType->Flags = Flags; + objectType->DeleteProcedure = DeleteProcedure; + objectType->NumberOfObjects = 0; + + *ObjectType = objectType; + + return status; +} + +/* KphDereferenceObject + * + * Dereferences the specified object. The object will be freed if + * its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * + * Return value: TRUE if the object was freed, otherwise FALSE. + * + * IRQL: <= APC_LEVEL + */ +BOOLEAN KphDereferenceObject( + __in PVOID Object + ) +{ + return KphDereferenceObjectEx(Object, 1, FALSE) == 0; +} + +/* KphDereferenceObjectDeferDelete + * + * Dereferences the specified object. The object will be freed in + * a worker thread if its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * + * Return value: TRUE if the object was freed, otherwise FALSE. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +BOOLEAN KphDereferenceObjectDeferDelete( + __in PVOID Object + ) +{ + return KphDereferenceObjectEx(Object, 1, TRUE) == 0; +} + +/* KphDereferenceObjectEx + * + * Dereferences the specified object. The object will be freed if + * its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * RefCount: The number of references to remove. + * + * Return value: The new reference count of the object. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool and deletion is being deferred, otherwise <= APC_LEVEL. + */ +LONG KphDereferenceObjectEx( + __in PVOID Object, + __in LONG RefCount, + __in BOOLEAN DeferDelete + ) +{ + PKPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + + /* Make sure we're not subtracting a negative reference count. */ + if (RefCount < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + objectHeader = KphObjectToObjectHeader(Object); + + /* Decrease the reference count. */ + oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount); + + /* Free the object if it has 0 references. */ + if (oldRefCount - RefCount == 0) + { + /* If we are at DISPATCH_LEVEL or higher, the type requests + * us to do so, or the caller requests us to do so, defer + * the deletion. + */ + if ( + DeferDelete || + (objectHeader->Type->Flags & KPHOBJTYPE_PASSIVE_LEVEL_DELETE) || + (KeGetCurrentIrql() > APC_LEVEL) + ) + { + KphpDeferDeleteObject(objectHeader); + } + else + { + /* Free the object. */ + KphpFreeObject(objectHeader); + } + } + + return oldRefCount - RefCount; +} + +/* KphGetObjectType + * + * Gets an object's type. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +PKPH_OBJECT_TYPE KphGetObjectType( + __in PVOID Object + ) +{ + return KphObjectToObjectHeader(Object)->Type; +} + +/* KphReferenceObject + * + * References the specified object. + * + * Object: A pointer to the object to reference. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +VOID KphReferenceObject( + __in PVOID Object + ) +{ + PKPH_OBJECT_HEADER objectHeader; + + objectHeader = KphObjectToObjectHeader(Object); + /* Increment the reference count. */ + InterlockedIncrement(&objectHeader->RefCount); +} + +/* KphReferenceObjectEx + * + * References the specified object. + * + * Object: A pointer to the object to reference. + * RefCount: The number of references to add. + * + * Return value: The new reference count of the object. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +LONG KphReferenceObjectEx( + __in PVOID Object, + __in LONG RefCount + ) +{ + PKPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + + /* Make sure we're not adding a negative reference count. */ + if (RefCount < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + objectHeader = KphObjectToObjectHeader(Object); + /* Increase the reference count. */ + oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, RefCount); + + return oldRefCount + RefCount; +} + +/* KphReferenceObjectSafe + * + * Attempts to reference an object and fails if it is being + * destroyed. + * + * Object: The object to reference if it is not being deleted. + * + * Return value: TRUE if the object was referenced, FALSE if + * it was being deleted and was not referenced. + * + * Remarks: + * This function is useful if a reference to an object is + * held, protected by a mutex, and the delete procedure of + * the object's type attempts to acquire the mutex. If this + * function is called while the mutex is owned, you can + * avoid referencing an object that is being destroyed. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +BOOLEAN KphReferenceObjectSafe( + __in PVOID Object + ) +{ + PKPH_OBJECT_HEADER objectHeader; + BOOLEAN result; + + objectHeader = KphObjectToObjectHeader(Object); + /* Increase the reference count only if it isn't 0 (atomically). */ + result = KphpInterlockedIncrementSafe(&objectHeader->RefCount); + + return result; +} + +/* KphpAllocateObject + * + * Allocates storage for an object. + * + * ObjectSize: The size of the object, excluding the header. + * PoolType: The pool in which to allocate the object. + */ +PKPH_OBJECT_HEADER KphpAllocateObject( + __in SIZE_T ObjectSize, + __in POOL_TYPE PoolType + ) +{ + return ExAllocatePoolWithTag( + PoolType, + KphpAddObjectHeaderSize(ObjectSize), + TAG_KPHOBJ + ); +} + +/* KphpDeferDeleteObject + * + * Queues an object for deletion. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +VOID KphpDeferDeleteObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ) +{ + PKPH_OBJECT_HEADER nextToFree; + + /* Add the object to the list while saving the old value, atomically. + * Note that it is first-in, last-out. + */ + while (TRUE) + { + nextToFree = KphObjectNextToFree; + ObjectHeader->NextToFree = nextToFree; + + /* Attempt to set the global next-to-free variable. */ + if (InterlockedCompareExchangePointer( + &KphObjectNextToFree, + ObjectHeader, + nextToFree + ) == nextToFree) + { + /* Success. */ + break; + } + + /* Someone else changed the next-to-free variable. + * Go back and try again. + */ + } + + /* Was the to-free list empty before? If so, we need to queue + * the work item. + */ + if (!nextToFree) + { + ExQueueWorkItem(&KphObjectDeferDeleteWorkItem, CriticalWorkQueue); + } +} + +/* KphpDeferDeleteObjectRoutine + * + * Removes and frees objects from the to-free list. + * + * IRQL: PASSIVE_LEVEL + */ +VOID KphpDeferDeleteObjectRoutine( + __in PVOID Parameter + ) +{ + PKPH_OBJECT_HEADER objectHeader = NULL; + + while (TRUE) + { + /* Get the next object to free while replacing the global variable with + * what we needed to free next. + */ + objectHeader = InterlockedExchangePointer(&KphObjectNextToFree, objectHeader); + + /* If we have an object to free, free it and move on to the + * next object. Otherwise, stop. + */ + if (objectHeader) + { + KphpFreeObject(objectHeader); + objectHeader = objectHeader->NextToFree; + } + else + { + break; + } + } +} + +/* KphpFreeObject + * + * Calls the delete procedure for an object and frees its + * allocated storage. + * + * ObjectHeader: A pointer to the object header of an allocated object. + */ +VOID KphpFreeObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ) +{ + /* Object type statistics. */ + InterlockedDecrement(&ObjectHeader->Type->NumberOfObjects); + + /* Remove the object from the global object list. + * If the object manager is being destroyed, don't do this - + * we will deadlock because the deinitialization function + * holds the mutex. + */ + if (!KphObjectDeinitializing) + { + ExAcquireFastMutex(&KphObjectListMutex); + RemoveEntryList(&ObjectHeader->GlobalObjectListEntry); + ExReleaseFastMutex(&KphObjectListMutex); + } + + /* Call the delete procedure if we have one. */ + if (ObjectHeader->Type->DeleteProcedure) + { + ObjectHeader->Type->DeleteProcedure( + KphObjectHeaderToObject(ObjectHeader), + ObjectHeader->Flags + ); + } + + ExFreePoolWithTag( + ObjectHeader, + TAG_KPHOBJ + ); +} diff --git a/2.x/trunk/KProcessHacker/resource.rc b/2.x/trunk/KProcessHacker/resource.rc new file mode 100644 index 000000000..d38b333d6 --- /dev/null +++ b/2.x/trunk/KProcessHacker/resource.rc @@ -0,0 +1,53 @@ +#include + +#define VER_COMMA 1,10,0,0 +#define VER_STR "1.10\0" + +#define VER_FILEVERSION VER_COMMA +#define VER_FILEVERSION_STR VER_STR +#define VER_PRODUCTVERSION VER_COMMA +#define VER_PRODUCTVERSION_STR VER_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#define VER_PRIVATEBUILD 0 +#define VER_PRERELEASE 0 + +#define VER_COMPANYNAME_STR "wj32\0" +#define VER_FILEDESCRIPTION_STR "KProcessHacker\0" +#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2009 wj32. Licensed under the GNU GPL, v3.\0" +#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0" +#define VER_PRODUCTNAME_STR "KProcessHacker\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DRV +FILESUBTYPE VFT2_DRV_SYSTEM +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/2.x/trunk/KProcessHacker/se.c b/2.x/trunk/KProcessHacker/se.c new file mode 100644 index 000000000..ce09c6607 --- /dev/null +++ b/2.x/trunk/KProcessHacker/se.c @@ -0,0 +1,102 @@ +/* + * Process Hacker Driver - + * security + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/se.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphOpenProcessTokenEx) +#endif + +/* KphOpenProcessTokenEx + * + * Opens the primary token of the specified process. + */ +NTSTATUS KphOpenProcessTokenEx( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG ObjectAttributes, + __out PHANDLE TokenHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + PACCESS_TOKEN tokenObject; + HANDLE tokenHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*SeTokenObjectType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= TOKEN_ALL_ACCESS; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + tokenObject = PsReferencePrimaryToken(processObject); + ObDereferenceObject(processObject); + + status = ObOpenObjectByPointer( + tokenObject, + ObjectAttributes, + &accessState, + 0, + *SeTokenObjectType, + AccessMode, + &tokenHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(tokenObject); + + if (NT_SUCCESS(status)) + *TokenHandle = tokenHandle; + + return status; +} diff --git a/2.x/trunk/KProcessHacker/sources b/2.x/trunk/KProcessHacker/sources new file mode 100644 index 000000000..ff6e07b5e --- /dev/null +++ b/2.x/trunk/KProcessHacker/sources @@ -0,0 +1,29 @@ +TARGETNAME=kprocesshacker +TARGETTYPE=DRIVER +TARGETPATH=.\ + +INCLUDES=$(DDK_INC_PATH) +LIBS=%BUILD%\lib + +SOURCES= \ + kprocesshacker.c \ + version.c \ + \ + kph.c \ + handle.c \ + hook.c \ + protect.c \ + ref.c \ + sync.c \ + sysservice.c \ + sysservicedata.c \ + test.c \ + trace.c \ + util.c \ + \ + io.c \ + mm.c \ + ob.c \ + ps.c \ + se.c \ + resource.rc diff --git a/2.x/trunk/KProcessHacker/sync.c b/2.x/trunk/KProcessHacker/sync.c new file mode 100644 index 000000000..99bd46bf8 --- /dev/null +++ b/2.x/trunk/KProcessHacker/sync.c @@ -0,0 +1,312 @@ +/* + * Process Hacker Driver - + * synchronization code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/sync.h" +#include "include/debug.h" + +ULONG KphpCountBits( + __in ULONG_PTR Number + ); + +VOID KphpProcessorLockDpc( + __in PKDPC Dpc, + __in PVOID DeferredContext, + __in PVOID SystemArgument1, + __in PVOID SystemArgument2 + ); + +/* KphfAcquireGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +VOID FASTCALL KphfAcquireGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KIRQL oldIrql; + + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + /* Raise to APC_LEVEL. */ + oldIrql = KeRaiseIrql(APC_LEVEL, &oldIrql); + + /* Acquire the spinlock. */ + KphAcquireBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT); + + /* Now that we have the lock, we must save the old IRQL. */ + /* Clear the old IRQL. */ + Lock->Value &= KPH_GUARDED_LOCK_FLAGS; + /* Set the new IRQL. */ + Lock->Value |= oldIrql; +} + +/* KphfReleaseGuardedLock + * + * Releases a guarded lock and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +VOID FASTCALL KphfReleaseGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KIRQL oldIrql; + + ASSERT(KeGetCurrentIrql() >= APC_LEVEL); + + /* Get the old IRQL. */ + oldIrql = (KIRQL)(Lock->Value & ~KPH_GUARDED_LOCK_FLAGS); + /* Unlock the spinlock. */ + KphReleaseBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT); + /* Restore the old IRQL. */ + KeLowerIrql(oldIrql); +} + +/* KphAcquireProcessorLock + * + * Raises the IRQL to DISPATCH_LEVEL and prevents threads from + * executing on other processors until the processor lock is released. + * Blocks if the supplied processor lock is already in use. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * Comments: + * Here is how the processor lock works: + * 1. Tries to acquire the mutex in the processor lock, and + * blocks until it can be obtained. + * 2. Initializes a DPC for each processor on the computer. + * 3. Raises the IRQL to DISPATCH_LEVEL to make sure the + * code is not interrupted by a context switch. + * 4. Queues each of the previously-initialized DPCs, except if + * it is targeted at the current processor. + * 5. Since DPCs run at DISPATCH_LEVEL, they have exclusive + * control of the processor. As each runs, they increment + * a counter in the processor lock. They then enter a loop. + * 6. The routine waits for the counter to become n - 1, + * signaling that all (other) processors have been acquired + * (where n is the number of processors). + * 7. It returns. Any code from here will be running in + * DISPATCH_LEVEL and will be the only code running on the + * machine. + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +BOOLEAN KphAcquireProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + ULONG i; + ULONG numberProcessors; + ULONG currentProcessor; + + /* Acquire the processor lock guarded lock. */ + KphAcquireGuardedLock(&ProcessorLock->Lock); + + /* Reset some state. */ + ASSERT(ProcessorLock->AcquiredProcessors == 0); + ProcessorLock->AcquiredProcessors = 0; + ProcessorLock->ReleaseSignal = 0; /* IMPORTANT */ + + /* Get the number of processors. */ + numberProcessors = KphpCountBits(KeQueryActiveProcessors()); + + /* If there's only one processor we can simply raise the IRQL and exit. */ + if (numberProcessors == 1) + { + dprintf("KphAcquireProcessorLock: Only one processor, raising IRQL and exiting...\n"); + KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql); + ProcessorLock->Acquired = TRUE; + + return TRUE; + } + + /* Allocate storage for the DPCs. */ + ProcessorLock->Dpcs = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(KDPC) * numberProcessors, + TAG_SYNC_DPC + ); + + if (!ProcessorLock->Dpcs) + { + dprintf("KphAcquireProcessorLock: Could not allocate storage for DPCs!\n"); + KphReleaseGuardedLock(&ProcessorLock->Lock); + return FALSE; + } + + /* Initialize the DPCs. */ + for (i = 0; i < numberProcessors; i++) + { + KeInitializeDpc(&ProcessorLock->Dpcs[i], KphpProcessorLockDpc, NULL); + KeSetTargetProcessorDpc(&ProcessorLock->Dpcs[i], (CCHAR)i); + KeSetImportanceDpc(&ProcessorLock->Dpcs[i], HighImportance); + } + + /* Raise the IRQL to DISPATCH_LEVEL to prevent context switching. */ + KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql); + /* Get the current processor number. */ + currentProcessor = KeGetCurrentProcessorNumber(); + + /* Queue the DPCs (except on the current processor). */ + for (i = 0; i < numberProcessors; i++) + if (i != currentProcessor) + KeInsertQueueDpc(&ProcessorLock->Dpcs[i], ProcessorLock, NULL); + + /* Spinwait for all (other) processors to be acquired. */ + KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, numberProcessors - 1); + + dprintf("KphAcquireProcessorLock: All processors acquired.\n"); + ProcessorLock->Acquired = TRUE; + + return TRUE; +} + +/* KphInitializeProcessorLock + * + * Initializes a processor lock. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * IRQL: Any + */ +VOID KphInitializeProcessorLock( + __out PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + KphInitializeGuardedLock(&ProcessorLock->Lock, FALSE); + ProcessorLock->Dpcs = NULL; + ProcessorLock->AcquiredProcessors = 0; + ProcessorLock->ReleaseSignal = 0; + ProcessorLock->OldIrql = PASSIVE_LEVEL; + ProcessorLock->Acquired = FALSE; +} + +/* KphReleaseProcessorLock + * + * Allows threads to execute on other processors and restores the IRQL. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * Comments: + * Here is how the processor lock is released: + * 1. Sets the signal to release the processors. The DPCs that are + * currently waiting for the signal will return and decrement + * the acquired processors counter. + * 2. Waits for the acquired processors counter to become zero. + * 3. Restores the old IRQL. This will always be APC_LEVEL due to + * the mutex. + * 4. Frees the storage allocated for the DPCs. + * 5. Releases the processor lock mutex. This will restore the IRQL + * back to normal. + * Thread safety: Full + * IRQL: DISPATCH_LEVEL + */ +VOID KphReleaseProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + if (!ProcessorLock->Acquired) + return; + + /* Signal for the acquired processors to be released. */ + InterlockedExchange(&ProcessorLock->ReleaseSignal, 1); + + /* Spinwait for all acquired processors to be released. */ + KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, 0); + + dprintf("KphReleaseProcessorLock: All processors released.\n"); + + /* Restore the old IRQL (should always be APC_LEVEL due to the + * fast mutex). */ + KeLowerIrql(ProcessorLock->OldIrql); + + /* Free the DPCs if necessary. */ + if (ProcessorLock->Dpcs != NULL) + { + ExFreePoolWithTag(ProcessorLock->Dpcs, TAG_SYNC_DPC); + ProcessorLock->Dpcs = NULL; + } + + ProcessorLock->Acquired = FALSE; + + /* Release the processor lock guarded lock. This will restore the + * IRQL back to what it was before the processor lock was + * acquired. + */ + KphReleaseGuardedLock(&ProcessorLock->Lock); +} + +/* KphpCountBits + * + * Counts the number of bits set in an integer. + */ +ULONG KphpCountBits( + __in ULONG_PTR Number + ) +{ + ULONG count = 0; + + while (Number) + { + count++; + Number &= Number - 1; + } + + return count; +} + +/* KphpProcessorLockDpc + * + * The DPC routine which "locks" processors. + * + * Thread safety: Full + * IRQL: DISPATCH_LEVEL + */ +VOID KphpProcessorLockDpc( + __in PKDPC Dpc, + __in PVOID DeferredContext, + __in PVOID SystemArgument1, + __in PVOID SystemArgument2 + ) +{ + PKPH_PROCESSOR_LOCK processorLock = (PKPH_PROCESSOR_LOCK)SystemArgument1; + + ASSERT(processorLock != NULL); + + dprintf("KphpProcessorLockDpc: Acquiring processor %d.\n", KeGetCurrentProcessorNumber()); + + /* Increase the number of acquired processors. */ + InterlockedIncrement(&processorLock->AcquiredProcessors); + + /* Spin until we get the signal to release the processor. */ + KphSpinUntilNotEqual(&processorLock->ReleaseSignal, 0); + + /* Decrease the number of acquired processors. */ + InterlockedDecrement(&processorLock->AcquiredProcessors); + + dprintf("KphpProcessorLockDpc: Releasing processor %d.\n", KeGetCurrentProcessorNumber()); +} diff --git a/2.x/trunk/KProcessHacker/sysservice.c b/2.x/trunk/KProcessHacker/sysservice.c new file mode 100644 index 000000000..784706b4b --- /dev/null +++ b/2.x/trunk/KProcessHacker/sysservice.c @@ -0,0 +1,2140 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* ================ IMPORTANT ================ + * Please read the comments in KphpSsNewKiFastCallEntry to find out how + * KiFastCallEntry can be hooked. + * + * Note that the ONLY SUPPORTED METHOD of hooking is KiFastCallEntry, + * which means you MUST be using a CPU which supports sysenter. + * =========================================== + */ + +#include "include/sysservicep.h" +#include "include/hook.h" +#include "include/sync.h" +#include "include/trace.h" + +extern PDRIVER_OBJECT KphDriverObject; + +/* A fast mutex guarding starting/stopping system service logging. */ +FAST_MUTEX KphSsMutex; +/* Whether system service logging has been initialized. */ +BOOLEAN KphSsInitialized = FALSE; +/* The KiFastCallEntry hook. */ +KPH_HOOK KphSsKiFastCallEntryHook; +/* The number of active loggers. */ +ULONG KphSsNumberOfActiveLoggers = 0; + +/* The object type for client entries. */ +PKPH_OBJECT_TYPE KphSsClientEntryType; +/* The object type for ruleset entries. */ +PKPH_OBJECT_TYPE KphSsRuleSetEntryType; +/* The object type for rule entries. */ +PKPH_OBJECT_TYPE KphSsRuleEntryType; + +/* The list of ruleset entries. */ +LIST_ENTRY KphSsRuleSetListHead; +/* A push lock guarding accesses to the ruleset list. */ +EX_PUSH_LOCK KphSsRuleSetListPushLock; + +/* KphSsLogInit + * + * Initializes system service logging. + */ +NTSTATUS KphSsLogInit() +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Initialize the system service call data. */ + KphSsDataInit(); + + /* Initialize the ruleset list. */ + InitializeListHead(&KphSsRuleSetListHead); + ExInitializeFastMutex(&KphSsMutex); + ExInitializePushLock(&KphSsRuleSetListPushLock); + + /* Initialize the object types. */ + status = KphCreateObjectType( + &KphSsClientEntryType, + NonPagedPool, + 0, + KphpSsClientEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + return status; + + status = KphCreateObjectType( + &KphSsRuleSetEntryType, + NonPagedPool, + 0, + KphpSsRuleSetEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + { + KphDereferenceObject(KphSsClientEntryType); + return status; + } + + status = KphCreateObjectType( + &KphSsRuleEntryType, + NonPagedPool, + 0, + NULL + ); + + if (!NT_SUCCESS(status)) + { + KphDereferenceObject(KphSsClientEntryType); + KphDereferenceObject(KphSsRuleSetEntryType); + return status; + } + + return status; +} + +/* KphSsLogDeinit + * + * Frees system service logging data. + */ +NTSTATUS KphSsLogDeinit() +{ + KphSsDataDeinit(); + + return STATUS_SUCCESS; +} + +/* KphSsLogStart + * + * Starts system service logging. + */ +NTSTATUS KphSsLogStart() +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + + /* Make sure we have the KiFastCallEntry+x address. */ + if (!__KiFastCallEntry) + return STATUS_NOT_SUPPORTED; + + ExAcquireFastMutex(&KphSsMutex); + + if (KphSsInitialized) + { + ExReleaseFastMutex(&KphSsMutex); + return STATUS_UNSUCCESSFUL; + } + + /* Hook KiFastCallEntry. Logging will start from now. */ + KphInitializeHook( + &KphSsKiFastCallEntryHook, + __KiFastCallEntry, + KphpSsNewKiFastCallEntry + ); + status = KphHook(&KphSsKiFastCallEntryHook); + + if (!NT_SUCCESS(status)) + { + ExReleaseFastMutex(&KphSsMutex); + return status; + } + + KphSsInitialized = TRUE; + + ExReleaseFastMutex(&KphSsMutex); + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphSsLogStop + * + * Stops system service logging. + */ +NTSTATUS KphSsLogStop() +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + + ExAcquireFastMutex(&KphSsMutex); + + if (!KphSsInitialized) + { + ExReleaseFastMutex(&KphSsMutex); + return STATUS_UNSUCCESSFUL; + } + + status = KphUnhook(&KphSsKiFastCallEntryHook); + + if (!NT_SUCCESS(status)) + { + ExReleaseFastMutex(&KphSsMutex); + return status; + } + + /* Spin until the logger count reaches 0. */ + KphSpinUntilEqual(&KphSsNumberOfActiveLoggers, 0); + + KphSsInitialized = FALSE; + + ExReleaseFastMutex(&KphSsMutex); + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphSsCreateClientEntry + * + * Creates a client entry which describes a client of the + * system service logger. Clients receive system service log events. + * Note that a client may have several ruleset entries associated + * with it. + * + * ClientEntry: A variable which receives a pointer to the client entry. + * ProcessHandle: A handle to the client process, with PROCESS_VM_WRITE + * access. + * ReadSemaphoreHandle: A handle to a semaphore which is released when an + * event is written to the client buffer. The client must wait for the + * semaphore when it is about to read a block. + * WriteSemaphoreHandle: A handle to a semaphore which is acquired when an + * event is about to be written to the client buffer. If the semaphore + * cannot be acquired immediately, the event is dropped. The client must + * continually read the buffer and release the semaphore. + * BufferBase: A pointer to a buffer in the client process. + * BufferSize: The size of the buffer, in bytes. + * AccessMode: The mode to use when probing arguments. + */ +NTSTATUS KphSsCreateClientEntry( + __out PKPHSS_CLIENT_ENTRY *ClientEntry, + __in HANDLE ProcessHandle, + __in HANDLE ReadSemaphoreHandle, + __in HANDLE WriteSemaphoreHandle, + __in PVOID BufferBase, + __in ULONG BufferSize, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_CLIENT_ENTRY clientEntry; + PEPROCESS processObject; + PKSEMAPHORE readSemaphore; + PKSEMAPHORE writeSemaphore; + + /* Probe. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(BufferBase, BufferSize, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Reference the client process. */ + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_WRITE, + *PsProcessType, + AccessMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Reference the read semaphore. */ + status = ObReferenceObjectByHandle( + ReadSemaphoreHandle, + SEMAPHORE_MODIFY_STATE, + *ExSemaphoreObjectType, + AccessMode, + &readSemaphore, + NULL + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + return status; + } + + /* Reference the write semaphore. */ + status = ObReferenceObjectByHandle( + WriteSemaphoreHandle, + SEMAPHORE_MODIFY_STATE, + *ExSemaphoreObjectType, + AccessMode, + &writeSemaphore, + NULL + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + ObDereferenceObject(readSemaphore); + return status; + } + + /* Create the client entry object. */ + status = KphCreateObject( + &clientEntry, + sizeof(KPHSS_CLIENT_ENTRY), + 0, + KphSsClientEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + ObDereferenceObject(readSemaphore); + ObDereferenceObject(writeSemaphore); + + return status; + } + + clientEntry->Process = processObject; + clientEntry->Enabled = TRUE; + clientEntry->ReadSemaphore = readSemaphore; + clientEntry->WriteSemaphore = writeSemaphore; + ExInitializeFastMutex(&clientEntry->BufferMutex); + clientEntry->BufferBase = BufferBase; + clientEntry->BufferSize = BufferSize; + clientEntry->BufferCursor = 0; + clientEntry->NumberOfBlocksWritten = 0; + clientEntry->NumberOfBlocksDropped = 0; + + *ClientEntry = clientEntry; + + return status; +} + +/* KphSsEnableClientEntry + * + * Enables or disables a client entry. + */ +NTSTATUS KphSsEnableClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in BOOLEAN Enable + ) +{ + if (Enable) + ClientEntry->Enabled = TRUE; + else + ClientEntry->Enabled = FALSE; + + return STATUS_SUCCESS; +} + +/* KphSsQueryClientEntry + * + * Queries information about a client entry. + */ +NTSTATUS KphSsQueryClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation, + __in ULONG ClientInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Probe the return length if necessary. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Check the length. */ + if (ClientInformationLength >= sizeof(KPHSS_CLIENT_INFORMATION)) + { + if (ClientInformation) + { + __try + { + /* Probe the buffer if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + ProbeForWrite(ClientInformation, sizeof(KPHSS_CLIENT_INFORMATION), 1); + + ClientInformation->ProcessId = PsGetProcessId(ClientEntry->Process); + ClientInformation->BufferBase = ClientEntry->BufferBase; + ClientInformation->BufferSize = ClientEntry->BufferSize; + ClientInformation->NumberOfBlocksWritten = ClientEntry->NumberOfBlocksWritten; + ClientInformation->NumberOfBlocksDropped = ClientEntry->NumberOfBlocksDropped; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + + /* Pass the return length back if requested. */ + if (ReturnLength) + { + __try + { + *ReturnLength = sizeof(KPHSS_CLIENT_INFORMATION); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* KphpSsClientEntryDeleteProcedure + * + * Performs cleanup for a client entry. + */ +VOID NTAPI KphpSsClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPHSS_CLIENT_ENTRY clientEntry = (PKPHSS_CLIENT_ENTRY)Object; + + ObDereferenceObject(clientEntry->Process); + ObDereferenceObject(clientEntry->ReadSemaphore); + ObDereferenceObject(clientEntry->WriteSemaphore); +} + +/* KphSsCreateRuleSetEntry + * + * Creates a ruleset entry which contains a list of rules + * and an action to perform. + */ +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + /* Make sure the action is valid. */ + if (Action < LogRuleSetAction || Action >= MaxRuleSetAction) + return STATUS_INVALID_PARAMETER_3; + + /* Create the ruleset object. */ + status = KphCreateObject( + &ruleSetEntry, + sizeof(KPHSS_RULESET_ENTRY), + 0, + KphSsRuleSetEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the ruleset object. */ + KphReferenceObject(ClientEntry); + ruleSetEntry->Client = ClientEntry; + ruleSetEntry->DefaultFilterType = DefaultFilterType; + ruleSetEntry->Action = Action; + ruleSetEntry->NextRuleHandle = 4; + ExInitializePushLock(&ruleSetEntry->RuleListPushLock); + InitializeListHead(&ruleSetEntry->RuleListHead); + + /* Add the ruleset to the list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock); + InsertHeadList(&KphSsRuleSetListHead, &ruleSetEntry->RuleSetListEntry); + ExReleasePushLock(&KphSsRuleSetListPushLock); + KeLeaveCriticalRegion(); + + *RuleSetEntry = ruleSetEntry; + + return status; +} + +/* KphpSsRuleSetEntryDeleteProcedure + * + * Performs cleanup for a ruleset entry. + */ +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPHSS_RULESET_ENTRY ruleSetEntry = (PKPHSS_RULESET_ENTRY)Object; + PLIST_ENTRY currentRuleListEntry; + + /* Dereference the client entry. */ + KphDereferenceObject(ruleSetEntry->Client); + + KeEnterCriticalRegion(); + + /* Dereference all rules in the ruleset. */ + ExAcquirePushLockExclusive(&ruleSetEntry->RuleListPushLock); + + currentRuleListEntry = ruleSetEntry->RuleListHead.Flink; + + while (currentRuleListEntry != &ruleSetEntry->RuleListHead) + { + PLIST_ENTRY nextEntry; + + /* Save the next entry pointer since currentRuleListEntry may + * be deallocated due to the dereference. + */ + nextEntry = currentRuleListEntry->Flink; + KphDereferenceObject(KPHSS_RULE_ENTRY(currentRuleListEntry)); + currentRuleListEntry = nextEntry; + } + + ExReleasePushLock(&ruleSetEntry->RuleListPushLock); + + /* Remove the ruleset from the list. */ + ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock); + RemoveEntryList(&ruleSetEntry->RuleSetListEntry); + ExReleasePushLock(&KphSsRuleSetListPushLock); + + KeLeaveCriticalRegion(); +} + +/* KphSsAddProcessIdRule + * + * Adds a process ID rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddProcessIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ProcessId + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ProcessIdRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->ProcessIdRule.ProcessId = ProcessId; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddThreadIdRule + * + * Adds a thread ID rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddThreadIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ThreadId + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ThreadIdRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->ThreadIdRule.ThreadId = ThreadId; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddPreviousModeRule + * + * Adds a previous mode rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddPreviousModeRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, PreviousModeRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->PreviousModeRule.PreviousMode = PreviousMode; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddNumberRule + * + * Adds a system service number rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddNumberRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in ULONG Number + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, NumberRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->NumberRule.Number = Number; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsGetHandleRule + * + * Gets the handle of a rule. + */ +HANDLE KphSsGetHandleRule( + __in PKPHSS_RULE_ENTRY RuleEntry + ) +{ + return RuleEntry->Handle; +} + +/* KphSsRemoveRule + * + * Removes a rule entry from a ruleset entry. + */ +NTSTATUS KphSsRemoveRule( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in HANDLE RuleEntryHandle + ) +{ + PLIST_ENTRY currentListEntry; + + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock); + + /* Find the rule in the ruleset. */ + + currentListEntry = RuleSetEntry->RuleListHead.Flink; + + while (currentListEntry != &RuleSetEntry->RuleListHead) + { + PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry); + + if (ruleEntry->Handle == RuleEntryHandle) + { + /* Remove the rule from the list. */ + RemoveEntryList(&ruleEntry->RuleListEntry); + /* Dereference the rule (it was referenced when it + * got added to the list). + */ + KphDereferenceObject(ruleEntry); + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + return STATUS_SUCCESS; + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + return STATUS_INVALID_PARAMETER_2; +} + +/* KphpSsAddRule + * + * Adds a rule entry to a ruleset entry. + */ +NTSTATUS KphpSsAddRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPHSS_RULE_TYPE RuleType + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Make sure the filter/rule type is valid. */ + if (FilterType < IncludeFilterType || FilterType >= MaxFilterType) + return STATUS_INVALID_PARAMETER_3; + if (RuleType < ProcessIdRuleType || RuleType >= MaxRuleType) + return STATUS_INVALID_PARAMETER_4; + + /* Create the rule entry object. */ + status = KphCreateObject( + &ruleEntry, + sizeof(KPHSS_RULE_ENTRY), + 0, + KphSsRuleEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the object. */ + ruleEntry->Initialized = FALSE; + ruleEntry->FilterType = FilterType; + ruleEntry->RuleType = RuleType; + + /* Get a handle for the rule. */ + ruleEntry->Handle = (HANDLE)(ULONG_PTR)InterlockedExchangeAdd( + &RuleSetEntry->NextRuleHandle, + KPHSS_RULE_HANDLE_INCREMENT + ); + + /* Add the rule to the ruleset. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock); + InsertTailList(&RuleSetEntry->RuleListHead, &ruleEntry->RuleListEntry); + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + /* Add a reference for the rule being on the list. */ + KphReferenceObject(ruleEntry); + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphpSsCreateEventBlock + * + * Allocates and initializes an event block. + * + * EventBlock: A variable which receives a pointer to the event block. + * Thread: The thread for which the event is being generated. + * Number: The system service number. + * Arguments: A pointer to the caller-supplied arguments. + * NumberOfArguments: The number of arguments, in ULONGs. + */ +NTSTATUS KphpSsCreateEventBlock( + __out PKPHSS_EVENT_BLOCK *EventBlock, + __in PKTHREAD Thread, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments + ) +{ + PKPHSS_EVENT_BLOCK eventBlock; + KPROCESSOR_MODE previousMode; + ULONG eventBlockSize; + ULONG argumentsSize; + ULONG traceSize; + PVOID stackTrace[MAX_STACK_DEPTH * 2]; + ULONG capturedFrames; + + /* Make sure the argument count isn't too large. */ + if (NumberOfArguments > MAX_USHORT) + return STATUS_INVALID_PARAMETER; + + previousMode = ExGetPreviousMode(); + + /* Capture kernel-mode and user-mode stack traces. + * We do this before we allocate the event block so + * we can calculate how large the block should be. + */ + + /* Get a kernel-mode stack trace. */ + capturedFrames = KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + 0, + stackTrace, + NULL + ); + + if (PsGetCurrentProcess() != PsInitialSystemProcess) + { + /* Get a user-mode stack trace. */ + capturedFrames += KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + RTL_WALK_USER_MODE_STACK, + &stackTrace[capturedFrames], + NULL + ); + } + + /* Calculate the size of the event block. */ + argumentsSize = NumberOfArguments * sizeof(ULONG); + traceSize = capturedFrames * sizeof(PVOID); + eventBlockSize = sizeof(KPHSS_EVENT_BLOCK) + argumentsSize + traceSize; + + /* Make sure the block size isn't too large. */ + if (eventBlockSize > MAX_USHORT) + return STATUS_INVALID_PARAMETER; + + /* Allocate the event block. */ + eventBlock = ExAllocatePoolWithTag(PagedPool, eventBlockSize, TAG_EVENT_BLOCK); + + if (!eventBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize the event block. */ + eventBlock->Header.Size = (USHORT)eventBlockSize; + eventBlock->Header.Type = EventBlockType; + eventBlock->Flags = 0; + KeQuerySystemTime(&eventBlock->Time); + eventBlock->ClientId.UniqueThread = PsGetThreadId(Thread); + eventBlock->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess(Thread)); + eventBlock->Number = Number; + eventBlock->NumberOfArguments = (USHORT)NumberOfArguments; + eventBlock->ArgumentsOffset = sizeof(KPHSS_EVENT_BLOCK); + eventBlock->TraceCount = (USHORT)capturedFrames; + eventBlock->TraceOffset = (USHORT)(sizeof(KPHSS_EVENT_BLOCK) + argumentsSize); + + /* Set the flags according to the previous mode. */ + if (previousMode == UserMode) + eventBlock->Flags |= KPHSS_EVENT_USER_MODE; + else if (previousMode == KernelMode) + eventBlock->Flags |= KPHSS_EVENT_KERNEL_MODE; + + /* Probe and copy the arguments. */ + if (previousMode != KernelMode) + { + __try + { + ProbeForRead(Arguments, argumentsSize, 4); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + eventBlock->Flags |= KPHSS_EVENT_PROBE_ARGUMENTS_FAILED; + } + } + + __try + { + /* Copy the arguments to the space immediately after the event block. */ + memcpy((PCHAR)eventBlock + eventBlock->ArgumentsOffset, Arguments, argumentsSize); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + eventBlock->Flags |= KPHSS_EVENT_COPY_ARGUMENTS_FAILED; + } + + /* Copy the stack trace. */ + memcpy((PCHAR)eventBlock + eventBlock->TraceOffset, stackTrace, traceSize); + + /* Pass the pointer to the event block back. */ + *EventBlock = eventBlock; + + return STATUS_SUCCESS; +} + +/* KphpSsFreeEventBlock + * + * Frees an event block created by KphpSsCreateEventBlock. + */ +VOID KphpSsFreeEventBlock( + __in PKPHSS_EVENT_BLOCK EventBlock + ) +{ + ExFreePoolWithTag(EventBlock, TAG_EVENT_BLOCK); +} + +/* KphpSsCaptureSimpleArgument + * + * Captures a simple (1-, 2-, 4- or 8-byte) argument. + */ +NTSTATUS KphpSsCaptureSimpleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in KPHSS_ARGUMENT_TYPE Type, + __in KPROCESSOR_MODE PreviousMode + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + LARGE_INTEGER value; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Get the proper argument size based on the argument type. */ + switch (Type) + { + case Int8Argument: + size = sizeof(BOOLEAN); + break; + case Int16Argument: + size = sizeof(SHORT); + break; + case Int32Argument: + size = sizeof(LONG); + break; + case Int64Argument: + size = sizeof(LARGE_INTEGER); + break; + default: + return STATUS_INVALID_PARAMETER_3; + } + + /* Probe and read the value. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, size, 1); + + memcpy(&value, Argument, size); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock(size, Type); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Copy the value into the argument block. */ + memcpy(&argumentBlock->Simple, &value, size); + *ArgumentBlock = argumentBlock; + + return STATUS_SUCCESS; +} + +/* KphpSsCaptureHandleArgument + * + * Captures a handle argument. + */ +NTSTATUS KphpSsCaptureHandleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in HANDLE Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG bufferLength; + PVOID object; + POBJECT_TYPE objectType; + PUNICODE_STRING objectTypeName; + PUNICODE_STRING objectNameInfo; + ULONG returnLength; + PKPHSS_HANDLE handleInfo; + PKPHSS_WSTRING wString; + + /* Return if we have a NULL handle. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Make sure the handle isn't a kernel handle if we're + * from user-mode. We need exceptions for the process + * and thread pseudo-handles. + */ + if (PreviousMode != KernelMode) + { + if ( + IsKernelHandle(Argument) && + Argument != NtCurrentProcess() && + Argument != NtCurrentThread() + ) + return STATUS_INVALID_HANDLE; + } + + /* Reference the object. */ + status = ObReferenceObjectByHandle( + Argument, + 0, + NULL, + KernelMode, + &object, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Get a pointer to the UNICODE_STRING containing the + * object type name. + */ + objectType = KphGetObjectTypeNt(object); + objectTypeName = (PUNICODE_STRING)KVOFF(objectType, OffOtName); + + /* Allocate a buffer for name information. */ + objectNameInfo = (PUNICODE_STRING)ExAllocatePoolWithTag( + PagedPool, + CAPTURE_HANDLE_BUFFER_SIZE, + TAG_CAPTURE_TEMP_BUFFER + ); + + if (!objectNameInfo) + goto CleanupObject; + + /* Query the name of the object. */ + status = KphQueryNameObject( + object, + objectNameInfo, + CAPTURE_HANDLE_BUFFER_SIZE, + &returnLength + ); + + if (!NT_SUCCESS(status)) + goto CleanupName; + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_HANDLE) + sizeof(KPHSS_WSTRING) + sizeof(KPHSS_WSTRING) + + objectTypeName->Length + objectNameInfo->Length, + HandleArgument + ); + + if (!argumentBlock) + goto CleanupName; + + handleInfo = &argumentBlock->Handle; + /* Calculate the offsets. */ + handleInfo->TypeNameOffset = sizeof(KPHSS_HANDLE); + handleInfo->NameOffset = + handleInfo->TypeNameOffset + sizeof(KPHSS_WSTRING) + + objectTypeName->Length; + + /* Copy the object type name into the block. */ + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->TypeNameOffset); + wString->Length = objectTypeName->Length; + memcpy(&wString->Buffer, objectTypeName->Buffer, wString->Length); + + /* Copy the object name into the block. */ + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->NameOffset); + wString->Length = objectNameInfo->Length; + memcpy(&wString->Buffer, objectNameInfo->Buffer, wString->Length); + + /* We may be able to get additional information for the + * object. + */ + + handleInfo->ClientId.UniqueProcess = NULL; + handleInfo->ClientId.UniqueThread = NULL; + + if (objectType == *PsProcessType) + { + handleInfo->ClientId.UniqueProcess = PsGetProcessId((PEPROCESS)object); + } + else if (objectType == *PsThreadType) + { + handleInfo->ClientId.UniqueThread = PsGetThreadId((PETHREAD)object); + handleInfo->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess((PETHREAD)object)); + } + + *ArgumentBlock = argumentBlock; + +CleanupName: + ExFreePoolWithTag(objectNameInfo, TAG_CAPTURE_TEMP_BUFFER); +CleanupObject: + ObDereferenceObject(object); + + return status; +} + +/* KphpSsCaptureUnicodeStringArgument + * + * Captures a UNICODE_STRING argument. + */ +NTSTATUS KphpSsCaptureUnicodeStringArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PUNICODE_STRING Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + UNICODE_STRING unicodeString; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the UNICODE_STRING structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(UNICODE_STRING), 1); + + memcpy(&unicodeString, Argument, sizeof(UNICODE_STRING)); + + /* Probe the buffer, if present. */ + if (unicodeString.Buffer && PreviousMode != KernelMode) + { + ProbeForRead(unicodeString.Buffer, unicodeString.Length, 1); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Check if the string is too large. */ + if (unicodeString.Length > CAPTURE_UNICODE_STRING_MAX_SIZE) + return STATUS_UNSUCCESSFUL; + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_UNICODE_STRING) + unicodeString.Length, + UnicodeStringArgument + ); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Copy the string into the argument block. */ + argumentBlock->UnicodeString.Length = unicodeString.Length; + argumentBlock->UnicodeString.MaximumLength = unicodeString.MaximumLength; + argumentBlock->UnicodeString.Pointer = unicodeString.Buffer; + + if (unicodeString.Buffer) + { + __try + { + memcpy(argumentBlock->UnicodeString.Buffer, unicodeString.Buffer, unicodeString.Length); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphpSsFreeArgumentBlock(argumentBlock); + return GetExceptionCode(); + } + } + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCaptureObjectAttributesArgument + * + * Captures an OBJECT_ATTRIBUTES argument. + */ +NTSTATUS KphpSsCaptureObjectAttributesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in POBJECT_ATTRIBUTES Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + OBJECT_ATTRIBUTES objectAttributes; + PKPHSS_ARGUMENT_BLOCK rootDirectoryArgumentBlock = NULL; + ULONG rootDirectoryArgumentBlockSize = 0; + PKPHSS_ARGUMENT_BLOCK objectNameArgumentBlock = NULL; + ULONG objectNameArgumentBlockSize = 0; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the OBJECT_ATTRIBUTES structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(OBJECT_ATTRIBUTES), 1); + + memcpy(&objectAttributes, Argument, sizeof(OBJECT_ATTRIBUTES)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* If we have a root directory, create an argument block from it + * and copy it to our argument block. + */ + if (objectAttributes.RootDirectory) + { + status = KphpSsCaptureHandleArgument( + &rootDirectoryArgumentBlock, + objectAttributes.RootDirectory, + PreviousMode + ); + + /* If we created the argument block, we need to calculate + * the size of the KPHSS_HANDLE structure. + */ + if (NT_SUCCESS(status)) + { + rootDirectoryArgumentBlockSize = + rootDirectoryArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD; + } + else + { + rootDirectoryArgumentBlock = NULL; + } + } + + /* If we have a object name, create an argument block from it and + * copy it to our argument block. + */ + if (objectAttributes.ObjectName) + { + status = KphpSsCaptureUnicodeStringArgument( + &objectNameArgumentBlock, + objectAttributes.ObjectName, + PreviousMode + ); + + /* If we created the argument block, we need to calculate + * the size of the KPHSS_UNICODE_STRING structure. + */ + if (NT_SUCCESS(status)) + { + objectNameArgumentBlockSize = + objectNameArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD; + } + else + { + objectNameArgumentBlock = NULL; + } + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize + objectNameArgumentBlockSize, + ObjectAttributesArgument + ); + + argumentBlock->ObjectAttributes.RootDirectoryOffset = 0; + argumentBlock->ObjectAttributes.ObjectNameOffset = 0; + + /* Copy the object attributes fields. */ + memcpy( + &argumentBlock->ObjectAttributes.ObjectAttributes, + &objectAttributes, + sizeof(OBJECT_ATTRIBUTES) + ); + + /* Copy the root directory structure, if we have one. */ + if (rootDirectoryArgumentBlock) + { + ULONG rootDirectoryOffset; + + /* It will go directly after the KPHSS_OBJECT_ATTRIBUTES structure. */ + rootDirectoryOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES); + argumentBlock->ObjectAttributes.RootDirectoryOffset = (USHORT)rootDirectoryOffset; + /* Copy it. */ + memcpy( + PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, rootDirectoryOffset), + &rootDirectoryArgumentBlock->Handle, + rootDirectoryArgumentBlockSize + ); + /* Free the block. */ + KphpSsFreeArgumentBlock(rootDirectoryArgumentBlock); + } + + /* Copy the object name structure, if we have one. */ + if (objectNameArgumentBlock) + { + ULONG objectNameOffset; + + /* We'll place the structure after the root directory structure, + * if present. + */ + objectNameOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize; + + /* Make sure the offset isn't too large. */ + if (objectNameOffset <= MAX_USHORT) + { + argumentBlock->ObjectAttributes.ObjectNameOffset = (USHORT)objectNameOffset; + /* Copy it. */ + memcpy( + PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, objectNameOffset), + &objectNameArgumentBlock->UnicodeString, + objectNameArgumentBlockSize + ); + } + + /* Free the block. */ + KphpSsFreeArgumentBlock(objectNameArgumentBlock); + } + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCaptureClientIdArgument + * + * Captures a CLIENT_ID argument. + */ +NTSTATUS KphpSsCaptureClientIdArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PCLIENT_ID Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + CLIENT_ID clientId; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + + /* Check if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the CLIENT_ID structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(CLIENT_ID), 1); + + memcpy(&clientId, Argument, sizeof(CLIENT_ID)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(CLIENT_ID), + ClientIdArgument + ); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Fill in the argument block. */ + memcpy(&argumentBlock->ClientId, &clientId, sizeof(CLIENT_ID)); + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCaptureBytesArgument + * + * Captures a binary blob as an argument. + */ +NTSTATUS KphpSsCaptureBytesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in ULONG Length, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + + /* Check if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Make sure the length isn't too big. */ + if (Length > CAPTURE_BYTES_MAX_SIZE) + return STATUS_INVALID_PARAMETER_3; + + /* Probe the bytes. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, Length, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_BYTES) + Length, + BytesArgument + ); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Copy the bytes. */ + __try + { + memcpy(argumentBlock->Bytes.Buffer, Argument, Length); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphpSsFreeArgumentBlock(argumentBlock); + return GetExceptionCode(); + } + + argumentBlock->Bytes.Length = (USHORT)Length; + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCreateArgumentBlock + * + * Allocates and initializes an argument block. + */ +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index, + __in_opt KPHSS_ARGUMENT_TYPE Type, + __in_opt PVOID Context + ) +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + KPROCESSOR_MODE previousMode; + PKPHSS_CALL_ENTRY callEntry; + KPHSS_ARGUMENT_TYPE argumentType; + + previousMode = ExGetPreviousMode(); + + /* Get a pointer to the call entry for the system service. + * If we don't have one, we can't proceed. + */ + callEntry = KphSsLookupCallEntry(Number); + + if (!callEntry) + return STATUS_INVALID_PARAMETER_2; + + /* Validate the argument index. */ + if (Index >= callEntry->NumberOfArguments) + return STATUS_INVALID_PARAMETER_3; + + if (Type != 0) + argumentType = Type; + else + argumentType = callEntry->Arguments[Index]; + + /* Is this a normal argument? If so, there's no point + * creating an argument block since the data is already + * in the event block. + */ + if (argumentType == NormalArgument) + return STATUS_UNSUCCESSFUL; + + /* Capture the argument. */ + + switch (argumentType) + { + case Int8Argument: + case Int16Argument: + case Int32Argument: + case Int64Argument: + status = KphpSsCaptureSimpleArgument( + &argumentBlock, + (PVOID)Argument, + argumentType, + previousMode + ); + break; + case HandleArgument: + status = KphpSsCaptureHandleArgument( + &argumentBlock, + (HANDLE)Argument, + previousMode + ); + break; + case UnicodeStringArgument: + status = KphpSsCaptureUnicodeStringArgument( + &argumentBlock, + (PUNICODE_STRING)Argument, + previousMode + ); + break; + case ObjectAttributesArgument: + status = KphpSsCaptureObjectAttributesArgument( + &argumentBlock, + (POBJECT_ATTRIBUTES)Argument, + previousMode + ); + break; + case ClientIdArgument: + status = KphpSsCaptureClientIdArgument( + &argumentBlock, + (PCLIENT_ID)Argument, + previousMode + ); + break; + case BytesArgument: + status = KphpSsCaptureBytesArgument( + &argumentBlock, + (PVOID)Argument, + (ULONG)Context, + previousMode + ); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (!NT_SUCCESS(status)) + return status; + + /* Put the index in. */ + argumentBlock->Index = (UCHAR)Index; + + *ArgumentBlock = argumentBlock; + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphpSsAllocateArgumentBlock + * + * Allocates an argument block and initializes some fields. + */ +PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock( + __in ULONG InnerSize, + __in KPHSS_ARGUMENT_TYPE Type + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + + size = KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize); + + /* Make sure the size isn't too large. */ + if (size > MAX_USHORT) + return NULL; + + argumentBlock = ExAllocatePoolWithTag( + PagedPool, + size, + TAG_ARGUMENT_BLOCK + ); + + if (!argumentBlock) + return NULL; + + argumentBlock->Header.Type = ArgumentBlockType; + argumentBlock->Header.Size = (USHORT)size; + argumentBlock->Type = Type; + + return argumentBlock; +} + +/* KphpSsFreeArgumentBlock + * + * Frees an argument block created by KphpSsCreateArgumentBlock. + */ +VOID KphpSsFreeArgumentBlock( + __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock + ) +{ + ExFreePoolWithTag(ArgumentBlock, TAG_ARGUMENT_BLOCK); +} + +/* KphpSsWriteBlock + * + * Writes a block into client memory. + */ +NTSTATUS KphpSsWriteBlock( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + LARGE_INTEGER zeroTimeout; + KPH_ATTACH_STATE attachState; + ULONG availableSpace; + HANDLE dupHandleInClient = NULL; + + zeroTimeout.QuadPart = 0; + + /* Take care of the sequence mode. If it isn't + * NoSequence, it is effectively a way for the caller + * to control the buffer mutex. + */ + if (SequenceMode == StartSequence) + { + ExAcquireFastMutex(&ClientEntry->BufferMutex); + return STATUS_SUCCESS; + } + else if (SequenceMode == EndSequence) + { + ExReleaseFastMutex(&ClientEntry->BufferMutex); + return STATUS_SUCCESS; + } + else + { + /* If we aren't manipulating the mutex, we need + * a block to write. + */ + if (!Block) + return STATUS_INVALID_PARAMETER_2; + + /* If we're in a sequence, don't acquire the mutex + * because the caller would have acquired it using + * StartSequence already. + */ + if (SequenceMode != InSequence) + ExAcquireFastMutex(&ClientEntry->BufferMutex); + } + + /* Try to acquire the write semaphore. If we can't acquire + * it immediately, drop the block. + */ + status = KeWaitForSingleObject( + ClientEntry->WriteSemaphore, + Executive, + KernelMode, + FALSE, + &zeroTimeout + ); + + if (!KPHSS_BLOCK_SUCCESS(status)) + { + if (status == STATUS_TIMEOUT) + { + dprintf("Ss: WARNING: Dropped block (server %#x).\n", ClientEntry->BufferCursor); + ClientEntry->NumberOfBlocksDropped++; + } + + goto CleanupBufferMutex; + } + + availableSpace = ClientEntry->BufferSize - ClientEntry->BufferCursor; + + /* Blocks are recorded in a circular buffer. + * In the case that there is not enough space for an entire block, + * we will record a reset block that tells the client to reset + * its read cursor to 0. In the case that there is not enough + * space for a block header, it is implied that the client will + * reset its read cursor. + */ + + /* Check if we have enough space for a block header. */ + if (availableSpace < sizeof(KPHSS_BLOCK_HEADER)) + { + /* Not enough space. Reset the cursor. */ + dprintf("Ss: Implicit cursor reset (server %#x).\n", ClientEntry->BufferCursor); + ClientEntry->BufferCursor = 0; + availableSpace = ClientEntry->BufferSize; + } + /* Check if we have enough space for the block. */ + else if (availableSpace < Block->Size) + { + KPHSS_RESET_BLOCK resetBlock; + + /* Not enough space for the block, but enough space + * for a reset block. Write the reset block and reset + * the cursor. + */ + resetBlock.Header.Size = sizeof(KPHSS_RESET_BLOCK); + resetBlock.Header.Type = ResetBlockType; + + /* Attach to the client process and copy the block. */ + KphAttachProcess(ClientEntry->Process, &attachState); + + __try + { + memcpy( + PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor), + &resetBlock, + resetBlock.Header.Size + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphDetachProcess(&attachState); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + dprintf("Ss: Wrote reset block (server %#x).\n", ClientEntry->BufferCursor); + KphDetachProcess(&attachState); + ClientEntry->BufferCursor = 0; + availableSpace = ClientEntry->BufferSize; + } + + /* Now that we have dealt with any end-of-buffer issues, + * we still have to check if we have enough space for the + * event. We may have a huge event or the client may have a + * tiny buffer. + */ + if (availableSpace < Block->Size) + { + dfprintf("Ss: WARNING: Insufficient buffer size (server %#x).\n", ClientEntry->BufferCursor); + status = STATUS_BUFFER_TOO_SMALL; + goto CleanupBufferMutex; + } + + /* Time to copy the block into the buffer. + */ + KphAttachProcess(ClientEntry->Process, &attachState); + + __try + { + memcpy( + PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor), + Block, + Block->Size + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + dfprintf("Ss: ERROR: Could not write to the client buffer (server %#x)!\n", ClientEntry->BufferCursor); + KphDetachProcess(&attachState); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + KphDetachProcess(&attachState); + + /* Now that we have succesfully copied the block, we need to + * release the read semaphore to notify to the client that they have + * a block to read. We also need to advance our cursor. + */ + + /* May cause an exception (STATUS_SEMAPHORE_LIMIT_EXCEEDED). */ + __try + { + KeReleaseSemaphore(ClientEntry->ReadSemaphore, 2, 1, FALSE); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + dfprintf("Ss: ERROR: Could not release read semaphore (server %#x)!\n", ClientEntry->BufferCursor); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + ClientEntry->BufferCursor += Block->Size; + ClientEntry->NumberOfBlocksWritten++; + + dprintf("Ss: Wrote block (server %#x).\n", ClientEntry->BufferCursor); + +CleanupBufferMutex: + if (SequenceMode != InSequence) + ExReleaseFastMutex(&ClientEntry->BufferMutex); + + return status; +} + +/* KphpSsLogSystemServiceCall + * + * Logs a system service. + * + * WARNING: This function CANNOT make any system calls. + * + * IRQL: <= APC_LEVEL + */ +VOID NTAPI KphpSsLogSystemServiceCall( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ) +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + KPROCESSOR_MODE previousMode; + PLIST_ENTRY currentListEntry; + PKPHSS_RULESET_ENTRY ruleSetEntryArray[KPHSS_RULESET_ENTRY_LIMIT]; + ULONG ruleSetEntryCount; + PKPHSS_EVENT_BLOCK eventBlock; + PKPHSS_ARGUMENT_BLOCK argumentBlockArray[KPHSS_MAXIMUM_ARGUMENT_BLOCKS]; + ULONG i, j; + + previousMode = ExGetPreviousMode(); + /* Ignore the Thread argument. Replace it with our own. */ + Thread = KeGetCurrentThread(); + + /* First, some checks. + * * We can't operate at IRQL > APC_LEVEL because + * of restrictions on logging. + * * We can't operate on unknown service tables like the + * shadow service table (yet). + * * We have to make sure we aren't attempting to log + * a call to ZwContinue because we caused an exception + * last time we were logging something. This will cause + * a deadlock! + */ + + if (KeGetCurrentIrql() > APC_LEVEL) + return; + if ( + ServiceTable->Base != __KeServiceDescriptorTable->Base || + ServiceTable->Number != __KeServiceDescriptorTable->Number || + ServiceTable->Limit != __KeServiceDescriptorTable->Limit + ) + return; + + /* Make sure we aren't logging ZwContinue if it's because + * we caused an exception somewhere. */ + if ( + ServiceTable->Base == __KeServiceDescriptorTable->Base && + Number == SsNtContinue && + NumberOfArguments == 2 && + previousMode == KernelMode + ) + { + /* "Reverse probe" the arguments. */ + if ( + (ULONG_PTR)Arguments > (ULONG_PTR)MmHighestUserAddress && + Arguments[0] > (ULONG_PTR)MmHighestUserAddress + ) + { + CONTEXT context; + + /* The first argument contains the context. */ + memcpy(&context, (PCONTEXT)Arguments[0], sizeof(CONTEXT)); + /* Check if the context Eip points into the KPH module. + * If so, abort the logging. + */ + if ( + context.Eip >= (ULONG_PTR)KphDriverObject->DriverStart && + context.Eip < (ULONG_PTR)KphDriverObject->DriverStart + KphDriverObject->DriverSize + ) + return; + } + } + + /* Build the ruleset entry array by going through the ruleset + * list, referencing each relevant one and copying them into + * the local array. This we way don't hold the lock for too + * long. + */ + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&KphSsRuleSetListPushLock); + + currentListEntry = KphSsRuleSetListHead.Flink; + ruleSetEntryCount = 0; + + while ( + currentListEntry != &KphSsRuleSetListHead && + ruleSetEntryCount < KPHSS_RULESET_ENTRY_LIMIT + ) + { + PKPHSS_RULESET_ENTRY ruleSetEntry = KPHSS_RULESET_ENTRY(currentListEntry); + + if (KphpSsMatchRuleSetEntry( + ruleSetEntry, + Number, + Arguments, + NumberOfArguments, + ServiceTable, + Thread, + previousMode + )) + { + /* Reference and store the ruleset entry in the local array. */ + if (KphReferenceObjectSafe(ruleSetEntry)) + { + /* Make sure the client is enabled. */ + if (ruleSetEntry->Client->Enabled) + { + ruleSetEntryArray[ruleSetEntryCount] = ruleSetEntry; + ruleSetEntryCount++; + } + else + { + /* We need to use defer delete here because we hold the + * ruleset list lock. + */ + KphDereferenceObjectDeferDelete(ruleSetEntry); + } + } + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&KphSsRuleSetListPushLock); + KeLeaveCriticalRegion(); + + /* If we didn't find any ruleset entries, don't bother creating the + * event block. + */ + if (ruleSetEntryCount == 0) + return; + + /* We have work to do. Create an event block first. */ + if (!NT_SUCCESS(KphpSsCreateEventBlock( + &eventBlock, + Thread, + Number, + Arguments, + NumberOfArguments + ))) + { + dfprintf("Ss: ERROR: Unable to create an event block!\n"); + return; + } + + memset(argumentBlockArray, 0, sizeof(argumentBlockArray)); + + /* Process specific argument blocks. */ + KphpSsProcessSpecificArguments( + argumentBlockArray, + Number, + Arguments, + NumberOfArguments, + previousMode + ); + + /* Create the (generic) argument blocks. If we fail to create one, + * set the array entry to NULL and we'll skip it later. + */ + + for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++) + { + ULONG argument; + + /* If we already have a specific argument block already, skip this one. */ + if (argumentBlockArray[i]) + continue; + + __try + { + /* We'll assume the arguments have already been probed + * since we created the event block successfully. + */ + argument = Arguments[i]; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* Silently skip this argument. Even though it is 99% likely + * that we will fail to read the next argument, continue + * anyway. + */ + argumentBlockArray[i] = NULL; + continue; + } + + status = KphpSsCreateArgumentBlock( + &argumentBlockArray[i], + Number, + argument, + i, + 0, + NULL + ); + + if (!NT_SUCCESS(status)) + argumentBlockArray[i] = NULL; + } + + /* Go through the ruleset entry array and write the blocks to each + * client. While we're doing that we can also dereference each + * ruleset entry. + */ + for (i = 0; i < ruleSetEntryCount; i++) + { + /* Begin a sequence. */ + status = KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, StartSequence); + + if (NT_SUCCESS(status)) + { + /* Write the event block. */ + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &eventBlock->Header, InSequence); + + /* Write the argument blocks. */ + for (j = 0; j < NumberOfArguments && j < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; j++) + { + if (argumentBlockArray[j]) + { + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &argumentBlockArray[j]->Header, InSequence); + } + } + + /* End the sequence. */ + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, EndSequence); + } + + KphDereferenceObject(ruleSetEntryArray[i]); + } + + /* Free the event block. */ + KphpSsFreeEventBlock(eventBlock); + + /* Free the argument blocks. */ + for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++) + { + if (argumentBlockArray[i]) + KphpSsFreeArgumentBlock(argumentBlockArray[i]); + } +#else + KeBugCheck(STATUS_NOT_SUPPORTED); +#endif +} + +#ifdef _X86_ + +/* KphpSsNewKiFastCallEntry + * + * The hook function called from within the hooked KiFastCallEntry. + */ +__declspec(naked) VOID NTAPI KphpSsNewKiFastCallEntry() +{ + /* KiFastCallEntry handles system service calls. User-mode applications + * will perform system calls like this: + * + * Nt*: + * mov eax, SystemServiceNumber + * mov edx, 0x7ffe0300 <-- at 0x7ffe0300 we have a pointer to KiFastSystemCall + * call [edx] + * ret + * + * At KiFastSystemCall: + * mov edx, esp + * sysenter + */ + /* This means that in KiFastCallEntry, eax will contain the system service + * number while edx will contain a pointer to the arguments for the system + * service. KiFastCallEntry will fill in edi with the service table, and + * esi will contain the caller KTHREAD. + * + * We cannot hook KiFastCallEntry from the beginning because it starts on the DPC + * stack. KiFastCallEntry switches to the proper thread stack, and we want to + * hook it just after it switches to the stack. That way we can avoid having to + * manually switch the thread stack ourselves. + * + * At this point: + * * eax contains the system service number. + * * edx contains a pointer to the user-supplied arguments for + * the system service. + * * edi contains a pointer to the service table associated with + * the system service number. + * * esi contains a pointer to the KTHREAD of the caller. + */ + /* Some context: + * + * push edx + * push eax + * call [_KeGdiFlushUserBatch] + * pop eax + * pop edx + * inc dword ptr fs:[PbSystemCalls] <-- this gets overwritten with a jmp to here + * mov edi, edx + * mov ebx, [edi+...] + * ... + */ + __asm + { + /* Save all registers first. */ + push ebp + push edi + push esi + push edx + push ecx + push ebx + push eax + + /* Since we overwrite the inc instruction when we did the hook, + * perform the job now - we have to increment the system calls + * counter. + */ + lea ebx, KphSsKiFastCallEntryHook /* get a pointer to the hook structure */ + mov ebx, dword ptr [ebx+KPH_HOOK.Bytes+3] /* get the PbSystemCalls offset from the original inc instruction */ + inc dword ptr fs:[ebx] /* increment PbSystemCalls in the PRCB */ + + /* Get the number of arguments for this system service. */ + mov ebx, dword ptr [edi+KSERVICE_TABLE_DESCRIPTOR.Number] /* ebx = a pointer to the argument table */ + xor ecx, ecx + mov cl, [ebx+eax] /* ecx = size of the arguments, in bytes. */ + shr ecx, 2 /* divide by 2 to get the number of arguments (all ULONGs) */ + + /* Call the KiFastCallEntry proc while maintaining the logger count + * so that the driver doesn't get unloaded while we're executing. + */ + push esi /* Thread */ + push edi /* ServiceTable */ + push ecx /* NumberOfArguments */ + push edx /* Arguments */ + push eax /* Number */ + lock inc dword ptr KphSsNumberOfActiveLoggers + call KphpSsLogSystemServiceCall + lock dec dword ptr KphSsNumberOfActiveLoggers + + /* Restore the registers and resume execution in KiFastCallEntry. */ + pop eax + pop ebx + pop ecx + pop edx + pop esi + pop edi + pop ebp + + /* Luckily, KiFastCallEntry will overwrite ebx when we jump back, so it's safe to use it. */ + lea ebx, __KiFastCallEntry + mov ebx, [ebx] /* ebx = KiFastCallEntry at the inc instruction */ + add ebx, 7 /* skip the inc instruction */ + jmp ebx /* jump back */ + } +} + +#else + +VOID NTAPI KphpSsNewKiFastCallEntry() +{ + KeBugCheck(STATUS_NOT_SUPPORTED); +} + +#endif diff --git a/2.x/trunk/KProcessHacker/sysservicedata.c b/2.x/trunk/KProcessHacker/sysservicedata.c new file mode 100644 index 000000000..c30c36917 --- /dev/null +++ b/2.x/trunk/KProcessHacker/sysservicedata.c @@ -0,0 +1,513 @@ +/* + * Process Hacker Driver - + * system service logging (data) + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _SYSSERVICEDATA_PRIVATE +#include "include/sysservicedata.h" + +PVOID KphpSsCallEntryAllocateRoutine( + __in PRTL_GENERIC_TABLE Table, + __in CLONG ByteSize + ); + +RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID FirstStruct, + __in PVOID SecondStruct + ); + +VOID KphpSsCallEntryFreeRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID Buffer + ); + +KPHSS_CALL_ENTRY SsEntries[] = +{ + /* NTSTATUS NtAddAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */ + { &SsNtAddAtom, "NtAddAtom", 3, { WStringArgument, 0, Int16Argument } }, + /* NTSTATUS NtAlertResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount) */ + { &SsNtAlertResumeThread, "NtAlertResumeThread", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtAlertThread(HANDLE ThreadHandle) */ + { &SsNtAlertThread, "NtAlertThread", 1, { HandleArgument } }, + /* NTSTATUS NtAllocateLocallyUniqueId(PLUID Luid) */ + { &SsNtAllocateLocallyUniqueId, "NtAllocateLocallyUniqueId", 1, { 0 } }, + /* NTSTATUS NtAllocateUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtAllocateUserPhysicalPages, "NtAllocateUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } }, + /* NTSTATUS NtAllocateUuids(PLARGE_INTEGER UuidLastTimeAllocated, PULONG UuidDeltaTime, PULONG UuidSequenceNumber, + * PUCHAR UuidSeed) */ + { &SsNtAllocateUuids, "NtAllocateUuids", 4, { Int64Argument, 0, 0, 0 } }, + /* NTSTATUS NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits, + * PULONG AllocationSize, ULONG AllocationType, ULONG Protect) */ + { &SsNtAllocateVirtualMemory, "NtAllocateVirtualMemory", 6, { HandleArgument, Int32Argument, 0, Int32Argument, 0, 0 } }, + /* NTSTATUS NtApphelpCacheControl(APPHELPCACHECONTROL ApphelpCacheControl, PUNICODE_STRING ApphelpCacheObject) */ + { &SsNtApphelpCacheControl, "NtApphelpCacheControl", 2, { 0, UnicodeStringArgument } }, + /* NTSTATUS NtAreMappedFilesTheSame(PVOID Address1, PVOID Address2) */ + { &SsNtAreMappedFilesTheSame, "NtAreMappedFilesTheSame", 2, { 0, 0 } }, + /* NTSTATUS NtAssignProcessToJobObject(HANDLE JobHandle, HANDLE ProcessHandle) */ + { &SsNtAssignProcessToJobObject, "NtAssignProcessToJobObject", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtCallbackReturn(PVOID Result, ULONG ResultLength, NTSTATUS Status) */ + { &SsNtCallbackReturn, "NtCallbackReturn", 3, { 0, 0, 0 } }, + /* NTSTATUS NtCancelDeviceWakeupRequest(HANDLE DeviceHandle) */ + { &SsNtCancelDeviceWakeupRequest, "NtCancelDeviceWakeupRequest", 1, { HandleArgument } }, + /* NTSTATUS NtCancelIoFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtCancelIoFile, "NtCancelIoFile", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtCancelTimer(HANDLE TimerHandle, PBOOLEAN CurrentState) */ + { &SsNtCancelTimer, "NtCancelTimer", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtClearEvent(HANDLE EventHandle) */ + { &SsNtClearEvent, "NtClearEvent", 1, { HandleArgument } }, + /* NTSTATUS NtClose(HANDLE Handle) */ + { &SsNtClose, "NtClose", 1, { HandleArgument } }, + /* NTSTATUS NtContinue(PCONTEXT Context, BOOLEAN TestAlert) */ + { &SsNtContinue, "NtContinue", 2, { ContextArgument, 0 } }, + /* NTSTATUS NtCreateDebugObject(PHANDLE DebugObjectHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG Flags) */ + { &SsNtCreateDebugObject, "NtCreateDebugObject", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateDirectoryObject, "NtCreateDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * EVENT_TYPE EventType, BOOLEAN InitialState) */ + { &SsNtCreateEvent, "NtCreateEvent", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } }, + /* NTSTATUS NtCreateEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateEventPair, "NtCreateEventPair", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, + * ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, + * PVOID EaBuffer, ULONG EaLength) */ + { &SsNtCreateFile, "NtCreateFile", 11, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG NumberOfConcurrentThreads) */ + { &SsNtCreateIoCompletion, "NtCreateIoCompletion", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateJobObject, "NtCreateJobObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateJobSet(ULONG NumJob, IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags) */ + { &SsNtCreateJobSet, "NtCreateJobSet", 3, { 0, 0, 0 } }, + /* NTSTATUS NtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, + * PULONG Disposition) */ + { &SsNtCreateKey, "NtCreateKey", 7, { 0, 0, ObjectAttributesArgument, 0, UnicodeStringArgument, 0, 0 } }, + /* NTSTATUS NtCreateKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG Flags) */ + { &SsNtCreateKeyedEvent, "NtCreateKeyedEvent", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateMailslotFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG CreateOptions, ULONG MailslotQuota, + * ULONG MaximumMessageSize, PLARGE_INTEGER ReadTimeout) */ + { &SsNtCreateMailslotFile, "NtCreateMailslotFile", 8, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, Int64Argument } }, + /* NTSTATUS NtCreateMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * BOOLEAN InitialOwner) */ + { &SsNtCreateMutant, "NtCreateMutant", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateNamedPipeFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG CreateDisposition, + * ULONG CreateOptions, BOOLEAN TypeMessage, BOOLEAN ReadmodeMessage, + * BOOLEAN Nonblocking, ULONG MaxInstances, ULONG InBufferSize, + * ULONG OutBufferSize, PLARGE_INTEGER DefaultTimeout) */ + { &SsNtCreateNamedPipeFile, "NtCreateNamedPipeFile", 14, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Int64Argument } }, + /* NTSTATUS NtCreatePagingFile(PUNICODE_STRING FileName, PULARGE_INTEGER MinimumSize, PULARGE_INTEGER MaximumSize, + * ULONG Priority) */ + { &SsNtCreatePagingFile, "NtCreatePagingFile", 4, { UnicodeStringArgument, Int64Argument, Int64Argument, 0 } }, + /* NTSTATUS NtCreatePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength, + * ULONG MaxMessageLength, ULONG MaxPoolUsage) */ + { &SsNtCreatePort, "NtCreatePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtCreatePrivateNamespace(PHANDLE PrivateNamespaceHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PBOUNDARY_DESCRIPTOR BoundaryDescriptor) */ + { &SsNtCreatePrivateNamespace, "NtCreatePrivateNamespace", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE InheritFromProcessHandle, BOOLEAN InheritHandles, HANDLE SectionHandle, + * HANDLE DebugPort, HANDLE ExceptionPort) */ + { &SsNtCreateProcess, "NtCreateProcess", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument } }, + /* NTSTATUS NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, + * HANDLE DebugPort, HANDLE ExceptionPort, ULONG JobMemberLevel */ + { &SsNtCreateProcessEx, "NtCreateProcessEx", 9, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument, 0 } }, + /* NTSTATUS NtCreateProfile(PHANDLE ProfileHandle, HANDLE ProcessHandle, PVOID Base, + * ULONG Size, ULONG BucketShift, PULONG Buffer, + * ULONG BufferLength, KPROFILE_SOURCE Source, ULONG ProcessorMask) */ + { &SsNtCreateProfile, "NtCreateProfile", 9, { 0, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PLARGE_INTEGER SectionSize, ULONG Protect, ULONG Attributes, + * HANDLE FileHandle) */ + { &SsNtCreateSection, "NtCreateSection", 7, { 0, 0, ObjectAttributesArgument, Int64Argument, 0, 0, HandleArgument } }, + /* NTSTATUS NtCreateSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * LONG InitialCount, LONG MaximumCount) */ + { &SsNtCreateSemaphore, "NtCreateSemaphore", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } }, + /* NTSTATUS NtCreateSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PUNICODE_STRING TargetName) */ + { &SsNtCreateSymbolicLinkObject, "NtCreateSymbolicLinkObject", 4, { 0, 0, ObjectAttributesArgument, UnicodeStringArgument } }, + /* NTSTATUS NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext, + * PINITIAL_TEB UserStack, BOOLEAN CreateSuspended) */ + { &SsNtCreateThread, "NtCreateThread", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, ContextArgument, InitialTebArgument, 0 } }, + /* NTSTATUS NtCreateTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * TIMER_TYPE TimerType) */ + { &SsNtCreateTimer, "NtCreateTimer", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateToken(PHANDLE TokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * TOKEN_TYPE Type, PLUID AuthenticationId, PLARGE_INTEGER ExpirationTime, + * PTOKEN_USER User, PTOKEN_GROUPS Groups, PTOKEN_PRIVILEGES Privileges, + * PTOKEN_OWNER Owner, PTOKEN_PRIMARY_GROUP PrimaryGroup, PTOKEN_DEFAULT_DACL DefaultDacl, + * PTOKEN_SOURCE Source) */ + { &SsNtCreateToken, "NtCreateToken", 13, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, Int64Argument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateWaitablePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength, + * ULONG MaxMessageLength, ULONG MaxPoolUsage) */ + { &SsNtCreateWaitablePort, "NtCreateWaitablePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtDebugActiveProcess(HANDLE ProcessHandle, HANDLE DebugObjectHandle) */ + { &SsNtDebugActiveProcess, "NtDebugActiveProcess", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtDebugContinue(HANDLE DebugObjectHandle, PCLIENT_ID ClientId, NTSTATUS ContinueStatus) */ + { &SsNtDebugContinue, "NtDebugContinue", 3, { HandleArgument, ClientIdArgument, 0 } }, + /* NTSTATUS NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER Interval) */ + { &SsNtDelayExecution, "NtDelayExecution", 2, { 0, Int64Argument } }, + /* NTSTATUS NtDeleteAtom(USHORT Atom) */ + { &SsNtDeleteAtom, "NtDeleteAtom", 1, { 0 } }, + /* NTSTATUS NtDeleteBootEntry(ULONG Id) */ + { &SsNtDeleteBootEntry, "NtDeleteBootEntry", 1, { 0 } }, + /* NTSTATUS NtDeleteDriverEntry(ULONG Id) */ + { &SsNtDeleteDriverEntry, "NtDeleteDriverEntry", 1, { 0 } }, + /* NTSTATUS NtDeleteFile(POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtDeleteFile, "NtDeleteFile", 1, { ObjectAttributesArgument } }, + /* NTSTATUS NtDeleteKey(HANDLE KeyHandle) */ + { &SsNtDeleteKey, "NtDeleteKey", 1, { HandleArgument } }, + /* NTSTATUS NtDeleteObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID HandleId, BOOLEAN GenerateOnClose) */ + { &SsNtDeleteObjectAuditAlarm, "NtDeleteObjectAuditAlarm", 3, { UnicodeStringArgument, 0, 0 } }, + /* NTSTATUS NtDeletePrivateNamespace(HANDLE PrivateNamespaceHandle) */ + { &SsNtDeletePrivateNamespace, "NtDeletePrivateNamespace", 1, { HandleArgument } }, + /* NTSTATUS NtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName) */ + { &SsNtDeleteValueKey, "NtDeleteValueKey", 2, { HandleArgument, UnicodeStringArgument } }, + /* NTSTATUS NtDeviceIoControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, + * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, + * ULONG OutputBufferLength) */ + { &SsNtDeviceIoControlFile, "NtDeviceIoControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtDisplayString(PUNICODE_STRING String) */ + { &SsNtDisplayString, "NtDisplayString", 1, { UnicodeStringArgument } }, + /* NTSTATUS NtDuplicateObject(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, + * PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, + * ULONG Options) */ + { &SsNtDuplicateObject, "NtDuplicateObject", 7, { HandleArgument, HandleArgument, HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtDuplicateToken(HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, PHANDLE NewTokenHandle) */ + { &SsNtDuplicateToken, "NtDuplicateToken", 6, { HandleArgument, 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtEnumerateBootEntries(PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateBootEntries, "NtEnumerateBootEntries", 2, { 0, Int32Argument } }, + /* NTSTATUS NtEnumerateDriverEntries(PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateDriverEntries, "NtEnumerateDriverEntries", 2, { 0, Int32Argument } }, + /* NTSTATUS NtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, + * PVOID KeyInformation, ULONG KeyInformationLength, PULONG ResultLength) */ + { &SsNtEnumerateKey, "NtEnumerateKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtEnumerateSystemEnvironmentValuesEx(ULONG InformationClass, PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateSystemEnvironmentValuesEx, "NtEnumerateSystemEnvironmentValuesEx", 3, { 0, 0, Int32Argument } }, + /* NTSTATUS NtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + * PVOID KeyValueInformation, ULONG KeyValueInformationLength, PULONG ResultLength) */ + { &SsNtEnumerateValueKey, "NtEnumerateValueKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtExtendSection(HANDLE SectionHandle, PLARGE_INTEGER SectionSize) */ + { &SsNtExtendSection, "NtExtendSection", 2, { HandleArgument, Int64Argument } }, + /* NTSTATUS NtFilterToken(HANDLE ExistingTokenHandle, ULONG Flags, PTOKEN_GROUPS SidsToDisable, + * PTOKEN_PRIVILEGES PrivilegesToDelete, PTOKEN_GROUPS SidsToRestricted, PHANDLE NewTokenHandle) */ + { &SsNtFilterToken, "NtFilterToken", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtFindAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */ + { &SsNtFindAtom, "NtFindAtom", 3, { WStringArgument, 0, 0 } }, + /* NTSTATUS NtFlushBuffersFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtFlushBuffersFile, "NtFlushBuffersFile", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtFlushInstructionCache(HANDLE ProcessHandle, PVOID BaseAddress, ULONG FlushSize) */ + { &SsNtFlushInstructionCache, "NtFlushInstructionCache", 3, { HandleArgument, 0, 0 } }, + /* NTSTATUS NtFlushKey(HANDLE KeyHandle) */ + { &SsNtFlushKey, "NtFlushKey", 1, { HandleArgument } }, + /* NTSTATUS NtFlushProcessWriteBuffers() */ + { &SsNtFlushProcessWriteBuffers, "NtFlushProcessWriteBuffers", 0 }, + /* NTSTATUS NtFlushVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FlushSize, + * PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtFlushVirtualMemory, "NtFlushVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtFlushWriteBuffer() */ + { &SsNtFlushWriteBuffer, "NtFlushWriteBuffer", 0 }, + /* NTSTATUS NtFreeUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtFreeUserPhysicalPages, "NtFreeUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } }, + /* NTSTATUS NtFreeVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FreeSize, + * ULONG FreeType) */ + { &SsNtFreeVirtualMemory, "NtFreeVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, + * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, + * ULONG OutputBufferLength) */ + { &SsNtFsControlFile, "NtFsControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context) */ + { &SsNtGetContextThread, "NtGetContextThread", 2, { HandleArgument, ContextArgument } }, + /* NTSTATUS NtGetCurrentProcessorNumber() */ + { &SsNtGetCurrentProcessorNumber, "NtGetCurrentProcessorNumber", 0 }, + /* NTSTATUS NtGetDevicePowerState(HANDLE DeviceHandle, PDEVICE_POWER_STATE DevicePowerState) */ + { &SsNtGetDevicePowerState, "NtGetDevicePowerState", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtGetNextProcess(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, + * ULONG Flags, PHANDLE NewProcessHandle) */ + { &SsNtGetNextProcess, "NtGetNextProcess", 5, { HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetNextThread(HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, + * ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle) */ + { &SsNtGetNextThread, "NtGetNextThread", 6, { HandleArgument, HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetPlugPlayEvent(HANDLE EventHandle, PVOID Context, PVOID Buffer, + * ULONG BufferLength) */ + { &SsNtGetPlugPlayEvent, "NtGetPlugPlayEvent", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtGetWriteWatch(HANDLE ProcessHandle, ULONG Flags, PVOID BaseAddress, + * ULONG RegionSize, PULONG Buffer, PULONG BufferEntries, + * PULONG Granularity) */ + { &SsNtGetWriteWatch, "NtGetWriteWatch", 7, { HandleArgument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtImpersonateAnonymousToken(HANDLE ThreadHandle) */ + { &SsNtImpersonateAnonymousToken, "NtImpersonateAnonymousToken", 1, { HandleArgument } }, + /* NTSTATUS NtImpersonateClientOfPort(HANDLE PortHandle, PPORT_MESSAGE Message) */ + { &SsNtImpersonateClientOfPort, "SsNtImpersonateClientOfPort", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtImpersonateThread(HANDLE ThreadHandle, HANDLE TargetThreadHandle, PSECURITY_QUALITY_OF_SERVICE SecurityQos) */ + { &SsNtImpersonateThread, "NtImpersonateThread", 3, { HandleArgument, HandleArgument, 0 } }, + /* NTSTATUS NtInitiatePowerAction(POWER_ACTION SystemAction, SYSTEM_POWER_STATE MinSystemState, ULONG Flags, + * BOOLEAN Asynchronous) */ + { &SsNtInitiatePowerAction, "NtInitiatePowerAction", 4, { 0, 0, 0, 0 } }, + /* NTSTATUS NtIsProcessInJob(HANDLE ProcessHandle, HANDLE JobHandle) */ + { &SsNtIsProcessInJob, "NtIsProcessInJob", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtIsSystemResumeAutomatic() */ + { &SsNtIsSystemResumeAutomatic, "NtIsSystemResumeAutomatic", 0 }, + /* NTSTATUS NtListenPort(HANDLE PortHandle, PPORT_MESSAGE Message) */ + { &SsNtListenPort, "NtListenPort", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtLoadDriver(PUNICODE_STRING DriverServiceName) */ + { &SsNtLoadDriver, "NtLoadDriver", 1, { UnicodeStringArgument } }, + /* NTSTATUS NtLoadKey(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes) */ + { &SsNtLoadKey, "NtLoadKey", 2, { ObjectAttributesArgument, ObjectAttributesArgument } }, + /* NTSTATUS NtLoadKey2(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes, ULONG Flags) */ + { &SsNtLoadKey2, "NtLoadKey2", 3, { ObjectAttributesArgument, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtLockFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PULARGE_INTEGER LockOffset, + * PULARGE_INTEGER LockLength, ULONG Key, BOOLEAN FailImmediately, + * BOOLEAN ExclusiveLock) */ + { &SsNtLockFile, "NtLockFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, Int64Argument, Int64Argument, 0, 0, 0 } }, + /* NTSTATUS NtLockVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG LockSize, + * ULONG LockType) */ + { &SsNtLockVirtualMemory, "NtLockVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtMakePermanentObject(HANDLE Handle) */ + { &SsNtMakePermanentObject, "NtMakePermanentObject", 1, { HandleArgument } }, + /* NTSTATUS NtMakeTemporaryObject(HANDLE Handle) */ + { &SsNtMakeTemporaryObject, "NtMakeTemporaryObject", 1, { HandleArgument } }, + /* NTSTATUS NtMapUserPhysicalPages(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtMapUserPhysicalPages, "NtMapUserPhysicalPages", 3, { 0, Int32Argument, 0 } }, + /* NTSTATUS NtMapUserPhysicalPagesScatter(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtMapUserPhysicalPagesScatter, "NtMapUserPhysicalPagesScatter", 3, { 0, Int32Argument, 0 } }, + /* NTSTATUS NtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID *BaseAddress, + * ULONG ZeroBits, ULONG CommitSize, PLARGE_INTEGER SectionOffset, + * PULONG ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, + * ULONG Protect) */ + { &SsNtMapViewOfSection, "NtMapViewOfSection", 10, { HandleArgument, HandleArgument, Int32Argument, 0, 0, Int64Argument, Int32Argument, 0, 0, 0 } }, + /* NTSTATUS NtModifyBootEntry(PBOOT_ENTRY BootEntry) */ + { &SsNtModifyBootEntry, "NtModifyBootEntry", 1, { 0 } }, + /* NTSTATUS NtModifyDriverEntry(PEFI_DRIVER_ENTRY DriverEntry) */ + { &SsNtModifyDriverEntry, "NtModifyDriverEntry", 1, { 0 } }, + /* NTSTATUS NtNotifyChangeDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PFILE_NOTIFY_INFORMATION Buffer, + * ULONG BufferLength, ULONG NotifyFilter, BOOLEAN WatchSubtree) */ + { &SsNtNotifyChangeDirectoryFile, "NtNotifyChangeDirectoryFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtNotifyChangeKey(HANDLE KeyHandle, HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter, + * BOOLEAN WatchSubtree, PVOID Buffer, ULONG BufferLength, + * BOOLEAN Asynchronous) */ + { &SsNtNotifyChangeKey, "NtNotifyChangeKey", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtNotifyChangeMultipleKeys(HANDLE KeyHandle, ULONG Flags, POBJECT_ATTRIBUTES KeyObjectAttributes, + * HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter, BOOLEAN WatchSubtree, + * PVOID Buffer, ULONG BufferLength, BOOLEAN Asynchronous) */ + { &SsNtNotifyChangeMultipleKeys, "NtNotifyChangeMultipleKeys", 12, { HandleArgument, 0, ObjectAttributesArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenDirectoryObject, "NtOpenDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenEvent, "NtOpenEvent", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenEventPair, "NtOpenEventPair", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions) */ + { &SsNtOpenFile, "NtOpenFile", 6, { 0, 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenIoCompletion, "NtOpenIoCompletion", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenJobObject, "NtOpenJobObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenKey, "NtOpenKey", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenKeyedEvent, "NtOpenKeyedEvent", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenMutant, "NtOpenMutant", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID *HandleId, PUNICODE_STRING ObjectTypeName, + * PUNICODE_STRING ObjectName, PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE TokenHandle, + * ACCESS_MASK DesiredAccess, ACCESS_MASK GrantedAccess, PPRIVILEGE_SET Privileges, + * BOOLEAN ObjectCreation, BOOLEAN AccessGranted, PBOOLEAN GenerateOnClose) */ + { &SsNtOpenObjectAuditAlarm, "NtOpenObjectAuditAlarm", 12, { UnicodeStringArgument, Int32Argument, UnicodeStringArgument, UnicodeStringArgument, 0, HandleArgument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PCLIENT_ID ClientId) */ + { &SsNtOpenProcess, "NtOpenProcess", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } }, + /* NTSTATUS NtOpenProcessToken(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, PHANDLE TokenHandle) */ + { &SsNtOpenProcessToken, "NtOpenProcessToken", 3, { HandleArgument, 0, 0 } }, + /* NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, + * PHANDLE TokenHandle) */ + { &SsNtOpenProcessTokenEx, "NtOpenProcessTokenEx", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSection, "NtOpenSection", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSemaphore, "NtOpenSemaphore", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSymbolicLinkObject, "NtOpenSymbolicLinkObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PCLIENT_ID ClientId) */ + { &SsNtOpenThread, "NtOpenThread", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } }, + /* NTSTATUS NtOpenThreadToken(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf, + * PHANDLE TokenHandle) */ + { &SsNtOpenThreadToken, "NtOpenThreadToken", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenThreadTokenEx(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf, + * ULONG HandleAttributes, PHANDLE TokenHandle) */ + { &SsNtOpenThreadTokenEx, "NtOpenThreadTokenEx", 5, { HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenTimer, "NtOpenTimer", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, + * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */ + { &SsNtReadFile, "NtReadFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } }, + /* NTSTATUS NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, + * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */ + { &SsNtWriteFile, "NtWriteFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } }, + + { NULL, "Dummy", 0 } +}; + +RTL_GENERIC_TABLE KphSsCallTable; +FAST_MUTEX KphSsCallTableMutex; + +/* KphSsDataInit + * + * Initializes all data structures so that system service entries + * can be looked up. + */ +VOID KphSsDataInit() +{ + ULONG i; + + RtlInitializeGenericTable( + &KphSsCallTable, + KphpSsCallEntryCompareRoutine, + KphpSsCallEntryAllocateRoutine, + KphpSsCallEntryFreeRoutine, + NULL + ); + + for (i = 0; i < sizeof(SsEntries) / sizeof(KPHSS_CALL_ENTRY); i++) + { + /* Ignore the dummy entry. */ + if (SsEntries[i].Number) + { + RtlInsertElementGenericTable( + &KphSsCallTable, + &SsEntries[i], + /* Save some space... */ + FIELD_OFFSET(KPHSS_CALL_ENTRY, Arguments) + + SsEntries[i].NumberOfArguments * sizeof(KPHSS_ARGUMENT_TYPE), + NULL + ); + } + } + + ExInitializeFastMutex(&KphSsCallTableMutex); +} + +/* KphSsDataDeinit + * + * Frees all memory associated with system service data. + */ +VOID KphSsDataDeinit() +{ + PKPHSS_CALL_ENTRY callEntry; + + while (callEntry = (PKPHSS_CALL_ENTRY)RtlGetElementGenericTable(&KphSsCallTable, 0)) + RtlDeleteElementGenericTable(&KphSsCallTable, callEntry); +} + +/* KphSsLookupCallEntry + * + * Lookups up a system service entry by system service number. + */ +PKPHSS_CALL_ENTRY KphSsLookupCallEntry( + __in ULONG Number + ) +{ + KPHSS_CALL_ENTRY callEntry; + PKPHSS_CALL_ENTRY foundEntry; + + callEntry.Number = &Number; + + ExAcquireFastMutex(&KphSsCallTableMutex); + foundEntry = (PKPHSS_CALL_ENTRY)RtlLookupElementGenericTable( + &KphSsCallTable, + &callEntry + ); + ExReleaseFastMutex(&KphSsCallTableMutex); + + return foundEntry; +} + +/* KphpSsCallEntryAllocateRoutine + * + * Allocates storage for a system service entry. + */ +PVOID KphpSsCallEntryAllocateRoutine( + __in PRTL_GENERIC_TABLE Table, + __in CLONG ByteSize + ) +{ + return ExAllocatePoolWithTag( + PagedPool, + ByteSize, + TAG_CALL_ENTRY + ); +} + +/* KphpSsCallEntryCompareRoutine + * + * Compares two system service entries. + */ +RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID FirstStruct, + __in PVOID SecondStruct + ) +{ + PKPHSS_CALL_ENTRY callEntry1, callEntry2; + + callEntry1 = (PKPHSS_CALL_ENTRY)FirstStruct; + callEntry2 = (PKPHSS_CALL_ENTRY)SecondStruct; + + if (*(callEntry1->Number) < *(callEntry2->Number)) + return GenericLessThan; + else if (*(callEntry1->Number) > *(callEntry2->Number)) + return GenericGreaterThan; + else + return GenericEqual; +} + +/* KphpSsCallEntryFreeRoutine + * + * Frees storage for a system service entry. + */ +VOID KphpSsCallEntryFreeRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID Buffer + ) +{ + ExFreePoolWithTag( + Buffer, + TAG_CALL_ENTRY + ); +} diff --git a/2.x/trunk/KProcessHacker/test.c b/2.x/trunk/KProcessHacker/test.c new file mode 100644 index 000000000..ffc9d6016 --- /dev/null +++ b/2.x/trunk/KProcessHacker/test.c @@ -0,0 +1,71 @@ +/* + * Process Hacker Driver - + * testing code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" + +static EX_PUSH_LOCK TestLock; + +VOID KphpTestPushLockThreadStart( + __in PVOID Context + ); + +VOID KphTestPushLock() +{ + ULONG i; + + ExInitializePushLock(&TestLock); + + for (i = 0; i < 10; i++) + { + HANDLE threadHandle; + OBJECT_ATTRIBUTES objectAttributes; + + InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + PsCreateSystemThread(&threadHandle, 0, &objectAttributes, NULL, NULL, KphpTestPushLockThreadStart, NULL); + } +} + +VOID KphpTestPushLockThreadStart( + __in PVOID Context + ) +{ + ULONG i, j; + + for (i = 0; i < 400000; i++) + { + ExAcquirePushLockShared(&TestLock); + + for (j = 0; j < 1000; j++) + YieldProcessor(); + + ExReleasePushLock(&TestLock); + + ExAcquirePushLockExclusive(&TestLock); + + for (j = 0; j < 9000; j++) + YieldProcessor(); + + ExReleasePushLock(&TestLock); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} diff --git a/2.x/trunk/KProcessHacker/trace.c b/2.x/trunk/KProcessHacker/trace.c new file mode 100644 index 000000000..57fc98ede --- /dev/null +++ b/2.x/trunk/KProcessHacker/trace.c @@ -0,0 +1,344 @@ +/* + * Process Hacker Driver - + * stack tracing + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" + +BOOLEAN KphpCaptureAndAddStack( + __in PRTL_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +VOID KphpTraceDatabaseDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +PKPH_OBJECT_TYPE KphTraceDatabaseType; + +/* KphTraceDatabaseInitialization + * + * Creates the TraceDatabase object type. + */ +NTSTATUS KphTraceDatabaseInitialization() +{ + NTSTATUS status = STATUS_SUCCESS; + + status = KphCreateObjectType( + &KphTraceDatabaseType, + PagedPool, + 0, + KphpTraceDatabaseDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + return status; + + return status; +} + +/* KphCaptureStackBackTrace + * + * Walks the stack, capturing the return address from each frame. + * + * Return value: the number of captured addresses in the buffer. + */ +ULONG KphCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __in_opt ULONG Flags, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG BackTraceHash + ) +{ + PVOID backTrace[MAX_STACK_DEPTH]; + ULONG framesFound; + ULONG hash; + ULONG i; + + /* Skip the current frame (for this function). */ + FramesToSkip++; + + /* Check the input. */ + /* Ensure we won't overrun the buffer. */ + if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH) + return 0; + /* Make sure the flags are correct. */ + if ((Flags & RTL_WALK_VALID_FLAGS) != Flags) + return 0; + + /* Walk the frame chain. */ + framesFound = RtlWalkFrameChain( + backTrace, + FramesToCapture + FramesToSkip, + Flags + ); + /* Return if we found fewer frames than we wanted to skip. */ + if (framesFound <= FramesToSkip) + return 0; + + /* Copy over the stack trace. + * At the same time we calculate the stack trace hash by + * summing the addresses. + */ + for (i = 0, hash = 0; i < FramesToCapture; i++) + { + if (FramesToSkip + i >= framesFound) + break; + + BackTrace[i] = backTrace[FramesToSkip + i]; + hash += PtrToUlong(BackTrace[i]); + } + + /* Pass the hash back if the caller requested it. */ + if (BackTraceHash) + *BackTraceHash = hash; + + /* Return the number of addresses we copied. */ + return i; +} + +/* KphCaptureAndAddStack + * + * Captures a stack trace and adds it to a trace database. + */ +BOOLEAN KphCaptureAndAddStack( + __in PKPH_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ) +{ + return KphpCaptureAndAddStack( + Database->Database, + Type, + TraceBlock + ); +} + +/* KphCreateTraceDatabase + * + * Creates a trace database. + */ +NTSTATUS KphCreateTraceDatabase( + __out PKPH_TRACE_DATABASE *Database, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, + __in ULONG Tag + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PRTL_TRACE_DATABASE rtlDatabase; + PKPH_TRACE_DATABASE database; + + /* Create the trace database. */ + rtlDatabase = RtlTraceDatabaseCreate( + 8, + MaximumSize, + Flags, + Tag, + NULL + ); + + if (!rtlDatabase) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Create the object. */ + status = KphCreateObject( + &database, + sizeof(KPH_TRACE_DATABASE), + 0, + KphTraceDatabaseType, + 0 + ); + + if (!NT_SUCCESS(status)) + { + /* Destroy the trace database, since we can't use it. */ + RtlTraceDatabaseDestroy(rtlDatabase); + + return status; + } + + /* Set up the trace database object. */ + database->Database = rtlDatabase; + *Database = database; + + return status; +} + +NTSTATUS KphQueryTraceDatabase( + __in PKPH_TRACE_DATABASE Database, + __out_bcount_opt(BufferLength) PKPH_TRACEDB_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PRTL_TRACE_DATABASE rtlDatabase = Database->Database; + PKPH_TRACEDB_INFORMATION nextEntry; + RTL_TRACE_ENUMERATE enumContext = { 0 }; + PRTL_TRACE_BLOCK currentBlock; + + /* Probe buffers. */ + if (AccessMode != KernelMode) + { + __try + { + if (Buffer) + ProbeForWrite(Buffer, BufferLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* First entry to write to. */ + /* Note that this is completely safe if Buffer is NULL. */ + nextEntry = Buffer; + + /* Enumerate the trace blocks. */ + while (RtlTraceDatabaseEnumerate(rtlDatabase, &enumContext, ¤tBlock)) + { + PKPH_TRACEDB_INFORMATION currentEntry; + + /* Save the pointer to the entry we are about to write to. */ + currentEntry = nextEntry; + /* Compute the location of the next entry. */ + nextEntry = (PKPH_TRACEDB_INFORMATION)( + (ULONG_PTR)currentEntry + /* Current entry plus */ + sizeof(KPH_TRACEDB_INFORMATION) - /* the size of the current entry minus */ + sizeof(PVOID) + /* the extra PVOID in the Trace array plus */ + currentBlock->Size * sizeof(PVOID) /* the size of the stack trace. */ + ); + + if ( + /* If we got an error last time we tried to write to the buffer, + * don't try again this time. */ + NT_SUCCESS(status) && + /* Make sure the buffer isn't NULL. */ + Buffer && + /* Make sure we don't exceed the buffer length. */ + ((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer) <= BufferLength + ) + { + __try + { + currentEntry->NextEntryOffset = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)currentEntry); + currentEntry->Count = currentBlock->Count; + currentEntry->TraceSize = currentBlock->Size; + memcpy(currentEntry->Trace, currentBlock->Trace, currentBlock->Size * sizeof(PVOID)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + { + __try + { + *ReturnLength = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* KphCaptureAndAddStack + * + * Captures a stack trace and adds it to a trace database. + */ +BOOLEAN KphpCaptureAndAddStack( + __in PRTL_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ) +{ + PVOID trace[MAX_STACK_DEPTH * 2]; + ULONG kmodeFramesFound = 0; + ULONG umodeFramesFound = 0; + + /* Check input. */ + if (Type >= KphCaptureAndAddMaximum) + return FALSE; + + /* Capture the kernel-mode stack if needed. */ + if ( + Type == KphCaptureAndAddKModeStack || + Type == KphCaptureAndAddBothStacks + ) + kmodeFramesFound = KphCaptureStackBackTrace( + 1, + MAX_STACK_DEPTH - 1, + 0, + trace, + NULL + ); + /* Capture the user-mode stack if needed. */ + if ( + Type == KphCaptureAndAddUModeStack || + Type == KphCaptureAndAddBothStacks + ) + umodeFramesFound = KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + RTL_WALK_USER_MODE_STACK, + &trace[kmodeFramesFound], + NULL + ); + + /* Add the trace to the database. */ + return RtlTraceDatabaseAdd( + Database, + kmodeFramesFound + umodeFramesFound, + trace, + TraceBlock + ); +} + +/* KphpTraceDatabaseDeleteProcedure + * + * Destroys a trace database. + */ +VOID KphpTraceDatabaseDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPH_TRACE_DATABASE database = (PKPH_TRACE_DATABASE)Object; + + RtlTraceDatabaseDestroy(database->Database); +} diff --git a/2.x/trunk/KProcessHacker/util.c b/2.x/trunk/KProcessHacker/util.c new file mode 100644 index 000000000..61e3b3ecf --- /dev/null +++ b/2.x/trunk/KProcessHacker/util.c @@ -0,0 +1,115 @@ +/* + * Process Hacker Driver - + * utility functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/util.h" + +/* KphInitializeStream + * + * Initializes a stream. + * + * Stream: The stream to initialize. + * Buffer: The buffer to use. + * Length: The maximum number of bytes that can be stored in + * the buffer. If an attempt is made to overrun or underrun + * the buffer, an exception will be raised. + */ +VOID KphInitializeStream( + __out PKPH_STREAM Stream, + __in PVOID Buffer, + __in ULONG Length + ) +{ + ASSERT(Length > 0); + + Stream->Buffer = Buffer; + Stream->Length = Length; + Stream->Position = 0; +} + +/* KphSeekStream + * + * Changes the position of a stream. + */ +ULONG KphSeekStream( + __inout PKPH_STREAM Stream, + __in LONG Offset, + __in KPH_STREAM_ORIGIN Origin + ) +{ + ULONG newPosition; + + switch (Origin) + { + case StartOrigin: + { + /* Can't seek to before the start of the buffer. */ + if (Offset < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + newPosition = Offset; + } + break; + + case CurrentOrigin: + { + newPosition = Stream->Position + Offset; + } + break; + + case EndOrigin: + { + newPosition = Stream->Length - Offset - 1; + } + break; + } + + /* Check the new position and raise an exception if + * appropriate. + */ + KphCheckStreamPosition(Stream, newPosition); + Stream->Position = newPosition; + + return newPosition; +} + +/* KphWriteDataStream + * + * Writes data to a stream. + */ +ULONG KphWriteDataStream( + __inout PKPH_STREAM Stream, + __in PVOID Data, + __in ULONG Length + ) +{ + /* Check if we are going to overrun the buffer. */ + KphCheckStreamPosition(Stream, Stream->Position + Length); + /* Copy the data. */ + memcpy( + PTR_ADD_OFFSET(Stream->Buffer, Stream->Position), + Data, + Length + ); + + /* Increase the position. */ + return Stream->Position += Length; +} diff --git a/2.x/trunk/KProcessHacker/version.c b/2.x/trunk/KProcessHacker/version.c new file mode 100644 index 000000000..ef2e9bb2b --- /dev/null +++ b/2.x/trunk/KProcessHacker/version.c @@ -0,0 +1,611 @@ +/* + * Process Hacker Driver - + * Windows version-specific data + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _VERSION_PRIVATE +#include "include/version.h" +#include "include/debug.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KvInit) +#pragma alloc_text(PAGE, KvScanProc) +#pragma alloc_text(PAGE, KvVerifyPrologue) +#endif + +/* + * mov edi, edi + * push ebp + * mov ebp, esp + */ +static char StandardPrologue[] = { 0x8b, 0xff, 0x55, 0x8b, 0xec }; + +/* KiFastCallEntry */ +/* + * Note that this scan will get the address of + * mov esi, edx + * within KiFastCallEntry, not the start of KiFastCallEntry. + * We will then subtract 7 to get the address of + * inc dword ptr fs:PbSystemCalls + * See sysservice.c for more details. + */ +static char KiFastCallEntry51[] = +{ + 0x8b, 0xf2, 0x8b, 0x5f, 0x0c, 0x33, 0xc9, 0x8a, + 0x0c, 0x18, 0x8b, 0x3f, 0x8b, 0x1c, 0x87, 0x2b +}; +static char KiFastCallEntry52[] = +{ + 0x8b, 0xf2, 0x8b, 0x5f, 0x0c, 0x33, 0xc9, 0x8a, + 0x0c, 0x18, 0x8b, 0x3f, 0x8b, 0x1c, 0x87, 0x2b +}; /* same as 5.1 */ +static char KiFastCallEntry60[] = +{ + 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b, + 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b +}; +static char KiFastCallEntry61[] = +{ + 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b, + 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b +}; /* same as 6.0 */ +/* Below is the scan to find the start of KiFastCallEntry. */ +/* static char KiFastCallEntry[] = +{ + 0xb9, 0x23, 0x00, 0x00, 0x00, 0x6a, 0x30, 0x0f, + 0xa1, 0x8e, 0xd9, 0x8e, 0xc1, 0x64, 0x8b, 0x0d +}; */ + +/* PsExitSpecialApc */ +static char PsExitSpecialApc51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x64, 0xa1, 0x24, + 0x01, 0x00, 0x00, 0x8b, 0x45, 0x08, 0xf6, 0x40 +}; +static char PsExitSpecialApc60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01 +}; +static char PsExitSpecialApc61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01 +}; /* same as 6.0 */ + +/* PsTerminateProcess/PspTerminateProcess */ +static char PspTerminateProcess51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x64, 0xa1, + 0x24, 0x01, 0x00, 0x00, 0x8b, 0x75, 0x08, 0x3b +}; +static char PspTerminateProcess52[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x8b, 0x75, + 0x08, 0x57, 0x8d, 0xbe, 0x40, 0x02, 0x00, 0x00 +}; +static char PsTerminateProcess60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57, + 0x33, 0xd2, 0x6a, 0x08, 0x42, 0x5e, 0x8d, 0xb9 +}; +static char PsTerminateProcess61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x51, 0x51, 0x53, + 0x56, 0x64, 0x8b, 0x35, 0x24, 0x01, 0x00, 0x00, + 0x66, 0xff, 0x8e, 0x84, 0x00, 0x00, 0x00, 0x57, + 0xc7, 0x45, 0xfc +}; /* a lot of functions seem to share the first + * 16 bytes of the Windows 7 PsTerminateProcess, + * and a few even share the first 24 bytes. + */ + +/* PspTerminateThreadByPointer */ +static char PspTerminateThreadByPointer51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xec, 0x0c, + 0x83, 0x4d, 0xf8, 0xff, 0x56, 0x57, 0x8b, 0x7d +}; +static char PspTerminateThreadByPointer52[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57, + 0x8b, 0x7d, 0x08, 0x8d, 0xb7, 0x40, 0x02, 0x00 +}; +static char PspTerminateThreadByPointer60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d, + 0xbe, 0x60, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40 +}; +static char PspTerminateThreadByPointer61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d, + 0xbe, 0x80, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40 +}; + +/* The following offsets took me a long time to work out, so + please do not steal them. If you want to use them, please + license your project under the GNU GPL (although you are + not legally required to). + */ +NTSTATUS KvInit() +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG majorVersion, minorVersion, servicePack, buildNumber; + + /* Get Windows version information. */ + + RtlWindowsVersion.dwOSVersionInfoSize = sizeof(RtlWindowsVersion); + status = RtlGetVersion((PRTL_OSVERSIONINFOW)&RtlWindowsVersion); + + if (!NT_SUCCESS(status)) + return status; + + majorVersion = RtlWindowsVersion.dwMajorVersion; + minorVersion = RtlWindowsVersion.dwMinorVersion; + servicePack = RtlWindowsVersion.wServicePackMajor; + buildNumber = RtlWindowsVersion.dwBuildNumber; + dfprintf("Windows %d.%d, SP%d.%d, build %d\n", + majorVersion, minorVersion, servicePack, + RtlWindowsVersion.wServicePackMinor, buildNumber + ); + + __NtClose = GetSystemRoutineAddress(L"NtClose"); + + /* NtClose is used as a reference point for most addresses + dependent on where the kernel is loaded, so if we don't + have it, we can't proceed. + */ + if (!__NtClose) + return STATUS_NOT_SUPPORTED; + + /* We also need the address of ZwClose to get KiFastCallEntry. */ + __ZwClose = GetSystemRoutineAddress(L"ZwClose"); + + if (!__ZwClose) + return STATUS_NOT_SUPPORTED; + + /* Windows XP */ + if (majorVersion == 5 && minorVersion == 1) + { + ULONG_PTR searchOffset = (ULONG_PTR)__NtClose; + + WindowsVersion = WINDOWS_XP; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; + + OffEtClientId = 0x1ec; + OffEtSpareByteForSs = 0x256; /* Padding, last */ + OffEtStartAddress = 0x224; + OffEtWin32StartAddress = 0x228; + OffEpJob = 0x134; + OffEpObjectTable = 0xc4; + OffEpProtectedProcessOff = 0; + OffEpProtectedProcessBit = 0; + OffEpRundownProtect = 0x80; + OffOhBody = 0x18; + OffOtName = 0x40; + OffOtiGenericMapping = 0x60 + 0x8; + OffOtiOpenProcedure = 0x60 + 0x30; + + SsNtContinue = 0x20; + + /* KiFastCallEntry isn't hooked properly yet. Disabled for now. */ + /* INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry51, + sizeof(KiFastCallEntry51), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -6 + ); */ + /* We are scanning for PspTerminateProcess which has + the same signature as PsTerminateProcess because + PsTerminateProcess is simply a wrapper on XP. + */ + INIT_SCAN( + PsTerminateProcessScan, + PspTerminateProcess51, + sizeof(PspTerminateProcess51), + searchOffset, SCAN_LENGTH, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer51, + sizeof(PspTerminateThreadByPointer51), + searchOffset, SCAN_LENGTH, 0 + ); + + /* Windows XP SP0 and 1 are not supported */ + if (servicePack == 0) + { + return STATUS_NOT_SUPPORTED; + } + else if (servicePack == 1) + { + return STATUS_NOT_SUPPORTED; + } + else if (servicePack == 2) + { + } + else if (servicePack == 3) + { + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows XP SP%d\n", servicePack); + } + /* Windows Server 2003 */ + else if (majorVersion == 5 && minorVersion == 2) + { + ULONG_PTR psSearchOffset = (ULONG_PTR)GetSystemRoutineAddress(L"RtlCreateHeap"); + + WindowsVersion = WINDOWS_SERVER_2003; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; + + OffEtClientId = 0x1e4; + OffEtSpareByteForSs = 0x24f; /* Padding, last */ + OffEtStartAddress = 0x21c; + OffEtWin32StartAddress = 0x220; + OffEpJob = 0x120; + OffEpObjectTable = 0xd4; + OffEpProtectedProcessOff = 0; + OffEpProtectedProcessBit = 0; + OffEpRundownProtect = 0x90; + OffOhBody = 0x18; + OffOtName = 0x40; + OffOtiGenericMapping = 0x60 + 0x8; + OffOtiOpenProcedure = 0x60 + 0x30; + + SsNtContinue = 0x22; + + /* Can't find on ntoskrnl *and* ntkrnlpa. Disabled for now. */ + /* INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry52, + sizeof(KiFastCallEntry52), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7 + ); */ + /* We are scanning for PspTerminateProcess which has + the same signature as PsTerminateProcess because + PsTerminateProcess is simply a wrapper on Server 2003. + */ + INIT_SCAN( + PsTerminateProcessScan, + PspTerminateProcess52, + sizeof(PspTerminateProcess52), + psSearchOffset - 0x50000, SCAN_LENGTH, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer52, + sizeof(PspTerminateThreadByPointer52), + psSearchOffset - 0x20000, SCAN_LENGTH, 0 + ); + + if (servicePack == 0) + { + } + else if (servicePack == 1) + { + } + else if (servicePack == 2) + { + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows Server 2003 SP%d\n", servicePack); + } + /* Windows Vista, Windows Server 2008 */ + else if (majorVersion == 6 && minorVersion == 0) + { + ULONG_PTR searchOffset = (ULONG_PTR)__NtClose; + + WindowsVersion = WINDOWS_VISTA; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1fff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + + OffEtClientId = 0x20c; + OffEtSpareByteForSs = 0x26f; /* Padding, second-last */ + OffEtStartAddress = 0x1f8; + OffEtWin32StartAddress = 0x240; + OffEpJob = 0x10c; + OffEpObjectTable = 0xdc; + OffEpProtectedProcessOff = 0x224; + OffEpProtectedProcessBit = 0xb; + OffEpRundownProtect = 0x98; + OffOhBody = 0x18; + + INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry60, + sizeof(KiFastCallEntry60), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7 + ); + INIT_SCAN( + PsTerminateProcessScan, + PsTerminateProcess60, + sizeof(PsTerminateProcess60), + searchOffset, SCAN_LENGTH, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer60, + sizeof(PspTerminateThreadByPointer60), + searchOffset - 0x50000, SCAN_LENGTH, 0 + ); + + /* SP0 */ + if (servicePack == 0) + { + OffOtName = 0x40; + OffOtiGenericMapping = 0x60 + 0xc; + OffOtiOpenProcedure = 0x60 + 0x30; + + SsNtContinue = 0x36; + } + /* SP1 */ + else if (servicePack == 1) + { + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; /* They got rid of the Mutex (an ERESOURCE) */ + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtContinue = 0x37; + } + /* SP2 */ + else if (servicePack == 2) + { + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtAddAtom = 0x8; + SsNtAlertResumeThread = 0xd; + SsNtAlertThread = 0xe; + SsNtAllocateLocallyUniqueId = 0xf; + SsNtAllocateUserPhysicalPages = 0x10; + SsNtAllocateUuids = 0x11; + SsNtAllocateVirtualMemory = 0x12; + SsNtApphelpCacheControl = 0x28; + SsNtAreMappedFilesTheSame = 0x29; + SsNtAssignProcessToJobObject = 0x2a; + SsNtCallbackReturn = 0x2b; + SsNtCancelDeviceWakeupRequest = 0x2c; + SsNtCancelIoFile = 0x2d; + SsNtCancelTimer = 0x2e; + SsNtClearEvent = 0x2f; + SsNtClose = 0x30; + SsNtContinue = 0x37; + SsNtCreateDebugObject = 0x38; + SsNtCreateDirectoryObject = 0x39; + SsNtCreateEvent = 0x3a; + SsNtCreateEventPair = 0x3b; + SsNtCreateFile = 0x3c; + SsNtCreateIoCompletion = 0x3d; + SsNtCreateJobObject = 0x3e; + SsNtCreateJobSet = 0x3f; + SsNtCreateKey = 0x40; + SsNtCreateKeyedEvent = 0x168; + SsNtCreateMailslotFile = 0x42; + SsNtCreateMutant = 0x43; + SsNtCreateNamedPipeFile = 0x44; + SsNtCreatePagingFile = 0x46; + SsNtCreatePort = 0x47; + SsNtCreatePrivateNamespace = 0x45; + SsNtCreateProcess = 0x48; + SsNtCreateProcessEx = 0x49; + SsNtCreateProfile = 0x4a; + SsNtCreateSection = 0x4b; + SsNtCreateSemaphore = 0x4c; + SsNtCreateSymbolicLinkObject = 0x4d; + SsNtCreateThread = 0x4e; + SsNtCreateTimer = 0x4f; + SsNtCreateToken = 0x50; + SsNtCreateUserProcess = 0x17f; + SsNtCreateWaitablePort = 0x73; + SsNtDebugActiveProcess = 0x74; + SsNtDebugContinue = 0x75; + SsNtDelayExecution = 0x76; + SsNtDeleteAtom = 0x77; + SsNtDeleteBootEntry = 0x78; + SsNtDeleteDriverEntry = 0x79; + SsNtDeleteFile = 0x7a; + SsNtDeleteKey = 0x7b; + SsNtDeletePrivateNamespace = 0x7c; + SsNtDeleteObjectAuditAlarm = 0x7d; + SsNtDeleteValueKey = 0x7e; + SsNtDeviceIoControlFile = 0x7f; + SsNtDisplayString = 0x80; + SsNtDuplicateObject = 0x81; + SsNtDuplicateToken = 0x82; + SsNtEnumerateBootEntries = 0x83; + SsNtEnumerateDriverEntries = 0x84; + SsNtEnumerateKey = 0x85; + SsNtEnumerateSystemEnvironmentValuesEx = 0x86; + SsNtEnumerateValueKey = 0x88; + SsNtExtendSection = 0x89; + SsNtFilterToken = 0x8a; + SsNtFindAtom = 0x8b; + SsNtFlushBuffersFile = 0x8c; + SsNtFlushInstructionCache = 0x8d; + SsNtFlushKey = 0x8e; + SsNtFlushProcessWriteBuffers = 0x8f; + SsNtFlushVirtualMemory = 0x90; + SsNtFlushWriteBuffer = 0x91; + SsNtFreeUserPhysicalPages = 0x92; + SsNtFreeVirtualMemory = 0x93; + SsNtFsControlFile = 0x96; + SsNtGetContextThread = 0x97; + SsNtGetDevicePowerState = 0x98; + SsNtGetPlugPlayEvent = 0x9a; + SsNtGetWriteWatch = 0x9b; + SsNtImpersonateAnonymousToken = 0x9c; + SsNtImpersonateClientOfPort = 0x9d; + SsNtImpersonateThread = 0x9e; + SsNtInitiatePowerAction = 0xa1; + SsNtIsProcessInJob = 0xa2; + SsNtIsSystemResumeAutomatic = 0xa3; + SsNtListenPort = 0xa4; + SsNtLoadDriver = 0xa5; + SsNtLoadKey = 0xa6; + SsNtLoadKey2 = 0xa7; + SsNtLockFile = 0xa9; + SsNtLockVirtualMemory = 0xac; + SsNtMakePermanentObject = 0xad; + SsNtMakeTemporaryObject = 0xae; + SsNtMapUserPhysicalPages = 0xaf; + SsNtMapUserPhysicalPagesScatter = 0xb0; + SsNtMapViewOfSection = 0xb1; + SsNtModifyBootEntry = 0xb2; + SsNtModifyDriverEntry = 0xb3; + SsNtNotifyChangeDirectoryFile = 0xb4; + SsNtNotifyChangeKey = 0xb5; + SsNtNotifyChangeMultipleKeys = 0xb6; + SsNtOpenDirectoryObject = 0xb7; + SsNtOpenEvent = 0xb8; + SsNtOpenEventPair = 0xb9; + SsNtOpenFile = 0xba; + SsNtOpenIoCompletion = 0xbb; + SsNtOpenJobObject = 0xbc; + SsNtOpenKey = 0xbd; + SsNtOpenKeyedEvent = 0x169; + SsNtOpenMutant = 0xbf; + SsNtOpenObjectAuditAlarm = 0xc1; + SsNtOpenProcess = 0xc2; + SsNtOpenProcessToken = 0xc3; + SsNtOpenProcessTokenEx = 0xc4; + SsNtOpenSection = 0xc5; + SsNtOpenSemaphore = 0xc6; + SsNtOpenSymbolicLinkObject = 0xc8; + SsNtOpenThread = 0xc9; + SsNtOpenThreadToken = 0xca; + SsNtOpenThreadTokenEx = 0xcb; + SsNtOpenTimer = 0xcc; + SsNtReadFile = 0x102; + SsNtWriteFile = 0x163; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows Vista SP%d/Windows Server 2008\n", servicePack); + } + /* Windows 7, Windows Server 2008 R2 */ + else if (majorVersion == 6 && minorVersion == 1) + { + ULONG_PTR psSearchOffset = (ULONG_PTR)GetSystemRoutineAddress(L"PsSetCreateProcessNotifyRoutine"); + ULONG psScanLength = 0x200000; + + if (!psSearchOffset) + return STATUS_NOT_SUPPORTED; + + WindowsVersion = WINDOWS_7; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1fff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + + OffEtClientId = 0x22c; + OffEtSpareByteForSs = 0x2b4; /* Padding, last */ + OffEtStartAddress = 0x218; + OffEtWin32StartAddress = 0x260; + OffEpJob = 0x124; + OffEpObjectTable = 0xf4; + OffEpProtectedProcessOff = 0x26c; + OffEpProtectedProcessBit = 0xb; + OffEpRundownProtect = 0xb0; + OffOhBody = 0x18; + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtContinue = 0x3c; + + INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry61, + sizeof(KiFastCallEntry61), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7 + ); + INIT_SCAN( + PsTerminateProcessScan, + PsTerminateProcess61, + sizeof(PsTerminateProcess61), + psSearchOffset, psScanLength, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer61, + sizeof(PspTerminateThreadByPointer61), + psSearchOffset, psScanLength, 0 + ); + + /* SP0 */ + if (servicePack == 0) + { + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows 7 SP%d\n", servicePack); + } + else + { + return STATUS_NOT_SUPPORTED; + } + + return status; +} + +PVOID KvScanProc( + PKV_SCANPROC ScanProc + ) +{ + PUCHAR bytes = ScanProc->Bytes; + ULONG length = ScanProc->Length; + ULONG_PTR endAddress = ScanProc->StartAddress + ScanProc->ScanLength; + ULONG_PTR i; + + for (i = ScanProc->StartAddress; i < endAddress; i++) + { + if (memcmp((PVOID)i, bytes, length) == 0) + return (PVOID)(i + ScanProc->Displacement); + } + + return NULL; +} + +PVOID KvVerifyPrologue( + PVOID Address + ) +{ + if (memcmp(Address, StandardPrologue, 5) == 0) + return Address; + else + return NULL; +}