xref: /reactos/ntoskrnl/dbgk/dbgkobj.c (revision 53221834)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/dbgk/dbgkobj.c
5  * PURPOSE:         User-Mode Debugging Support, Debug Object Management.
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 POBJECT_TYPE DbgkDebugObjectType;
16 FAST_MUTEX DbgkpProcessDebugPortMutex;
17 ULONG DbgkpTraceLevel = 0;
18 
19 GENERIC_MAPPING DbgkDebugObjectMapping =
20 {
21     STANDARD_RIGHTS_READ    | DEBUG_OBJECT_WAIT_STATE_CHANGE,
22     STANDARD_RIGHTS_WRITE   | DEBUG_OBJECT_ADD_REMOVE_PROCESS,
23     STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
24     DEBUG_OBJECT_ALL_ACCESS
25 };
26 
27 static const INFORMATION_CLASS_INFO DbgkpDebugObjectInfoClass[] =
28 {
29     /* DebugObjectUnusedInformation */
30     IQS_SAME(ULONG, ULONG, 0),
31     /* DebugObjectKillProcessOnExitInformation */
32     IQS_SAME(DEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION, ULONG, ICIF_SET),
33 };
34 
35 /* PRIVATE FUNCTIONS *********************************************************/
36 
37 NTSTATUS
38 NTAPI
39 DbgkpQueueMessage(IN PEPROCESS Process,
40                   IN PETHREAD Thread,
41                   IN PDBGKM_MSG Message,
42                   IN ULONG Flags,
43                   IN PDEBUG_OBJECT TargetObject OPTIONAL)
44 {
45     PDEBUG_EVENT DebugEvent;
46     DEBUG_EVENT LocalDebugEvent;
47     PDEBUG_OBJECT DebugObject;
48     NTSTATUS Status;
49     BOOLEAN NewEvent;
50     PAGED_CODE();
51     DBGKTRACE(DBGK_MESSAGE_DEBUG,
52               "Process: %p Thread: %p Message: %p Flags: %lx\n",
53               Process, Thread, Message, Flags);
54 
55     /* Check if we have to allocate a debug event */
56     NewEvent = (Flags & DEBUG_EVENT_NOWAIT) ? TRUE : FALSE;
57     if (NewEvent)
58     {
59         /* Allocate it */
60         DebugEvent = ExAllocatePoolWithTag(NonPagedPool,
61                                            sizeof(DEBUG_EVENT),
62                                            'EgbD');
63         if (!DebugEvent) return STATUS_INSUFFICIENT_RESOURCES;
64 
65         /* Set flags */
66         DebugEvent->Flags = Flags | DEBUG_EVENT_INACTIVE;
67 
68         /* Reference the thread and process */
69         ObReferenceObject(Thread);
70         ObReferenceObject(Process);
71 
72         /* Set the current thread */
73         DebugEvent->BackoutThread = PsGetCurrentThread();
74 
75         /* Set the debug object */
76         DebugObject = TargetObject;
77     }
78     else
79     {
80         /* Use the debug event on the stack */
81         DebugEvent = &LocalDebugEvent;
82         DebugEvent->Flags = Flags;
83 
84         /* Acquire the port lock */
85         ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
86 
87         /* Get the debug object */
88         DebugObject = Process->DebugPort;
89 
90         /* Check what kind of API message this is */
91         switch (Message->ApiNumber)
92         {
93             /* Process or thread creation */
94             case DbgKmCreateThreadApi:
95             case DbgKmCreateProcessApi:
96 
97                 /* Make sure we're not skipping creation messages */
98                 if (Thread->SkipCreationMsg) DebugObject = NULL;
99                 break;
100 
101             /* Process or thread exit */
102             case DbgKmExitThreadApi:
103             case DbgKmExitProcessApi:
104 
105                 /* Make sure we're not skipping exit messages */
106                 if (Thread->SkipTerminationMsg) DebugObject = NULL;
107 
108             /* No special handling for other messages */
109             default:
110                 break;
111         }
112     }
113 
114     /* Setup the Debug Event */
115     KeInitializeEvent(&DebugEvent->ContinueEvent, SynchronizationEvent, FALSE);
116     DebugEvent->Process = Process;
117     DebugEvent->Thread = Thread;
118     DebugEvent->ApiMsg = *Message;
119     DebugEvent->ClientId = Thread->Cid;
120 
121     /* Check if we have a port object */
122     if (!DebugObject)
123     {
124         /* Fail */
125         Status = STATUS_PORT_NOT_SET;
126     }
127     else
128     {
129         /* Acquire the debug object mutex */
130         ExAcquireFastMutex(&DebugObject->Mutex);
131 
132         /* Check if a debugger is active */
133         if (!DebugObject->DebuggerInactive)
134         {
135             /* Add the event into the object's list */
136             DBGKTRACE(DBGK_MESSAGE_DEBUG, "Inserting: %p %d\n",
137                       DebugEvent, Message->ApiNumber);
138             InsertTailList(&DebugObject->EventList, &DebugEvent->EventList);
139 
140             /* Check if we have to signal it */
141             if (!NewEvent)
142             {
143                 /* Signal it */
144                 KeSetEvent(&DebugObject->EventsPresent,
145                            IO_NO_INCREMENT,
146                            FALSE);
147             }
148 
149             /* Set success */
150             Status = STATUS_SUCCESS;
151         }
152         else
153         {
154             /* No debugger */
155             Status = STATUS_DEBUGGER_INACTIVE;
156         }
157 
158         /* Release the object lock */
159         ExReleaseFastMutex(&DebugObject->Mutex);
160     }
161 
162     /* Check if we had acquired the port lock */
163     if (!NewEvent)
164     {
165         /* Release it */
166         ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
167 
168         /* Check if we got here through success */
169         if (NT_SUCCESS(Status))
170         {
171             /* Wait on the continue event */
172             KeWaitForSingleObject(&DebugEvent->ContinueEvent,
173                                   Executive,
174                                   KernelMode,
175                                   FALSE,
176                                   NULL);
177 
178             /* Copy API Message back */
179             *Message = DebugEvent->ApiMsg;
180 
181             /* Set return status */
182             Status = DebugEvent->Status;
183         }
184     }
185     else
186     {
187         /* Check if we failed */
188         if (!NT_SUCCESS(Status))
189         {
190             /* Dereference the process and thread */
191             ObDereferenceObject(Thread);
192             ObDereferenceObject(Process);
193 
194             /* Free the debug event */
195             ExFreePoolWithTag(DebugEvent, 'EgbD');
196         }
197     }
198 
199     /* Return status */
200     DBGKTRACE(DBGK_MESSAGE_DEBUG, "Status: %lx\n", Status);
201     return Status;
202 }
203 
204 NTSTATUS
205 NTAPI
206 DbgkpSendApiMessageLpc(IN OUT PDBGKM_MSG Message,
207                        IN PVOID Port,
208                        IN BOOLEAN SuspendProcess)
209 {
210     NTSTATUS Status;
211     UCHAR Buffer[PORT_MAXIMUM_MESSAGE_LENGTH];
212     BOOLEAN Suspended = FALSE;
213     PAGED_CODE();
214 
215     /* Suspend process if required */
216     if (SuspendProcess) Suspended = DbgkpSuspendProcess();
217 
218     /* Set return status */
219     Message->ReturnedStatus = STATUS_PENDING;
220 
221     /* Set create process reported state */
222     PspSetProcessFlag(PsGetCurrentProcess(), PSF_CREATE_REPORTED_BIT);
223 
224     /* Send the LPC command */
225     Status = LpcRequestWaitReplyPort(Port,
226                                      (PPORT_MESSAGE)Message,
227                                      (PPORT_MESSAGE)&Buffer[0]);
228 
229     /* Flush the instruction cache */
230     ZwFlushInstructionCache(NtCurrentProcess(), NULL, 0);
231 
232     /* Copy the buffer back */
233     if (NT_SUCCESS(Status)) RtlCopyMemory(Message, Buffer, sizeof(DBGKM_MSG));
234 
235     /* Resume the process if it was suspended */
236     if (Suspended) DbgkpResumeProcess();
237     return Status;
238 }
239 
240 NTSTATUS
241 NTAPI
242 DbgkpSendApiMessage(IN OUT PDBGKM_MSG ApiMsg,
243                     IN BOOLEAN SuspendProcess)
244 {
245     NTSTATUS Status;
246     BOOLEAN Suspended = FALSE;
247     PAGED_CODE();
248     DBGKTRACE(DBGK_MESSAGE_DEBUG, "ApiMsg: %p SuspendProcess: %lx\n", ApiMsg, SuspendProcess);
249 
250     /* Suspend process if required */
251     if (SuspendProcess) Suspended = DbgkpSuspendProcess();
252 
253     /* Set return status */
254     ApiMsg->ReturnedStatus = STATUS_PENDING;
255 
256     /* Set create process reported state */
257     PspSetProcessFlag(PsGetCurrentProcess(), PSF_CREATE_REPORTED_BIT);
258 
259     /* Send the LPC command */
260     Status = DbgkpQueueMessage(PsGetCurrentProcess(),
261                                PsGetCurrentThread(),
262                                ApiMsg,
263                                0,
264                                NULL);
265 
266     /* Flush the instruction cache */
267     ZwFlushInstructionCache(NtCurrentProcess(), NULL, 0);
268 
269     /* Resume the process if it was suspended */
270     if (Suspended) DbgkpResumeProcess();
271     return Status;
272 }
273 
274 VOID
275 NTAPI
276 DbgkCopyProcessDebugPort(IN PEPROCESS Process,
277                          IN PEPROCESS Parent)
278 {
279     PDEBUG_OBJECT DebugObject;
280     PAGED_CODE();
281     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Parent: %p\n", Process, Parent);
282 
283     /* Clear this process's port */
284     Process->DebugPort = NULL;
285 
286     /* Check if the parent has one */
287     if (!Parent->DebugPort) return;
288 
289     /* It does, acquire the mutex */
290     ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
291 
292     /* Make sure it still has one, and that we should inherit */
293     DebugObject = Parent->DebugPort;
294     if ((DebugObject) && !(Process->NoDebugInherit))
295     {
296         /* Acquire the debug object's lock */
297         ExAcquireFastMutex(&DebugObject->Mutex);
298 
299         /* Make sure the debugger is active */
300         if (!DebugObject->DebuggerInactive)
301         {
302             /* Reference the object and set it */
303             ObReferenceObject(DebugObject);
304             Process->DebugPort = DebugObject;
305         }
306 
307         /* Release the debug object */
308         ExReleaseFastMutex(&DebugObject->Mutex);
309     }
310 
311     /* Release the port mutex */
312     ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
313 }
314 
315 BOOLEAN
316 NTAPI
317 DbgkForwardException(IN PEXCEPTION_RECORD ExceptionRecord,
318                      IN BOOLEAN DebugPort,
319                      IN BOOLEAN SecondChance)
320 {
321     DBGKM_MSG ApiMessage;
322     PDBGKM_EXCEPTION DbgKmException = &ApiMessage.Exception;
323     NTSTATUS Status;
324     PEPROCESS Process = PsGetCurrentProcess();
325     PVOID Port;
326     BOOLEAN UseLpc = FALSE;
327     PAGED_CODE();
328     DBGKTRACE(DBGK_EXCEPTION_DEBUG,
329               "ExceptionRecord: %p Port: %u\n", ExceptionRecord, DebugPort);
330 
331     /* Setup the API Message */
332     ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
333                              (8 + sizeof(DBGKM_EXCEPTION));
334     ApiMessage.h.u2.ZeroInit = 0;
335     ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
336     ApiMessage.ApiNumber = DbgKmExceptionApi;
337 
338     /* Check if this is to be sent on the debug port */
339     if (DebugPort)
340     {
341         /* Use the debug port, unless the thread is being hidden */
342         Port = PsGetCurrentThread()->HideFromDebugger ?
343                NULL : Process->DebugPort;
344     }
345     else
346     {
347         /* Otherwise, use the exception port */
348         Port = Process->ExceptionPort;
349         ApiMessage.h.u2.ZeroInit = 0;
350         ApiMessage.h.u2.s2.Type = LPC_EXCEPTION;
351         UseLpc = TRUE;
352     }
353 
354     /* Break out if there's no port */
355     if (!Port) return FALSE;
356 
357     /* Fill out the exception information */
358     DbgKmException->ExceptionRecord = *ExceptionRecord;
359     DbgKmException->FirstChance = !SecondChance;
360 
361     /* Check if we should use LPC */
362     if (UseLpc)
363     {
364         /* Send the message on the LPC Port */
365         Status = DbgkpSendApiMessageLpc(&ApiMessage, Port, DebugPort);
366     }
367     else
368     {
369         /* Use native debug object */
370         Status = DbgkpSendApiMessage(&ApiMessage, DebugPort);
371     }
372 
373     /* Check if we failed, and for a debug port, also check the return status */
374     if (!(NT_SUCCESS(Status)) ||
375         ((DebugPort) &&
376          (!(NT_SUCCESS(ApiMessage.ReturnedStatus)) ||
377            (ApiMessage.ReturnedStatus == DBG_EXCEPTION_NOT_HANDLED))))
378     {
379         /* Fail */
380         return FALSE;
381     }
382 
383     /* Otherwise, we're ok */
384     return TRUE;
385 }
386 
387 VOID
388 NTAPI
389 DbgkpFreeDebugEvent(IN PDEBUG_EVENT DebugEvent)
390 {
391     PHANDLE Handle = NULL;
392     PAGED_CODE();
393     DBGKTRACE(DBGK_OBJECT_DEBUG, "DebugEvent: %p\n", DebugEvent);
394 
395     /* Check if this event had a file handle */
396     switch (DebugEvent->ApiMsg.ApiNumber)
397     {
398         /* Create process has a handle */
399         case DbgKmCreateProcessApi:
400 
401             /* Get the pointer */
402             Handle = &DebugEvent->ApiMsg.CreateProcess.FileHandle;
403             break;
404 
405         /* As does DLL load */
406         case DbgKmLoadDllApi:
407 
408             /* Get the pointer */
409             Handle = &DebugEvent->ApiMsg.LoadDll.FileHandle;
410 
411         default:
412             break;
413     }
414 
415     /* Close the handle if it exsts */
416     if ((Handle) && (*Handle)) ObCloseHandle(*Handle, KernelMode);
417 
418     /* Dereference process and thread and free the event */
419     ObDereferenceObject(DebugEvent->Process);
420     ObDereferenceObject(DebugEvent->Thread);
421     ExFreePoolWithTag(DebugEvent, 'EgbD');
422 }
423 
424 VOID
425 NTAPI
426 DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent)
427 {
428     PETHREAD Thread = DebugEvent->Thread;
429     PAGED_CODE();
430     DBGKTRACE(DBGK_OBJECT_DEBUG, "DebugEvent: %p\n", DebugEvent);
431 
432     /* Check if we have to wake the thread */
433     if (DebugEvent->Flags & DEBUG_EVENT_SUSPEND) PsResumeThread(Thread, NULL);
434 
435     /* Check if we had locked the thread */
436     if (DebugEvent->Flags & DEBUG_EVENT_RELEASE)
437     {
438         /* Unlock it */
439         ExReleaseRundownProtection(&Thread->RundownProtect);
440     }
441 
442     /* Check if we have to wake up the event */
443     if (DebugEvent->Flags & DEBUG_EVENT_NOWAIT)
444     {
445         /* Otherwise, free the debug event */
446         DbgkpFreeDebugEvent(DebugEvent);
447     }
448     else
449     {
450         /* Signal the continue event */
451         KeSetEvent(&DebugEvent->ContinueEvent, IO_NO_INCREMENT, FALSE);
452     }
453 }
454 
455 NTSTATUS
456 NTAPI
457 DbgkpPostFakeModuleMessages(IN PEPROCESS Process,
458                             IN PETHREAD Thread,
459                             IN PDEBUG_OBJECT DebugObject)
460 {
461     PPEB Peb = Process->Peb;
462     PPEB_LDR_DATA LdrData;
463     PLDR_DATA_TABLE_ENTRY LdrEntry;
464     PLIST_ENTRY ListHead, NextEntry;
465     DBGKM_MSG ApiMessage;
466     PDBGKM_LOAD_DLL LoadDll = &ApiMessage.LoadDll;
467     ULONG i;
468     PIMAGE_NT_HEADERS NtHeader;
469     UNICODE_STRING ModuleName;
470     OBJECT_ATTRIBUTES ObjectAttributes;
471     IO_STATUS_BLOCK IoStatusBlock;
472     NTSTATUS Status;
473     UNICODE_STRING FullDllName;
474     PAGED_CODE();
475     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Thread: %p DebugObject: %p\n",
476               Process, Thread, DebugObject);
477 
478     /* Quit if there's no PEB */
479     if (!Peb) return STATUS_SUCCESS;
480 
481     /* Accessing user memory, need SEH */
482     _SEH2_TRY
483     {
484         /* Get the Loader Data List */
485         ProbeForRead(Peb, sizeof(*Peb), 1);
486         LdrData = Peb->Ldr;
487         ProbeForRead(LdrData, sizeof(*LdrData), 1);
488         ListHead = &LdrData->InLoadOrderModuleList;
489         ProbeForRead(ListHead, sizeof(*ListHead), 1);
490         NextEntry = ListHead->Flink;
491 
492         /* Loop the modules */
493         i = 0;
494         while ((NextEntry != ListHead) && (i < 500))
495         {
496             ProbeForRead(NextEntry, sizeof(*NextEntry), 1);
497             /* Skip the first entry */
498             if (!i)
499             {
500                 /* Go to the next module */
501                 NextEntry = NextEntry->Flink;
502                 i++;
503                 continue;
504             }
505 
506             /* Get the entry */
507             LdrEntry = CONTAINING_RECORD(NextEntry,
508                                          LDR_DATA_TABLE_ENTRY,
509                                          InLoadOrderLinks);
510             ProbeForRead(LdrEntry, sizeof(*LdrEntry), 1);
511 
512             /* Setup the API Message */
513             RtlZeroMemory(&ApiMessage, sizeof(DBGKM_MSG));
514             ApiMessage.ApiNumber = DbgKmLoadDllApi;
515 
516             /* Set base and clear the name */
517             LoadDll->BaseOfDll = LdrEntry->DllBase;
518             LoadDll->NamePointer = NULL;
519 
520             /* Get the NT Headers */
521             NtHeader = RtlImageNtHeader(LoadDll->BaseOfDll);
522             if (NtHeader)
523             {
524                 /* Save debug data */
525                 LoadDll->DebugInfoFileOffset = NtHeader->FileHeader.
526                                                PointerToSymbolTable;
527                 LoadDll->DebugInfoSize = NtHeader->FileHeader.NumberOfSymbols;
528             }
529 
530             /* Trace */
531             FullDllName = LdrEntry->FullDllName;
532             ProbeForRead(FullDllName.Buffer, FullDllName.MaximumLength, 1);
533             DBGKTRACE(DBGK_PROCESS_DEBUG, "Name: %wZ. Base: %p\n",
534                       &FullDllName, LdrEntry->DllBase);
535 
536             /* Get the name of the DLL */
537             Status = MmGetFileNameForAddress(NtHeader, &ModuleName);
538             if (NT_SUCCESS(Status))
539             {
540                 /* Setup the object attributes */
541                 InitializeObjectAttributes(&ObjectAttributes,
542                                            &ModuleName,
543                                            OBJ_FORCE_ACCESS_CHECK |
544                                            OBJ_KERNEL_HANDLE |
545                                            OBJ_CASE_INSENSITIVE,
546                                            NULL,
547                                            NULL);
548 
549                 /* Open the file to get a handle to it */
550                 Status = ZwOpenFile(&LoadDll->FileHandle,
551                                     GENERIC_READ | SYNCHRONIZE,
552                                     &ObjectAttributes,
553                                     &IoStatusBlock,
554                                     FILE_SHARE_READ |
555                                     FILE_SHARE_WRITE |
556                                     FILE_SHARE_DELETE,
557                                     FILE_SYNCHRONOUS_IO_NONALERT);
558                 if (!NT_SUCCESS(Status)) LoadDll->FileHandle = NULL;
559 
560                 /* Free the name now */
561                 RtlFreeUnicodeString(&ModuleName);
562             }
563 
564             /* Send the fake module load message */
565             Status = DbgkpQueueMessage(Process,
566                                        Thread,
567                                        &ApiMessage,
568                                        DEBUG_EVENT_NOWAIT,
569                                        DebugObject);
570             if (!NT_SUCCESS(Status))
571             {
572                 /* Message send failed, close the file handle if we had one */
573                 if (LoadDll->FileHandle) ObCloseHandle(LoadDll->FileHandle,
574                                                        KernelMode);
575             }
576 
577             /* Go to the next module */
578             NextEntry = NextEntry->Flink;
579             i++;
580         }
581     }
582     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
583     {
584         NOTHING;
585     }
586     _SEH2_END;
587 
588     /* Return success */
589     return STATUS_SUCCESS;
590 }
591 
592 NTSTATUS
593 NTAPI
594 DbgkpPostFakeThreadMessages(IN PEPROCESS Process,
595                             IN PDEBUG_OBJECT DebugObject,
596                             IN PETHREAD StartThread,
597                             OUT PETHREAD *FirstThread,
598                             OUT PETHREAD *LastThread)
599 {
600     PETHREAD pFirstThread = NULL, ThisThread, OldThread = NULL, pLastThread;
601     NTSTATUS Status = STATUS_UNSUCCESSFUL;
602     BOOLEAN IsFirstThread;
603     ULONG Flags;
604     DBGKM_MSG ApiMessage;
605     PDBGKM_CREATE_THREAD CreateThread = &ApiMessage.CreateThread;
606     PDBGKM_CREATE_PROCESS CreateProcess = &ApiMessage.CreateProcess;
607     BOOLEAN First;
608     PIMAGE_NT_HEADERS NtHeader;
609     PAGED_CODE();
610     DBGKTRACE(DBGK_THREAD_DEBUG, "Process: %p StartThread: %p Object: %p\n",
611               Process, StartThread, DebugObject);
612 
613     /* Check if we have a start thread */
614     if (StartThread)
615     {
616         /* Then the one we'll find won't be the first one */
617         IsFirstThread = FALSE;
618         pFirstThread = StartThread;
619         ThisThread = StartThread;
620 
621         /* Reference it */
622         ObReferenceObject(StartThread);
623     }
624     else
625     {
626         /* Get the first thread ourselves */
627         ThisThread = PsGetNextProcessThread(Process, NULL);
628         IsFirstThread = TRUE;
629     }
630 
631     /* Start thread loop */
632     do
633     {
634         /* Dereference the previous thread if we had one */
635         if (OldThread) ObDereferenceObject(OldThread);
636 
637         /* Set this as the last thread and lock it */
638         pLastThread = ThisThread;
639         ObReferenceObject(ThisThread);
640         if (ExAcquireRundownProtection(&ThisThread->RundownProtect))
641         {
642             /* Acquire worked, set flags */
643             Flags = DEBUG_EVENT_RELEASE | DEBUG_EVENT_NOWAIT;
644 
645             /* Check if this is a user thread */
646             if (!ThisThread->SystemThread)
647             {
648                 /* Suspend it */
649                 if (NT_SUCCESS(PsSuspendThread(ThisThread, NULL)))
650                 {
651                     /* Remember this */
652                     Flags |= DEBUG_EVENT_SUSPEND;
653                 }
654             }
655         }
656         else
657         {
658             /* Couldn't acquire rundown */
659             Flags = DEBUG_EVENT_PROTECT_FAILED | DEBUG_EVENT_NOWAIT;
660         }
661 
662         /* Clear the API Message */
663         RtlZeroMemory(&ApiMessage, sizeof(ApiMessage));
664 
665         /* Check if this is the first thread */
666         if ((IsFirstThread) &&
667             !(Flags & DEBUG_EVENT_PROTECT_FAILED) &&
668             !(ThisThread->SystemThread) &&
669             (ThisThread->GrantedAccess))
670         {
671             /* It is, save the flag */
672             First = TRUE;
673         }
674         else
675         {
676             /* It isn't, save the flag */
677             First = FALSE;
678         }
679 
680         /* Check if this is the first */
681         if (First)
682         {
683             /* So we'll start with the create process message */
684             ApiMessage.ApiNumber = DbgKmCreateProcessApi;
685 
686             /* Get the file handle */
687             if (Process->SectionObject)
688             {
689                 /* Use the section object */
690                 CreateProcess->FileHandle =
691                     DbgkpSectionToFileHandle(Process->SectionObject);
692             }
693             else
694             {
695                 /* Don't return any handle */
696                 CreateProcess->FileHandle = NULL;
697             }
698 
699             /* Set the base address */
700             CreateProcess->BaseOfImage = Process->SectionBaseAddress;
701 
702             /* Get the NT Header */
703             NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
704             if (NtHeader)
705             {
706                 /* Fill out data from the header */
707                 CreateProcess->DebugInfoFileOffset = NtHeader->FileHeader.
708                                                      PointerToSymbolTable;
709                 CreateProcess->DebugInfoSize = NtHeader->FileHeader.
710                                                NumberOfSymbols;
711             }
712         }
713         else
714         {
715             /* Otherwise it's a thread message */
716             ApiMessage.ApiNumber = DbgKmCreateThreadApi;
717             CreateThread->StartAddress = ThisThread->StartAddress;
718         }
719 
720         /* Trace */
721         DBGKTRACE(DBGK_THREAD_DEBUG, "Thread: %p. First: %lx, OldThread: %p\n",
722                   ThisThread, First, OldThread);
723         DBGKTRACE(DBGK_THREAD_DEBUG, "Start Address: %p\n",
724                   ThisThread->StartAddress);
725 
726         /* Queue the message */
727         Status = DbgkpQueueMessage(Process,
728                                    ThisThread,
729                                    &ApiMessage,
730                                    Flags,
731                                    DebugObject);
732         if (!NT_SUCCESS(Status))
733         {
734             /* Resume the thread if it was suspended */
735             if (Flags & DEBUG_EVENT_SUSPEND) PsResumeThread(ThisThread, NULL);
736 
737             /* Check if we acquired rundown */
738             if (Flags & DEBUG_EVENT_RELEASE)
739             {
740                 /* Release it */
741                 ExReleaseRundownProtection(&ThisThread->RundownProtect);
742             }
743 
744             /* If this was a process create, check if we got a handle */
745             if ((ApiMessage.ApiNumber == DbgKmCreateProcessApi) &&
746                  (CreateProcess->FileHandle))
747             {
748                 /* Close it */
749                 ObCloseHandle(CreateProcess->FileHandle, KernelMode);
750             }
751 
752             /* Release our reference and break out */
753             ObDereferenceObject(ThisThread);
754             break;
755         }
756 
757         /* Check if this was the first message */
758         if (First)
759         {
760             /* It isn't the first thread anymore */
761             IsFirstThread = FALSE;
762 
763             /* Reference this thread and set it as first */
764             ObReferenceObject(ThisThread);
765             pFirstThread = ThisThread;
766         }
767 
768         /* Get the next thread */
769         ThisThread = PsGetNextProcessThread(Process, ThisThread);
770         OldThread = pLastThread;
771     } while (ThisThread);
772 
773     /* Check the API status */
774     if (!NT_SUCCESS(Status))
775     {
776         /* Dereference and fail */
777         if (pFirstThread) ObDereferenceObject(pFirstThread);
778         ObDereferenceObject(pLastThread);
779         return Status;
780     }
781 
782     /* Make sure we have a first thread */
783     if (!pFirstThread) return STATUS_UNSUCCESSFUL;
784 
785     /* Return thread pointers */
786     *FirstThread = pFirstThread;
787     *LastThread = pLastThread;
788     return Status;
789 }
790 
791 NTSTATUS
792 NTAPI
793 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process,
794                                    IN PDEBUG_OBJECT DebugObject,
795                                    OUT PETHREAD *LastThread)
796 {
797     KAPC_STATE ApcState;
798     PETHREAD FirstThread, FinalThread;
799     PETHREAD ReturnThread = NULL;
800     NTSTATUS Status;
801     PAGED_CODE();
802     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
803               Process, DebugObject);
804 
805     /* Attach to the process */
806     KeStackAttachProcess(&Process->Pcb, &ApcState);
807 
808     /* Post the fake thread messages */
809     Status = DbgkpPostFakeThreadMessages(Process,
810                                          DebugObject,
811                                          NULL,
812                                          &FirstThread,
813                                          &FinalThread);
814     if (NT_SUCCESS(Status))
815     {
816         /* Send the fake module messages too */
817         Status = DbgkpPostFakeModuleMessages(Process,
818                                              FirstThread,
819                                              DebugObject);
820         if (!NT_SUCCESS(Status))
821         {
822             /* We failed, dereference the final thread */
823             ObDereferenceObject(FinalThread);
824         }
825         else
826         {
827             /* Set the final thread */
828             ReturnThread = FinalThread;
829         }
830 
831         /* Dereference the first thread */
832         ObDereferenceObject(FirstThread);
833     }
834 
835     /* Detach from the process */
836     KeUnstackDetachProcess(&ApcState);
837 
838     /* Return the last thread */
839     *LastThread = ReturnThread;
840     return Status;
841 }
842 
843 VOID
844 NTAPI
845 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
846                                     IN PDEBUG_EVENT DebugEvent)
847 {
848     DBGKTRACE(DBGK_OBJECT_DEBUG, "DebugEvent: %p\n", DebugEvent);
849 
850     /* Start by copying the client ID */
851     WaitStateChange->AppClientId = DebugEvent->ClientId;
852 
853     /* Now check which kind of event this was */
854     switch (DebugEvent->ApiMsg.ApiNumber)
855     {
856         /* New process */
857         case DbgKmCreateProcessApi:
858 
859             /* Set the right native code */
860             WaitStateChange->NewState = DbgCreateProcessStateChange;
861 
862             /* Copy the information */
863             WaitStateChange->StateInfo.CreateProcessInfo.NewProcess =
864                 DebugEvent->ApiMsg.CreateProcess;
865 
866             /* Clear the file handle for us */
867             DebugEvent->ApiMsg.CreateProcess.FileHandle = NULL;
868             break;
869 
870         /* New thread */
871         case DbgKmCreateThreadApi:
872 
873             /* Set the right native code */
874             WaitStateChange->NewState = DbgCreateThreadStateChange;
875 
876             /* Copy information */
877             WaitStateChange->StateInfo.CreateThread.NewThread.StartAddress =
878                 DebugEvent->ApiMsg.CreateThread.StartAddress;
879             WaitStateChange->StateInfo.CreateThread.NewThread.SubSystemKey =
880                 DebugEvent->ApiMsg.CreateThread.SubSystemKey;
881             break;
882 
883         /* Exception (or breakpoint/step) */
884         case DbgKmExceptionApi:
885 
886             /* Look at the exception code */
887             if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
888                 STATUS_BREAKPOINT)
889             {
890                 /* Update this as a breakpoint exception */
891                 WaitStateChange->NewState = DbgBreakpointStateChange;
892             }
893             else if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
894                      STATUS_SINGLE_STEP)
895             {
896                 /* Update this as a single step exception */
897                 WaitStateChange->NewState = DbgSingleStepStateChange;
898             }
899             else
900             {
901                 /* Otherwise, set default exception */
902                 WaitStateChange->NewState = DbgExceptionStateChange;
903             }
904 
905             /* Copy the exception record */
906             WaitStateChange->StateInfo.Exception.ExceptionRecord =
907                 DebugEvent->ApiMsg.Exception.ExceptionRecord;
908             /* Copy FirstChance flag */
909             WaitStateChange->StateInfo.Exception.FirstChance =
910                 DebugEvent->ApiMsg.Exception.FirstChance;
911             break;
912 
913         /* Process exited */
914         case DbgKmExitProcessApi:
915 
916             /* Set the right native code and copy the exit code */
917             WaitStateChange->NewState = DbgExitProcessStateChange;
918             WaitStateChange->StateInfo.ExitProcess.ExitStatus =
919                 DebugEvent->ApiMsg.ExitProcess.ExitStatus;
920             break;
921 
922         /* Thread exited */
923         case DbgKmExitThreadApi:
924 
925             /* Set the right native code */
926             WaitStateChange->NewState = DbgExitThreadStateChange;
927             WaitStateChange->StateInfo.ExitThread.ExitStatus =
928                 DebugEvent->ApiMsg.ExitThread.ExitStatus;
929             break;
930 
931         /* DLL Load */
932         case DbgKmLoadDllApi:
933 
934             /* Set the native code */
935             WaitStateChange->NewState = DbgLoadDllStateChange;
936 
937             /* Copy the data */
938             WaitStateChange->StateInfo.LoadDll = DebugEvent->ApiMsg.LoadDll;
939 
940             /* Clear the file handle for us */
941             DebugEvent->ApiMsg.LoadDll.FileHandle = NULL;
942             break;
943 
944         /* DLL Unload */
945         case DbgKmUnloadDllApi:
946 
947             /* Set the native code and copy the address */
948             WaitStateChange->NewState = DbgUnloadDllStateChange;
949             WaitStateChange->StateInfo.UnloadDll.BaseAddress =
950                 DebugEvent->ApiMsg.UnloadDll.BaseAddress;
951             break;
952 
953         default:
954 
955             /* Shouldn't happen */
956             ASSERT(FALSE);
957     }
958 }
959 
960 VOID
961 NTAPI
962 DbgkpMarkProcessPeb(IN PEPROCESS Process)
963 {
964     KAPC_STATE ApcState;
965     PAGED_CODE();
966     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p\n", Process);
967 
968     /* Acquire process rundown */
969     if (!ExAcquireRundownProtection(&Process->RundownProtect)) return;
970 
971     /* Make sure we have a PEB */
972     if (Process->Peb)
973     {
974         /* Attach to the process */
975         KeStackAttachProcess(&Process->Pcb, &ApcState);
976 
977         /* Acquire the debug port mutex */
978         ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
979 
980         /* Set the IsBeingDebugged member of the PEB */
981         Process->Peb->BeingDebugged = (Process->DebugPort) ? TRUE: FALSE;
982 
983         /* Release lock */
984         ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
985 
986         /* Detach from the process */
987         KeUnstackDetachProcess(&ApcState);
988     }
989 
990     /* Release rundown protection */
991     ExReleaseRundownProtection(&Process->RundownProtect);
992 }
993 
994 VOID
995 NTAPI
996 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
997                  IN PEPROCESS Process,
998                  IN PETHREAD Thread)
999 {
1000     NTSTATUS Status;
1001     HANDLE Handle;
1002     PHANDLE DupHandle;
1003     PAGED_CODE();
1004     DBGKTRACE(DBGK_OBJECT_DEBUG, "Process: %p Thread: %p State: %lx\n",
1005               Process, Thread, WaitStateChange->NewState);
1006 
1007     /* Check which state this is */
1008     switch (WaitStateChange->NewState)
1009     {
1010         /* New thread */
1011         case DbgCreateThreadStateChange:
1012 
1013             /* Get handle to thread */
1014             Status = ObOpenObjectByPointer(Thread,
1015                                            0,
1016                                            NULL,
1017                                            THREAD_ALL_ACCESS,
1018                                            PsThreadType,
1019                                            KernelMode,
1020                                            &Handle);
1021             if (NT_SUCCESS(Status))
1022             {
1023                 /* Save the thread handle */
1024                 WaitStateChange->
1025                     StateInfo.CreateThread.HandleToThread = Handle;
1026             }
1027             return;
1028 
1029         /* New process */
1030         case DbgCreateProcessStateChange:
1031 
1032             /* Get handle to thread */
1033             Status = ObOpenObjectByPointer(Thread,
1034                                            0,
1035                                            NULL,
1036                                            THREAD_ALL_ACCESS,
1037                                            PsThreadType,
1038                                            KernelMode,
1039                                            &Handle);
1040             if (NT_SUCCESS(Status))
1041             {
1042                 /* Save the thread handle */
1043                 WaitStateChange->
1044                     StateInfo.CreateProcessInfo.HandleToThread = Handle;
1045             }
1046 
1047             /* Get handle to process */
1048             Status = ObOpenObjectByPointer(Process,
1049                                            0,
1050                                            NULL,
1051                                            PROCESS_ALL_ACCESS,
1052                                            PsProcessType,
1053                                            KernelMode,
1054                                            &Handle);
1055             if (NT_SUCCESS(Status))
1056             {
1057                 /* Save the process handle */
1058                 WaitStateChange->
1059                     StateInfo.CreateProcessInfo.HandleToProcess = Handle;
1060             }
1061 
1062             /* Fall through to duplicate file handle */
1063             DupHandle = &WaitStateChange->
1064                             StateInfo.CreateProcessInfo.NewProcess.FileHandle;
1065             break;
1066 
1067         /* DLL Load */
1068         case DbgLoadDllStateChange:
1069 
1070             /* Fall through to duplicate file handle */
1071             DupHandle = &WaitStateChange->StateInfo.LoadDll.FileHandle;
1072             break;
1073 
1074         /* Anything else has no handles */
1075         default:
1076             return;
1077     }
1078 
1079     /* If we got here, then we have to duplicate a handle, possibly */
1080     Handle = *DupHandle;
1081     if (Handle)
1082     {
1083         /* Duplicate it */
1084         Status = ObDuplicateObject(PsGetCurrentProcess(),
1085                                    Handle,
1086                                    PsGetCurrentProcess(),
1087                                    DupHandle,
1088                                    0,
1089                                    0,
1090                                    DUPLICATE_SAME_ACCESS,
1091                                    KernelMode);
1092         if (!NT_SUCCESS(Status)) *DupHandle = NULL;
1093 
1094         /* Close the original handle */
1095         ObCloseHandle(Handle, KernelMode);
1096     }
1097 }
1098 
1099 VOID
1100 NTAPI
1101 DbgkpDeleteObject(IN PVOID DebugObject)
1102 {
1103     PAGED_CODE();
1104 
1105     /* Sanity check */
1106     ASSERT(IsListEmpty(&((PDEBUG_OBJECT)DebugObject)->EventList));
1107 }
1108 
1109 VOID
1110 NTAPI
1111 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL,
1112                  IN PVOID ObjectBody,
1113                  IN ACCESS_MASK GrantedAccess,
1114                  IN ULONG HandleCount,
1115                  IN ULONG SystemHandleCount)
1116 {
1117     PDEBUG_OBJECT DebugObject = ObjectBody;
1118     PEPROCESS Process = NULL;
1119     BOOLEAN DebugPortCleared = FALSE;
1120     PLIST_ENTRY DebugEventList;
1121     PDEBUG_EVENT DebugEvent;
1122     PAGED_CODE();
1123     DBGKTRACE(DBGK_OBJECT_DEBUG, "OwnerProcess: %p DebugObject: %p\n",
1124               OwnerProcess, DebugObject);
1125 
1126     /* If this isn't the last handle, do nothing */
1127     if (SystemHandleCount > 1) return;
1128 
1129     /* Otherwise, lock the debug object */
1130     ExAcquireFastMutex(&DebugObject->Mutex);
1131 
1132     /* Set it as inactive */
1133     DebugObject->DebuggerInactive = TRUE;
1134 
1135     /* Remove it from the debug event list */
1136     DebugEventList = DebugObject->EventList.Flink;
1137     InitializeListHead(&DebugObject->EventList);
1138 
1139     /* Release the lock */
1140     ExReleaseFastMutex(&DebugObject->Mutex);
1141 
1142     /* Signal the wait event */
1143     KeSetEvent(&DebugObject->EventsPresent, IO_NO_INCREMENT, FALSE);
1144 
1145     /* Start looping each process */
1146     while ((Process = PsGetNextProcess(Process)))
1147     {
1148         /* Check if the process has us as their debug port */
1149         if (Process->DebugPort == DebugObject)
1150         {
1151             /* Acquire the process debug port lock */
1152             ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1153 
1154             /* Check if it's still us */
1155             if (Process->DebugPort == DebugObject)
1156             {
1157                 /* Clear it and remember */
1158                 Process->DebugPort = NULL;
1159                 DebugPortCleared = TRUE;
1160             }
1161 
1162             /* Release the port lock */
1163             ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1164 
1165             /* Check if we cleared the debug port */
1166             if (DebugPortCleared)
1167             {
1168                 /* Mark this in the PEB */
1169                 DbgkpMarkProcessPeb(Process);
1170 
1171                 /* Check if we terminate on exit */
1172                 if (DebugObject->KillProcessOnExit)
1173                 {
1174                     /* Terminate the process */
1175                     PsTerminateProcess(Process, STATUS_DEBUGGER_INACTIVE);
1176                 }
1177 
1178                 /* Dereference the debug object */
1179                 ObDereferenceObject(DebugObject);
1180             }
1181         }
1182     }
1183 
1184     /* Loop debug events */
1185     while (DebugEventList != &DebugObject->EventList)
1186     {
1187         /* Get the debug event */
1188         DebugEvent = CONTAINING_RECORD(DebugEventList, DEBUG_EVENT, EventList);
1189 
1190         /* Go to the next entry */
1191         DebugEventList = DebugEventList->Flink;
1192 
1193         /* Wake it up */
1194         DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
1195         DbgkpWakeTarget(DebugEvent);
1196     }
1197 }
1198 
1199 NTSTATUS
1200 NTAPI
1201 DbgkpSetProcessDebugObject(IN PEPROCESS Process,
1202                            IN PDEBUG_OBJECT DebugObject,
1203                            IN NTSTATUS MsgStatus,
1204                            IN PETHREAD LastThread)
1205 {
1206     NTSTATUS Status;
1207     LIST_ENTRY TempList;
1208     BOOLEAN GlobalHeld = FALSE, DoSetEvent = TRUE;
1209     PETHREAD ThisThread, FirstThread;
1210     PLIST_ENTRY NextEntry;
1211     PDEBUG_EVENT DebugEvent;
1212     PETHREAD EventThread;
1213     PAGED_CODE();
1214     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
1215               Process, DebugObject);
1216 
1217     /* Initialize the temporary list */
1218     InitializeListHead(&TempList);
1219 
1220     /* Check if we have a success message */
1221     if (NT_SUCCESS(MsgStatus))
1222     {
1223         /* Then default to STATUS_SUCCESS */
1224         Status = STATUS_SUCCESS;
1225     }
1226     else
1227     {
1228         /* No last thread, and set the failure code */
1229         LastThread = NULL;
1230         Status = MsgStatus;
1231     }
1232 
1233     /* Now check what status we have here */
1234     if (NT_SUCCESS(Status))
1235     {
1236         /* Acquire the global lock */
1237 ThreadScan:
1238         GlobalHeld = TRUE;
1239         ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1240 
1241         /* Check if we already have a port */
1242         if (Process->DebugPort)
1243         {
1244             /* Set failure */
1245             Status = STATUS_PORT_ALREADY_SET;
1246         }
1247         else
1248         {
1249             /* Otherwise, set the port and reference the thread */
1250             Process->DebugPort = DebugObject;
1251             ObReferenceObject(LastThread);
1252 
1253             /* Get the next thread */
1254             ThisThread  = PsGetNextProcessThread(Process, LastThread);
1255             if (ThisThread)
1256             {
1257                 /* Clear the debug port and release the lock */
1258                 Process->DebugPort = NULL;
1259                 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1260                 GlobalHeld = FALSE;
1261 
1262                 /* Dereference the thread */
1263                 ObDereferenceObject(LastThread);
1264 
1265                 /* Post fake messages */
1266                 Status = DbgkpPostFakeThreadMessages(Process,
1267                                                      DebugObject,
1268                                                      ThisThread,
1269                                                      &FirstThread,
1270                                                      &LastThread);
1271                 if (!NT_SUCCESS(Status))
1272                 {
1273                     /* Clear the last thread */
1274                     LastThread = NULL;
1275                 }
1276                 else
1277                 {
1278                     /* Dereference the first thread and re-acquire the lock */
1279                     ObDereferenceObject(FirstThread);
1280                     goto ThreadScan;
1281                 }
1282             }
1283         }
1284     }
1285 
1286     /* Acquire the debug object's lock */
1287     ExAcquireFastMutex(&DebugObject->Mutex);
1288 
1289     /* Check our status here */
1290     if (NT_SUCCESS(Status))
1291     {
1292         /* Check if we're disconnected */
1293         if (DebugObject->DebuggerInactive)
1294         {
1295             /* Set status */
1296             Process->DebugPort = NULL;
1297             Status = STATUS_DEBUGGER_INACTIVE;
1298         }
1299         else
1300         {
1301             /* Set the process flags */
1302             PspSetProcessFlag(Process,
1303                               PSF_NO_DEBUG_INHERIT_BIT |
1304                               PSF_CREATE_REPORTED_BIT);
1305 
1306             /* Reference the debug object */
1307             ObReferenceObject(DebugObject);
1308         }
1309     }
1310 
1311     /* Loop the events list */
1312     NextEntry = DebugObject->EventList.Flink;
1313     while (NextEntry != &DebugObject->EventList)
1314     {
1315         /* Get the debug event and go to the next entry */
1316         DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1317         NextEntry = NextEntry->Flink;
1318         DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1319                   DebugEvent, DebugEvent->Flags,
1320                   DebugEvent->BackoutThread, PsGetCurrentThread());
1321 
1322         /* Check for if the debug event queue needs flushing */
1323         if ((DebugEvent->Flags & DEBUG_EVENT_INACTIVE) &&
1324             (DebugEvent->BackoutThread == PsGetCurrentThread()))
1325         {
1326             /* Get the event's thread */
1327             EventThread = DebugEvent->Thread;
1328             DBGKTRACE(DBGK_PROCESS_DEBUG, "EventThread: %p MsgStatus: %lx\n",
1329                       EventThread, MsgStatus);
1330 
1331             /* Check if the status is success */
1332             if ((MsgStatus == STATUS_SUCCESS) &&
1333                 (EventThread->GrantedAccess) &&
1334                 (!EventThread->SystemThread))
1335             {
1336                 /* Check if we couldn't acquire rundown for it */
1337                 if (DebugEvent->Flags & DEBUG_EVENT_PROTECT_FAILED)
1338                 {
1339                     /* Set the skip termination flag */
1340                     PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1341 
1342                     /* Insert it into the temp list */
1343                     RemoveEntryList(&DebugEvent->EventList);
1344                     InsertTailList(&TempList, &DebugEvent->EventList);
1345                 }
1346                 else
1347                 {
1348                     /* Do we need to signal the event */
1349                     if (DoSetEvent)
1350                     {
1351                         /* Do it */
1352                         DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1353                         KeSetEvent(&DebugObject->EventsPresent,
1354                                    IO_NO_INCREMENT,
1355                                    FALSE);
1356                         DoSetEvent = FALSE;
1357                     }
1358 
1359                     /* Clear the backout thread */
1360                     DebugEvent->BackoutThread = NULL;
1361 
1362                     /* Set skip flag */
1363                     PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1364                 }
1365             }
1366             else
1367             {
1368                 /* Insert it into the temp list */
1369                 RemoveEntryList(&DebugEvent->EventList);
1370                 InsertTailList(&TempList, &DebugEvent->EventList);
1371             }
1372 
1373             /* Check if the lock is held */
1374             if (DebugEvent->Flags & DEBUG_EVENT_RELEASE)
1375             {
1376                 /* Release it */
1377                 DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
1378                 ExReleaseRundownProtection(&EventThread->RundownProtect);
1379             }
1380         }
1381     }
1382 
1383     /* Release the debug object */
1384     ExReleaseFastMutex(&DebugObject->Mutex);
1385 
1386     /* Release the global lock if acquired */
1387     if (GlobalHeld) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1388 
1389     /* Check if there's a thread to dereference */
1390     if (LastThread) ObDereferenceObject(LastThread);
1391 
1392     /* Loop our temporary list */
1393     while (!IsListEmpty(&TempList))
1394     {
1395         /* Remove the event */
1396         NextEntry = RemoveHeadList(&TempList);
1397         DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1398 
1399         /* Wake it */
1400         DbgkpWakeTarget(DebugEvent);
1401     }
1402 
1403     /* Check if we got here through success and mark the PEB, then return */
1404     if (NT_SUCCESS(Status)) DbgkpMarkProcessPeb(Process);
1405     return Status;
1406 }
1407 
1408 NTSTATUS
1409 NTAPI
1410 DbgkClearProcessDebugObject(IN PEPROCESS Process,
1411                             IN PDEBUG_OBJECT SourceDebugObject OPTIONAL)
1412 {
1413     PDEBUG_OBJECT DebugObject;
1414     PDEBUG_EVENT DebugEvent;
1415     LIST_ENTRY TempList;
1416     PLIST_ENTRY NextEntry;
1417     PAGED_CODE();
1418     DBGKTRACE(DBGK_OBJECT_DEBUG, "Process: %p DebugObject: %p\n",
1419               Process, SourceDebugObject);
1420 
1421     /* Acquire the port lock */
1422     ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1423 
1424     /* Get the Process Debug Object */
1425     DebugObject = Process->DebugPort;
1426 
1427     /*
1428      * Check if the process had an object and it matches,
1429      * or if the process had an object but none was specified
1430      * (in which we are called from NtTerminateProcess)
1431      */
1432     if ((DebugObject) &&
1433         ((DebugObject == SourceDebugObject) ||
1434          (SourceDebugObject == NULL)))
1435     {
1436         /* Clear the debug port */
1437         Process->DebugPort = NULL;
1438 
1439         /* Release the port lock and remove the PEB flag */
1440         ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1441         DbgkpMarkProcessPeb(Process);
1442     }
1443     else
1444     {
1445         /* Release the port lock and fail */
1446         ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1447         return STATUS_PORT_NOT_SET;
1448     }
1449 
1450     /* Initialize the temporary list */
1451     InitializeListHead(&TempList);
1452 
1453     /* Acquire the Object */
1454     ExAcquireFastMutex(&DebugObject->Mutex);
1455 
1456     /* Loop the events */
1457     NextEntry = DebugObject->EventList.Flink;
1458     while (NextEntry != &DebugObject->EventList)
1459     {
1460         /* Get the Event and go to the next entry */
1461         DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1462         NextEntry = NextEntry->Flink;
1463 
1464         /* Check that it belongs to the specified process */
1465         if (DebugEvent->Process == Process)
1466         {
1467             /* Insert it into the temporary list */
1468             RemoveEntryList(&DebugEvent->EventList);
1469             InsertTailList(&TempList, &DebugEvent->EventList);
1470         }
1471     }
1472 
1473     /* Release the Object */
1474     ExReleaseFastMutex(&DebugObject->Mutex);
1475 
1476     /* Release the initial reference */
1477     ObDereferenceObject(DebugObject);
1478 
1479     /* Loop our temporary list */
1480     while (!IsListEmpty(&TempList))
1481     {
1482         /* Remove the event */
1483         NextEntry = RemoveHeadList(&TempList);
1484         DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1485 
1486         /* Wake it up */
1487         DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
1488         DbgkpWakeTarget(DebugEvent);
1489     }
1490 
1491     /* Return Success */
1492     return STATUS_SUCCESS;
1493 }
1494 
1495 CODE_SEG("INIT")
1496 VOID
1497 NTAPI
1498 DbgkInitialize(VOID)
1499 {
1500     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1501     UNICODE_STRING Name;
1502     PAGED_CODE();
1503 
1504     /* Initialize the process debug port mutex */
1505     ExInitializeFastMutex(&DbgkpProcessDebugPortMutex);
1506 
1507     /* Create the Debug Object Type */
1508     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1509     RtlInitUnicodeString(&Name, L"DebugObject");
1510     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1511     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DEBUG_OBJECT);
1512     ObjectTypeInitializer.GenericMapping = DbgkDebugObjectMapping;
1513     ObjectTypeInitializer.PoolType = NonPagedPool;
1514     ObjectTypeInitializer.ValidAccessMask = DEBUG_OBJECT_ALL_ACCESS;
1515     ObjectTypeInitializer.SecurityRequired = TRUE;
1516     ObjectTypeInitializer.CloseProcedure = DbgkpCloseObject;
1517     ObjectTypeInitializer.DeleteProcedure = DbgkpDeleteObject;
1518     ObCreateObjectType(&Name,
1519                        &ObjectTypeInitializer,
1520                        NULL,
1521                        &DbgkDebugObjectType);
1522 }
1523 
1524 NTSTATUS
1525 NTAPI
1526 DbgkOpenProcessDebugPort(IN PEPROCESS Process,
1527                          IN KPROCESSOR_MODE PreviousMode,
1528                          OUT HANDLE *DebugHandle)
1529 {
1530     PDEBUG_OBJECT DebugObject;
1531     NTSTATUS Status;
1532     PAGED_CODE();
1533 
1534     /* If there's no debug port, just exit */
1535     if (!Process->DebugPort) return STATUS_PORT_NOT_SET;
1536 
1537     /* Otherwise, acquire the lock while we grab the port */
1538     ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1539 
1540     /* Grab it and reference it if it exists */
1541     DebugObject = Process->DebugPort;
1542     if (DebugObject) ObReferenceObject(DebugObject);
1543 
1544     /* Release the lock now */
1545     ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1546 
1547     /* Bail out if it doesn't exist */
1548     if (!DebugObject) return STATUS_PORT_NOT_SET;
1549 
1550     /* Now get a handle to it */
1551     Status = ObOpenObjectByPointer(DebugObject,
1552                                    0,
1553                                    NULL,
1554                                    MAXIMUM_ALLOWED,
1555                                    DbgkDebugObjectType,
1556                                    PreviousMode,
1557                                    DebugHandle);
1558     if (!NT_SUCCESS(Status)) ObDereferenceObject(DebugObject);
1559 
1560     /* Return status */
1561     return Status;
1562 }
1563 
1564 /* PUBLIC FUNCTIONS **********************************************************/
1565 
1566 /*
1567  * @implemented
1568  */
1569 NTSTATUS
1570 NTAPI
1571 NtCreateDebugObject(OUT PHANDLE DebugHandle,
1572                     IN ACCESS_MASK DesiredAccess,
1573                     IN POBJECT_ATTRIBUTES ObjectAttributes,
1574                     IN ULONG Flags)
1575 {
1576     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1577     PDEBUG_OBJECT DebugObject;
1578     HANDLE hDebug;
1579     NTSTATUS Status;
1580     PAGED_CODE();
1581 
1582     /* Check if we were called from user mode*/
1583     if (PreviousMode != KernelMode)
1584     {
1585         /* Enter SEH for probing */
1586         _SEH2_TRY
1587         {
1588             /* Probe the handle */
1589             ProbeForWriteHandle(DebugHandle);
1590         }
1591         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1592         {
1593             /* Return the exception code */
1594             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1595         } _SEH2_END;
1596     }
1597 
1598     /* Check for invalid flags */
1599     if (Flags & ~DBGK_ALL_FLAGS) return STATUS_INVALID_PARAMETER;
1600 
1601     /* Create the Object */
1602     Status = ObCreateObject(PreviousMode,
1603                             DbgkDebugObjectType,
1604                             ObjectAttributes,
1605                             PreviousMode,
1606                             NULL,
1607                             sizeof(DEBUG_OBJECT),
1608                             0,
1609                             0,
1610                             (PVOID*)&DebugObject);
1611     if (NT_SUCCESS(Status))
1612     {
1613         /* Initialize the Debug Object's Fast Mutex */
1614         ExInitializeFastMutex(&DebugObject->Mutex);
1615 
1616         /* Initialize the State Event List */
1617         InitializeListHead(&DebugObject->EventList);
1618 
1619         /* Initialize the Debug Object's Wait Event */
1620         KeInitializeEvent(&DebugObject->EventsPresent,
1621                           NotificationEvent,
1622                           FALSE);
1623 
1624         /* Set the Flags */
1625         DebugObject->Flags = 0;
1626         if (Flags & DBGK_KILL_PROCESS_ON_EXIT)
1627         {
1628             DebugObject->KillProcessOnExit = TRUE;
1629         }
1630 
1631         /* Insert it */
1632         Status = ObInsertObject((PVOID)DebugObject,
1633                                  NULL,
1634                                  DesiredAccess,
1635                                  0,
1636                                  NULL,
1637                                  &hDebug);
1638         if (NT_SUCCESS(Status))
1639         {
1640             /* Enter SEH to protect the write */
1641             _SEH2_TRY
1642             {
1643                 /* Return the handle */
1644                 *DebugHandle = hDebug;
1645             }
1646             _SEH2_EXCEPT(ExSystemExceptionFilter())
1647             {
1648                 /* Get the exception code */
1649                 Status = _SEH2_GetExceptionCode();
1650             } _SEH2_END;
1651         }
1652     }
1653 
1654     /* Return Status */
1655     DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p DebugObject: %p\n",
1656               hDebug, DebugObject);
1657     return Status;
1658 }
1659 
1660 /*
1661  * @implemented
1662  */
1663 NTSTATUS
1664 NTAPI
1665 NtDebugContinue(IN HANDLE DebugHandle,
1666                 IN PCLIENT_ID AppClientId,
1667                 IN NTSTATUS ContinueStatus)
1668 {
1669     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1670     PDEBUG_OBJECT DebugObject;
1671     NTSTATUS Status;
1672     PDEBUG_EVENT DebugEvent = NULL, DebugEventToWake = NULL;
1673     PLIST_ENTRY ListHead, NextEntry;
1674     BOOLEAN NeedsWake = FALSE;
1675     CLIENT_ID ClientId;
1676     PAGED_CODE();
1677     DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p Status: %d\n",
1678               DebugHandle, ContinueStatus);
1679 
1680     /* Check if we were called from user mode*/
1681     if (PreviousMode != KernelMode)
1682     {
1683         /* Enter SEH for probing */
1684         _SEH2_TRY
1685         {
1686             /* Probe the handle */
1687             ProbeForRead(AppClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1688             ClientId = *AppClientId;
1689             AppClientId = &ClientId;
1690         }
1691         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1692         {
1693             /* Return the exception code */
1694             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1695         } _SEH2_END;
1696     }
1697 
1698     /* Make sure that the status is valid */
1699     if ((ContinueStatus != DBG_CONTINUE) &&
1700         (ContinueStatus != DBG_EXCEPTION_HANDLED) &&
1701         (ContinueStatus != DBG_EXCEPTION_NOT_HANDLED) &&
1702         (ContinueStatus != DBG_TERMINATE_THREAD) &&
1703         (ContinueStatus != DBG_TERMINATE_PROCESS))
1704     {
1705         /* Invalid status */
1706         Status = STATUS_INVALID_PARAMETER;
1707     }
1708     else
1709     {
1710         /* Get the debug object */
1711         Status = ObReferenceObjectByHandle(DebugHandle,
1712                                            DEBUG_OBJECT_WAIT_STATE_CHANGE,
1713                                            DbgkDebugObjectType,
1714                                            PreviousMode,
1715                                            (PVOID*)&DebugObject,
1716                                            NULL);
1717         if (NT_SUCCESS(Status))
1718         {
1719             /* Acquire the mutex */
1720             ExAcquireFastMutex(&DebugObject->Mutex);
1721 
1722             /* Loop the state list */
1723             ListHead = &DebugObject->EventList;
1724             NextEntry = ListHead->Flink;
1725             while (ListHead != NextEntry)
1726             {
1727                 /* Get the current debug event */
1728                 DebugEvent = CONTAINING_RECORD(NextEntry,
1729                                                DEBUG_EVENT,
1730                                                EventList);
1731 
1732                 /* Compare process ID */
1733                 if (DebugEvent->ClientId.UniqueProcess ==
1734                     AppClientId->UniqueProcess)
1735                 {
1736                     /* Check if we already found a match */
1737                     if (NeedsWake)
1738                     {
1739                         /* Wake it up and break out */
1740                         DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1741                         KeSetEvent(&DebugObject->EventsPresent,
1742                                    IO_NO_INCREMENT,
1743                                    FALSE);
1744                         break;
1745                     }
1746 
1747                     /* Compare thread ID and flag */
1748                     if ((DebugEvent->ClientId.UniqueThread ==
1749                         AppClientId->UniqueThread) && (DebugEvent->Flags & DEBUG_EVENT_READ))
1750                     {
1751                         /* Remove the event from the list */
1752                         RemoveEntryList(NextEntry);
1753 
1754                         /* Remember who to wake */
1755                         NeedsWake = TRUE;
1756                         DebugEventToWake = DebugEvent;
1757                     }
1758                 }
1759 
1760                 /* Go to the next entry */
1761                 NextEntry = NextEntry->Flink;
1762             }
1763 
1764             /* Release the mutex */
1765             ExReleaseFastMutex(&DebugObject->Mutex);
1766 
1767             /* Dereference the object */
1768             ObDereferenceObject(DebugObject);
1769 
1770             /* Check if need a wait */
1771             if (NeedsWake)
1772             {
1773                 /* Set the continue status */
1774                 DebugEventToWake->ApiMsg.ReturnedStatus = ContinueStatus;
1775                 DebugEventToWake->Status = STATUS_SUCCESS;
1776 
1777                 /* Wake the target */
1778                 DbgkpWakeTarget(DebugEventToWake);
1779             }
1780             else
1781             {
1782                 /* Fail */
1783                 Status = STATUS_INVALID_PARAMETER;
1784             }
1785         }
1786     }
1787 
1788     /* Return status */
1789     return Status;
1790 }
1791 
1792 /*
1793  * @implemented
1794  */
1795 NTSTATUS
1796 NTAPI
1797 NtDebugActiveProcess(IN HANDLE ProcessHandle,
1798                      IN HANDLE DebugHandle)
1799 {
1800     PEPROCESS Process;
1801     PDEBUG_OBJECT DebugObject;
1802     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1803     PETHREAD LastThread;
1804     NTSTATUS Status;
1805     PAGED_CODE();
1806     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1807               ProcessHandle, DebugHandle);
1808 
1809     /* Reference the process */
1810     Status = ObReferenceObjectByHandle(ProcessHandle,
1811                                        PROCESS_SUSPEND_RESUME,
1812                                        PsProcessType,
1813                                        PreviousMode,
1814                                        (PVOID*)&Process,
1815                                        NULL);
1816     if (!NT_SUCCESS(Status)) return Status;
1817 
1818     /* Don't allow debugging the current process or the system process */
1819     if ((Process == PsGetCurrentProcess()) ||
1820          (Process == PsInitialSystemProcess))
1821     {
1822         /* Dereference and fail */
1823         ObDereferenceObject(Process);
1824         return STATUS_ACCESS_DENIED;
1825     }
1826 
1827     /* Reference the debug object */
1828     Status = ObReferenceObjectByHandle(DebugHandle,
1829                                        DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1830                                        DbgkDebugObjectType,
1831                                        PreviousMode,
1832                                        (PVOID*)&DebugObject,
1833                                        NULL);
1834     if (!NT_SUCCESS(Status))
1835     {
1836         /* Dereference the process and exit */
1837         ObDereferenceObject(Process);
1838         return Status;
1839     }
1840 
1841     /* Acquire process rundown protection */
1842     if (!ExAcquireRundownProtection(&Process->RundownProtect))
1843     {
1844         /* Dereference the process and debug object and exit */
1845         ObDereferenceObject(Process);
1846         ObDereferenceObject(DebugObject);
1847         return STATUS_PROCESS_IS_TERMINATING;
1848     }
1849 
1850     /* Send fake create messages for debuggers to have a consistent state */
1851     Status = DbgkpPostFakeProcessCreateMessages(Process,
1852                                                 DebugObject,
1853                                                 &LastThread);
1854     Status = DbgkpSetProcessDebugObject(Process,
1855                                         DebugObject,
1856                                         Status,
1857                                         LastThread);
1858 
1859     /* Release rundown protection */
1860     ExReleaseRundownProtection(&Process->RundownProtect);
1861 
1862     /* Dereference the process and debug object and return status */
1863     ObDereferenceObject(Process);
1864     ObDereferenceObject(DebugObject);
1865     return Status;
1866 }
1867 
1868 /*
1869  * @implemented
1870  */
1871 NTSTATUS
1872 NTAPI
1873 NtRemoveProcessDebug(IN HANDLE ProcessHandle,
1874                      IN HANDLE DebugHandle)
1875 {
1876     PEPROCESS Process;
1877     PDEBUG_OBJECT DebugObject;
1878     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1879     NTSTATUS Status;
1880     PAGED_CODE();
1881     DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1882               ProcessHandle, DebugHandle);
1883 
1884     /* Reference the process */
1885     Status = ObReferenceObjectByHandle(ProcessHandle,
1886                                        PROCESS_SUSPEND_RESUME,
1887                                        PsProcessType,
1888                                        PreviousMode,
1889                                        (PVOID*)&Process,
1890                                        NULL);
1891     if (!NT_SUCCESS(Status)) return Status;
1892 
1893     /* Reference the debug object */
1894     Status = ObReferenceObjectByHandle(DebugHandle,
1895                                        DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1896                                        DbgkDebugObjectType,
1897                                        PreviousMode,
1898                                        (PVOID*)&DebugObject,
1899                                        NULL);
1900     if (!NT_SUCCESS(Status))
1901     {
1902         /* Dereference the process and exit */
1903         ObDereferenceObject(Process);
1904         return Status;
1905     }
1906 
1907     /* Remove the debug object */
1908     Status = DbgkClearProcessDebugObject(Process, DebugObject);
1909 
1910     /* Dereference the process and debug object and return status */
1911     ObDereferenceObject(Process);
1912     ObDereferenceObject(DebugObject);
1913     return Status;
1914 }
1915 
1916 /*
1917  * @implemented
1918  */
1919 NTSTATUS
1920 NTAPI
1921 NtSetInformationDebugObject(IN HANDLE DebugHandle,
1922                             IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,
1923                             IN PVOID DebugInformation,
1924                             IN ULONG DebugInformationLength,
1925                             OUT PULONG ReturnLength OPTIONAL)
1926 {
1927     PDEBUG_OBJECT DebugObject;
1928     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1929     NTSTATUS Status;
1930     PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo = DebugInformation;
1931     PAGED_CODE();
1932 
1933     /* Check buffers and parameters */
1934     Status = DefaultSetInfoBufferCheck(DebugObjectInformationClass,
1935                                        DbgkpDebugObjectInfoClass,
1936                                        sizeof(DbgkpDebugObjectInfoClass) /
1937                                        sizeof(DbgkpDebugObjectInfoClass[0]),
1938                                        DebugInformation,
1939                                        DebugInformationLength,
1940                                        PreviousMode);
1941     if (!NT_SUCCESS(Status)) return Status;
1942 
1943     /* Check if the caller wanted the return length */
1944     if (ReturnLength)
1945     {
1946         /* Enter SEH for probe */
1947         _SEH2_TRY
1948         {
1949             /* Return required length to user-mode */
1950             ProbeForWriteUlong(ReturnLength);
1951             *ReturnLength = sizeof(*DebugInfo);
1952         }
1953         _SEH2_EXCEPT(ExSystemExceptionFilter())
1954         {
1955             /* Return the exception code */
1956             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1957         }
1958         _SEH2_END;
1959     }
1960 
1961     /* Open the Object */
1962     Status = ObReferenceObjectByHandle(DebugHandle,
1963                                        DEBUG_OBJECT_WAIT_STATE_CHANGE,
1964                                        DbgkDebugObjectType,
1965                                        PreviousMode,
1966                                        (PVOID*)&DebugObject,
1967                                        NULL);
1968     if (NT_SUCCESS(Status))
1969     {
1970         /* Acquire the object */
1971         ExAcquireFastMutex(&DebugObject->Mutex);
1972 
1973         /* Set the proper flag */
1974         if (DebugInfo->KillProcessOnExit)
1975         {
1976             /* Enable killing the process */
1977             DebugObject->KillProcessOnExit = TRUE;
1978         }
1979         else
1980         {
1981             /* Disable */
1982             DebugObject->KillProcessOnExit = FALSE;
1983         }
1984 
1985         /* Release the mutex */
1986         ExReleaseFastMutex(&DebugObject->Mutex);
1987 
1988         /* Release the Object */
1989         ObDereferenceObject(DebugObject);
1990     }
1991 
1992     /* Return Status */
1993     return Status;
1994 }
1995 
1996 /*
1997  * @implemented
1998  */
1999 NTSTATUS
2000 NTAPI
2001 NtWaitForDebugEvent(IN HANDLE DebugHandle,
2002                     IN BOOLEAN Alertable,
2003                     IN PLARGE_INTEGER Timeout OPTIONAL,
2004                     OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
2005 {
2006     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2007     LARGE_INTEGER LocalTimeOut;
2008     PEPROCESS Process;
2009     LARGE_INTEGER StartTime;
2010     PETHREAD Thread;
2011     BOOLEAN GotEvent;
2012     LARGE_INTEGER NewTime;
2013     PDEBUG_OBJECT DebugObject;
2014     DBGUI_WAIT_STATE_CHANGE WaitStateChange;
2015     NTSTATUS Status;
2016     PDEBUG_EVENT DebugEvent = NULL, DebugEvent2;
2017     PLIST_ENTRY ListHead, NextEntry, NextEntry2;
2018     PAGED_CODE();
2019     DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p\n", DebugHandle);
2020 
2021     /* Clear the initial wait state change structure and the timeout */
2022     RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange));
2023     LocalTimeOut.QuadPart = 0;
2024 
2025     /* Check if we were called from user mode */
2026     if (PreviousMode != KernelMode)
2027     {
2028         /* Protect probe in SEH */
2029         _SEH2_TRY
2030         {
2031             /* Check if we came with a timeout */
2032             if (Timeout)
2033             {
2034                 /* Probe it */
2035                 ProbeForReadLargeInteger(Timeout);
2036 
2037                 /* Make a local copy */
2038                 LocalTimeOut = *Timeout;
2039                 Timeout = &LocalTimeOut;
2040             }
2041 
2042             /* Probe the state change structure */
2043             ProbeForWrite(StateChange, sizeof(*StateChange), sizeof(ULONG));
2044         }
2045         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2046         {
2047             /* Return the exception code */
2048             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2049         }
2050         _SEH2_END;
2051     }
2052     else
2053     {
2054         /* Copy directly */
2055         if (Timeout) LocalTimeOut = *Timeout;
2056     }
2057 
2058     /* If we were passed a timeout, query the current time */
2059     if (Timeout) KeQuerySystemTime(&StartTime);
2060 
2061     /* Get the debug object */
2062     Status = ObReferenceObjectByHandle(DebugHandle,
2063                                        DEBUG_OBJECT_WAIT_STATE_CHANGE,
2064                                        DbgkDebugObjectType,
2065                                        PreviousMode,
2066                                        (PVOID*)&DebugObject,
2067                                        NULL);
2068     if (!NT_SUCCESS(Status)) return Status;
2069 
2070     /* Clear process and thread */
2071     Process = NULL;
2072     Thread = NULL;
2073 
2074     /* Wait on the debug object given to us */
2075     while (TRUE)
2076     {
2077         Status = KeWaitForSingleObject(&DebugObject->EventsPresent,
2078                                        Executive,
2079                                        PreviousMode,
2080                                        Alertable,
2081                                        Timeout);
2082         if (!NT_SUCCESS(Status) ||
2083             (Status == STATUS_TIMEOUT) ||
2084             (Status == STATUS_ALERTED) ||
2085             (Status == STATUS_USER_APC))
2086         {
2087             /* Break out the wait */
2088             break;
2089         }
2090 
2091         /* Lock the object */
2092         GotEvent = FALSE;
2093         ExAcquireFastMutex(&DebugObject->Mutex);
2094 
2095         /* Check if a debugger is connected */
2096         if (DebugObject->DebuggerInactive)
2097         {
2098             /* Not connected */
2099             Status = STATUS_DEBUGGER_INACTIVE;
2100         }
2101         else
2102         {
2103             /* Loop the events */
2104             ListHead = &DebugObject->EventList;
2105             NextEntry =  ListHead->Flink;
2106             while (ListHead != NextEntry)
2107             {
2108                 /* Get the debug event */
2109                 DebugEvent = CONTAINING_RECORD(NextEntry,
2110                                                DEBUG_EVENT,
2111                                                EventList);
2112                 DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx\n",
2113                           DebugEvent, DebugEvent->Flags);
2114 
2115                 /* Check flags */
2116                 if (!(DebugEvent->Flags & (DEBUG_EVENT_INACTIVE | DEBUG_EVENT_READ)))
2117                 {
2118                     /* We got an event */
2119                     GotEvent = TRUE;
2120 
2121                     /* Loop the list internally */
2122                     NextEntry2 = DebugObject->EventList.Flink;
2123                     while (NextEntry2 != NextEntry)
2124                     {
2125                         /* Get the debug event */
2126                         DebugEvent2 = CONTAINING_RECORD(NextEntry2,
2127                                                         DEBUG_EVENT,
2128                                                         EventList);
2129 
2130                         /* Try to match process IDs */
2131                         if (DebugEvent2->ClientId.UniqueProcess ==
2132                             DebugEvent->ClientId.UniqueProcess)
2133                         {
2134                             /* Found it, break out */
2135                             DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
2136                             DebugEvent->BackoutThread = NULL;
2137                             GotEvent = FALSE;
2138                             break;
2139                         }
2140 
2141                         /* Move to the next entry */
2142                         NextEntry2 = NextEntry2->Flink;
2143                     }
2144 
2145                     /* Check if we still have a valid event */
2146                     if (GotEvent) break;
2147                 }
2148 
2149                 /* Move to the next entry */
2150                 NextEntry = NextEntry->Flink;
2151             }
2152 
2153             /* Check if we have an event */
2154             if (GotEvent)
2155             {
2156                 /* Save and reference the process and thread */
2157                 Process = DebugEvent->Process;
2158                 Thread = DebugEvent->Thread;
2159                 ObReferenceObject(Process);
2160                 ObReferenceObject(Thread);
2161 
2162                 /* Convert to user-mode structure */
2163                 DbgkpConvertKernelToUserStateChange(&WaitStateChange,
2164                                                     DebugEvent);
2165 
2166                 /* Set flag */
2167                 DebugEvent->Flags |= DEBUG_EVENT_READ;
2168             }
2169             else
2170             {
2171                 /* Unsignal the event */
2172                 KeClearEvent(&DebugObject->EventsPresent);
2173             }
2174 
2175             /* Set success */
2176             Status = STATUS_SUCCESS;
2177         }
2178 
2179         /* Release the mutex */
2180         ExReleaseFastMutex(&DebugObject->Mutex);
2181         if (!NT_SUCCESS(Status)) break;
2182 
2183         /* Check if we got an event */
2184         if (!GotEvent)
2185         {
2186             /* Check if we can wait again */
2187             if (LocalTimeOut.QuadPart < 0)
2188             {
2189                 /* Query the new time */
2190                 KeQuerySystemTime(&NewTime);
2191 
2192                 /* Substract times */
2193                 LocalTimeOut.QuadPart += (NewTime.QuadPart - StartTime.QuadPart);
2194                 StartTime = NewTime;
2195 
2196                 /* Check if we've timed out */
2197                 if (LocalTimeOut.QuadPart >= 0)
2198                 {
2199                     /* We have, break out of the loop */
2200                     Status = STATUS_TIMEOUT;
2201                     break;
2202                 }
2203             }
2204         }
2205         else
2206         {
2207             /* Open the handles and dereference the objects */
2208             DbgkpOpenHandles(&WaitStateChange, Process, Thread);
2209             ObDereferenceObject(Process);
2210             ObDereferenceObject(Thread);
2211             break;
2212         }
2213     }
2214 
2215     /* We're done, dereference the object */
2216     ObDereferenceObject(DebugObject);
2217 
2218     /* Protect write with SEH */
2219     _SEH2_TRY
2220     {
2221         /* Return our wait state change structure */
2222         *StateChange = WaitStateChange;
2223     }
2224     _SEH2_EXCEPT(ExSystemExceptionFilter())
2225     {
2226         /* Get SEH Exception code */
2227         Status = _SEH2_GetExceptionCode();
2228     }
2229     _SEH2_END;
2230 
2231     /* Return status */
2232     return Status;
2233 }
2234