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)
9 /* INCLUDES ******************************************************************/
15 POBJECT_TYPE DbgkDebugObjectType
;
16 FAST_MUTEX DbgkpProcessDebugPortMutex
;
17 ULONG DbgkpTraceLevel
= 0;
19 GENERIC_MAPPING DbgkDebugObjectMapping
=
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
27 static const INFORMATION_CLASS_INFO DbgkpDebugObjectInfoClass
[] =
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
),
35 /* PRIVATE FUNCTIONS *********************************************************/
39 DbgkpQueueMessage(IN PEPROCESS Process
,
41 IN PDBGKM_MSG Message
,
43 IN PDEBUG_OBJECT TargetObject OPTIONAL
)
45 PDEBUG_EVENT DebugEvent
;
46 DEBUG_EVENT LocalDebugEvent
;
47 PDEBUG_OBJECT DebugObject
;
51 DBGKTRACE(DBGK_MESSAGE_DEBUG
,
52 "Process: %p Thread: %p Message: %p Flags: %lx\n",
53 Process
, Thread
, Message
, Flags
);
55 /* Check if we have to allocate a debug event */
56 NewEvent
= (Flags
& DEBUG_EVENT_NOWAIT
) ? TRUE
: FALSE
;
60 DebugEvent
= ExAllocatePoolWithTag(NonPagedPool
,
63 if (!DebugEvent
) return STATUS_INSUFFICIENT_RESOURCES
;
66 DebugEvent
->Flags
= Flags
| DEBUG_EVENT_INACTIVE
;
68 /* Reference the thread and process */
69 ObReferenceObject(Thread
);
70 ObReferenceObject(Process
);
72 /* Set the current thread */
73 DebugEvent
->BackoutThread
= PsGetCurrentThread();
75 /* Set the debug object */
76 DebugObject
= TargetObject
;
80 /* Use the debug event on the stack */
81 DebugEvent
= &LocalDebugEvent
;
82 DebugEvent
->Flags
= Flags
;
84 /* Acquire the port lock */
85 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
87 /* Get the debug object */
88 DebugObject
= Process
->DebugPort
;
90 /* Check what kind of API message this is */
91 switch (Message
->ApiNumber
)
93 /* Process or thread creation */
94 case DbgKmCreateThreadApi
:
95 case DbgKmCreateProcessApi
:
97 /* Make sure we're not skipping creation messages */
98 if (Thread
->SkipCreationMsg
) DebugObject
= NULL
;
101 /* Process or thread exit */
102 case DbgKmExitThreadApi
:
103 case DbgKmExitProcessApi
:
105 /* Make sure we're not skipping exit messages */
106 if (Thread
->SkipTerminationMsg
) DebugObject
= NULL
;
108 /* No special handling for other messages */
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
;
121 /* Check if we have a port object */
125 Status
= STATUS_PORT_NOT_SET
;
129 /* Acquire the debug object mutex */
130 ExAcquireFastMutex(&DebugObject
->Mutex
);
132 /* Check if a debugger is active */
133 if (!DebugObject
->DebuggerInactive
)
135 /* Add the event into the object's list */
136 DBGKTRACE(DBGK_MESSAGE_DEBUG
, "Inserting: %p %d\n",
137 DebugEvent
, Message
->ApiNumber
);
138 InsertTailList(&DebugObject
->EventList
, &DebugEvent
->EventList
);
140 /* Check if we have to signal it */
144 KeSetEvent(&DebugObject
->EventsPresent
,
150 Status
= STATUS_SUCCESS
;
155 Status
= STATUS_DEBUGGER_INACTIVE
;
158 /* Release the object lock */
159 ExReleaseFastMutex(&DebugObject
->Mutex
);
162 /* Check if we had acquired the port lock */
166 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
168 /* Check if we got here through success */
169 if (NT_SUCCESS(Status
))
171 /* Wait on the continue event */
172 KeWaitForSingleObject(&DebugEvent
->ContinueEvent
,
178 /* Copy API Message back */
179 *Message
= DebugEvent
->ApiMsg
;
181 /* Set return status */
182 Status
= DebugEvent
->Status
;
187 /* Check if we failed */
188 if (!NT_SUCCESS(Status
))
190 /* Dereference the process and thread */
191 ObDereferenceObject(Thread
);
192 ObDereferenceObject(Process
);
194 /* Free the debug event */
195 ExFreePoolWithTag(DebugEvent
, 'EgbD');
200 DBGKTRACE(DBGK_MESSAGE_DEBUG
, "Status: %lx\n", Status
);
206 DbgkpSendApiMessageLpc(IN OUT PDBGKM_MSG Message
,
208 IN BOOLEAN SuspendProcess
)
211 UCHAR Buffer
[PORT_MAXIMUM_MESSAGE_LENGTH
];
212 BOOLEAN Suspended
= FALSE
;
215 /* Suspend process if required */
216 if (SuspendProcess
) Suspended
= DbgkpSuspendProcess();
218 /* Set return status */
219 Message
->ReturnedStatus
= STATUS_PENDING
;
221 /* Set create process reported state */
222 PspSetProcessFlag(PsGetCurrentProcess(), PSF_CREATE_REPORTED_BIT
);
224 /* Send the LPC command */
225 Status
= LpcRequestWaitReplyPort(Port
,
226 (PPORT_MESSAGE
)Message
,
227 (PPORT_MESSAGE
)&Buffer
[0]);
229 /* Flush the instruction cache */
230 ZwFlushInstructionCache(NtCurrentProcess(), NULL
, 0);
232 /* Copy the buffer back */
233 if (NT_SUCCESS(Status
)) RtlCopyMemory(Message
, Buffer
, sizeof(DBGKM_MSG
));
235 /* Resume the process if it was suspended */
236 if (Suspended
) DbgkpResumeProcess();
242 DbgkpSendApiMessage(IN OUT PDBGKM_MSG ApiMsg
,
243 IN BOOLEAN SuspendProcess
)
246 BOOLEAN Suspended
= FALSE
;
248 DBGKTRACE(DBGK_MESSAGE_DEBUG
, "ApiMsg: %p SuspendProcess: %lx\n", ApiMsg
, SuspendProcess
);
250 /* Suspend process if required */
251 if (SuspendProcess
) Suspended
= DbgkpSuspendProcess();
253 /* Set return status */
254 ApiMsg
->ReturnedStatus
= STATUS_PENDING
;
256 /* Set create process reported state */
257 PspSetProcessFlag(PsGetCurrentProcess(), PSF_CREATE_REPORTED_BIT
);
259 /* Send the LPC command */
260 Status
= DbgkpQueueMessage(PsGetCurrentProcess(),
261 PsGetCurrentThread(),
266 /* Flush the instruction cache */
267 ZwFlushInstructionCache(NtCurrentProcess(), NULL
, 0);
269 /* Resume the process if it was suspended */
270 if (Suspended
) DbgkpResumeProcess();
276 DbgkCopyProcessDebugPort(IN PEPROCESS Process
,
279 PDEBUG_OBJECT DebugObject
;
281 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Parent: %p\n", Process
, Parent
);
283 /* Clear this process's port */
284 Process
->DebugPort
= NULL
;
286 /* Check if the parent has one */
287 if (!Parent
->DebugPort
) return;
289 /* It does, acquire the mutex */
290 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
292 /* Make sure it still has one, and that we should inherit */
293 DebugObject
= Parent
->DebugPort
;
294 if ((DebugObject
) && !(Process
->NoDebugInherit
))
296 /* Acquire the debug object's lock */
297 ExAcquireFastMutex(&DebugObject
->Mutex
);
299 /* Make sure the debugger is active */
300 if (!DebugObject
->DebuggerInactive
)
302 /* Reference the object and set it */
303 ObReferenceObject(DebugObject
);
304 Process
->DebugPort
= DebugObject
;
307 /* Release the debug object */
308 ExReleaseFastMutex(&DebugObject
->Mutex
);
311 /* Release the port mutex */
312 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
317 DbgkForwardException(IN PEXCEPTION_RECORD ExceptionRecord
,
318 IN BOOLEAN DebugPort
,
319 IN BOOLEAN SecondChance
)
321 DBGKM_MSG ApiMessage
;
322 PDBGKM_EXCEPTION DbgKmException
= &ApiMessage
.Exception
;
324 PEPROCESS Process
= PsGetCurrentProcess();
326 BOOLEAN UseLpc
= FALSE
;
328 DBGKTRACE(DBGK_EXCEPTION_DEBUG
,
329 "ExceptionRecord: %p Port: %u\n", ExceptionRecord
, DebugPort
);
331 /* Setup the API Message */
332 ApiMessage
.h
.u1
.Length
= sizeof(DBGKM_MSG
) << 16 |
333 (8 + sizeof(DBGKM_EXCEPTION
));
334 ApiMessage
.h
.u2
.ZeroInit
= 0;
335 ApiMessage
.h
.u2
.s2
.Type
= LPC_DEBUG_EVENT
;
336 ApiMessage
.ApiNumber
= DbgKmExceptionApi
;
338 /* Check if this is to be sent on the debug port */
341 /* Use the debug port, unless the thread is being hidden */
342 Port
= PsGetCurrentThread()->HideFromDebugger
?
343 NULL
: Process
->DebugPort
;
347 /* Otherwise, use the exception port */
348 Port
= Process
->ExceptionPort
;
349 ApiMessage
.h
.u2
.ZeroInit
= 0;
350 ApiMessage
.h
.u2
.s2
.Type
= LPC_EXCEPTION
;
354 /* Break out if there's no port */
355 if (!Port
) return FALSE
;
357 /* Fill out the exception information */
358 DbgKmException
->ExceptionRecord
= *ExceptionRecord
;
359 DbgKmException
->FirstChance
= !SecondChance
;
361 /* Check if we should use LPC */
364 /* Send the message on the LPC Port */
365 Status
= DbgkpSendApiMessageLpc(&ApiMessage
, Port
, DebugPort
);
369 /* Use native debug object */
370 Status
= DbgkpSendApiMessage(&ApiMessage
, DebugPort
);
373 /* Check if we failed, and for a debug port, also check the return status */
374 if (!(NT_SUCCESS(Status
)) ||
376 (!(NT_SUCCESS(ApiMessage
.ReturnedStatus
)) ||
377 (ApiMessage
.ReturnedStatus
== DBG_EXCEPTION_NOT_HANDLED
))))
383 /* Otherwise, we're ok */
389 DbgkpFreeDebugEvent(IN PDEBUG_EVENT DebugEvent
)
391 PHANDLE Handle
= NULL
;
393 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
395 /* Check if this event had a file handle */
396 switch (DebugEvent
->ApiMsg
.ApiNumber
)
398 /* Create process has a handle */
399 case DbgKmCreateProcessApi
:
401 /* Get the pointer */
402 Handle
= &DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
;
405 /* As does DLL load */
406 case DbgKmLoadDllApi
:
408 /* Get the pointer */
409 Handle
= &DebugEvent
->ApiMsg
.LoadDll
.FileHandle
;
415 /* Close the handle if it exsts */
416 if ((Handle
) && (*Handle
)) ObCloseHandle(*Handle
, KernelMode
);
418 /* Dereference process and thread and free the event */
419 ObDereferenceObject(DebugEvent
->Process
);
420 ObDereferenceObject(DebugEvent
->Thread
);
421 ExFreePoolWithTag(DebugEvent
, 'EgbD');
426 DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent
)
428 PETHREAD Thread
= DebugEvent
->Thread
;
430 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
432 /* Check if we have to wake the thread */
433 if (DebugEvent
->Flags
& DEBUG_EVENT_SUSPEND
) PsResumeThread(Thread
, NULL
);
435 /* Check if we had locked the thread */
436 if (DebugEvent
->Flags
& DEBUG_EVENT_RELEASE
)
439 ExReleaseRundownProtection(&Thread
->RundownProtect
);
442 /* Check if we have to wake up the event */
443 if (DebugEvent
->Flags
& DEBUG_EVENT_NOWAIT
)
445 /* Otherwise, free the debug event */
446 DbgkpFreeDebugEvent(DebugEvent
);
450 /* Signal the continue event */
451 KeSetEvent(&DebugEvent
->ContinueEvent
, IO_NO_INCREMENT
, FALSE
);
457 DbgkpPostFakeModuleMessages(IN PEPROCESS Process
,
459 IN PDEBUG_OBJECT DebugObject
)
461 PPEB Peb
= Process
->Peb
;
462 PPEB_LDR_DATA LdrData
;
463 PLDR_DATA_TABLE_ENTRY LdrEntry
;
464 PLIST_ENTRY ListHead
, NextEntry
;
465 DBGKM_MSG ApiMessage
;
466 PDBGKM_LOAD_DLL LoadDll
= &ApiMessage
.LoadDll
;
468 PIMAGE_NT_HEADERS NtHeader
;
469 UNICODE_STRING ModuleName
;
470 OBJECT_ATTRIBUTES ObjectAttributes
;
471 IO_STATUS_BLOCK IoStatusBlock
;
473 UNICODE_STRING FullDllName
;
475 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Thread: %p DebugObject: %p\n",
476 Process
, Thread
, DebugObject
);
478 /* Quit if there's no PEB */
479 if (!Peb
) return STATUS_SUCCESS
;
481 /* Accessing user memory, need SEH */
484 /* Get the Loader Data List */
485 ProbeForRead(Peb
, sizeof(*Peb
), 1);
487 ProbeForRead(LdrData
, sizeof(*LdrData
), 1);
488 ListHead
= &LdrData
->InLoadOrderModuleList
;
489 ProbeForRead(ListHead
, sizeof(*ListHead
), 1);
490 NextEntry
= ListHead
->Flink
;
492 /* Loop the modules */
494 while ((NextEntry
!= ListHead
) && (i
< 500))
496 ProbeForRead(NextEntry
, sizeof(*NextEntry
), 1);
497 /* Skip the first entry */
500 /* Go to the next module */
501 NextEntry
= NextEntry
->Flink
;
507 LdrEntry
= CONTAINING_RECORD(NextEntry
,
508 LDR_DATA_TABLE_ENTRY
,
510 ProbeForRead(LdrEntry
, sizeof(*LdrEntry
), 1);
512 /* Setup the API Message */
513 RtlZeroMemory(&ApiMessage
, sizeof(DBGKM_MSG
));
514 ApiMessage
.ApiNumber
= DbgKmLoadDllApi
;
516 /* Set base and clear the name */
517 LoadDll
->BaseOfDll
= LdrEntry
->DllBase
;
518 LoadDll
->NamePointer
= NULL
;
520 /* Get the NT Headers */
521 NtHeader
= RtlImageNtHeader(LoadDll
->BaseOfDll
);
524 /* Save debug data */
525 LoadDll
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
526 PointerToSymbolTable
;
527 LoadDll
->DebugInfoSize
= NtHeader
->FileHeader
.NumberOfSymbols
;
531 FullDllName
= LdrEntry
->FullDllName
;
532 ProbeForRead(FullDllName
.Buffer
, FullDllName
.MaximumLength
, 1);
533 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Name: %wZ. Base: %p\n",
534 &FullDllName
, LdrEntry
->DllBase
);
536 /* Get the name of the DLL */
537 Status
= MmGetFileNameForAddress(NtHeader
, &ModuleName
);
538 if (NT_SUCCESS(Status
))
540 /* Setup the object attributes */
541 InitializeObjectAttributes(&ObjectAttributes
,
543 OBJ_FORCE_ACCESS_CHECK
|
545 OBJ_CASE_INSENSITIVE
,
549 /* Open the file to get a handle to it */
550 Status
= ZwOpenFile(&LoadDll
->FileHandle
,
551 GENERIC_READ
| SYNCHRONIZE
,
557 FILE_SYNCHRONOUS_IO_NONALERT
);
558 if (!NT_SUCCESS(Status
)) LoadDll
->FileHandle
= NULL
;
560 /* Free the name now */
561 ExFreePool(ModuleName
.Buffer
);
564 /* Send the fake module load message */
565 Status
= DbgkpQueueMessage(Process
,
570 if (!NT_SUCCESS(Status
))
572 /* Message send failed, close the file handle if we had one */
573 if (LoadDll
->FileHandle
) ObCloseHandle(LoadDll
->FileHandle
,
577 /* Go to the next module */
578 NextEntry
= NextEntry
->Flink
;
582 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
589 return STATUS_SUCCESS
;
594 DbgkpPostFakeThreadMessages(IN PEPROCESS Process
,
595 IN PDEBUG_OBJECT DebugObject
,
596 IN PETHREAD StartThread
,
597 OUT PETHREAD
*FirstThread
,
598 OUT PETHREAD
*LastThread
)
600 PETHREAD pFirstThread
= NULL
, ThisThread
, OldThread
= NULL
, pLastThread
;
601 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
602 BOOLEAN IsFirstThread
;
604 DBGKM_MSG ApiMessage
;
605 PDBGKM_CREATE_THREAD CreateThread
= &ApiMessage
.CreateThread
;
606 PDBGKM_CREATE_PROCESS CreateProcess
= &ApiMessage
.CreateProcess
;
608 PIMAGE_NT_HEADERS NtHeader
;
610 DBGKTRACE(DBGK_THREAD_DEBUG
, "Process: %p StartThread: %p Object: %p\n",
611 Process
, StartThread
, DebugObject
);
613 /* Check if we have a start thread */
616 /* Then the one we'll find won't be the first one */
617 IsFirstThread
= FALSE
;
618 pFirstThread
= StartThread
;
619 ThisThread
= StartThread
;
622 ObReferenceObject(StartThread
);
626 /* Get the first thread ourselves */
627 ThisThread
= PsGetNextProcessThread(Process
, NULL
);
628 IsFirstThread
= TRUE
;
631 /* Start thread loop */
634 /* Dereference the previous thread if we had one */
635 if (OldThread
) ObDereferenceObject(OldThread
);
637 /* Set this as the last thread and lock it */
638 pLastThread
= ThisThread
;
639 ObReferenceObject(ThisThread
);
640 if (ExAcquireRundownProtection(&ThisThread
->RundownProtect
))
642 /* Acquire worked, set flags */
643 Flags
= DEBUG_EVENT_RELEASE
| DEBUG_EVENT_NOWAIT
;
645 /* Check if this is a user thread */
646 if (!ThisThread
->SystemThread
)
649 if (NT_SUCCESS(PsSuspendThread(ThisThread
, NULL
)))
652 Flags
|= DEBUG_EVENT_SUSPEND
;
658 /* Couldn't acquire rundown */
659 Flags
= DEBUG_EVENT_PROTECT_FAILED
| DEBUG_EVENT_NOWAIT
;
662 /* Clear the API Message */
663 RtlZeroMemory(&ApiMessage
, sizeof(ApiMessage
));
665 /* Check if this is the first thread */
666 if ((IsFirstThread
) &&
667 !(Flags
& DEBUG_EVENT_PROTECT_FAILED
) &&
668 !(ThisThread
->SystemThread
) &&
669 (ThisThread
->GrantedAccess
))
671 /* It is, save the flag */
676 /* It isn't, save the flag */
680 /* Check if this is the first */
683 /* So we'll start with the create process message */
684 ApiMessage
.ApiNumber
= DbgKmCreateProcessApi
;
686 /* Get the file handle */
687 if (Process
->SectionObject
)
689 /* Use the section object */
690 CreateProcess
->FileHandle
=
691 DbgkpSectionToFileHandle(Process
->SectionObject
);
695 /* Don't return any handle */
696 CreateProcess
->FileHandle
= NULL
;
699 /* Set the base address */
700 CreateProcess
->BaseOfImage
= Process
->SectionBaseAddress
;
702 /* Get the NT Header */
703 NtHeader
= RtlImageNtHeader(Process
->SectionBaseAddress
);
706 /* Fill out data from the header */
707 CreateProcess
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
708 PointerToSymbolTable
;
709 CreateProcess
->DebugInfoSize
= NtHeader
->FileHeader
.
715 /* Otherwise it's a thread message */
716 ApiMessage
.ApiNumber
= DbgKmCreateThreadApi
;
717 CreateThread
->StartAddress
= ThisThread
->StartAddress
;
721 DBGKTRACE(DBGK_THREAD_DEBUG
, "Thread: %p. First: %lx, OldThread: %p\n",
722 ThisThread
, First
, OldThread
);
723 DBGKTRACE(DBGK_THREAD_DEBUG
, "Start Address: %p\n",
724 ThisThread
->StartAddress
);
726 /* Queue the message */
727 Status
= DbgkpQueueMessage(Process
,
732 if (!NT_SUCCESS(Status
))
734 /* Resume the thread if it was suspended */
735 if (Flags
& DEBUG_EVENT_SUSPEND
) PsResumeThread(ThisThread
, NULL
);
737 /* Check if we acquired rundown */
738 if (Flags
& DEBUG_EVENT_RELEASE
)
741 ExReleaseRundownProtection(&ThisThread
->RundownProtect
);
744 /* If this was a process create, check if we got a handle */
745 if ((ApiMessage
.ApiNumber
== DbgKmCreateProcessApi
) &&
746 (CreateProcess
->FileHandle
))
749 ObCloseHandle(CreateProcess
->FileHandle
, KernelMode
);
752 /* Release our reference and break out */
753 ObDereferenceObject(ThisThread
);
757 /* Check if this was the first message */
760 /* It isn't the first thread anymore */
761 IsFirstThread
= FALSE
;
763 /* Reference this thread and set it as first */
764 ObReferenceObject(ThisThread
);
765 pFirstThread
= ThisThread
;
768 /* Get the next thread */
769 ThisThread
= PsGetNextProcessThread(Process
, ThisThread
);
770 OldThread
= pLastThread
;
771 } while (ThisThread
);
773 /* Check the API status */
774 if (!NT_SUCCESS(Status
))
776 /* Dereference and fail */
777 if (pFirstThread
) ObDereferenceObject(pFirstThread
);
778 ObDereferenceObject(pLastThread
);
782 /* Make sure we have a first thread */
783 if (!pFirstThread
) return STATUS_UNSUCCESSFUL
;
785 /* Return thread pointers */
786 *FirstThread
= pFirstThread
;
787 *LastThread
= pLastThread
;
793 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process
,
794 IN PDEBUG_OBJECT DebugObject
,
795 OUT PETHREAD
*LastThread
)
798 PETHREAD FirstThread
, FinalThread
;
799 PETHREAD ReturnThread
= NULL
;
802 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
803 Process
, DebugObject
);
805 /* Attach to the process */
806 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
808 /* Post the fake thread messages */
809 Status
= DbgkpPostFakeThreadMessages(Process
,
814 if (NT_SUCCESS(Status
))
816 /* Send the fake module messages too */
817 Status
= DbgkpPostFakeModuleMessages(Process
,
820 if (!NT_SUCCESS(Status
))
822 /* We failed, dereference the final thread */
823 ObDereferenceObject(FinalThread
);
827 /* Set the final thread */
828 ReturnThread
= FinalThread
;
831 /* Dereference the first thread */
832 ObDereferenceObject(FirstThread
);
835 /* Detach from the process */
836 KeUnstackDetachProcess(&ApcState
);
838 /* Return the last thread */
839 *LastThread
= ReturnThread
;
845 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
846 IN PDEBUG_EVENT DebugEvent
)
848 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
850 /* Start by copying the client ID */
851 WaitStateChange
->AppClientId
= DebugEvent
->ClientId
;
853 /* Now check which kind of event this was */
854 switch (DebugEvent
->ApiMsg
.ApiNumber
)
857 case DbgKmCreateProcessApi
:
859 /* Set the right native code */
860 WaitStateChange
->NewState
= DbgCreateProcessStateChange
;
862 /* Copy the information */
863 WaitStateChange
->StateInfo
.CreateProcessInfo
.NewProcess
=
864 DebugEvent
->ApiMsg
.CreateProcess
;
866 /* Clear the file handle for us */
867 DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
= NULL
;
871 case DbgKmCreateThreadApi
:
873 /* Set the right native code */
874 WaitStateChange
->NewState
= DbgCreateThreadStateChange
;
876 /* Copy information */
877 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.StartAddress
=
878 DebugEvent
->ApiMsg
.CreateThread
.StartAddress
;
879 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.SubSystemKey
=
880 DebugEvent
->ApiMsg
.CreateThread
.SubSystemKey
;
883 /* Exception (or breakpoint/step) */
884 case DbgKmExceptionApi
:
886 /* Look at the exception code */
887 if ((NTSTATUS
)DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
890 /* Update this as a breakpoint exception */
891 WaitStateChange
->NewState
= DbgBreakpointStateChange
;
893 else if ((NTSTATUS
)DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
896 /* Update this as a single step exception */
897 WaitStateChange
->NewState
= DbgSingleStepStateChange
;
901 /* Otherwise, set default exception */
902 WaitStateChange
->NewState
= DbgExceptionStateChange
;
905 /* Copy the exception record */
906 WaitStateChange
->StateInfo
.Exception
.ExceptionRecord
=
907 DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
;
908 /* Copy FirstChance flag */
909 WaitStateChange
->StateInfo
.Exception
.FirstChance
=
910 DebugEvent
->ApiMsg
.Exception
.FirstChance
;
914 case DbgKmExitProcessApi
:
916 /* Set the right native code and copy the exit code */
917 WaitStateChange
->NewState
= DbgExitProcessStateChange
;
918 WaitStateChange
->StateInfo
.ExitProcess
.ExitStatus
=
919 DebugEvent
->ApiMsg
.ExitProcess
.ExitStatus
;
923 case DbgKmExitThreadApi
:
925 /* Set the right native code */
926 WaitStateChange
->NewState
= DbgExitThreadStateChange
;
927 WaitStateChange
->StateInfo
.ExitThread
.ExitStatus
=
928 DebugEvent
->ApiMsg
.ExitThread
.ExitStatus
;
932 case DbgKmLoadDllApi
:
934 /* Set the native code */
935 WaitStateChange
->NewState
= DbgLoadDllStateChange
;
938 WaitStateChange
->StateInfo
.LoadDll
= DebugEvent
->ApiMsg
.LoadDll
;
940 /* Clear the file handle for us */
941 DebugEvent
->ApiMsg
.LoadDll
.FileHandle
= NULL
;
945 case DbgKmUnloadDllApi
:
947 /* Set the native code and copy the address */
948 WaitStateChange
->NewState
= DbgUnloadDllStateChange
;
949 WaitStateChange
->StateInfo
.UnloadDll
.BaseAddress
=
950 DebugEvent
->ApiMsg
.UnloadDll
.BaseAddress
;
955 /* Shouldn't happen */
962 DbgkpMarkProcessPeb(IN PEPROCESS Process
)
966 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p\n", Process
);
968 /* Acquire process rundown */
969 if (!ExAcquireRundownProtection(&Process
->RundownProtect
)) return;
971 /* Make sure we have a PEB */
974 /* Attach to the process */
975 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
977 /* Acquire the debug port mutex */
978 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
980 /* Set the IsBeingDebugged member of the PEB */
981 Process
->Peb
->BeingDebugged
= (Process
->DebugPort
) ? TRUE
: FALSE
;
984 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
986 /* Detach from the process */
987 KeUnstackDetachProcess(&ApcState
);
990 /* Release rundown protection */
991 ExReleaseRundownProtection(&Process
->RundownProtect
);
996 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
997 IN PEPROCESS Process
,
1004 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Process: %p Thread: %p State: %lx\n",
1005 Process
, Thread
, WaitStateChange
->NewState
);
1007 /* Check which state this is */
1008 switch (WaitStateChange
->NewState
)
1011 case DbgCreateThreadStateChange
:
1013 /* Get handle to thread */
1014 Status
= ObOpenObjectByPointer(Thread
,
1021 if (NT_SUCCESS(Status
))
1023 /* Save the thread handle */
1025 StateInfo
.CreateThread
.HandleToThread
= Handle
;
1030 case DbgCreateProcessStateChange
:
1032 /* Get handle to thread */
1033 Status
= ObOpenObjectByPointer(Thread
,
1040 if (NT_SUCCESS(Status
))
1042 /* Save the thread handle */
1044 StateInfo
.CreateProcessInfo
.HandleToThread
= Handle
;
1047 /* Get handle to process */
1048 Status
= ObOpenObjectByPointer(Process
,
1055 if (NT_SUCCESS(Status
))
1057 /* Save the process handle */
1059 StateInfo
.CreateProcessInfo
.HandleToProcess
= Handle
;
1062 /* Fall through to duplicate file handle */
1063 DupHandle
= &WaitStateChange
->
1064 StateInfo
.CreateProcessInfo
.NewProcess
.FileHandle
;
1068 case DbgLoadDllStateChange
:
1070 /* Fall through to duplicate file handle */
1071 DupHandle
= &WaitStateChange
->StateInfo
.LoadDll
.FileHandle
;
1074 /* Anything else has no handles */
1079 /* If we got here, then we have to duplicate a handle, possibly */
1080 Handle
= *DupHandle
;
1084 Status
= ObDuplicateObject(PsGetCurrentProcess(),
1086 PsGetCurrentProcess(),
1090 DUPLICATE_SAME_ACCESS
,
1092 if (!NT_SUCCESS(Status
)) *DupHandle
= NULL
;
1094 /* Close the original handle */
1095 ObCloseHandle(Handle
, KernelMode
);
1101 DbgkpDeleteObject(IN PVOID DebugObject
)
1106 ASSERT(IsListEmpty(&((PDEBUG_OBJECT
)DebugObject
)->EventList
));
1111 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL
,
1112 IN PVOID ObjectBody
,
1113 IN ACCESS_MASK GrantedAccess
,
1114 IN ULONG HandleCount
,
1115 IN ULONG SystemHandleCount
)
1117 PDEBUG_OBJECT DebugObject
= ObjectBody
;
1118 PEPROCESS Process
= NULL
;
1119 BOOLEAN DebugPortCleared
= FALSE
;
1120 PLIST_ENTRY DebugEventList
;
1121 PDEBUG_EVENT DebugEvent
;
1123 DBGKTRACE(DBGK_OBJECT_DEBUG
, "OwnerProcess: %p DebugObject: %p\n",
1124 OwnerProcess
, DebugObject
);
1126 /* If this isn't the last handle, do nothing */
1127 if (SystemHandleCount
> 1) return;
1129 /* Otherwise, lock the debug object */
1130 ExAcquireFastMutex(&DebugObject
->Mutex
);
1132 /* Set it as inactive */
1133 DebugObject
->DebuggerInactive
= TRUE
;
1135 /* Remove it from the debug event list */
1136 DebugEventList
= DebugObject
->EventList
.Flink
;
1137 InitializeListHead(&DebugObject
->EventList
);
1139 /* Release the lock */
1140 ExReleaseFastMutex(&DebugObject
->Mutex
);
1142 /* Signal the wait event */
1143 KeSetEvent(&DebugObject
->EventsPresent
, IO_NO_INCREMENT
, FALSE
);
1145 /* Start looping each process */
1146 while ((Process
= PsGetNextProcess(Process
)))
1148 /* Check if the process has us as their debug port */
1149 if (Process
->DebugPort
== DebugObject
)
1151 /* Acquire the process debug port lock */
1152 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1154 /* Check if it's still us */
1155 if (Process
->DebugPort
== DebugObject
)
1157 /* Clear it and remember */
1158 Process
->DebugPort
= NULL
;
1159 DebugPortCleared
= TRUE
;
1162 /* Release the port lock */
1163 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1165 /* Check if we cleared the debug port */
1166 if (DebugPortCleared
)
1168 /* Mark this in the PEB */
1169 DbgkpMarkProcessPeb(Process
);
1171 /* Check if we terminate on exit */
1172 if (DebugObject
->KillProcessOnExit
)
1174 /* Terminate the process */
1175 PsTerminateProcess(Process
, STATUS_DEBUGGER_INACTIVE
);
1178 /* Dereference the debug object */
1179 ObDereferenceObject(DebugObject
);
1184 /* Loop debug events */
1185 while (DebugEventList
!= &DebugObject
->EventList
)
1187 /* Get the debug event */
1188 DebugEvent
= CONTAINING_RECORD(DebugEventList
, DEBUG_EVENT
, EventList
);
1190 /* Go to the next entry */
1191 DebugEventList
= DebugEventList
->Flink
;
1194 DebugEvent
->Status
= STATUS_DEBUGGER_INACTIVE
;
1195 DbgkpWakeTarget(DebugEvent
);
1201 DbgkpSetProcessDebugObject(IN PEPROCESS Process
,
1202 IN PDEBUG_OBJECT DebugObject
,
1203 IN NTSTATUS MsgStatus
,
1204 IN PETHREAD LastThread
)
1207 LIST_ENTRY TempList
;
1208 BOOLEAN GlobalHeld
= FALSE
, DoSetEvent
= TRUE
;
1209 PETHREAD ThisThread
, FirstThread
;
1210 PLIST_ENTRY NextEntry
;
1211 PDEBUG_EVENT DebugEvent
;
1212 PETHREAD EventThread
;
1214 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
1215 Process
, DebugObject
);
1217 /* Initialize the temporary list */
1218 InitializeListHead(&TempList
);
1220 /* Check if we have a success message */
1221 if (NT_SUCCESS(MsgStatus
))
1223 /* Then default to STATUS_SUCCESS */
1224 Status
= STATUS_SUCCESS
;
1228 /* No last thread, and set the failure code */
1233 /* Now check what status we have here */
1234 if (NT_SUCCESS(Status
))
1236 /* Acquire the global lock */
1239 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1241 /* Check if we already have a port */
1242 if (Process
->DebugPort
)
1245 Status
= STATUS_PORT_ALREADY_SET
;
1249 /* Otherwise, set the port and reference the thread */
1250 Process
->DebugPort
= DebugObject
;
1251 ObReferenceObject(LastThread
);
1253 /* Get the next thread */
1254 ThisThread
= PsGetNextProcessThread(Process
, LastThread
);
1257 /* Clear the debug port and release the lock */
1258 Process
->DebugPort
= NULL
;
1259 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1262 /* Dereference the thread */
1263 ObDereferenceObject(LastThread
);
1265 /* Post fake messages */
1266 Status
= DbgkpPostFakeThreadMessages(Process
,
1271 if (!NT_SUCCESS(Status
))
1273 /* Clear the last thread */
1278 /* Dereference the first thread and re-acquire the lock */
1279 ObDereferenceObject(FirstThread
);
1286 /* Acquire the debug object's lock */
1287 ExAcquireFastMutex(&DebugObject
->Mutex
);
1289 /* Check our status here */
1290 if (NT_SUCCESS(Status
))
1292 /* Check if we're disconnected */
1293 if (DebugObject
->DebuggerInactive
)
1296 Process
->DebugPort
= NULL
;
1297 Status
= STATUS_DEBUGGER_INACTIVE
;
1301 /* Set the process flags */
1302 PspSetProcessFlag(Process
,
1303 PSF_NO_DEBUG_INHERIT_BIT
|
1304 PSF_CREATE_REPORTED_BIT
);
1306 /* Reference the debug object */
1307 ObReferenceObject(DebugObject
);
1311 /* Loop the events list */
1312 NextEntry
= DebugObject
->EventList
.Flink
;
1313 while (NextEntry
!= &DebugObject
->EventList
)
1315 /* Get the debug event and go to the next entry */
1316 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1317 NextEntry
= NextEntry
->Flink
;
1318 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1319 DebugEvent
, DebugEvent
->Flags
,
1320 DebugEvent
->BackoutThread
, PsGetCurrentThread());
1322 /* Check for if the debug event queue needs flushing */
1323 if ((DebugEvent
->Flags
& DEBUG_EVENT_INACTIVE
) &&
1324 (DebugEvent
->BackoutThread
== PsGetCurrentThread()))
1326 /* Get the event's thread */
1327 EventThread
= DebugEvent
->Thread
;
1328 DBGKTRACE(DBGK_PROCESS_DEBUG
, "EventThread: %p MsgStatus: %lx\n",
1329 EventThread
, MsgStatus
);
1331 /* Check if the status is success */
1332 if ((MsgStatus
== STATUS_SUCCESS
) &&
1333 (EventThread
->GrantedAccess
) &&
1334 (!EventThread
->SystemThread
))
1336 /* Check if we couldn't acquire rundown for it */
1337 if (DebugEvent
->Flags
& DEBUG_EVENT_PROTECT_FAILED
)
1339 /* Set the skip termination flag */
1340 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1342 /* Insert it into the temp list */
1343 RemoveEntryList(&DebugEvent
->EventList
);
1344 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1348 /* Do we need to signal the event */
1352 DebugEvent
->Flags
&= ~DEBUG_EVENT_INACTIVE
;
1353 KeSetEvent(&DebugObject
->EventsPresent
,
1359 /* Clear the backout thread */
1360 DebugEvent
->BackoutThread
= NULL
;
1363 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1368 /* Insert it into the temp list */
1369 RemoveEntryList(&DebugEvent
->EventList
);
1370 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1373 /* Check if the lock is held */
1374 if (DebugEvent
->Flags
& DEBUG_EVENT_RELEASE
)
1377 DebugEvent
->Flags
&= ~DEBUG_EVENT_RELEASE
;
1378 ExReleaseRundownProtection(&EventThread
->RundownProtect
);
1383 /* Release the debug object */
1384 ExReleaseFastMutex(&DebugObject
->Mutex
);
1386 /* Release the global lock if acquired */
1387 if (GlobalHeld
) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1389 /* Check if there's a thread to dereference */
1390 if (LastThread
) ObDereferenceObject(LastThread
);
1392 /* Loop our temporary list */
1393 while (!IsListEmpty(&TempList
))
1395 /* Remove the event */
1396 NextEntry
= RemoveHeadList(&TempList
);
1397 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1400 DbgkpWakeTarget(DebugEvent
);
1403 /* Check if we got here through success and mark the PEB, then return */
1404 if (NT_SUCCESS(Status
)) DbgkpMarkProcessPeb(Process
);
1410 DbgkClearProcessDebugObject(IN PEPROCESS Process
,
1411 IN PDEBUG_OBJECT SourceDebugObject OPTIONAL
)
1413 PDEBUG_OBJECT DebugObject
;
1414 PDEBUG_EVENT DebugEvent
;
1415 LIST_ENTRY TempList
;
1416 PLIST_ENTRY NextEntry
;
1418 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Process: %p DebugObject: %p\n",
1419 Process
, SourceDebugObject
);
1421 /* Acquire the port lock */
1422 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1424 /* Get the Process Debug Object */
1425 DebugObject
= Process
->DebugPort
;
1428 * Check if the process had an object and it matches,
1429 * or if the process had an object but none was specified
1430 * (in which we are called from NtTerminateProcess)
1432 if ((DebugObject
) &&
1433 ((DebugObject
== SourceDebugObject
) ||
1434 (SourceDebugObject
== NULL
)))
1436 /* Clear the debug port */
1437 Process
->DebugPort
= NULL
;
1439 /* Release the port lock and remove the PEB flag */
1440 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1441 DbgkpMarkProcessPeb(Process
);
1445 /* Release the port lock and fail */
1446 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1447 return STATUS_PORT_NOT_SET
;
1450 /* Initialize the temporary list */
1451 InitializeListHead(&TempList
);
1453 /* Acquire the Object */
1454 ExAcquireFastMutex(&DebugObject
->Mutex
);
1456 /* Loop the events */
1457 NextEntry
= DebugObject
->EventList
.Flink
;
1458 while (NextEntry
!= &DebugObject
->EventList
)
1460 /* Get the Event and go to the next entry */
1461 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1462 NextEntry
= NextEntry
->Flink
;
1464 /* Check that it belongs to the specified process */
1465 if (DebugEvent
->Process
== Process
)
1467 /* Insert it into the temporary list */
1468 RemoveEntryList(&DebugEvent
->EventList
);
1469 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1473 /* Release the Object */
1474 ExReleaseFastMutex(&DebugObject
->Mutex
);
1476 /* Release the initial reference */
1477 ObDereferenceObject(DebugObject
);
1479 /* Loop our temporary list */
1480 while (!IsListEmpty(&TempList
))
1482 /* Remove the event */
1483 NextEntry
= RemoveHeadList(&TempList
);
1484 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1487 DebugEvent
->Status
= STATUS_DEBUGGER_INACTIVE
;
1488 DbgkpWakeTarget(DebugEvent
);
1491 /* Return Success */
1492 return STATUS_SUCCESS
;
1498 DbgkInitialize(VOID
)
1500 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
1501 UNICODE_STRING Name
;
1504 /* Initialize the process debug port mutex */
1505 ExInitializeFastMutex(&DbgkpProcessDebugPortMutex
);
1507 /* Create the Debug Object Type */
1508 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
1509 RtlInitUnicodeString(&Name
, L
"DebugObject");
1510 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
1511 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(DEBUG_OBJECT
);
1512 ObjectTypeInitializer
.GenericMapping
= DbgkDebugObjectMapping
;
1513 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
1514 ObjectTypeInitializer
.ValidAccessMask
= DEBUG_OBJECT_ALL_ACCESS
;
1515 ObjectTypeInitializer
.SecurityRequired
= TRUE
;
1516 ObjectTypeInitializer
.CloseProcedure
= DbgkpCloseObject
;
1517 ObjectTypeInitializer
.DeleteProcedure
= DbgkpDeleteObject
;
1518 ObCreateObjectType(&Name
,
1519 &ObjectTypeInitializer
,
1521 &DbgkDebugObjectType
);
1526 DbgkOpenProcessDebugPort(IN PEPROCESS Process
,
1527 IN KPROCESSOR_MODE PreviousMode
,
1528 OUT HANDLE
*DebugHandle
)
1530 PDEBUG_OBJECT DebugObject
;
1534 /* If there's no debug port, just exit */
1535 if (!Process
->DebugPort
) return STATUS_PORT_NOT_SET
;
1537 /* Otherwise, acquire the lock while we grab the port */
1538 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1540 /* Grab it and reference it if it exists */
1541 DebugObject
= Process
->DebugPort
;
1542 if (DebugObject
) ObReferenceObject(DebugObject
);
1544 /* Release the lock now */
1545 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1547 /* Bail out if it doesn't exist */
1548 if (!DebugObject
) return STATUS_PORT_NOT_SET
;
1550 /* Now get a handle to it */
1551 Status
= ObOpenObjectByPointer(DebugObject
,
1555 DbgkDebugObjectType
,
1558 if (!NT_SUCCESS(Status
)) ObDereferenceObject(DebugObject
);
1564 /* PUBLIC FUNCTIONS **********************************************************/
1571 NtCreateDebugObject(OUT PHANDLE DebugHandle
,
1572 IN ACCESS_MASK DesiredAccess
,
1573 IN POBJECT_ATTRIBUTES ObjectAttributes
,
1576 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1577 PDEBUG_OBJECT DebugObject
;
1582 /* Check if we were called from user mode*/
1583 if (PreviousMode
!= KernelMode
)
1585 /* Enter SEH for probing */
1588 /* Probe the handle */
1589 ProbeForWriteHandle(DebugHandle
);
1591 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1593 /* Return the exception code */
1594 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1598 /* Check for invalid flags */
1599 if (Flags
& ~DBGK_ALL_FLAGS
) return STATUS_INVALID_PARAMETER
;
1601 /* Create the Object */
1602 Status
= ObCreateObject(PreviousMode
,
1603 DbgkDebugObjectType
,
1607 sizeof(DEBUG_OBJECT
),
1610 (PVOID
*)&DebugObject
);
1611 if (NT_SUCCESS(Status
))
1613 /* Initialize the Debug Object's Fast Mutex */
1614 ExInitializeFastMutex(&DebugObject
->Mutex
);
1616 /* Initialize the State Event List */
1617 InitializeListHead(&DebugObject
->EventList
);
1619 /* Initialize the Debug Object's Wait Event */
1620 KeInitializeEvent(&DebugObject
->EventsPresent
,
1625 DebugObject
->Flags
= 0;
1626 if (Flags
& DBGK_KILL_PROCESS_ON_EXIT
)
1628 DebugObject
->KillProcessOnExit
= TRUE
;
1632 Status
= ObInsertObject((PVOID
)DebugObject
,
1638 if (NT_SUCCESS(Status
))
1640 /* Enter SEH to protect the write */
1643 /* Return the handle */
1644 *DebugHandle
= hDebug
;
1646 _SEH2_EXCEPT(ExSystemExceptionFilter())
1648 /* Get the exception code */
1649 Status
= _SEH2_GetExceptionCode();
1655 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p DebugObject: %p\n",
1656 hDebug
, DebugObject
);
1665 NtDebugContinue(IN HANDLE DebugHandle
,
1666 IN PCLIENT_ID AppClientId
,
1667 IN NTSTATUS ContinueStatus
)
1669 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1670 PDEBUG_OBJECT DebugObject
;
1672 PDEBUG_EVENT DebugEvent
= NULL
, DebugEventToWake
= NULL
;
1673 PLIST_ENTRY ListHead
, NextEntry
;
1674 BOOLEAN NeedsWake
= FALSE
;
1677 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p Status: %d\n",
1678 DebugHandle
, ContinueStatus
);
1680 /* Check if we were called from user mode*/
1681 if (PreviousMode
!= KernelMode
)
1683 /* Enter SEH for probing */
1686 /* Probe the handle */
1687 ProbeForRead(AppClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
1688 ClientId
= *AppClientId
;
1689 AppClientId
= &ClientId
;
1691 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1693 /* Return the exception code */
1694 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1698 /* Make sure that the status is valid */
1699 if ((ContinueStatus
!= DBG_CONTINUE
) &&
1700 (ContinueStatus
!= DBG_EXCEPTION_HANDLED
) &&
1701 (ContinueStatus
!= DBG_EXCEPTION_NOT_HANDLED
) &&
1702 (ContinueStatus
!= DBG_TERMINATE_THREAD
) &&
1703 (ContinueStatus
!= DBG_TERMINATE_PROCESS
))
1705 /* Invalid status */
1706 Status
= STATUS_INVALID_PARAMETER
;
1710 /* Get the debug object */
1711 Status
= ObReferenceObjectByHandle(DebugHandle
,
1712 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1713 DbgkDebugObjectType
,
1715 (PVOID
*)&DebugObject
,
1717 if (NT_SUCCESS(Status
))
1719 /* Acquire the mutex */
1720 ExAcquireFastMutex(&DebugObject
->Mutex
);
1722 /* Loop the state list */
1723 ListHead
= &DebugObject
->EventList
;
1724 NextEntry
= ListHead
->Flink
;
1725 while (ListHead
!= NextEntry
)
1727 /* Get the current debug event */
1728 DebugEvent
= CONTAINING_RECORD(NextEntry
,
1732 /* Compare process ID */
1733 if (DebugEvent
->ClientId
.UniqueProcess
==
1734 AppClientId
->UniqueProcess
)
1736 /* Check if we already found a match */
1739 /* Wake it up and break out */
1740 DebugEvent
->Flags
&= ~DEBUG_EVENT_INACTIVE
;
1741 KeSetEvent(&DebugObject
->EventsPresent
,
1747 /* Compare thread ID and flag */
1748 if ((DebugEvent
->ClientId
.UniqueThread
==
1749 AppClientId
->UniqueThread
) && (DebugEvent
->Flags
& DEBUG_EVENT_READ
))
1751 /* Remove the event from the list */
1752 RemoveEntryList(NextEntry
);
1754 /* Remember who to wake */
1756 DebugEventToWake
= DebugEvent
;
1760 /* Go to the next entry */
1761 NextEntry
= NextEntry
->Flink
;
1764 /* Release the mutex */
1765 ExReleaseFastMutex(&DebugObject
->Mutex
);
1767 /* Dereference the object */
1768 ObDereferenceObject(DebugObject
);
1770 /* Check if need a wait */
1773 /* Set the continue status */
1774 DebugEventToWake
->ApiMsg
.ReturnedStatus
= ContinueStatus
;
1775 DebugEventToWake
->Status
= STATUS_SUCCESS
;
1777 /* Wake the target */
1778 DbgkpWakeTarget(DebugEventToWake
);
1783 Status
= STATUS_INVALID_PARAMETER
;
1797 NtDebugActiveProcess(IN HANDLE ProcessHandle
,
1798 IN HANDLE DebugHandle
)
1801 PDEBUG_OBJECT DebugObject
;
1802 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1803 PETHREAD LastThread
;
1806 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1807 ProcessHandle
, DebugHandle
);
1809 /* Reference the process */
1810 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1811 PROCESS_SUSPEND_RESUME
,
1816 if (!NT_SUCCESS(Status
)) return Status
;
1818 /* Don't allow debugging the current process or the system process */
1819 if ((Process
== PsGetCurrentProcess()) ||
1820 (Process
== PsInitialSystemProcess
))
1822 /* Dereference and fail */
1823 ObDereferenceObject(Process
);
1824 return STATUS_ACCESS_DENIED
;
1827 /* Reference the debug object */
1828 Status
= ObReferenceObjectByHandle(DebugHandle
,
1829 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1830 DbgkDebugObjectType
,
1832 (PVOID
*)&DebugObject
,
1834 if (!NT_SUCCESS(Status
))
1836 /* Dereference the process and exit */
1837 ObDereferenceObject(Process
);
1841 /* Acquire process rundown protection */
1842 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1844 /* Dereference the process and debug object and exit */
1845 ObDereferenceObject(Process
);
1846 ObDereferenceObject(DebugObject
);
1847 return STATUS_PROCESS_IS_TERMINATING
;
1850 /* Send fake create messages for debuggers to have a consistent state */
1851 Status
= DbgkpPostFakeProcessCreateMessages(Process
,
1854 Status
= DbgkpSetProcessDebugObject(Process
,
1859 /* Release rundown protection */
1860 ExReleaseRundownProtection(&Process
->RundownProtect
);
1862 /* Dereference the process and debug object and return status */
1863 ObDereferenceObject(Process
);
1864 ObDereferenceObject(DebugObject
);
1873 NtRemoveProcessDebug(IN HANDLE ProcessHandle
,
1874 IN HANDLE DebugHandle
)
1877 PDEBUG_OBJECT DebugObject
;
1878 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1881 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1882 ProcessHandle
, DebugHandle
);
1884 /* Reference the process */
1885 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1886 PROCESS_SUSPEND_RESUME
,
1891 if (!NT_SUCCESS(Status
)) return Status
;
1893 /* Reference the debug object */
1894 Status
= ObReferenceObjectByHandle(DebugHandle
,
1895 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1896 DbgkDebugObjectType
,
1898 (PVOID
*)&DebugObject
,
1900 if (!NT_SUCCESS(Status
))
1902 /* Dereference the process and exit */
1903 ObDereferenceObject(Process
);
1907 /* Remove the debug object */
1908 Status
= DbgkClearProcessDebugObject(Process
, DebugObject
);
1910 /* Dereference the process and debug object and return status */
1911 ObDereferenceObject(Process
);
1912 ObDereferenceObject(DebugObject
);
1921 NtSetInformationDebugObject(IN HANDLE DebugHandle
,
1922 IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass
,
1923 IN PVOID DebugInformation
,
1924 IN ULONG DebugInformationLength
,
1925 OUT PULONG ReturnLength OPTIONAL
)
1927 PDEBUG_OBJECT DebugObject
;
1928 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1930 PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo
= DebugInformation
;
1933 /* Check buffers and parameters */
1934 Status
= DefaultSetInfoBufferCheck(DebugObjectInformationClass
,
1935 DbgkpDebugObjectInfoClass
,
1936 sizeof(DbgkpDebugObjectInfoClass
) /
1937 sizeof(DbgkpDebugObjectInfoClass
[0]),
1939 DebugInformationLength
,
1941 if (!NT_SUCCESS(Status
)) return Status
;
1943 /* Check if the caller wanted the return length */
1946 /* Enter SEH for probe */
1949 /* Return required length to user-mode */
1950 ProbeForWriteUlong(ReturnLength
);
1951 *ReturnLength
= sizeof(*DebugInfo
);
1953 _SEH2_EXCEPT(ExSystemExceptionFilter())
1955 /* Return the exception code */
1956 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1961 /* Open the Object */
1962 Status
= ObReferenceObjectByHandle(DebugHandle
,
1963 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1964 DbgkDebugObjectType
,
1966 (PVOID
*)&DebugObject
,
1968 if (NT_SUCCESS(Status
))
1970 /* Acquire the object */
1971 ExAcquireFastMutex(&DebugObject
->Mutex
);
1973 /* Set the proper flag */
1974 if (DebugInfo
->KillProcessOnExit
)
1976 /* Enable killing the process */
1977 DebugObject
->KillProcessOnExit
= TRUE
;
1982 DebugObject
->KillProcessOnExit
= FALSE
;
1985 /* Release the mutex */
1986 ExReleaseFastMutex(&DebugObject
->Mutex
);
1988 /* Release the Object */
1989 ObDereferenceObject(DebugObject
);
2001 NtWaitForDebugEvent(IN HANDLE DebugHandle
,
2002 IN BOOLEAN Alertable
,
2003 IN PLARGE_INTEGER Timeout OPTIONAL
,
2004 OUT PDBGUI_WAIT_STATE_CHANGE StateChange
)
2006 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2007 LARGE_INTEGER LocalTimeOut
;
2009 LARGE_INTEGER StartTime
;
2012 LARGE_INTEGER NewTime
;
2013 PDEBUG_OBJECT DebugObject
;
2014 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
2016 PDEBUG_EVENT DebugEvent
= NULL
, DebugEvent2
;
2017 PLIST_ENTRY ListHead
, NextEntry
, NextEntry2
;
2019 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p\n", DebugHandle
);
2021 /* Clear the initial wait state change structure and the timeout */
2022 RtlZeroMemory(&WaitStateChange
, sizeof(WaitStateChange
));
2023 LocalTimeOut
.QuadPart
= 0;
2025 /* Check if we were called from user mode */
2026 if (PreviousMode
!= KernelMode
)
2028 /* Protect probe in SEH */
2031 /* Check if we came with a timeout */
2035 ProbeForReadLargeInteger(Timeout
);
2037 /* Make a local copy */
2038 LocalTimeOut
= *Timeout
;
2039 Timeout
= &LocalTimeOut
;
2042 /* Probe the state change structure */
2043 ProbeForWrite(StateChange
, sizeof(*StateChange
), sizeof(ULONG
));
2045 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2047 /* Return the exception code */
2048 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2055 if (Timeout
) LocalTimeOut
= *Timeout
;
2058 /* If we were passed a timeout, query the current time */
2059 if (Timeout
) KeQuerySystemTime(&StartTime
);
2061 /* Get the debug object */
2062 Status
= ObReferenceObjectByHandle(DebugHandle
,
2063 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
2064 DbgkDebugObjectType
,
2066 (PVOID
*)&DebugObject
,
2068 if (!NT_SUCCESS(Status
)) return Status
;
2070 /* Clear process and thread */
2074 /* Wait on the debug object given to us */
2077 Status
= KeWaitForSingleObject(&DebugObject
->EventsPresent
,
2082 if (!NT_SUCCESS(Status
) ||
2083 (Status
== STATUS_TIMEOUT
) ||
2084 (Status
== STATUS_ALERTED
) ||
2085 (Status
== STATUS_USER_APC
))
2087 /* Break out the wait */
2091 /* Lock the object */
2093 ExAcquireFastMutex(&DebugObject
->Mutex
);
2095 /* Check if a debugger is connected */
2096 if (DebugObject
->DebuggerInactive
)
2099 Status
= STATUS_DEBUGGER_INACTIVE
;
2103 /* Loop the events */
2104 ListHead
= &DebugObject
->EventList
;
2105 NextEntry
= ListHead
->Flink
;
2106 while (ListHead
!= NextEntry
)
2108 /* Get the debug event */
2109 DebugEvent
= CONTAINING_RECORD(NextEntry
,
2112 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx\n",
2113 DebugEvent
, DebugEvent
->Flags
);
2116 if (!(DebugEvent
->Flags
& (DEBUG_EVENT_INACTIVE
| DEBUG_EVENT_READ
)))
2118 /* We got an event */
2121 /* Loop the list internally */
2122 NextEntry2
= DebugObject
->EventList
.Flink
;
2123 while (NextEntry2
!= NextEntry
)
2125 /* Get the debug event */
2126 DebugEvent2
= CONTAINING_RECORD(NextEntry2
,
2130 /* Try to match process IDs */
2131 if (DebugEvent2
->ClientId
.UniqueProcess
==
2132 DebugEvent
->ClientId
.UniqueProcess
)
2134 /* Found it, break out */
2135 DebugEvent
->Flags
|= DEBUG_EVENT_INACTIVE
;
2136 DebugEvent
->BackoutThread
= NULL
;
2141 /* Move to the next entry */
2142 NextEntry2
= NextEntry2
->Flink
;
2145 /* Check if we still have a valid event */
2146 if (GotEvent
) break;
2149 /* Move to the next entry */
2150 NextEntry
= NextEntry
->Flink
;
2153 /* Check if we have an event */
2156 /* Save and reference the process and thread */
2157 Process
= DebugEvent
->Process
;
2158 Thread
= DebugEvent
->Thread
;
2159 ObReferenceObject(Process
);
2160 ObReferenceObject(Thread
);
2162 /* Convert to user-mode structure */
2163 DbgkpConvertKernelToUserStateChange(&WaitStateChange
,
2167 DebugEvent
->Flags
|= DEBUG_EVENT_READ
;
2171 /* Unsignal the event */
2172 KeClearEvent(&DebugObject
->EventsPresent
);
2176 Status
= STATUS_SUCCESS
;
2179 /* Release the mutex */
2180 ExReleaseFastMutex(&DebugObject
->Mutex
);
2181 if (!NT_SUCCESS(Status
)) break;
2183 /* Check if we got an event */
2186 /* Check if we can wait again */
2187 if (LocalTimeOut
.QuadPart
< 0)
2189 /* Query the new time */
2190 KeQuerySystemTime(&NewTime
);
2192 /* Substract times */
2193 LocalTimeOut
.QuadPart
+= (NewTime
.QuadPart
- StartTime
.QuadPart
);
2194 StartTime
= NewTime
;
2196 /* Check if we've timed out */
2197 if (LocalTimeOut
.QuadPart
>= 0)
2199 /* We have, break out of the loop */
2200 Status
= STATUS_TIMEOUT
;
2207 /* Open the handles and dereference the objects */
2208 DbgkpOpenHandles(&WaitStateChange
, Process
, Thread
);
2209 ObDereferenceObject(Process
);
2210 ObDereferenceObject(Thread
);
2215 /* We're done, dereference the object */
2216 ObDereferenceObject(DebugObject
);
2218 /* Protect write with SEH */
2221 /* Return our wait state change structure */
2222 *StateChange
= WaitStateChange
;
2224 _SEH2_EXCEPT(ExSystemExceptionFilter())
2226 /* Get SEH Exception code */
2227 Status
= _SEH2_GetExceptionCode();