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