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 ******************************************************************/
13 #include <internal/debug.h>
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
& 2) ? TRUE
: FALSE
;
60 DebugEvent
= ExAllocatePoolWithTag(NonPagedPool
,
62 TAG('D', 'b', 'g', 'E'));
63 if (!DebugEvent
) return STATUS_INSUFFICIENT_RESOURCES
;
66 DebugEvent
->Flags
= Flags
| 4;
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 RtlCopyMemory(&DebugEvent
->ApiMsg
, Message
, sizeof(DBGKM_MSG
));
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 RtlCopyMemory(Message
, &DebugEvent
->ApiMsg
, sizeof(DBGKM_MSG
));
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
];
214 /* Suspend process if required */
215 if (SuspendProcess
) DbgkpSuspendProcess();
217 /* Set return status */
218 Message
->ReturnedStatus
= STATUS_PENDING
;
220 /* Set create process reported state */
221 PsGetCurrentProcess()->CreateReported
= TRUE
;
223 /* Send the LPC command */
224 Status
= LpcRequestWaitReplyPort(Port
,
225 (PPORT_MESSAGE
)Message
,
226 (PPORT_MESSAGE
)&Buffer
[0]);
228 /* Flush the instruction cache */
229 ZwFlushInstructionCache(NtCurrentProcess(), NULL
, 0);
231 /* Copy the buffer back */
232 if (NT_SUCCESS(Status
)) RtlCopyMemory(Message
, Buffer
, sizeof(DBGKM_MSG
));
234 /* Resume the process if it was suspended */
235 if (SuspendProcess
) DbgkpResumeProcess();
241 DbgkpSendApiMessage(IN OUT PDBGKM_MSG ApiMsg
,
246 DBGKTRACE(DBGK_MESSAGE_DEBUG
, "ApiMsg: %p Flags: %lx\n", ApiMsg
, Flags
);
248 /* Suspend process if required */
249 if (Flags
) DbgkpSuspendProcess();
251 /* Set return status */
252 ApiMsg
->ReturnedStatus
= STATUS_PENDING
;
254 /* Set create process reported state */
255 PsGetCurrentProcess()->CreateReported
= TRUE
;
257 /* Send the LPC command */
258 Status
= DbgkpQueueMessage(PsGetCurrentProcess(),
259 PsGetCurrentThread(),
264 /* Flush the instruction cache */
265 ZwFlushInstructionCache(NtCurrentProcess(), NULL
, 0);
267 /* Resume the process if it was suspended */
268 if (Flags
) DbgkpResumeProcess();
274 DbgkCopyProcessDebugPort(IN PEPROCESS Process
,
277 PDEBUG_OBJECT DebugObject
;
279 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Parent: %p\n", Process
, Parent
);
281 /* Clear this process's port */
282 Process
->DebugPort
= NULL
;
284 /* Check if the parent has one */
285 if (!Parent
->DebugPort
) return;
287 /* It does, acquire the mutex */
288 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
290 /* Make sure it still has one, and that we should inherit */
291 DebugObject
= Parent
->DebugPort
;
292 if ((DebugObject
) && !(Process
->NoDebugInherit
))
294 /* Acquire the debug object's lock */
295 ExAcquireFastMutex(&DebugObject
->Mutex
);
297 /* Make sure the debugger is active */
298 if (!DebugObject
->DebuggerInactive
)
300 /* Reference the object and set it */
301 ObReferenceObject(DebugObject
);
302 Process
->DebugPort
= DebugObject
;
305 /* Release the debug object */
306 ExReleaseFastMutex(&DebugObject
->Mutex
);
309 /* Release the port mutex */
310 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
315 DbgkForwardException(IN PEXCEPTION_RECORD ExceptionRecord
,
316 IN BOOLEAN DebugPort
,
317 IN BOOLEAN SecondChance
)
319 DBGKM_MSG ApiMessage
;
320 PDBGKM_EXCEPTION DbgKmException
= &ApiMessage
.Exception
;
322 PEPROCESS Process
= PsGetCurrentProcess();
324 BOOLEAN UseLpc
= FALSE
;
326 DBGKTRACE(DBGK_EXCEPTION_DEBUG
,
327 "ExceptionRecord: %p Port: %p\n", ExceptionRecord
, DebugPort
);
329 /* Setup the API Message */
330 ApiMessage
.h
.u1
.Length
= sizeof(DBGKM_MSG
) << 16 |
331 (8 + sizeof(DBGKM_EXCEPTION
));
332 ApiMessage
.h
.u2
.ZeroInit
= LPC_DEBUG_EVENT
;
333 ApiMessage
.ApiNumber
= DbgKmExceptionApi
;
335 /* Check if this is to be sent on the debug port */
338 /* Use the debug port, unless the thread is being hidden */
339 Port
= PsGetCurrentThread()->HideFromDebugger
?
340 NULL
: Process
->DebugPort
;
344 /* Otherwise, use the exception port */
345 Port
= Process
->ExceptionPort
;
346 ApiMessage
.h
.u2
.ZeroInit
= LPC_EXCEPTION
;
350 /* Break out if there's no port */
351 if (!Port
) return FALSE
;
353 /* Fill out the exception information */
354 DbgKmException
->ExceptionRecord
= *ExceptionRecord
;
355 DbgKmException
->FirstChance
= !SecondChance
;
357 /* Check if we should use LPC */
360 /* Send the message on the LPC Port */
361 Status
= DbgkpSendApiMessageLpc(&ApiMessage
, Port
, DebugPort
);
365 /* Use native debug object */
366 Status
= DbgkpSendApiMessage(&ApiMessage
, DebugPort
);
369 /* Check if we failed, and for a debug port, also check the return status */
370 if (!(NT_SUCCESS(Status
)) ||
372 (!(NT_SUCCESS(ApiMessage
.ReturnedStatus
)) ||
373 (ApiMessage
.ReturnedStatus
== DBG_EXCEPTION_NOT_HANDLED
))))
379 /* Otherwise, we're ok */
385 DbgkpFreeDebugEvent(IN PDEBUG_EVENT DebugEvent
)
387 PHANDLE Handle
= NULL
;
389 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
391 /* Check if this event had a file handle */
392 switch (DebugEvent
->ApiMsg
.ApiNumber
)
394 /* Create process has a handle */
395 case DbgKmCreateProcessApi
:
397 /* Get the pointer */
398 Handle
= &DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
;
401 /* As does DLL load */
402 case DbgKmLoadDllApi
:
404 /* Get the pointer */
405 Handle
= &DebugEvent
->ApiMsg
.LoadDll
.FileHandle
;
411 /* Close the handle if it exsts */
412 if ((Handle
) && (*Handle
)) ObCloseHandle(*Handle
, KernelMode
);
414 /* Dereference process and thread and free the event */
415 ObDereferenceObject(DebugEvent
->Process
);
416 ObDereferenceObject(DebugEvent
->Thread
);
417 ExFreePool(DebugEvent
);
422 DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent
)
424 PETHREAD Thread
= DebugEvent
->Thread
;
426 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
428 /* Check if we have to wake the thread */
429 if (DebugEvent
->Flags
& 20) PsResumeThread(Thread
, NULL
);
431 /* Check if we had locked the thread */
432 if (DebugEvent
->Flags
& 8)
435 ExReleaseRundownProtection(&Thread
->RundownProtect
);
438 /* Check if we have to wake up the event */
439 if (DebugEvent
->Flags
& 2)
441 /* Otherwise, free the debug event */
442 DbgkpFreeDebugEvent(DebugEvent
);
446 /* Signal the continue event */
447 KeSetEvent(&DebugEvent
->ContinueEvent
, IO_NO_INCREMENT
, FALSE
);
453 DbgkpPostFakeModuleMessages(IN PEPROCESS Process
,
455 IN PDEBUG_OBJECT DebugObject
)
457 PPEB Peb
= Process
->Peb
;
458 PPEB_LDR_DATA LdrData
;
459 PLDR_DATA_TABLE_ENTRY LdrEntry
;
460 PLIST_ENTRY ListHead
, NextEntry
;
461 DBGKM_MSG ApiMessage
;
462 PDBGKM_LOAD_DLL LoadDll
= &ApiMessage
.LoadDll
;
464 PIMAGE_NT_HEADERS NtHeader
;
465 UNICODE_STRING ModuleName
;
466 OBJECT_ATTRIBUTES ObjectAttributes
;
467 IO_STATUS_BLOCK IoStatusBlock
;
470 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Thread: %p DebugObject: %p\n",
471 Process
, Thread
, DebugObject
);
473 /* Quit if there's no PEB */
474 if (!Peb
) return STATUS_SUCCESS
;
476 /* Get the Loader Data List */
478 ListHead
= &LdrData
->InLoadOrderModuleList
;
479 NextEntry
= ListHead
->Flink
;
481 /* Loop the modules */
483 while ((NextEntry
!= ListHead
) && (i
< 500))
485 /* Skip the first entry */
488 /* Go to the next module */
489 NextEntry
= NextEntry
->Flink
;
495 LdrEntry
= CONTAINING_RECORD(NextEntry
,
496 LDR_DATA_TABLE_ENTRY
,
499 /* Setup the API Message */
500 RtlZeroMemory(&ApiMessage
, sizeof(DBGKM_MSG
));
501 ApiMessage
.ApiNumber
= DbgKmLoadDllApi
;
503 /* Set base and clear the name */
504 LoadDll
->BaseOfDll
= LdrEntry
->DllBase
;
505 LoadDll
->NamePointer
= NULL
;
507 /* Get the NT Headers */
508 NtHeader
= RtlImageNtHeader(LoadDll
->BaseOfDll
);
511 /* Save debug data */
512 LoadDll
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
513 PointerToSymbolTable
;
514 LoadDll
->DebugInfoSize
= NtHeader
->FileHeader
.NumberOfSymbols
;
518 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Name: %wZ. Base: %p\n",
519 &LdrEntry
->FullDllName
, LdrEntry
->DllBase
);
521 /* Get the name of the DLL */
522 Status
= MmGetFileNameForAddress(NtHeader
, &ModuleName
);
523 if (NT_SUCCESS(Status
))
525 /* Setup the object attributes */
526 InitializeObjectAttributes(&ObjectAttributes
,
528 OBJ_FORCE_ACCESS_CHECK
|
530 OBJ_CASE_INSENSITIVE
,
534 /* Open the file to get a handle to it */
535 Status
= ZwOpenFile(&LoadDll
->FileHandle
,
536 GENERIC_READ
| SYNCHRONIZE
,
542 FILE_SYNCHRONOUS_IO_NONALERT
);
543 if (!NT_SUCCESS(Status
)) LoadDll
->FileHandle
= NULL
;
545 /* Free the name now */
546 ExFreePool(ModuleName
.Buffer
);
549 /* Send the fake module load message */
550 Status
= DbgkpQueueMessage(Process
,
555 if (!NT_SUCCESS(Status
))
557 /* Message send failed, close the file handle if we had one */
558 if (LoadDll
->FileHandle
) ObCloseHandle(LoadDll
->FileHandle
,
562 /* Go to the next module */
563 NextEntry
= NextEntry
->Flink
;
568 return STATUS_SUCCESS
;
573 DbgkpPostFakeThreadMessages(IN PEPROCESS Process
,
574 IN PDEBUG_OBJECT DebugObject
,
575 IN PETHREAD StartThread
,
576 OUT PETHREAD
*FirstThread
,
577 OUT PETHREAD
*LastThread
)
579 PETHREAD pFirstThread
= NULL
, ThisThread
, OldThread
= NULL
, pLastThread
;
580 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
581 BOOLEAN IsFirstThread
;
583 DBGKM_MSG ApiMessage
;
584 PDBGKM_CREATE_THREAD CreateThread
= &ApiMessage
.CreateThread
;
585 PDBGKM_CREATE_PROCESS CreateProcess
= &ApiMessage
.CreateProcess
;
587 PIMAGE_NT_HEADERS NtHeader
;
589 DBGKTRACE(DBGK_THREAD_DEBUG
, "Process: %p StartThread: %p Object: %p\n",
590 Process
, StartThread
, DebugObject
);
592 /* Check if we have a start thread */
595 /* Then the one we'll find won't be the first one */
596 IsFirstThread
= FALSE
;
597 pFirstThread
= StartThread
;
598 ThisThread
= StartThread
;
601 ObReferenceObject(StartThread
);
605 /* Get the first thread ourselves */
606 ThisThread
= PsGetNextProcessThread(Process
, OldThread
);
607 IsFirstThread
= TRUE
;
610 /* Start thread loop */
613 /* Dereference the previous thread if we had one */
614 if (OldThread
) ObDereferenceObject(OldThread
);
616 /* Set this as the last thread and lock it */
617 pLastThread
= ThisThread
;
618 ObReferenceObject(ThisThread
);
619 if (ExAcquireRundownProtection(&ThisThread
->RundownProtect
))
621 /* Acquire worked, set flags */
624 /* Check if this is a user thread */
625 if (!ThisThread
->SystemThread
)
628 if (NT_SUCCESS(PsSuspendThread(ThisThread
, NULL
)))
631 Flags
= 0x8 | 0x2 | 0x20;
637 /* Couldn't acquire rundown */
641 /* Clear the API Message */
642 RtlZeroMemory(&ApiMessage
, sizeof(ApiMessage
));
644 /* Check if this is the first thread */
645 if ((IsFirstThread
) &&
647 !(ThisThread
->SystemThread
) &&
648 (ThisThread
->GrantedAccess
))
650 /* It is, save the flag */
655 /* It isn't, save the flag */
659 /* Check if this is the first */
662 /* So we'll start with the create process message */
663 ApiMessage
.ApiNumber
= DbgKmCreateProcessApi
;
665 /* Get the file handle */
666 if (Process
->SectionObject
)
668 /* Use the section object */
669 CreateProcess
->FileHandle
=
670 DbgkpSectionToFileHandle(Process
->SectionObject
);
674 /* Don't return any handle */
675 CreateProcess
->FileHandle
= NULL
;
678 /* Set the base address */
679 CreateProcess
->BaseOfImage
= Process
->SectionBaseAddress
;
681 /* Get the NT Header */
682 NtHeader
= RtlImageNtHeader(Process
->SectionBaseAddress
);
685 /* Fill out data from the header */
686 CreateProcess
->DebugInfoFileOffset
= NtHeader
->FileHeader
.
687 PointerToSymbolTable
;
688 CreateProcess
->DebugInfoSize
= NtHeader
->FileHeader
.
694 /* Otherwise it's a thread message */
695 ApiMessage
.ApiNumber
= DbgKmCreateThreadApi
;
696 CreateThread
->StartAddress
= ThisThread
->StartAddress
;
700 DBGKTRACE(DBGK_THREAD_DEBUG
, "Thread: %p. First: %lx, OldThread: %p\n",
701 ThisThread
, First
, OldThread
);
702 DBGKTRACE(DBGK_THREAD_DEBUG
, "Start Address: %p\n",
703 ThisThread
->StartAddress
);
705 /* Queue the message */
706 Status
= DbgkpQueueMessage(Process
,
711 if (!NT_SUCCESS(Status
))
713 /* We failed. FIXME: Handle this */
714 DPRINT1("Unhandled Dbgk codepath!\n");
718 /* Check if this was the first message */
721 /* It isn't the first thread anymore */
722 IsFirstThread
= FALSE
;
724 /* Reference this thread and set it as first */
725 ObDereferenceObject(ThisThread
);
726 pFirstThread
= ThisThread
;
729 /* Get the next thread */
730 ThisThread
= PsGetNextProcessThread(Process
, ThisThread
);
731 OldThread
= pLastThread
;
732 } while (ThisThread
);
734 /* Check the API status */
735 if (!NT_SUCCESS(Status
))
737 /* We failed. FIXME: Handle this */
738 DPRINT1("Unhandled Dbgk codepath!\n");
742 /* Make sure we have a first thread */
743 if (!pFirstThread
) return STATUS_UNSUCCESSFUL
;
745 /* Return thread pointers */
746 *FirstThread
= pFirstThread
;
747 *LastThread
= pLastThread
;
753 DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process
,
754 IN PDEBUG_OBJECT DebugObject
,
755 OUT PETHREAD
*LastThread
)
758 PETHREAD FirstThread
, FinalThread
;
759 PETHREAD ReturnThread
= NULL
;
762 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
763 Process
, DebugObject
);
765 /* Attach to the process */
766 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
768 /* Post the fake thread messages */
769 Status
= DbgkpPostFakeThreadMessages(Process
,
774 if (NT_SUCCESS(Status
))
776 /* Send the fake module messages too */
777 Status
= DbgkpPostFakeModuleMessages(Process
,
780 if (!NT_SUCCESS(Status
))
782 /* We failed, dereference the final thread */
783 ObDereferenceObject(FinalThread
);
787 /* Set the final thread */
788 ReturnThread
= FinalThread
;
791 /* Dereference the first thread */
792 ObDereferenceObject(FirstThread
);
795 /* Detach from the process */
796 KeUnstackDetachProcess(&ApcState
);
798 /* Return the last thread */
799 *LastThread
= ReturnThread
;
805 DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
806 IN PDEBUG_EVENT DebugEvent
)
808 DBGKTRACE(DBGK_OBJECT_DEBUG
, "DebugEvent: %p\n", DebugEvent
);
810 /* Start by copying the client ID */
811 WaitStateChange
->AppClientId
= DebugEvent
->ClientId
;
813 /* Now check which kind of event this was */
814 switch (DebugEvent
->ApiMsg
.ApiNumber
)
817 case DbgKmCreateProcessApi
:
819 /* Set the right native code */
820 WaitStateChange
->NewState
= DbgCreateProcessStateChange
;
822 /* Copy the information */
823 WaitStateChange
->StateInfo
.CreateProcessInfo
.NewProcess
=
824 DebugEvent
->ApiMsg
.CreateProcess
;
826 /* Clear the file handle for us */
827 DebugEvent
->ApiMsg
.CreateProcess
.FileHandle
= NULL
;
831 case DbgKmCreateThreadApi
:
833 /* Set the right native code */
834 WaitStateChange
->NewState
= DbgCreateThreadStateChange
;
836 /* Copy information */
837 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.StartAddress
=
838 DebugEvent
->ApiMsg
.CreateThread
.StartAddress
;
839 WaitStateChange
->StateInfo
.CreateThread
.NewThread
.SubSystemKey
=
840 DebugEvent
->ApiMsg
.CreateThread
.SubSystemKey
;
843 /* Exception (or breakpoint/step) */
844 case DbgKmExceptionApi
:
846 /* Look at the exception code */
847 if (DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
850 /* Update this as a breakpoint exception */
851 WaitStateChange
->NewState
= DbgBreakpointStateChange
;
853 else if (DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
.ExceptionCode
==
856 /* Update this as a single step exception */
857 WaitStateChange
->NewState
= DbgSingleStepStateChange
;
861 /* Otherwise, set default exception */
862 WaitStateChange
->NewState
= DbgExceptionStateChange
;
865 /* Copy the exception record */
866 WaitStateChange
->StateInfo
.Exception
.ExceptionRecord
=
867 DebugEvent
->ApiMsg
.Exception
.ExceptionRecord
;
871 case DbgKmExitProcessApi
:
873 /* Set the right native code and copy the exit code */
874 WaitStateChange
->NewState
= DbgExitProcessStateChange
;
875 WaitStateChange
->StateInfo
.ExitProcess
.ExitStatus
=
876 DebugEvent
->ApiMsg
.ExitProcess
.ExitStatus
;
880 case DbgKmExitThreadApi
:
882 /* Set the right native code */
883 WaitStateChange
->NewState
= DbgExitThreadStateChange
;
884 WaitStateChange
->StateInfo
.ExitThread
.ExitStatus
=
885 DebugEvent
->ApiMsg
.ExitThread
.ExitStatus
;
889 case DbgKmLoadDllApi
:
891 /* Set the native code */
892 WaitStateChange
->NewState
= DbgLoadDllStateChange
;
895 WaitStateChange
->StateInfo
.LoadDll
= DebugEvent
->ApiMsg
.LoadDll
;
897 /* Clear the file handle for us */
898 DebugEvent
->ApiMsg
.LoadDll
.FileHandle
= NULL
;
902 case DbgKmUnloadDllApi
:
904 /* Set the native code and copy the address */
905 WaitStateChange
->NewState
= DbgUnloadDllStateChange
;
906 WaitStateChange
->StateInfo
.UnloadDll
.BaseAddress
=
907 DebugEvent
->ApiMsg
.UnloadDll
.BaseAddress
;
912 /* Shouldn't happen */
919 DbgkpMarkProcessPeb(IN PEPROCESS Process
)
923 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p\n", Process
);
925 /* Acquire process rundown */
926 if (!ExAcquireRundownProtection(&Process
->RundownProtect
)) return;
928 /* Make sure we have a PEB */
931 /* Attach to the process */
932 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
934 /* Acquire the debug port mutex */
935 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
937 /* Set the IsBeingDebugged member of the PEB */
938 Process
->Peb
->BeingDebugged
= (Process
->DebugPort
) ? TRUE
: FALSE
;
941 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
943 /* Detach from the process */
944 KeUnstackDetachProcess(&ApcState
);
947 /* Release rundown protection */
948 ExReleaseRundownProtection(&Process
->RundownProtect
);
953 DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange
,
954 IN PEPROCESS Process
,
961 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Process: %p Thread: %p State: %lx\n",
962 Process
, Thread
, WaitStateChange
->NewState
);
964 /* Check which state this is */
965 switch (WaitStateChange
->NewState
)
968 case DbgCreateThreadStateChange
:
970 /* Get handle to thread */
971 Status
= ObOpenObjectByPointer(Thread
,
978 if (NT_SUCCESS(Status
))
980 /* Save the thread handle */
982 StateInfo
.CreateThread
.HandleToThread
= Handle
;
987 case DbgCreateProcessStateChange
:
989 /* Get handle to thread */
990 Status
= ObOpenObjectByPointer(Thread
,
997 if (NT_SUCCESS(Status
))
999 /* Save the thread handle */
1001 StateInfo
.CreateProcessInfo
.HandleToThread
= Handle
;
1004 /* Get handle to process */
1005 Status
= ObOpenObjectByPointer(Process
,
1012 if (NT_SUCCESS(Status
))
1014 /* Save the process handle */
1016 StateInfo
.CreateProcessInfo
.HandleToProcess
= Handle
;
1019 /* Fall through to duplicate file handle */
1020 DupHandle
= &WaitStateChange
->
1021 StateInfo
.CreateProcessInfo
.NewProcess
.FileHandle
;
1025 case DbgLoadDllStateChange
:
1027 /* Fall through to duplicate file handle */
1028 DupHandle
= &WaitStateChange
->StateInfo
.LoadDll
.FileHandle
;
1031 /* Anything else has no handles */
1036 /* If we got here, then we have to duplicate a handle, possibly */
1037 Handle
= *DupHandle
;
1041 Status
= ObDuplicateObject(PsGetCurrentProcess(),
1043 PsGetCurrentProcess(),
1047 DUPLICATE_SAME_ACCESS
,
1049 if (!NT_SUCCESS(Status
)) *DupHandle
= NULL
;
1051 /* Close the original handle */
1052 ObCloseHandle(Handle
, KernelMode
);
1058 DbgkpDeleteObject(IN PVOID DebugObject
)
1063 ASSERT(IsListEmpty(&((PDEBUG_OBJECT
)DebugObject
)->EventList
));
1068 DbgkpCloseObject(IN PEPROCESS OwnerProcess OPTIONAL
,
1069 IN PVOID ObjectBody
,
1070 IN ACCESS_MASK GrantedAccess
,
1071 IN ULONG HandleCount
,
1072 IN ULONG SystemHandleCount
)
1074 PDEBUG_OBJECT DebugObject
= ObjectBody
;
1075 PEPROCESS Process
= NULL
;
1076 BOOLEAN DebugPortCleared
= FALSE
;
1077 PLIST_ENTRY DebugEventList
;
1078 PDEBUG_EVENT DebugEvent
;
1079 DBGKTRACE(DBGK_OBJECT_DEBUG
, "OwnerProcess: %p DebugObject: %p\n",
1080 OwnerProcess
, DebugObject
);
1082 /* If this isn't the last handle, do nothing */
1083 if (SystemHandleCount
> 1) return;
1085 /* Otherwise, lock the debug object */
1086 ExAcquireFastMutex(&DebugObject
->Mutex
);
1088 /* Set it as inactive */
1089 DebugObject
->DebuggerInactive
= TRUE
;
1091 /* Remove it from the debug event list */
1092 DebugEventList
= DebugObject
->EventList
.Flink
;
1093 InitializeListHead(&DebugObject
->EventList
);
1095 /* Release the lock */
1096 ExReleaseFastMutex(&DebugObject
->Mutex
);
1098 /* Signal the wait event */
1099 KeSetEvent(&DebugObject
->EventsPresent
, IO_NO_INCREMENT
, FALSE
);
1101 /* Start looping each process */
1102 while ((Process
= PsGetNextProcess(Process
)))
1104 /* Check if the process has us as their debug port */
1105 if (Process
->DebugPort
== DebugObject
)
1107 /* Acquire the process debug port lock */
1108 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1110 /* Check if it's still us */
1111 if (Process
->DebugPort
== DebugObject
)
1113 /* Clear it and remember */
1114 Process
->DebugPort
= NULL
;
1115 DebugPortCleared
= TRUE
;
1118 /* Release the port lock */
1119 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1121 /* Check if we cleared the debug port */
1122 if (DebugPortCleared
)
1124 /* Mark this in the PEB */
1125 DbgkpMarkProcessPeb(OwnerProcess
);
1127 /* Check if we terminate on exit */
1128 if (DebugObject
->KillProcessOnExit
)
1130 /* Terminate the process */
1131 PsTerminateProcess(OwnerProcess
, STATUS_DEBUGGER_INACTIVE
);
1134 /* Dereference the debug object */
1135 ObDereferenceObject(DebugObject
);
1140 /* Loop debug events */
1141 while (DebugEventList
!= &DebugObject
->EventList
)
1143 /* Get the debug event */
1144 DebugEvent
= CONTAINING_RECORD(DebugEventList
, DEBUG_EVENT
, EventList
);
1146 /* Go to the next entry */
1147 DebugEventList
= DebugEventList
->Flink
;
1150 DebugEvent
->Status
= STATUS_DEBUGGER_INACTIVE
;
1151 DbgkpWakeTarget(DebugEvent
);
1157 DbgkpSetProcessDebugObject(IN PEPROCESS Process
,
1158 IN PDEBUG_OBJECT DebugObject
,
1159 IN NTSTATUS MsgStatus
,
1160 IN PETHREAD LastThread
)
1163 LIST_ENTRY TempList
;
1164 BOOLEAN GlobalHeld
= FALSE
, DoSetEvent
= TRUE
;
1165 PETHREAD ThisThread
, FirstThread
;
1166 PLIST_ENTRY NextEntry
;
1167 PDEBUG_EVENT DebugEvent
;
1168 PETHREAD EventThread
;
1170 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
1171 Process
, DebugObject
);
1173 /* Initialize the temporary list */
1174 InitializeListHead(&TempList
);
1176 /* Check if we have a success message */
1177 if (NT_SUCCESS(MsgStatus
))
1179 /* Then default to STATUS_SUCCESS */
1180 Status
= STATUS_SUCCESS
;
1184 /* No last thread, and set the failure code */
1189 /* Now check what status we have here */
1190 if (NT_SUCCESS(Status
))
1192 /* Acquire the global lock */
1194 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1196 /* Check if we already have a port */
1197 if (Process
->DebugPort
)
1200 Status
= STATUS_PORT_ALREADY_SET
;
1205 /* Otherwise, set the port and reference the thread */
1206 Process
->DebugPort
= DebugObject
;
1207 ObReferenceObject(LastThread
);
1209 /* Get the next thread */
1210 ThisThread
= PsGetNextProcessThread(Process
, LastThread
);
1213 /* Clear the debug port and release the lock */
1214 Process
->DebugPort
= NULL
;
1215 ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1218 /* Dereference the thread */
1219 ObDereferenceObject(LastThread
);
1221 /* Post fake messages */
1222 Status
= DbgkpPostFakeThreadMessages(Process
,
1227 if (!NT_SUCCESS(Status
))
1229 /* Clear the last thread */
1234 /* Dereference the first thread and re-acquire the lock */
1235 ObDereferenceObject(FirstThread
);
1237 ExAcquireFastMutex(&DbgkpProcessDebugPortMutex
);
1239 /* Check if we should loop again */
1240 if (!Process
->DebugPort
) goto ThreadScan
;
1242 /* Otherwise, we already have a port */
1243 Status
= STATUS_PORT_ALREADY_SET
;
1249 /* Acquire the debug object's lock */
1250 ExAcquireFastMutex(&DebugObject
->Mutex
);
1252 /* Check our status here */
1253 if (NT_SUCCESS(Status
))
1255 /* Check if we're disconnected */
1256 if (DebugObject
->DebuggerInactive
)
1259 Process
->DebugPort
= NULL
;
1260 Status
= STATUS_DEBUGGER_INACTIVE
;
1264 /* Set the process flags */
1265 InterlockedOr((PLONG
)&Process
->Flags
,
1266 PSF_NO_DEBUG_INHERIT_BIT
|
1267 PSF_CREATE_REPORTED_BIT
);
1269 /* Reference the debug object */
1270 ObDereferenceObject(DebugObject
);
1274 /* Loop the events list */
1275 NextEntry
= DebugObject
->EventList
.Flink
;
1276 while (NextEntry
!= &DebugObject
->EventList
)
1278 /* Get the debug event */
1279 DebugEvent
= CONTAINING_RECORD(NextEntry
, DEBUG_EVENT
, EventList
);
1280 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx TH: %p/%p\n",
1281 DebugEvent
, DebugEvent
->Flags
,
1282 DebugEvent
->BackoutThread
, PsGetCurrentThread());
1284 /* Check for if the debug event queue needs flushing */
1285 if ((DebugEvent
->Flags
& 4) &&
1286 (DebugEvent
->BackoutThread
== PsGetCurrentThread()))
1288 /* Get the event's thread */
1289 EventThread
= DebugEvent
->Thread
;
1290 DBGKTRACE(DBGK_PROCESS_DEBUG
, "EventThread: %p MsgStatus: %lx\n",
1291 EventThread
, MsgStatus
);
1293 /* Check if the status is success */
1294 if ((MsgStatus
== STATUS_SUCCESS
) &&
1295 (EventThread
->GrantedAccess
) &&
1296 (!EventThread
->SystemThread
))
1298 /* Check if we couldn't acquire rundown for it */
1299 if (DebugEvent
->Flags
& 0x10)
1301 /* Set the skip termination flag */
1302 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1304 /* Insert it into the temp list */
1305 RemoveEntryList(&DebugEvent
->EventList
);
1306 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1310 /* Do we need to signal the event */
1314 DebugEvent
->Flags
&= ~4;
1315 KeSetEvent(&DebugObject
->EventsPresent
,
1321 /* Clear the backout thread */
1322 DebugEvent
->BackoutThread
= NULL
;
1325 PspSetCrossThreadFlag(EventThread
, CT_SKIP_CREATION_MSG_BIT
);
1330 /* Insert it into the temp list */
1331 RemoveEntryList(&DebugEvent
->EventList
);
1332 InsertTailList(&TempList
, &DebugEvent
->EventList
);
1335 /* Check if the lock is held */
1336 if (DebugEvent
->Flags
& 8)
1339 DebugEvent
->Flags
&= ~8;
1340 ExReleaseRundownProtection(&EventThread
->RundownProtect
);
1344 /* Go to the next entry */
1345 NextEntry
= NextEntry
->Flink
;
1348 /* Release the debug object */
1349 ExReleaseFastMutex(&DebugObject
->Mutex
);
1351 /* Release the global lock if acquired */
1352 if (GlobalHeld
) ExReleaseFastMutex(&DbgkpProcessDebugPortMutex
);
1354 /* Check if there's a thread to dereference */
1355 if (LastThread
) ObDereferenceObject(LastThread
);
1357 /* Loop our temporary list */
1358 while (!IsListEmpty(&TempList
))
1360 /* Remove the event */
1361 NextEntry
= RemoveHeadList(&TempList
);
1362 DebugEvent
= CONTAINING_RECORD (NextEntry
, DEBUG_EVENT
, EventList
);
1365 DbgkpWakeTarget(DebugEvent
);
1368 /* Check if we got here through success and mark the PEB, then return */
1369 if (NT_SUCCESS(Status
)) DbgkpMarkProcessPeb(Process
);
1375 DbgkClearProcessDebugObject(IN PEPROCESS Process
,
1376 IN PDEBUG_OBJECT SourceDebugObject
)
1379 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p DebugObject: %p\n",
1380 Process
, SourceDebugObject
);
1381 return STATUS_UNSUCCESSFUL
;
1387 DbgkInitialize(VOID
)
1389 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
1390 UNICODE_STRING Name
;
1393 /* Initialize the process debug port mutex */
1394 ExInitializeFastMutex(&DbgkpProcessDebugPortMutex
);
1396 /* Create the Debug Object Type */
1397 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
1398 RtlInitUnicodeString(&Name
, L
"DebugObject");
1399 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
1400 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(DEBUG_OBJECT
);
1401 ObjectTypeInitializer
.GenericMapping
= DbgkDebugObjectMapping
;
1402 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
1403 ObjectTypeInitializer
.ValidAccessMask
= DEBUG_OBJECT_ALL_ACCESS
;
1404 ObjectTypeInitializer
.UseDefaultObject
= TRUE
;
1405 ObjectTypeInitializer
.CloseProcedure
= DbgkpCloseObject
;
1406 ObjectTypeInitializer
.DeleteProcedure
= DbgkpDeleteObject
;
1407 ObCreateObjectType(&Name
,
1408 &ObjectTypeInitializer
,
1410 &DbgkDebugObjectType
);
1413 /* PUBLIC FUNCTIONS **********************************************************/
1417 NtCreateDebugObject(OUT PHANDLE DebugHandle
,
1418 IN ACCESS_MASK DesiredAccess
,
1419 IN POBJECT_ATTRIBUTES ObjectAttributes
,
1420 IN BOOLEAN KillProcessOnExit
)
1422 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1423 PDEBUG_OBJECT DebugObject
;
1425 NTSTATUS Status
= STATUS_SUCCESS
;
1428 /* Check if we were called from user mode*/
1429 if (PreviousMode
!= KernelMode
)
1431 /* Enter SEH for probing */
1434 /* Probe the handle */
1435 ProbeForWriteHandle(DebugHandle
);
1439 /* Get exception error */
1440 Status
= _SEH_GetExceptionCode();
1442 if (!NT_SUCCESS(Status
)) return Status
;
1445 /* Create the Object */
1446 Status
= ObCreateObject(PreviousMode
,
1447 DbgkDebugObjectType
,
1451 sizeof(DEBUG_OBJECT
),
1454 (PVOID
*)&DebugObject
);
1455 if (NT_SUCCESS(Status
))
1457 /* Initialize the Debug Object's Fast Mutex */
1458 ExInitializeFastMutex(&DebugObject
->Mutex
);
1460 /* Initialize the State Event List */
1461 InitializeListHead(&DebugObject
->EventList
);
1463 /* Initialize the Debug Object's Wait Event */
1464 KeInitializeEvent(&DebugObject
->EventsPresent
,
1469 DebugObject
->KillProcessOnExit
= KillProcessOnExit
;
1472 Status
= ObInsertObject((PVOID
)DebugObject
,
1478 if (NT_SUCCESS(Status
))
1482 *DebugHandle
= hDebug
;
1486 Status
= _SEH_GetExceptionCode();
1492 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p DebugObject: %p\n",
1493 hDebug
, DebugObject
);
1499 NtDebugContinue(IN HANDLE DebugHandle
,
1500 IN PCLIENT_ID AppClientId
,
1501 IN NTSTATUS ContinueStatus
)
1503 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1504 PDEBUG_OBJECT DebugObject
;
1505 NTSTATUS Status
= STATUS_SUCCESS
;
1506 PDEBUG_EVENT DebugEvent
= NULL
, DebugEventToWake
= NULL
;
1507 PLIST_ENTRY ListHead
, NextEntry
;
1508 BOOLEAN NeedsWake
= FALSE
;
1511 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p Status: %p\n",
1512 DebugHandle
, ContinueStatus
);
1514 /* Check if we were called from user mode*/
1515 if (PreviousMode
!= KernelMode
)
1517 /* Enter SEH for probing */
1520 /* Probe the handle */
1521 ProbeForRead(AppClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
1522 ClientId
= *AppClientId
;
1523 AppClientId
= &ClientId
;
1527 /* Get exception error */
1528 Status
= _SEH_GetExceptionCode();
1530 if (!NT_SUCCESS(Status
)) return Status
;
1533 /* Make sure that the status is valid */
1534 if ((ContinueStatus
!= DBG_CONTINUE
) &&
1535 (ContinueStatus
!= DBG_EXCEPTION_HANDLED
) &&
1536 (ContinueStatus
!= DBG_EXCEPTION_NOT_HANDLED
) &&
1537 (ContinueStatus
!= DBG_TERMINATE_THREAD
) &&
1538 (ContinueStatus
!= DBG_TERMINATE_PROCESS
))
1540 /* Invalid status */
1541 Status
= STATUS_INVALID_PARAMETER
;
1545 /* Get the debug object */
1546 Status
= ObReferenceObjectByHandle(DebugHandle
,
1547 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1548 DbgkDebugObjectType
,
1550 (PVOID
*)&DebugObject
,
1552 if (NT_SUCCESS(Status
))
1554 /* Acquire the mutex */
1555 ExAcquireFastMutex(&DebugObject
->Mutex
);
1557 /* Loop the state list */
1558 ListHead
= &DebugObject
->EventList
;
1559 NextEntry
= ListHead
->Flink
;
1560 while (ListHead
!= NextEntry
)
1562 /* Get the current debug event */
1563 DebugEvent
= CONTAINING_RECORD(NextEntry
,
1567 /* Compare process ID */
1568 if (DebugEvent
->ClientId
.UniqueProcess
==
1569 AppClientId
->UniqueProcess
)
1571 /* Check if we already found a match */
1574 /* Wake it up and break out */
1575 DebugEvent
->Flags
&= ~4;
1576 KeSetEvent(&DebugEvent
->ContinueEvent
,
1582 /* Compare thread ID and flag */
1583 if ((DebugEvent
->ClientId
.UniqueThread
==
1584 AppClientId
->UniqueThread
) && (DebugEvent
->Flags
& 1))
1586 /* Remove the event from the list */
1587 RemoveEntryList(NextEntry
);
1589 /* Remember who to wake */
1591 DebugEventToWake
= DebugEvent
;
1595 /* Go to the next entry */
1596 NextEntry
= NextEntry
->Flink
;
1599 /* Release the mutex */
1600 ExReleaseFastMutex(&DebugObject
->Mutex
);
1602 /* Dereference the object */
1603 ObDereferenceObject(DebugObject
);
1605 /* Check if need a wait */
1608 /* Set the continue status */
1609 DebugEvent
->ApiMsg
.ReturnedStatus
= Status
;
1610 DebugEvent
->Status
= STATUS_SUCCESS
;
1612 /* Wake the target */
1613 DbgkpWakeTarget(DebugEvent
);
1618 Status
= STATUS_INVALID_PARAMETER
;
1629 NtDebugActiveProcess(IN HANDLE ProcessHandle
,
1630 IN HANDLE DebugHandle
)
1633 PDEBUG_OBJECT DebugObject
;
1634 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1635 PETHREAD LastThread
;
1638 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1639 ProcessHandle
, DebugHandle
);
1641 /* Reference the process */
1642 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1643 PROCESS_SUSPEND_RESUME
,
1648 if (!NT_SUCCESS(Status
)) return Status
;
1650 /* Don't allow debugging the initial system process */
1651 if (Process
== PsInitialSystemProcess
) return STATUS_ACCESS_DENIED
;
1653 /* Reference the debug object */
1654 Status
= ObReferenceObjectByHandle(DebugHandle
,
1655 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1656 DbgkDebugObjectType
,
1658 (PVOID
*)&DebugObject
,
1660 if (!NT_SUCCESS(Status
))
1662 /* Dereference the process and exit */
1663 ObDereferenceObject(Process
);
1667 /* Acquire process rundown protection */
1668 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1670 /* Dereference the process and debug object and exit */
1671 ObDereferenceObject(Process
);
1672 ObDereferenceObject(DebugObject
);
1673 return STATUS_PROCESS_IS_TERMINATING
;
1676 /* Send fake create messages for debuggers to have a consistent state */
1677 Status
= DbgkpPostFakeProcessCreateMessages(Process
,
1680 Status
= DbgkpSetProcessDebugObject(Process
,
1685 /* Release rundown protection */
1686 ExReleaseRundownProtection(&Process
->RundownProtect
);
1688 /* Dereference the process and debug object and return status */
1689 ObDereferenceObject(Process
);
1690 ObDereferenceObject(DebugObject
);
1696 NtRemoveProcessDebug(IN HANDLE ProcessHandle
,
1697 IN HANDLE DebugHandle
)
1700 PDEBUG_OBJECT DebugObject
;
1701 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1704 DBGKTRACE(DBGK_PROCESS_DEBUG
, "Process: %p Handle: %p\n",
1705 ProcessHandle
, DebugHandle
);
1707 /* Reference the process */
1708 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1709 PROCESS_SUSPEND_RESUME
,
1714 if (!NT_SUCCESS(Status
)) return Status
;
1716 /* Reference the debug object */
1717 Status
= ObReferenceObjectByHandle(DebugHandle
,
1718 DEBUG_OBJECT_ADD_REMOVE_PROCESS
,
1719 DbgkDebugObjectType
,
1721 (PVOID
*)&DebugObject
,
1723 if (!NT_SUCCESS(Status
))
1725 /* Dereference the process and exit */
1726 ObDereferenceObject(Process
);
1730 /* Remove the debug object */
1731 Status
= DbgkClearProcessDebugObject(Process
, DebugObject
);
1733 /* Dereference the process and debug object and return status */
1734 ObDereferenceObject(Process
);
1735 ObDereferenceObject(DebugObject
);
1741 NtSetInformationDebugObject(IN HANDLE DebugHandle
,
1742 IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass
,
1743 IN PVOID DebugInformation
,
1744 IN ULONG DebugInformationLength
,
1745 OUT PULONG ReturnLength OPTIONAL
)
1747 PDEBUG_OBJECT DebugObject
;
1748 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1749 NTSTATUS Status
= STATUS_SUCCESS
;
1750 PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo
= DebugInformation
;
1753 /* Check buffers and parameters */
1754 Status
= DefaultSetInfoBufferCheck(DebugObjectInformationClass
,
1755 DbgkpDebugObjectInfoClass
,
1756 sizeof(DbgkpDebugObjectInfoClass
) /
1757 sizeof(DbgkpDebugObjectInfoClass
[0]),
1759 DebugInformationLength
,
1762 /* Check if the caller wanted the return length */
1765 /* Enter SEH for probe */
1768 /* Return required length to user-mode */
1769 ProbeForWriteUlong(ReturnLength
);
1770 *ReturnLength
= sizeof(*DebugInfo
);
1772 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
1774 /* Get SEH Exception code */
1775 Status
= _SEH_GetExceptionCode();
1779 if (!NT_SUCCESS(Status
)) return Status
;
1781 /* Open the Object */
1782 Status
= ObReferenceObjectByHandle(DebugHandle
,
1783 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1784 DbgkDebugObjectType
,
1786 (PVOID
*)&DebugObject
,
1788 if (NT_SUCCESS(Status
))
1790 /* Acquire the object */
1791 ExAcquireFastMutex(&DebugObject
->Mutex
);
1793 /* Set the proper flag */
1794 if (DebugInfo
->KillProcessOnExit
)
1796 /* Enable killing the process */
1797 DebugObject
->KillProcessOnExit
= TRUE
;
1802 DebugObject
->KillProcessOnExit
= FALSE
;
1805 /* Release the mutex */
1806 ExReleaseFastMutex(&DebugObject
->Mutex
);
1808 /* Release the Object */
1809 ObDereferenceObject(DebugObject
);
1818 NtWaitForDebugEvent(IN HANDLE DebugHandle
,
1819 IN BOOLEAN Alertable
,
1820 IN PLARGE_INTEGER Timeout OPTIONAL
,
1821 OUT PDBGUI_WAIT_STATE_CHANGE StateChange
)
1823 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1824 LARGE_INTEGER SafeTimeOut
;
1826 LARGE_INTEGER StartTime
;
1829 LARGE_INTEGER NewTime
;
1830 PDEBUG_OBJECT DebugObject
;
1831 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
1832 NTSTATUS Status
= STATUS_SUCCESS
;
1833 PDEBUG_EVENT DebugEvent
= NULL
, DebugEvent2
;
1834 PLIST_ENTRY ListHead
, NextEntry
, NextEntry2
;
1836 DBGKTRACE(DBGK_OBJECT_DEBUG
, "Handle: %p\n", DebugHandle
);
1838 /* Clear the initial wait state change structure */
1839 RtlZeroMemory(&WaitStateChange
, sizeof(WaitStateChange
));
1841 /* Protect probe in SEH */
1844 /* Check if we came with a timeout */
1847 /* Check if the call was from user mode */
1848 if (PreviousMode
!= KernelMode
)
1851 ProbeForReadLargeInteger(Timeout
);
1854 /* Make a local copy */
1855 SafeTimeOut
= *Timeout
;
1856 Timeout
= &SafeTimeOut
;
1858 /* Query the current time */
1859 KeQuerySystemTime(&StartTime
);
1862 /* Check if the call was from user mode */
1863 if (PreviousMode
!= KernelMode
)
1865 /* Probe the state change structure */
1866 ProbeForWrite(StateChange
, sizeof(*StateChange
), sizeof(ULONG
));
1871 /* Get the exception code */
1872 Status
= _SEH_GetExceptionCode();
1875 if (!NT_SUCCESS(Status
)) return Status
;
1877 /* Get the debug object */
1878 Status
= ObReferenceObjectByHandle(DebugHandle
,
1879 DEBUG_OBJECT_WAIT_STATE_CHANGE
,
1880 DbgkDebugObjectType
,
1882 (PVOID
*)&DebugObject
,
1884 if (!NT_SUCCESS(Status
)) return Status
;
1886 /* Clear process and thread */
1890 /* Wait on the debug object given to us */
1893 Status
= KeWaitForSingleObject(DebugObject
,
1898 if (!NT_SUCCESS(Status
) ||
1899 (Status
== STATUS_TIMEOUT
) ||
1900 (Status
== STATUS_ALERTED
) ||
1901 (Status
== STATUS_USER_APC
))
1903 /* Break out the wait */
1907 /* Lock the object */
1909 ExAcquireFastMutex(&DebugObject
->Mutex
);
1911 /* Check if a debugger is connected */
1912 if (DebugObject
->DebuggerInactive
)
1915 Status
= STATUS_DEBUGGER_INACTIVE
;
1919 /* Loop the events */
1920 ListHead
= &DebugObject
->EventList
;
1921 NextEntry
= ListHead
->Flink
;
1922 while (ListHead
!= NextEntry
)
1924 /* Get the debug event */
1925 DebugEvent
= CONTAINING_RECORD(NextEntry
,
1928 DBGKTRACE(DBGK_PROCESS_DEBUG
, "DebugEvent: %p Flags: %lx\n",
1929 DebugEvent
, DebugEvent
->Flags
);
1932 if (!(DebugEvent
->Flags
& (4 | 1)))
1934 /* We got an event */
1937 /* Loop the list internally */
1938 NextEntry2
= DebugObject
->EventList
.Flink
;
1939 while (NextEntry2
!= NextEntry
)
1941 /* Get the debug event */
1942 DebugEvent2
= CONTAINING_RECORD(NextEntry2
,
1946 /* Try to match process IDs */
1947 if (DebugEvent2
->ClientId
.UniqueProcess
==
1948 DebugEvent
->ClientId
.UniqueProcess
)
1950 /* Found it, break out */
1951 DebugEvent
->Flags
|= 4;
1952 DebugEvent
->BackoutThread
= NULL
;
1957 /* Move to the next entry */
1958 NextEntry2
= NextEntry2
->Flink
;
1961 /* Check if we still have a valid event */
1962 if (GotEvent
) break;
1965 /* Move to the next entry */
1966 NextEntry
= NextEntry
->Flink
;
1969 /* Check if we have an event */
1972 /* Save and reference the process and thread */
1973 Process
= DebugEvent
->Process
;
1974 Thread
= DebugEvent
->Thread
;
1975 ObReferenceObject(Process
);
1976 ObReferenceObject(Thread
);
1978 /* Convert to user-mode structure */
1979 DbgkpConvertKernelToUserStateChange(&WaitStateChange
,
1983 DebugEvent
->Flags
|= 1;
1987 /* Unsignal the event */
1988 KeClearEvent(&DebugObject
->EventsPresent
);
1992 Status
= STATUS_SUCCESS
;
1995 /* Release the mutex */
1996 ExReleaseFastMutex(&DebugObject
->Mutex
);
1997 if (!NT_SUCCESS(Status
)) break;
1999 /* Check if we got an event */
2002 /* Check if we can wait again */
2003 if (SafeTimeOut
.QuadPart
< 0)
2005 /* Query the new time */
2006 KeQuerySystemTime(&NewTime
);
2008 /* Substract times */
2009 SafeTimeOut
.QuadPart
+= (NewTime
.QuadPart
- StartTime
.QuadPart
);
2010 StartTime
= NewTime
;
2012 /* Check if we've timed out */
2013 if (SafeTimeOut
.QuadPart
> 0)
2015 /* We have, break out of the loop */
2016 Status
= STATUS_TIMEOUT
;
2023 /* Open the handles and dereference the objects */
2024 DbgkpOpenHandles(&WaitStateChange
, Process
, Thread
);
2025 ObDereferenceObject(Process
);
2026 ObDereferenceObject(Thread
);
2031 /* We're done, dereference the object */
2032 ObDereferenceObject(DebugObject
);
2034 /* Protect write with SEH */
2037 /* Return our wait state change structure */
2038 RtlCopyMemory(StateChange
,
2040 sizeof(DBGUI_WAIT_STATE_CHANGE
));
2042 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
2044 /* Get SEH Exception code */
2045 Status
= _SEH_GetExceptionCode();