- DBGK: Use flags instead of magic values for Debug Events -- names taken from http...
[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 TAG('D', 'b', 'g', 'E'));
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 PsGetCurrentProcess()->CreateReported = TRUE;
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 PsGetCurrentProcess()->CreateReported = TRUE;
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 /* We failed. FIXME: Handle this */
716 DPRINT1("Unhandled Dbgk codepath!\n");
717 ASSERT(FALSE);
718 }
719
720 /* Check if this was the first message */
721 if (First)
722 {
723 /* It isn't the first thread anymore */
724 IsFirstThread = FALSE;
725
726 /* Reference this thread and set it as first */
727 ObReferenceObject(ThisThread);
728 pFirstThread = ThisThread;
729 }
730
731 /* Get the next thread */
732 ThisThread = PsGetNextProcessThread(Process, ThisThread);
733 OldThread = pLastThread;
734 } while (ThisThread);
735
736 /* Check the API status */
737 if (!NT_SUCCESS(Status))
738 {
739 /* We failed. FIXME: Handle this */
740 DPRINT1("Unhandled Dbgk codepath!\n");
741 ASSERT(FALSE);
742 }
743
744 /* Make sure we have a first thread */
745 if (!pFirstThread) return STATUS_UNSUCCESSFUL;
746
747 /* Return thread pointers */
748 *FirstThread = pFirstThread;
749 *LastThread = pLastThread;
750 return Status;
751 }
752
753 NTSTATUS
754 NTAPI
755 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process,
756 IN PDEBUG_OBJECT DebugObject,
757 OUT PETHREAD *LastThread)
758 {
759 KAPC_STATE ApcState;
760 PETHREAD FirstThread, FinalThread;
761 PETHREAD ReturnThread = NULL;
762 NTSTATUS Status;
763 PAGED_CODE();
764 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
765 Process, DebugObject);
766
767 /* Attach to the process */
768 KeStackAttachProcess(&Process->Pcb, &ApcState);
769
770 /* Post the fake thread messages */
771 Status = DbgkpPostFakeThreadMessages(Process,
772 DebugObject,
773 NULL,
774 &FirstThread,
775 &FinalThread);
776 if (NT_SUCCESS(Status))
777 {
778 /* Send the fake module messages too */
779 Status = DbgkpPostFakeModuleMessages(Process,
780 FirstThread,
781 DebugObject);
782 if (!NT_SUCCESS(Status))
783 {
784 /* We failed, dereference the final thread */
785 ObDereferenceObject(FinalThread);
786 }
787 else
788 {
789 /* Set the final thread */
790 ReturnThread = FinalThread;
791 }
792
793 /* Dereference the first thread */
794 ObDereferenceObject(FirstThread);
795 }
796
797 /* Detach from the process */
798 KeUnstackDetachProcess(&ApcState);
799
800 /* Return the last thread */
801 *LastThread = ReturnThread;
802 return Status;
803 }
804
805 VOID
806 NTAPI
807 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
808 IN PDEBUG_EVENT DebugEvent)
809 {
810 DBGKTRACE(DBGK_OBJECT_DEBUG, "DebugEvent: %p\n", DebugEvent);
811
812 /* Start by copying the client ID */
813 WaitStateChange->AppClientId = DebugEvent->ClientId;
814
815 /* Now check which kind of event this was */
816 switch (DebugEvent->ApiMsg.ApiNumber)
817 {
818 /* New process */
819 case DbgKmCreateProcessApi:
820
821 /* Set the right native code */
822 WaitStateChange->NewState = DbgCreateProcessStateChange;
823
824 /* Copy the information */
825 WaitStateChange->StateInfo.CreateProcessInfo.NewProcess =
826 DebugEvent->ApiMsg.CreateProcess;
827
828 /* Clear the file handle for us */
829 DebugEvent->ApiMsg.CreateProcess.FileHandle = NULL;
830 break;
831
832 /* New thread */
833 case DbgKmCreateThreadApi:
834
835 /* Set the right native code */
836 WaitStateChange->NewState = DbgCreateThreadStateChange;
837
838 /* Copy information */
839 WaitStateChange->StateInfo.CreateThread.NewThread.StartAddress =
840 DebugEvent->ApiMsg.CreateThread.StartAddress;
841 WaitStateChange->StateInfo.CreateThread.NewThread.SubSystemKey =
842 DebugEvent->ApiMsg.CreateThread.SubSystemKey;
843 break;
844
845 /* Exception (or breakpoint/step) */
846 case DbgKmExceptionApi:
847
848 /* Look at the exception code */
849 if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
850 STATUS_BREAKPOINT)
851 {
852 /* Update this as a breakpoint exception */
853 WaitStateChange->NewState = DbgBreakpointStateChange;
854 }
855 else if ((NTSTATUS)DebugEvent->ApiMsg.Exception.ExceptionRecord.ExceptionCode ==
856 STATUS_SINGLE_STEP)
857 {
858 /* Update this as a single step exception */
859 WaitStateChange->NewState = DbgSingleStepStateChange;
860 }
861 else
862 {
863 /* Otherwise, set default exception */
864 WaitStateChange->NewState = DbgExceptionStateChange;
865 }
866
867 /* Copy the exception record */
868 WaitStateChange->StateInfo.Exception.ExceptionRecord =
869 DebugEvent->ApiMsg.Exception.ExceptionRecord;
870 /* Copy FirstChance flag */
871 WaitStateChange->StateInfo.Exception.FirstChance =
872 DebugEvent->ApiMsg.Exception.FirstChance;
873 break;
874
875 /* Process exited */
876 case DbgKmExitProcessApi:
877
878 /* Set the right native code and copy the exit code */
879 WaitStateChange->NewState = DbgExitProcessStateChange;
880 WaitStateChange->StateInfo.ExitProcess.ExitStatus =
881 DebugEvent->ApiMsg.ExitProcess.ExitStatus;
882 break;
883
884 /* Thread exited */
885 case DbgKmExitThreadApi:
886
887 /* Set the right native code */
888 WaitStateChange->NewState = DbgExitThreadStateChange;
889 WaitStateChange->StateInfo.ExitThread.ExitStatus =
890 DebugEvent->ApiMsg.ExitThread.ExitStatus;
891 break;
892
893 /* DLL Load */
894 case DbgKmLoadDllApi:
895
896 /* Set the native code */
897 WaitStateChange->NewState = DbgLoadDllStateChange;
898
899 /* Copy the data */
900 WaitStateChange->StateInfo.LoadDll = DebugEvent->ApiMsg.LoadDll;
901
902 /* Clear the file handle for us */
903 DebugEvent->ApiMsg.LoadDll.FileHandle = NULL;
904 break;
905
906 /* DLL Unload */
907 case DbgKmUnloadDllApi:
908
909 /* Set the native code and copy the address */
910 WaitStateChange->NewState = DbgUnloadDllStateChange;
911 WaitStateChange->StateInfo.UnloadDll.BaseAddress =
912 DebugEvent->ApiMsg.UnloadDll.BaseAddress;
913 break;
914
915 default:
916
917 /* Shouldn't happen */
918 ASSERT(FALSE);
919 }
920 }
921
922 VOID
923 NTAPI
924 DbgkpMarkProcessPeb(IN PEPROCESS Process)
925 {
926 KAPC_STATE ApcState;
927 PAGED_CODE();
928 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p\n", Process);
929
930 /* Acquire process rundown */
931 if (!ExAcquireRundownProtection(&Process->RundownProtect)) return;
932
933 /* Make sure we have a PEB */
934 if (Process->Peb)
935 {
936 /* Attach to the process */
937 KeStackAttachProcess(&Process->Pcb, &ApcState);
938
939 /* Acquire the debug port mutex */
940 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
941
942 /* Set the IsBeingDebugged member of the PEB */
943 Process->Peb->BeingDebugged = (Process->DebugPort) ? TRUE: FALSE;
944
945 /* Release lock */
946 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
947
948 /* Detach from the process */
949 KeUnstackDetachProcess(&ApcState);
950 }
951
952 /* Release rundown protection */
953 ExReleaseRundownProtection(&Process->RundownProtect);
954 }
955
956 VOID
957 NTAPI
958 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
959 IN PEPROCESS Process,
960 IN PETHREAD Thread)
961 {
962 NTSTATUS Status;
963 HANDLE Handle;
964 PHANDLE DupHandle;
965 PAGED_CODE();
966 DBGKTRACE(DBGK_OBJECT_DEBUG, "Process: %p Thread: %p State: %lx\n",
967 Process, Thread, WaitStateChange->NewState);
968
969 /* Check which state this is */
970 switch (WaitStateChange->NewState)
971 {
972 /* New thread */
973 case DbgCreateThreadStateChange:
974
975 /* Get handle to thread */
976 Status = ObOpenObjectByPointer(Thread,
977 0,
978 NULL,
979 THREAD_ALL_ACCESS,
980 PsThreadType,
981 KernelMode,
982 &Handle);
983 if (NT_SUCCESS(Status))
984 {
985 /* Save the thread handle */
986 WaitStateChange->
987 StateInfo.CreateThread.HandleToThread = Handle;
988 }
989 return;
990
991 /* New process */
992 case DbgCreateProcessStateChange:
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.CreateProcessInfo.HandleToThread = Handle;
1007 }
1008
1009 /* Get handle to process */
1010 Status = ObOpenObjectByPointer(Process,
1011 0,
1012 NULL,
1013 PROCESS_ALL_ACCESS,
1014 PsProcessType,
1015 KernelMode,
1016 &Handle);
1017 if (NT_SUCCESS(Status))
1018 {
1019 /* Save the process handle */
1020 WaitStateChange->
1021 StateInfo.CreateProcessInfo.HandleToProcess = Handle;
1022 }
1023
1024 /* Fall through to duplicate file handle */
1025 DupHandle = &WaitStateChange->
1026 StateInfo.CreateProcessInfo.NewProcess.FileHandle;
1027 break;
1028
1029 /* DLL Load */
1030 case DbgLoadDllStateChange:
1031
1032 /* Fall through to duplicate file handle */
1033 DupHandle = &WaitStateChange->StateInfo.LoadDll.FileHandle;
1034 break;
1035
1036 /* Anything else has no handles */
1037 default:
1038 return;
1039 }
1040
1041 /* If we got here, then we have to duplicate a handle, possibly */
1042 Handle = *DupHandle;
1043 if (Handle)
1044 {
1045 /* Duplicate it */
1046 Status = ObDuplicateObject(PsGetCurrentProcess(),
1047 Handle,
1048 PsGetCurrentProcess(),
1049 DupHandle,
1050 0,
1051 0,
1052 DUPLICATE_SAME_ACCESS,
1053 KernelMode);
1054 if (!NT_SUCCESS(Status)) *DupHandle = NULL;
1055
1056 /* Close the original handle */
1057 ObCloseHandle(Handle, KernelMode);
1058 }
1059 }
1060
1061 VOID
1062 NTAPI
1063 DbgkpDeleteObject(IN PVOID DebugObject)
1064 {
1065 PAGED_CODE();
1066
1067 /* Sanity check */
1068 ASSERT(IsListEmpty(&((PDEBUG_OBJECT)DebugObject)->EventList));
1069 }
1070
1071 VOID
1072 NTAPI
1073 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL,
1074 IN PVOID ObjectBody,
1075 IN ACCESS_MASK GrantedAccess,
1076 IN ULONG HandleCount,
1077 IN ULONG SystemHandleCount)
1078 {
1079 PDEBUG_OBJECT DebugObject = ObjectBody;
1080 PEPROCESS Process = NULL;
1081 BOOLEAN DebugPortCleared = FALSE;
1082 PLIST_ENTRY DebugEventList;
1083 PDEBUG_EVENT DebugEvent;
1084 DBGKTRACE(DBGK_OBJECT_DEBUG, "OwnerProcess: %p DebugObject: %p\n",
1085 OwnerProcess, DebugObject);
1086
1087 /* If this isn't the last handle, do nothing */
1088 if (SystemHandleCount > 1) return;
1089
1090 /* Otherwise, lock the debug object */
1091 ExAcquireFastMutex(&DebugObject->Mutex);
1092
1093 /* Set it as inactive */
1094 DebugObject->DebuggerInactive = TRUE;
1095
1096 /* Remove it from the debug event list */
1097 DebugEventList = DebugObject->EventList.Flink;
1098 InitializeListHead(&DebugObject->EventList);
1099
1100 /* Release the lock */
1101 ExReleaseFastMutex(&DebugObject->Mutex);
1102
1103 /* Signal the wait event */
1104 KeSetEvent(&DebugObject->EventsPresent, IO_NO_INCREMENT, FALSE);
1105
1106 /* Start looping each process */
1107 while ((Process = PsGetNextProcess(Process)))
1108 {
1109 /* Check if the process has us as their debug port */
1110 if (Process->DebugPort == DebugObject)
1111 {
1112 /* Acquire the process debug port lock */
1113 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1114
1115 /* Check if it's still us */
1116 if (Process->DebugPort == DebugObject)
1117 {
1118 /* Clear it and remember */
1119 Process->DebugPort = NULL;
1120 DebugPortCleared = TRUE;
1121 }
1122
1123 /* Release the port lock */
1124 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1125
1126 /* Check if we cleared the debug port */
1127 if (DebugPortCleared)
1128 {
1129 /* Mark this in the PEB */
1130 DbgkpMarkProcessPeb(Process);
1131
1132 /* Check if we terminate on exit */
1133 if (DebugObject->KillProcessOnExit)
1134 {
1135 /* Terminate the process */
1136 PsTerminateProcess(Process, STATUS_DEBUGGER_INACTIVE);
1137 }
1138
1139 /* Dereference the debug object */
1140 ObDereferenceObject(DebugObject);
1141 }
1142 }
1143 }
1144
1145 /* Loop debug events */
1146 while (DebugEventList != &DebugObject->EventList)
1147 {
1148 /* Get the debug event */
1149 DebugEvent = CONTAINING_RECORD(DebugEventList, DEBUG_EVENT, EventList);
1150
1151 /* Go to the next entry */
1152 DebugEventList = DebugEventList->Flink;
1153
1154 /* Wake it up */
1155 DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
1156 DbgkpWakeTarget(DebugEvent);
1157 }
1158 }
1159
1160 NTSTATUS
1161 NTAPI
1162 DbgkpSetProcessDebugObject(IN PEPROCESS Process,
1163 IN PDEBUG_OBJECT DebugObject,
1164 IN NTSTATUS MsgStatus,
1165 IN PETHREAD LastThread)
1166 {
1167 NTSTATUS Status;
1168 LIST_ENTRY TempList;
1169 BOOLEAN GlobalHeld = FALSE, DoSetEvent = TRUE;
1170 PETHREAD ThisThread, FirstThread;
1171 PLIST_ENTRY NextEntry;
1172 PDEBUG_EVENT DebugEvent;
1173 PETHREAD EventThread;
1174 PAGED_CODE();
1175 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
1176 Process, DebugObject);
1177
1178 /* Initialize the temporary list */
1179 InitializeListHead(&TempList);
1180
1181 /* Check if we have a success message */
1182 if (NT_SUCCESS(MsgStatus))
1183 {
1184 /* Then default to STATUS_SUCCESS */
1185 Status = STATUS_SUCCESS;
1186 }
1187 else
1188 {
1189 /* No last thread, and set the failure code */
1190 LastThread = NULL;
1191 Status = MsgStatus;
1192 }
1193
1194 /* Now check what status we have here */
1195 if (NT_SUCCESS(Status))
1196 {
1197 /* Acquire the global lock */
1198 ThreadScan:
1199 GlobalHeld = TRUE;
1200 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);
1201
1202 /* Check if we already have a port */
1203 if (Process->DebugPort)
1204 {
1205 /* Set failure */
1206 Status = STATUS_PORT_ALREADY_SET;
1207 }
1208 else
1209 {
1210 /* Otherwise, set the port and reference the thread */
1211 Process->DebugPort = DebugObject;
1212 ObReferenceObject(LastThread);
1213
1214 /* Get the next thread */
1215 ThisThread = PsGetNextProcessThread(Process, LastThread);
1216 if (ThisThread)
1217 {
1218 /* Clear the debug port and release the lock */
1219 Process->DebugPort = NULL;
1220 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1221 GlobalHeld = FALSE;
1222
1223 /* Dereference the thread */
1224 ObDereferenceObject(LastThread);
1225
1226 /* Post fake messages */
1227 Status = DbgkpPostFakeThreadMessages(Process,
1228 DebugObject,
1229 ThisThread,
1230 &FirstThread,
1231 &LastThread);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 /* Clear the last thread */
1235 LastThread = NULL;
1236 }
1237 else
1238 {
1239 /* Dereference the first thread and re-acquire the lock */
1240 ObDereferenceObject(FirstThread);
1241 goto ThreadScan;
1242 }
1243 }
1244 }
1245 }
1246
1247 /* Acquire the debug object's lock */
1248 ExAcquireFastMutex(&DebugObject->Mutex);
1249
1250 /* Check our status here */
1251 if (NT_SUCCESS(Status))
1252 {
1253 /* Check if we're disconnected */
1254 if (DebugObject->DebuggerInactive)
1255 {
1256 /* Set status */
1257 Process->DebugPort = NULL;
1258 Status = STATUS_DEBUGGER_INACTIVE;
1259 }
1260 else
1261 {
1262 /* Set the process flags */
1263 InterlockedOr((PLONG)&Process->Flags,
1264 PSF_NO_DEBUG_INHERIT_BIT | PSF_CREATE_REPORTED_BIT);
1265
1266 /* Reference the debug object */
1267 ObReferenceObject(DebugObject);
1268 }
1269 }
1270
1271 /* Loop the events list */
1272 NextEntry = DebugObject->EventList.Flink;
1273 while (NextEntry != &DebugObject->EventList)
1274 {
1275 /* Get the debug event */
1276 DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList);
1277 DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1278 DebugEvent, DebugEvent->Flags,
1279 DebugEvent->BackoutThread, PsGetCurrentThread());
1280
1281 /* Check for if the debug event queue needs flushing */
1282 if ((DebugEvent->Flags & DEBUG_EVENT_INACTIVE) &&
1283 (DebugEvent->BackoutThread == PsGetCurrentThread()))
1284 {
1285 /* Get the event's thread */
1286 EventThread = DebugEvent->Thread;
1287 DBGKTRACE(DBGK_PROCESS_DEBUG, "EventThread: %p MsgStatus: %lx\n",
1288 EventThread, MsgStatus);
1289
1290 /* Check if the status is success */
1291 if ((MsgStatus == STATUS_SUCCESS) &&
1292 (EventThread->GrantedAccess) &&
1293 (!EventThread->SystemThread))
1294 {
1295 /* Check if we couldn't acquire rundown for it */
1296 if (DebugEvent->Flags & DEBUG_EVENT_PROTECT_FAILED)
1297 {
1298 /* Set the skip termination flag */
1299 PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1300
1301 /* Insert it into the temp list */
1302 RemoveEntryList(&DebugEvent->EventList);
1303 InsertTailList(&TempList, &DebugEvent->EventList);
1304 }
1305 else
1306 {
1307 /* Do we need to signal the event */
1308 if (DoSetEvent)
1309 {
1310 /* Do it */
1311 DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1312 KeSetEvent(&DebugObject->EventsPresent,
1313 IO_NO_INCREMENT,
1314 FALSE);
1315 DoSetEvent = FALSE;
1316 }
1317
1318 /* Clear the backout thread */
1319 DebugEvent->BackoutThread = NULL;
1320
1321 /* Set skip flag */
1322 PspSetCrossThreadFlag(EventThread, CT_SKIP_CREATION_MSG_BIT);
1323 }
1324 }
1325 else
1326 {
1327 /* Insert it into the temp list */
1328 RemoveEntryList(&DebugEvent->EventList);
1329 InsertTailList(&TempList, &DebugEvent->EventList);
1330 }
1331
1332 /* Check if the lock is held */
1333 if (DebugEvent->Flags & DEBUG_EVENT_RELEASE)
1334 {
1335 /* Release it */
1336 DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
1337 ExReleaseRundownProtection(&EventThread->RundownProtect);
1338 }
1339 }
1340
1341 /* Go to the next entry */
1342 NextEntry = NextEntry->Flink;
1343 }
1344
1345 /* Release the debug object */
1346 ExReleaseFastMutex(&DebugObject->Mutex);
1347
1348 /* Release the global lock if acquired */
1349 if (GlobalHeld) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);
1350
1351 /* Check if there's a thread to dereference */
1352 if (LastThread) ObDereferenceObject(LastThread);
1353
1354 /* Loop our temporary list */
1355 while (!IsListEmpty(&TempList))
1356 {
1357 /* Remove the event */
1358 NextEntry = RemoveHeadList(&TempList);
1359 DebugEvent = CONTAINING_RECORD (NextEntry, DEBUG_EVENT, EventList);
1360
1361 /* Wake it */
1362 DbgkpWakeTarget(DebugEvent);
1363 }
1364
1365 /* Check if we got here through success and mark the PEB, then return */
1366 if (NT_SUCCESS(Status)) DbgkpMarkProcessPeb(Process);
1367 return Status;
1368 }
1369
1370 NTSTATUS
1371 NTAPI
1372 DbgkClearProcessDebugObject(IN PEPROCESS Process,
1373 IN PDEBUG_OBJECT SourceDebugObject)
1374 {
1375 /* FIXME: TODO */
1376 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p DebugObject: %p\n",
1377 Process, SourceDebugObject);
1378 return STATUS_UNSUCCESSFUL;
1379 }
1380
1381 VOID
1382 INIT_FUNCTION
1383 NTAPI
1384 DbgkInitialize(VOID)
1385 {
1386 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1387 UNICODE_STRING Name;
1388 PAGED_CODE();
1389
1390 /* Initialize the process debug port mutex */
1391 ExInitializeFastMutex(&DbgkpProcessDebugPortMutex);
1392
1393 /* Create the Debug Object Type */
1394 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1395 RtlInitUnicodeString(&Name, L"DebugObject");
1396 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1397 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DEBUG_OBJECT);
1398 ObjectTypeInitializer.GenericMapping = DbgkDebugObjectMapping;
1399 ObjectTypeInitializer.PoolType = NonPagedPool;
1400 ObjectTypeInitializer.ValidAccessMask = DEBUG_OBJECT_ALL_ACCESS;
1401 ObjectTypeInitializer.UseDefaultObject = TRUE;
1402 ObjectTypeInitializer.CloseProcedure = DbgkpCloseObject;
1403 ObjectTypeInitializer.DeleteProcedure = DbgkpDeleteObject;
1404 ObCreateObjectType(&Name,
1405 &ObjectTypeInitializer,
1406 NULL,
1407 &DbgkDebugObjectType);
1408 }
1409
1410 /* PUBLIC FUNCTIONS **********************************************************/
1411
1412 NTSTATUS
1413 NTAPI
1414 NtCreateDebugObject(OUT PHANDLE DebugHandle,
1415 IN ACCESS_MASK DesiredAccess,
1416 IN POBJECT_ATTRIBUTES ObjectAttributes,
1417 IN BOOLEAN KillProcessOnExit)
1418 {
1419 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1420 PDEBUG_OBJECT DebugObject;
1421 HANDLE hDebug;
1422 NTSTATUS Status = STATUS_SUCCESS;
1423 PAGED_CODE();
1424
1425 /* Check if we were called from user mode*/
1426 if (PreviousMode != KernelMode)
1427 {
1428 /* Enter SEH for probing */
1429 _SEH2_TRY
1430 {
1431 /* Probe the handle */
1432 ProbeForWriteHandle(DebugHandle);
1433 }
1434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1435 {
1436 /* Get exception error */
1437 Status = _SEH2_GetExceptionCode();
1438 } _SEH2_END;
1439 if (!NT_SUCCESS(Status)) return Status;
1440 }
1441
1442 /* Create the Object */
1443 Status = ObCreateObject(PreviousMode,
1444 DbgkDebugObjectType,
1445 ObjectAttributes,
1446 PreviousMode,
1447 NULL,
1448 sizeof(DEBUG_OBJECT),
1449 0,
1450 0,
1451 (PVOID*)&DebugObject);
1452 if (NT_SUCCESS(Status))
1453 {
1454 /* Initialize the Debug Object's Fast Mutex */
1455 ExInitializeFastMutex(&DebugObject->Mutex);
1456
1457 /* Initialize the State Event List */
1458 InitializeListHead(&DebugObject->EventList);
1459
1460 /* Initialize the Debug Object's Wait Event */
1461 KeInitializeEvent(&DebugObject->EventsPresent,
1462 NotificationEvent,
1463 FALSE);
1464
1465 /* Set the Flags */
1466 DebugObject->KillProcessOnExit = KillProcessOnExit;
1467
1468 /* Insert it */
1469 Status = ObInsertObject((PVOID)DebugObject,
1470 NULL,
1471 DesiredAccess,
1472 0,
1473 NULL,
1474 &hDebug);
1475 if (NT_SUCCESS(Status))
1476 {
1477 _SEH2_TRY
1478 {
1479 *DebugHandle = hDebug;
1480 }
1481 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1482 {
1483 Status = _SEH2_GetExceptionCode();
1484 } _SEH2_END;
1485 }
1486 }
1487
1488 /* Return Status */
1489 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p DebugObject: %p\n",
1490 hDebug, DebugObject);
1491 return Status;
1492 }
1493
1494 NTSTATUS
1495 NTAPI
1496 NtDebugContinue(IN HANDLE DebugHandle,
1497 IN PCLIENT_ID AppClientId,
1498 IN NTSTATUS ContinueStatus)
1499 {
1500 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1501 PDEBUG_OBJECT DebugObject;
1502 NTSTATUS Status = STATUS_SUCCESS;
1503 PDEBUG_EVENT DebugEvent = NULL, DebugEventToWake = NULL;
1504 PLIST_ENTRY ListHead, NextEntry;
1505 BOOLEAN NeedsWake = FALSE;
1506 CLIENT_ID ClientId;
1507 PAGED_CODE();
1508 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p Status: %p\n",
1509 DebugHandle, ContinueStatus);
1510
1511 /* Check if we were called from user mode*/
1512 if (PreviousMode != KernelMode)
1513 {
1514 /* Enter SEH for probing */
1515 _SEH2_TRY
1516 {
1517 /* Probe the handle */
1518 ProbeForRead(AppClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1519 ClientId = *AppClientId;
1520 AppClientId = &ClientId;
1521 }
1522 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1523 {
1524 /* Get exception error */
1525 Status = _SEH2_GetExceptionCode();
1526 } _SEH2_END;
1527 if (!NT_SUCCESS(Status)) return Status;
1528 }
1529
1530 /* Make sure that the status is valid */
1531 if ((ContinueStatus != DBG_CONTINUE) &&
1532 (ContinueStatus != DBG_EXCEPTION_HANDLED) &&
1533 (ContinueStatus != DBG_EXCEPTION_NOT_HANDLED) &&
1534 (ContinueStatus != DBG_TERMINATE_THREAD) &&
1535 (ContinueStatus != DBG_TERMINATE_PROCESS))
1536 {
1537 /* Invalid status */
1538 Status = STATUS_INVALID_PARAMETER;
1539 }
1540 else
1541 {
1542 /* Get the debug object */
1543 Status = ObReferenceObjectByHandle(DebugHandle,
1544 DEBUG_OBJECT_WAIT_STATE_CHANGE,
1545 DbgkDebugObjectType,
1546 PreviousMode,
1547 (PVOID*)&DebugObject,
1548 NULL);
1549 if (NT_SUCCESS(Status))
1550 {
1551 /* Acquire the mutex */
1552 ExAcquireFastMutex(&DebugObject->Mutex);
1553
1554 /* Loop the state list */
1555 ListHead = &DebugObject->EventList;
1556 NextEntry = ListHead->Flink;
1557 while (ListHead != NextEntry)
1558 {
1559 /* Get the current debug event */
1560 DebugEvent = CONTAINING_RECORD(NextEntry,
1561 DEBUG_EVENT,
1562 EventList);
1563
1564 /* Compare process ID */
1565 if (DebugEvent->ClientId.UniqueProcess ==
1566 AppClientId->UniqueProcess)
1567 {
1568 /* Check if we already found a match */
1569 if (NeedsWake)
1570 {
1571 /* Wake it up and break out */
1572 DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
1573 KeSetEvent(&DebugEvent->ContinueEvent,
1574 IO_NO_INCREMENT,
1575 FALSE);
1576 break;
1577 }
1578
1579 /* Compare thread ID and flag */
1580 if ((DebugEvent->ClientId.UniqueThread ==
1581 AppClientId->UniqueThread) && (DebugEvent->Flags & DEBUG_EVENT_READ))
1582 {
1583 /* Remove the event from the list */
1584 RemoveEntryList(NextEntry);
1585
1586 /* Remember who to wake */
1587 NeedsWake = TRUE;
1588 DebugEventToWake = DebugEvent;
1589 }
1590 }
1591
1592 /* Go to the next entry */
1593 NextEntry = NextEntry->Flink;
1594 }
1595
1596 /* Release the mutex */
1597 ExReleaseFastMutex(&DebugObject->Mutex);
1598
1599 /* Dereference the object */
1600 ObDereferenceObject(DebugObject);
1601
1602 /* Check if need a wait */
1603 if (NeedsWake)
1604 {
1605 /* Set the continue status */
1606 DebugEvent->ApiMsg.ReturnedStatus = ContinueStatus;
1607 DebugEvent->Status = STATUS_SUCCESS;
1608
1609 /* Wake the target */
1610 DbgkpWakeTarget(DebugEvent);
1611 }
1612 else
1613 {
1614 /* Fail */
1615 Status = STATUS_INVALID_PARAMETER;
1616 }
1617 }
1618 }
1619
1620 /* Return status */
1621 return Status;
1622 }
1623
1624 NTSTATUS
1625 NTAPI
1626 NtDebugActiveProcess(IN HANDLE ProcessHandle,
1627 IN HANDLE DebugHandle)
1628 {
1629 PEPROCESS Process;
1630 PDEBUG_OBJECT DebugObject;
1631 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1632 PETHREAD LastThread;
1633 NTSTATUS Status;
1634 PAGED_CODE();
1635 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1636 ProcessHandle, DebugHandle);
1637
1638 /* Reference the process */
1639 Status = ObReferenceObjectByHandle(ProcessHandle,
1640 PROCESS_SUSPEND_RESUME,
1641 PsProcessType,
1642 PreviousMode,
1643 (PVOID*)&Process,
1644 NULL);
1645 if (!NT_SUCCESS(Status)) return Status;
1646
1647 /* Don't allow debugging the initial system process */
1648 if (Process == PsInitialSystemProcess)
1649 {
1650 /* Dereference and fail */
1651 ObDereferenceObject(Process);
1652 return STATUS_ACCESS_DENIED;
1653 }
1654
1655 /* Reference the debug object */
1656 Status = ObReferenceObjectByHandle(DebugHandle,
1657 DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1658 DbgkDebugObjectType,
1659 PreviousMode,
1660 (PVOID*)&DebugObject,
1661 NULL);
1662 if (!NT_SUCCESS(Status))
1663 {
1664 /* Dereference the process and exit */
1665 ObDereferenceObject(Process);
1666 return Status;
1667 }
1668
1669 /* Acquire process rundown protection */
1670 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1671 {
1672 /* Dereference the process and debug object and exit */
1673 ObDereferenceObject(Process);
1674 ObDereferenceObject(DebugObject);
1675 return STATUS_PROCESS_IS_TERMINATING;
1676 }
1677
1678 /* Send fake create messages for debuggers to have a consistent state */
1679 Status = DbgkpPostFakeProcessCreateMessages(Process,
1680 DebugObject,
1681 &LastThread);
1682 Status = DbgkpSetProcessDebugObject(Process,
1683 DebugObject,
1684 Status,
1685 LastThread);
1686
1687 /* Release rundown protection */
1688 ExReleaseRundownProtection(&Process->RundownProtect);
1689
1690 /* Dereference the process and debug object and return status */
1691 ObDereferenceObject(Process);
1692 ObDereferenceObject(DebugObject);
1693 return Status;
1694 }
1695
1696 NTSTATUS
1697 NTAPI
1698 NtRemoveProcessDebug(IN HANDLE ProcessHandle,
1699 IN HANDLE DebugHandle)
1700 {
1701 PEPROCESS Process;
1702 PDEBUG_OBJECT DebugObject;
1703 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1704 NTSTATUS Status;
1705 PAGED_CODE();
1706 DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n",
1707 ProcessHandle, DebugHandle);
1708
1709 /* Reference the process */
1710 Status = ObReferenceObjectByHandle(ProcessHandle,
1711 PROCESS_SUSPEND_RESUME,
1712 PsProcessType,
1713 PreviousMode,
1714 (PVOID*)&Process,
1715 NULL);
1716 if (!NT_SUCCESS(Status)) return Status;
1717
1718 /* Reference the debug object */
1719 Status = ObReferenceObjectByHandle(DebugHandle,
1720 DEBUG_OBJECT_ADD_REMOVE_PROCESS,
1721 DbgkDebugObjectType,
1722 PreviousMode,
1723 (PVOID*)&DebugObject,
1724 NULL);
1725 if (!NT_SUCCESS(Status))
1726 {
1727 /* Dereference the process and exit */
1728 ObDereferenceObject(Process);
1729 return Status;
1730 }
1731
1732 /* Remove the debug object */
1733 Status = DbgkClearProcessDebugObject(Process, DebugObject);
1734
1735 /* Dereference the process and debug object and return status */
1736 ObDereferenceObject(Process);
1737 ObDereferenceObject(DebugObject);
1738 return Status;
1739 }
1740
1741 NTSTATUS
1742 NTAPI
1743 NtSetInformationDebugObject(IN HANDLE DebugHandle,
1744 IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,
1745 IN PVOID DebugInformation,
1746 IN ULONG DebugInformationLength,
1747 OUT PULONG ReturnLength OPTIONAL)
1748 {
1749 PDEBUG_OBJECT DebugObject;
1750 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1751 NTSTATUS Status = STATUS_SUCCESS;
1752 PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo = DebugInformation;
1753 PAGED_CODE();
1754
1755 /* Check buffers and parameters */
1756 Status = DefaultSetInfoBufferCheck(DebugObjectInformationClass,
1757 DbgkpDebugObjectInfoClass,
1758 sizeof(DbgkpDebugObjectInfoClass) /
1759 sizeof(DbgkpDebugObjectInfoClass[0]),
1760 DebugInformation,
1761 DebugInformationLength,
1762 PreviousMode);
1763
1764 /* Check if the caller wanted the return length */
1765 if (ReturnLength)
1766 {
1767 /* Enter SEH for probe */
1768 _SEH2_TRY
1769 {
1770 /* Return required length to user-mode */
1771 ProbeForWriteUlong(ReturnLength);
1772 *ReturnLength = sizeof(*DebugInfo);
1773 }
1774 _SEH2_EXCEPT(ExSystemExceptionFilter())
1775 {
1776 /* Get SEH Exception code */
1777 Status = _SEH2_GetExceptionCode();
1778 }
1779 _SEH2_END;
1780 }
1781 if (!NT_SUCCESS(Status)) return Status;
1782
1783 /* Open the Object */
1784 Status = ObReferenceObjectByHandle(DebugHandle,
1785 DEBUG_OBJECT_WAIT_STATE_CHANGE,
1786 DbgkDebugObjectType,
1787 PreviousMode,
1788 (PVOID*)&DebugObject,
1789 NULL);
1790 if (NT_SUCCESS(Status))
1791 {
1792 /* Acquire the object */
1793 ExAcquireFastMutex(&DebugObject->Mutex);
1794
1795 /* Set the proper flag */
1796 if (DebugInfo->KillProcessOnExit)
1797 {
1798 /* Enable killing the process */
1799 DebugObject->KillProcessOnExit = TRUE;
1800 }
1801 else
1802 {
1803 /* Disable */
1804 DebugObject->KillProcessOnExit = FALSE;
1805 }
1806
1807 /* Release the mutex */
1808 ExReleaseFastMutex(&DebugObject->Mutex);
1809
1810 /* Release the Object */
1811 ObDereferenceObject(DebugObject);
1812 }
1813
1814 /* Return Status */
1815 return Status;
1816 }
1817
1818 NTSTATUS
1819 NTAPI
1820 NtWaitForDebugEvent(IN HANDLE DebugHandle,
1821 IN BOOLEAN Alertable,
1822 IN PLARGE_INTEGER Timeout OPTIONAL,
1823 OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
1824 {
1825 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1826 LARGE_INTEGER SafeTimeOut;
1827 PEPROCESS Process;
1828 LARGE_INTEGER StartTime;
1829 PETHREAD Thread;
1830 BOOLEAN GotEvent;
1831 LARGE_INTEGER NewTime;
1832 PDEBUG_OBJECT DebugObject;
1833 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
1834 NTSTATUS Status = STATUS_SUCCESS;
1835 PDEBUG_EVENT DebugEvent = NULL, DebugEvent2;
1836 PLIST_ENTRY ListHead, NextEntry, NextEntry2;
1837 PAGED_CODE();
1838 DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p\n", DebugHandle);
1839
1840 /* Clear the initial wait state change structure */
1841 RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange));
1842
1843 /* Protect probe in SEH */
1844 _SEH2_TRY
1845 {
1846 /* Check if we came with a timeout */
1847 if (Timeout)
1848 {
1849 /* Check if the call was from user mode */
1850 if (PreviousMode != KernelMode)
1851 {
1852 /* Probe it */
1853 ProbeForReadLargeInteger(Timeout);
1854 }
1855
1856 /* Make a local copy */
1857 SafeTimeOut = *Timeout;
1858 Timeout = &SafeTimeOut;
1859
1860 /* Query the current time */
1861 KeQuerySystemTime(&StartTime);
1862 }
1863
1864 /* Check if the call was from user mode */
1865 if (PreviousMode != KernelMode)
1866 {
1867 /* Probe the state change structure */
1868 ProbeForWrite(StateChange, sizeof(*StateChange), sizeof(ULONG));
1869 }
1870 }
1871 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1872 {
1873 /* Get the exception code */
1874 Status = _SEH2_GetExceptionCode();
1875 }
1876 _SEH2_END;
1877 if (!NT_SUCCESS(Status)) return Status;
1878
1879 /* Get the debug object */
1880 Status = ObReferenceObjectByHandle(DebugHandle,
1881 DEBUG_OBJECT_WAIT_STATE_CHANGE,
1882 DbgkDebugObjectType,
1883 PreviousMode,
1884 (PVOID*)&DebugObject,
1885 NULL);
1886 if (!NT_SUCCESS(Status)) return Status;
1887
1888 /* Clear process and thread */
1889 Process = NULL;
1890 Thread = NULL;
1891
1892 /* Wait on the debug object given to us */
1893 while (TRUE)
1894 {
1895 Status = KeWaitForSingleObject(DebugObject,
1896 Executive,
1897 PreviousMode,
1898 Alertable,
1899 Timeout);
1900 if (!NT_SUCCESS(Status) ||
1901 (Status == STATUS_TIMEOUT) ||
1902 (Status == STATUS_ALERTED) ||
1903 (Status == STATUS_USER_APC))
1904 {
1905 /* Break out the wait */
1906 break;
1907 }
1908
1909 /* Lock the object */
1910 GotEvent = FALSE;
1911 ExAcquireFastMutex(&DebugObject->Mutex);
1912
1913 /* Check if a debugger is connected */
1914 if (DebugObject->DebuggerInactive)
1915 {
1916 /* Not connected */
1917 Status = STATUS_DEBUGGER_INACTIVE;
1918 }
1919 else
1920 {
1921 /* Loop the events */
1922 ListHead = &DebugObject->EventList;
1923 NextEntry = ListHead->Flink;
1924 while (ListHead != NextEntry)
1925 {
1926 /* Get the debug event */
1927 DebugEvent = CONTAINING_RECORD(NextEntry,
1928 DEBUG_EVENT,
1929 EventList);
1930 DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx\n",
1931 DebugEvent, DebugEvent->Flags);
1932
1933 /* Check flags */
1934 if (!(DebugEvent->Flags & (DEBUG_EVENT_INACTIVE | DEBUG_EVENT_READ)))
1935 {
1936 /* We got an event */
1937 GotEvent = TRUE;
1938
1939 /* Loop the list internally */
1940 NextEntry2 = DebugObject->EventList.Flink;
1941 while (NextEntry2 != NextEntry)
1942 {
1943 /* Get the debug event */
1944 DebugEvent2 = CONTAINING_RECORD(NextEntry2,
1945 DEBUG_EVENT,
1946 EventList);
1947
1948 /* Try to match process IDs */
1949 if (DebugEvent2->ClientId.UniqueProcess ==
1950 DebugEvent->ClientId.UniqueProcess)
1951 {
1952 /* Found it, break out */
1953 DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
1954 DebugEvent->BackoutThread = NULL;
1955 GotEvent = FALSE;
1956 break;
1957 }
1958
1959 /* Move to the next entry */
1960 NextEntry2 = NextEntry2->Flink;
1961 }
1962
1963 /* Check if we still have a valid event */
1964 if (GotEvent) break;
1965 }
1966
1967 /* Move to the next entry */
1968 NextEntry = NextEntry->Flink;
1969 }
1970
1971 /* Check if we have an event */
1972 if (GotEvent)
1973 {
1974 /* Save and reference the process and thread */
1975 Process = DebugEvent->Process;
1976 Thread = DebugEvent->Thread;
1977 ObReferenceObject(Process);
1978 ObReferenceObject(Thread);
1979
1980 /* Convert to user-mode structure */
1981 DbgkpConvertKernelToUserStateChange(&WaitStateChange,
1982 DebugEvent);
1983
1984 /* Set flag */
1985 DebugEvent->Flags |= DEBUG_EVENT_READ;
1986 }
1987 else
1988 {
1989 /* Unsignal the event */
1990 KeClearEvent(&DebugObject->EventsPresent);
1991 }
1992
1993 /* Set success */
1994 Status = STATUS_SUCCESS;
1995 }
1996
1997 /* Release the mutex */
1998 ExReleaseFastMutex(&DebugObject->Mutex);
1999 if (!NT_SUCCESS(Status)) break;
2000
2001 /* Check if we got an event */
2002 if (!GotEvent)
2003 {
2004 /* Check if we can wait again */
2005 if (SafeTimeOut.QuadPart < 0)
2006 {
2007 /* Query the new time */
2008 KeQuerySystemTime(&NewTime);
2009
2010 /* Substract times */
2011 SafeTimeOut.QuadPart += (NewTime.QuadPart - StartTime.QuadPart);
2012 StartTime = NewTime;
2013
2014 /* Check if we've timed out */
2015 if (SafeTimeOut.QuadPart >= 0)
2016 {
2017 /* We have, break out of the loop */
2018 Status = STATUS_TIMEOUT;
2019 break;
2020 }
2021 }
2022 }
2023 else
2024 {
2025 /* Open the handles and dereference the objects */
2026 DbgkpOpenHandles(&WaitStateChange, Process, Thread);
2027 ObDereferenceObject(Process);
2028 ObDereferenceObject(Thread);
2029 break;
2030 }
2031 }
2032
2033 /* We're done, dereference the object */
2034 ObDereferenceObject(DebugObject);
2035
2036 /* Protect write with SEH */
2037 _SEH2_TRY
2038 {
2039 /* Return our wait state change structure */
2040 *StateChange = WaitStateChange;
2041 }
2042 _SEH2_EXCEPT(ExSystemExceptionFilter())
2043 {
2044 /* Get SEH Exception code */
2045 Status = _SEH2_GetExceptionCode();
2046 }
2047 _SEH2_END;
2048
2049 /* Return status */
2050 return Status;
2051 }