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