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: %lx %p\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 ExFreePool(DebugEvent
);
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: %p\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
= LPC_DEBUG_EVENT
;
335 ApiMessage
.ApiNumber
= DbgKmExceptionApi
;
337 /* Check if this is to be sent on the debug port */
340 /* Use the debug port, unless the thread is being hidden */
341 Port
= PsGetCurrentThread()->HideFromDebugger
?
342 NULL
: Process
->DebugPort
;
346 /* Otherwise, use the exception port */
347 Port
= Process
->ExceptionPort
;
348 ApiMessage
.h
.u2
.ZeroInit
= LPC_EXCEPTION
;
352 /* Break out if there's no port */
353 if (!Port
) return FALSE
;
355 /* Fill out the exception information */
356 DbgKmException
->ExceptionRecord
= *ExceptionRecord
;
357 DbgKmException
->FirstChance
= !SecondChance
;
359 /* Check if we should use LPC */
362 /* Send the message on the LPC Port */
363 Status
= DbgkpSendApiMessageLpc(&ApiMessage
, Port
, DebugPort
);
367 /* Use native debug object */
368 Status
= DbgkpSendApiMessage(&ApiMessage
, DebugPort
);
371 /* Check if we failed, and for a debug port, also check the return status */
372 if (!(NT_SUCCESS(Status
)) ||
374 (!(NT_SUCCESS(ApiMessage
.ReturnedStatus
)) ||
375 (ApiMessage
.ReturnedStatus
== DBG_EXCEPTION_NOT_HANDLED
))))
381 /* Otherwise, we're ok */
387 DbgkpFreeDebugEvent(IN PDEBUG_EVENT DebugEvent
)
389 PHANDLE Handle
= NULL
;
391 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
393 /* Check if this event had a file handle */
394 switch (DebugEvent
->ApiMsg
.ApiNumber
)
396 /* Create process has a handle */
397 case DbgKmCreateProcessApi
:
399 /* Get the pointer */
400 Handle
= &DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
;
403 /* As does DLL load */
404 case DbgKmLoadDllApi
:
406 /* Get the pointer */
407 Handle
= &DebugEvent
->ApiMsg
.LoadDll
.FileHandle
;
413 /* Close the handle if it exsts */
414 if ((Handle
) && (*Handle
)) ObCloseHandle(*Handle
, KernelMode
);
416 /* Dereference process and thread and free the event */
417 ObDereferenceObject(DebugEvent
->Process
);
418 ObDereferenceObject(DebugEvent
->Thread
);
419 ExFreePool(DebugEvent
);
424 DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent
)
426 PETHREAD Thread
= DebugEvent
->Thread
;
428 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
430 /* Check if we have to wake the thread */
431 if (DebugEvent
->Flags
& DEBUG_EVENT_SUSPEND
) PsResumeThread(Thread
, NULL
);
433 /* Check if we had locked the thread */
434 if (DebugEvent
->Flags
& DEBUG_EVENT_RELEASE
)
437 ExReleaseRundownProtection(&Thread
->RundownProtect
);
440 /* Check if we have to wake up the event */
441 if (DebugEvent
->Flags
& DEBUG_EVENT_NOWAIT
)
443 /* Otherwise, free the debug event */
444 DbgkpFreeDebugEvent(DebugEvent
);
448 /* Signal the continue event */
449 KeSetEvent(&DebugEvent
->ContinueEvent
, IO_NO_INCREMENT
, FALSE
);
455 DbgkpPostFakeModuleMessages(IN PEPROCESS Process
,
457 IN PDEBUG_OBJECT DebugObject
)
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
;
466 PIMAGE_NT_HEADERS NtHeader
;
467 UNICODE_STRING ModuleName
;
468 OBJECT_ATTRIBUTES ObjectAttributes
;
469 IO_STATUS_BLOCK IoStatusBlock
;
472 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Thread: %p DebugObject: %p\n",
473 Process
, Thread
, DebugObject
);
475 /* Quit if there's no PEB */
476 if (!Peb
) return STATUS_SUCCESS
;
478 /* Get the Loader Data List */
480 ListHead
= &LdrData
->InLoadOrderModuleList
;
481 NextEntry
= ListHead
->Flink
;
483 /* Loop the modules */
485 while ((NextEntry
!= ListHead
) && (i
< 500))
487 /* Skip the first entry */
490 /* Go to the next module */
491 NextEntry
= NextEntry
->Flink
;
497 LdrEntry
= CONTAINING_RECORD(NextEntry
,
498 LDR_DATA_TABLE_ENTRY
,
501 /* Setup the API Message */
502 RtlZeroMemory(&ApiMessage
, sizeof(DBGKM_MSG
));
503 ApiMessage
.ApiNumber
= DbgKmLoadDllApi
;
505 /* Set base and clear the name */
506 LoadDll
->BaseOfDll
= LdrEntry
->DllBase
;
507 LoadDll
->NamePointer
= NULL
;
509 /* Get the NT Headers */
510 NtHeader
= RtlImageNtHeader(LoadDll
->BaseOfDll
);
513 /* Save debug data */
514 LoadDll
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
515 PointerToSymbolTable
;
516 LoadDll
->DebugInfoSize
= NtHeader
->FileHeader
.NumberOfSymbols
;
520 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Name: %wZ. Base: %p\n",
521 &LdrEntry
->FullDllName
, LdrEntry
->DllBase
);
523 /* Get the name of the DLL */
524 Status
= MmGetFileNameForAddress(NtHeader
, &ModuleName
);
525 if (NT_SUCCESS(Status
))
527 /* Setup the object attributes */
528 InitializeObjectAttributes(&ObjectAttributes
,
530 OBJ_FORCE_ACCESS_CHECK
|
532 OBJ_CASE_INSENSITIVE
,
536 /* Open the file to get a handle to it */
537 Status
= ZwOpenFile(&LoadDll
->FileHandle
,
538 GENERIC_READ
| SYNCHRONIZE
,
544 FILE_SYNCHRONOUS_IO_NONALERT
);
545 if (!NT_SUCCESS(Status
)) LoadDll
->FileHandle
= NULL
;
547 /* Free the name now */
548 ExFreePool(ModuleName
.Buffer
);
551 /* Send the fake module load message */
552 Status
= DbgkpQueueMessage(Process
,
557 if (!NT_SUCCESS(Status
))
559 /* Message send failed, close the file handle if we had one */
560 if (LoadDll
->FileHandle
) ObCloseHandle(LoadDll
->FileHandle
,
564 /* Go to the next module */
565 NextEntry
= NextEntry
->Flink
;
570 return STATUS_SUCCESS
;
575 DbgkpPostFakeThreadMessages(IN PEPROCESS Process
,
576 IN PDEBUG_OBJECT DebugObject
,
577 IN PETHREAD StartThread
,
578 OUT PETHREAD
*FirstThread
,
579 OUT PETHREAD
*LastThread
)
581 PETHREAD pFirstThread
= NULL
, ThisThread
, OldThread
= NULL
, pLastThread
;
582 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
583 BOOLEAN IsFirstThread
;
585 DBGKM_MSG ApiMessage
;
586 PDBGKM_CREATE_THREAD CreateThread
= &ApiMessage
.CreateThread
;
587 PDBGKM_CREATE_PROCESS CreateProcess
= &ApiMessage
.CreateProcess
;
589 PIMAGE_NT_HEADERS NtHeader
;
591 DBGKTRACE(DBGK_THREAD_DEBUG
, "Process: %p StartThread: %p Object: %p\n",
592 Process
, StartThread
, DebugObject
);
594 /* Check if we have a start thread */
597 /* Then the one we'll find won't be the first one */
598 IsFirstThread
= FALSE
;
599 pFirstThread
= StartThread
;
600 ThisThread
= StartThread
;
603 ObReferenceObject(StartThread
);
607 /* Get the first thread ourselves */
608 ThisThread
= PsGetNextProcessThread(Process
, NULL
);
609 IsFirstThread
= TRUE
;
612 /* Start thread loop */
615 /* Dereference the previous thread if we had one */
616 if (OldThread
) ObDereferenceObject(OldThread
);
618 /* Set this as the last thread and lock it */
619 pLastThread
= ThisThread
;
620 ObReferenceObject(ThisThread
);
621 if (ExAcquireRundownProtection(&ThisThread
->RundownProtect
))
623 /* Acquire worked, set flags */
624 Flags
= DEBUG_EVENT_RELEASE
| DEBUG_EVENT_NOWAIT
;
626 /* Check if this is a user thread */
627 if (!ThisThread
->SystemThread
)
630 if (NT_SUCCESS(PsSuspendThread(ThisThread
, NULL
)))
633 Flags
|= DEBUG_EVENT_SUSPEND
;
639 /* Couldn't acquire rundown */
640 Flags
= DEBUG_EVENT_PROTECT_FAILED
| DEBUG_EVENT_NOWAIT
;
643 /* Clear the API Message */
644 RtlZeroMemory(&ApiMessage
, sizeof(ApiMessage
));
646 /* Check if this is the first thread */
647 if ((IsFirstThread
) &&
648 !(Flags
& DEBUG_EVENT_PROTECT_FAILED
) &&
649 !(ThisThread
->SystemThread
) &&
650 (ThisThread
->GrantedAccess
))
652 /* It is, save the flag */
657 /* It isn't, save the flag */
661 /* Check if this is the first */
664 /* So we'll start with the create process message */
665 ApiMessage
.ApiNumber
= DbgKmCreateProcessApi
;
667 /* Get the file handle */
668 if (Process
->SectionObject
)
670 /* Use the section object */
671 CreateProcess
->FileHandle
=
672 DbgkpSectionToFileHandle(Process
->SectionObject
);
676 /* Don't return any handle */
677 CreateProcess
->FileHandle
= NULL
;
680 /* Set the base address */
681 CreateProcess
->BaseOfImage
= Process
->SectionBaseAddress
;
683 /* Get the NT Header */
684 NtHeader
= RtlImageNtHeader(Process
->SectionBaseAddress
);
687 /* Fill out data from the header */
688 CreateProcess
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
689 PointerToSymbolTable
;
690 CreateProcess
->DebugInfoSize
= NtHeader
->FileHeader
.
696 /* Otherwise it's a thread message */
697 ApiMessage
.ApiNumber
= DbgKmCreateThreadApi
;
698 CreateThread
->StartAddress
= ThisThread
->StartAddress
;
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
);
707 /* Queue the message */
708 Status
= DbgkpQueueMessage(Process
,
713 if (!NT_SUCCESS(Status
))
715 /* Resume the thread if it was suspended */
716 if (Flags
& DEBUG_EVENT_SUSPEND
) PsResumeThread(ThisThread
, NULL
);
718 /* Check if we acquired rundown */
719 if (Flags
& DEBUG_EVENT_RELEASE
)
722 ExReleaseRundownProtection(&ThisThread
->RundownProtect
);
725 /* If this was a process create, check if we got a handle */
726 if ((ApiMessage
.ApiNumber
== DbgKmCreateProcessApi
) &&
727 (CreateProcess
->FileHandle
))
730 ObCloseHandle(CreateProcess
->FileHandle
, KernelMode
);
733 /* Release our reference and break out */
734 ObDereferenceObject(ThisThread
);
738 /* Check if this was the first message */
741 /* It isn't the first thread anymore */
742 IsFirstThread
= FALSE
;
744 /* Reference this thread and set it as first */
745 ObReferenceObject(ThisThread
);
746 pFirstThread
= ThisThread
;
749 /* Get the next thread */
750 ThisThread
= PsGetNextProcessThread(Process
, ThisThread
);
751 OldThread
= pLastThread
;
752 } while (ThisThread
);
754 /* Check the API status */
755 if (!NT_SUCCESS(Status
))
757 /* Dereference and fail */
758 if (pFirstThread
) ObDereferenceObject(pFirstThread
);
759 if (pLastThread
) ObDereferenceObject(pLastThread
);
763 /* Make sure we have a first thread */
764 if (!pFirstThread
) return STATUS_UNSUCCESSFUL
;
766 /* Return thread pointers */
767 *FirstThread
= pFirstThread
;
768 *LastThread
= pLastThread
;
774 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process
,
775 IN PDEBUG_OBJECT DebugObject
,
776 OUT PETHREAD
*LastThread
)
779 PETHREAD FirstThread
, FinalThread
;
780 PETHREAD ReturnThread
= NULL
;
783 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
784 Process
, DebugObject
);
786 /* Attach to the process */
787 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
789 /* Post the fake thread messages */
790 Status
= DbgkpPostFakeThreadMessages(Process
,
795 if (NT_SUCCESS(Status
))
797 /* Send the fake module messages too */
798 Status
= DbgkpPostFakeModuleMessages(Process
,
801 if (!NT_SUCCESS(Status
))
803 /* We failed, dereference the final thread */
804 ObDereferenceObject(FinalThread
);
808 /* Set the final thread */
809 ReturnThread
= FinalThread
;
812 /* Dereference the first thread */
813 ObDereferenceObject(FirstThread
);
816 /* Detach from the process */
817 KeUnstackDetachProcess(&ApcState
);
819 /* Return the last thread */
820 *LastThread
= ReturnThread
;
826 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
827 IN PDEBUG_EVENT DebugEvent
)
829 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
831 /* Start by copying the client ID */
832 WaitStateChange
->AppClientId
= DebugEvent
->ClientId
;
834 /* Now check which kind of event this was */
835 switch (DebugEvent
->ApiMsg
.ApiNumber
)
838 case DbgKmCreateProcessApi
:
840 /* Set the right native code */
841 WaitStateChange
->NewState
= DbgCreateProcessStateChange
;
843 /* Copy the information */
844 WaitStateChange
->StateInfo
.CreateProcessInfo
.NewProcess
=
845 DebugEvent
->ApiMsg
.CreateProcess
;
847 /* Clear the file handle for us */
848 DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
= NULL
;
852 case DbgKmCreateThreadApi
:
854 /* Set the right native code */
855 WaitStateChange
->NewState
= DbgCreateThreadStateChange
;
857 /* Copy information */
858 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.StartAddress
=
859 DebugEvent
->ApiMsg
.CreateThread
.StartAddress
;
860 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.SubSystemKey
=
861 DebugEvent
->ApiMsg
.CreateThread
.SubSystemKey
;
864 /* Exception (or breakpoint/step) */
865 case DbgKmExceptionApi
:
867 /* Look at the exception code */
868 if ((NTSTATUS
)DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
871 /* Update this as a breakpoint exception */
872 WaitStateChange
->NewState
= DbgBreakpointStateChange
;
874 else if ((NTSTATUS
)DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
877 /* Update this as a single step exception */
878 WaitStateChange
->NewState
= DbgSingleStepStateChange
;
882 /* Otherwise, set default exception */
883 WaitStateChange
->NewState
= DbgExceptionStateChange
;
886 /* Copy the exception record */
887 WaitStateChange
->StateInfo
.Exception
.ExceptionRecord
=
888 DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
;
889 /* Copy FirstChance flag */
890 WaitStateChange
->StateInfo
.Exception
.FirstChance
=
891 DebugEvent
->ApiMsg
.Exception
.FirstChance
;
895 case DbgKmExitProcessApi
:
897 /* Set the right native code and copy the exit code */
898 WaitStateChange
->NewState
= DbgExitProcessStateChange
;
899 WaitStateChange
->StateInfo
.ExitProcess
.ExitStatus
=
900 DebugEvent
->ApiMsg
.ExitProcess
.ExitStatus
;
904 case DbgKmExitThreadApi
:
906 /* Set the right native code */
907 WaitStateChange
->NewState
= DbgExitThreadStateChange
;
908 WaitStateChange
->StateInfo
.ExitThread
.ExitStatus
=
909 DebugEvent
->ApiMsg
.ExitThread
.ExitStatus
;
913 case DbgKmLoadDllApi
:
915 /* Set the native code */
916 WaitStateChange
->NewState
= DbgLoadDllStateChange
;
919 WaitStateChange
->StateInfo
.LoadDll
= DebugEvent
->ApiMsg
.LoadDll
;
921 /* Clear the file handle for us */
922 DebugEvent
->ApiMsg
.LoadDll
.FileHandle
= NULL
;
926 case DbgKmUnloadDllApi
:
928 /* Set the native code and copy the address */
929 WaitStateChange
->NewState
= DbgUnloadDllStateChange
;
930 WaitStateChange
->StateInfo
.UnloadDll
.BaseAddress
=
931 DebugEvent
->ApiMsg
.UnloadDll
.BaseAddress
;
936 /* Shouldn't happen */
943 DbgkpMarkProcessPeb(IN PEPROCESS Process
)
947 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p\n", Process
);
949 /* Acquire process rundown */
950 if (!ExAcquireRundownProtection(&Process
->RundownProtect
)) return;
952 /* Make sure we have a PEB */
955 /* Attach to the process */
956 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
958 /* Acquire the debug port mutex */
959 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
961 /* Set the IsBeingDebugged member of the PEB */
962 Process
->Peb
->BeingDebugged
= (Process
->DebugPort
) ? TRUE
: FALSE
;
965 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
967 /* Detach from the process */
968 KeUnstackDetachProcess(&ApcState
);
971 /* Release rundown protection */
972 ExReleaseRundownProtection(&Process
->RundownProtect
);
977 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
978 IN PEPROCESS Process
,
985 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Process: %p Thread: %p State: %lx\n",
986 Process
, Thread
, WaitStateChange
->NewState
);
988 /* Check which state this is */
989 switch (WaitStateChange
->NewState
)
992 case DbgCreateThreadStateChange
:
994 /* Get handle to thread */
995 Status
= ObOpenObjectByPointer(Thread
,
1002 if (NT_SUCCESS(Status
))
1004 /* Save the thread handle */
1006 StateInfo
.CreateThread
.HandleToThread
= Handle
;
1011 case DbgCreateProcessStateChange
:
1013 /* Get handle to thread */
1014 Status
= ObOpenObjectByPointer(Thread
,
1021 if (NT_SUCCESS(Status
))
1023 /* Save the thread handle */
1025 StateInfo
.CreateProcessInfo
.HandleToThread
= Handle
;
1028 /* Get handle to process */
1029 Status
= ObOpenObjectByPointer(Process
,
1036 if (NT_SUCCESS(Status
))
1038 /* Save the process handle */
1040 StateInfo
.CreateProcessInfo
.HandleToProcess
= Handle
;
1043 /* Fall through to duplicate file handle */
1044 DupHandle
= &WaitStateChange
->
1045 StateInfo
.CreateProcessInfo
.NewProcess
.FileHandle
;
1049 case DbgLoadDllStateChange
:
1051 /* Fall through to duplicate file handle */
1052 DupHandle
= &WaitStateChange
->StateInfo
.LoadDll
.FileHandle
;
1055 /* Anything else has no handles */
1060 /* If we got here, then we have to duplicate a handle, possibly */
1061 Handle
= *DupHandle
;
1065 Status
= ObDuplicateObject(PsGetCurrentProcess(),
1067 PsGetCurrentProcess(),
1071 DUPLICATE_SAME_ACCESS
,
1073 if (!NT_SUCCESS(Status
)) *DupHandle
= NULL
;
1075 /* Close the original handle */
1076 ObCloseHandle(Handle
, KernelMode
);
1082 DbgkpDeleteObject(IN PVOID DebugObject
)
1087 ASSERT(IsListEmpty(&((PDEBUG_OBJECT
)DebugObject
)->EventList
));
1092 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL
,
1093 IN PVOID ObjectBody
,
1094 IN ACCESS_MASK GrantedAccess
,
1095 IN ULONG HandleCount
,
1096 IN ULONG SystemHandleCount
)
1098 PDEBUG_OBJECT DebugObject
= ObjectBody
;
1099 PEPROCESS Process
= NULL
;
1100 BOOLEAN DebugPortCleared
= FALSE
;
1101 PLIST_ENTRY DebugEventList
;
1102 PDEBUG_EVENT DebugEvent
;
1104 DBGKTRACE(DBGK_OBJECT_DEBUG
, "OwnerProcess: %p DebugObject: %p\n",
1105 OwnerProcess
, DebugObject
);
1107 /* If this isn't the last handle, do nothing */
1108 if (SystemHandleCount
> 1) return;
1110 /* Otherwise, lock the debug object */
1111 ExAcquireFastMutex(&DebugObject
->Mutex
);
1113 /* Set it as inactive */
1114 DebugObject
->DebuggerInactive
= TRUE
;
1116 /* Remove it from the debug event list */
1117 DebugEventList
= DebugObject
->EventList
.Flink
;
1118 InitializeListHead(&DebugObject
->EventList
);
1120 /* Release the lock */
1121 ExReleaseFastMutex(&DebugObject
->Mutex
);
1123 /* Signal the wait event */
1124 KeSetEvent(&DebugObject
->EventsPresent
, IO_NO_INCREMENT
, FALSE
);
1126 /* Start looping each process */
1127 while ((Process
= PsGetNextProcess(Process
)))
1129 /* Check if the process has us as their debug port */
1130 if (Process
->DebugPort
== DebugObject
)
1132 /* Acquire the process debug port lock */
1133 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1135 /* Check if it's still us */
1136 if (Process
->DebugPort
== DebugObject
)
1138 /* Clear it and remember */
1139 Process
->DebugPort
= NULL
;
1140 DebugPortCleared
= TRUE
;
1143 /* Release the port lock */
1144 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1146 /* Check if we cleared the debug port */
1147 if (DebugPortCleared
)
1149 /* Mark this in the PEB */
1150 DbgkpMarkProcessPeb(Process
);
1152 /* Check if we terminate on exit */
1153 if (DebugObject
->KillProcessOnExit
)
1155 /* Terminate the process */
1156 PsTerminateProcess(Process
, STATUS_DEBUGGER_INACTIVE
);
1159 /* Dereference the debug object */
1160 ObDereferenceObject(DebugObject
);
1165 /* Loop debug events */
1166 while (DebugEventList
!= &DebugObject
->EventList
)
1168 /* Get the debug event */
1169 DebugEvent
= CONTAINING_RECORD(DebugEventList
, DEBUG_EVENT
, EventList
);
1171 /* Go to the next entry */
1172 DebugEventList
= DebugEventList
->Flink
;
1175 DebugEvent
->Status
= STATUS_DEBUGGER_INACTIVE
;
1176 DbgkpWakeTarget(DebugEvent
);
1182 DbgkpSetProcessDebugObject(IN PEPROCESS Process
,
1183 IN PDEBUG_OBJECT DebugObject
,
1184 IN NTSTATUS MsgStatus
,
1185 IN PETHREAD LastThread
)
1188 LIST_ENTRY TempList
;
1189 BOOLEAN GlobalHeld
= FALSE
, DoSetEvent
= TRUE
;
1190 PETHREAD ThisThread
, FirstThread
;
1191 PLIST_ENTRY NextEntry
;
1192 PDEBUG_EVENT DebugEvent
;
1193 PETHREAD EventThread
;
1195 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
1196 Process
, DebugObject
);
1198 /* Initialize the temporary list */
1199 InitializeListHead(&TempList
);
1201 /* Check if we have a success message */
1202 if (NT_SUCCESS(MsgStatus
))
1204 /* Then default to STATUS_SUCCESS */
1205 Status
= STATUS_SUCCESS
;
1209 /* No last thread, and set the failure code */
1214 /* Now check what status we have here */
1215 if (NT_SUCCESS(Status
))
1217 /* Acquire the global lock */
1220 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1222 /* Check if we already have a port */
1223 if (Process
->DebugPort
)
1226 Status
= STATUS_PORT_ALREADY_SET
;
1230 /* Otherwise, set the port and reference the thread */
1231 Process
->DebugPort
= DebugObject
;
1232 ObReferenceObject(LastThread
);
1234 /* Get the next thread */
1235 ThisThread
= PsGetNextProcessThread(Process
, LastThread
);
1238 /* Clear the debug port and release the lock */
1239 Process
->DebugPort
= NULL
;
1240 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1243 /* Dereference the thread */
1244 ObDereferenceObject(LastThread
);
1246 /* Post fake messages */
1247 Status
= DbgkpPostFakeThreadMessages(Process
,
1252 if (!NT_SUCCESS(Status
))
1254 /* Clear the last thread */
1259 /* Dereference the first thread and re-acquire the lock */
1260 ObDereferenceObject(FirstThread
);
1267 /* Acquire the debug object's lock */
1268 ExAcquireFastMutex(&DebugObject
->Mutex
);
1270 /* Check our status here */
1271 if (NT_SUCCESS(Status
))
1273 /* Check if we're disconnected */
1274 if (DebugObject
->DebuggerInactive
)
1277 Process
->DebugPort
= NULL
;
1278 Status
= STATUS_DEBUGGER_INACTIVE
;
1282 /* Set the process flags */
1283 PspSetProcessFlag(Process
,
1284 PSF_NO_DEBUG_INHERIT_BIT
|
1285 PSF_CREATE_REPORTED_BIT
);
1287 /* Reference the debug object */
1288 ObReferenceObject(DebugObject
);
1292 /* Loop the events list */
1293 NextEntry
= DebugObject
->EventList
.Flink
;
1294 while (NextEntry
!= &DebugObject
->EventList
)
1296 /* Get the debug event and go to the next entry */
1297 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1298 NextEntry
= NextEntry
->Flink
;
1299 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1300 DebugEvent
, DebugEvent
->Flags
,
1301 DebugEvent
->BackoutThread
, PsGetCurrentThread());
1303 /* Check for if the debug event queue needs flushing */
1304 if ((DebugEvent
->Flags
& DEBUG_EVENT_INACTIVE
) &&
1305 (DebugEvent
->BackoutThread
== PsGetCurrentThread()))
1307 /* Get the event's thread */
1308 EventThread
= DebugEvent
->Thread
;
1309 DBGKTRACE(DBGK_PROCESS_DEBUG
, "EventThread: %p MsgStatus: %lx\n",
1310 EventThread
, MsgStatus
);
1312 /* Check if the status is success */
1313 if ((MsgStatus
== STATUS_SUCCESS
) &&
1314 (EventThread
->GrantedAccess
) &&
1315 (!EventThread
->SystemThread
))
1317 /* Check if we couldn't acquire rundown for it */
1318 if (DebugEvent
->Flags
& DEBUG_EVENT_PROTECT_FAILED
)
1320 /* Set the skip termination flag */
1321 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1323 /* Insert it into the temp list */
1324 RemoveEntryList(&DebugEvent
->EventList
);
1325 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1329 /* Do we need to signal the event */
1333 DebugEvent
->Flags
&= ~DEBUG_EVENT_INACTIVE
;
1334 KeSetEvent(&DebugObject
->EventsPresent
,
1340 /* Clear the backout thread */
1341 DebugEvent
->BackoutThread
= NULL
;
1344 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1349 /* Insert it into the temp list */
1350 RemoveEntryList(&DebugEvent
->EventList
);
1351 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1354 /* Check if the lock is held */
1355 if (DebugEvent
->Flags
& DEBUG_EVENT_RELEASE
)
1358 DebugEvent
->Flags
&= ~DEBUG_EVENT_RELEASE
;
1359 ExReleaseRundownProtection(&EventThread
->RundownProtect
);
1364 /* Release the debug object */
1365 ExReleaseFastMutex(&DebugObject
->Mutex
);
1367 /* Release the global lock if acquired */
1368 if (GlobalHeld
) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1370 /* Check if there's a thread to dereference */
1371 if (LastThread
) ObDereferenceObject(LastThread
);
1373 /* Loop our temporary list */
1374 while (!IsListEmpty(&TempList
))
1376 /* Remove the event */
1377 NextEntry
= RemoveHeadList(&TempList
);
1378 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1381 DbgkpWakeTarget(DebugEvent
);
1384 /* Check if we got here through success and mark the PEB, then return */
1385 if (NT_SUCCESS(Status
)) DbgkpMarkProcessPeb(Process
);
1391 DbgkClearProcessDebugObject(IN PEPROCESS Process
,
1392 IN PDEBUG_OBJECT SourceDebugObject OPTIONAL
)
1394 PDEBUG_OBJECT DebugObject
;
1395 PDEBUG_EVENT DebugEvent
;
1396 LIST_ENTRY TempList
;
1397 PLIST_ENTRY NextEntry
;
1399 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Process: %p DebugObject: %p\n",
1400 Process
, SourceDebugObject
);
1402 /* Acquire the port lock */
1403 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1405 /* Get the Process Debug Object */
1406 DebugObject
= Process
->DebugPort
;
1409 * Check if the process had an object and it matches,
1410 * or if the process had an object but none was specified
1411 * (in which we are called from NtTerminateProcess)
1413 if ((DebugObject
) &&
1414 ((DebugObject
== SourceDebugObject
) ||
1415 (SourceDebugObject
== NULL
)))
1417 /* Clear the debug port */
1418 Process
->DebugPort
= NULL
;
1420 /* Release the port lock and remove the PEB flag */
1421 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1422 DbgkpMarkProcessPeb(Process
);
1426 /* Release the port lock and fail */
1427 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1428 return STATUS_PORT_NOT_SET
;
1431 /* Initialize the temporary list */
1432 InitializeListHead(&TempList
);
1434 /* Acquire the Object */
1435 ExAcquireFastMutex(&DebugObject
->Mutex
);
1437 /* Loop the events */
1438 NextEntry
= DebugObject
->EventList
.Flink
;
1439 while (NextEntry
!= &DebugObject
->EventList
)
1441 /* Get the Event and go to the next entry */
1442 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1443 NextEntry
= NextEntry
->Flink
;
1445 /* Check that it belongs to the specified process */
1446 if (DebugEvent
->Process
== Process
)
1448 /* Insert it into the temporary list */
1449 RemoveEntryList(&DebugEvent
->EventList
);
1450 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1454 /* Release the Object */
1455 ExReleaseFastMutex(&DebugObject
->Mutex
);
1457 /* Release the initial reference */
1458 ObDereferenceObject(DebugObject
);
1460 /* Loop our temporary list */
1461 while (!IsListEmpty(&TempList
))
1463 /* Remove the event */
1464 NextEntry
= RemoveHeadList(&TempList
);
1465 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1468 DebugEvent
->Status
= STATUS_DEBUGGER_INACTIVE
;
1469 DbgkpWakeTarget(DebugEvent
);
1472 /* Return Success */
1473 return STATUS_SUCCESS
;
1479 DbgkInitialize(VOID
)
1481 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
1482 UNICODE_STRING Name
;
1485 /* Initialize the process debug port mutex */
1486 ExInitializeFastMutex(&DbgkpProcessDebugPortMutex
);
1488 /* Create the Debug Object Type */
1489 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
1490 RtlInitUnicodeString(&Name
, L
"DebugObject");
1491 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
1492 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(DEBUG_OBJECT
);
1493 ObjectTypeInitializer
.GenericMapping
= DbgkDebugObjectMapping
;
1494 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
1495 ObjectTypeInitializer
.ValidAccessMask
= DEBUG_OBJECT_ALL_ACCESS
;
1496 ObjectTypeInitializer
.UseDefaultObject
= TRUE
;
1497 ObjectTypeInitializer
.CloseProcedure
= DbgkpCloseObject
;
1498 ObjectTypeInitializer
.DeleteProcedure
= DbgkpDeleteObject
;
1499 ObCreateObjectType(&Name
,
1500 &ObjectTypeInitializer
,
1502 &DbgkDebugObjectType
);
1505 /* PUBLIC FUNCTIONS **********************************************************/
1512 NtCreateDebugObject(OUT PHANDLE DebugHandle
,
1513 IN ACCESS_MASK DesiredAccess
,
1514 IN POBJECT_ATTRIBUTES ObjectAttributes
,
1517 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1518 PDEBUG_OBJECT DebugObject
;
1523 /* Check if we were called from user mode*/
1524 if (PreviousMode
!= KernelMode
)
1526 /* Enter SEH for probing */
1529 /* Probe the handle */
1530 ProbeForWriteHandle(DebugHandle
);
1532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1534 /* Return the exception code */
1535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1539 /* Check for invalid flags */
1540 if (Flags
& ~DBGK_ALL_FLAGS
) return STATUS_INVALID_PARAMETER
;
1542 /* Create the Object */
1543 Status
= ObCreateObject(PreviousMode
,
1544 DbgkDebugObjectType
,
1548 sizeof(DEBUG_OBJECT
),
1551 (PVOID
*)&DebugObject
);
1552 if (NT_SUCCESS(Status
))
1554 /* Initialize the Debug Object's Fast Mutex */
1555 ExInitializeFastMutex(&DebugObject
->Mutex
);
1557 /* Initialize the State Event List */
1558 InitializeListHead(&DebugObject
->EventList
);
1560 /* Initialize the Debug Object's Wait Event */
1561 KeInitializeEvent(&DebugObject
->EventsPresent
,
1566 DebugObject
->Flags
= 0;
1567 if (Flags
& DBGK_KILL_PROCESS_ON_EXIT
)
1569 DebugObject
->KillProcessOnExit
= TRUE
;
1573 Status
= ObInsertObject((PVOID
)DebugObject
,
1579 if (NT_SUCCESS(Status
))
1581 /* Enter SEH to protect the write */
1584 /* Return the handle */
1585 *DebugHandle
= hDebug
;
1587 _SEH2_EXCEPT(ExSystemExceptionFilter())
1589 /* Get the exception code */
1590 Status
= _SEH2_GetExceptionCode();
1596 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p DebugObject: %p\n",
1597 hDebug
, DebugObject
);
1606 NtDebugContinue(IN HANDLE DebugHandle
,
1607 IN PCLIENT_ID AppClientId
,
1608 IN NTSTATUS ContinueStatus
)
1610 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1611 PDEBUG_OBJECT DebugObject
;
1613 PDEBUG_EVENT DebugEvent
= NULL
, DebugEventToWake
= NULL
;
1614 PLIST_ENTRY ListHead
, NextEntry
;
1615 BOOLEAN NeedsWake
= FALSE
;
1618 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p Status: %p\n",
1619 DebugHandle
, ContinueStatus
);
1621 /* Check if we were called from user mode*/
1622 if (PreviousMode
!= KernelMode
)
1624 /* Enter SEH for probing */
1627 /* Probe the handle */
1628 ProbeForRead(AppClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
1629 ClientId
= *AppClientId
;
1630 AppClientId
= &ClientId
;
1632 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1634 /* Return the exception code */
1635 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1639 /* Make sure that the status is valid */
1640 if ((ContinueStatus
!= DBG_CONTINUE
) &&
1641 (ContinueStatus
!= DBG_EXCEPTION_HANDLED
) &&
1642 (ContinueStatus
!= DBG_EXCEPTION_NOT_HANDLED
) &&
1643 (ContinueStatus
!= DBG_TERMINATE_THREAD
) &&
1644 (ContinueStatus
!= DBG_TERMINATE_PROCESS
))
1646 /* Invalid status */
1647 Status
= STATUS_INVALID_PARAMETER
;
1651 /* Get the debug object */
1652 Status
= ObReferenceObjectByHandle(DebugHandle
,
1653 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1654 DbgkDebugObjectType
,
1656 (PVOID
*)&DebugObject
,
1658 if (NT_SUCCESS(Status
))
1660 /* Acquire the mutex */
1661 ExAcquireFastMutex(&DebugObject
->Mutex
);
1663 /* Loop the state list */
1664 ListHead
= &DebugObject
->EventList
;
1665 NextEntry
= ListHead
->Flink
;
1666 while (ListHead
!= NextEntry
)
1668 /* Get the current debug event */
1669 DebugEvent
= CONTAINING_RECORD(NextEntry
,
1673 /* Compare process ID */
1674 if (DebugEvent
->ClientId
.UniqueProcess
==
1675 AppClientId
->UniqueProcess
)
1677 /* Check if we already found a match */
1680 /* Wake it up and break out */
1681 DebugEvent
->Flags
&= ~DEBUG_EVENT_INACTIVE
;
1682 KeSetEvent(&DebugObject
->EventsPresent
,
1688 /* Compare thread ID and flag */
1689 if ((DebugEvent
->ClientId
.UniqueThread
==
1690 AppClientId
->UniqueThread
) && (DebugEvent
->Flags
& DEBUG_EVENT_READ
))
1692 /* Remove the event from the list */
1693 RemoveEntryList(NextEntry
);
1695 /* Remember who to wake */
1697 DebugEventToWake
= DebugEvent
;
1701 /* Go to the next entry */
1702 NextEntry
= NextEntry
->Flink
;
1705 /* Release the mutex */
1706 ExReleaseFastMutex(&DebugObject
->Mutex
);
1708 /* Dereference the object */
1709 ObDereferenceObject(DebugObject
);
1711 /* Check if need a wait */
1714 /* Set the continue status */
1715 DebugEventToWake
->ApiMsg
.ReturnedStatus
= ContinueStatus
;
1716 DebugEventToWake
->Status
= STATUS_SUCCESS
;
1718 /* Wake the target */
1719 DbgkpWakeTarget(DebugEventToWake
);
1724 Status
= STATUS_INVALID_PARAMETER
;
1738 NtDebugActiveProcess(IN HANDLE ProcessHandle
,
1739 IN HANDLE DebugHandle
)
1742 PDEBUG_OBJECT DebugObject
;
1743 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1744 PETHREAD LastThread
;
1747 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1748 ProcessHandle
, DebugHandle
);
1750 /* Reference the process */
1751 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1752 PROCESS_SUSPEND_RESUME
,
1757 if (!NT_SUCCESS(Status
)) return Status
;
1759 /* Don't allow debugging the current process or the system process */
1760 if ((Process
== PsGetCurrentProcess()) ||
1761 (Process
== PsInitialSystemProcess
))
1763 /* Dereference and fail */
1764 ObDereferenceObject(Process
);
1765 return STATUS_ACCESS_DENIED
;
1768 /* Reference the debug object */
1769 Status
= ObReferenceObjectByHandle(DebugHandle
,
1770 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1771 DbgkDebugObjectType
,
1773 (PVOID
*)&DebugObject
,
1775 if (!NT_SUCCESS(Status
))
1777 /* Dereference the process and exit */
1778 ObDereferenceObject(Process
);
1782 /* Acquire process rundown protection */
1783 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1785 /* Dereference the process and debug object and exit */
1786 ObDereferenceObject(Process
);
1787 ObDereferenceObject(DebugObject
);
1788 return STATUS_PROCESS_IS_TERMINATING
;
1791 /* Send fake create messages for debuggers to have a consistent state */
1792 Status
= DbgkpPostFakeProcessCreateMessages(Process
,
1795 Status
= DbgkpSetProcessDebugObject(Process
,
1800 /* Release rundown protection */
1801 ExReleaseRundownProtection(&Process
->RundownProtect
);
1803 /* Dereference the process and debug object and return status */
1804 ObDereferenceObject(Process
);
1805 ObDereferenceObject(DebugObject
);
1814 NtRemoveProcessDebug(IN HANDLE ProcessHandle
,
1815 IN HANDLE DebugHandle
)
1818 PDEBUG_OBJECT DebugObject
;
1819 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1822 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1823 ProcessHandle
, DebugHandle
);
1825 /* Reference the process */
1826 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1827 PROCESS_SUSPEND_RESUME
,
1832 if (!NT_SUCCESS(Status
)) return Status
;
1834 /* Reference the debug object */
1835 Status
= ObReferenceObjectByHandle(DebugHandle
,
1836 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1837 DbgkDebugObjectType
,
1839 (PVOID
*)&DebugObject
,
1841 if (!NT_SUCCESS(Status
))
1843 /* Dereference the process and exit */
1844 ObDereferenceObject(Process
);
1848 /* Remove the debug object */
1849 Status
= DbgkClearProcessDebugObject(Process
, DebugObject
);
1851 /* Dereference the process and debug object and return status */
1852 ObDereferenceObject(Process
);
1853 ObDereferenceObject(DebugObject
);
1862 NtSetInformationDebugObject(IN HANDLE DebugHandle
,
1863 IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass
,
1864 IN PVOID DebugInformation
,
1865 IN ULONG DebugInformationLength
,
1866 OUT PULONG ReturnLength OPTIONAL
)
1868 PDEBUG_OBJECT DebugObject
;
1869 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1871 PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo
= DebugInformation
;
1874 /* Check buffers and parameters */
1875 Status
= DefaultSetInfoBufferCheck(DebugObjectInformationClass
,
1876 DbgkpDebugObjectInfoClass
,
1877 sizeof(DbgkpDebugObjectInfoClass
) /
1878 sizeof(DbgkpDebugObjectInfoClass
[0]),
1880 DebugInformationLength
,
1882 if (!NT_SUCCESS(Status
)) return Status
;
1884 /* Check if the caller wanted the return length */
1887 /* Enter SEH for probe */
1890 /* Return required length to user-mode */
1891 ProbeForWriteUlong(ReturnLength
);
1892 *ReturnLength
= sizeof(*DebugInfo
);
1894 _SEH2_EXCEPT(ExSystemExceptionFilter())
1896 /* Return the exception code */
1897 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1902 /* Open the Object */
1903 Status
= ObReferenceObjectByHandle(DebugHandle
,
1904 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1905 DbgkDebugObjectType
,
1907 (PVOID
*)&DebugObject
,
1909 if (NT_SUCCESS(Status
))
1911 /* Acquire the object */
1912 ExAcquireFastMutex(&DebugObject
->Mutex
);
1914 /* Set the proper flag */
1915 if (DebugInfo
->KillProcessOnExit
)
1917 /* Enable killing the process */
1918 DebugObject
->KillProcessOnExit
= TRUE
;
1923 DebugObject
->KillProcessOnExit
= FALSE
;
1926 /* Release the mutex */
1927 ExReleaseFastMutex(&DebugObject
->Mutex
);
1929 /* Release the Object */
1930 ObDereferenceObject(DebugObject
);
1942 NtWaitForDebugEvent(IN HANDLE DebugHandle
,
1943 IN BOOLEAN Alertable
,
1944 IN PLARGE_INTEGER Timeout OPTIONAL
,
1945 OUT PDBGUI_WAIT_STATE_CHANGE StateChange
)
1947 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1948 LARGE_INTEGER LocalTimeOut
;
1950 LARGE_INTEGER StartTime
;
1953 LARGE_INTEGER NewTime
;
1954 PDEBUG_OBJECT DebugObject
;
1955 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
1957 PDEBUG_EVENT DebugEvent
= NULL
, DebugEvent2
;
1958 PLIST_ENTRY ListHead
, NextEntry
, NextEntry2
;
1960 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p\n", DebugHandle
);
1962 /* Clear the initial wait state change structure and the timeout */
1963 RtlZeroMemory(&WaitStateChange
, sizeof(WaitStateChange
));
1964 LocalTimeOut
.QuadPart
= 0;
1966 /* Check if we were called from user mode */
1967 if (PreviousMode
!= KernelMode
)
1969 /* Protect probe in SEH */
1972 /* Check if we came with a timeout */
1976 ProbeForReadLargeInteger(Timeout
);
1978 /* Make a local copy */
1979 LocalTimeOut
= *Timeout
;
1980 Timeout
= &LocalTimeOut
;
1983 /* Probe the state change structure */
1984 ProbeForWrite(StateChange
, sizeof(*StateChange
), sizeof(ULONG
));
1986 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1988 /* Return the exception code */
1989 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1996 if (Timeout
) LocalTimeOut
= *Timeout
;
1999 /* If we were passed a timeout, query the current time */
2000 if (Timeout
) KeQuerySystemTime(&StartTime
);
2002 /* Get the debug object */
2003 Status
= ObReferenceObjectByHandle(DebugHandle
,
2004 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
2005 DbgkDebugObjectType
,
2007 (PVOID
*)&DebugObject
,
2009 if (!NT_SUCCESS(Status
)) return Status
;
2011 /* Clear process and thread */
2015 /* Wait on the debug object given to us */
2018 Status
= KeWaitForSingleObject(&DebugObject
->EventsPresent
,
2023 if (!NT_SUCCESS(Status
) ||
2024 (Status
== STATUS_TIMEOUT
) ||
2025 (Status
== STATUS_ALERTED
) ||
2026 (Status
== STATUS_USER_APC
))
2028 /* Break out the wait */
2032 /* Lock the object */
2034 ExAcquireFastMutex(&DebugObject
->Mutex
);
2036 /* Check if a debugger is connected */
2037 if (DebugObject
->DebuggerInactive
)
2040 Status
= STATUS_DEBUGGER_INACTIVE
;
2044 /* Loop the events */
2045 ListHead
= &DebugObject
->EventList
;
2046 NextEntry
= ListHead
->Flink
;
2047 while (ListHead
!= NextEntry
)
2049 /* Get the debug event */
2050 DebugEvent
= CONTAINING_RECORD(NextEntry
,
2053 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx\n",
2054 DebugEvent
, DebugEvent
->Flags
);
2057 if (!(DebugEvent
->Flags
& (DEBUG_EVENT_INACTIVE
| DEBUG_EVENT_READ
)))
2059 /* We got an event */
2062 /* Loop the list internally */
2063 NextEntry2
= DebugObject
->EventList
.Flink
;
2064 while (NextEntry2
!= NextEntry
)
2066 /* Get the debug event */
2067 DebugEvent2
= CONTAINING_RECORD(NextEntry2
,
2071 /* Try to match process IDs */
2072 if (DebugEvent2
->ClientId
.UniqueProcess
==
2073 DebugEvent
->ClientId
.UniqueProcess
)
2075 /* Found it, break out */
2076 DebugEvent
->Flags
|= DEBUG_EVENT_INACTIVE
;
2077 DebugEvent
->BackoutThread
= NULL
;
2082 /* Move to the next entry */
2083 NextEntry2
= NextEntry2
->Flink
;
2086 /* Check if we still have a valid event */
2087 if (GotEvent
) break;
2090 /* Move to the next entry */
2091 NextEntry
= NextEntry
->Flink
;
2094 /* Check if we have an event */
2097 /* Save and reference the process and thread */
2098 Process
= DebugEvent
->Process
;
2099 Thread
= DebugEvent
->Thread
;
2100 ObReferenceObject(Process
);
2101 ObReferenceObject(Thread
);
2103 /* Convert to user-mode structure */
2104 DbgkpConvertKernelToUserStateChange(&WaitStateChange
,
2108 DebugEvent
->Flags
|= DEBUG_EVENT_READ
;
2112 /* Unsignal the event */
2113 KeClearEvent(&DebugObject
->EventsPresent
);
2117 Status
= STATUS_SUCCESS
;
2120 /* Release the mutex */
2121 ExReleaseFastMutex(&DebugObject
->Mutex
);
2122 if (!NT_SUCCESS(Status
)) break;
2124 /* Check if we got an event */
2127 /* Check if we can wait again */
2128 if (LocalTimeOut
.QuadPart
< 0)
2130 /* Query the new time */
2131 KeQuerySystemTime(&NewTime
);
2133 /* Substract times */
2134 LocalTimeOut
.QuadPart
+= (NewTime
.QuadPart
- StartTime
.QuadPart
);
2135 StartTime
= NewTime
;
2137 /* Check if we've timed out */
2138 if (LocalTimeOut
.QuadPart
>= 0)
2140 /* We have, break out of the loop */
2141 Status
= STATUS_TIMEOUT
;
2148 /* Open the handles and dereference the objects */
2149 DbgkpOpenHandles(&WaitStateChange
, Process
, Thread
);
2150 ObDereferenceObject(Process
);
2151 ObDereferenceObject(Thread
);
2156 /* We're done, dereference the object */
2157 ObDereferenceObject(DebugObject
);
2159 /* Protect write with SEH */
2162 /* Return our wait state change structure */
2163 *StateChange
= WaitStateChange
;
2165 _SEH2_EXCEPT(ExSystemExceptionFilter())
2167 /* Get SEH Exception code */
2168 Status
= _SEH2_GetExceptionCode();