* Sync to trunk r63845.
[reactos.git] / ntoskrnl / dbgk / dbgkobj.c
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 ICI_SQ_SAME(sizeof(ULONG), sizeof(ULONG), 0),
31 /* DebugObjectKillProcessOnExitInformation */
32 ICI_SQ_SAME(sizeof(DEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION), sizeof(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 PAGED_CODE();
474 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Thread: %p DebugObject: %p\n",
475 Process, Thread, DebugObject);
476
477 /* Quit if there's no PEB */
478 if (!Peb) return STATUS_SUCCESS;
479
480 /* Get the Loader Data List */
481 LdrData = Peb->Ldr;
482 ListHead = &LdrData->InLoadOrderModuleList;
483 NextEntry = ListHead->Flink;
484
485 /* Loop the modules */
486 i = 0;
487 while ((NextEntry != ListHead) && (i < 500))
488 {
489 /* Skip the first entry */
490 if (!i)
491 {
492 /* Go to the next module */
493 NextEntry = NextEntry->Flink;
494 i++;
495 continue;
496 }
497
498 /* Get the entry */
499 LdrEntry = CONTAINING_RECORD(NextEntry,
500 LDR_DATA_TABLE_ENTRY,
501 InLoadOrderLinks);
502
503 /* Setup the API Message */
504 RtlZeroMemory(&ApiMessage, sizeof(DBGKM_MSG));
505 ApiMessage.ApiNumber = DbgKmLoadDllApi;
506
507 /* Set base and clear the name */
508 LoadDll->BaseOfDll = LdrEntry->DllBase;
509 LoadDll->NamePointer = NULL;
510
511 /* Get the NT Headers */
512 NtHeader = RtlImageNtHeader(LoadDll->BaseOfDll);
513 if (NtHeader)
514 {
515 /* Save debug data */
516 LoadDll->DebugInfoFileOffset = NtHeader->FileHeader.
517 PointerToSymbolTable;
518 LoadDll->DebugInfoSize = NtHeader->FileHeader.NumberOfSymbols;
519 }
520
521 /* Trace */
522 DBGKTRACE(DBGK_PROCESS_DEBUG, "Name: %wZ. Base: %p\n",
523 &LdrEntry->FullDllName, LdrEntry->DllBase);
524
525 /* Get the name of the DLL */
526 Status = MmGetFileNameForAddress(NtHeader, &ModuleName);
527 if (NT_SUCCESS(Status))
528 {
529 /* Setup the object attributes */
530 InitializeObjectAttributes(&ObjectAttributes,
531 &ModuleName,
532 OBJ_FORCE_ACCESS_CHECK |
533 OBJ_KERNEL_HANDLE |
534 OBJ_CASE_INSENSITIVE,
535 NULL,
536 NULL);
537
538 /* Open the file to get a handle to it */
539 Status = ZwOpenFile(&LoadDll->FileHandle,
540 GENERIC_READ | SYNCHRONIZE,
541 &ObjectAttributes,
542 &IoStatusBlock,
543 FILE_SHARE_READ |
544 FILE_SHARE_WRITE |
545 FILE_SHARE_DELETE,
546 FILE_SYNCHRONOUS_IO_NONALERT);
547 if (!NT_SUCCESS(Status)) LoadDll->FileHandle = NULL;
548
549 /* Free the name now */
550 ExFreePool(ModuleName.Buffer);
551 }
552
553 /* Send the fake module load message */
554 Status = DbgkpQueueMessage(Process,
555 Thread,
556 &ApiMessage,
557 DEBUG_EVENT_NOWAIT,
558 DebugObject);
559 if (!NT_SUCCESS(Status))
560 {
561 /* Message send failed, close the file handle if we had one */
562 if (LoadDll->FileHandle) ObCloseHandle(LoadDll->FileHandle,
563 KernelMode);
564 }
565
566 /* Go to the next module */
567 NextEntry = NextEntry->Flink;
568 i++;
569 }
570
571 /* Return success */
572 return STATUS_SUCCESS;
573 }
574
575 NTSTATUS
576 NTAPI
577 DbgkpPostFakeThreadMessages(IN PEPROCESS Process,
578 IN PDEBUG_OBJECT DebugObject,
579 IN PETHREAD StartThread,
580 OUT PETHREAD *FirstThread,
581 OUT PETHREAD *LastThread)
582 {
583 PETHREAD pFirstThread = NULL, ThisThread, OldThread = NULL, pLastThread;
584 NTSTATUS Status = STATUS_UNSUCCESSFUL;
585 BOOLEAN IsFirstThread;
586 ULONG Flags;
587 DBGKM_MSG ApiMessage;
588 PDBGKM_CREATE_THREAD CreateThread = &ApiMessage.CreateThread;
589 PDBGKM_CREATE_PROCESS CreateProcess = &ApiMessage.CreateProcess;
590 BOOLEAN First;
591 PIMAGE_NT_HEADERS NtHeader;
592 PAGED_CODE();
593 DBGKTRACE(DBGK_THREAD_DEBUG, "Process: %p StartThread: %p Object: %p\n",
594 Process, StartThread, DebugObject);
595
596 /* Check if we have a start thread */
597 if (StartThread)
598 {
599 /* Then the one we'll find won't be the first one */
600 IsFirstThread = FALSE;
601 pFirstThread = StartThread;
602 ThisThread = StartThread;
603
604 /* Reference it */
605 ObReferenceObject(StartThread);
606 }
607 else
608 {
609 /* Get the first thread ourselves */
610 ThisThread = PsGetNextProcessThread(Process, NULL);
611 IsFirstThread = TRUE;
612 }
613
614 /* Start thread loop */
615 do
616 {
617 /* Dereference the previous thread if we had one */
618 if (OldThread) ObDereferenceObject(OldThread);
619
620 /* Set this as the last thread and lock it */
621 pLastThread = ThisThread;
622 ObReferenceObject(ThisThread);
623 if (ExAcquireRundownProtection(&ThisThread->RundownProtect))
624 {
625 /* Acquire worked, set flags */
626 Flags = DEBUG_EVENT_RELEASE | DEBUG_EVENT_NOWAIT;
627
628 /* Check if this is a user thread */
629 if (!ThisThread->SystemThread)
630 {
631 /* Suspend it */
632 if (NT_SUCCESS(PsSuspendThread(ThisThread, NULL)))
633 {
634 /* Remember this */
635 Flags |= DEBUG_EVENT_SUSPEND;
636 }
637 }
638 }
639 else
640 {
641 /* Couldn't acquire rundown */
642 Flags = DEBUG_EVENT_PROTECT_FAILED | DEBUG_EVENT_NOWAIT;
643 }
644
645 /* Clear the API Message */
646 RtlZeroMemory(&ApiMessage, sizeof(ApiMessage));
647
648 /* Check if this is the first thread */
649 if ((IsFirstThread) &&
650 !(Flags & DEBUG_EVENT_PROTECT_FAILED) &&
651 !(ThisThread->SystemThread) &&
652 (ThisThread->GrantedAccess))
653 {
654 /* It is, save the flag */
655 First = TRUE;
656 }
657 else
658 {
659 /* It isn't, save the flag */
660 First = FALSE;
661 }
662
663 /* Check if this is the first */
664 if (First)
665 {
666 /* So we'll start with the create process message */
667 ApiMessage.ApiNumber = DbgKmCreateProcessApi;
668
669 /* Get the file handle */
670 if (Process->SectionObject)
671 {
672 /* Use the section object */
673 CreateProcess->FileHandle =
674 DbgkpSectionToFileHandle(Process->SectionObject);
675 }
676 else
677 {
678 /* Don't return any handle */
679 CreateProcess->FileHandle = NULL;
680 }
681
682 /* Set the base address */
683 CreateProcess->BaseOfImage = Process->SectionBaseAddress;
684
685 /* Get the NT Header */
686 NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
687 if (NtHeader)
688 {
689 /* Fill out data from the header */
690 CreateProcess->DebugInfoFileOffset = NtHeader->FileHeader.
691 PointerToSymbolTable;
692 CreateProcess->DebugInfoSize = NtHeader->FileHeader.
693 NumberOfSymbols;
694 }
695 }
696 else
697 {
698 /* Otherwise it's a thread message */
699 ApiMessage.ApiNumber = DbgKmCreateThreadApi;
700 CreateThread->StartAddress = ThisThread->StartAddress;
701 }
702
703 /* Trace */
704 DBGKTRACE(DBGK_THREAD_DEBUG, "Thread: %p. First: %lx, OldThread: %p\n",
705 ThisThread, First, OldThread);
706 DBGKTRACE(DBGK_THREAD_DEBUG, "Start Address: %p\n",
707 ThisThread->StartAddress);
708
709 /* Queue the message */
710 Status = DbgkpQueueMessage(Process,
711 ThisThread,
712 &ApiMessage,
713 Flags,
714 DebugObject);
715 if (!NT_SUCCESS(Status))
716 {
717 /* Resume the thread if it was suspended */
718 if (Flags & DEBUG_EVENT_SUSPEND) PsResumeThread(ThisThread, NULL);
719
720 /* Check if we acquired rundown */
721 if (Flags & DEBUG_EVENT_RELEASE)
722 {
723 /* Release it */
724 ExReleaseRundownProtection(&ThisThread->RundownProtect);
725 }
726
727 /* If this was a process create, check if we got a handle */
728 if ((ApiMessage.ApiNumber == DbgKmCreateProcessApi) &&
729 (CreateProcess->FileHandle))
730 {
731 /* Close it */
732 ObCloseHandle(CreateProcess->FileHandle, KernelMode);
733 }
734
735 /* Release our reference and break out */
736 ObDereferenceObject(ThisThread);
737 break;
738 }
739
740 /* Check if this was the first message */
741 if (First)
742 {
743 /* It isn't the first thread anymore */
744 IsFirstThread = FALSE;
745
746 /* Reference this thread and set it as first */
747 ObReferenceObject(ThisThread);
748 pFirstThread = ThisThread;
749 }
750
751 /* Get the next thread */
752 ThisThread = PsGetNextProcessThread(Process, ThisThread);
753 OldThread = pLastThread;
754 } while (ThisThread);
755
756 /* Check the API status */
757 if (!NT_SUCCESS(Status))
758 {
759 /* Dereference and fail */
760 if (pFirstThread) ObDereferenceObject(pFirstThread);
761 if (pLastThread) ObDereferenceObject(pLastThread);
762 return Status;
763 }
764
765 /* Make sure we have a first thread */
766 if (!pFirstThread) return STATUS_UNSUCCESSFUL;
767
768 /* Return thread pointers */
769 *FirstThread = pFirstThread;
770 *LastThread = pLastThread;
771 return Status;
772 }
773
774 NTSTATUS
775 NTAPI
776 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process,
777 IN PDEBUG_OBJECT DebugObject,
778 OUT PETHREAD *LastThread)
779 {
780 KAPC_STATE ApcState;
781 PETHREAD FirstThread, FinalThread;
782 PETHREAD ReturnThread = NULL;
783 NTSTATUS Status;
784 PAGED_CODE();
785 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
786 Process, DebugObject);
787
788 /* Attach to the process */
789 KeStackAttachProcess(&Process->Pcb, &ApcState);
790
791 /* Post the fake thread messages */
792 Status = DbgkpPostFakeThreadMessages(Process,
793 DebugObject,
794 NULL,
795 &FirstThread,
796 &FinalThread);
797 if (NT_SUCCESS(Status))
798 {
799 /* Send the fake module messages too */
800 Status = DbgkpPostFakeModuleMessages(Process,
801 FirstThread,
802 DebugObject);
803 if (!NT_SUCCESS(Status))
804 {
805 /* We failed, dereference the final thread */
806 ObDereferenceObject(FinalThread);
807 }
808 else
809 {
810 /* Set the final thread */
811 ReturnThread = FinalThread;
812 }
813
814 /* Dereference the first thread */
815 ObDereferenceObject(FirstThread);
816 }
817
818 /* Detach from the process */
819 KeUnstackDetachProcess(&ApcState);
820
821 /* Return the last thread */
822 *LastThread = ReturnThread;
823 return Status;
824 }
825
826 VOID
827 NTAPI
828 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
829 IN PDEBUG_EVENT DebugEvent)
830 {
831 DBGKTRACE(DBGK_OBJECT_DEBUG, "DebugEvent: %p\n", DebugEvent);
832
833 /* Start by copying the client ID */
834 WaitStateChange->AppClientId = DebugEvent->ClientId;
835
836 /* Now check which kind of event this was */
837 switch (DebugEvent->ApiMsg.ApiNumber)
838 {
839 /* New process */
840 case DbgKmCreateProcessApi:
841
842 /* Set the right native code */
843 WaitStateChange->NewState = DbgCreateProcessStateChange;
844
845 /* Copy the information */
846 WaitStateChange->StateInfo.CreateProcessInfo.NewProcess =
847 DebugEvent->ApiMsg.CreateProcess;
848
849 /* Clear the file handle for us */
850 DebugEvent->ApiMsg.CreateProcess.FileHandle = NULL;
851 break;
852
853 /* New thread */
854 case DbgKmCreateThreadApi:
855
856 /* Set the right native code */
857 WaitStateChange->NewState = DbgCreateThreadStateChange;
858
859 /* Copy information */
860 WaitStateChange->StateInfo.CreateThread.NewThread.StartAddress =
861 DebugEvent->ApiMsg.CreateThread.StartAddress;
862 WaitStateChange->StateInfo.CreateThread.NewThread.SubSystemKey =
863 DebugEvent->ApiMsg.CreateThread.SubSystemKey;
864 break;
865
866 /* Exception (or breakpoint/step) */
867 case DbgKmExceptionApi:
868
869 /* Look at the exception code */
870 if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
871 STATUS_BREAKPOINT)
872 {
873 /* Update this as a breakpoint exception */
874 WaitStateChange->NewState = DbgBreakpointStateChange;
875 }
876 else if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
877 STATUS_SINGLE_STEP)
878 {
879 /* Update this as a single step exception */
880 WaitStateChange->NewState = DbgSingleStepStateChange;
881 }
882 else
883 {
884 /* Otherwise, set default exception */
885 WaitStateChange->NewState = DbgExceptionStateChange;
886 }
887
888 /* Copy the exception record */
889 WaitStateChange->StateInfo.Exception.ExceptionRecord =
890 DebugEvent->ApiMsg.Exception.ExceptionRecord;
891 /* Copy FirstChance flag */
892 WaitStateChange->StateInfo.Exception.FirstChance =
893 DebugEvent->ApiMsg.Exception.FirstChance;
894 break;
895
896 /* Process exited */
897 case DbgKmExitProcessApi:
898
899 /* Set the right native code and copy the exit code */
900 WaitStateChange->NewState = DbgExitProcessStateChange;
901 WaitStateChange->StateInfo.ExitProcess.ExitStatus =
902 DebugEvent->ApiMsg.ExitProcess.ExitStatus;
903 break;
904
905 /* Thread exited */
906 case DbgKmExitThreadApi:
907
908 /* Set the right native code */
909 WaitStateChange->NewState = DbgExitThreadStateChange;
910 WaitStateChange->StateInfo.ExitThread.ExitStatus =
911 DebugEvent->ApiMsg.ExitThread.ExitStatus;
912 break;
913
914 /* DLL Load */
915 case DbgKmLoadDllApi:
916
917 /* Set the native code */
918 WaitStateChange->NewState = DbgLoadDllStateChange;
919
920 /* Copy the data */
921 WaitStateChange->StateInfo.LoadDll = DebugEvent->ApiMsg.LoadDll;
922
923 /* Clear the file handle for us */
924 DebugEvent->ApiMsg.LoadDll.FileHandle = NULL;
925 break;
926
927 /* DLL Unload */
928 case DbgKmUnloadDllApi:
929
930 /* Set the native code and copy the address */
931 WaitStateChange->NewState = DbgUnloadDllStateChange;
932 WaitStateChange->StateInfo.UnloadDll.BaseAddress =
933 DebugEvent->ApiMsg.UnloadDll.BaseAddress;
934 break;
935
936 default:
937
938 /* Shouldn't happen */
939 ASSERT(FALSE);
940 }
941 }
942
943 VOID
944 NTAPI
945 DbgkpMarkProcessPeb(IN PEPROCESS Process)
946 {
947 KAPC_STATE ApcState;
948 PAGED_CODE();
949 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p\n", Process);
950
951 /* Acquire process rundown */
952 if (!ExAcquireRundownProtection(&Process->RundownProtect)) return;
953
954 /* Make sure we have a PEB */
955 if (Process->Peb)
956 {
957 /* Attach to the process */
958 KeStackAttachProcess(&Process->Pcb, &ApcState);
959
960 /* Acquire the debug port mutex */
961 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
962
963 /* Set the IsBeingDebugged member of the PEB */
964 Process->Peb->BeingDebugged = (Process->DebugPort) ? TRUE: FALSE;
965
966 /* Release lock */
967 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
968
969 /* Detach from the process */
970 KeUnstackDetachProcess(&ApcState);
971 }
972
973 /* Release rundown protection */
974 ExReleaseRundownProtection(&Process->RundownProtect);
975 }
976
977 VOID
978 NTAPI
979 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
980 IN PEPROCESS Process,
981 IN PETHREAD Thread)
982 {
983 NTSTATUS Status;
984 HANDLE Handle;
985 PHANDLE DupHandle;
986 PAGED_CODE();
987 DBGKTRACE(DBGK_OBJECT_DEBUG, "Process: %p Thread: %p State: %lx\n",
988 Process, Thread, WaitStateChange->NewState);
989
990 /* Check which state this is */
991 switch (WaitStateChange->NewState)
992 {
993 /* New thread */
994 case DbgCreateThreadStateChange:
995
996 /* Get handle to thread */
997 Status = ObOpenObjectByPointer(Thread,
998 0,
999 NULL,
1000 THREAD_ALL_ACCESS,
1001 PsThreadType,
1002 KernelMode,
1003 &Handle);
1004 if (NT_SUCCESS(Status))
1005 {
1006 /* Save the thread handle */
1007 WaitStateChange->
1008 StateInfo.CreateThread.HandleToThread = Handle;
1009 }
1010 return;
1011
1012 /* New process */
1013 case DbgCreateProcessStateChange:
1014
1015 /* Get handle to thread */
1016 Status = ObOpenObjectByPointer(Thread,
1017 0,
1018 NULL,
1019 THREAD_ALL_ACCESS,
1020 PsThreadType,
1021 KernelMode,
1022 &Handle);
1023 if (NT_SUCCESS(Status))
1024 {
1025 /* Save the thread handle */
1026 WaitStateChange->
1027 StateInfo.CreateProcessInfo.HandleToThread = Handle;
1028 }
1029
1030 /* Get handle to process */
1031 Status = ObOpenObjectByPointer(Process,
1032 0,
1033 NULL,
1034 PROCESS_ALL_ACCESS,
1035 PsProcessType,
1036 KernelMode,
1037 &Handle);
1038 if (NT_SUCCESS(Status))
1039 {
1040 /* Save the process handle */
1041 WaitStateChange->
1042 StateInfo.CreateProcessInfo.HandleToProcess = Handle;
1043 }
1044
1045 /* Fall through to duplicate file handle */
1046 DupHandle = &WaitStateChange->
1047 StateInfo.CreateProcessInfo.NewProcess.FileHandle;
1048 break;
1049
1050 /* DLL Load */
1051 case DbgLoadDllStateChange:
1052
1053 /* Fall through to duplicate file handle */
1054 DupHandle = &WaitStateChange->StateInfo.LoadDll.FileHandle;
1055 break;
1056
1057 /* Anything else has no handles */
1058 default:
1059 return;
1060 }
1061
1062 /* If we got here, then we have to duplicate a handle, possibly */
1063 Handle = *DupHandle;
1064 if (Handle)
1065 {
1066 /* Duplicate it */
1067 Status = ObDuplicateObject(PsGetCurrentProcess(),
1068 Handle,
1069 PsGetCurrentProcess(),
1070 DupHandle,
1071 0,
1072 0,
1073 DUPLICATE_SAME_ACCESS,
1074 KernelMode);
1075 if (!NT_SUCCESS(Status)) *DupHandle = NULL;
1076
1077 /* Close the original handle */
1078 ObCloseHandle(Handle, KernelMode);
1079 }
1080 }
1081
1082 VOID
1083 NTAPI
1084 DbgkpDeleteObject(IN PVOID DebugObject)
1085 {
1086 PAGED_CODE();
1087
1088 /* Sanity check */
1089 ASSERT(IsListEmpty(&((PDEBUG_OBJECT)DebugObject)->EventList));
1090 }
1091
1092 VOID
1093 NTAPI
1094 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL,
1095 IN PVOID ObjectBody,
1096 IN ACCESS_MASK GrantedAccess,
1097 IN ULONG HandleCount,
1098 IN ULONG SystemHandleCount)
1099 {
1100 PDEBUG_OBJECT DebugObject = ObjectBody;
1101 PEPROCESS Process = NULL;
1102 BOOLEAN DebugPortCleared = FALSE;
1103 PLIST_ENTRY DebugEventList;
1104 PDEBUG_EVENT DebugEvent;
1105 PAGED_CODE();
1106 DBGKTRACE(DBGK_OBJECT_DEBUG, "OwnerProcess: %p DebugObject: %p\n",
1107 OwnerProcess, DebugObject);
1108
1109 /* If this isn't the last handle, do nothing */
1110 if (SystemHandleCount > 1) return;
1111
1112 /* Otherwise, lock the debug object */
1113 ExAcquireFastMutex(&DebugObject->Mutex);
1114
1115 /* Set it as inactive */
1116 DebugObject->DebuggerInactive = TRUE;
1117
1118 /* Remove it from the debug event list */
1119 DebugEventList = DebugObject->EventList.Flink;
1120 InitializeListHead(&DebugObject->EventList);
1121
1122 /* Release the lock */
1123 ExReleaseFastMutex(&DebugObject->Mutex);
1124
1125 /* Signal the wait event */
1126 KeSetEvent(&DebugObject->EventsPresent, IO_NO_INCREMENT, FALSE);
1127
1128 /* Start looping each process */
1129 while ((Process = PsGetNextProcess(Process)))
1130 {
1131 /* Check if the process has us as their debug port */
1132 if (Process->DebugPort == DebugObject)
1133 {
1134 /* Acquire the process debug port lock */
1135 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1136
1137 /* Check if it's still us */
1138 if (Process->DebugPort == DebugObject)
1139 {
1140 /* Clear it and remember */
1141 Process->DebugPort = NULL;
1142 DebugPortCleared = TRUE;
1143 }
1144
1145 /* Release the port lock */
1146 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1147
1148 /* Check if we cleared the debug port */
1149 if (DebugPortCleared)
1150 {
1151 /* Mark this in the PEB */
1152 DbgkpMarkProcessPeb(Process);
1153
1154 /* Check if we terminate on exit */
1155 if (DebugObject->KillProcessOnExit)
1156 {
1157 /* Terminate the process */
1158 PsTerminateProcess(Process, STATUS_DEBUGGER_INACTIVE);
1159 }
1160
1161 /* Dereference the debug object */
1162 ObDereferenceObject(DebugObject);
1163 }
1164 }
1165 }
1166
1167 /* Loop debug events */
1168 while (DebugEventList != &DebugObject->EventList)
1169 {
1170 /* Get the debug event */
1171 DebugEvent = CONTAINING_RECORD(DebugEventList, DEBUG_EVENT, EventList);
1172
1173 /* Go to the next entry */
1174 DebugEventList = DebugEventList->Flink;
1175
1176 /* Wake it up */
1177 DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
1178 DbgkpWakeTarget(DebugEvent);
1179 }
1180 }
1181
1182 NTSTATUS
1183 NTAPI
1184 DbgkpSetProcessDebugObject(IN PEPROCESS Process,
1185 IN PDEBUG_OBJECT DebugObject,
1186 IN NTSTATUS MsgStatus,
1187 IN PETHREAD LastThread)
1188 {
1189 NTSTATUS Status;
1190 LIST_ENTRY TempList;
1191 BOOLEAN GlobalHeld = FALSE, DoSetEvent = TRUE;
1192 PETHREAD ThisThread, FirstThread;
1193 PLIST_ENTRY NextEntry;
1194 PDEBUG_EVENT DebugEvent;
1195 PETHREAD EventThread;
1196 PAGED_CODE();
1197 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
1198 Process, DebugObject);
1199
1200 /* Initialize the temporary list */
1201 InitializeListHead(&TempList);
1202
1203 /* Check if we have a success message */
1204 if (NT_SUCCESS(MsgStatus))
1205 {
1206 /* Then default to STATUS_SUCCESS */
1207 Status = STATUS_SUCCESS;
1208 }
1209 else
1210 {
1211 /* No last thread, and set the failure code */
1212 LastThread = NULL;
1213 Status = MsgStatus;
1214 }
1215
1216 /* Now check what status we have here */
1217 if (NT_SUCCESS(Status))
1218 {
1219 /* Acquire the global lock */
1220 ThreadScan:
1221 GlobalHeld = TRUE;
1222 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1223
1224 /* Check if we already have a port */
1225 if (Process->DebugPort)
1226 {
1227 /* Set failure */
1228 Status = STATUS_PORT_ALREADY_SET;
1229 }
1230 else
1231 {
1232 /* Otherwise, set the port and reference the thread */
1233 Process->DebugPort = DebugObject;
1234 ObReferenceObject(LastThread);
1235
1236 /* Get the next thread */
1237 ThisThread = PsGetNextProcessThread(Process, LastThread);
1238 if (ThisThread)
1239 {
1240 /* Clear the debug port and release the lock */
1241 Process->DebugPort = NULL;
1242 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1243 GlobalHeld = FALSE;
1244
1245 /* Dereference the thread */
1246 ObDereferenceObject(LastThread);
1247
1248 /* Post fake messages */
1249 Status = DbgkpPostFakeThreadMessages(Process,
1250 DebugObject,
1251 ThisThread,
1252 &FirstThread,
1253 &LastThread);
1254 if (!NT_SUCCESS(Status))
1255 {
1256 /* Clear the last thread */
1257 LastThread = NULL;
1258 }
1259 else
1260 {
1261 /* Dereference the first thread and re-acquire the lock */
1262 ObDereferenceObject(FirstThread);
1263 goto ThreadScan;
1264 }
1265 }
1266 }
1267 }
1268
1269 /* Acquire the debug object's lock */
1270 ExAcquireFastMutex(&DebugObject->Mutex);
1271
1272 /* Check our status here */
1273 if (NT_SUCCESS(Status))
1274 {
1275 /* Check if we're disconnected */
1276 if (DebugObject->DebuggerInactive)
1277 {
1278 /* Set status */
1279 Process->DebugPort = NULL;
1280 Status = STATUS_DEBUGGER_INACTIVE;
1281 }
1282 else
1283 {
1284 /* Set the process flags */
1285 PspSetProcessFlag(Process,
1286 PSF_NO_DEBUG_INHERIT_BIT |
1287 PSF_CREATE_REPORTED_BIT);
1288
1289 /* Reference the debug object */
1290 ObReferenceObject(DebugObject);
1291 }
1292 }
1293
1294 /* Loop the events list */
1295 NextEntry = DebugObject->EventList.Flink;
1296 while (NextEntry != &DebugObject->EventList)
1297 {
1298 /* Get the debug event and go to the next entry */
1299 DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1300 NextEntry = NextEntry->Flink;
1301 DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1302 DebugEvent, DebugEvent->Flags,
1303 DebugEvent->BackoutThread, PsGetCurrentThread());
1304
1305 /* Check for if the debug event queue needs flushing */
1306 if ((DebugEvent->Flags & DEBUG_EVENT_INACTIVE) &&
1307 (DebugEvent->BackoutThread == PsGetCurrentThread()))
1308 {
1309 /* Get the event's thread */
1310 EventThread = DebugEvent->Thread;
1311 DBGKTRACE(DBGK_PROCESS_DEBUG, "EventThread: %p MsgStatus: %lx\n",
1312 EventThread, MsgStatus);
1313
1314 /* Check if the status is success */
1315 if ((MsgStatus == STATUS_SUCCESS) &&
1316 (EventThread->GrantedAccess) &&
1317 (!EventThread->SystemThread))
1318 {
1319 /* Check if we couldn't acquire rundown for it */
1320 if (DebugEvent->Flags & DEBUG_EVENT_PROTECT_FAILED)
1321 {
1322 /* Set the skip termination flag */
1323 PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1324
1325 /* Insert it into the temp list */
1326 RemoveEntryList(&DebugEvent->EventList);
1327 InsertTailList(&TempList, &DebugEvent->EventList);
1328 }
1329 else
1330 {
1331 /* Do we need to signal the event */
1332 if (DoSetEvent)
1333 {
1334 /* Do it */
1335 DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1336 KeSetEvent(&DebugObject->EventsPresent,
1337 IO_NO_INCREMENT,
1338 FALSE);
1339 DoSetEvent = FALSE;
1340 }
1341
1342 /* Clear the backout thread */
1343 DebugEvent->BackoutThread = NULL;
1344
1345 /* Set skip flag */
1346 PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1347 }
1348 }
1349 else
1350 {
1351 /* Insert it into the temp list */
1352 RemoveEntryList(&DebugEvent->EventList);
1353 InsertTailList(&TempList, &DebugEvent->EventList);
1354 }
1355
1356 /* Check if the lock is held */
1357 if (DebugEvent->Flags & DEBUG_EVENT_RELEASE)
1358 {
1359 /* Release it */
1360 DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
1361 ExReleaseRundownProtection(&EventThread->RundownProtect);
1362 }
1363 }
1364 }
1365
1366 /* Release the debug object */
1367 ExReleaseFastMutex(&DebugObject->Mutex);
1368
1369 /* Release the global lock if acquired */
1370 if (GlobalHeld) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1371
1372 /* Check if there's a thread to dereference */
1373 if (LastThread) ObDereferenceObject(LastThread);
1374
1375 /* Loop our temporary list */
1376 while (!IsListEmpty(&TempList))
1377 {
1378 /* Remove the event */
1379 NextEntry = RemoveHeadList(&TempList);
1380 DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1381
1382 /* Wake it */
1383 DbgkpWakeTarget(DebugEvent);
1384 }
1385
1386 /* Check if we got here through success and mark the PEB, then return */
1387 if (NT_SUCCESS(Status)) DbgkpMarkProcessPeb(Process);
1388 return Status;
1389 }
1390
1391 NTSTATUS
1392 NTAPI
1393 DbgkClearProcessDebugObject(IN PEPROCESS Process,
1394 IN PDEBUG_OBJECT SourceDebugObject OPTIONAL)
1395 {
1396 PDEBUG_OBJECT DebugObject;
1397 PDEBUG_EVENT DebugEvent;
1398 LIST_ENTRY TempList;
1399 PLIST_ENTRY NextEntry;
1400 PAGED_CODE();
1401 DBGKTRACE(DBGK_OBJECT_DEBUG, "Process: %p DebugObject: %p\n",
1402 Process, SourceDebugObject);
1403
1404 /* Acquire the port lock */
1405 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1406
1407 /* Get the Process Debug Object */
1408 DebugObject = Process->DebugPort;
1409
1410 /*
1411 * Check if the process had an object and it matches,
1412 * or if the process had an object but none was specified
1413 * (in which we are called from NtTerminateProcess)
1414 */
1415 if ((DebugObject) &&
1416 ((DebugObject == SourceDebugObject) ||
1417 (SourceDebugObject == NULL)))
1418 {
1419 /* Clear the debug port */
1420 Process->DebugPort = NULL;
1421
1422 /* Release the port lock and remove the PEB flag */
1423 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1424 DbgkpMarkProcessPeb(Process);
1425 }
1426 else
1427 {
1428 /* Release the port lock and fail */
1429 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1430 return STATUS_PORT_NOT_SET;
1431 }
1432
1433 /* Initialize the temporary list */
1434 InitializeListHead(&TempList);
1435
1436 /* Acquire the Object */
1437 ExAcquireFastMutex(&DebugObject->Mutex);
1438
1439 /* Loop the events */
1440 NextEntry = DebugObject->EventList.Flink;
1441 while (NextEntry != &DebugObject->EventList)
1442 {
1443 /* Get the Event and go to the next entry */
1444 DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1445 NextEntry = NextEntry->Flink;
1446
1447 /* Check that it belongs to the specified process */
1448 if (DebugEvent->Process == Process)
1449 {
1450 /* Insert it into the temporary list */
1451 RemoveEntryList(&DebugEvent->EventList);
1452 InsertTailList(&TempList, &DebugEvent->EventList);
1453 }
1454 }
1455
1456 /* Release the Object */
1457 ExReleaseFastMutex(&DebugObject->Mutex);
1458
1459 /* Release the initial reference */
1460 ObDereferenceObject(DebugObject);
1461
1462 /* Loop our temporary list */
1463 while (!IsListEmpty(&TempList))
1464 {
1465 /* Remove the event */
1466 NextEntry = RemoveHeadList(&TempList);
1467 DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1468
1469 /* Wake it up */
1470 DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
1471 DbgkpWakeTarget(DebugEvent);
1472 }
1473
1474 /* Return Success */
1475 return STATUS_SUCCESS;
1476 }
1477
1478 VOID
1479 INIT_FUNCTION
1480 NTAPI
1481 DbgkInitialize(VOID)
1482 {
1483 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1484 UNICODE_STRING Name;
1485 PAGED_CODE();
1486
1487 /* Initialize the process debug port mutex */
1488 ExInitializeFastMutex(&DbgkpProcessDebugPortMutex);
1489
1490 /* Create the Debug Object Type */
1491 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1492 RtlInitUnicodeString(&Name, L"DebugObject");
1493 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1494 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DEBUG_OBJECT);
1495 ObjectTypeInitializer.GenericMapping = DbgkDebugObjectMapping;
1496 ObjectTypeInitializer.PoolType = NonPagedPool;
1497 ObjectTypeInitializer.ValidAccessMask = DEBUG_OBJECT_ALL_ACCESS;
1498 ObjectTypeInitializer.SecurityRequired = TRUE;
1499 ObjectTypeInitializer.CloseProcedure = DbgkpCloseObject;
1500 ObjectTypeInitializer.DeleteProcedure = DbgkpDeleteObject;
1501 ObCreateObjectType(&Name,
1502 &ObjectTypeInitializer,
1503 NULL,
1504 &DbgkDebugObjectType);
1505 }
1506
1507 NTSTATUS
1508 NTAPI
1509 DbgkOpenProcessDebugPort(IN PEPROCESS Process,
1510 IN KPROCESSOR_MODE PreviousMode,
1511 OUT HANDLE *DebugHandle)
1512 {
1513 PDEBUG_OBJECT DebugObject;
1514 NTSTATUS Status;
1515 PAGED_CODE();
1516
1517 /* If there's no debug port, just exit */
1518 if (!Process->DebugPort) return STATUS_PORT_NOT_SET;
1519
1520 /* Otherwise, acquire the lock while we grab the port */
1521 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1522
1523 /* Grab it and reference it if it exists */
1524 DebugObject = Process->DebugPort;
1525 if (DebugObject) ObReferenceObject(DebugObject);
1526
1527 /* Release the lock now */
1528 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1529
1530 /* Bail out if it doesn't exist */
1531 if (!DebugObject) return STATUS_PORT_NOT_SET;
1532
1533 /* Now get a handle to it */
1534 Status = ObOpenObjectByPointer(DebugObject,
1535 0,
1536 NULL,
1537 MAXIMUM_ALLOWED,
1538 DbgkDebugObjectType,
1539 PreviousMode,
1540 DebugHandle);
1541 if (!NT_SUCCESS(Status)) ObDereferenceObject(DebugObject);
1542
1543 /* Return status */
1544 return Status;
1545 }
1546
1547 /* PUBLIC FUNCTIONS **********************************************************/
1548
1549 /*
1550 * @implemented
1551 */
1552 NTSTATUS
1553 NTAPI
1554 NtCreateDebugObject(OUT PHANDLE DebugHandle,
1555 IN ACCESS_MASK DesiredAccess,
1556 IN POBJECT_ATTRIBUTES ObjectAttributes,
1557 IN ULONG Flags)
1558 {
1559 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1560 PDEBUG_OBJECT DebugObject;
1561 HANDLE hDebug;
1562 NTSTATUS Status;
1563 PAGED_CODE();
1564
1565 /* Check if we were called from user mode*/
1566 if (PreviousMode != KernelMode)
1567 {
1568 /* Enter SEH for probing */
1569 _SEH2_TRY
1570 {
1571 /* Probe the handle */
1572 ProbeForWriteHandle(DebugHandle);
1573 }
1574 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1575 {
1576 /* Return the exception code */
1577 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1578 } _SEH2_END;
1579 }
1580
1581 /* Check for invalid flags */
1582 if (Flags & ~DBGK_ALL_FLAGS) return STATUS_INVALID_PARAMETER;
1583
1584 /* Create the Object */
1585 Status = ObCreateObject(PreviousMode,
1586 DbgkDebugObjectType,
1587 ObjectAttributes,
1588 PreviousMode,
1589 NULL,
1590 sizeof(DEBUG_OBJECT),
1591 0,
1592 0,
1593 (PVOID*)&DebugObject);
1594 if (NT_SUCCESS(Status))
1595 {
1596 /* Initialize the Debug Object's Fast Mutex */
1597 ExInitializeFastMutex(&DebugObject->Mutex);
1598
1599 /* Initialize the State Event List */
1600 InitializeListHead(&DebugObject->EventList);
1601
1602 /* Initialize the Debug Object's Wait Event */
1603 KeInitializeEvent(&DebugObject->EventsPresent,
1604 NotificationEvent,
1605 FALSE);
1606
1607 /* Set the Flags */
1608 DebugObject->Flags = 0;
1609 if (Flags & DBGK_KILL_PROCESS_ON_EXIT)
1610 {
1611 DebugObject->KillProcessOnExit = TRUE;
1612 }
1613
1614 /* Insert it */
1615 Status = ObInsertObject((PVOID)DebugObject,
1616 NULL,
1617 DesiredAccess,
1618 0,
1619 NULL,
1620 &hDebug);
1621 if (NT_SUCCESS(Status))
1622 {
1623 /* Enter SEH to protect the write */
1624 _SEH2_TRY
1625 {
1626 /* Return the handle */
1627 *DebugHandle = hDebug;
1628 }
1629 _SEH2_EXCEPT(ExSystemExceptionFilter())
1630 {
1631 /* Get the exception code */
1632 Status = _SEH2_GetExceptionCode();
1633 } _SEH2_END;
1634 }
1635 }
1636
1637 /* Return Status */
1638 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p DebugObject: %p\n",
1639 hDebug, DebugObject);
1640 return Status;
1641 }
1642
1643 /*
1644 * @implemented
1645 */
1646 NTSTATUS
1647 NTAPI
1648 NtDebugContinue(IN HANDLE DebugHandle,
1649 IN PCLIENT_ID AppClientId,
1650 IN NTSTATUS ContinueStatus)
1651 {
1652 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1653 PDEBUG_OBJECT DebugObject;
1654 NTSTATUS Status;
1655 PDEBUG_EVENT DebugEvent = NULL, DebugEventToWake = NULL;
1656 PLIST_ENTRY ListHead, NextEntry;
1657 BOOLEAN NeedsWake = FALSE;
1658 CLIENT_ID ClientId;
1659 PAGED_CODE();
1660 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p Status: %d\n",
1661 DebugHandle, ContinueStatus);
1662
1663 /* Check if we were called from user mode*/
1664 if (PreviousMode != KernelMode)
1665 {
1666 /* Enter SEH for probing */
1667 _SEH2_TRY
1668 {
1669 /* Probe the handle */
1670 ProbeForRead(AppClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1671 ClientId = *AppClientId;
1672 AppClientId = &ClientId;
1673 }
1674 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1675 {
1676 /* Return the exception code */
1677 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1678 } _SEH2_END;
1679 }
1680
1681 /* Make sure that the status is valid */
1682 if ((ContinueStatus != DBG_CONTINUE) &&
1683 (ContinueStatus != DBG_EXCEPTION_HANDLED) &&
1684 (ContinueStatus != DBG_EXCEPTION_NOT_HANDLED) &&
1685 (ContinueStatus != DBG_TERMINATE_THREAD) &&
1686 (ContinueStatus != DBG_TERMINATE_PROCESS))
1687 {
1688 /* Invalid status */
1689 Status = STATUS_INVALID_PARAMETER;
1690 }
1691 else
1692 {
1693 /* Get the debug object */
1694 Status = ObReferenceObjectByHandle(DebugHandle,
1695 DEBUG_OBJECT_WAIT_STATE_CHANGE,
1696 DbgkDebugObjectType,
1697 PreviousMode,
1698 (PVOID*)&DebugObject,
1699 NULL);
1700 if (NT_SUCCESS(Status))
1701 {
1702 /* Acquire the mutex */
1703 ExAcquireFastMutex(&DebugObject->Mutex);
1704
1705 /* Loop the state list */
1706 ListHead = &DebugObject->EventList;
1707 NextEntry = ListHead->Flink;
1708 while (ListHead != NextEntry)
1709 {
1710 /* Get the current debug event */
1711 DebugEvent = CONTAINING_RECORD(NextEntry,
1712 DEBUG_EVENT,
1713 EventList);
1714
1715 /* Compare process ID */
1716 if (DebugEvent->ClientId.UniqueProcess ==
1717 AppClientId->UniqueProcess)
1718 {
1719 /* Check if we already found a match */
1720 if (NeedsWake)
1721 {
1722 /* Wake it up and break out */
1723 DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1724 KeSetEvent(&DebugObject->EventsPresent,
1725 IO_NO_INCREMENT,
1726 FALSE);
1727 break;
1728 }
1729
1730 /* Compare thread ID and flag */
1731 if ((DebugEvent->ClientId.UniqueThread ==
1732 AppClientId->UniqueThread) && (DebugEvent->Flags & DEBUG_EVENT_READ))
1733 {
1734 /* Remove the event from the list */
1735 RemoveEntryList(NextEntry);
1736
1737 /* Remember who to wake */
1738 NeedsWake = TRUE;
1739 DebugEventToWake = DebugEvent;
1740 }
1741 }
1742
1743 /* Go to the next entry */
1744 NextEntry = NextEntry->Flink;
1745 }
1746
1747 /* Release the mutex */
1748 ExReleaseFastMutex(&DebugObject->Mutex);
1749
1750 /* Dereference the object */
1751 ObDereferenceObject(DebugObject);
1752
1753 /* Check if need a wait */
1754 if (NeedsWake)
1755 {
1756 /* Set the continue status */
1757 DebugEventToWake->ApiMsg.ReturnedStatus = ContinueStatus;
1758 DebugEventToWake->Status = STATUS_SUCCESS;
1759
1760 /* Wake the target */
1761 DbgkpWakeTarget(DebugEventToWake);
1762 }
1763 else
1764 {
1765 /* Fail */
1766 Status = STATUS_INVALID_PARAMETER;
1767 }
1768 }
1769 }
1770
1771 /* Return status */
1772 return Status;
1773 }
1774
1775 /*
1776 * @implemented
1777 */
1778 NTSTATUS
1779 NTAPI
1780 NtDebugActiveProcess(IN HANDLE ProcessHandle,
1781 IN HANDLE DebugHandle)
1782 {
1783 PEPROCESS Process;
1784 PDEBUG_OBJECT DebugObject;
1785 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1786 PETHREAD LastThread;
1787 NTSTATUS Status;
1788 PAGED_CODE();
1789 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1790 ProcessHandle, DebugHandle);
1791
1792 /* Reference the process */
1793 Status = ObReferenceObjectByHandle(ProcessHandle,
1794 PROCESS_SUSPEND_RESUME,
1795 PsProcessType,
1796 PreviousMode,
1797 (PVOID*)&Process,
1798 NULL);
1799 if (!NT_SUCCESS(Status)) return Status;
1800
1801 /* Don't allow debugging the current process or the system process */
1802 if ((Process == PsGetCurrentProcess()) ||
1803 (Process == PsInitialSystemProcess))
1804 {
1805 /* Dereference and fail */
1806 ObDereferenceObject(Process);
1807 return STATUS_ACCESS_DENIED;
1808 }
1809
1810 /* Reference the debug object */
1811 Status = ObReferenceObjectByHandle(DebugHandle,
1812 DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1813 DbgkDebugObjectType,
1814 PreviousMode,
1815 (PVOID*)&DebugObject,
1816 NULL);
1817 if (!NT_SUCCESS(Status))
1818 {
1819 /* Dereference the process and exit */
1820 ObDereferenceObject(Process);
1821 return Status;
1822 }
1823
1824 /* Acquire process rundown protection */
1825 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1826 {
1827 /* Dereference the process and debug object and exit */
1828 ObDereferenceObject(Process);
1829 ObDereferenceObject(DebugObject);
1830 return STATUS_PROCESS_IS_TERMINATING;
1831 }
1832
1833 /* Send fake create messages for debuggers to have a consistent state */
1834 Status = DbgkpPostFakeProcessCreateMessages(Process,
1835 DebugObject,
1836 &LastThread);
1837 Status = DbgkpSetProcessDebugObject(Process,
1838 DebugObject,
1839 Status,
1840 LastThread);
1841
1842 /* Release rundown protection */
1843 ExReleaseRundownProtection(&Process->RundownProtect);
1844
1845 /* Dereference the process and debug object and return status */
1846 ObDereferenceObject(Process);
1847 ObDereferenceObject(DebugObject);
1848 return Status;
1849 }
1850
1851 /*
1852 * @implemented
1853 */
1854 NTSTATUS
1855 NTAPI
1856 NtRemoveProcessDebug(IN HANDLE ProcessHandle,
1857 IN HANDLE DebugHandle)
1858 {
1859 PEPROCESS Process;
1860 PDEBUG_OBJECT DebugObject;
1861 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1862 NTSTATUS Status;
1863 PAGED_CODE();
1864 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1865 ProcessHandle, DebugHandle);
1866
1867 /* Reference the process */
1868 Status = ObReferenceObjectByHandle(ProcessHandle,
1869 PROCESS_SUSPEND_RESUME,
1870 PsProcessType,
1871 PreviousMode,
1872 (PVOID*)&Process,
1873 NULL);
1874 if (!NT_SUCCESS(Status)) return Status;
1875
1876 /* Reference the debug object */
1877 Status = ObReferenceObjectByHandle(DebugHandle,
1878 DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1879 DbgkDebugObjectType,
1880 PreviousMode,
1881 (PVOID*)&DebugObject,
1882 NULL);
1883 if (!NT_SUCCESS(Status))
1884 {
1885 /* Dereference the process and exit */
1886 ObDereferenceObject(Process);
1887 return Status;
1888 }
1889
1890 /* Remove the debug object */
1891 Status = DbgkClearProcessDebugObject(Process, DebugObject);
1892
1893 /* Dereference the process and debug object and return status */
1894 ObDereferenceObject(Process);
1895 ObDereferenceObject(DebugObject);
1896 return Status;
1897 }
1898
1899 /*
1900 * @implemented
1901 */
1902 NTSTATUS
1903 NTAPI
1904 NtSetInformationDebugObject(IN HANDLE DebugHandle,
1905 IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,
1906 IN PVOID DebugInformation,
1907 IN ULONG DebugInformationLength,
1908 OUT PULONG ReturnLength OPTIONAL)
1909 {
1910 PDEBUG_OBJECT DebugObject;
1911 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1912 NTSTATUS Status;
1913 PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo = DebugInformation;
1914 PAGED_CODE();
1915
1916 /* Check buffers and parameters */
1917 Status = DefaultSetInfoBufferCheck(DebugObjectInformationClass,
1918 DbgkpDebugObjectInfoClass,
1919 sizeof(DbgkpDebugObjectInfoClass) /
1920 sizeof(DbgkpDebugObjectInfoClass[0]),
1921 DebugInformation,
1922 DebugInformationLength,
1923 PreviousMode);
1924 if (!NT_SUCCESS(Status)) return Status;
1925
1926 /* Check if the caller wanted the return length */
1927 if (ReturnLength)
1928 {
1929 /* Enter SEH for probe */
1930 _SEH2_TRY
1931 {
1932 /* Return required length to user-mode */
1933 ProbeForWriteUlong(ReturnLength);
1934 *ReturnLength = sizeof(*DebugInfo);
1935 }
1936 _SEH2_EXCEPT(ExSystemExceptionFilter())
1937 {
1938 /* Return the exception code */
1939 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1940 }
1941 _SEH2_END;
1942 }
1943
1944 /* Open the Object */
1945 Status = ObReferenceObjectByHandle(DebugHandle,
1946 DEBUG_OBJECT_WAIT_STATE_CHANGE,
1947 DbgkDebugObjectType,
1948 PreviousMode,
1949 (PVOID*)&DebugObject,
1950 NULL);
1951 if (NT_SUCCESS(Status))
1952 {
1953 /* Acquire the object */
1954 ExAcquireFastMutex(&DebugObject->Mutex);
1955
1956 /* Set the proper flag */
1957 if (DebugInfo->KillProcessOnExit)
1958 {
1959 /* Enable killing the process */
1960 DebugObject->KillProcessOnExit = TRUE;
1961 }
1962 else
1963 {
1964 /* Disable */
1965 DebugObject->KillProcessOnExit = FALSE;
1966 }
1967
1968 /* Release the mutex */
1969 ExReleaseFastMutex(&DebugObject->Mutex);
1970
1971 /* Release the Object */
1972 ObDereferenceObject(DebugObject);
1973 }
1974
1975 /* Return Status */
1976 return Status;
1977 }
1978
1979 /*
1980 * @implemented
1981 */
1982 NTSTATUS
1983 NTAPI
1984 NtWaitForDebugEvent(IN HANDLE DebugHandle,
1985 IN BOOLEAN Alertable,
1986 IN PLARGE_INTEGER Timeout OPTIONAL,
1987 OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
1988 {
1989 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1990 LARGE_INTEGER LocalTimeOut;
1991 PEPROCESS Process;
1992 LARGE_INTEGER StartTime;
1993 PETHREAD Thread;
1994 BOOLEAN GotEvent;
1995 LARGE_INTEGER NewTime;
1996 PDEBUG_OBJECT DebugObject;
1997 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
1998 NTSTATUS Status;
1999 PDEBUG_EVENT DebugEvent = NULL, DebugEvent2;
2000 PLIST_ENTRY ListHead, NextEntry, NextEntry2;
2001 PAGED_CODE();
2002 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p\n", DebugHandle);
2003
2004 /* Clear the initial wait state change structure and the timeout */
2005 RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange));
2006 LocalTimeOut.QuadPart = 0;
2007
2008 /* Check if we were called from user mode */
2009 if (PreviousMode != KernelMode)
2010 {
2011 /* Protect probe in SEH */
2012 _SEH2_TRY
2013 {
2014 /* Check if we came with a timeout */
2015 if (Timeout)
2016 {
2017 /* Probe it */
2018 ProbeForReadLargeInteger(Timeout);
2019
2020 /* Make a local copy */
2021 LocalTimeOut = *Timeout;
2022 Timeout = &LocalTimeOut;
2023 }
2024
2025 /* Probe the state change structure */
2026 ProbeForWrite(StateChange, sizeof(*StateChange), sizeof(ULONG));
2027 }
2028 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2029 {
2030 /* Return the exception code */
2031 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2032 }
2033 _SEH2_END;
2034 }
2035 else
2036 {
2037 /* Copy directly */
2038 if (Timeout) LocalTimeOut = *Timeout;
2039 }
2040
2041 /* If we were passed a timeout, query the current time */
2042 if (Timeout) KeQuerySystemTime(&StartTime);
2043
2044 /* Get the debug object */
2045 Status = ObReferenceObjectByHandle(DebugHandle,
2046 DEBUG_OBJECT_WAIT_STATE_CHANGE,
2047 DbgkDebugObjectType,
2048 PreviousMode,
2049 (PVOID*)&DebugObject,
2050 NULL);
2051 if (!NT_SUCCESS(Status)) return Status;
2052
2053 /* Clear process and thread */
2054 Process = NULL;
2055 Thread = NULL;
2056
2057 /* Wait on the debug object given to us */
2058 while (TRUE)
2059 {
2060 Status = KeWaitForSingleObject(&DebugObject->EventsPresent,
2061 Executive,
2062 PreviousMode,
2063 Alertable,
2064 Timeout);
2065 if (!NT_SUCCESS(Status) ||
2066 (Status == STATUS_TIMEOUT) ||
2067 (Status == STATUS_ALERTED) ||
2068 (Status == STATUS_USER_APC))
2069 {
2070 /* Break out the wait */
2071 break;
2072 }
2073
2074 /* Lock the object */
2075 GotEvent = FALSE;
2076 ExAcquireFastMutex(&DebugObject->Mutex);
2077
2078 /* Check if a debugger is connected */
2079 if (DebugObject->DebuggerInactive)
2080 {
2081 /* Not connected */
2082 Status = STATUS_DEBUGGER_INACTIVE;
2083 }
2084 else
2085 {
2086 /* Loop the events */
2087 ListHead = &DebugObject->EventList;
2088 NextEntry = ListHead->Flink;
2089 while (ListHead != NextEntry)
2090 {
2091 /* Get the debug event */
2092 DebugEvent = CONTAINING_RECORD(NextEntry,
2093 DEBUG_EVENT,
2094 EventList);
2095 DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx\n",
2096 DebugEvent, DebugEvent->Flags);
2097
2098 /* Check flags */
2099 if (!(DebugEvent->Flags & (DEBUG_EVENT_INACTIVE | DEBUG_EVENT_READ)))
2100 {
2101 /* We got an event */
2102 GotEvent = TRUE;
2103
2104 /* Loop the list internally */
2105 NextEntry2 = DebugObject->EventList.Flink;
2106 while (NextEntry2 != NextEntry)
2107 {
2108 /* Get the debug event */
2109 DebugEvent2 = CONTAINING_RECORD(NextEntry2,
2110 DEBUG_EVENT,
2111 EventList);
2112
2113 /* Try to match process IDs */
2114 if (DebugEvent2->ClientId.UniqueProcess ==
2115 DebugEvent->ClientId.UniqueProcess)
2116 {
2117 /* Found it, break out */
2118 DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
2119 DebugEvent->BackoutThread = NULL;
2120 GotEvent = FALSE;
2121 break;
2122 }
2123
2124 /* Move to the next entry */
2125 NextEntry2 = NextEntry2->Flink;
2126 }
2127
2128 /* Check if we still have a valid event */
2129 if (GotEvent) break;
2130 }
2131
2132 /* Move to the next entry */
2133 NextEntry = NextEntry->Flink;
2134 }
2135
2136 /* Check if we have an event */
2137 if (GotEvent)
2138 {
2139 /* Save and reference the process and thread */
2140 Process = DebugEvent->Process;
2141 Thread = DebugEvent->Thread;
2142 ObReferenceObject(Process);
2143 ObReferenceObject(Thread);
2144
2145 /* Convert to user-mode structure */
2146 DbgkpConvertKernelToUserStateChange(&WaitStateChange,
2147 DebugEvent);
2148
2149 /* Set flag */
2150 DebugEvent->Flags |= DEBUG_EVENT_READ;
2151 }
2152 else
2153 {
2154 /* Unsignal the event */
2155 KeClearEvent(&DebugObject->EventsPresent);
2156 }
2157
2158 /* Set success */
2159 Status = STATUS_SUCCESS;
2160 }
2161
2162 /* Release the mutex */
2163 ExReleaseFastMutex(&DebugObject->Mutex);
2164 if (!NT_SUCCESS(Status)) break;
2165
2166 /* Check if we got an event */
2167 if (!GotEvent)
2168 {
2169 /* Check if we can wait again */
2170 if (LocalTimeOut.QuadPart < 0)
2171 {
2172 /* Query the new time */
2173 KeQuerySystemTime(&NewTime);
2174
2175 /* Substract times */
2176 LocalTimeOut.QuadPart += (NewTime.QuadPart - StartTime.QuadPart);
2177 StartTime = NewTime;
2178
2179 /* Check if we've timed out */
2180 if (LocalTimeOut.QuadPart >= 0)
2181 {
2182 /* We have, break out of the loop */
2183 Status = STATUS_TIMEOUT;
2184 break;
2185 }
2186 }
2187 }
2188 else
2189 {
2190 /* Open the handles and dereference the objects */
2191 DbgkpOpenHandles(&WaitStateChange, Process, Thread);
2192 ObDereferenceObject(Process);
2193 ObDereferenceObject(Thread);
2194 break;
2195 }
2196 }
2197
2198 /* We're done, dereference the object */
2199 ObDereferenceObject(DebugObject);
2200
2201 /* Protect write with SEH */
2202 _SEH2_TRY
2203 {
2204 /* Return our wait state change structure */
2205 *StateChange = WaitStateChange;
2206 }
2207 _SEH2_EXCEPT(ExSystemExceptionFilter())
2208 {
2209 /* Get SEH Exception code */
2210 Status = _SEH2_GetExceptionCode();
2211 }
2212 _SEH2_END;
2213
2214 /* Return status */
2215 return Status;
2216 }