// // Debug Object // typedef struct _DEBUG_OBJECT { KEVENT EventsPresent; FAST_MUTEX Mutex; LIST_ENTRY EventList; union { ULONG Flags; struct { UCHAR DebuggerInactive:1; UCHAR KillProcessOnExit:1; }; }; } DEBUG_OBJECT, *PDEBUG_OBJECT;
// // Debug Event // typedef struct _DEBUG_EVENT { LIST_ENTRY EventList; KEVENT ContinueEvent; CLIENT_ID ClientId; PEPROCESS Process; PETHREAD Thread; NTSTATUS Status; ULONG Flags; PETHREAD BackoutThread; DBGKM_MSG ApiMsg; } DEBUG_EVENT, *PDEBUG_EVENT;
// // Debug Message API Number // typedef enum _DBGKM_APINUMBER { DbgKmExceptionApi = 0, DbgKmCreateThreadApi = 1, DbgKmCreateProcessApi = 2, DbgKmExitThreadApi = 3, DbgKmExitProcessApi = 4, DbgKmLoadDllApi = 5, DbgKmUnloadDllApi = 6, DbgKmErrorReportApi = 7, DbgKmMaxApiNumber = 8, } DBGKM_APINUMBER;
// // LPC Debug Message // typedef struct _DBGKM_MSG { PORT_MESSAGE h; DBGKM_APINUMBER ApiNumber; ULONG ReturnedStatus; union { DBGKM_EXCEPTION Exception; DBGKM_CREATE_THREAD CreateThread; DBGKM_CREATE_PROCESS CreateProcess; DBGKM_EXIT_THREAD ExitThread; DBGKM_EXIT_PROCESS ExitProcess; DBGKM_LOAD_DLL LoadDll; DBGKM_UNLOAD_DLL UnloadDll; }; } DBGKM_MSG, *PDBGKM_MSG;
NTSTATUS NTAPI NtCreateDebugObject(OUT PHANDLE DebugHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN KillProcessOnExit) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); PDEBUG_OBJECT DebugObject; HANDLE hDebug; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); /* Check if we were called from user mode*/ if (PreviousMode != KernelMode) { /* Enter SEH for probing */ _SEH_TRY { /* Probe the handle */ ProbeForWriteHandle(DebugHandle); } _SEH_HANDLE { /* Get exception error */ Status = _SEH_GetExceptionCode(); } _SEH_END; if (!NT_SUCCESS(Status)) return Status; } /* Create the Object */ Status = ObCreateObject(PreviousMode, DbgkDebugObjectType, ObjectAttributes, PreviousMode, NULL, sizeof(DEBUG_OBJECT), 0, 0, (PVOID*)&DebugObject); if (NT_SUCCESS(Status)) { /* Initialize the Debug Object's Fast Mutex */ ExInitializeFastMutex(&DebugObject->Mutex); /* Initialize the State Event List */ InitializeListHead(&DebugObject->EventList); /* Initialize the Debug Object's Wait Event */ KeInitializeEvent(&DebugObject->EventsPresent, NotificationEvent, FALSE); /* Set the Flags */ DebugObject->KillProcessOnExit = KillProcessOnExit; /* Insert it */ Status = ObInsertObject((PVOID)DebugObject, NULL, DesiredAccess, 0, NULL, &hDebug); if (NT_SUCCESS(Status)) { _SEH_TRY { *DebugHandle = hDebug; } _SEH_HANDLE { Status = _SEH_GetExceptionCode(); } _SEH_END; } } /* Return Status */ DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p DebugObject: %p\n", hDebug, DebugObject); return Status;
NTSTATUS NTAPI NtDebugActiveProcess(IN HANDLE ProcessHandle, IN HANDLE DebugHandle) { PEPROCESS Process; PDEBUG_OBJECT DebugObject; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD LastThread; NTSTATUS Status; PAGED_CODE(); DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n", ProcessHandle, DebugHandle); /* Reference the process */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_SUSPEND_RESUME, PsProcessType, PreviousMode, (PVOID*)&Process, NULL); if (!NT_SUCCESS(Status)) return Status; /* Don't allow debugging the initial system process */ if (Process == PsInitialSystemProcess) return STATUS_ACCESS_DENIED; /* Reference the debug object */ Status = ObReferenceObjectByHandle(DebugHandle, DEBUG_OBJECT_ADD_REMOVE_PROCESS, DbgkDebugObjectType, PreviousMode, (PVOID*)&DebugObject, NULL); if (!NT_SUCCESS(Status)) { /* Dereference the process and exit */ ObDereferenceObject(Process); return Status; } /* Acquire process rundown protection */ if (!ExAcquireRundownProtection(&Process->RundownProtect)) { /* Dereference the process and debug object and exit */ ObDereferenceObject(Process); ObDereferenceObject(DebugObject); return STATUS_PROCESS_IS_TERMINATING; } /* Send fake create messages for debuggers to have a consistent state */ Status = DbgkpPostFakeProcessCreateMessages(Process, DebugObject, &LastThread); Status = DbgkpSetProcessDebugObject(Process, DebugObject, Status, LastThread); /* Release rundown protection */ ExReleaseRundownProtection(&Process->RundownProtect); /* Dereference the process and debug object and return status */ ObDereferenceObject(Process); ObDereferenceObject(DebugObject); return Status; }
NTSTATUS NTAPI NtRemoveProcessDebug(IN HANDLE ProcessHandle, IN HANDLE DebugHandle) { PEPROCESS Process; PDEBUG_OBJECT DebugObject; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PAGED_CODE(); DBGKTRACE(DBGK_PROCESS_DEBUG, "Process: %p Handle: %p\n", ProcessHandle, DebugHandle); /* Reference the process */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_SUSPEND_RESUME, PsProcessType, PreviousMode, (PVOID*)&Process, NULL); if (!NT_SUCCESS(Status)) return Status; /* Reference the debug object */ Status = ObReferenceObjectByHandle(DebugHandle, DEBUG_OBJECT_ADD_REMOVE_PROCESS, DbgkDebugObjectType, PreviousMode, (PVOID*)&DebugObject, NULL); if (!NT_SUCCESS(Status)) { /* Dereference the process and exit */ ObDereferenceObject(Process); return Status; } /* Remove the debug object */ Status = DbgkClearProcessDebugObject(Process, DebugObject); /* Dereference the process and debug object and return status */ ObDereferenceObject(Process); ObDereferenceObject(DebugObject); return Status; }
NTSTATUS NTAPI NtSetInformationDebugObject(IN HANDLE DebugHandle, IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass, IN PVOID DebugInformation, IN ULONG DebugInformationLength, OUT PULONG ReturnLength OPTIONAL) { PDEBUG_OBJECT DebugObject; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo = DebugInformation; PAGED_CODE(); /* Check buffers and parameters */ Status = DefaultSetInfoBufferCheck(DebugObjectInformationClass, DbgkpDebugObjectInfoClass, sizeof(DbgkpDebugObjectInfoClass) / sizeof(DbgkpDebugObjectInfoClass[0]), DebugInformation, DebugInformationLength, PreviousMode); /* Check if the caller wanted the return length */ if (ReturnLength) { /* Enter SEH for probe */ _SEH_TRY { /* Return required length to user-mode */ ProbeForWriteUlong(ReturnLength); *ReturnLength = sizeof(*DebugInfo); } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { /* Get SEH Exception code */ Status = _SEH_GetExceptionCode(); } _SEH_END; } if (!NT_SUCCESS(Status)) return Status; /* Open the Object */ Status = ObReferenceObjectByHandle(DebugHandle, DEBUG_OBJECT_WAIT_STATE_CHANGE, DbgkDebugObjectType, PreviousMode, (PVOID*)&DebugObject, NULL); if (NT_SUCCESS(Status)) { /* Acquire the object */ ExAcquireFastMutex(&DebugObject->Mutex); /* Set the proper flag */ if (DebugInfo->KillProcessOnExit) { /* Enable killing the process */ DebugObject->KillProcessOnExit = TRUE; } else { /* Disable */ DebugObject->KillProcessOnExit = FALSE; } /* Release the mutex */ ExReleaseFastMutex(&DebugObject->Mutex); /* Release the Object */ ObDereferenceObject(DebugObject); } /* Return Status */ return Status; }
NTSTATUS NTAPI NtWaitForDebugEvent(IN HANDLE DebugHandle, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL, OUT PDBGUI_WAIT_STATE_CHANGE StateChange) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); LARGE_INTEGER SafeTimeOut; PEPROCESS Process; LARGE_INTEGER StartTime; PETHREAD Thread; BOOLEAN GotEvent; LARGE_INTEGER NewTime; PDEBUG_OBJECT DebugObject; DBGUI_WAIT_STATE_CHANGE WaitStateChange; NTSTATUS Status = STATUS_SUCCESS; PDEBUG_EVENT DebugEvent, DebugEvent2; PLIST_ENTRY ListHead, NextEntry, NextEntry2; PAGED_CODE(); DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p\n", DebugHandle); /* Clear the initial wait state change structure */ RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange)); /* Protect probe in SEH */ _SEH_TRY { /* Check if we came with a timeout */ if (Timeout) { /* Check if the call was from user mode */ if (PreviousMode != KernelMode) { /* Probe it */ ProbeForReadLargeInteger(Timeout); } /* Make a local copy */ SafeTimeOut = *Timeout; Timeout = &SafeTimeOut; /* Query the current time */ KeQuerySystemTime(&StartTime); } /* Check if the call was from user mode */ if (PreviousMode != KernelMode) { /* Probe the state change structure */ ProbeForWrite(StateChange, sizeof(*StateChange), sizeof(ULONG)); } } _SEH_HANDLE { /* Get the exception code */ Status = _SEH_GetExceptionCode(); } _SEH_END; if (!NT_SUCCESS(Status)) return Status; /* Get the debug object */ Status = ObReferenceObjectByHandle(DebugHandle, DEBUG_OBJECT_WAIT_STATE_CHANGE, DbgkDebugObjectType, PreviousMode, (PVOID*)&DebugObject, NULL); if (!NT_SUCCESS(Status)) return Status; /* Clear process and thread */ Process = NULL; Thread = NULL; /* Start wait loop */ while (TRUE) { /* Wait on the debug object given to us */ Status = KeWaitForSingleObject(DebugObject, Executive, PreviousMode, Alertable, Timeout); if (!NT_SUCCESS(Status) || (Status == STATUS_TIMEOUT) || (Status == STATUS_ALERTED) || (Status == STATUS_USER_APC)) { /* Break out the wait */ break; } /* Lock the object */ GotEvent = FALSE; ExAcquireFastMutex(&DebugObject->Mutex); /* Check if a debugger is connected */ if (DebugObject->DebuggerInactive) { /* Not connected */ Status = STATUS_DEBUGGER_INACTIVE; } else { /* Loop the events */ ListHead = &DebugObject->EventList; NextEntry = ListHead->Flink; while (ListHead != NextEntry) { /* Get the debug event */ DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList); DBGKTRACE(DBGK_PROCESS_DEBUG, "DebugEvent: %p Flags: %lx\n", DebugEvent, DebugEvent->Flags); /* Check flags */ if (!(DebugEvent->Flags & (DEBUG_EVENT_FLAGS_USED | DEBUG_EVENT_FLAGS_INACTIVE))) { /* We got an event */ GotEvent = TRUE; /* Loop the list internally */ NextEntry2 = DebugObject->EventList.Flink; while (NextEntry2 != NextEntry) { /* Get the debug event */ DebugEvent2 = CONTAINING_RECORD(NextEntry2, DEBUG_EVENT, EventList); /* Try to match process IDs */ if (DebugEvent2->ClientId.UniqueProcess == DebugEvent->ClientId.UniqueProcess) { /* Found it, break out */ DebugEvent->Flags |= DEBUG_EVENT_FLAGS_USED; DebugEvent->BackoutThread = NULL; GotEvent = FALSE; break; } /* Move to the next entry */ NextEntry2 = NextEntry2->Flink; } /* Check if we still have a valid event */ if (GotEvent) break; } /* Move to the next entry */ NextEntry = NextEntry->Flink; } /* Check if we have an event */ if (GotEvent) { /* Save and reference the process and thread */ Process = DebugEvent->Process; Thread = DebugEvent->Thread; ObReferenceObject(Process); ObReferenceObject(Thread); /* Convert to user-mode structure */ DbgkpConvertKernelToUserStateChange(&WaitStateChange, DebugEvent); /* Set flag */ DebugEvent->Flags |= DEBUG_EVENT_FLAGS_INACTIVE; } else { /* Unsignal the event */ KeClearEvent(&DebugObject->EventsPresent); } /* Set success */ Status = STATUS_SUCCESS; } /* Release the mutex */ ExReleaseFastMutex(&DebugObject->Mutex); if (!NT_SUCCESS(Status)) break; /* Check if we got an event */ if (!GotEvent) { /* Check if we can wait again */ if (SafeTimeOut.QuadPart < 0) { /* Query the new time */ KeQuerySystemTime(&NewTime); /* Substract times */ SafeTimeOut.QuadPart += (NewTime.QuadPart - StartTime.QuadPart); StartTime = NewTime; /* Check if we've timed out */ if (SafeTimeOut.QuadPart > 0) { /* We have, break out of the loop */ Status = STATUS_TIMEOUT; break; } } } else { /* Open the handles and dereference the objects */ DbgkpOpenHandles(&WaitStateChange, Process, Thread); ObDereferenceObject(Process); ObDereferenceObject(Thread); break; } } /* We're done, dereference the object */ ObDereferenceObject(DebugObject); /* Protect write with SEH */ _SEH_TRY { /* Return our wait state change structure */ RtlCopyMemory(StateChange, &WaitStateChange, sizeof(DBGUI_WAIT_STATE_CHANGE)); } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) { /* Get SEH Exception code */ Status = _SEH_GetExceptionCode(); } _SEH_END; /* Return status */ return Status; }
NTSTATUS NTAPI NtDebugContinue(IN HANDLE DebugHandle, IN PCLIENT_ID AppClientId, IN NTSTATUS ContinueStatus) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); PDEBUG_OBJECT DebugObject; NTSTATUS Status = STATUS_SUCCESS; PDEBUG_EVENT DebugEvent = NULL, DebugEventToWake = NULL; PLIST_ENTRY ListHead, NextEntry; BOOLEAN NeedsWake = FALSE; CLIENT_ID ClientId; PAGED_CODE(); DBGKTRACE(DBGK_OBJECT_DEBUG, "Handle: %p Status: %p\n", DebugHandle, ContinueStatus); /* Check if we were called from user mode*/ if (PreviousMode != KernelMode) { /* Enter SEH for probing */ _SEH_TRY { /* Probe the handle */ ProbeForRead(AppClientId, sizeof(CLIENT_ID), sizeof(ULONG)); ClientId = *AppClientId; AppClientId = &ClientId; } _SEH_HANDLE { /* Get exception error */ Status = _SEH_GetExceptionCode(); } _SEH_END; if (!NT_SUCCESS(Status)) return Status; } /* Make sure that the status is valid */ if ((ContinueStatus != DBG_CONTINUE) && (ContinueStatus != DBG_EXCEPTION_HANDLED) && (ContinueStatus != DBG_EXCEPTION_NOT_HANDLED) && (ContinueStatus != DBG_TERMINATE_THREAD) && (ContinueStatus != DBG_TERMINATE_PROCESS)) { /* Invalid status */ Status = STATUS_INVALID_PARAMETER; } else { /* Get the debug object */ Status = ObReferenceObjectByHandle(DebugHandle, DEBUG_OBJECT_WAIT_STATE_CHANGE, DbgkDebugObjectType, PreviousMode, (PVOID*)&DebugObject, NULL); if (NT_SUCCESS(Status)) { /* Acquire the mutex */ ExAcquireFastMutex(&DebugObject->Mutex); /* Loop the state list */ ListHead = &DebugObject->EventList; NextEntry = ListHead->Flink; while (ListHead != NextEntry) { /* Get the current debug event */ DebugEvent = CONTAINING_RECORD(NextEntry, DEBUG_EVENT, EventList); /* Compare process ID */ if (DebugEvent->ClientId.UniqueProcess == AppClientId->UniqueProcess) { /* Check if we already found a match */ if (NeedsWake) { /* Wake it up and break out */ DebugEvent->Flags &= ~DEBUG_EVENT_FLAGS_USED; KeSetEvent(&DebugEvent->ContinueEvent, IO_NO_INCREMENT, FALSE); break; } /* Compare thread ID and flag */ if ((DebugEvent->ClientId.UniqueThread == AppClientId->UniqueThread) && (DebugEvent->Flags & DEBUG_EVENT_FLAGS_INACTIVE)) { /* Remove the event from the list */ RemoveEntryList(NextEntry); /* Remember who to wake */ NeedsWake = TRUE; DebugEventToWake = DebugEvent; } } /* Go to the next entry */ NextEntry = NextEntry->Flink; } /* Release the mutex */ ExReleaseFastMutex(&DebugObject->Mutex); /* Dereference the object */ ObDereferenceObject(DebugObject); /* Check if need a wait */ if (NeedsWake) { /* Set the continue status */ DebugEvent->ApiMsg.ReturnedStatus = Status; DebugEvent->Status = STATUS_SUCCESS; /* Wake the target */ DbgkpWakeTarget(DebugEvent); } else { /* Fail */ Status = STATUS_INVALID_PARAMETER; } } } /* Return status */ return Status; }
There are 31,320 total registered users.
[+] expand