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