NTSTATUS
NTAPI
NtWaitForDebugEvent(IN HANDLE DebugHandle,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL,
OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
LARGE_INTEGER SafeTimeOut;
PEPROCESS Process;
LARGE_INTEGER StartTime;
PETHREAD Thread;
BOOLEAN GotEvent;
LARGE_INTEGER NewTime;
PDEBUG_OBJECT DebugObject;
DBGUI_WAIT_STATE_CHANGE WaitStateChange;
NTSTATUS Status = STATUS_SUCCESS;
PDEBUG_EVENT DebugEvent = NULL, DebugEvent2;
PLIST_ENTRY ListHead, NextEntry, NextEntry2;
PAGED_CODE();
DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p\n", DebugHandle);
/* Clear the initial wait state change structure */
RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange));
/* Protect probe in SEH */
_SEH_TRY
{
/* Check if we came with a timeout */
if (Timeout)
{
/* Check if the call was from user mode */
if (PreviousMode != KernelMode)
{
/* Probe it */
ProbeForReadLargeInteger(Timeout);
}
/* Make a local copy */
SafeTimeOut = *Timeout;
Timeout = &SafeTimeOut;
/* Query the current time */
KeQuerySystemTime(&StartTime);
}
/* Check if the call was from user mode */
if (PreviousMode != KernelMode)
{
/* Probe the state change structure */
ProbeForWrite(StateChange, sizeof(*StateChange), sizeof(ULONG));
}
}
_SEH_HANDLE
{
/* Get the exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
if (!NT_SUCCESS(Status)) return Status;
/* Get the debug object */
Status = ObReferenceObjectByHandle(DebugHandle,
DEBUG_OBJECT_WAIT_STATE_CHANGE,
DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Clear process and thread */
Process = NULL;
Thread = NULL;
/* Wait on the debug object given to us */
while (TRUE)
{
Status = KeWaitForSingleObject(DebugObject,
Executive,
PreviousMode,
Alertable,
Timeout);
if (!NT_SUCCESS(Status) ||
(Status == STATUS_TIMEOUT) ||
(Status == STATUS_ALERTED) ||
(Status == STATUS_USER_APC))
{
/* Break out the wait */
break;
}
/* Lock the object */
GotEvent = FALSE;
ExAcquireFastMutex(&DebugObject->Mutex);
/* Check if a debugger is connected */
if (DebugObject->DebuggerInactive)
{
/* Not connected */
Status = STATUS_DEBUGGER_INACTIVE;
}
else
{
/* Loop the events */
ListHead = &DebugObject->EventList;
NextEntry = ListHead->Flink;
while (ListHead != NextEntry)
{
/* Get the debug event */
DebugEvent = CONTAINING_RECORD(NextEntry,
DEBUG_EVENT,
EventList);
DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx\n",
DebugEvent, DebugEvent->Flags);
/* Check flags */
if (!(DebugEvent->Flags & (4 | 1)))
{
/* We got an event */
GotEvent = TRUE;
/* Loop the list internally */
NextEntry2 = DebugObject->EventList.Flink;
while (NextEntry2 != NextEntry)
{
/* Get the debug event */
DebugEvent2 = CONTAINING_RECORD(NextEntry2,
DEBUG_EVENT,
EventList);
/* Try to match process IDs */
if (DebugEvent2->ClientId.UniqueProcess ==
DebugEvent->ClientId.UniqueProcess)
{
/* Found it, break out */
DebugEvent->Flags |= 4;
DebugEvent->BackoutThread = NULL;
GotEvent = FALSE;
break;
}
/* Move to the next entry */
NextEntry2 = NextEntry2->Flink;
}
/* Check if we still have a valid event */
if (GotEvent) break;
}
/* Move to the next entry */
NextEntry = NextEntry->Flink;
}
/* Check if we have an event */
if (GotEvent)
{
/* Save and reference the process and thread */
Process = DebugEvent->Process;
Thread = DebugEvent->Thread;
ObReferenceObject(Process);
ObReferenceObject(Thread);
/* Convert to user-mode structure */
DbgkpConvertKernelToUserStateChange(&WaitStateChange,
DebugEvent);
/* Set flag */
DebugEvent->Flags |= 1;
}
else
{
/* Unsignal the event */
KeClearEvent(&DebugObject->EventsPresent);
}
/* Set success */
Status = STATUS_SUCCESS;
}
/* Release the mutex */
ExReleaseFastMutex(&DebugObject->Mutex);
if (!NT_SUCCESS(Status)) break;
/* Check if we got an event */
if (!GotEvent)
{
/* Check if we can wait again */
if (SafeTimeOut.QuadPart < 0)
{
/* Query the new time */
KeQuerySystemTime(&NewTime);
/* Substract times */
SafeTimeOut.QuadPart += (NewTime.QuadPart - StartTime.QuadPart);
StartTime = NewTime;
/* Check if we've timed out */
if (SafeTimeOut.QuadPart > 0)
{
/* We have, break out of the loop */
Status = STATUS_TIMEOUT;
break;
}
}
}
else
{
/* Open the handles and dereference the objects */
DbgkpOpenHandles(&WaitStateChange, Process, Thread);
ObDereferenceObject(Process);
ObDereferenceObject(Thread);
break;
}
}
/* We're done, dereference the object */
ObDereferenceObject(DebugObject);
/* Protect write with SEH */
_SEH_TRY
{
/* Return our wait state change structure */
RtlCopyMemory(StateChange,
&WaitStateChange,
sizeof(DBGUI_WAIT_STATE_CHANGE));
}
_SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
{
/* Get SEH Exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
/* Return status */
return Status;
}