You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2686 lines
72 KiB
2686 lines
72 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbtrsup.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to implement the kernel mode SmbTrace
|
|
component within the LanMan server and redirector.
|
|
The interface between the kernel mode component and the
|
|
server/redirector is found in nt\private\inc\smbtrsup.h
|
|
The interface providing user-level access to SmbTrace is found in
|
|
nt\private\inc\smbtrace.h
|
|
|
|
Author:
|
|
|
|
Peter Gray (w-peterg) 23-March-1992
|
|
|
|
Revision History:
|
|
|
|
Stephan Mueller (t-stephm) 21-July-1992
|
|
|
|
Completed, fixed bugs, moved all associated declarations here
|
|
from various places in the server, ported to the redirector
|
|
and converted to a kernel DLL.
|
|
|
|
--*/
|
|
|
|
#include <ntifs.h>
|
|
#include <smbtrace.h> // for names and structs shared with user-mode app
|
|
|
|
#define _SMBTRSUP_SYS_ 1 // to get correct definitions for exported variables
|
|
#include <smbtrsup.h> // for functions exported to server/redirector
|
|
|
|
#if DBG
|
|
ULONG SmbtrsupDebug = 0;
|
|
#define TrPrint(x) if (SmbtrsupDebug) KdPrint(x)
|
|
#else
|
|
#define TrPrint(x)
|
|
#endif
|
|
|
|
//
|
|
// we assume all well-known names are #defined in Unicode, and require
|
|
// them to be so: in the SmbTrace application and the smbtrsup.sys package
|
|
//
|
|
#ifndef UNICODE
|
|
#error "UNICODE build required"
|
|
#endif
|
|
|
|
|
|
#if DBG
|
|
#define PAGED_DBG 1
|
|
#endif
|
|
#ifdef PAGED_DBG
|
|
#undef PAGED_CODE
|
|
#define PAGED_CODE() \
|
|
struct { ULONG bogus; } ThisCodeCantBePaged; \
|
|
ThisCodeCantBePaged; \
|
|
if (KeGetCurrentIrql() > APC_LEVEL) { \
|
|
KdPrint(( "SMBTRSUP: Pageable code called at IRQL %d. File %s, Line %d\n", KeGetCurrentIrql(), __FILE__, __LINE__ )); \
|
|
ASSERT(FALSE); \
|
|
}
|
|
#define PAGED_CODE_CHECK() if (ThisCodeCantBePaged) ;
|
|
ULONG ThisCodeCantBePaged;
|
|
#else
|
|
#define PAGED_CODE_CHECK()
|
|
#endif
|
|
|
|
|
|
#if PAGED_DBG
|
|
#define ACQUIRE_SPIN_LOCK(a, b) { \
|
|
PAGED_CODE_CHECK(); \
|
|
KeAcquireSpinLock(a, b); \
|
|
}
|
|
#define RELEASE_SPIN_LOCK(a, b) { \
|
|
PAGED_CODE_CHECK(); \
|
|
KeReleaseSpinLock(a, b); \
|
|
}
|
|
|
|
#else
|
|
#define ACQUIRE_SPIN_LOCK(a, b) KeAcquireSpinLock(a, b)
|
|
#define RELEASE_SPIN_LOCK(a, b) KeReleaseSpinLock(a, b)
|
|
#endif
|
|
|
|
//
|
|
// Increment shared variable in instance data using appropriate interlock
|
|
//
|
|
#define LOCK_INC_ID(var) \
|
|
ExInterlockedAddUlong( (PULONG)&ID(var), \
|
|
1, &ID(var##Interlock) )
|
|
|
|
//
|
|
// Zero shared variable in instance data using appropriate interlock
|
|
//
|
|
#define LOCK_ZERO_ID(var) { \
|
|
ID(var) = 0; \
|
|
}
|
|
|
|
|
|
//
|
|
// The various states SmbTrace can be in. These states are internal
|
|
// only. The external SmbTraceActive variable contains much less
|
|
// detailed information: it is TRUE when TraceRunning, FALSE in any other
|
|
// state.
|
|
//
|
|
typedef enum _SMBTRACE_STATE {
|
|
TraceStopped, // not running
|
|
TraceStarting, // preparing to run
|
|
TraceStartStopFile, // starting, but want to shut down immediately
|
|
// because the FileObject closed
|
|
TraceStartStopNull, // starting, but want to shut down immediately
|
|
// because a new fsctl came in
|
|
TraceAppWaiting, // waiting for application to die
|
|
TraceRunning, // processing SMBs
|
|
TraceStopping // waiting for smbtrace thread to stop
|
|
} SMBTRACE_STATE;
|
|
|
|
|
|
//
|
|
// Structure used to hold information regarding an SMB which is put into
|
|
// the SmbTrace thread queue.
|
|
//
|
|
typedef struct _SMBTRACE_QUEUE_ENTRY {
|
|
LIST_ENTRY ListEntry; // usual doubly-linked list
|
|
ULONG SmbLength; // the length of this SMB
|
|
PVOID Buffer; // pointer into SmbTracePortMemoryHeap
|
|
// or non-paged pool
|
|
PVOID SmbAddress; // address of real SMB, if SMB still
|
|
// available (i.e. if slow mode)
|
|
BOOLEAN BufferNonPaged; // TRUE if Buffer in non-paged pool, FALSE if
|
|
// Buffer in SmbTracePortMemoryHeap
|
|
// Redirector-specific
|
|
PKEVENT WaitEvent; // pointer to worker thread event to be
|
|
// signalled when SMB has been processed
|
|
// slow mode specific
|
|
} SMBTRACE_QUEUE_ENTRY, *PSMBTRACE_QUEUE_ENTRY;
|
|
|
|
|
|
//
|
|
// Instance data is specific to the component being traced. In order
|
|
// to unclutter the source code, use the following macro to access
|
|
// instance specific data.
|
|
// Every exported function either has an explicit parameter (named
|
|
// Component) which the caller provides, or is implicitly applicable
|
|
// only to one component, and has a local variable named Component
|
|
// which is always set to the appropriate value.
|
|
//
|
|
#define ID(field) (SmbTraceData[Component].field)
|
|
|
|
//
|
|
// Instance data. The fields which need to be statically initialized
|
|
// are declared before those that we don't care to initialize.
|
|
//
|
|
typedef struct _INSTANCE_DATA {
|
|
|
|
//
|
|
// Statically initialized fields.
|
|
//
|
|
|
|
//
|
|
// Names for identifying the component being traced in KdPrint messages,
|
|
// and global objects
|
|
//
|
|
PCHAR ComponentName;
|
|
PWSTR SharedMemoryName;
|
|
PWSTR NewSmbEventName;
|
|
PWSTR DoneSmbEventName;
|
|
|
|
//
|
|
// Prevent reinitializing resources if rdr/srv reloaded
|
|
//
|
|
BOOLEAN InstanceInitialized;
|
|
|
|
//
|
|
// some tracing parameters, from SmbTrace application
|
|
//
|
|
BOOLEAN SingleSmbMode;
|
|
CLONG Verbosity;
|
|
|
|
//
|
|
// State of the current trace.
|
|
//
|
|
SMBTRACE_STATE TraceState;
|
|
|
|
//
|
|
// Pointer to file object of client who started the current trace.
|
|
//
|
|
PFILE_OBJECT StartersFileObject;
|
|
|
|
//
|
|
// Fsp process of the component we're tracing in.
|
|
//
|
|
PEPROCESS FspProcess;
|
|
|
|
//
|
|
// All subsequent fields are not expliticly statically initiliazed.
|
|
//
|
|
|
|
//
|
|
// Current count of number of SMBs lost since last one output.
|
|
// Use an interlock to access, cleared when an SMB is sent to
|
|
// the client successfully. This lock is used with ExInterlockedXxx
|
|
// routines, so it cannot be treated as a real spin lock (i.e.
|
|
// don't use KeAcquireSpinLock.)
|
|
//
|
|
KSPIN_LOCK SmbsLostInterlock;
|
|
ULONG SmbsLost;
|
|
|
|
//
|
|
// some events, only accessed within the kernel
|
|
//
|
|
KEVENT ActiveEvent;
|
|
KEVENT TerminatedEvent;
|
|
KEVENT TerminationEvent;
|
|
KEVENT AppTerminationEvent;
|
|
KEVENT NeedMemoryEvent;
|
|
|
|
//
|
|
// some events, shared with the outside world
|
|
//
|
|
HANDLE NewSmbEvent;
|
|
HANDLE DoneSmbEvent;
|
|
|
|
//
|
|
// Handle to the shared memory used for communication between
|
|
// the server/redirector and SmbTrace.
|
|
//
|
|
HANDLE SectionHandle;
|
|
|
|
//
|
|
// Pointers to control the shared memory for the SmbTrace application.
|
|
// The port memory heap handle is initialized to NULL to indicate that
|
|
// there is no connection with SmbTrace yet.
|
|
//
|
|
PVOID PortMemoryBase;
|
|
ULONG_PTR PortMemorySize;
|
|
ULONG TableSize;
|
|
PVOID PortMemoryHeap;
|
|
|
|
//
|
|
// serialized access to the heap,
|
|
// to allow clean shutdown (StateInterlock)
|
|
//
|
|
KSPIN_LOCK HeapReferenceCountLock;
|
|
PERESOURCE StateInterlock;
|
|
PERESOURCE HeapInterlock;
|
|
ULONG HeapReferenceCount;
|
|
|
|
WORK_QUEUE_ITEM DereferenceWorkQueueItem;
|
|
|
|
//
|
|
// Pointers to the structured data, located in the shared memory.
|
|
//
|
|
PSMBTRACE_TABLE_HEADER TableHeader;
|
|
PSMBTRACE_TABLE_ENTRY Table;
|
|
|
|
//
|
|
// Fields for the SmbTrace queue. The server/redirector puts
|
|
// incoming and outgoing SMBs into this queue (when
|
|
// SmbTraceActive[Component] is TRUE and they are processed
|
|
// by the SmbTrace thread.
|
|
//
|
|
LIST_ENTRY Queue; // The queue itself
|
|
KSPIN_LOCK QueueInterlock; // Synchronizes access to queue
|
|
KSEMAPHORE QueueSemaphore; // Counts elements in queue
|
|
|
|
} INSTANCE_DATA;
|
|
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGESMBD")
|
|
#endif
|
|
//
|
|
// Global variables for SmbTrace support
|
|
//
|
|
|
|
INSTANCE_DATA SmbTraceData[] = {
|
|
|
|
//
|
|
// Server data
|
|
//
|
|
|
|
{
|
|
"Srv", // ComponentName
|
|
SMBTRACE_SRV_SHARED_MEMORY_NAME, // SharedMemoryName
|
|
SMBTRACE_SRV_NEW_SMB_EVENT_NAME, // NewSmbEventName
|
|
SMBTRACE_SRV_DONE_SMB_EVENT_NAME, // DoneSmbEventName
|
|
|
|
FALSE, // InstanceInitialized
|
|
|
|
FALSE, // SingleSmbMode
|
|
SMBTRACE_VERBOSITY_ERROR, // Verbosity
|
|
|
|
TraceStopped, // TraceState
|
|
|
|
NULL, // StartersFileObject
|
|
NULL // FspProcess
|
|
|
|
// rest of fields expected to get 'all-zeroes'
|
|
},
|
|
|
|
//
|
|
// Redirector data
|
|
//
|
|
|
|
{
|
|
"Rdr", // ComponentName
|
|
SMBTRACE_LMR_SHARED_MEMORY_NAME, // SharedMemoryName
|
|
SMBTRACE_LMR_NEW_SMB_EVENT_NAME, // NewSmbEventName
|
|
SMBTRACE_LMR_DONE_SMB_EVENT_NAME, // DoneSmbEventName
|
|
|
|
FALSE, // InstanceInitialized
|
|
|
|
FALSE, // SingleSmbMode
|
|
SMBTRACE_VERBOSITY_ERROR, // Verbosity
|
|
|
|
TraceStopped, // TraceState
|
|
|
|
NULL, // StartersFileObject
|
|
NULL // FspProcess
|
|
|
|
// rest of fields expected to get 'all-zeroes'
|
|
}
|
|
};
|
|
|
|
|
|
//
|
|
// some state booleans, exported to clients. For this reason,
|
|
// they're stored separately from the rest of the instance data.
|
|
// Initially, SmbTrace is neither active nor transitioning.
|
|
//
|
|
BOOLEAN SmbTraceActive[] = {FALSE, FALSE};
|
|
BOOLEAN SmbTraceTransitioning[] = {FALSE, FALSE};
|
|
|
|
HANDLE
|
|
SmbTraceDiscardableCodeHandle = 0;
|
|
|
|
HANDLE
|
|
SmbTraceDiscardableDataHandle = 0;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
//
|
|
// Forward declarations of internal routines
|
|
//
|
|
|
|
BOOLEAN
|
|
SmbTraceReferenceHeap(
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
VOID
|
|
SmbTraceDereferenceHeap(
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
VOID
|
|
SmbTraceDisconnect(
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
VOID
|
|
SmbTraceEmptyQueue (
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
VOID
|
|
SmbTraceThreadEntry(
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbTraceFreeMemory (
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
VOID
|
|
SmbTraceToClient(
|
|
IN PVOID Smb,
|
|
IN CLONG SmbLength,
|
|
IN PVOID SmbAddress,
|
|
IN SMBTRACE_COMPONENT Component
|
|
);
|
|
|
|
ULONG
|
|
SmbTraceMdlLength(
|
|
IN PMDL Mdl
|
|
);
|
|
|
|
VOID
|
|
SmbTraceCopyMdlContiguous(
|
|
OUT PVOID Destination,
|
|
IN PMDL Mdl,
|
|
IN ULONG Length
|
|
);
|
|
|
|
//NTSTATUS
|
|
//DriverEntry(
|
|
// IN PDRIVER_OBJECT DriverObject,
|
|
// IN PUNICODE_STRING RegistryPath
|
|
// );
|
|
|
|
VOID
|
|
SmbTraceDeferredDereferenceHeap(
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SmbTraceInitialize)
|
|
#pragma alloc_text(PAGE, SmbTraceTerminate)
|
|
#pragma alloc_text(PAGE, SmbTraceStart)
|
|
#pragma alloc_text(PAGE, SmbTraceStop)
|
|
#pragma alloc_text(PAGE, SmbTraceCompleteSrv)
|
|
#pragma alloc_text(PAGE, SmbTraceDisconnect)
|
|
#pragma alloc_text(PAGE, SmbTraceEmptyQueue)
|
|
#pragma alloc_text(PAGE, SmbTraceThreadEntry)
|
|
#pragma alloc_text(PAGE, SmbTraceFreeMemory)
|
|
#pragma alloc_text(PAGE, SmbTraceToClient)
|
|
#pragma alloc_text(PAGE, SmbTraceDeferredDereferenceHeap)
|
|
#pragma alloc_text(PAGESMBC, SmbTraceCompleteRdr)
|
|
#pragma alloc_text(PAGESMBC, SmbTraceReferenceHeap)
|
|
#pragma alloc_text(PAGESMBC, SmbTraceDereferenceHeap)
|
|
#pragma alloc_text(PAGESMBC, SmbTraceMdlLength)
|
|
#pragma alloc_text(PAGESMBC, SmbTraceCopyMdlContiguous)
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// Exported routines
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
SmbTraceInitialize (
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the SmbTrace component-specific instance
|
|
globals. On first-ever invocation, it performs truly global
|
|
initialization.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates failure if unable to allocate resources
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if ( ID(InstanceInitialized) == FALSE ) {
|
|
//
|
|
// Component specific initialization -- events and locks.
|
|
//
|
|
|
|
KeInitializeEvent( &ID(ActiveEvent), NotificationEvent, FALSE);
|
|
KeInitializeEvent( &ID(TerminatedEvent), NotificationEvent, FALSE);
|
|
KeInitializeEvent( &ID(TerminationEvent), NotificationEvent, FALSE);
|
|
KeInitializeEvent( &ID(AppTerminationEvent), NotificationEvent, FALSE);
|
|
KeInitializeEvent( &ID(NeedMemoryEvent), NotificationEvent, FALSE);
|
|
|
|
KeInitializeSpinLock( &ID(SmbsLostInterlock) );
|
|
KeInitializeSpinLock( &ID(HeapReferenceCountLock) );
|
|
|
|
ID(StateInterlock) = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(ERESOURCE),
|
|
'tbmS'
|
|
);
|
|
if ( ID(StateInterlock) == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
ExInitializeResourceLite( ID(StateInterlock) );
|
|
|
|
ID(HeapInterlock) = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(ERESOURCE),
|
|
'tbmS'
|
|
);
|
|
if ( ID(HeapInterlock) == NULL ) {
|
|
ExDeleteResourceLite( ID(StateInterlock) );
|
|
ExFreePool( ID(StateInterlock) );
|
|
ID(StateInterlock) = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
ExInitializeResourceLite( ID(HeapInterlock) );
|
|
|
|
ID(InstanceInitialized) = TRUE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SmbTraceInitialize
|
|
|
|
|
|
VOID
|
|
SmbTraceTerminate (
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up the SmbTrace component-specific instance
|
|
globals. It should be called by the component when the component
|
|
is unloaded.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if ( ID(InstanceInitialized) ) {
|
|
|
|
ExDeleteResourceLite( ID(StateInterlock) );
|
|
ExFreePool( ID(StateInterlock) );
|
|
|
|
ExDeleteResourceLite( ID(HeapInterlock) );
|
|
ExFreePool( ID(HeapInterlock) );
|
|
|
|
ID(InstanceInitialized) = FALSE;
|
|
}
|
|
|
|
return;
|
|
|
|
} // SmbTraceTerminate
|
|
|
|
|
|
NTSTATUS
|
|
SmbTraceStart (
|
|
IN ULONG InputBufferLength,
|
|
IN ULONG OutputBufferLength,
|
|
IN OUT PVOID ConfigInOut,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs all the work necessary to connect the server/
|
|
redirector to SmbTrace. It creates the section of shared memory to
|
|
be used, then creates the events needed. All these objects are then
|
|
opened by the client (smbtrace) program. This code initializes the
|
|
table, the heap stored in the section and table header. This routine
|
|
must be called from an Fsp process.
|
|
|
|
Arguments:
|
|
|
|
InputBufferLength - Length of the ConfigInOut packet
|
|
|
|
OutputBufferLength - Length expected for the ConfigInOut packet returned
|
|
|
|
ConfigInOut - A structure that has configuration information.
|
|
|
|
FileObject - FileObject of the process requesting that SmbTrace be started,
|
|
used to automatically shut down when the app dies.
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation.
|
|
|
|
--*/
|
|
|
|
// size of our one, particular, ACL
|
|
#define ACL_LENGTH (ULONG)sizeof(ACL) + \
|
|
(ULONG)sizeof(ACCESS_ALLOWED_ACE) + \
|
|
sizeof(LUID) + \
|
|
8
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING memoryNameU;
|
|
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
UCHAR Buffer[ACL_LENGTH];
|
|
PACL AdminAcl = (PACL)(&Buffer[0]);
|
|
SECURITY_DESCRIPTOR securityDescriptor;
|
|
|
|
UNICODE_STRING eventNameU;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG i;
|
|
LARGE_INTEGER sectionSize;
|
|
PSMBTRACE_CONFIG_PACKET_REQ ConfigPacket;
|
|
PSMBTRACE_CONFIG_PACKET_RESP ConfigPacketResp;
|
|
HANDLE threadHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( ID(InstanceInitialized) );
|
|
|
|
//
|
|
// Validate the buffer lengths passed in.
|
|
//
|
|
|
|
if ( ( InputBufferLength != sizeof( SMBTRACE_CONFIG_PACKET_REQ ) )
|
|
|| ( OutputBufferLength != sizeof( SMBTRACE_CONFIG_PACKET_RESP ) )
|
|
) {
|
|
|
|
TrPrint(( "%s!SmbTraceStart: config packet(s) of wrong size!\n",
|
|
ID(ComponentName) ));
|
|
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite( ID(StateInterlock), TRUE );
|
|
|
|
if ( ID(TraceState) != TraceStopped ) {
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
return STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
ASSERT(!SmbTraceActive[Component]);
|
|
|
|
ASSERT (SmbTraceDiscardableDataHandle == NULL);
|
|
|
|
ASSERT (SmbTraceDiscardableCodeHandle == NULL);
|
|
|
|
SmbTraceDiscardableCodeHandle = MmLockPagableCodeSection(SmbTraceReferenceHeap);
|
|
|
|
SmbTraceDiscardableDataHandle = MmLockPagableDataSection(SmbTraceData);
|
|
|
|
ID(TraceState) = TraceStarting;
|
|
|
|
//
|
|
// Initialize global variables so that we know what to close on errexit
|
|
//
|
|
|
|
ID(SectionHandle) = NULL;
|
|
ID(PortMemoryHeap) = NULL;
|
|
ID(NewSmbEvent) = NULL;
|
|
ID(DoneSmbEvent) = NULL;
|
|
|
|
//
|
|
// Caution! Both input and output packets are the same, we must
|
|
// read all of the input before we write any output.
|
|
//
|
|
|
|
ConfigPacket = (PSMBTRACE_CONFIG_PACKET_REQ) ConfigInOut;
|
|
ConfigPacketResp = (PSMBTRACE_CONFIG_PACKET_RESP) ConfigInOut;
|
|
|
|
//
|
|
// Set the mode of operation (read all values).
|
|
//
|
|
|
|
ID(SingleSmbMode) = ConfigPacket->SingleSmbMode;
|
|
ID(Verbosity) = ConfigPacket->Verbosity;
|
|
ID(PortMemorySize) = ConfigPacket->BufferSize;
|
|
ID(TableSize) = ConfigPacket->TableSize;
|
|
|
|
//
|
|
// Create a security descriptor containing a discretionary Acl
|
|
// allowing administrator access. This SD will be used to allow
|
|
// Smbtrace access to the shared memory and the notification events.
|
|
//
|
|
|
|
// Create Acl allowing administrator access using well-known Sid.
|
|
|
|
status = RtlCreateAcl( AdminAcl, ACL_LENGTH, ACL_REVISION2 );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: RtlCreateAcl failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
status = RtlAddAccessAllowedAce(
|
|
AdminAcl,
|
|
ACL_REVISION2,
|
|
GENERIC_ALL,
|
|
SeExports->SeAliasAdminsSid
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: RtlAddAccessAllowedAce failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
// Create SecurityDescriptor containing AdminAcl as a discrectionary ACL.
|
|
|
|
RtlCreateSecurityDescriptor(
|
|
&securityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION1
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: RtlCreateSecurityDescriptor failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
status = RtlSetDaclSecurityDescriptor(
|
|
&securityDescriptor,
|
|
TRUE,
|
|
AdminAcl,
|
|
FALSE
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: "
|
|
"RtlSetDAclAllowedSecurityDescriptor failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
//
|
|
// Create the section to be used for communication between the
|
|
// server/redirector and SmbTrace.
|
|
//
|
|
|
|
// Define the object name.
|
|
|
|
RtlInitUnicodeString( &memoryNameU, ID(SharedMemoryName) );
|
|
|
|
// Define the object information, including security descriptor and name.
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&memoryNameU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
&securityDescriptor
|
|
);
|
|
|
|
// Setup the section size.
|
|
|
|
sectionSize.QuadPart = ID(PortMemorySize);
|
|
|
|
// Create the named section of memory with all of our attributes.
|
|
|
|
status = ZwCreateSection(
|
|
&ID(SectionHandle),
|
|
SECTION_MAP_READ | SECTION_MAP_WRITE,
|
|
&objectAttributes,
|
|
§ionSize,
|
|
PAGE_READWRITE,
|
|
SEC_RESERVE,
|
|
NULL // file handle
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint(( "%s!SmbTraceStart: ZwCreateSection failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
// Now, map it into our address space.
|
|
|
|
ID(PortMemoryBase) = NULL;
|
|
|
|
status = ZwMapViewOfSection(
|
|
ID(SectionHandle),
|
|
NtCurrentProcess(),
|
|
&ID(PortMemoryBase),
|
|
0, // zero bits (don't care)
|
|
0, // commit size
|
|
NULL, // SectionOffset
|
|
&ID(PortMemorySize), // viewSize
|
|
ViewUnmap, // inheritDisposition
|
|
0L, // allocation type
|
|
PAGE_READWRITE // protection
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint(( "%s!SmbTraceStart: NtMapViewOfSection failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
|
|
//
|
|
// Set up the shared section memory as a heap.
|
|
//
|
|
// *** Note that the HeapInterlock for the client instance is passed
|
|
// to the heap manager to be used for serialization of
|
|
// allocation and deallocation. It is necessary for the
|
|
// resource to be allocated FROM NONPAGED POOL externally to the
|
|
// heap manager, because if we let the heap manager allocate
|
|
// the resource, if would allocate it from process virtual
|
|
// memory.
|
|
//
|
|
|
|
ID(PortMemoryHeap) = RtlCreateHeap(
|
|
0, // Flags
|
|
ID(PortMemoryBase), // HeapBase
|
|
ID(PortMemorySize), // ReserveSize
|
|
PAGE_SIZE, // CommitSize
|
|
ID(HeapInterlock), // Lock
|
|
0 // Reserved
|
|
);
|
|
|
|
//
|
|
// Allocate and initialize the table and its header.
|
|
//
|
|
|
|
ID(TableHeader) = RtlAllocateHeap(
|
|
ID(PortMemoryHeap), 0,
|
|
sizeof( SMBTRACE_TABLE_HEADER )
|
|
);
|
|
|
|
ID(Table) = RtlAllocateHeap(
|
|
ID(PortMemoryHeap), 0,
|
|
sizeof( SMBTRACE_TABLE_ENTRY ) * ID(TableSize)
|
|
);
|
|
|
|
if ( (ID(TableHeader) == NULL) || (ID(Table) == NULL) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: Not enough memory!\n",
|
|
ID(ComponentName) ));
|
|
|
|
status = STATUS_NO_MEMORY;
|
|
|
|
goto errexit;
|
|
}
|
|
|
|
// Initialize the values inside.
|
|
|
|
ID(TableHeader)->HighestConsumed = 0;
|
|
ID(TableHeader)->NextFree = 1;
|
|
ID(TableHeader)->ApplicationStop = FALSE;
|
|
|
|
for ( i = 0; i < ID(TableSize); i++) {
|
|
ID(Table)[i].BufferOffset = 0L;
|
|
ID(Table)[i].SmbLength = 0L;
|
|
}
|
|
|
|
//
|
|
// Create the required event handles.
|
|
//
|
|
|
|
// Define the object information.
|
|
|
|
RtlInitUnicodeString( &eventNameU, ID(NewSmbEventName) );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&eventNameU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
&securityDescriptor
|
|
);
|
|
|
|
// Open the named object.
|
|
|
|
status = ZwCreateEvent(
|
|
&ID(NewSmbEvent),
|
|
EVENT_ALL_ACCESS,
|
|
&objectAttributes,
|
|
NotificationEvent,
|
|
FALSE // initial state
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint(( "%s!SmbTraceStart: ZwCreateEvent (1st) failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
|
|
goto errexit;
|
|
}
|
|
|
|
if ( ID(SingleSmbMode) ) { // this event may not be required.
|
|
|
|
// Define the object information.
|
|
|
|
RtlInitUnicodeString( &eventNameU, ID(DoneSmbEventName) );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&eventNameU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
&securityDescriptor
|
|
);
|
|
|
|
// Create the named object.
|
|
|
|
status = ZwCreateEvent(
|
|
&ID(DoneSmbEvent),
|
|
EVENT_ALL_ACCESS,
|
|
&objectAttributes,
|
|
NotificationEvent,
|
|
FALSE // initial state
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceStart: NtCreateEvent (2nd) failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
goto errexit;
|
|
}
|
|
TrPrint(( "%s!SmbTraceStart: DoneSmbEvent handle %x in process %x\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
|
|
}
|
|
|
|
//
|
|
// Reset any events that may be in the wrong state from a previous run.
|
|
//
|
|
|
|
KeResetEvent(&ID(TerminationEvent));
|
|
KeResetEvent(&ID(TerminatedEvent));
|
|
|
|
//
|
|
// Connection was successful, now start the SmbTrace thread.
|
|
//
|
|
|
|
//
|
|
// Create the SmbTrace thread and wait for it to finish
|
|
// initializing (at which point SmbTraceActiveEvent is set)
|
|
//
|
|
|
|
status = PsCreateSystemThread(
|
|
&threadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
NtCurrentProcess(),
|
|
NULL,
|
|
(PKSTART_ROUTINE) SmbTraceThreadEntry,
|
|
(PVOID)Component
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
TrPrint((
|
|
"%s!SmbTraceStart: PsCreateSystemThread failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
|
|
goto errexit;
|
|
}
|
|
|
|
//
|
|
// Wait until SmbTraceThreadEntry has finished initializing
|
|
//
|
|
|
|
(VOID)KeWaitForSingleObject(
|
|
&ID(ActiveEvent),
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Close the handle to the process so the object will be
|
|
// destroyed when the thread dies.
|
|
//
|
|
|
|
ZwClose( threadHandle );
|
|
|
|
|
|
//
|
|
// Record who started SmbTrace so we can stop if he dies or otherwise
|
|
// closes this handle to us.
|
|
//
|
|
|
|
ID(StartersFileObject) = FileObject;
|
|
|
|
//
|
|
// Record caller's process; which is always the appropriate Fsp
|
|
// process.
|
|
//
|
|
|
|
ID(FspProcess) = PsGetCurrentProcess();
|
|
|
|
|
|
//
|
|
// Setup the response packet, since everything worked (write all values).
|
|
//
|
|
|
|
ConfigPacketResp->HeaderOffset = (ULONG)
|
|
( (ULONG_PTR)ID(TableHeader)
|
|
- (ULONG_PTR)ID(PortMemoryBase) );
|
|
|
|
ConfigPacketResp->TableOffset = (ULONG)
|
|
( (ULONG_PTR)ID(Table)
|
|
- (ULONG_PTR)ID(PortMemoryBase) );
|
|
|
|
TrPrint(( "%s!SmbTraceStart: SmbTrace started.\n", ID(ComponentName) ));
|
|
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
|
|
//
|
|
// if someone wanted it shut down while it was starting, shut it down
|
|
//
|
|
|
|
switch ( ID(TraceState) ) {
|
|
|
|
case TraceStartStopFile :
|
|
SmbTraceStop( ID(StartersFileObject), Component );
|
|
return STATUS_UNSUCCESSFUL; // app closed, so we should shut down
|
|
break;
|
|
|
|
case TraceStartStopNull :
|
|
SmbTraceStop( NULL, Component );
|
|
return STATUS_UNSUCCESSFUL; // someone requested a shut down
|
|
break;
|
|
|
|
default :
|
|
ID(TraceState) = TraceRunning;
|
|
SmbTraceActive[Component] = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
errexit:
|
|
|
|
SmbTraceDisconnect( Component );
|
|
|
|
ID(TraceState) = TraceStopped;
|
|
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
|
|
//
|
|
// return original failure status code, not success of cleanup
|
|
//
|
|
|
|
return status;
|
|
|
|
} // SmbTraceStart
|
|
|
|
// constant only of interest while constructing the particular Acl
|
|
// in SmbTraceStart
|
|
#undef ACL_LENGTH
|
|
|
|
|
|
NTSTATUS
|
|
SmbTraceStop(
|
|
IN PFILE_OBJECT FileObject OPTIONAL,
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stops tracing in the server/redirector. If no
|
|
FileObject is provided, the SmbTrace application is stopped.
|
|
If a FileObject is provided, SmbTrace is stopped if the
|
|
FileObject refers to the one who started it.
|
|
|
|
Arguments:
|
|
|
|
FileObject - FileObject of a process that terminated. If it's the process
|
|
that requested SmbTracing, we shut down automatically.
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation. Possible results are:
|
|
STATUS_SUCCESS - SmbTrace was stopped
|
|
STATUS_UNSUCCESSFUL - SmbTrace was not stopped because the
|
|
provided FileObject did not refer to the SmbTrace starter
|
|
or because SmbTrace was not running.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If we haven't been initialized, there's nothing to stop. (And no
|
|
// resource to acquire!)
|
|
//
|
|
|
|
if ( !ID(InstanceInitialized) ) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// If it's not the FileObject that started SmbTrace, we don't care.
|
|
// From then on, if ARGUMENT_PRESENT(FileObject) it's the right one.
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(FileObject) &&
|
|
FileObject != ID(StartersFileObject)
|
|
) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite( ID(StateInterlock), TRUE );
|
|
|
|
//
|
|
// Depending on the current state of SmbTrace and whether this is
|
|
// a FileObject or unconditional shutdown request, we do different
|
|
// things. It is always clear at this point, though, that
|
|
// SmbTraceActive should be set to FALSE.
|
|
//
|
|
|
|
SmbTraceActive[Component] = FALSE;
|
|
|
|
switch ( ID(TraceState) ) {
|
|
case TraceStopped :
|
|
case TraceStopping :
|
|
case TraceStartStopFile :
|
|
case TraceStartStopNull :
|
|
|
|
// if we're not running or already in a mode where we know we'll
|
|
// soon be shut down, ignore the request.
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
return STATUS_UNSUCCESSFUL;
|
|
break;
|
|
|
|
case TraceStarting :
|
|
|
|
// inform starting SmbTrace that it should shut down immediately
|
|
// upon finishing initialization. It needs to know whether this
|
|
// is a FileObject or unconditional shutdown request.
|
|
|
|
ID(TraceState) = ARGUMENT_PRESENT(FileObject)
|
|
? TraceStartStopFile
|
|
: TraceStartStopNull;
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
return STATUS_SUCCESS;
|
|
break;
|
|
|
|
case TraceAppWaiting :
|
|
|
|
// we're waiting for the application to die already, so ignore
|
|
// new unconditional requests. But FileObject requests are
|
|
// welcomed. We cause the SmbTrace thread to kill itself.
|
|
if ( ARGUMENT_PRESENT(FileObject) ) {
|
|
break; // thread kill code follows switch
|
|
} else {
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case TraceRunning :
|
|
|
|
// if it's a FileObject request, the app is dead, so we cause
|
|
// the SmbTrace thread to kill itself. Otherwise, we need to
|
|
// signal the app to stop and return. When the app is gone, we
|
|
// will be called again; this time with a FileObject.
|
|
|
|
if ( ARGUMENT_PRESENT(FileObject) ) {
|
|
break; // thread kill code follows switch
|
|
} else {
|
|
KeSetEvent( &ID(AppTerminationEvent), 2, FALSE );
|
|
ID(TraceState) = TraceAppWaiting;
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
default :
|
|
ASSERT(!"SmbTraceStop: invalid TraceState");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We reach here from within the switch only in the case where
|
|
// we actually want to kill the SmbTrace thread. Signal it to
|
|
// wake up, and wait until it terminates. Signal DoneSmbEvent
|
|
// in case it is currently waiting for the application to signal
|
|
// it in slow mode.
|
|
//
|
|
|
|
ID(StartersFileObject) = NULL;
|
|
|
|
if ( ID(SingleSmbMode)) {
|
|
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
|
|
if (PsGetCurrentProcess() != ID(FspProcess)) {
|
|
KeAttachProcess(ID(FspProcess));
|
|
ProcessAttached = TRUE;
|
|
}
|
|
|
|
TrPrint(( "%s!SmbTraceStop: Signal DoneSmbEvent, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
ZwSetEvent( ID(DoneSmbEvent), NULL );
|
|
|
|
if (ProcessAttached) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
}
|
|
|
|
TrPrint(( "%s!SmbTraceStop: Signal Termination Event.\n", ID(ComponentName) ));
|
|
ID(TraceState) = TraceStopping;
|
|
KeSetEvent( &ID(TerminationEvent), 2, FALSE );
|
|
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
|
|
KeWaitForSingleObject(
|
|
&ID(TerminatedEvent),
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
TrPrint(( "%s!SmbTraceStop: Terminated Event is set.\n", ID(ComponentName) ));
|
|
ExAcquireResourceExclusiveLite( ID(StateInterlock), TRUE );
|
|
|
|
ID(TraceState) = TraceStopped;
|
|
|
|
ExReleaseResourceLite( ID(StateInterlock) );
|
|
|
|
TrPrint(( "%s!SmbTraceStop: SmbTrace stopped.\n", ID(ComponentName) ));
|
|
|
|
MmUnlockPagableImageSection(SmbTraceDiscardableCodeHandle);
|
|
|
|
SmbTraceDiscardableCodeHandle = NULL;
|
|
|
|
MmUnlockPagableImageSection(SmbTraceDiscardableDataHandle);
|
|
|
|
SmbTraceDiscardableDataHandle = NULL;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SmbTraceStop
|
|
|
|
|
|
VOID
|
|
SmbTraceCompleteSrv (
|
|
IN PMDL SmbMdl,
|
|
IN PVOID Smb,
|
|
IN CLONG SmbLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Server version.
|
|
|
|
Snapshot an SMB and export it to the SmbTrace application. How
|
|
this happens is determined by which mode (fast or slow) SmbTracing
|
|
was requested in. In the server, it is easy to guarantee that when
|
|
tracing, a thread is always executing in the Fsp.
|
|
|
|
Fast mode: the SMB is copied into shared memory and an entry for it
|
|
is queued to the server SmbTrace thread, which asynchronously
|
|
passes SMBs to the app. If there is insufficient memory
|
|
for anything (SMB, queue entry, etc.) the SMB is lost.
|
|
|
|
Slow mode: identical to Fast mode except that this thread waits
|
|
until the server SmbTrace thread signals that the app has finished
|
|
processing the SMB. Because each thread waits until its SMB has
|
|
been completely processed, there is much less chance of running
|
|
out of any resources.
|
|
|
|
The SMB is either contained in SmbMdl, or at address Smb with length
|
|
SmbLength.
|
|
|
|
Arguments:
|
|
|
|
SmbMdl - an Mdl containing the SMB.
|
|
|
|
Smb - a pointer to the SMB.
|
|
|
|
SmbLength - the length of the SMB.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMBTRACE_QUEUE_ENTRY queueEntry;
|
|
PVOID buffer;
|
|
SMBTRACE_COMPONENT Component = SMBTRACE_SERVER;
|
|
KEVENT WaitEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// This routine is server specific.
|
|
//
|
|
|
|
ASSERT( ID(TraceState) == TraceRunning );
|
|
ASSERT( SmbTraceActive[SMBTRACE_SERVER] );
|
|
|
|
//
|
|
// We want either an Mdl, or a pointer and a length, or occasionally,
|
|
// a completely NULL response.
|
|
//
|
|
|
|
ASSERT( ( SmbMdl == NULL && Smb != NULL && SmbLength != 0 )
|
|
|| ( SmbMdl != NULL && Smb == NULL && SmbLength == 0 )
|
|
|| ( SmbMdl == NULL && Smb == NULL && SmbLength == 0 ) );
|
|
|
|
//
|
|
// We've taken pains not to be at DPC level and to be in
|
|
// the Fsp context too, for that matter.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
ASSERT( PsGetCurrentProcess() == ID(FspProcess) );
|
|
|
|
//
|
|
// Ensure that SmbTrace really is still active and hence, the
|
|
// shared memory is still around.
|
|
//
|
|
|
|
if ( SmbTraceReferenceHeap( Component ) == FALSE ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the SMB is currently in an MDL, we don't yet have the length,
|
|
// which we need, to know how much memory to allocate.
|
|
//
|
|
|
|
if ( SmbMdl != NULL ) {
|
|
SmbLength = SmbTraceMdlLength(SmbMdl);
|
|
}
|
|
|
|
//
|
|
// If we are in slow mode, then we wait after queuing the SMB
|
|
// to the SmbTrace thread. If we are set for fast mode we
|
|
// garbage collect in case of no memory.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) ) {
|
|
KeInitializeEvent( &WaitEvent, NotificationEvent, FALSE );
|
|
}
|
|
|
|
queueEntry = ExAllocatePoolWithTag( NonPagedPool,
|
|
sizeof(SMBTRACE_QUEUE_ENTRY),
|
|
'tbmS'
|
|
);
|
|
|
|
if ( queueEntry == NULL ) {
|
|
// No free memory, this SMB is lost. Record its loss.
|
|
LOCK_INC_ID(SmbsLost);
|
|
SmbTraceDereferenceHeap( Component );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate the required amount of memory in our heap
|
|
// in the shared memory.
|
|
//
|
|
|
|
buffer = RtlAllocateHeap( ID(PortMemoryHeap), 0, SmbLength );
|
|
|
|
if ( buffer == NULL ) {
|
|
// No free memory, this SMB is lost. Record its loss.
|
|
// Very unlikely in slow mode.
|
|
LOCK_INC_ID(SmbsLost);
|
|
ExFreePool( queueEntry );
|
|
|
|
if ( !ID(SingleSmbMode) ) {
|
|
//
|
|
// Encourage some garbage collection.
|
|
//
|
|
KeSetEvent( &ID(NeedMemoryEvent), 0, FALSE );
|
|
}
|
|
|
|
SmbTraceDereferenceHeap( Component );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy the SMB to shared memory pointed to by the queue entry,
|
|
// keeping in mind whether it's in an Mdl or contiguous to begin
|
|
// with, and also preserving the address of the real SMB...
|
|
//
|
|
|
|
if ( SmbMdl != NULL ) {
|
|
SmbTraceCopyMdlContiguous( buffer, SmbMdl, SmbLength );
|
|
queueEntry->SmbAddress = SmbMdl;
|
|
} else {
|
|
RtlCopyMemory( buffer, Smb, SmbLength );
|
|
queueEntry->SmbAddress = Smb;
|
|
}
|
|
|
|
queueEntry->SmbLength = SmbLength;
|
|
queueEntry->Buffer = buffer;
|
|
queueEntry->BufferNonPaged = FALSE;
|
|
|
|
//
|
|
// In slow mode, we want to wait until the SMB has been eaten,
|
|
// in fast mode, we don't want to pass the address of the real
|
|
// SMB along, since the SMB is long gone by the time it gets
|
|
// decoded and printed.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) ) {
|
|
queueEntry->WaitEvent = &WaitEvent;
|
|
} else {
|
|
queueEntry->WaitEvent = NULL;
|
|
queueEntry->SmbAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// ...queue the entry to the SmbTrace thread...
|
|
//
|
|
|
|
ExInterlockedInsertTailList(
|
|
&ID(Queue),
|
|
&queueEntry->ListEntry,
|
|
&ID(QueueInterlock)
|
|
);
|
|
|
|
KeReleaseSemaphore(
|
|
&ID(QueueSemaphore),
|
|
SEMAPHORE_INCREMENT,
|
|
1,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// ...and wait for the SMB to be eaten, in slow mode.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) ) {
|
|
TrPrint(( "%s!SmbTraceCompleteSrv: Slow mode wait\n", ID(ComponentName) ));
|
|
KeWaitForSingleObject(
|
|
&WaitEvent,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
TrPrint(( "%s!SmbTraceCompleteSrv: Slow mode wait done\n", ID(ComponentName) ));
|
|
}
|
|
|
|
SmbTraceDereferenceHeap( Component );
|
|
|
|
return;
|
|
|
|
} // SmbTraceCompleteSrv
|
|
|
|
|
|
VOID
|
|
SmbTraceCompleteRdr (
|
|
IN PMDL SmbMdl,
|
|
IN PVOID Smb,
|
|
IN CLONG SmbLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Redirector version
|
|
|
|
Snapshot an SMB and export it to the SmbTrace application. How
|
|
this happens is determined by which mode (fast or slow) SmbTracing
|
|
was requested in, and which context (DPC, Fsp or Fsd) the current
|
|
thread is executing in.
|
|
|
|
Fast mode: the SMB is copied into shared memory and an entry for it
|
|
is queued to the redirector SmbTrace thread, which asynchronously
|
|
passes SMBs to the app. (When in DPC, the SMB is copied to non-paged
|
|
pool instead of shared memory, and the SmbTrace thread deals with
|
|
moving it to shared memory later.) If there is insufficient memory
|
|
for anything (SMB, queue entry, etc.) the SMB is lost.
|
|
|
|
Slow mode: identical to Fast mode except that this thread waits
|
|
until the server SmbTrace thread signals that the app has finished
|
|
processing the SMB. Because each thread waits until its SMB has
|
|
been completely processed, there is much less chance of running
|
|
out of any resources. If at DPC level, we behave exactly as in the
|
|
fast mode case, because it would be a Bad Thing to block this thread
|
|
at DPC level.
|
|
|
|
The SMB is either contained in SmbMdl, or at address Smb with length
|
|
SmbLength.
|
|
|
|
Arguments:
|
|
|
|
SmbMdl - an Mdl containing the SMB.
|
|
|
|
Smb - a pointer to the SMB.
|
|
|
|
SmbLength - the length of the SMB.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMBTRACE_QUEUE_ENTRY queueEntry;
|
|
PVOID buffer;
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
BOOLEAN AtDpcLevel;
|
|
SMBTRACE_COMPONENT Component = SMBTRACE_REDIRECTOR;
|
|
KEVENT WaitEvent;
|
|
|
|
//
|
|
// This routine is redirector specific.
|
|
//
|
|
|
|
ASSERT( ID(TraceState) == TraceRunning );
|
|
ASSERT( SmbTraceActive[SMBTRACE_REDIRECTOR] );
|
|
|
|
//
|
|
// We want either an Mdl, or a pointer and a length, or occasionally,
|
|
// a completely NULL response
|
|
//
|
|
|
|
ASSERT( ( SmbMdl == NULL && Smb != NULL && SmbLength != 0 )
|
|
|| ( SmbMdl != NULL && Smb == NULL && SmbLength == 0 )
|
|
|| ( SmbMdl == NULL && Smb == NULL && SmbLength == 0 ) );
|
|
|
|
//
|
|
// Ensure that SmbTrace really is still active and hence, the
|
|
// shared memory is still around.
|
|
//
|
|
|
|
if ( SmbTraceReferenceHeap( Component ) == FALSE ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// To avoid multiple system calls, we find out once and for all.
|
|
//
|
|
|
|
AtDpcLevel = (BOOLEAN)(KeGetCurrentIrql() >= DISPATCH_LEVEL);
|
|
|
|
//
|
|
// If the SMB is currently in an MDL, we don't yet have the length,
|
|
// which we need to know how much memory to allocate.
|
|
//
|
|
|
|
if ( SmbMdl != NULL ) {
|
|
SmbLength = SmbTraceMdlLength(SmbMdl);
|
|
}
|
|
|
|
//
|
|
// If we are in slow mode, then we wait after queuing the SMB
|
|
// to the SmbTrace thread. If we are set for fast mode we
|
|
// garbage collect in case of no memory. If we're at DPC level,
|
|
// we store the SMB in non-paged pool.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) ) {
|
|
KeInitializeEvent( &WaitEvent, NotificationEvent, FALSE );
|
|
}
|
|
|
|
//
|
|
// allocate queue entry
|
|
//
|
|
|
|
queueEntry = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMBTRACE_QUEUE_ENTRY),
|
|
'tbmS'
|
|
);
|
|
|
|
if ( queueEntry == NULL ) {
|
|
// No free memory, this SMB is lost. Record its loss.
|
|
LOCK_INC_ID(SmbsLost);
|
|
SmbTraceDereferenceHeap( Component );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// allocate buffer for SMB, in non-paged pool or shared heap as
|
|
// appropriate
|
|
//
|
|
|
|
if ( AtDpcLevel ) {
|
|
|
|
buffer = ExAllocatePoolWithTag( NonPagedPool, SmbLength, 'tbmS' );
|
|
queueEntry->BufferNonPaged = TRUE;
|
|
|
|
} else {
|
|
|
|
if ( PsGetCurrentProcess() != ID(FspProcess) ) {
|
|
KeAttachProcess(ID(FspProcess));
|
|
ProcessAttached = TRUE;
|
|
}
|
|
|
|
buffer = RtlAllocateHeap( ID(PortMemoryHeap), 0, SmbLength );
|
|
queueEntry->BufferNonPaged = FALSE;
|
|
|
|
}
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
if ( ProcessAttached ) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
// No free memory, this SMB is lost. Record its loss.
|
|
LOCK_INC_ID(SmbsLost);
|
|
|
|
if (!ID(SingleSmbMode)) {
|
|
|
|
//
|
|
// If it was shared memory we ran out of, encourage
|
|
// some garbage collection.
|
|
//
|
|
if ( !queueEntry->BufferNonPaged ) {
|
|
KeSetEvent( &ID(NeedMemoryEvent), 0, FALSE );
|
|
}
|
|
}
|
|
|
|
ExFreePool( queueEntry );
|
|
SmbTraceDereferenceHeap( Component );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy the SMB to shared or non-paged memory pointed to by the
|
|
// queue entry, keeping in mind whether it's in an Mdl or contiguous
|
|
// to begin with, and also preserving the address of the real SMB...
|
|
//
|
|
|
|
if ( SmbMdl != NULL ) {
|
|
SmbTraceCopyMdlContiguous( buffer, SmbMdl, SmbLength );
|
|
queueEntry->SmbAddress = SmbMdl;
|
|
} else {
|
|
RtlCopyMemory( buffer, Smb, SmbLength );
|
|
queueEntry->SmbAddress = Smb;
|
|
}
|
|
|
|
if ( ProcessAttached ) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
queueEntry->SmbLength = SmbLength;
|
|
queueEntry->Buffer = buffer;
|
|
|
|
//
|
|
// In slow mode, we want to wait until the SMB has been eaten,
|
|
// in fast mode, we don't want to pass the address of the real
|
|
// SMB along, since the SMB is long gone by the time it gets
|
|
// decoded and printed.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) && !AtDpcLevel ) {
|
|
queueEntry->WaitEvent = &WaitEvent;
|
|
} else {
|
|
queueEntry->WaitEvent = NULL;
|
|
queueEntry->SmbAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// ...queue the entry to the SmbTrace thread...
|
|
//
|
|
|
|
ExInterlockedInsertTailList(
|
|
&ID(Queue),
|
|
&queueEntry->ListEntry,
|
|
&ID(QueueInterlock)
|
|
);
|
|
|
|
KeReleaseSemaphore(
|
|
&ID(QueueSemaphore),
|
|
SEMAPHORE_INCREMENT,
|
|
1,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// ...and wait for the SMB to be eaten, in slow mode.
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) && !AtDpcLevel ) {
|
|
TrPrint(( "%s!SmbTraceCompleteRdr: Slow mode wait\n", ID(ComponentName) ));
|
|
KeWaitForSingleObject(
|
|
&WaitEvent,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
TrPrint(( "%s!SmbTraceCompleteRdr: Slow mode wait done\n", ID(ComponentName) ));
|
|
}
|
|
|
|
SmbTraceDereferenceHeap( Component );
|
|
|
|
return;
|
|
|
|
} // SmbTraceCompleteRdr
|
|
|
|
|
|
//
|
|
// Internal routines
|
|
//
|
|
|
|
|
|
BOOLEAN
|
|
SmbTraceReferenceHeap(
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references the SmbTrace shared memory heap,
|
|
ensuring it isn't disposed of while caller is using it.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if SmbTrace is still active, and hence
|
|
heap exists and was successfully referenced.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN retval = TRUE; // assume we'll get it
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK( &ID(HeapReferenceCountLock), &OldIrql );
|
|
|
|
if ( ID(TraceState) != TraceRunning ) {
|
|
retval = FALSE;
|
|
} else {
|
|
ASSERT( ID(HeapReferenceCount) > 0 );
|
|
if( ID(HeapReferenceCount) > 0 )
|
|
{
|
|
ID(HeapReferenceCount)++;
|
|
TrPrint(( "%s!SmbTraceReferenceHeap: Count now %lx\n",
|
|
ID(ComponentName),
|
|
ID(HeapReferenceCount) ));
|
|
}
|
|
else
|
|
{
|
|
retval = FALSE;
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK( &ID(HeapReferenceCountLock), OldIrql );
|
|
|
|
return retval;
|
|
|
|
} // SmbTraceReferenceHeap
|
|
|
|
|
|
VOID
|
|
SmbTraceDeferredDereferenceHeap(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If a caller dereferences a heap to 0 from DPC_LEVEL, this routine will
|
|
be called in a system thread to complete the dereference at task time.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
SmbTraceDereferenceHeap((SMBTRACE_COMPONENT)Context);
|
|
}
|
|
|
|
|
|
VOID
|
|
SmbTraceDereferenceHeap(
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences the SmbTrace shared memory heap,
|
|
disposing of it when the reference count is zero.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG oldCount;
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK( &ID(HeapReferenceCountLock), &OldIrql );
|
|
|
|
if (ID(HeapReferenceCount) > 1) {
|
|
ID(HeapReferenceCount) --;
|
|
|
|
TrPrint(( "%s!SmbTraceDereferenceHeap: Count now %lx\n",
|
|
ID(ComponentName),
|
|
ID(HeapReferenceCount) ));
|
|
|
|
RELEASE_SPIN_LOCK( &ID(HeapReferenceCountLock), OldIrql );
|
|
|
|
return;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK( &ID(HeapReferenceCountLock), OldIrql );
|
|
|
|
//
|
|
// If we are executing at DPC_LEVEL, we cannot dereference the heap
|
|
// to 0.
|
|
//
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
|
|
ExInitializeWorkItem(&ID(DereferenceWorkQueueItem), SmbTraceDeferredDereferenceHeap, (PVOID)Component);
|
|
|
|
ExQueueWorkItem(&ID(DereferenceWorkQueueItem), DelayedWorkQueue);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK( &ID(HeapReferenceCountLock), &OldIrql );
|
|
|
|
oldCount = ID(HeapReferenceCount)--;
|
|
|
|
TrPrint(( "%s!SmbTraceDereferenceHeap: Count now %lx\n",
|
|
ID(ComponentName),
|
|
ID(HeapReferenceCount) ));
|
|
|
|
RELEASE_SPIN_LOCK( &ID(HeapReferenceCountLock), OldIrql );
|
|
|
|
if ( oldCount == 1 ) {
|
|
|
|
//
|
|
// Free the section, release the handles and such.
|
|
//
|
|
|
|
SmbTraceDisconnect( Component );
|
|
}
|
|
|
|
return;
|
|
|
|
} // SmbTraceDereferenceHeap
|
|
|
|
|
|
VOID
|
|
SmbTraceDisconnect (
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reverses all the effects of SmbTraceStart. Mostly,
|
|
it just needs to close certain handles to do this.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None - always works
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PsGetCurrentProcess() != ID(FspProcess)) {
|
|
KeAttachProcess(ID(FspProcess));
|
|
ProcessAttached = TRUE;
|
|
|
|
}
|
|
|
|
|
|
if ( ID(DoneSmbEvent) != NULL ) {
|
|
// Worker thread may be blocked on this, so we set it first
|
|
TrPrint(( "%s!SmbTraceDisconnect: Signal DoneSmbEvent, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
ZwSetEvent( ID(DoneSmbEvent), NULL );
|
|
|
|
TrPrint(( "%s!SmbTraceDisconnect: Close DoneSmbEvent, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
ZwClose( ID(DoneSmbEvent) );
|
|
ID(DoneSmbEvent) = NULL;
|
|
}
|
|
|
|
if ( ID(NewSmbEvent) != NULL ) {
|
|
ZwClose( ID(NewSmbEvent) );
|
|
ID(NewSmbEvent) = NULL;
|
|
}
|
|
|
|
if ( ID(PortMemoryHeap) != NULL ) {
|
|
RtlDestroyHeap( ID(PortMemoryHeap) );
|
|
ID(PortMemoryHeap) = NULL;
|
|
}
|
|
|
|
if ( ID(SectionHandle) != NULL ) {
|
|
ZwClose( ID(SectionHandle) );
|
|
ID(SectionHandle) = NULL;
|
|
}
|
|
|
|
if (ProcessAttached) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
return;
|
|
|
|
} // SmbTraceDisconnect
|
|
|
|
|
|
VOID
|
|
SmbTraceEmptyQueue (
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine empties the queue of unprocessed SMBs.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None - always works
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
PSMBTRACE_QUEUE_ENTRY queueEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
while ( ( listEntry = ExInterlockedRemoveHeadList(
|
|
&ID(Queue),
|
|
&ID(QueueInterlock)
|
|
)
|
|
) != NULL
|
|
) {
|
|
queueEntry = CONTAINING_RECORD(
|
|
listEntry,
|
|
SMBTRACE_QUEUE_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// If data for this entry is in non-paged pool, free it too.
|
|
// This only ever happens in the redirector.
|
|
//
|
|
|
|
if ( queueEntry->BufferNonPaged ) {
|
|
|
|
ASSERT( Component == SMBTRACE_REDIRECTOR );
|
|
|
|
ExFreePool( queueEntry->Buffer );
|
|
}
|
|
|
|
//
|
|
// If a worker thread is waiting on this event, let it go.
|
|
// This only ever happens in slow mode.
|
|
//
|
|
|
|
if ( queueEntry->WaitEvent != NULL ) {
|
|
|
|
ASSERT( ID(SingleSmbMode) == TRUE );
|
|
|
|
KeSetEvent( queueEntry->WaitEvent, 0, FALSE );
|
|
}
|
|
|
|
ExFreePool( queueEntry );
|
|
}
|
|
|
|
return;
|
|
|
|
} // SmbTraceEmptyQueue
|
|
|
|
|
|
VOID
|
|
SmbTraceThreadEntry (
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the entry point of the SmbTrace thread for the server/
|
|
redirector. It is started by SmbTraceStart. This thread loops
|
|
continuously until the client SmbTrace dies or another SmbTrace sends
|
|
an FsCtl to stop the trace.
|
|
|
|
Arguments:
|
|
|
|
Context - pointer to context block containing component from which
|
|
we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
// we wait for termination, work-to-do and need-memory events
|
|
#define NUMBER_OF_BLOCKING_OBJECTS 4
|
|
|
|
// keep these definitions in sync
|
|
|
|
#define INDEX_WAIT_TERMINATIONEVENT 0
|
|
#define INDEX_WAIT_APPTERMINATIONEVENT 1
|
|
#define INDEX_WAIT_NEEDMEMORYEVENT 2
|
|
#define INDEX_WAIT_QUEUESEMAPHORE 3
|
|
|
|
#define STATUS_WAIT_TERMINATIONEVENT STATUS_WAIT_0
|
|
#define STATUS_WAIT_APPTERMINATIONEVENT STATUS_WAIT_1
|
|
#define STATUS_WAIT_NEEDMEMORYEVENT STATUS_WAIT_2
|
|
#define STATUS_WAIT_QUEUESEMAPHORE STATUS_WAIT_3
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PLIST_ENTRY listEntry;
|
|
PSMBTRACE_QUEUE_ENTRY queueEntry;
|
|
PVOID buffer;
|
|
PVOID waitObjects[NUMBER_OF_BLOCKING_OBJECTS];
|
|
SMBTRACE_COMPONENT Component;
|
|
BOOLEAN Looping;
|
|
|
|
#if NUMBER_OF_BLOCKING_OBJECTS > THREAD_WAIT_OBJECTS
|
|
//
|
|
// If we try to wait on too many objects, we need to allocate
|
|
// our own wait blocks.
|
|
//
|
|
|
|
KWAIT_BLOCK waitBlocks[NUMBER_OF_BLOCKING_OBJECTS];
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Context is really just the component
|
|
//
|
|
Component = (SMBTRACE_COMPONENT)(UINT_PTR)Context;
|
|
|
|
//
|
|
// Initialize the queue.
|
|
//
|
|
|
|
InitializeListHead( &ID(Queue) );
|
|
KeInitializeSpinLock( &ID(QueueInterlock) );
|
|
KeInitializeSemaphore( &ID(QueueSemaphore), 0, 0x7FFFFFFF );
|
|
|
|
//
|
|
// Set up the array of objects to wait on. We wait (in order)
|
|
// for our termination event, the appliction termination event,
|
|
// a no shared memory event or an SMB request to show up in the
|
|
// SmbTrace queue.
|
|
//
|
|
|
|
waitObjects[INDEX_WAIT_TERMINATIONEVENT] = &ID(TerminationEvent);
|
|
waitObjects[INDEX_WAIT_APPTERMINATIONEVENT] = &ID(AppTerminationEvent);
|
|
waitObjects[INDEX_WAIT_NEEDMEMORYEVENT] = &ID(NeedMemoryEvent);
|
|
waitObjects[INDEX_WAIT_QUEUESEMAPHORE] = &ID(QueueSemaphore);
|
|
|
|
//
|
|
// No SMBs have been lost yet, and this thread is the first user
|
|
// of the shared memory. It's also a special user in that it gets
|
|
// access before TraceState == TraceRunning, a requirement for all
|
|
// subsequent referencers.
|
|
//
|
|
|
|
ID(SmbsLost) = 0L;
|
|
ID(HeapReferenceCount) = 1;
|
|
|
|
//
|
|
// Signal to the FSP that we are ready to start capturing SMBs.
|
|
//
|
|
|
|
KeSetEvent( &ID(ActiveEvent), 0, FALSE );
|
|
|
|
//
|
|
// Main loop, executed until the thread is terminated.
|
|
//
|
|
|
|
TrPrint(( "%s!SmbTraceThread: Tracing started.\n", ID(ComponentName) ));
|
|
|
|
Looping = TRUE;
|
|
while( Looping ) {
|
|
|
|
TrPrint(( "%s!SmbTraceThread: WaitForMultiple.\n", ID(ComponentName) ));
|
|
status = KeWaitForMultipleObjects(
|
|
NUMBER_OF_BLOCKING_OBJECTS,
|
|
&waitObjects[0],
|
|
WaitAny,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL,
|
|
#if NUMBER_OF_BLOCKING_OBJECTS > THREAD_WAIT_OBJECTS
|
|
&waitBlocks[0]
|
|
#else
|
|
NULL
|
|
#endif
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
TrPrint((
|
|
"%s!SmbTraceThreadEntry: KeWaitForMultipleObjectsfailed: %X\n",
|
|
ID(ComponentName), status ));
|
|
} else {
|
|
TrPrint((
|
|
"%s!SmbTraceThreadEntry: %lx\n",
|
|
ID(ComponentName), status ));
|
|
}
|
|
|
|
switch( status ) {
|
|
|
|
case STATUS_WAIT_TERMINATIONEVENT:
|
|
|
|
//
|
|
// Stop looping, and then proceed to clean up and die.
|
|
//
|
|
|
|
Looping = FALSE;
|
|
break;
|
|
|
|
case STATUS_WAIT_APPTERMINATIONEVENT:
|
|
|
|
// Turn off the event so we don't go in a tight loop
|
|
KeResetEvent(&ID(AppTerminationEvent));
|
|
|
|
//
|
|
// Inform the app that it is time to die. The NULL SMB
|
|
// sent here may not be the next to be processed by the
|
|
// app, but the ApplicationStop bit will be detected
|
|
// immediately.
|
|
//
|
|
|
|
ID(TableHeader)->ApplicationStop = TRUE;
|
|
SmbTraceToClient( NULL, 0, NULL, Component );
|
|
|
|
break;
|
|
|
|
case STATUS_WAIT_NEEDMEMORYEVENT:
|
|
|
|
// Turn off the event so we don't go in a loop.
|
|
KeResetEvent(&ID(NeedMemoryEvent));
|
|
//
|
|
// Do a garbage collection, freeing all memory that is
|
|
// allocated in the shared memory but that has been read
|
|
// by the client.
|
|
//
|
|
|
|
SmbTraceFreeMemory( Component );
|
|
|
|
break;
|
|
|
|
case STATUS_WAIT_QUEUESEMAPHORE:
|
|
|
|
//
|
|
// If any get through once we've gone into AppWaiting
|
|
// state, don't bother sending them on, they're not
|
|
// going to get processed.
|
|
//
|
|
|
|
if ( ID(TraceState) == TraceAppWaiting ) {
|
|
SmbTraceEmptyQueue( Component );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remove the first element in the our queue. A
|
|
// work item is represented by our header followed by
|
|
// an SMB. We must free the entry after we are done
|
|
// with it.
|
|
//
|
|
|
|
listEntry = ExInterlockedRemoveHeadList(
|
|
&ID(Queue),
|
|
&ID(QueueInterlock)
|
|
);
|
|
|
|
if ( listEntry != NULL ) {
|
|
|
|
//
|
|
// Get the address of the queue entry.
|
|
//
|
|
|
|
queueEntry = CONTAINING_RECORD(
|
|
listEntry,
|
|
SMBTRACE_QUEUE_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// If the data is in non-paged pool, move it to shared
|
|
// memory and free the non-paged pool before passing
|
|
// the SMB to the client. Note that in this case,
|
|
// there's no need to signal anyone. They ain't waiting.
|
|
//
|
|
|
|
if ( queueEntry->BufferNonPaged ) {
|
|
|
|
//
|
|
// Server never uses non-paged pool.
|
|
//
|
|
|
|
ASSERT( Component != SMBTRACE_SERVER );
|
|
|
|
buffer = RtlAllocateHeap( ID(PortMemoryHeap), 0,
|
|
queueEntry->SmbLength );
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
LOCK_INC_ID(SmbsLost);
|
|
|
|
ExFreePool( queueEntry->Buffer );
|
|
ExFreePool( queueEntry );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
RtlCopyMemory( buffer, queueEntry->Buffer,
|
|
queueEntry->SmbLength );
|
|
|
|
ExFreePool( queueEntry->Buffer );
|
|
|
|
//
|
|
// Send it off. Because the original SMB is long
|
|
// dead, we don't pass its real address along (not
|
|
// that we have it, anyway.)
|
|
//
|
|
|
|
ASSERT( queueEntry->SmbAddress == NULL );
|
|
|
|
SmbTraceToClient(
|
|
buffer,
|
|
queueEntry->SmbLength,
|
|
NULL,
|
|
Component
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Enter the SMB into the table and send it to the
|
|
// client. Can block in slow mode. When it does so, we'll
|
|
// signal the applicable thread.
|
|
//
|
|
|
|
SmbTraceToClient(
|
|
queueEntry->Buffer,
|
|
queueEntry->SmbLength,
|
|
queueEntry->SmbAddress,
|
|
Component
|
|
);
|
|
|
|
if ( queueEntry->WaitEvent != NULL ) {
|
|
KeSetEvent( queueEntry->WaitEvent, 0, FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, we must free the queue entry.
|
|
//
|
|
|
|
ExFreePool( queueEntry );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Clean up!
|
|
//
|
|
TrPrint(( "%s!SmbTraceThread: Tracing clean up.\n", ID(ComponentName) ));
|
|
|
|
SmbTraceDereferenceHeap( Component );
|
|
|
|
SmbTraceEmptyQueue( Component );
|
|
|
|
//
|
|
// Signal to SmbTraceStop that we're dying.
|
|
//
|
|
|
|
TrPrint(( "%s!SmbTraceThread: Tracing terminated.\n", ID(ComponentName) ));
|
|
|
|
KeSetEvent( &ID(TerminatedEvent), 0, FALSE );
|
|
|
|
//
|
|
// Kill this thread.
|
|
//
|
|
|
|
status = PsTerminateSystemThread( STATUS_SUCCESS );
|
|
|
|
// Shouldn't get here
|
|
TrPrint((
|
|
"%s!SmbTraceThreadEntry: PsTerminateSystemThread() failed: %X\n",
|
|
ID(ComponentName), status ));
|
|
|
|
} // SmbTraceThreadEntry
|
|
|
|
// constant only of interest while constructing waitObject arrays
|
|
// in SmbTraceThreadEntry
|
|
#undef NUMBER_OF_BLOCKING_OBJECTS
|
|
|
|
|
|
NTSTATUS
|
|
SmbTraceFreeMemory (
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure frees any memory that may have been allocated to an
|
|
SMB that the client has already consumed. It does not alter table
|
|
entries, except to record that the memory buffer has been cleared.
|
|
This routinue is not espectally fast, it should not be called often,
|
|
only when needed.
|
|
|
|
Arguments:
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID buffer;
|
|
PSMBTRACE_TABLE_ENTRY tableEntry;
|
|
ULONG tableIndex;
|
|
|
|
PAGED_CODE();
|
|
|
|
TrPrint(( "%s!SmbTraceFreeMemory: Called for garbage collection.\n",
|
|
ID(ComponentName) ));
|
|
|
|
//
|
|
// No free memory in the heap, perhaps we can free some by freeing
|
|
// memory in old table entries. This is expensive for time.
|
|
//
|
|
|
|
tableIndex = ID(TableHeader)->NextFree;
|
|
|
|
while( tableIndex != ID(TableHeader)->HighestConsumed ) {
|
|
|
|
tableEntry = ID(Table) + tableIndex;
|
|
|
|
//
|
|
// Check if this table entry has been used but its memory has not
|
|
// been freed yet. If so, free it.
|
|
//
|
|
|
|
if ( tableEntry->BufferOffset != 0L ) {
|
|
|
|
buffer = (PVOID)( (ULONG_PTR)tableEntry->BufferOffset
|
|
+ (ULONG_PTR)ID(PortMemoryBase) );
|
|
|
|
RtlFreeHeap( ID(PortMemoryHeap), 0, buffer);
|
|
|
|
tableEntry->BufferOffset = 0L;
|
|
}
|
|
|
|
|
|
tableIndex = (tableIndex + 1) % ID(TableSize);
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
|
|
} // SmbTraceFreeMemory
|
|
|
|
|
|
VOID
|
|
SmbTraceToClient(
|
|
IN PVOID Smb,
|
|
IN CLONG SmbLength,
|
|
IN PVOID SmbAddress,
|
|
IN SMBTRACE_COMPONENT Component
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enter an SMB already found in shared memory into the table. Set
|
|
an event for the client. If there is no table space, the SMB is
|
|
not saved. If in slow mode, wait for the client to finish with
|
|
and then free the memory occupied by the SMB.
|
|
|
|
Arguments:
|
|
|
|
Smb - a pointer to the SMB (which is ALREADY in shared memory).
|
|
Can be NULL, indicating no new SMB is to be added, but the
|
|
application is to be signalled anyway.
|
|
|
|
SmbLength - the length of the SMB.
|
|
|
|
SmbAddress - the address of the real SMB, not in shared memory.
|
|
|
|
Component - Context from which we're called: server or redirector
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
PSMBTRACE_TABLE_ENTRY tableEntry;
|
|
ULONG tableIndex;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Reset DoneSmbEvent so we can determine when the request has been processed
|
|
//
|
|
|
|
if ( ID(SingleSmbMode) ) {
|
|
PKEVENT DoneEvent;
|
|
|
|
TrPrint(( "%s!SmbTraceToClient: Reset DoneSmbEvent, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
|
|
status = ObReferenceObjectByHandle( ID(DoneSmbEvent),
|
|
EVENT_MODIFY_STATE,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID *)&DoneEvent,
|
|
NULL
|
|
);
|
|
|
|
ASSERT ( NT_SUCCESS(status) );
|
|
|
|
KeResetEvent(DoneEvent);
|
|
|
|
ObDereferenceObject(DoneEvent);
|
|
}
|
|
|
|
if (Smb != NULL) {
|
|
|
|
//
|
|
// See if there is room in the table for a pointer to our SMB.
|
|
//
|
|
|
|
if ( ID(TableHeader)->NextFree == ID(TableHeader)->HighestConsumed ) {
|
|
// Tough luck. No memory in the table, this SMB is lost.
|
|
LOCK_INC_ID( SmbsLost );
|
|
RtlFreeHeap( ID(PortMemoryHeap), 0, Smb );
|
|
return;
|
|
}
|
|
|
|
tableIndex = ID(TableHeader)->NextFree;
|
|
|
|
tableEntry = ID(Table) + tableIndex;
|
|
|
|
//
|
|
// Record the number of SMBs that were lost before this one and
|
|
// (maybe) zero the count for the next one.
|
|
//
|
|
|
|
tableEntry->NumberMissed = ID(SmbsLost);
|
|
|
|
if ( tableEntry->NumberMissed != 0 ) {
|
|
LOCK_ZERO_ID(SmbsLost);
|
|
}
|
|
|
|
//
|
|
// Check if this table entry has been used but its memory has not
|
|
// been freed yet. If so, free it.
|
|
//
|
|
if ( tableEntry->BufferOffset != 0L ) {
|
|
|
|
buffer = (PVOID)( (ULONG_PTR)tableEntry->BufferOffset
|
|
+ (ULONG_PTR)ID(PortMemoryBase) );
|
|
|
|
RtlFreeHeap( ID(PortMemoryHeap), 0, buffer);
|
|
tableEntry->BufferOffset = 0L;
|
|
}
|
|
|
|
//
|
|
// Record the location and size of this SMB in the table.
|
|
//
|
|
|
|
tableEntry->BufferOffset = (ULONG)((ULONG_PTR)Smb - (ULONG_PTR)ID(PortMemoryBase));
|
|
tableEntry->SmbLength = SmbLength;
|
|
|
|
//
|
|
// Record the real address of the actual SMB (i.e. not the shared
|
|
// memory copy) if it's available.
|
|
//
|
|
|
|
tableEntry->SmbAddress = SmbAddress;
|
|
|
|
//
|
|
// Increment the Next Free counter.
|
|
//
|
|
|
|
ID(TableHeader)->NextFree = (tableIndex + 1) % ID(TableSize);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Unlock the client so it will process this new SMB.
|
|
//
|
|
|
|
TrPrint(( "%s!SmbTraceToClient: Set NewSmbEvent.\n", ID(ComponentName) ));
|
|
status = ZwSetEvent( ID(NewSmbEvent), NULL );
|
|
|
|
//
|
|
// When stopping the trace we set TraceState to TraceStopping and then
|
|
// DoneSmbEvent. This prevents this routine from blocking indefinitely
|
|
// because it Resets DoneSmbEvent processes the Smb and then checks TraceState
|
|
// before blocking.
|
|
//
|
|
if (( ID(SingleSmbMode) ) &&
|
|
( ID(TraceState) == TraceRunning )) {
|
|
|
|
//
|
|
// Wait for the app to acknowledge that the SMB has been
|
|
// processed.
|
|
//
|
|
|
|
TrPrint(( "%s!SmbTraceToClient: Waiting for DoneSmbEvent, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
status = ZwWaitForSingleObject(
|
|
ID(DoneSmbEvent),
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
TrPrint(( "%s!SmbTraceToClient: DoneSmbEvent is set, handle %x, process %x.\n",
|
|
ID(ComponentName), ID(DoneSmbEvent), PsGetCurrentProcess()));
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
if (Smb != NULL) {
|
|
|
|
tableEntry->BufferOffset = 0L;
|
|
RtlFreeHeap( ID(PortMemoryHeap), 0, Smb);
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // SmbTraceToClient
|
|
|
|
|
|
ULONG
|
|
SmbTraceMdlLength(
|
|
IN PMDL Mdl
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the total number of bytes of data found in an Mdl.
|
|
|
|
Arguments:
|
|
|
|
Mdl - a pointer to an Mdl whose length is to be calculated
|
|
|
|
Return Value:
|
|
|
|
ULONG - total number of data bytes in Mdl
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Bytes = 0;
|
|
|
|
while (Mdl != NULL) {
|
|
Bytes += MmGetMdlByteCount(Mdl);
|
|
Mdl = Mdl->Next;
|
|
}
|
|
|
|
return Bytes;
|
|
} // SmbTraceMdlLength
|
|
|
|
|
|
VOID
|
|
SmbTraceCopyMdlContiguous(
|
|
OUT PVOID Destination,
|
|
IN PMDL Mdl,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy the data stored in Mdl into the contiguous memory at
|
|
Destination. Length is present to keep the same interface
|
|
as RtlCopyMemory.
|
|
|
|
Arguments:
|
|
|
|
Destination - a pointer to previously allocated memory into which
|
|
the Mdl is to be copied.
|
|
|
|
Mdl - a pointer to an Mdl which is to be copied to Destination
|
|
|
|
Length - number of data bytes expected in Mdl
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR Dest = Destination;
|
|
PCHAR Src = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(Length);
|
|
|
|
while (Mdl != NULL) {
|
|
|
|
Src = MmGetSystemAddressForMdlSafe(Mdl,NormalPagePriority);
|
|
|
|
if (Src != NULL) {
|
|
RtlCopyMemory(
|
|
Dest,
|
|
Src,
|
|
MmGetMdlByteCount(Mdl)
|
|
);
|
|
}
|
|
|
|
Dest += MmGetMdlByteCount(Mdl);
|
|
Mdl = Mdl->Next;
|
|
}
|
|
|
|
ASSERT((ULONG)(Dest - (PCHAR)Destination) == Length);
|
|
|
|
return;
|
|
|
|
} // SmbTraceCopyMdlContiguous
|
|
|