diff --git a/trunk/KProcessHacker/HACKING.txt b/trunk/KProcessHacker/HACKING.txt index 8bec4e5fd..00c104cfa 100644 --- a/trunk/KProcessHacker/HACKING.txt +++ b/trunk/KProcessHacker/HACKING.txt @@ -54,11 +54,14 @@ following files contain "ported" ReactOS code (with modifications): - 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. sysservicep.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/trunk/KProcessHacker/i386/kprocesshacker.sys b/trunk/KProcessHacker/i386/kprocesshacker.sys index b4699c76d..12526a8b3 100644 Binary files a/trunk/KProcessHacker/i386/kprocesshacker.sys and b/trunk/KProcessHacker/i386/kprocesshacker.sys differ diff --git a/trunk/KProcessHacker/include/kph.h b/trunk/KProcessHacker/include/kph.h index b4c073d0b..a91c62762 100644 --- a/trunk/KProcessHacker/include/kph.h +++ b/trunk/KProcessHacker/include/kph.h @@ -306,6 +306,20 @@ NTSTATUS KphQueryInformationDriver( __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, diff --git a/trunk/KProcessHacker/include/kprocesshacker.h b/trunk/KProcessHacker/include/kprocesshacker.h index 6103b16d9..bb51d12e8 100644 --- a/trunk/KProcessHacker/include/kprocesshacker.h +++ b/trunk/KProcessHacker/include/kprocesshacker.h @@ -86,7 +86,9 @@ #define KPH_SSREF KPH_CTL_CODE(42) #define KPH_SSUNREF KPH_CTL_CODE(43) #define KPH_SSCREATECLIENTENTRY KPH_CTL_CODE(44) -#define KPH_SSCREATEPROCESSENTRY KPH_CTL_CODE(45) +#define KPH_SSCREATERULESETENTRY KPH_CTL_CODE(45) +#define KPH_SSREMOVERULE KPH_CTL_CODE(46) +#define KPH_SSADDPROCESSIDRULE KPH_CTL_CODE(47) NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); VOID DriverUnload(PDRIVER_OBJECT DriverObject); @@ -113,6 +115,9 @@ typedef struct _KPH_CLIENT_ENTRY LONG SsStartCount; } KPH_CLIENT_ENTRY, *PKPH_CLIENT_ENTRY; +VOID SsRef(LONG count); +VOID SsUnref(LONG count); + VOID NTAPI ClientEntryDeleteProcedure( __in PVOID Object, __in ULONG Flags, diff --git a/trunk/KProcessHacker/include/sysservice.h b/trunk/KProcessHacker/include/sysservice.h index a7afc1b25..06dd8a0a3 100644 --- a/trunk/KProcessHacker/include/sysservice.h +++ b/trunk/KProcessHacker/include/sysservice.h @@ -24,18 +24,136 @@ #define _SYSSERVICE_H #include "kph.h" +#include "sysservicedata.h" -/* If neither mode flags are specified, both modes are assumed. */ -#define KPHSS_LOG_USER_MODE 0x00000001 -#define KPHSS_LOG_KERNEL_MODE 0x00000002 -#define KPHSS_LOG_VALID_FLAGS 0x00000003 +/* Define opaque object types */ struct _KPHSS_CLIENT_ENTRY; typedef struct _KPHSS_CLIENT_ENTRY *PKPHSS_CLIENT_ENTRY; -struct _KPHSS_PROCESS_ENTRY; -typedef struct _KPHSS_PROCESS_ENTRY *PKPHSS_PROCESS_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; + +/* 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, + 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 +} KPHSS_BLOCK_TYPE; + +typedef struct _KPHSS_BLOCK_HEADER +{ + ULONG Size; /* a.k.a. NextEntryOffset */ + ULONG 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; + ULONG Flags; + LARGE_INTEGER Time; + CLIENT_ID ClientId; + + /* The system service number. */ + ULONG Number; + /* The number of ULONG arguments to the system service. */ + ULONG NumberOfArguments; + ULONG ArgumentsOffset; + + /* The number of PVOIDs in the trace. */ + ULONG TraceCount; + ULONG TraceOffset; +} KPHSS_EVENT_BLOCK, *PKPHSS_EVENT_BLOCK; + +/* Argument Blocks + * + * These blocks provide additional information about + * arguments. + */ + +#define TAG_ARGUMENT_BLOCK ('BAhP') + +#define KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize) \ + (FIELD_OFFSET(KPHSS_ARGUMENT_BLOCK, Normal) + InnerSize) + +typedef struct _KPHSS_ARGUMENT_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + ULONG Index; + KPHSS_ARGUMENT_TYPE 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; + }; +} KPHSS_ARGUMENT_BLOCK, *PKPHSS_ARGUMENT_BLOCK; + +/* Functions */ NTSTATUS KphSsLogInit(); +NTSTATUS KphSsLogDeinit(); NTSTATUS KphSsLogStart(); NTSTATUS KphSsLogStop(); @@ -49,11 +167,44 @@ NTSTATUS KphSsCreateClientEntry( __in KPROCESSOR_MODE AccessMode ); -NTSTATUS KphSsCreateProcessEntry( - __out PKPHSS_PROCESS_ENTRY *ProcessEntry, +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, __in PKPHSS_CLIENT_ENTRY ClientEntry, - __in HANDLE TargetProcessHandle, - __in ULONG Flags + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action + ); + +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/trunk/KProcessHacker/include/sysservicedata.h b/trunk/KProcessHacker/include/sysservicedata.h new file mode 100644 index 000000000..2a6a1b279 --- /dev/null +++ b/trunk/KProcessHacker/include/sysservicedata.h @@ -0,0 +1,156 @@ +/* + * 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" + +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 +} KPHSS_ARGUMENT_TYPE; + +typedef struct _KPHSS_HANDLE +{ + ULONG TypeNameOffset; /* KPHSS_WSTRING */ + ULONG 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 +{ + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; + + ULONG RootDirectoryOffset; /* KPHSS_HANDLE */ + ULONG 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; + +#ifndef _SYSSERVICEDATA_PRIVATE +extern RTL_GENERIC_TABLE KphSsCallTable; +#endif + +#define TAG_CALL_ENTRY ('cShP') + +#define KPHSS_MAXIMUM_ARGUMENT_BLOCKS 10 + +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/trunk/KProcessHacker/include/sysservicep.h b/trunk/KProcessHacker/include/sysservicep.h index 06b8000e8..5983cdfb8 100644 --- a/trunk/KProcessHacker/include/sysservicep.h +++ b/trunk/KProcessHacker/include/sysservicep.h @@ -39,73 +39,367 @@ typedef VOID (NTAPI *PKPHPSS_KIFASTCALLENTRYPROC)( __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; + + /* 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; -#define KPHSS_PROCESS_ENTRY(ListEntry) \ - CONTAINING_RECORD((ListEntry), KPHSS_PROCESS_ENTRY, ProcessListEntry) -#define KPHSS_PROCESS_ENTRY_LIMIT 10 +/* Rulesets + * + * Rulesets contain a list of rules and an action to take if a + * system service matches the set of rules. + */ -typedef struct _KPHSS_PROCESS_ENTRY +#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 ProcessListEntry; - + LIST_ENTRY RuleSetListEntry; + /* The client is referenced. */ PKPHSS_CLIENT_ENTRY Client; - PEPROCESS TargetProcess; - ULONG Flags; -} KPHSS_PROCESS_ENTRY, *PKPHSS_PROCESS_ENTRY; - -typedef enum _KPHPSS_BLOCK_TYPE -{ - ResetBlockType, - EventBlockType -} KPHPSS_BLOCK_TYPE; - -typedef struct _KPHPSS_BLOCK_HEADER -{ - ULONG Size; /* a.k.a. NextEntryOffset */ - ULONG Type; -} KPHPSS_BLOCK_HEADER, *PKPHPSS_BLOCK_HEADER; - -typedef struct _KPHPSS_RESET_BLOCK -{ - KPHPSS_BLOCK_HEADER Header; -} KPHPSS_RESET_BLOCK, *PKPHPSS_RESET_BLOCK; - -#define TAG_EVENT_BLOCK ('BEhP') - -#define KPHPSS_EVENT_BLOCK_MAX_SIZE 0x200 - -#define KPHPSS_EVENT_PROBE_ARGUMENTS_FAILED 0x00000001 -#define KPHPSS_EVENT_COPY_ARGUMENTS_FAILED 0x00000002 -#define KPHPSS_EVENT_KERNEL_MODE 0x00000004 -#define KPHPSS_EVENT_USER_MODE 0x00000008 - -typedef struct _KPHPSS_EVENT_BLOCK -{ - KPHPSS_BLOCK_HEADER Header; - ULONG Flags; - LARGE_INTEGER Time; - CLIENT_ID ClientId; - /* The system service number. */ - ULONG Number; - /* The number of ULONG arguments to the system service. */ - ULONG NumberOfArguments; - ULONG ArgumentsOffset; + KPHSS_RULESET_ACTION Action; + KPHSS_FILTER_TYPE DefaultFilterType; - /* The number of PVOIDs in the trace. */ - ULONG TraceCount; - ULONG TraceOffset; -} KPHPSS_EVENT_BLOCK, *PKPHPSS_EVENT_BLOCK; + ULONG NextRuleHandle; + FAST_MUTEX RuleListMutex; + /* 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') + +FORCEINLINE PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock( + __in ULONG InnerSize, + __in KPHSS_ARGUMENT_TYPE Type + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + + size = KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize); + argumentBlock = ExAllocatePoolWithTag( + PagedPool, + size, + TAG_ARGUMENT_BLOCK + ); + + if (!argumentBlock) + return NULL; + + argumentBlock->Header.Type = ArgumentBlockType; + argumentBlock->Header.Size = size; + argumentBlock->Type = Type; + + return argumentBlock; +} + +FORCEINLINE NTSTATUS KphpSsCaptureSimple( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in KPHSS_ARGUMENT_TYPE Type + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + LARGE_INTEGER value; + + 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; + } + + __try + { + ProbeForRead(Argument, size, 1); + memcpy(&value, Argument, size); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + argumentBlock = KphpSsAllocateArgumentBlock(size, Type); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + memcpy(&argumentBlock->Simple, &value, size); + *ArgumentBlock = argumentBlock; + + return STATUS_SUCCESS; +} + +#define CAPTURE_HANDLE_BUFFER_SIZE 0x400 + +FORCEINLINE NTSTATUS KphpSsCaptureHandle( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in HANDLE Argument + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + PVOID object; + PUNICODE_STRING objectTypeName; + PUNICODE_STRING objectNameInfo; + ULONG returnLength; + PKPHSS_WSTRING wString; + + /* 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. + */ + objectTypeName = (PUNICODE_STRING)KVOFF( + OBJECT_TO_OBJECT_HEADER(object)->Type, + OffOtName + ); + + /* Allocate a buffer for name information. */ + objectNameInfo = 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; + + /* Copy the type name into the block. */ + argumentBlock->Handle.TypeNameOffset = sizeof(KPHSS_HANDLE); + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(&argumentBlock->Handle, argumentBlock->Handle.TypeNameOffset); + wString->Length = objectTypeName->Length; + memcpy(&wString->Buffer, objectTypeName->Buffer, wString->Length); + + /* Copy the object name into the block. */ + argumentBlock->Handle.NameOffset = + argumentBlock->Handle.TypeNameOffset + sizeof(KPHSS_WSTRING) + + wString->Length; + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(&argumentBlock->Handle, argumentBlock->Handle.NameOffset); + wString->Length = objectNameInfo->Length; + memcpy(&wString->Buffer, objectNameInfo->Buffer, wString->Length); + + *ArgumentBlock = argumentBlock; + +CleanupName: + ExFreePoolWithTag(objectNameInfo, TAG_CAPTURE_TEMP_BUFFER); +CleanupObject: + ObDereferenceObject(object); + + return status; +} + +/* KphpSsMatchRuleSetEntry + * + * Determines if a ruleset is relevant to an event. + * + * Note: This function is inlined for performance reasons. + */ +FORCEINLINE BOOLEAN 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; + BOOLEAN isRuleSetMatch = FALSE; + + /* Get the default filter type. If it is the Include + * filter type, we assume the ruleset matches. If it + * is the Exclude filter type, we assume it doesn't. + */ + if (RuleSetEntry->DefaultFilterType == IncludeFilterType) + isRuleSetMatch = TRUE; + else if (RuleSetEntry->DefaultFilterType == ExcludeFilterType) + isRuleSetMatch = FALSE; + + ExAcquireFastMutex(&RuleSetEntry->RuleListMutex); + + 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. */ + if (!ruleEntry->Initialized) + { + 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 + * whether we should continue: + * + * * For the Include filter type, we note that the + * we have a match, but we still have to continue + * going down the rule list since there may be + * Exclude filters. + * * For the Exclude filter type, we can simply stop + * the matching and return - Exclude filters take + * precedence. + */ + if (isRuleMatch) + { + if (ruleEntry->FilterType == IncludeFilterType) + { + isRuleSetMatch = TRUE; + } + else if (ruleEntry->FilterType == ExcludeFilterType) + { + isRuleSetMatch = FALSE; + break; + } + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleaseFastMutex(&RuleSetEntry->RuleListMutex); + + return isRuleSetMatch; +} + +/* Functions */ VOID NTAPI KphpSsClientEntryDeleteProcedure( __in PVOID Object, @@ -113,14 +407,27 @@ VOID NTAPI KphpSsClientEntryDeleteProcedure( __in SIZE_T Size ); -VOID NTAPI KphpSsProcessEntryDeleteProcedure( +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags, + __in SIZE_T Size + ); + +NTSTATUS KphpSsAddRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPHSS_RULE_TYPE RuleType + ); + +VOID NTAPI KphpSsRuleEntryDeleteProcedure( __in PVOID Object, __in ULONG Flags, __in SIZE_T Size ); NTSTATUS KphpSsCreateEventBlock( - __out PKPHPSS_EVENT_BLOCK *EventBlock, + __out PKPHSS_EVENT_BLOCK *EventBlock, __in PKTHREAD Thread, __in ULONG Number, __in ULONG *Arguments, @@ -128,12 +435,24 @@ NTSTATUS KphpSsCreateEventBlock( ); VOID KphpSsFreeEventBlock( - __in PKPHPSS_EVENT_BLOCK EventBlock + __in PKPHSS_EVENT_BLOCK EventBlock + ); + +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index + ); + +VOID KphpSsFreeArgumentBlock( + __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock ); NTSTATUS KphpSsWriteBlock( __in PKPHSS_CLIENT_ENTRY ClientEntry, - __in PKPHPSS_BLOCK_HEADER Block + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode ); VOID NTAPI KphpSsLogSystemServiceCall( @@ -146,25 +465,4 @@ VOID NTAPI KphpSsLogSystemServiceCall( VOID NTAPI KphpSsNewKiFastCallEntry(); -/* KphpSsIsProcessEntryRelevant - * - * Returns whether a system service call should be logged based on - * a process entry. - */ -FORCEINLINE BOOLEAN KphpSsIsProcessEntryRelevant( - __in PKPHSS_PROCESS_ENTRY ProcessEntry, - __in PEPROCESS Process, - __in KPROCESSOR_MODE PreviousMode - ) -{ - return - /* Check if the process entry is referring to the caller. */ - ProcessEntry->TargetProcess == Process && - /* Check the mode. */ - ( - ((ProcessEntry->Flags & KPHSS_LOG_USER_MODE) && (PreviousMode == UserMode)) || - ((ProcessEntry->Flags & KPHSS_LOG_KERNEL_MODE) && (PreviousMode == KernelMode)) - ); -} - #endif diff --git a/trunk/KProcessHacker/include/version.h b/trunk/KProcessHacker/include/version.h index fb3f1aa7c..c2ed977c9 100644 --- a/trunk/KProcessHacker/include/version.h +++ b/trunk/KProcessHacker/include/version.h @@ -96,6 +96,7 @@ EXT ULONG OffEpProtectedProcessOff; EXT ULONG OffEpProtectedProcessBit; EXT ULONG OffEpRundownProtect; EXT ULONG OffOhBody; +EXT ULONG OffOtName; EXT ULONG OffOtiGenericMapping; EXT ULONG OffOtiOpenProcedure; @@ -108,6 +109,10 @@ EXT KV_SCANPROC PspTerminateThreadByPointerScan SCANNULL; /* System Call Numbers */ -EXT ULONG SysCallZwContinue; +EXT ULONG SsNtAddAtom; +EXT ULONG SsNtAlertResumeThread; +EXT ULONG SsNtClose; +EXT ULONG SsNtContinue; +EXT ULONG SsNtDelayExecution; #endif diff --git a/trunk/KProcessHacker/kprocesshacker.c b/trunk/KProcessHacker/kprocesshacker.c index 3421e37a8..59e53d486 100644 --- a/trunk/KProcessHacker/kprocesshacker.c +++ b/trunk/KProcessHacker/kprocesshacker.c @@ -192,6 +192,13 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject) 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(); @@ -475,62 +482,6 @@ NTSTATUS ReferenceClientHandle( return status; } -/* from YAPM */ -NTSTATUS GetObjectName(PFILE_OBJECT FileObject, PVOID Buffer, ULONG BufferLength, PULONG ReturnLength) -{ - ULONG nameLength = 0; - PFILE_OBJECT relatedFile; - PVOID name = Buffer; - - if (FileObject->DeviceObject) - { - ObQueryNameString((PVOID)FileObject->DeviceObject, name, BufferLength, ReturnLength); - (PCHAR)name += *ReturnLength - 2; /* minus the null terminator */ - BufferLength -= *ReturnLength - 2; - } - else - { - /* It's a UNICODE_STRING. we need to subtract the space - * Length and MaximumLength take up. - */ - (PCHAR)name += 4; - BufferLength -= 4; - } - - if (!FileObject->FileName.Buffer) - return STATUS_SUCCESS; - - relatedFile = FileObject; - - do - { - nameLength += relatedFile->FileName.Length; - relatedFile = relatedFile->RelatedFileObject; - } - while (relatedFile); - - *ReturnLength += nameLength; - - if (nameLength > BufferLength) - { - return STATUS_BUFFER_TOO_SMALL; - } - - (PCHAR)name += nameLength; - *(PUSHORT)name = 0; - - relatedFile = FileObject; - do - { - (PCHAR)name -= relatedFile->FileName.Length; - memcpy(name, relatedFile->FileName.Buffer, relatedFile->FileName.Length); - relatedFile = relatedFile->RelatedFileObject; - } - while (relatedFile); - - return STATUS_SUCCESS; -} - PCHAR GetIoControlName(ULONG ControlCode) { switch (ControlCode) @@ -623,8 +574,12 @@ PCHAR GetIoControlName(ULONG ControlCode) return "SsUnref"; case KPH_SSCREATECLIENTENTRY: return "SsCreateClientEntry"; - case KPH_SSCREATEPROCESSENTRY: - return "SsCreateProcessEntry"; + case KPH_SSCREATERULESETENTRY: + return "SsCreateRuleSetEntry"; + case KPH_SSREMOVERULE: + return "SsRemoveRule"; + case KPH_SSADDPROCESSIDRULE: + return "SsAddProcessIdRule"; default: return "Unknown"; } @@ -731,22 +686,14 @@ NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) __try { - if (((PFILE_OBJECT)object)->Busy || ((PFILE_OBJECT)object)->Waiters) - { - status = GetObjectName((PFILE_OBJECT)object, dataBuffer, outLength, &retLength); - ObDereferenceObject(object); - } - else - { - status = ObQueryNameString( - object, (POBJECT_NAME_INFORMATION)dataBuffer, outLength, &retLength); - ObDereferenceObject(object); - } + status = KphQueryNameFileObject(object, dataBuffer, outLength, &retLength); } __except (EXCEPTION_EXECUTE_HANDLER) { - status = STATUS_ACCESS_VIOLATION; + status = GetExceptionCode(); } + + ObDereferenceObject(object); } break; @@ -1302,7 +1249,7 @@ NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { HANDLE ProcessHandle; HANDLE Handle; - ULONG ObjectInformationClass; + OBJECT_INFORMATION_CLASS ObjectInformationClass; } *args = dataBuffer; struct { @@ -2006,24 +1953,24 @@ NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) } break; - /* SsCreateProcessEntry + /* SsCreateRuleSetEntry * - * Creates a system service logging process entry. + * Creates a system service logging ruleset entry. */ - case KPH_SSCREATEPROCESSENTRY: + case KPH_SSCREATERULESETENTRY: { struct { HANDLE ClientEntryHandle; - HANDLE TargetProcessHandle; - ULONG Flags; + KPHSS_FILTER_TYPE DefaultFilterType; + KPHSS_RULESET_ACTION Action; } *args = dataBuffer; struct { - HANDLE ProcessEntryHandle; + HANDLE RuleSetEntryHandle; } *ret = dataBuffer; PKPHSS_CLIENT_ENTRY clientEntry; - PKPHSS_PROCESS_ENTRY processEntry; + PKPHSS_RULESET_ENTRY ruleSetEntry; CHECK_IN_OUT_LENGTH; @@ -2032,19 +1979,87 @@ NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) if (!NT_SUCCESS(status)) goto IoControlEnd; - status = KphSsCreateProcessEntry( - &processEntry, + status = KphSsCreateRuleSetEntry( + &ruleSetEntry, clientEntry, - args->TargetProcessHandle, - args->Flags + args->DefaultFilterType, + args->Action ); KphDereferenceObject(clientEntry); if (!NT_SUCCESS(status)) goto IoControlEnd; - status = CreateClientHandle(NULL, processEntry, &ret->ProcessEntryHandle); - KphDereferenceObject(processEntry); + 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; + + status = ReferenceClientHandle(NULL, args->RuleSetEntryHandle, &ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + 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; + + status = ReferenceClientHandle(NULL, args->RuleSetEntryHandle, &ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = KphSsAddProcessIdRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->ProcessId + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = CreateClientHandle(NULL, ruleEntry, &ret->RuleEntryHandle); + KphDereferenceObject(ruleEntry); retLength = sizeof(*ret); } break; diff --git a/trunk/KProcessHacker/ob.c b/trunk/KProcessHacker/ob.c index ccc055b6b..7e20ecd59 100644 --- a/trunk/KProcessHacker/ob.c +++ b/trunk/KProcessHacker/ob.c @@ -280,6 +280,117 @@ NTSTATUS KphOpenNamedObject( return status; } +/* KphQueryFileObjectName + * + * Queries the name of a file object. + * + * From YAPM. + */ +NTSTATUS KphQueryNameFileObject( + __in PFILE_OBJECT FileObject, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ) +{ + ULONG returnLength = 0; + ULONG nameLength = 0; + /* Pointer to the parent of the current file object. */ + PFILE_OBJECT relatedFileObject; + PVOID name = Buffer; + + /* Check if the file object has an associated device. */ + if (FileObject->DeviceObject) + { + /* Query the name of the device (e.g. "\Device\HarddiskVolume1"). */ + ObQueryNameString(FileObject->DeviceObject, name, BufferLength, &returnLength); + /* Add on the length, in bytes, of the name we just queried + * (minus the null terminator, since the return length + * includes that). + */ + (PCHAR)name += returnLength - sizeof(WCHAR); + BufferLength -= returnLength - sizeof(WCHAR); + } + else + { + (PCHAR)name += sizeof(UNICODE_STRING); + BufferLength -= sizeof(UNICODE_STRING); + } + + if (!FileObject->FileName.Buffer) + return STATUS_SUCCESS; + + /* Walk up the file object tree to get the total length needed. */ + + relatedFileObject = FileObject; + + do + { + nameLength += relatedFileObject->FileName.Length; + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + returnLength += nameLength; + + if (nameLength + sizeof(UNICODE_STRING) > BufferLength) + { + return STATUS_BUFFER_TOO_SMALL; + } + + /* We are going to copy over the individual paths in reverse order. */ + + (PCHAR)name += nameLength; + /* Write the null terminator. */ + *(PUSHORT)name = 0; + + relatedFileObject = FileObject; + do + { + (PCHAR)name -= relatedFileObject->FileName.Length; + memcpy(name, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length); + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + /* Write some length information. */ + /* FIXME: Is the null terminator always present? */ + Buffer->Length = (USHORT)(returnLength - sizeof(UNICODE_STRING) - sizeof(WCHAR)); + + if (ReturnLength) + *ReturnLength = returnLength; + + 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; + + if ( + OBJECT_TO_OBJECT_HEADER(Object)->Type == *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. diff --git a/trunk/KProcessHacker/sources b/trunk/KProcessHacker/sources index 399ce6505..c870dd2de 100644 --- a/trunk/KProcessHacker/sources +++ b/trunk/KProcessHacker/sources @@ -15,6 +15,7 @@ SOURCES= \ protect.c \ sync.c \ sysservice.c \ + sysservicedata.c \ trace.c \ \ io.c \ diff --git a/trunk/KProcessHacker/sysservice.c b/trunk/KProcessHacker/sysservice.c index b8f5b8a0f..3deb135f7 100644 --- a/trunk/KProcessHacker/sysservice.c +++ b/trunk/KProcessHacker/sysservice.c @@ -36,6 +36,7 @@ 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; @@ -44,11 +45,17 @@ KPH_HOOK KphSsKiFastCallEntryHook; /* The number of active loggers. */ ULONG KphSsNumberOfActiveLoggers = 0; +/* The object type for client entries. */ PKPH_OBJECT_TYPE KphSsClientEntryType; -PKPH_OBJECT_TYPE KphSsProcessEntryType; +/* The object type for ruleset entries. */ +PKPH_OBJECT_TYPE KphSsRuleSetEntryType; +/* The object type for rule entries. */ +PKPH_OBJECT_TYPE KphSsRuleEntryType; -FAST_MUTEX KphSsProcessListMutex; -LIST_ENTRY KphSsProcessListHead; +/* A fast mutex guarding all accesses to the ruleset list. */ +FAST_MUTEX KphSsRuleSetListMutex; +/* The list of ruleset entries. */ +LIST_ENTRY KphSsRuleSetListHead; /* KphSsLogInit * @@ -58,10 +65,13 @@ NTSTATUS KphSsLogInit() { NTSTATUS status = STATUS_SUCCESS; + /* Initialize the system service call data. */ + KphSsDataInit(); + /* Initialize the process list. */ - InitializeListHead(&KphSsProcessListHead); + InitializeListHead(&KphSsRuleSetListHead); ExInitializeFastMutex(&KphSsMutex); - ExInitializeFastMutex(&KphSsProcessListMutex); + ExInitializeFastMutex(&KphSsRuleSetListMutex); /* Initialize the object types. */ status = KphCreateObjectType( @@ -74,9 +84,9 @@ NTSTATUS KphSsLogInit() return status; status = KphCreateObjectType( - &KphSsProcessEntryType, + &KphSsRuleSetEntryType, NonPagedPool, - KphpSsProcessEntryDeleteProcedure + KphpSsRuleSetEntryDeleteProcedure ); if (!NT_SUCCESS(status)) @@ -85,9 +95,33 @@ NTSTATUS KphSsLogInit() return status; } + status = KphCreateObjectType( + &KphSsRuleEntryType, + NonPagedPool, + 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. @@ -167,7 +201,7 @@ NTSTATUS KphSsLogStop() * * Creates a client entry which describes a client of the * system service logger. Clients receieve system service log events. - * Note that a client may have several process entries associated + * Note that a client may have several ruleset entries associated * with it. * * ClientEntry: A variable which receives a pointer to the client entry. @@ -284,6 +318,8 @@ NTSTATUS KphSsCreateClientEntry( clientEntry->BufferBase = BufferBase; clientEntry->BufferSize = BufferSize; clientEntry->BufferCursor = 0; + clientEntry->NumberOfBlocksWritten = 0; + clientEntry->NumberOfBlocksDropped = 0; *ClientEntry = clientEntry; @@ -307,91 +343,291 @@ VOID NTAPI KphpSsClientEntryDeleteProcedure( ObDereferenceObject(clientEntry->WriteSemaphore); } -/* KphSsCreateProcessEntry +/* KphSsCreateRuleSetEntry * - * Creates a process entry which describes a process for which - * system services will be logged. + * Creates a ruleset entry which contains a list of rules + * and an action to perform. */ -NTSTATUS KphSsCreateProcessEntry( - __out PKPHSS_PROCESS_ENTRY *ProcessEntry, +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, __in PKPHSS_CLIENT_ENTRY ClientEntry, - __in HANDLE TargetProcessHandle, - __in ULONG Flags + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action ) { NTSTATUS status = STATUS_SUCCESS; - PKPHSS_PROCESS_ENTRY processEntry; - PEPROCESS processObject; + PKPHSS_RULESET_ENTRY ruleSetEntry; - /* Check if the flags are valid. */ - if ((Flags & KPHSS_LOG_VALID_FLAGS) != Flags) - return STATUS_INVALID_PARAMETER_4; + /* Make sure the action is valid. */ + if (Action < LogRuleSetAction || Action >= MaxRuleSetAction) + return STATUS_INVALID_PARAMETER_3; - /* If the caller didn't specify any mode flags, assume both modes. */ - if (!(Flags & (KPHSS_LOG_USER_MODE | KPHSS_LOG_KERNEL_MODE))) - Flags |= KPHSS_LOG_USER_MODE | KPHSS_LOG_KERNEL_MODE; - - /* Reference the process object. Note that we don't actually - * need to keep the process object alive since we don't - * access it at any point. - */ - status = ObReferenceObjectByHandle( - TargetProcessHandle, - 0, - *PsProcessType, - KernelMode, - &processObject, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - ObDereferenceObject(processObject); - - /* Create the process entry object. */ + /* Create the ruleset object. */ status = KphCreateObject( - &processEntry, - sizeof(KPHSS_PROCESS_ENTRY), + &ruleSetEntry, + sizeof(KPHSS_RULESET_ENTRY), 0, - KphSsProcessEntryType, + KphSsRuleSetEntryType, 0 ); if (!NT_SUCCESS(status)) return status; + /* Initialize the ruleset object. */ KphReferenceObject(ClientEntry); - processEntry->Client = ClientEntry; - processEntry->TargetProcess = processObject; - processEntry->Flags = Flags; + ruleSetEntry->Client = ClientEntry; + ruleSetEntry->DefaultFilterType = DefaultFilterType; + ruleSetEntry->Action = Action; + ruleSetEntry->NextRuleHandle = 4; + ExInitializeFastMutex(&ruleSetEntry->RuleListMutex); + InitializeListHead(&ruleSetEntry->RuleListHead); - ExAcquireFastMutex(&KphSsProcessListMutex); - InsertHeadList(&KphSsProcessListHead, &processEntry->ProcessListEntry); - ExReleaseFastMutex(&KphSsProcessListMutex); + /* Add the ruleset to the list. */ + ExAcquireFastMutex(&KphSsRuleSetListMutex); + InsertHeadList(&KphSsRuleSetListHead, &ruleSetEntry->RuleSetListEntry); + ExReleaseFastMutex(&KphSsRuleSetListMutex); - *ProcessEntry = processEntry; + *RuleSetEntry = ruleSetEntry; return status; } -/* KphpSsProcessEntryDeleteProcedure +/* KphpSsRuleSetEntryDeleteProcedure * - * Performs cleanup for a process entry. + * Performs cleanup for a ruleset entry. */ -VOID NTAPI KphpSsProcessEntryDeleteProcedure( +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( __in PVOID Object, __in ULONG Flags, __in SIZE_T Size ) { - PKPHSS_PROCESS_ENTRY processEntry = (PKPHSS_PROCESS_ENTRY)Object; + PKPHSS_RULESET_ENTRY ruleSetEntry = (PKPHSS_RULESET_ENTRY)Object; + PLIST_ENTRY currentRuleListEntry; - KphDereferenceObject(processEntry->Client); + /* Dereference the client entry. */ + KphDereferenceObject(ruleSetEntry->Client); - ExAcquireFastMutex(&KphSsProcessListMutex); - RemoveEntryList(&processEntry->ProcessListEntry); - ExReleaseFastMutex(&KphSsProcessListMutex); + /* Dereference all rules in the ruleset. */ + ExAcquireFastMutex(&ruleSetEntry->RuleListMutex); + + currentRuleListEntry = ruleSetEntry->RuleListHead.Flink; + + while (currentRuleListEntry != &ruleSetEntry->RuleListHead) + { + KphDereferenceObject(KPHSS_RULE_ENTRY(currentRuleListEntry)); + currentRuleListEntry = currentRuleListEntry->Flink; + } + + ExReleaseFastMutex(&ruleSetEntry->RuleListMutex); + + /* Remove the ruleset from the list. */ + ExAcquireFastMutex(&KphSsRuleSetListMutex); + RemoveEntryList(&ruleSetEntry->RuleSetListEntry); + ExReleaseFastMutex(&KphSsRuleSetListMutex); +} + +/* 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; +} + +/* KphSsRemoveRule + * + * Removes a rule entry from a ruleset entry. + */ +NTSTATUS KphSsRemoveRule( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in HANDLE RuleEntryHandle + ) +{ + PLIST_ENTRY currentListEntry; + + ExAcquireFastMutex(&RuleSetEntry->RuleListMutex); + + currentListEntry = RuleSetEntry->RuleListHead.Flink; + + while (currentListEntry != &RuleSetEntry->RuleListHead) + { + PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry); + + if (ruleEntry->Handle == RuleEntryHandle) + { + RemoveEntryList(&ruleEntry->RuleListEntry); + ExReleaseFastMutex(&RuleSetEntry->RuleListMutex); + + return STATUS_SUCCESS; + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleaseFastMutex(&RuleSetEntry->RuleListMutex); + + 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)InterlockedExchangeAdd( + &RuleSetEntry->NextRuleHandle, + KPHSS_RULE_HANDLE_INCREMENT + ); + + /* Add the rule to the ruleset. */ + ExAcquireFastMutex(&RuleSetEntry->RuleListMutex); + InsertTailList(&RuleSetEntry->RuleListHead, &ruleEntry->RuleListEntry); + ExReleaseFastMutex(&RuleSetEntry->RuleListMutex); + /* Add a reference for the rule being on the list. */ + KphReferenceObject(ruleEntry); + + *RuleEntry = ruleEntry; + + return status; } /* KphpSsCreateEventBlock @@ -405,14 +641,14 @@ VOID NTAPI KphpSsProcessEntryDeleteProcedure( * NumberOfArguments: The number of arguments, in ULONGs. */ NTSTATUS KphpSsCreateEventBlock( - __out PKPHPSS_EVENT_BLOCK *EventBlock, + __out PKPHSS_EVENT_BLOCK *EventBlock, __in PKTHREAD Thread, __in ULONG Number, __in ULONG *Arguments, __in ULONG NumberOfArguments ) { - PKPHPSS_EVENT_BLOCK eventBlock; + PKPHSS_EVENT_BLOCK eventBlock; KPROCESSOR_MODE previousMode; ULONG eventBlockSize; ULONG argumentsSize; @@ -451,11 +687,7 @@ NTSTATUS KphpSsCreateEventBlock( /* Calculate the size of the event block. */ argumentsSize = NumberOfArguments * sizeof(ULONG); traceSize = capturedFrames * sizeof(PVOID); - eventBlockSize = sizeof(KPHPSS_EVENT_BLOCK) + argumentsSize + traceSize; - - /* Check if the event block is too large. */ - if (eventBlockSize > KPHPSS_EVENT_BLOCK_MAX_SIZE) - return STATUS_UNSUCCESSFUL; + eventBlockSize = sizeof(KPHSS_EVENT_BLOCK) + argumentsSize + traceSize; /* Allocate the event block. */ eventBlock = ExAllocatePoolWithTag(PagedPool, eventBlockSize, TAG_EVENT_BLOCK); @@ -472,15 +704,15 @@ NTSTATUS KphpSsCreateEventBlock( eventBlock->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess(Thread)); eventBlock->Number = Number; eventBlock->NumberOfArguments = NumberOfArguments; - eventBlock->ArgumentsOffset = sizeof(KPHPSS_EVENT_BLOCK); + eventBlock->ArgumentsOffset = sizeof(KPHSS_EVENT_BLOCK); eventBlock->TraceCount = capturedFrames; - eventBlock->TraceOffset = sizeof(KPHPSS_EVENT_BLOCK) + argumentsSize; + eventBlock->TraceOffset = sizeof(KPHSS_EVENT_BLOCK) + argumentsSize; /* Set the flags according to the previous mode. */ if (previousMode == UserMode) - eventBlock->Flags |= KPHPSS_EVENT_USER_MODE; + eventBlock->Flags |= KPHSS_EVENT_USER_MODE; else if (previousMode == KernelMode) - eventBlock->Flags |= KPHPSS_EVENT_KERNEL_MODE; + eventBlock->Flags |= KPHSS_EVENT_KERNEL_MODE; /* Probe and copy the arguments. */ if (previousMode != KernelMode) @@ -491,7 +723,7 @@ NTSTATUS KphpSsCreateEventBlock( } __except (EXCEPTION_EXECUTE_HANDLER) { - eventBlock->Flags |= KPHPSS_EVENT_PROBE_ARGUMENTS_FAILED; + eventBlock->Flags |= KPHSS_EVENT_PROBE_ARGUMENTS_FAILED; } } @@ -502,7 +734,7 @@ NTSTATUS KphpSsCreateEventBlock( } __except (EXCEPTION_EXECUTE_HANDLER) { - eventBlock->Flags |= KPHPSS_EVENT_COPY_ARGUMENTS_FAILED; + eventBlock->Flags |= KPHSS_EVENT_COPY_ARGUMENTS_FAILED; } /* Copy the stack trace. */ @@ -519,19 +751,104 @@ NTSTATUS KphpSsCreateEventBlock( * Frees an event block created by KphpSsCreateEventBlock. */ VOID KphpSsFreeEventBlock( - __in PKPHPSS_EVENT_BLOCK EventBlock + __in PKPHSS_EVENT_BLOCK EventBlock ) { ExFreePoolWithTag(EventBlock, TAG_EVENT_BLOCK); } +/* KphpSsCreateArgumentBlock + * + * Allocates and initializes an argument block. + */ +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + PKPHSS_CALL_ENTRY callEntry; + KPHSS_ARGUMENT_TYPE argumentType; + + /* 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; + + /* Is this a normal argument? If so, there's no point + * creating an argument block since the data is already + * in the event block. + */ + argumentType = callEntry->Arguments[Index]; + + if (argumentType == NormalArgument) + return STATUS_UNSUCCESSFUL; + + /* Capture the argument. */ + + switch (argumentType) + { + case Int8Argument: + case Int16Argument: + case Int32Argument: + case Int64Argument: + status = KphpSsCaptureSimple( + &argumentBlock, + (PVOID)Argument, + argumentType + ); + break; + case HandleArgument: + status = KphpSsCaptureHandle( + &argumentBlock, + (HANDLE)Argument + ); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (!NT_SUCCESS(status)) + return status; + + /* Put the index in. */ + argumentBlock->Index = Index; + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* 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 PKPHPSS_BLOCK_HEADER Block + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode ) { NTSTATUS status = STATUS_SUCCESS; @@ -541,7 +858,35 @@ NTSTATUS KphpSsWriteBlock( zeroTimeout.QuadPart = 0; - ExAcquireFastMutex(&ClientEntry->BufferMutex); + /* 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. @@ -554,13 +899,15 @@ NTSTATUS KphpSsWriteBlock( &zeroTimeout ); - if (!NT_SUCCESS(status) || status == STATUS_TIMEOUT) + if (!KPHSS_BLOCK_SUCCESS(status)) { if (status == STATUS_TIMEOUT) - dfprintf("Ss: WARNING: Dropped block (server %#x).\n", ClientEntry->BufferCursor); + { + dprintf("Ss: WARNING: Dropped block (server %#x).\n", ClientEntry->BufferCursor); + ClientEntry->NumberOfBlocksDropped++; + } - ExReleaseFastMutex(&ClientEntry->BufferMutex); - return status; + goto CleanupBufferMutex; } availableSpace = ClientEntry->BufferSize - ClientEntry->BufferCursor; @@ -574,23 +921,23 @@ NTSTATUS KphpSsWriteBlock( */ /* Check if we have enough space for a block header. */ - if (availableSpace < sizeof(KPHPSS_BLOCK_HEADER)) + if (availableSpace < sizeof(KPHSS_BLOCK_HEADER)) { /* Not enough space. Reset the cursor. */ - dfprintf("Ss: Implicit cursor reset (server %#x).\n", ClientEntry->BufferCursor); + 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) { - KPHPSS_RESET_BLOCK resetBlock; + 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(KPHPSS_RESET_BLOCK); + resetBlock.Header.Size = sizeof(KPHSS_RESET_BLOCK); resetBlock.Header.Type = ResetBlockType; /* Attach to the client process and copy the block. */ @@ -607,12 +954,11 @@ NTSTATUS KphpSsWriteBlock( __except (EXCEPTION_EXECUTE_HANDLER) { KphDetachProcess(&attachState); - ExReleaseFastMutex(&ClientEntry->BufferMutex); - - return GetExceptionCode(); + status = GetExceptionCode(); + goto CleanupBufferMutex; } - dfprintf("Ss: Wrote reset block (server %#x).\n", ClientEntry->BufferCursor); + dprintf("Ss: Wrote reset block (server %#x).\n", ClientEntry->BufferCursor); KphDetachProcess(&attachState); ClientEntry->BufferCursor = 0; availableSpace = ClientEntry->BufferSize; @@ -626,8 +972,8 @@ NTSTATUS KphpSsWriteBlock( if (availableSpace < Block->Size) { dfprintf("Ss: WARNING: Insufficient buffer size (server %#x).\n", ClientEntry->BufferCursor); - ExReleaseFastMutex(&ClientEntry->BufferMutex); - return STATUS_BUFFER_TOO_SMALL; + status = STATUS_BUFFER_TOO_SMALL; + goto CleanupBufferMutex; } /* Time to copy the block into the buffer. @@ -646,8 +992,8 @@ NTSTATUS KphpSsWriteBlock( { dfprintf("Ss: ERROR: Could not write to the client buffer (server %#x)!\n", ClientEntry->BufferCursor); KphDetachProcess(&attachState); - ExReleaseFastMutex(&ClientEntry->BufferMutex); - return GetExceptionCode(); + status = GetExceptionCode(); + goto CleanupBufferMutex; } KphDetachProcess(&attachState); @@ -665,15 +1011,18 @@ NTSTATUS KphpSsWriteBlock( __except (EXCEPTION_EXECUTE_HANDLER) { dfprintf("Ss: ERROR: Could not release read semaphore (server %#x)!\n", ClientEntry->BufferCursor); - ExReleaseFastMutex(&ClientEntry->BufferMutex); - return GetExceptionCode(); + status = GetExceptionCode(); + goto CleanupBufferMutex; } ClientEntry->BufferCursor += Block->Size; + ClientEntry->NumberOfBlocksWritten++; - dfprintf("Ss: Wrote block (server %#x).\n", ClientEntry->BufferCursor); + dprintf("Ss: Wrote block (server %#x).\n", ClientEntry->BufferCursor); - ExReleaseFastMutex(&ClientEntry->BufferMutex); +CleanupBufferMutex: + if (SequenceMode != InSequence) + ExReleaseFastMutex(&ClientEntry->BufferMutex); return status; } @@ -694,13 +1043,14 @@ VOID NTAPI KphpSsLogSystemServiceCall( __in PKTHREAD Thread ) { + NTSTATUS status = STATUS_SUCCESS; KPROCESSOR_MODE previousMode; - PEPROCESS process; PLIST_ENTRY currentListEntry; - PKPHSS_PROCESS_ENTRY processEntryArray[KPHSS_PROCESS_ENTRY_LIMIT]; - ULONG processEntryCount; - PKPHPSS_EVENT_BLOCK eventBlock; - ULONG i; + 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 = KeGetPreviousMode(); /* Ignore the Thread argument. Replace it with our own. */ @@ -730,7 +1080,7 @@ VOID NTAPI KphpSsLogSystemServiceCall( * we caused an exception somewhere. */ if ( ServiceTable->Base == __KeServiceDescriptorTable->Base && - Number == SysCallZwContinue && + Number == SsNtContinue && NumberOfArguments == 2 && previousMode == KernelMode ) @@ -756,53 +1106,53 @@ VOID NTAPI KphpSsLogSystemServiceCall( } } - /* Build the process entry array by going through the process + /* 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 mutex for too * long. */ - process = IoThreadToProcess(Thread); + ExAcquireFastMutex(&KphSsRuleSetListMutex); - if (!process) /* should never happen */ - { - dfprintf("Ss: ERROR: No process for thread!\n"); - return; - } - - ExAcquireFastMutex(&KphSsProcessListMutex); - - currentListEntry = KphSsProcessListHead.Flink; - processEntryCount = 0; + currentListEntry = KphSsRuleSetListHead.Flink; + ruleSetEntryCount = 0; while ( - currentListEntry != &KphSsProcessListHead && - processEntryCount < KPHSS_PROCESS_ENTRY_LIMIT + currentListEntry != &KphSsRuleSetListHead && + ruleSetEntryCount < KPHSS_RULESET_ENTRY_LIMIT ) { - PKPHSS_PROCESS_ENTRY processEntry = KPHSS_PROCESS_ENTRY(currentListEntry); + PKPHSS_RULESET_ENTRY ruleSetEntry = KPHSS_RULESET_ENTRY(currentListEntry); if ( - KphpSsIsProcessEntryRelevant(processEntry, process, previousMode) && - /* Make sure the process entry isn't being destroyed. */ - !KphIsDestroyedObject(processEntry) + KphpSsMatchRuleSetEntry( + ruleSetEntry, + Number, + Arguments, + NumberOfArguments, + ServiceTable, + Thread, + previousMode + ) && + /* Make sure the ruleset entry isn't being destroyed. */ + !KphIsDestroyedObject(ruleSetEntry) ) { - /* Reference and store the process entry in the local array. */ - KphReferenceObject(processEntry); - processEntryArray[processEntryCount] = processEntry; - processEntryCount++; + /* Reference and store the ruleset entry in the local array. */ + KphReferenceObject(ruleSetEntry); + ruleSetEntryArray[ruleSetEntryCount] = ruleSetEntry; + ruleSetEntryCount++; } currentListEntry = currentListEntry->Flink; } - ExReleaseFastMutex(&KphSsProcessListMutex); + ExReleaseFastMutex(&KphSsRuleSetListMutex); - /* If we didn't find any process entries, don't bother creating the + /* If we didn't find any ruleset entries, don't bother creating the * event block. */ - if (processEntryCount == 0) + if (ruleSetEntryCount == 0) return; /* We have work to do. Create an event block first. */ @@ -818,18 +1168,78 @@ VOID NTAPI KphpSsLogSystemServiceCall( return; } - /* Go through the process entry array and write the block to each - * client. While we're doing that we can also dereference each - * process entry. + /* Create the argument blocks. If we fail to create one, + * set the array entry to NULL and we'll skip it later. */ - for (i = 0; i < processEntryCount; i++) + + for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++) { - KphpSsWriteBlock(processEntryArray[i]->Client, &eventBlock->Header); - KphDereferenceObject(processEntryArray[i]); + ULONG argument; + + __try + { + /* We'll assume the arguments have already been probed + * since we created the event block successfully. + */ + argument = Arguments[i]; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* The caller is probably malicious. Exit. */ + KphpSsFreeEventBlock(eventBlock); + return; + } + + status = KphpSsCreateArgumentBlock( + &argumentBlockArray[i], + Number, + argument, + i + ); + + 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]); + } } /* KphpSsNewKiFastCallEntry diff --git a/trunk/KProcessHacker/sysservicedata.c b/trunk/KProcessHacker/sysservicedata.c new file mode 100644 index 000000000..55ca9d8c3 --- /dev/null +++ b/trunk/KProcessHacker/sysservicedata.c @@ -0,0 +1,160 @@ +/* + * 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 + ); + +/* NTSTATUS NtAddAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */ +KPHSS_CALL_ENTRY SsNtAddAtomEntry = { &SsNtAddAtom, "NtAddAtom", 3, { WStringArgument, 0, Int16Argument } }; +/* NTSTATUS NtAlertResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount) */ +KPHSS_CALL_ENTRY SsNtAlertResumeThreadEntry = { &SsNtAlertResumeThread, "NtAlertResumeThread", 2, { HandleArgument, NormalArgument } }; +/* NTSTATUS NtClose(HANDLE Handle) */ +KPHSS_CALL_ENTRY SsNtCloseEntry = { &SsNtClose, "NtClose", 1, { HandleArgument } }; +/* NTSTATUS NtContinue(PCONTEXT Context, BOOLEAN TestAlert) */ +KPHSS_CALL_ENTRY SsNtContinueEntry = { &SsNtContinue, "NtContinue", 2, { ContextArgument, 0 } }; + +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, NormalArgument } }, + /* NTSTATUS NtClose(HANDLE Handle) */ + { &SsNtClose, "NtClose", 1, { HandleArgument } }, + /* NTSTATUS NtContinue(PCONTEXT Context, BOOLEAN TestAlert) */ + { &SsNtContinue, "NtContinue", 2, { ContextArgument, 0 } }, + /* NTSTATUS NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER Interval) */ + { &SsNtDelayExecution, "NtDelayExecution", 2, { 0, Int64Argument } }, + + { NULL, "Dummy", 0 } +}; + +RTL_GENERIC_TABLE KphSsCallTable; + +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 + ); + } + } +} + +VOID KphSsDataDeinit() +{ + PKPHSS_CALL_ENTRY callEntry; + + while (callEntry = (PKPHSS_CALL_ENTRY)RtlGetElementGenericTable(&KphSsCallTable, 0)) + RtlDeleteElementGenericTable(&KphSsCallTable, callEntry); +} + +PKPHSS_CALL_ENTRY KphSsLookupCallEntry( + __in ULONG Number + ) +{ + KPHSS_CALL_ENTRY callEntry; + + callEntry.Number = &Number; + + return (PKPHSS_CALL_ENTRY)RtlLookupElementGenericTable( + &KphSsCallTable, + &callEntry + ); +} + +PVOID KphpSsCallEntryAllocateRoutine( + __in PRTL_GENERIC_TABLE Table, + __in CLONG ByteSize + ) +{ + return ExAllocatePoolWithTag( + PagedPool, + ByteSize, + TAG_CALL_ENTRY + ); +} + +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; +} + +VOID KphpSsCallEntryFreeRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID Buffer + ) +{ + ExFreePoolWithTag( + Buffer, + TAG_CALL_ENTRY + ); +} diff --git a/trunk/KProcessHacker/version.c b/trunk/KProcessHacker/version.c index 4ed39913d..e18045657 100644 --- a/trunk/KProcessHacker/version.c +++ b/trunk/KProcessHacker/version.c @@ -180,10 +180,11 @@ NTSTATUS KvInit() OffEpProtectedProcessBit = 0; OffEpRundownProtect = 0x80; OffOhBody = 0x18; + OffOtName = 0x40; OffOtiGenericMapping = 0x60 + 0x8; OffOtiOpenProcedure = 0x60 + 0x30; - SysCallZwContinue = 0x20; + SsNtContinue = 0x20; /* We are scanning for PspTerminateProcess which has the same signature as PsTerminateProcess because @@ -262,26 +263,33 @@ NTSTATUS KvInit() /* SP0 */ if (servicePack == 0) { + OffOtName = 0x40; OffOtiGenericMapping = 0x60 + 0xc; OffOtiOpenProcedure = 0x60 + 0x30; - SysCallZwContinue = 0x36; + SsNtContinue = 0x36; } /* SP1 */ else if (servicePack == 1) { + OffOtName = 0x8; OffOtiGenericMapping = 0x28 + 0xc; /* They got rid of the Mutex (an ERESOURCE) */ OffOtiOpenProcedure = 0x28 + 0x34; - SysCallZwContinue = 0x37; + SsNtContinue = 0x37; } /* SP2 */ else if (servicePack == 2) { + OffOtName = 0x8; OffOtiGenericMapping = 0x28 + 0xc; OffOtiOpenProcedure = 0x28 + 0x34; - SysCallZwContinue = 0x37; + SsNtAddAtom = 0x8; + SsNtAlertResumeThread = 0xd; + SsNtClose = 0x30; + SsNtContinue = 0x37; + SsNtDelayExecution = 0x76; } else { @@ -311,10 +319,11 @@ NTSTATUS KvInit() OffEpProtectedProcessBit = 0xb; OffEpRundownProtect = 0xb0; OffOhBody = 0x18; + OffOtName = 0x8; OffOtiGenericMapping = 0x28 + 0xc; OffOtiOpenProcedure = 0x28 + 0x34; - SysCallZwContinue = 0x3c; + SsNtContinue = 0x3c; INIT_SCAN( PsTerminateProcessScan, diff --git a/trunk/ProcessHacker.Native/KProcessHacker.cs b/trunk/ProcessHacker.Native/KProcessHacker.cs index e814742fe..5cdc6dc16 100644 --- a/trunk/ProcessHacker.Native/KProcessHacker.cs +++ b/trunk/ProcessHacker.Native/KProcessHacker.cs @@ -96,7 +96,9 @@ namespace ProcessHacker.Native SsRef, SsUnref, SsCreateClientEntry, - SsCreateProcessEntry + SsCreateRuleSetEntry, + SsRemoveRule, + SsAddProcessIdRule } [Flags] @@ -848,6 +850,24 @@ namespace ProcessHacker.Native _fileHandle.IoControl(CtlCode(Control.SetProcessToken), inData, 8, null, 0); } + public IntPtr SsAddProcessIdRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + KphSsFilterType filterType, + IntPtr processId + ) + { + byte* inData = stackalloc byte[0xc]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)filterType; + *(int*)(inData + 0x8) = processId.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.SsAddProcessIdRule), inData, 0xc, outData, 4); + + return (*(int*)outData).ToIntPtr(); + } + public KphSsClientEntryHandle SsCreateClientEntry( ProcessHandle processHandle, SemaphoreHandle readSemaphoreHandle, @@ -870,22 +890,35 @@ namespace ProcessHacker.Native return new KphSsClientEntryHandle((*(int*)outData).ToIntPtr()); } - public KphSsProcessEntryHandle SsCreateProcessEntry( + public KphSsRuleSetEntryHandle SsCreateRuleSetEntry( KphSsClientEntryHandle clientEntryHandle, - ProcessHandle targetProcessHandle, - KphSsLogFlags flags + KphSsFilterType defaultFilterType, + KphSsRuleSetAction action ) { byte* inData = stackalloc byte[0xc]; byte* outData = stackalloc byte[4]; *(int*)inData = clientEntryHandle.Handle.ToInt32(); - *(int*)(inData + 0x4) = targetProcessHandle; - *(int*)(inData + 0x8) = (int)flags; + *(int*)(inData + 0x4) = (int)defaultFilterType; + *(int*)(inData + 0x8) = (int)action; - _fileHandle.IoControl(CtlCode(Control.SsCreateProcessEntry), inData, 0xc, outData, 4); + _fileHandle.IoControl(CtlCode(Control.SsCreateRuleSetEntry), inData, 0xc, outData, 4); - return new KphSsProcessEntryHandle((*(int*)outData).ToIntPtr()); + return new KphSsRuleSetEntryHandle((*(int*)outData).ToIntPtr()); + } + + public void SsRemoveRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + IntPtr ruleEntryHandle + ) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 4) = ruleEntryHandle.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.SsRemoveRule), inData, 8, null, 0); } public void SsRef() @@ -940,10 +973,29 @@ namespace ProcessHacker.Native DriverServiceKeyNameInformation } + public enum KphSsArgumentType : int + { + Normal = 0, + Int8, + Int16, + Int32, + Int64, + Handle, + String, + WString, + AnsiString, + UnicodeString, + ObjectAttributes, + ClientId, + Context, + InitialTeb + } + public enum KphSsBlockType : int { Reset, - Event + Event, + Argument } [Flags] @@ -955,13 +1007,24 @@ namespace ProcessHacker.Native UserMode = 0x8 } + public enum KphSsFilterType : int + { + Include, + Exclude + } + [Flags] - public enum KphSsLogFlags : int + public enum KphSsModeFlags : int { UserMode = 0x1, KernelMode = 0x2 } + public enum KphSsRuleSetAction : int + { + Log + } + public class KphHandle : BaseObject { private IntPtr _handle; @@ -989,9 +1052,9 @@ namespace ProcessHacker.Native { } } - public class KphSsProcessEntryHandle : KphHandle + public class KphSsRuleSetEntryHandle : KphHandle { - internal KphSsProcessEntryHandle(IntPtr handle) + internal KphSsRuleSetEntryHandle(IntPtr handle) : base(handle) { } } @@ -1004,6 +1067,30 @@ namespace ProcessHacker.Native public int DriverSize; } + [StructLayout(LayoutKind.Sequential)] + public struct KphSsArgumentBlock + { + [StructLayout(LayoutKind.Explicit)] + public struct KphSsArgumentUnion + { + [FieldOffset(0)] + public int Normal; + [FieldOffset(0)] + public byte Int8; + [FieldOffset(0)] + public short Int16; + [FieldOffset(0)] + public int Int32; + [FieldOffset(0)] + public long Int64; + } + + public KphSsBlockHeader Header; + public int Index; + public KphSsArgumentType Type; + public KphSsArgumentUnion Data; + } + [StructLayout(LayoutKind.Sequential)] public struct KphSsBlockHeader { @@ -1014,7 +1101,7 @@ namespace ProcessHacker.Native [StructLayout(LayoutKind.Sequential)] public struct KphSsEventBlock { - KphSsBlockHeader Header; + public KphSsBlockHeader Header; public int Flags; public long Time; public ClientId ClientId; @@ -1027,6 +1114,20 @@ namespace ProcessHacker.Native public int TraceOffset; } + [StructLayout(LayoutKind.Sequential)] + public struct KphSsHandle + { + public int TypeNameOffset; + public int NameOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsWString + { + public ushort Length; + public byte Buffer; + } + [StructLayout(LayoutKind.Sequential)] public struct ProcessHandleInformation {