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 000000000..9c86f5624 Binary files /dev/null and b/2.x/trunk/KProcessHacker/amd64/kprocesshacker.sys differ diff --git a/2.x/trunk/KProcessHacker/auto.cmd b/2.x/trunk/KProcessHacker/auto.cmd new file mode 100644 index 000000000..28cde9736 --- /dev/null +++ b/2.x/trunk/KProcessHacker/auto.cmd @@ -0,0 +1,7 @@ +@echo off + +build -cZ +if not %errorlevel%==0 goto end +copy i386\kprocesshacker.sys ..\ProcessHacker\bin\Release\ +copy i386\kprocesshacker.pdb ..\ProcessHacker\bin\Release\ +:end \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/autoreload.cmd b/2.x/trunk/KProcessHacker/autoreload.cmd new file mode 100644 index 000000000..b1e09e9c1 --- /dev/null +++ b/2.x/trunk/KProcessHacker/autoreload.cmd @@ -0,0 +1,2 @@ +@echo off +auto & sc stop kprocesshacker & sc start kprocesshacker \ No newline at end of file diff --git a/2.x/trunk/KProcessHacker/handle.c b/2.x/trunk/KProcessHacker/handle.c new file mode 100644 index 000000000..43b94fd9a --- /dev/null +++ b/2.x/trunk/KProcessHacker/handle.c @@ -0,0 +1,355 @@ +/* + * 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 . + */ + +#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 000000000..247c5f1fa Binary files /dev/null and b/2.x/trunk/KProcessHacker/i386/kprocesshacker.sys differ 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; +}