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