代码拉取完成,页面将自动刷新
同步操作将从 咆哮猫/dbgk 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.
Module Name:
dbgkobj.c
Abstract:
This module houses routines to handle the debug object
--*/
#include "dbgkp.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DbgkInitialize)
#pragma alloc_text(PAGE, NtCreateDebugObject)
#pragma alloc_text(PAGE, NtDebugActiveProcess)
#pragma alloc_text(PAGE, NtRemoveProcessDebug)
#pragma alloc_text(PAGE, NtWaitForDebugEvent)
#pragma alloc_text(PAGE, NtDebugContinue)
#pragma alloc_text(PAGE, NtSetInformationDebugObject)
#pragma alloc_text(PAGE, DbgkpDeleteObject)
#pragma alloc_text(PAGE, DbgkpCloseObject)
#pragma alloc_text(PAGE, DbgkCopyProcessDebugPort)
#pragma alloc_text(PAGE, DbgkOpenProcessDebugPort)
#pragma alloc_text(PAGE, DbgkpSetProcessDebugObject)
#pragma alloc_text(PAGE, DbgkpQueueMessage)
#pragma alloc_text(PAGE, DbgkpOpenHandles)
#pragma alloc_text(PAGE, DbgkClearProcessDebugObject)
#pragma alloc_text(PAGE, DbgkpConvertKernelToUserStateChange)
#pragma alloc_text(PAGE, DbgkpMarkProcessPeb)
#pragma alloc_text(PAGE, DbgkpFreeDebugEvent)
#pragma alloc_text(PAGE, DbgkpPostFakeProcessCreateMessages)
#pragma alloc_text(PAGE, DbgkpPostFakeModuleMessages)
#pragma alloc_text(PAGE, DbgkpPostFakeThreadMessages)
#pragma alloc_text(PAGE, DbgkpWakeTarget)
#pragma alloc_text(PAGE, DbgkpPostAdditionalThreadMessages)
#endif
//
// Non-pageable data
//
//
// This mutex protects the debug port object of processes.
//
FAST_MUTEX DbgkpProcessDebugPortMutex;
//
// Pageable data
//
//#ifdef ALLOC_PRAGMA
//#pragma data_seg("PAGEDATA")
//#endif
POBJECT_TYPE DbgkDebugObjectType = NULL;
//#ifdef ALLOC_PRAGMA
//#pragma data_seg()
//#endif
NTSTATUS
DbgkInitialize (
VOID
)
/*++
Routine Description:
Initialize the debug system
Arguments:
None
Return Value:
NTSTATUS - Status of operation
--*/
{
NTSTATUS Status;
UNICODE_STRING Name;
OBJECT_TYPE_INITIALIZER oti = {0};
GENERIC_MAPPING GenericMapping = {STANDARD_RIGHTS_READ | DEBUG_READ_EVENT,
STANDARD_RIGHTS_WRITE | DEBUG_PROCESS_ASSIGN,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
DEBUG_ALL_ACCESS};
PAGED_CODE ();
ExInitializeFastMutex (&DbgkpProcessDebugPortMutex);
RtlInitUnicodeString (&Name, L"DebugObject");
oti.Length = sizeof (oti);
oti.SecurityRequired = TRUE;
oti.InvalidAttributes = 0;
oti.PoolType = NonPagedPool;
oti.DeleteProcedure = DbgkpDeleteObject;
oti.CloseProcedure = DbgkpCloseObject;
oti.ValidAccessMask = DEBUG_ALL_ACCESS;
oti.GenericMapping = GenericMapping;
oti.DefaultPagedPoolCharge = 0;
oti.DefaultNonPagedPoolCharge = 0;
Status = ObCreateObjectType (&Name, &oti, NULL, &DbgkDebugObjectType);
if (!NT_SUCCESS (Status)) {
return Status;
}
return Status;
}
VOID
DbgkpDeleteObject (
IN PVOID Object
)
/*++
Routine Description:
Called by the object manager when the last reference to the object goes away.
Arguments:
Object - Debug object being deleted
Return Value:
None.
--*/
{
#if DBG
PDEBUG_OBJECT DebugObject;
#endif
PAGED_CODE();
#if DBG
DebugObject = Object;
ASSERT (IsListEmpty (&DebugObject->EventList));
#else
UNREFERENCED_PARAMETER(Object);
#endif
}
VOID
DbgkpMarkProcessPeb (
PEPROCESS Process
)
/*++
Routine Description:
This routine writes the debug variable in the PEB
Arguments:
Process - Process that needs its PEB modified
Return Value:
None.
--*/
{
KAPC_STATE ApcState;
PAGED_CODE ();
//
// Acquire process rundown protection as we are about to look at the processes address space
//
if (ExAcquireRundownProtection (&Process->RundownProtect)) {
if (Process->Peb != NULL) {
KeStackAttachProcess(&Process->Pcb, &ApcState);
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
try {
Process->Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL ? TRUE : FALSE);
#if defined(_WIN64)
if (Process->Wow64Process != NULL) {
PPEB32 Peb32 = (PPEB32)Process->Wow64Process->Wow64;
if (Peb32 != NULL) {
Peb32->BeingDebugged = Process->Peb->BeingDebugged;
}
}
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
}
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
KeUnstackDetachProcess(&ApcState);
}
ExReleaseRundownProtection (&Process->RundownProtect);
}
}
VOID
DbgkpWakeTarget (
IN PDEBUG_EVENT DebugEvent
)
{
PETHREAD Thread;
Thread = DebugEvent->Thread;
if ((DebugEvent->Flags&DEBUG_EVENT_SUSPEND) != 0) {
PsResumeThread (DebugEvent->Thread, NULL);
}
if (DebugEvent->Flags&DEBUG_EVENT_RELEASE) {
ExReleaseRundownProtection (&Thread->RundownProtect);
}
//
// If we have an actual thread waiting then wake it up else free the memory.
//
if ((DebugEvent->Flags&DEBUG_EVENT_NOWAIT) == 0) {
KeSetEvent (&DebugEvent->ContinueEvent, 0, FALSE); // Wake up waiting process
} else {
DbgkpFreeDebugEvent (DebugEvent);
}
}
VOID
DbgkpCloseObject (
IN PEPROCESS Process,
IN PVOID Object,
IN ACCESS_MASK GrantedAccess,
IN ULONG_PTR ProcessHandleCount,
IN ULONG_PTR SystemHandleCount
)
/*++
Routine Description:
Called by the object manager when a handle is closed to the object.
Arguments:
Process - Process doing the close
Object - Debug object being deleted
GrantedAccess - Access ranted for this handle
ProcessHandleCount - Unused and unmaintained by OB
SystemHandleCount - Current handle count for this object
Return Value:
None.
--*/
{
PDEBUG_OBJECT DebugObject = Object;
PDEBUG_EVENT DebugEvent;
PLIST_ENTRY ListPtr;
BOOLEAN Deref;
PAGED_CODE ();
UNREFERENCED_PARAMETER (GrantedAccess);
UNREFERENCED_PARAMETER (ProcessHandleCount);
//
// If this isn't the last handle then do nothing.
//
if (SystemHandleCount > 1) {
return;
}
ExAcquireFastMutex (&DebugObject->Mutex);
//
// Mark this object as going away and wake up any processes that are waiting.
//
DebugObject->Flags |= DEBUG_OBJECT_DELETE_PENDING;
//
// Remove any events and queue them to a temporary queue
//
ListPtr = DebugObject->EventList.Flink;
InitializeListHead (&DebugObject->EventList);
ExReleaseFastMutex (&DebugObject->Mutex);
//
// Wake anyone waiting. They need to leave this object alone now as its deleting
//
KeSetEvent (&DebugObject->EventsPresent, 0, FALSE);
//
// Loop over all processes and remove the debug port from any that still have it.
// Debug port propagation was disabled by setting the delete pending flag above so we only have to do this
// once. No more refs can appear now.
//
for (Process = PsGetNextProcess (NULL);
Process != NULL;
Process = PsGetNextProcess (Process)) {
if (Process->DebugPort == DebugObject) {
Deref = FALSE;
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
if (Process->DebugPort == DebugObject) {
Process->DebugPort = NULL;
Deref = TRUE;
}
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
if (Deref) {
DbgkpMarkProcessPeb (Process);
//
// If the caller wanted process deletion on debugger dying (old interface) then kill off the process.
//
if (DebugObject->Flags&DEBUG_OBJECT_KILL_ON_CLOSE) {
PsTerminateProcess (Process, STATUS_DEBUGGER_INACTIVE);
}
ObDereferenceObject (DebugObject);
}
}
}
//
// Wake up all the removed threads.
//
while (ListPtr != &DebugObject->EventList) {
DebugEvent = CONTAINING_RECORD (ListPtr, DEBUG_EVENT, EventList);
ListPtr = ListPtr->Flink;
DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
DbgkpWakeTarget (DebugEvent);
}
}
VOID
DbgkCopyProcessDebugPort (
IN PEPROCESS TargetProcess,
IN PEPROCESS SourceProcess
)
/*++
Routine Description:
Copies a debug port from one process to another.
Arguments:
TargetProcess - Process to move port to
sourceProcess - Process to move port from
Return Value:
None
--*/
{
PDEBUG_OBJECT DebugObject;
PAGED_CODE ();
TargetProcess->DebugPort = NULL; // New process. Needs no locks.
if (SourceProcess->DebugPort != NULL) {
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
DebugObject = SourceProcess->DebugPort;
if (DebugObject != NULL && (SourceProcess->Flags&PS_PROCESS_FLAGS_NO_DEBUG_INHERIT) == 0) {
//
// We must not propagate a debug port thats got no handles left.
//
ExAcquireFastMutex (&DebugObject->Mutex);
//
// If the object is delete pending then don't propagate this object.
//
if ((DebugObject->Flags&DEBUG_OBJECT_DELETE_PENDING) == 0) {
ObReferenceObject (DebugObject);
TargetProcess->DebugPort = DebugObject;
}
ExReleaseFastMutex (&DebugObject->Mutex);
}
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
}
}
NTSTATUS
DbgkOpenProcessDebugPort (
IN PEPROCESS Process,
IN KPROCESSOR_MODE PreviousMode,
OUT HANDLE *pHandle
)
/*++
Routine Description:
References the target processes debug port.
Arguments:
Process - Process to reference debug port
Return Value:
PDEBUG_OBJECT - Referenced object or NULL
--*/
{
PDEBUG_OBJECT DebugObject;
NTSTATUS Status;
PAGED_CODE ();
Status = STATUS_PORT_NOT_SET;
if (Process->DebugPort != NULL) {
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
DebugObject = Process->DebugPort;
if (DebugObject != NULL) {
ObReferenceObject (DebugObject);
}
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
if (DebugObject != NULL) {
Status = ObOpenObjectByPointer (DebugObject,
0,
NULL,
MAXIMUM_ALLOWED,
DbgkDebugObjectType,
PreviousMode,
pHandle);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (DebugObject);
}
}
}
return Status;
}
NTSTATUS
NtCreateDebugObject (
OUT PHANDLE DebugObjectHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG Flags
)
/*++
Routine Description:
Creates a new debug object that maintains the context for a single debug session. Multiple processes may be
associated with a single debug object.
Arguments:
DebugObjectHandle - Pointer to a handle to recive the output objects handle
DesiredAccess - Required handle access
ObjectAttributes - Standard object attributes structure
Flags - Only one flag DEBUG_KILL_ON_CLOSE
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS Status;
HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
PAGED_CODE();
//
// Get previous processor mode and probe output arguments if necessary.
// Zero the handle for error paths.
//
PreviousMode = KeGetPreviousMode();
try {
if (PreviousMode != KernelMode) {
ProbeForWriteHandle (DebugObjectHandle);
}
*DebugObjectHandle = NULL;
} except (ExSystemExceptionFilter ()) { // If previous mode is kernel then don't handle the exception
return GetExceptionCode ();
}
if (Flags & ~DEBUG_KILL_ON_CLOSE) {
return STATUS_INVALID_PARAMETER;
}
//
// Create a new debug object and initialize it.
//
Status = ObCreateObject (PreviousMode,
DbgkDebugObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof (DEBUG_OBJECT),
0,
0,
&DebugObject);
if (!NT_SUCCESS (Status)) {
return Status;
}
ExInitializeFastMutex (&DebugObject->Mutex);
InitializeListHead (&DebugObject->EventList);
KeInitializeEvent (&DebugObject->EventsPresent, NotificationEvent, FALSE);
if (Flags & DEBUG_KILL_ON_CLOSE) {
DebugObject->Flags = DEBUG_OBJECT_KILL_ON_CLOSE;
} else {
DebugObject->Flags = 0;
}
//
// Insert the object into the handle table
//
Status = ObInsertObject (DebugObject,
NULL,
DesiredAccess,
0,
NULL,
&Handle);
if (!NT_SUCCESS (Status)) {
return Status;
}
try {
*DebugObjectHandle = Handle;
} except (ExSystemExceptionFilter ()) {
//
// The caller changed the page protection or deleted the memory for the handle.
// No point closing the handle as process rundown will do that and we don't know its still the same handle
//
Status = GetExceptionCode ();
}
return Status;
}
VOID
DbgkpFreeDebugEvent (
IN PDEBUG_EVENT DebugEvent
)
{
NTSTATUS Status;
PAGED_CODE ();
switch (DebugEvent->ApiMsg.ApiNumber) {
case DbgKmCreateProcessApi :
if (DebugEvent->ApiMsg.u.CreateProcessInfo.FileHandle != NULL) {
Status = ObCloseHandle (DebugEvent->ApiMsg.u.CreateProcessInfo.FileHandle, KernelMode);
}
break;
case DbgKmLoadDllApi :
if (DebugEvent->ApiMsg.u.LoadDll.FileHandle != NULL) {
Status = ObCloseHandle (DebugEvent->ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
break;
}
ObDereferenceObject (DebugEvent->Process);
ObDereferenceObject (DebugEvent->Thread);
ExFreePool (DebugEvent);
}
NTSTATUS
DbgkpQueueMessage (
IN PEPROCESS Process,
IN PETHREAD Thread,
IN OUT PDBGKM_APIMSG ApiMsg,
IN ULONG Flags,
IN PDEBUG_OBJECT TargetDebugObject
)
/*++
Routine Description:
Queues a debug message to the port for a user mode debugger to get.
Arguments:
Process - Process being debugged
Thread - Thread making call
ApiMsg - Message being sent and received
NoWait - Don't wait for a response. Buffer message and return.
TargetDebugObject - Port to queue nowait messages to
Return Value:
NTSTATUS - Status of call.
--*/
{
PDEBUG_EVENT DebugEvent;
DEBUG_EVENT StaticDebugEvent;
PDEBUG_OBJECT DebugObject;
NTSTATUS Status;
PAGED_CODE ();
if (Flags&DEBUG_EVENT_NOWAIT) {
DebugEvent = ExAllocatePoolWithQuotaTag (NonPagedPool|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
sizeof (*DebugEvent),
'EgbD');
if (DebugEvent == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
DebugEvent->Flags = Flags|DEBUG_EVENT_INACTIVE;
ObReferenceObject (Process);
ObReferenceObject (Thread);
DebugEvent->BackoutThread = PsGetCurrentThread ();
DebugObject = TargetDebugObject;
} else {
DebugEvent = &StaticDebugEvent;
DebugEvent->Flags = Flags;
//锁住这里可以让全部的调试器卡死
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
DebugObject = Process->DebugPort;
//
// See if this create message has already been sent.
//
if (ApiMsg->ApiNumber == DbgKmCreateThreadApi ||
ApiMsg->ApiNumber == DbgKmCreateProcessApi) {
if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG) {
DebugObject = NULL;
}
}
//
// See if this exit message is for a thread that never had a create
//
if (ApiMsg->ApiNumber == DbgKmExitThreadApi ||
ApiMsg->ApiNumber == DbgKmExitProcessApi) {
if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG) {
DebugObject = NULL;
}
}
}
KeInitializeEvent (&DebugEvent->ContinueEvent, SynchronizationEvent, FALSE);
DebugEvent->Process = Process;
DebugEvent->Thread = Thread;
DebugEvent->ApiMsg = *ApiMsg;
DebugEvent->ClientId = Thread->Cid;
if (DebugObject == NULL) {
Status = STATUS_PORT_NOT_SET;
} else {
//
// We must not use a debug port thats got no handles left.
//
ExAcquireFastMutex (&DebugObject->Mutex);
//
// If the object is delete pending then don't use this object.
//
if ((DebugObject->Flags&DEBUG_OBJECT_DELETE_PENDING) == 0) {
InsertTailList (&DebugObject->EventList, &DebugEvent->EventList);
//
// Set the event to say there is an unread event in the object
//
if ((Flags&DEBUG_EVENT_NOWAIT) == 0) {
KeSetEvent (&DebugObject->EventsPresent, 0, FALSE);
}
Status = STATUS_SUCCESS;
} else {
Status = STATUS_DEBUGGER_INACTIVE;
}
ExReleaseFastMutex (&DebugObject->Mutex);
}
if ((Flags&DEBUG_EVENT_NOWAIT) == 0) {
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
if (NT_SUCCESS (Status)) {
KeWaitForSingleObject (&DebugEvent->ContinueEvent,
Executive,
KernelMode,
FALSE,
NULL);
Status = DebugEvent->Status;
*ApiMsg = DebugEvent->ApiMsg;
}
} else {
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
ObDereferenceObject (Thread);
ExFreePool (DebugEvent);
}
}
return Status;
}
NTSTATUS
DbgkClearProcessDebugObject (
IN PEPROCESS Process,
IN PDEBUG_OBJECT SourceDebugObject
)
/*++
Routine Description:
Remove a debug object from a process.
Arguments:
Process - Process to be debugged
sourceDebugObject - Debug object to detach
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS Status;
PDEBUG_OBJECT DebugObject;
PDEBUG_EVENT DebugEvent;
LIST_ENTRY TempList;
PLIST_ENTRY Entry;
PAGED_CODE ();
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
DebugObject = Process->DebugPort;
if (DebugObject == NULL || (DebugObject != SourceDebugObject && SourceDebugObject != NULL)) {
DebugObject = NULL;
Status = STATUS_PORT_NOT_SET;
} else {
Process->DebugPort = NULL;
Status = STATUS_SUCCESS;
}
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
if (NT_SUCCESS (Status)) {
DbgkpMarkProcessPeb (Process);
}
//
// Remove any events for this process and wake up the threads.
//
if (DebugObject) {
//
// Remove any events and queue them to a temporary queue
//
InitializeListHead (&TempList);
ExAcquireFastMutex (&DebugObject->Mutex);
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
) {
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
Entry = Entry->Flink;
if (DebugEvent->Process == Process) {
RemoveEntryList (&DebugEvent->EventList);
InsertTailList (&TempList, &DebugEvent->EventList);
}
}
ExReleaseFastMutex (&DebugObject->Mutex);
ObDereferenceObject (DebugObject);
//
// Wake up all the removed threads.
//
while (!IsListEmpty (&TempList)) {
Entry = RemoveHeadList (&TempList);
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
DbgkpWakeTarget (DebugEvent);
}
}
return Status;
}
NTSTATUS
DbgkpSetProcessDebugObject (
IN PEPROCESS Process,
IN PDEBUG_OBJECT DebugObject,
IN NTSTATUS MsgStatus,
IN PETHREAD LastThread
)
/*++
Routine Description:
Attach a debug object to a process.
Arguments:
Process - Process to be debugged
DebugObject - Debug object to attach
MsgStatus - Status from queing the messages
LastThread - Last thread seen in attach loop.
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS Status;
PETHREAD ThisThread;
LIST_ENTRY TempList;
PLIST_ENTRY Entry;
PDEBUG_EVENT DebugEvent;
BOOLEAN First;
PETHREAD Thread;
BOOLEAN GlobalHeld;
PETHREAD FirstThread;
PAGED_CODE ();
ThisThread = PsGetCurrentThread ();
InitializeListHead (&TempList);
First = TRUE;
GlobalHeld = FALSE;
if (!NT_SUCCESS (MsgStatus)) {
LastThread = NULL;
Status = MsgStatus;
} else {
Status = STATUS_SUCCESS;
}
//
// Pick up any threads we missed
//
if (NT_SUCCESS (Status)) {
while (1) {
//
// Acquire the debug port mutex so we know that any new threads will
// have to wait to behind us.
//
GlobalHeld = TRUE;
ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);
//
// If the port has been set then exit now.
//
if (Process->DebugPort != NULL) {
Status = STATUS_PORT_ALREADY_SET;
break;
}
//
// Assign the debug port to the process to pick up any new threads
//
Process->DebugPort = DebugObject;
//
// Reference the last thread so we can deref outside the lock
//
ObReferenceObject (LastThread);
//
// Search forward for new threads
//
Thread = PsGetNextProcessThread (Process, LastThread);
if (Thread != NULL) {
//
// Remove the debug port from the process as we are
// about to drop the lock
//
Process->DebugPort = NULL;
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
GlobalHeld = FALSE;
ObDereferenceObject (LastThread);
//
// Queue any new thread messages and repeat.
//
Status = DbgkpPostFakeThreadMessages (Process,
DebugObject,
Thread,
&FirstThread,
&LastThread);
if (!NT_SUCCESS (Status)) {
LastThread = NULL;
break;
}
ObDereferenceObject (FirstThread);
} else {
break;
}
}
}
//
// Lock the debug object so we can check its deleted status
//
ExAcquireFastMutex (&DebugObject->Mutex);
//
// We must not propagate a debug port thats got no handles left.
//
if (NT_SUCCESS (Status)) {
if ((DebugObject->Flags&DEBUG_OBJECT_DELETE_PENDING) == 0) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT|PS_PROCESS_FLAGS_CREATE_REPORTED);
ObReferenceObject (DebugObject);
} else {
Process->DebugPort = NULL;
Status = STATUS_DEBUGGER_INACTIVE;
}
}
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
) {
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
Entry = Entry->Flink;
if ((DebugEvent->Flags&DEBUG_EVENT_INACTIVE) != 0 && DebugEvent->BackoutThread == ThisThread) {
Thread = DebugEvent->Thread;
//
// If the thread has not been inserted by CreateThread yet then don't
// create a handle. We skip system threads here also
//
if (NT_SUCCESS (Status) && Thread->GrantedAccess != 0 && !IS_SYSTEM_THREAD (Thread)) {
//
// If we could not acquire rundown protection on this
// thread then we need to suppress its exit message.
//
if ((DebugEvent->Flags&DEBUG_EVENT_PROTECT_FAILED) != 0) {
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG);
RemoveEntryList (&DebugEvent->EventList);
InsertTailList (&TempList, &DebugEvent->EventList);
} else {
if (First) {
DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
KeSetEvent (&DebugObject->EventsPresent, 0, FALSE);
First = FALSE;
}
DebugEvent->BackoutThread = NULL;
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG);
}
} else {
RemoveEntryList (&DebugEvent->EventList);
InsertTailList (&TempList, &DebugEvent->EventList);
}
if (DebugEvent->Flags&DEBUG_EVENT_RELEASE) {
DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
ExReleaseRundownProtection (&Thread->RundownProtect);
}
}
}
ExReleaseFastMutex (&DebugObject->Mutex);
if (GlobalHeld) {
ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
}
if (LastThread != NULL) {
ObDereferenceObject (LastThread);
}
while (!IsListEmpty (&TempList)) {
Entry = RemoveHeadList (&TempList);
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
DbgkpWakeTarget (DebugEvent);
}
if (NT_SUCCESS (Status)) {
DbgkpMarkProcessPeb (Process);
}
return Status;
}
NTSTATUS
DbgkpPostFakeThreadMessages (
IN PEPROCESS Process,
IN PDEBUG_OBJECT DebugObject,
IN PETHREAD StartThread,
OUT PETHREAD *pFirstThread,
OUT PETHREAD *pLastThread
)
/*++
Routine Description:
This routine posts the faked initial process create, thread create messages
Arguments:
Process - Process to be debugged
DebugObject - Debug object to queue messages to
StartThread - Thread to start search from
pFirstThread - First thread found in the list
pLastThread - Last thread found in the list
Return Value:
None.
--*/
{
NTSTATUS Status;
PETHREAD Thread, FirstThread, LastThread;
DBGKM_APIMSG ApiMsg;
BOOLEAN First = TRUE;
BOOLEAN IsFirstThread;
PIMAGE_NT_HEADERS NtHeaders;
ULONG Flags;
NTSTATUS Status1;
PAGED_CODE ();
LastThread = FirstThread = NULL;
Status = STATUS_UNSUCCESSFUL;
if (StartThread != NULL) {
First = FALSE;
FirstThread = StartThread;
ObReferenceObject (FirstThread);
} else {
StartThread = PsGetNextProcessThread (Process, NULL);
First = TRUE;
}
for (Thread = StartThread;
Thread != NULL;
Thread = PsGetNextProcessThread (Process, Thread)) {
Flags = DEBUG_EVENT_NOWAIT;
//
// Keep a track ont he last thread we have seen.
// We use this as a starting point for new threads after we
// really attach so we can pick up any new threads.
//
if (LastThread != NULL) {
ObDereferenceObject (LastThread);
}
LastThread = Thread;
ObReferenceObject (LastThread);
//
// Acquire rundown protection of the thread.
// This stops the thread exiting so we know it can't send
// it's termination message
//
if (ExAcquireRundownProtection (&Thread->RundownProtect)) {
Flags |= DEBUG_EVENT_RELEASE;
//
// Suspend the thread if we can for the debugger
// We don't suspend terminating threads as we will not be giving details
// of these to the debugger.
//
if (!IS_SYSTEM_THREAD (Thread)) {
Status1 = PsSuspendThread (Thread, NULL);
if (NT_SUCCESS (Status1)) {
Flags |= DEBUG_EVENT_SUSPEND;
}
}
} else {
//
// Rundown protection failed for this thread.
// This means the thread is exiting. We will mark this thread
// later so it doesn't sent a thread termination message.
// We can't do this now because this attach might fail.
//
Flags |= DEBUG_EVENT_PROTECT_FAILED;
}
RtlZeroMemory (&ApiMsg, sizeof (ApiMsg));
if (First && (Flags&DEBUG_EVENT_PROTECT_FAILED) == 0 &&
!IS_SYSTEM_THREAD (Thread) && Thread->GrantedAccess != 0) {
IsFirstThread = TRUE;
} else {
IsFirstThread = FALSE;
}
if (IsFirstThread) {
ApiMsg.ApiNumber = DbgKmCreateProcessApi;
if (Process->SectionObject != NULL) { // system process doesn't have one of these!
ApiMsg.u.CreateProcessInfo.FileHandle = DbgkpSectionToFileHandle (Process->SectionObject);
} else {
ApiMsg.u.CreateProcessInfo.FileHandle = NULL;
}
ApiMsg.u.CreateProcessInfo.BaseOfImage = Process->SectionBaseAddress;
try {
NtHeaders = RtlImageNtHeader(Process->SectionBaseAddress);
if (NtHeaders) {
ApiMsg.u.CreateProcessInfo.InitialThread.StartAddress = NULL; // Filling this in breaks MSDEV!
// (PVOID)(NtHeaders->OptionalHeader.ImageBase + NtHeaders->OptionalHeader.AddressOfEntryPoint);
ApiMsg.u.CreateProcessInfo.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.CreateProcessInfo.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
ApiMsg.u.CreateProcessInfo.InitialThread.StartAddress = NULL;
ApiMsg.u.CreateProcessInfo.DebugInfoFileOffset = 0;
ApiMsg.u.CreateProcessInfo.DebugInfoSize = 0;
}
} else {
ApiMsg.ApiNumber = DbgKmCreateThreadApi;
ApiMsg.u.CreateThread.StartAddress = Thread->StartAddress;
}
Status = DbgkpQueueMessage (Process,
Thread,
&ApiMsg,
Flags,
DebugObject);
if (!NT_SUCCESS (Status)) {
if (Flags&DEBUG_EVENT_SUSPEND) {
PsResumeThread (Thread, NULL);
}
if (Flags&DEBUG_EVENT_RELEASE) {
ExReleaseRundownProtection (&Thread->RundownProtect);
}
if (ApiMsg.ApiNumber == DbgKmCreateProcessApi && ApiMsg.u.CreateProcessInfo.FileHandle != NULL) {
ObCloseHandle (ApiMsg.u.CreateProcessInfo.FileHandle, KernelMode);
}
PsQuitNextProcessThread (Thread);
break;
} else if (IsFirstThread) {
First = FALSE;
ObReferenceObject (Thread);
FirstThread = Thread;
}
}
if (!NT_SUCCESS (Status)) {
if (FirstThread) {
ObDereferenceObject (FirstThread);
}
if (LastThread != NULL) {
ObDereferenceObject (LastThread);
}
} else {
if (FirstThread) {
*pFirstThread = FirstThread;
*pLastThread = LastThread;
} else {
Status = STATUS_UNSUCCESSFUL;
}
}
return Status;
}
NTSTATUS
DbgkpPostFakeModuleMessages (
IN PEPROCESS Process,
IN PETHREAD Thread,
IN PDEBUG_OBJECT DebugObject)
/*++
Routine Description:
This routine posts the faked module load messages when we debug an active process.
Arguments:
ProcessHandle - Handle to a process to be debugged
DebugObjectHandle - Handle to a debug object
Return Value:
None.
--*/
{
PPEB Peb = Process->Peb;
PPEB_LDR_DATA Ldr;
PLIST_ENTRY LdrHead, LdrNext;
PLDR_DATA_TABLE_ENTRY LdrEntry;
DBGKM_APIMSG ApiMsg;
ULONG i;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING Name;
PIMAGE_NT_HEADERS NtHeaders;
NTSTATUS Status;
IO_STATUS_BLOCK iosb;
PAGED_CODE ();
if (Peb == NULL) {
return STATUS_SUCCESS;
}
try {
Ldr = Peb->Ldr;
LdrHead = &Ldr->InLoadOrderModuleList;
ProbeForReadSmallStructure (LdrHead, sizeof (LIST_ENTRY), sizeof (UCHAR));
for (LdrNext = LdrHead->Flink, i = 0;
LdrNext != LdrHead && i < 500;
LdrNext = LdrNext->Flink, i++) {
//
// First image got send with process create message
//
if (i > 0) {
RtlZeroMemory (&ApiMsg, sizeof (ApiMsg));
LdrEntry = CONTAINING_RECORD (LdrNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
ProbeForReadSmallStructure (LdrEntry, sizeof (LDR_DATA_TABLE_ENTRY), sizeof (UCHAR));
ApiMsg.ApiNumber = DbgKmLoadDllApi;
ApiMsg.u.LoadDll.BaseOfDll = LdrEntry->DllBase;
ApiMsg.u.LoadDll.NamePointer = NULL;
ProbeForReadSmallStructure (ApiMsg.u.LoadDll.BaseOfDll, sizeof (IMAGE_DOS_HEADER), sizeof (UCHAR));
NtHeaders = RtlImageNtHeader (ApiMsg.u.LoadDll.BaseOfDll);
if (NtHeaders) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.LoadDll.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
Status = MmGetFileNameForAddress (NtHeaders, &Name);
if (NT_SUCCESS (Status)) {
InitializeObjectAttributes (&oa,
&Name,
OBJ_FORCE_ACCESS_CHECK|OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenFile (&ApiMsg.u.LoadDll.FileHandle,
GENERIC_READ|SYNCHRONIZE,
&oa,
&iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS (Status)) {
ApiMsg.u.LoadDll.FileHandle = NULL;
}
ExFreePool (Name.Buffer);
}
Status = DbgkpQueueMessage (Process,
Thread,
&ApiMsg,
DEBUG_EVENT_NOWAIT,
DebugObject);
if (!NT_SUCCESS (Status) && ApiMsg.u.LoadDll.FileHandle != NULL) {
ObCloseHandle (ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
}
ProbeForReadSmallStructure (LdrNext, sizeof (LIST_ENTRY), sizeof (UCHAR));
}
} except (EXCEPTION_EXECUTE_HANDLER) {
}
#if defined(_WIN64)
if (Process->Wow64Process != NULL && Process->Wow64Process->Wow64 != NULL) {
PPEB32 Peb32;
PPEB_LDR_DATA32 Ldr32;
PLIST_ENTRY32 LdrHead32, LdrNext32;
PLDR_DATA_TABLE_ENTRY32 LdrEntry32;
PWCHAR pSys;
Peb32 = (PPEB32)Process->Wow64Process->Wow64;
try {
Ldr32 = (PVOID) UlongToPtr(Peb32->Ldr);
LdrHead32 = &Ldr32->InLoadOrderModuleList;
ProbeForReadSmallStructure (LdrHead32, sizeof (LIST_ENTRY32), sizeof (UCHAR));
for (LdrNext32 = (PVOID) UlongToPtr(LdrHead32->Flink), i = 0;
LdrNext32 != LdrHead32 && i < 500;
LdrNext32 = (PVOID) UlongToPtr(LdrNext32->Flink), i++) {
if (i > 0) {
RtlZeroMemory (&ApiMsg, sizeof (ApiMsg));
LdrEntry32 = CONTAINING_RECORD (LdrNext32, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
ProbeForReadSmallStructure (LdrEntry32, sizeof (LDR_DATA_TABLE_ENTRY32), sizeof (UCHAR));
ApiMsg.ApiNumber = DbgKmLoadDllApi;
ApiMsg.u.LoadDll.BaseOfDll = (PVOID) UlongToPtr(LdrEntry32->DllBase);
ApiMsg.u.LoadDll.NamePointer = NULL;
ProbeForReadSmallStructure (ApiMsg.u.LoadDll.BaseOfDll, sizeof (IMAGE_DOS_HEADER), sizeof (UCHAR));
NtHeaders = RtlImageNtHeader(ApiMsg.u.LoadDll.BaseOfDll);
if (NtHeaders) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.LoadDll.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
Status = MmGetFileNameForAddress (NtHeaders, &Name);
if (NT_SUCCESS (Status)) {
ASSERT (sizeof (L"SYSTEM32") == sizeof (WOW64_SYSTEM_DIRECTORY_U));
pSys = wcsstr (Name.Buffer, L"\\SYSTEM32\\");
if (pSys != NULL) {
RtlCopyMemory (pSys+1,
WOW64_SYSTEM_DIRECTORY_U,
sizeof(WOW64_SYSTEM_DIRECTORY_U) - sizeof(UNICODE_NULL));
}
InitializeObjectAttributes (&oa,
&Name,
OBJ_FORCE_ACCESS_CHECK|OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenFile (&ApiMsg.u.LoadDll.FileHandle,
GENERIC_READ|SYNCHRONIZE,
&oa,
&iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS (Status)) {
ApiMsg.u.LoadDll.FileHandle = NULL;
}
ExFreePool (Name.Buffer);
}
Status = DbgkpQueueMessage (Process,
Thread,
&ApiMsg,
DEBUG_EVENT_NOWAIT,
DebugObject);
if (!NT_SUCCESS (Status) && ApiMsg.u.LoadDll.FileHandle != NULL) {
ObCloseHandle (ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
}
ProbeForReadSmallStructure (LdrNext32, sizeof (LIST_ENTRY32), sizeof (UCHAR));
}
} except (EXCEPTION_EXECUTE_HANDLER) {
}
}
#endif
return STATUS_SUCCESS;
}
NTSTATUS
DbgkpPostFakeProcessCreateMessages (
IN PEPROCESS Process,
IN PDEBUG_OBJECT DebugObject,
IN PETHREAD *pLastThread
)
/*++
Routine Description:
This routine posts the faked initial process create, thread create and mudule load messages
Arguments:
ProcessHandle - Handle to a process to be debugged
DebugObjectHandle - Handle to a debug object
Return Value:
None.
--*/
{
NTSTATUS Status;
KAPC_STATE ApcState;
PETHREAD Thread;
PETHREAD LastThread;
PAGED_CODE ();
//
// Attach to the process so we can touch its address space
//
KeStackAttachProcess(&Process->Pcb, &ApcState);
Status = DbgkpPostFakeThreadMessages (Process,
DebugObject,
NULL,
&Thread,
&LastThread);
if (NT_SUCCESS (Status)) {
Status = DbgkpPostFakeModuleMessages (Process, Thread, DebugObject);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (LastThread);
LastThread = NULL;
}
ObDereferenceObject (Thread);
} else {
LastThread = NULL;
}
KeUnstackDetachProcess(&ApcState);
*pLastThread = LastThread;
return Status;
}
NTSTATUS
NtDebugActiveProcess (
IN HANDLE ProcessHandle,
IN HANDLE DebugObjectHandle
)
/*++
Routine Description:
Attach a debug object to a process.
Arguments:
ProcessHandle - Handle to a process to be debugged
DebugObjectHandle - Handle to a debug object
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
PEPROCESS Process;
PETHREAD LastThread;
PAGED_CODE ();
PreviousMode = KeGetPreviousMode();
Status = ObReferenceObjectByHandle (ProcessHandle,
PROCESS_SET_PORT,
PsProcessType,
PreviousMode,
&Process,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
//
// Don't let us debug ourselves or the system process.
//
if (Process == PsGetCurrentProcess () || Process == PsInitialSystemProcess) {
ObDereferenceObject (Process);
return STATUS_ACCESS_DENIED;
}
Status = ObReferenceObjectByHandle (DebugObjectHandle,
DEBUG_PROCESS_ASSIGN,
DbgkDebugObjectType,
PreviousMode,
&DebugObject,
NULL);
if (NT_SUCCESS (Status)) {
//
// We will be touching process address space. Block process rundown.
//
if (ExAcquireRundownProtection (&Process->RundownProtect)) {
//
// Post the fake process create messages etc.
//
Status = DbgkpPostFakeProcessCreateMessages (Process,
DebugObject,
&LastThread);
//
// Set the debug port. If this fails it will remove any faked messages.
//
Status = DbgkpSetProcessDebugObject (Process,
DebugObject,
Status,
LastThread);
ExReleaseRundownProtection (&Process->RundownProtect);
} else {
Status = STATUS_PROCESS_IS_TERMINATING;
}
ObDereferenceObject (DebugObject);
}
ObDereferenceObject (Process);
return Status;
}
NTSTATUS
NtRemoveProcessDebug (
IN HANDLE ProcessHandle,
IN HANDLE DebugObjectHandle
)
/*++
Routine Description:
Remove a debug object from a process.
Arguments:
ProcessHandle - Handle to a process currently being debugged
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
PEPROCESS Process;
PAGED_CODE ();
PreviousMode = KeGetPreviousMode();
Status = ObReferenceObjectByHandle (ProcessHandle,
PROCESS_SET_PORT,
PsProcessType,
PreviousMode,
&Process,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
Status = ObReferenceObjectByHandle (DebugObjectHandle,
DEBUG_PROCESS_ASSIGN,
DbgkDebugObjectType,
PreviousMode,
&DebugObject,
NULL);
if (NT_SUCCESS (Status)) {
Status = DbgkClearProcessDebugObject (Process,
DebugObject);
ObDereferenceObject (DebugObject);
}
ObDereferenceObject (Process);
return Status;
}
VOID
DbgkpOpenHandles (
PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
PEPROCESS Process,
PETHREAD Thread
)
/*++
Routine Description:
Opens up process, thread and filehandles if need be for some of the requests
Arguments:
WaitStateChange - User mode format change block
Process - Pointer to target process
Thread - Pointer to target thread
Return Value:
None
--*/
{
NTSTATUS Status;
PEPROCESS CurrentProcess;
HANDLE OldHandle;
PAGED_CODE ();
switch (WaitStateChange->NewState) {
case DbgCreateThreadStateChange :
//
// We have the right to open up any thread in the process if we are allowed to debug it.
// Use kernel mode here so we are always granted it regardless of protection.
//
Status = ObOpenObjectByPointer (Thread,
0,
NULL,
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | \
THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_TERMINATE |
READ_CONTROL | SYNCHRONIZE,
PsThreadType,
KernelMode,
&WaitStateChange->StateInfo.CreateThread.HandleToThread);
if (!NT_SUCCESS (Status)) {
WaitStateChange->StateInfo.CreateThread.HandleToThread = NULL;
}
break;
case DbgCreateProcessStateChange :
Status = ObOpenObjectByPointer (Thread,
0,
NULL,
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | \
THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_TERMINATE |
READ_CONTROL | SYNCHRONIZE,
PsThreadType,
KernelMode,
&WaitStateChange->StateInfo.CreateProcessInfo.HandleToThread);
if (!NT_SUCCESS (Status)) {
WaitStateChange->StateInfo.CreateProcessInfo.HandleToThread = NULL;
}
Status = ObOpenObjectByPointer (Process,
0,
NULL,
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION |
PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION |
PROCESS_CREATE_THREAD | PROCESS_TERMINATE |
READ_CONTROL | SYNCHRONIZE,
PsProcessType,
KernelMode,
&WaitStateChange->StateInfo.CreateProcessInfo.HandleToProcess);
if (!NT_SUCCESS (Status)) {
WaitStateChange->StateInfo.CreateProcessInfo.HandleToProcess = NULL;
}
OldHandle = WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.FileHandle;
if (OldHandle != NULL) {
CurrentProcess = PsGetCurrentProcess ();
Status = ObDuplicateObject (CurrentProcess,
OldHandle,
CurrentProcess,
&WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.FileHandle,
0,
0,
DUPLICATE_SAME_ACCESS,
KernelMode);
if (!NT_SUCCESS (Status)) {
WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.FileHandle = NULL;
}
ObCloseHandle (OldHandle, KernelMode);
}
break;
case DbgLoadDllStateChange :
OldHandle = WaitStateChange->StateInfo.LoadDll.FileHandle;
if (OldHandle != NULL) {
CurrentProcess = PsGetCurrentProcess ();
Status = ObDuplicateObject (CurrentProcess,
OldHandle,
CurrentProcess,
&WaitStateChange->StateInfo.LoadDll.FileHandle,
0,
0,
DUPLICATE_SAME_ACCESS,
KernelMode);
if (!NT_SUCCESS (Status)) {
WaitStateChange->StateInfo.LoadDll.FileHandle = NULL;
}
ObCloseHandle (OldHandle, KernelMode);
}
break;
default :
break;
}
}
VOID
DbgkpConvertKernelToUserStateChange (
PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
PDEBUG_EVENT DebugEvent)
/*++
Routine Description:
Converts a kernel message to one the user expects
Arguments:
WaitStateChange - User mode format
DebugEvent - Debug event block to copy from
Return Value:
None
--*/
{
PAGED_CODE ();
WaitStateChange->AppClientId = DebugEvent->ClientId;
switch (DebugEvent->ApiMsg.ApiNumber) {
case DbgKmExceptionApi :
switch (DebugEvent->ApiMsg.u.Exception.ExceptionRecord.ExceptionCode) {
case STATUS_BREAKPOINT :
WaitStateChange->NewState = DbgBreakpointStateChange;
break;
case STATUS_SINGLE_STEP :
WaitStateChange->NewState = DbgSingleStepStateChange;
break;
default :
WaitStateChange->NewState = DbgExceptionStateChange;
break;
}
WaitStateChange->StateInfo.Exception = DebugEvent->ApiMsg.u.Exception;
break;
case DbgKmCreateThreadApi :
WaitStateChange->NewState = DbgCreateThreadStateChange;
WaitStateChange->StateInfo.CreateThread.NewThread = DebugEvent->ApiMsg.u.CreateThread;
break;
case DbgKmCreateProcessApi :
WaitStateChange->NewState = DbgCreateProcessStateChange;
WaitStateChange->StateInfo.CreateProcessInfo.NewProcess = DebugEvent->ApiMsg.u.CreateProcessInfo;
//
// clear out the handle in the message as we will close this when we duplicate.
//
DebugEvent->ApiMsg.u.CreateProcessInfo.FileHandle = NULL;
break;
case DbgKmExitThreadApi :
WaitStateChange->NewState = DbgExitThreadStateChange;
WaitStateChange->StateInfo.ExitThread = DebugEvent->ApiMsg.u.ExitThread;
break;
case DbgKmExitProcessApi :
WaitStateChange->NewState = DbgExitProcessStateChange;
WaitStateChange->StateInfo.ExitProcess = DebugEvent->ApiMsg.u.ExitProcess;
break;
case DbgKmLoadDllApi :
WaitStateChange->NewState = DbgLoadDllStateChange;
WaitStateChange->StateInfo.LoadDll = DebugEvent->ApiMsg.u.LoadDll;
//
// clear out the handle in the message as we will close this when we duplicate.
//
DebugEvent->ApiMsg.u.LoadDll.FileHandle = NULL;
break;
case DbgKmUnloadDllApi :
WaitStateChange->NewState = DbgUnloadDllStateChange;
WaitStateChange->StateInfo.UnloadDll = DebugEvent->ApiMsg.u.UnloadDll;
break;
default :
ASSERT (FALSE);
}
}
NTSTATUS
NtWaitForDebugEvent (
IN HANDLE DebugObjectHandle,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL,
OUT PDBGUI_WAIT_STATE_CHANGE WaitStateChange
)
/*++
Routine Description:
Waits for a debug event and returns it to the user if one arrives
Arguments:
DebugObjectHandle - Handle to a debug object
Alertable - TRUE is the wait is to be alertable
Timeout - Operation timeout value
WaitStateChange - Returned debug event
Return Value:
Status of operation
--*/
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
LARGE_INTEGER Tmo = {0};
LARGE_INTEGER StartTime = {0};
DBGUI_WAIT_STATE_CHANGE tWaitStateChange = {0};
PEPROCESS Process;
PETHREAD Thread;
PLIST_ENTRY Entry, Entry2;
PDEBUG_EVENT DebugEvent, DebugEvent2;
BOOLEAN GotEvent;
PAGED_CODE ();
PreviousMode = KeGetPreviousMode();
try {
if (ARGUMENT_PRESENT (Timeout)) {
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure (Timeout, sizeof (*Timeout), sizeof (UCHAR));
}
Tmo = *Timeout;
Timeout = &Tmo;
KeQuerySystemTime (&StartTime);
}
if (PreviousMode != KernelMode) {
ProbeForWriteSmallStructure (WaitStateChange, sizeof (*WaitStateChange), sizeof (UCHAR));
}
} except (ExSystemExceptionFilter ()) { // If previous mode is kernel then don't handle the exception
return GetExceptionCode ();
}
Status = ObReferenceObjectByHandle (DebugObjectHandle,
DEBUG_READ_EVENT,
DbgkDebugObjectType,
PreviousMode,
&DebugObject,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
Process = NULL;
Thread = NULL;
while (1) {
Status = KeWaitForSingleObject (&DebugObject->EventsPresent,
Executive,
PreviousMode,
Alertable,
Timeout);
if (!NT_SUCCESS (Status) || Status == STATUS_TIMEOUT || Status == STATUS_ALERTED || Status == STATUS_USER_APC) {
break;
}
GotEvent = FALSE;
DebugEvent = NULL;
ExAcquireFastMutex (&DebugObject->Mutex);
//
// If the object is delete pending then return an error.
//
if ((DebugObject->Flags&DEBUG_OBJECT_DELETE_PENDING) == 0) {
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
Entry = Entry->Flink) {
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
//
// If this event has not been given back to the user yet and is not
// inactive then pass it back.
// We check to see if we have any other outstanding messages for this
// thread as this confuses VC. You can only get multiple events
// for the same thread for the attach faked messages.
//
if ((DebugEvent->Flags&(DEBUG_EVENT_READ|DEBUG_EVENT_INACTIVE)) == 0) {
GotEvent = TRUE;
for (Entry2 = DebugObject->EventList.Flink;
Entry2 != Entry;
Entry2 = Entry2->Flink) {
DebugEvent2 = CONTAINING_RECORD (Entry2, DEBUG_EVENT, EventList);
if (DebugEvent->ClientId.UniqueProcess == DebugEvent2->ClientId.UniqueProcess) {
//
// This event has the same process as an earlier event. Mark it as inactive.
//
DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
DebugEvent->BackoutThread = NULL;
GotEvent = FALSE;
break;
}
}
if (GotEvent) {
break;
}
}
}
if (GotEvent) {
Process = DebugEvent->Process;
Thread = DebugEvent->Thread;
ObReferenceObject (Thread);
ObReferenceObject (Process);
DbgkpConvertKernelToUserStateChange (&tWaitStateChange, DebugEvent);
DebugEvent->Flags |= DEBUG_EVENT_READ;
} else {
//
// No unread events there. Clear the event.
//
KeClearEvent (&DebugObject->EventsPresent);
}
Status = STATUS_SUCCESS;
} else {
Status = STATUS_DEBUGGER_INACTIVE;
}
ExReleaseFastMutex (&DebugObject->Mutex);
if (NT_SUCCESS (Status)) {
//
// If we woke up and found nothing
//
if (GotEvent == FALSE) {
//
// If timeout is a delta time then adjust it for the wait so far.
//
if (Tmo.QuadPart < 0) {
LARGE_INTEGER NewTime;
KeQuerySystemTime (&NewTime);
Tmo.QuadPart = Tmo.QuadPart + (NewTime.QuadPart - StartTime.QuadPart);
StartTime = NewTime;
if (Tmo.QuadPart >= 0) {
Status = STATUS_TIMEOUT;
break;
}
}
} else {
//
// Fixup needed handles. The caller could have guessed the thread id etc by now and made the target thread
// continue. This isn't a problem as we won't do anything damaging to the system in this case. The caller
// won't get the correct results but they set out to break us.
//
DbgkpOpenHandles (&tWaitStateChange, Process, Thread);
ObDereferenceObject (Thread);
ObDereferenceObject (Process);
break;
}
} else {
break;
}
}
ObDereferenceObject (DebugObject);
try {
*WaitStateChange = tWaitStateChange;
} except (ExSystemExceptionFilter ()) { // If previous mode is kernel then don't handle the exception
Status = GetExceptionCode ();
}
return Status;
}
NTSTATUS
NtDebugContinue (
IN HANDLE DebugObjectHandle,
IN PCLIENT_ID ClientId,
IN NTSTATUS ContinueStatus
)
/*++
Routine Description:
Continues a stalled debugged thread
Arguments:
DebugObjectHandle - Handle to a debug object
ClientId - ClientId of thread tro continue
ContinueStatus - Status of continue
Return Value:
Status of operation
--*/
{
NTSTATUS Status;
PDEBUG_OBJECT DebugObject;
PDEBUG_EVENT DebugEvent, FoundDebugEvent;
KPROCESSOR_MODE PreviousMode;
CLIENT_ID Clid;
PLIST_ENTRY Entry;
BOOLEAN GotEvent;
PreviousMode = KeGetPreviousMode();
try {
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure (ClientId, sizeof (*ClientId), sizeof (UCHAR));
}
Clid = *ClientId;
} except (ExSystemExceptionFilter ()) { // If previous mode is kernel then don't handle the exception
return GetExceptionCode ();
}
switch (ContinueStatus) {
case DBG_EXCEPTION_HANDLED :
case DBG_EXCEPTION_NOT_HANDLED :
case DBG_TERMINATE_THREAD :
case DBG_TERMINATE_PROCESS :
case DBG_CONTINUE :
break;
default :
return STATUS_INVALID_PARAMETER;
}
Status = ObReferenceObjectByHandle (DebugObjectHandle,
DEBUG_READ_EVENT,
DbgkDebugObjectType,
PreviousMode,
&DebugObject,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
GotEvent = FALSE;
FoundDebugEvent = NULL;
ExAcquireFastMutex (&DebugObject->Mutex);
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
Entry = Entry->Flink) {
DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
//
// Make sure the client ID matches and that the debugger saw all the events.
// We don't allow the caller to start a thread that it never saw a message for.
//
if (DebugEvent->ClientId.UniqueProcess == Clid.UniqueProcess) {
if (!GotEvent) {
if (DebugEvent->ClientId.UniqueThread == Clid.UniqueThread &&
(DebugEvent->Flags&DEBUG_EVENT_READ) != 0) {
RemoveEntryList (Entry);
FoundDebugEvent = DebugEvent;
GotEvent = TRUE;
}
} else {
//
// VC breaks if it sees more than one event at a time
// for the same process.
//
DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
KeSetEvent (&DebugObject->EventsPresent, 0, FALSE);
break;
}
}
}
ExReleaseFastMutex (&DebugObject->Mutex);
ObDereferenceObject (DebugObject);
if (GotEvent) {
FoundDebugEvent->ApiMsg.ReturnedStatus = ContinueStatus;
FoundDebugEvent->Status = STATUS_SUCCESS;
DbgkpWakeTarget (FoundDebugEvent);
} else {
Status = STATUS_INVALID_PARAMETER;
}
return Status;
}
NTSTATUS
NtSetInformationDebugObject (
IN HANDLE DebugObjectHandle,
IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,
IN PVOID DebugInformation,
IN ULONG DebugInformationLength,
OUT PULONG ReturnLength OPTIONAL
)
/*++
Routine Description:
This function sets the state of a debug object.
Arguments:
ProcessHandle - Supplies a handle to a process object.
ProcessInformationClass - Supplies the class of information being
set.
ProcessInformation - Supplies a pointer to a record that contains the
information to set.
ProcessInformationLength - Supplies the length of the record that contains
the information to set.
Return Value:
NTSTATUS - Status of call
--*/
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PDEBUG_OBJECT DebugObject;
ULONG Flags;
PreviousMode = KeGetPreviousMode();
try {
if (PreviousMode != KernelMode) {
ProbeForRead (DebugInformation,
DebugInformationLength,
sizeof (ULONG));
if (ARGUMENT_PRESENT (ReturnLength)) {
ProbeForWriteUlong (ReturnLength);
}
}
if (ARGUMENT_PRESENT (ReturnLength)) {
*ReturnLength = 0;
}
switch (DebugObjectInformationClass) {
case DebugObjectFlags : {
if (DebugInformationLength != sizeof (ULONG)) {
if (ARGUMENT_PRESENT (ReturnLength)) {
*ReturnLength = sizeof (ULONG);
}
return STATUS_INFO_LENGTH_MISMATCH;
}
Flags = *(PULONG) DebugInformation;
break;
}
default : {
return STATUS_INVALID_PARAMETER;
}
}
} except (ExSystemExceptionFilter ()) {
return GetExceptionCode ();
}
switch (DebugObjectInformationClass) {
case DebugObjectFlags : {
if (Flags & ~DEBUG_KILL_ON_CLOSE) {
return STATUS_INVALID_PARAMETER;
}
Status = ObReferenceObjectByHandle (DebugObjectHandle,
DEBUG_SET_INFORMATION,
DbgkDebugObjectType,
PreviousMode,
&DebugObject,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
ExAcquireFastMutex (&DebugObject->Mutex);
if (Flags&DEBUG_KILL_ON_CLOSE) {
DebugObject->Flags |= DEBUG_OBJECT_KILL_ON_CLOSE;
} else {
DebugObject->Flags &= ~DEBUG_OBJECT_KILL_ON_CLOSE;
}
ExReleaseFastMutex (&DebugObject->Mutex);
ObDereferenceObject (DebugObject);
}
}
return STATUS_SUCCESS;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。