xref: /reactos/ntoskrnl/dbgk/dbgkobj.c (revision 3bc2d590)
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
DbgkpQueueMessage(IN PEPROCESS Process,IN PETHREAD Thread,IN PDBGKM_MSG Message,IN ULONG Flags,IN PDEBUG_OBJECT TargetObject OPTIONAL)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                                            TAG_DEBUG_EVENT);
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, TAG_DEBUG_EVENT);
196         }
197     }
198 
199     /* Return status */
200     DBGKTRACE(DBGK_MESSAGE_DEBUG, "Status: %lx\n", Status);
201     return Status;
202 }
203 
204 NTSTATUS
205 NTAPI
DbgkpSendApiMessageLpc(IN OUT PDBGKM_MSG Message,IN PVOID Port,IN BOOLEAN SuspendProcess)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
DbgkpSendApiMessage(IN OUT PDBGKM_MSG ApiMsg,IN BOOLEAN SuspendProcess)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
DbgkCopyProcessDebugPort(IN PEPROCESS Process,IN PEPROCESS Parent)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
DbgkForwardException(IN PEXCEPTION_RECORD ExceptionRecord,IN BOOLEAN DebugPort,IN BOOLEAN SecondChance)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
DbgkpFreeDebugEvent(IN PDEBUG_EVENT DebugEvent)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, TAG_DEBUG_EVENT);
422 }
423 
424 VOID
425 NTAPI
DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent)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
DbgkpPostFakeModuleMessages(IN PEPROCESS Process,IN PETHREAD Thread,IN PDEBUG_OBJECT DebugObject)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
DbgkpPostFakeThreadMessages(IN PEPROCESS Process,IN PDEBUG_OBJECT DebugObject,IN PETHREAD StartThread,OUT PETHREAD * FirstThread,OUT PETHREAD * LastThread)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
DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process,IN PDEBUG_OBJECT DebugObject,OUT PETHREAD * LastThread)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
DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,IN PDEBUG_EVENT DebugEvent)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
DbgkpMarkProcessPeb(IN PEPROCESS Process)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
DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,IN PEPROCESS Process,IN PETHREAD Thread)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
DbgkpDeleteObject(IN PVOID DebugObject)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
DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL,IN PVOID ObjectBody,IN ACCESS_MASK GrantedAccess,IN ULONG HandleCount,IN ULONG SystemHandleCount)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
DbgkpSetProcessDebugObject(IN PEPROCESS Process,IN PDEBUG_OBJECT DebugObject,IN NTSTATUS MsgStatus,IN PETHREAD LastThread)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
DbgkClearProcessDebugObject(IN PEPROCESS Process,IN PDEBUG_OBJECT SourceDebugObject OPTIONAL)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
DbgkInitialize(VOID)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
DbgkOpenProcessDebugPort(IN PEPROCESS Process,IN KPROCESSOR_MODE PreviousMode,OUT HANDLE * DebugHandle)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
NtCreateDebugObject(OUT PHANDLE DebugHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG Flags)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
NtDebugContinue(IN HANDLE DebugHandle,IN PCLIENT_ID AppClientId,IN NTSTATUS ContinueStatus)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
NtDebugActiveProcess(IN HANDLE ProcessHandle,IN HANDLE DebugHandle)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
NtRemoveProcessDebug(IN HANDLE ProcessHandle,IN HANDLE DebugHandle)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
NtSetInformationDebugObject(IN HANDLE DebugHandle,IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,IN PVOID DebugInformation,IN ULONG DebugInformationLength,OUT PULONG ReturnLength OPTIONAL)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
NtWaitForDebugEvent(IN HANDLE DebugHandle,IN BOOLEAN Alertable,IN PLARGE_INTEGER Timeout OPTIONAL,OUT PDBGUI_WAIT_STATE_CHANGE StateChange)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