Files
BeneficialCode-WinArk/KernelLibrary/kDbgSys.cpp
T

2650 lines
64 KiB
C++

#include "pch.h"
#include "kDbgCore.h"
#include "kDbgSys.h"
#include <ntimage.h>
#include "kDbgUtil.h"
#include "NtWow64.h"
// 调试对象类型
POBJECT_TYPE* DbgkDebugObjectType;
// 保护对EPROCESS的DebugPort的访问
PFAST_MUTEX g_pDbgkpProcessDebugPortMutex;
PULONG g_pDbgkpMaxModuleMsgs;
extern PULONG g_pPspNotifyEnableMask;
NTSTATUS NTAPI
NewNtCreateDebugObject(
_Out_ PHANDLE DebugObjectHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ ULONG Flags
)
/*++
Routine Description:
Creates a new debug object that maintains the context for a single debug session. Multiple processes may be
associated with a single debug object.
Arguments:
DebugObjectHandle - Pointer to a handle to recive the output objects handle
DesiredAccess - Required handle access
ObjectAttributes - Standard object attributes structure
Flags - Only one flag DEBUG_KILL_ON_CLOSE
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS status;
HANDLE handle;
PDEBUG_OBJECT DebugObject;
KPROCESSOR_MODE PreviousMode;
PreviousMode = ExGetPreviousMode();
// 判断用户层句柄地址是否合法
__try {
if (PreviousMode != KernelMode) {
ProbeForWriteHandle(DebugObjectHandle);
}
*DebugObjectHandle = nullptr;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
if (Flags & ~DEBUG_KILL_ON_CLOSE) {
return STATUS_INVALID_PARAMETER;
}
// 创建调试对象
status = ObCreateObject(
PreviousMode,
*DbgkDebugObjectType,
ObjectAttributes,
Flags,
nullptr,
sizeof(DEBUG_OBJECT),
0,
0,
(PVOID*)&DebugObject);
if (!NT_SUCCESS(status)) {
return status;
}
// 初始化调试对象
ExInitializeFastMutex(&DebugObject->Mutex);
InitializeListHead(&DebugObject->EventList);
KeInitializeEvent(&DebugObject->EventsPresent, NotificationEvent, FALSE);
if (Flags & DEBUG_KILL_ON_CLOSE) {
DebugObject->Flags = DEBUG_OBJECT_KILL_ON_CLOSE;
}
else {
DebugObject->Flags = 0;
}
// _EWOW64PROCESS
// 调试对象插入句柄表
status = ObInsertObject(
DebugObject,
nullptr,
DesiredAccess,
0,
nullptr,
&handle
);
if (!NT_SUCCESS(status)) {
return status;
}
__try {
*DebugObjectHandle = handle;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
return status;
}
NTSTATUS NewNtDebugActiveProcess(
_In_ HANDLE ProcessHandle,
_In_ HANDLE DebugObjectHandle
)
/*++
Routine Description:
Attach a debug object to a process.
Arguments:
ProcessHandle - Handle to a process to be debugged
DebugObjectHandle - Handle to a debug object
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS status;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
PEPROCESS Process, CurrentProcess;
PETHREAD LastThread = nullptr;
PreviousMode = ExGetPreviousMode();
// 得到被调试进程的eprocess
status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_SUSPEND_RESUME,
*PsProcessType,
PreviousMode,
(PVOID*)&Process,
nullptr);
if (!NT_SUCCESS(status)) {
return status;
}
CurrentProcess = PsGetCurrentProcess();
//
// Don't let us debug ourselves or the system process.
//
if (Process == CurrentProcess || Process == PsInitialSystemProcess) {
ObDereferenceObject(Process);
return STATUS_ACCESS_DENIED;
}
// 得到调试句柄关联的调试对象DebugObject
status = ObReferenceObjectByHandle(DebugObjectHandle,
DEBUG_OBJECT_ADD_REMOVE_PROCESS,
*DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
nullptr);
if (NT_SUCCESS(status)) {
//
// We will be touching process address space. Block process rundown.
//
if (ExAcquireRundownProtection(kDbgUtil::GetProcessRundownProtect(Process))) {
//
// Post the fake process create messages etc.
//
status = DbgkpPostFakeProcessCreateMessages(Process, DebugObject, &LastThread);
//
// Set the debug port. If this fails it will remove any faked messages.
//
status = DbgkpSetProcessDebugObject(Process, DebugObject, status, LastThread);
ExReleaseRundownProtection(kDbgUtil::GetProcessRundownProtect(Process));
}
else {
status = STATUS_PROCESS_IS_TERMINATING;
}
ObDereferenceObject(DebugObject);
}
ObDereferenceObject(Process);
return status;
}
NTSTATUS DbgkpPostFakeProcessCreateMessages(
_In_ PEPROCESS Process,
_In_ PDEBUG_OBJECT DebugObject,
_In_ PETHREAD* pLastThread
)
/*++
Routine Description:
This routine posts the faked initial process create, thread create and mudule load messages
Arguments:
ProcessHandle - Handle to a process to be debugged
DebugObjectHandle - Handle to a debug object
Return Value:
None.
--*/
{
NTSTATUS status;
KAPC_STATE ApcState;
PETHREAD StartThread, Thread;
PETHREAD LastThread;
// 收集所有线程创建的消息
status = kDbgUtil::g_pDbgkpPostFakeThreadMessages(
Process,
DebugObject,
nullptr,
&Thread,
&LastThread
);
if (NT_SUCCESS(status)) {
//
// Attach to the process so we can touch its address space
//
KeStackAttachProcess(Process, &ApcState);
// 收集模块创建的消息
DbgkpPostModuleMessages(Process, Thread, DebugObject);
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Thread);
}
else {
LastThread = nullptr;
}
*pLastThread = LastThread;
return status;
}
VOID DbgkpFreeDebugEvent(
_In_ PDEBUG_EVENT DebugEvent
) {
NTSTATUS status;
switch (DebugEvent->ApiMsg.ApiNumber) {
case DbgKmCreateProcessApi:
if (DebugEvent->ApiMsg.u.CreateProcess.FileHandle != nullptr) {
status = ObCloseHandle(DebugEvent->ApiMsg.u.CreateProcess.FileHandle, KernelMode);
}
break;
case DbgKmCreateThreadApi:
if (DebugEvent->ApiMsg.u.LoadDll.FileHandle != nullptr) {
status = ObCloseHandle(DebugEvent->ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
break;
}
ObDereferenceObject(DebugEvent->Process);
ObDereferenceObject(DebugEvent->Thread);
ExFreePool(DebugEvent);
}
VOID DbgkpMarkProcessPeb(
_In_ PEPROCESS Process
)
/*++
Routine Description:
This routine writes the debug variable in the PEB
Arguments:
Process - Process that needs its PEB modified
Return Value:
None.
--*/
{
KAPC_STATE ApcState;
//
// Acquire process rundown protection as we are about to look at the processes address space
//
if (ExAcquireRundownProtection(kDbgUtil::GetProcessRundownProtect(Process))) {
PPEB Peb = kDbgUtil::GetProcessPeb(Process);
if (Peb != nullptr) {
KeStackAttachProcess(Process, &ApcState);
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
__try {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
PBOOLEAN pBeingDebugged = kDbgUtil::GetPEBBeingDebugged(Peb);
*pBeingDebugged = (*pDebugObject != nullptr) ? TRUE : FALSE;
#ifdef _WIN64
PWOW64_PROCESS Wow64Process = (PWOW64_PROCESS)kDbgUtil::GetProcessWow64Process(Process);
if (Wow64Process != nullptr) {
PPEB32 Peb32 = (PPEB32)Wow64Process->Wow64;
if (Peb32 != nullptr) {
Peb32->BeingDebugged = *pBeingDebugged;
}
}
#endif //
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
KeUnstackDetachProcess(&ApcState);
}
ExReleaseRundownProtection(kDbgUtil::GetProcessRundownProtect(Process));
}
}
NTSTATUS
DbgkpSetProcessDebugObject(
_In_ PEPROCESS Process,
_In_ PDEBUG_OBJECT DebugObject,
_In_ NTSTATUS MsgStatus,
_In_ PETHREAD LastThread
)
/*++
Routine Description:
Attach a debug object to a process.
Arguments:
Process - Process to be debugged
DebugObject - Debug object to attach
MsgStatus - Status from queing the messages
LastThread - Last thread seen in attach loop.
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS status;
PETHREAD ThisThread;
LIST_ENTRY TempList;
PLIST_ENTRY Entry;
PDEBUG_EVENT DebugEvent;
BOOLEAN First;
PETHREAD Thread;
BOOLEAN GlobalHeld;
PETHREAD FirstThread;
PAGED_CODE();
ThisThread = PsGetCurrentThread();
InitializeListHead(&TempList);
First = TRUE;
GlobalHeld = FALSE;
if (!NT_SUCCESS(MsgStatus)) {
LastThread = nullptr;
status = MsgStatus;
}
else {
status = STATUS_SUCCESS;
}
PDEBUG_OBJECT* pProcessDebugPort = kDbgUtil::GetProcessDebugPort(Process);
//
// Pick up any threads we missed
//
if (NT_SUCCESS(status)) {
while (true) {
//
// Acquire the debug port mutex so we know that any new threads will
// have to wait to behind us.
//
GlobalHeld = TRUE;
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
//
// If the port has been set then exit now.
//
// 如果被调试进程的Debug Port已经设置,那么跳出循环
if (*pProcessDebugPort != nullptr) {
status = STATUS_PORT_ALREADY_SET;
break;
}
//
// Assign the debug port to the process to pick up any new threads
//
//没有设置debugport,在这里设置
*pProcessDebugPort = DebugObject;
//
// Reference the last thread so we can deref outside the lock
//
ObReferenceObject(LastThread);
//
// Search forward for new threads
//
Thread = kDbgUtil::g_pPsGetNextProcessThread(Process, LastThread);
if (Thread != nullptr) {
//
// Remove the debug port from the process as we are
// about to drop the lock
//
pProcessDebugPort = nullptr;
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
GlobalHeld = FALSE;
ObDereferenceObject(LastThread);
// 通知线程创建消息
//
// Queue any new thread messages and repeat.
//
status = DbgkpPostFakeThreadMessages(
Process,
DebugObject,
Thread,
&FirstThread,
&LastThread
);
if (!NT_SUCCESS(status)) {
LastThread = nullptr;
break;
}
ObDereferenceObject(FirstThread);
}
else {
break;
}
}
}
//
// Lock the debug object so we can check its deleted status
//
ExAcquireFastMutex(&DebugObject->Mutex);
//
// We must not propagate a debug port thats got no handles left.
//
if (NT_SUCCESS(status)) {
PULONG pFlags = kDbgUtil::GetProcessFlags(Process);
if ((DebugObject->Flags & DEBUG_OBJECT_DELETE_PENDING) == 0) {
PS_SET_BITS(pFlags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT | PS_PROCESS_FLAGS_CREATE_REPORTED);
ObReferenceObject(DebugObject);
}
else {
pProcessDebugPort = nullptr;
status = STATUS_DEBUGGER_INACTIVE;
}
}
// 遍历所有调试事件
for (Entry = DebugObject->EventList.Flink; Entry != &DebugObject->EventList;) {
// 取出调试事件
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
Entry = Entry->Flink;
if ((DebugEvent->Flags & DEBUG_EVENT_INACTIVE) != 0 && DebugEvent->BackoutThread == ThisThread) {
Thread = DebugEvent->Thread;
//
// If the thread has not been inserted by CreateThread yet then don't
// create a handle. We skip system threads here also
//
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(Thread);
bool isSystemThread = (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_SYSTEM) != 0;
if (NT_SUCCESS(status) &&
!isSystemThread) {
//
// If we could not acquire rundown protection on this
// thread then we need to suppress its exit message.
//
if ((DebugEvent->Flags & DEBUG_EVENT_PROTECT_FAILED) != 0) {
PS_SET_BITS(pCrossThreadFlags, PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG);
RemoveEntryList(&DebugEvent->EventList);
InsertTailList(&TempList, &DebugEvent->EventList);
}
else {
if (First) {
DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
KeSetEvent(&DebugObject->EventsPresent, 0, FALSE);
First = FALSE;
}
DebugEvent->BackoutThread = nullptr;
PS_SET_BITS(pCrossThreadFlags, PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG);
}
}
else {
RemoveEntryList(&DebugEvent->EventList);
InsertTailList(&TempList, &DebugEvent->EventList);
}
if (DebugEvent->Flags & DEBUG_EVENT_RELEASE) {
DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
PEX_RUNDOWN_REF RundownProtect = kDbgUtil::GetThreadRundownProtect(Thread);
ExReleaseRundownProtection(RundownProtect);
}
}
}
ExReleaseFastMutex(&DebugObject->Mutex);
if (GlobalHeld) {
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
}
if (LastThread != nullptr) {
ObDereferenceObject(LastThread);
}
while (!IsListEmpty(&TempList)) {
Entry = RemoveHeadList(&TempList);
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
kDbgUtil::g_pDbgkpWakeTarget(DebugEvent);
}
if (NT_SUCCESS(status)) {
kDbgUtil::g_pDbgkpMarkProcessPeb(Process);
}
return status;
}
VOID DbgkSendSystemDllMessages(
PETHREAD Thread,
PDEBUG_OBJECT DebugObject,
PDBGKM_APIMSG ApiMsg
)
{
NTSTATUS status;
PDBGKM_LOAD_DLL LoadDll;
PIMAGE_NT_HEADERS NtHeaders;
PTEB Teb;
PPS_SYSTEM_DLL_INFO SystemDllInfo;
IO_STATUS_BLOCK IoStatusBlock;
PEPROCESS Process;
BOOLEAN Attached;
KAPC_STATE ApcState;
if (Thread) {
Process = PsGetThreadProcess(Thread);
}
else {
Thread = KeGetCurrentThread();
Process = kDbgUtil::GetThreadApcState(Thread)->Process;
}
LoadDll = &ApiMsg->u.LoadDll;
for (int i = 0; i < 2; i++) {
SystemDllInfo = kDbgUtil::g_pPsQuerySystemDllInfo(i);
if (SystemDllInfo && (i != 1
#ifdef _WIN64
|| kDbgUtil::GetProcessWow64Process(Process)
#endif // _WIN64
)) {
memset(LoadDll, 0, sizeof(DBGKM_LOAD_DLL));
LoadDll->BaseOfDll = SystemDllInfo->ImageBase;
if (Thread && i) {
Attached = TRUE;
KeStackAttachProcess(Process, &ApcState);
}
else {
Attached = FALSE;
}
NtHeaders = RtlImageNtHeader(SystemDllInfo->ImageBase);
if (NtHeaders) {
LoadDll->DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
LoadDll->DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
if (Thread) {
Teb = nullptr;
}
else {
Teb = (PTEB)PsGetCurrentThreadTeb();
if (Teb) {
RtlStringCbCopyW(Teb->StaticUnicodeBuffer,
sizeof(Teb->StaticUnicodeBuffer),
SystemDllInfo->DllName);
PWCHAR* pArbitraryUserPointer = (PWCHAR*)Teb->NtTib.ArbitraryUserPointer;
*pArbitraryUserPointer = Teb->StaticUnicodeBuffer;
LoadDll->NamePointer = pArbitraryUserPointer;
}
}
if (Attached) {
KeUnstackDetachProcess(&ApcState);
}
OBJECT_ATTRIBUTES ObjectAttr;
InitializeObjectAttributes(&ObjectAttr, &SystemDllInfo->Ntdll32Path,
OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK | OBJ_KERNEL_HANDLE,
nullptr, nullptr);
status = ZwOpenFile(&LoadDll->FileHandle, GENERIC_READ | SYNCHRONIZE,
&ObjectAttr,
&IoStatusBlock,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(status)) {
LoadDll->FileHandle = nullptr;
}
DBGKM_FORMAT_API_MSG(*ApiMsg, DbgKmLoadDllApi, sizeof(DBGKM_LOAD_DLL));
if (Thread) {
status = DbgkpQueueMessage(Process, Thread, ApiMsg, 2, DebugObject);
if (!NT_SUCCESS(status)) {
ObCloseHandle(LoadDll->FileHandle, KernelMode);
}
}
else {
DbgkpSendApiMessage(3, ApiMsg);
if (LoadDll->FileHandle) {
ObCloseHandle(LoadDll->FileHandle, KernelMode);
}
if (Teb) {
Teb->NtTib.ArbitraryUserPointer = nullptr;
}
}
}
}
}
NTSTATUS DbgkpPostFakeThreadMessages(
PEPROCESS Process,
PDEBUG_OBJECT DebugObject,
PETHREAD StartThread,
PETHREAD* pFirstThread,
PETHREAD* pLastThread
)
/*++
Routine Description:
This routine posts the faked initial process create, thread create messages
Arguments:
Process - Process to be debugged
DebugObject - Debug object to queue messages to
StartThread - Thread to start search from
pFirstThread - First thread found in the list
pLastThread - Last thread found in the list
Return Value:
None.
--*/
{
NTSTATUS status;
PETHREAD Thread, FirstThread, LastThread, CurrentThread;
DBGKM_APIMSG ApiMsg;
BOOLEAN First = TRUE;
BOOLEAN IsFirstThread;
PIMAGE_NT_HEADERS NtHeaders;
ULONG Flags;
KAPC_STATE ApcState;
status = STATUS_UNSUCCESSFUL;
LastThread = FirstThread = nullptr;
CurrentThread = PsGetCurrentThread();
if (StartThread != nullptr) {
First = FALSE;
FirstThread = StartThread;
ObfReferenceObject(StartThread);
}
else {
StartThread = kDbgUtil::g_pPsGetNextProcessThread(Process, nullptr);
First = TRUE;
}
// 遍历调试进程的所有线程
for (Thread = StartThread;
Thread != nullptr;
Thread = kDbgUtil::g_pPsGetNextProcessThread(Process, Thread)) {
Flags = DEBUG_EVENT_NOWAIT;
//
// Keep a track on the last thread we have seen.
// We use this as a starting point for new threads after we
// really attach so we can pick up any new threads.
//
if (LastThread != nullptr) {
ObDereferenceObject(LastThread);
}
LastThread = Thread;
ObDereferenceObject(LastThread);
//
// Acquire rundown protection of the thread.
// This stops the thread exiting so we know it can't send
// it's termination message
//
PEX_RUNDOWN_REF RundownProtect = kDbgUtil::GetThreadRundownProtect(Thread);
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(Thread);
bool isSystemThread = (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_SYSTEM) != 0;
if (ExAcquireRundownProtection(RundownProtect)) {
Flags |= DEBUG_EVENT_RELEASE;
//
// Suspend the thread if we can for the debugger
// We don't suspend terminating threads as we will not be giving details
// of these to the debugger.
//
if (!isSystemThread) {
status = kDbgUtil::g_pPsSuspendThread(Thread, nullptr);
if (NT_SUCCESS(status)) {
Flags |= DEBUG_EVENT_SUSPEND;
}
}
}
else {
//
// Rundown protection failed for this thread.
// This means the thread is exiting. We will mark this thread
// later so it doesn't sent a thread termination message.
// We can't do this now because this attach might fail.
//
Flags |= DEBUG_EVENT_PROTECT_FAILED;
}
// 每次构造一个DBGKM_APIMSG结构
RtlZeroMemory(&ApiMsg, sizeof(ApiMsg));
if (First && (Flags & DEBUG_EVENT_PROTECT_FAILED) == 0 &&
!isSystemThread) {
// 进程的第一个线程
IsFirstThread = TRUE;
}
else {
IsFirstThread = FALSE;
}
if (IsFirstThread) {
ApiMsg.ApiNumber = DbgKmCreateProcessApi;
VOID* SectionObject = kDbgUtil::GetProcessSectionObject(Process);
if (SectionObject) {
ApiMsg.u.CreateProcess.FileHandle = kDbgUtil::g_pDbgkpSectionToFileHandle(SectionObject);
}
else {
ApiMsg.u.CreateProcess.FileHandle = nullptr;
}
PVOID SectionBaseAddress = kDbgUtil::GetProcessSectionBaseAddress(Process);
ApiMsg.u.CreateProcess.BaseOfImage = SectionBaseAddress;
KeStackAttachProcess(Process, &ApcState);
__try {
NtHeaders = RtlImageNtHeader(SectionBaseAddress);
if (NtHeaders) {
ApiMsg.u.CreateProcess.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.CreateProcess.InitialThread.StartAddress = nullptr;
ApiMsg.u.CreateProcess.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ApiMsg.u.CreateProcess.InitialThread.StartAddress = nullptr;
ApiMsg.u.CreateProcess.DebugInfoFileOffset = 0;
ApiMsg.u.CreateProcess.DebugInfoSize = 0;
}
KeUnstackDetachProcess(&ApcState);
}
else {
ApiMsg.ApiNumber = DbgKmCreateThreadApi;
ApiMsg.u.CreateThread.StartAddress = kDbgUtil::GetThreadStartAddress(Thread);
}
status = DbgkpQueueMessage(Process, Thread, &ApiMsg,
Flags,
DebugObject);
if (!NT_SUCCESS(status)) {
if (Flags & DEBUG_EVENT_SUSPEND) {
kDbgUtil::g_pPsResumeThread(Thread, nullptr);
}
if (Flags & DEBUG_EVENT_RELEASE) {
ExReleaseRundownProtection(RundownProtect);
}
if (ApiMsg.ApiNumber == DbgKmCreateProcessApi && ApiMsg.u.CreateProcess.FileHandle != nullptr) {
ObCloseHandle(ApiMsg.u.CreateProcess.FileHandle, KernelMode);
}
ObfDereferenceObject(Thread);
break;
}
else if (IsFirstThread) {
First = FALSE;
ObReferenceObject(Thread);
FirstThread = Thread;
DbgkSendSystemDllMessages(Thread, DebugObject, &ApiMsg);
}
}
if (!NT_SUCCESS(status)) {
if (FirstThread) {
ObDereferenceObject(FirstThread);
}
if (LastThread != nullptr) {
ObDereferenceObject(LastThread);
}
}
else {
if (FirstThread) {
*pFirstThread = (PETHREAD)FirstThread;
*pLastThread = (PETHREAD)LastThread;
}
else {
if (LastThread != nullptr) {
ObDereferenceObject(LastThread);
}
status = STATUS_UNSUCCESSFUL;
}
}
return status;
}
NTSTATUS
DbgkpPostModuleMessages(
_In_ PEPROCESS Process,
_In_ PETHREAD Thread,
_In_ PDEBUG_OBJECT DebugObject)
/*++
Routine Description:
This routine posts the module load messages when we debug an active process.
--*/
{
PPEB Peb = kDbgUtil::GetProcessPeb(Process);
PPEB_LDR_DATA Ldr = nullptr;
PLIST_ENTRY LdrHead, LdrNext;
PLDR_DATA_TABLE_ENTRY LdrEntry;
ULONG i;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING Name;
PIMAGE_NT_HEADERS NtHeaders;
NTSTATUS status;
IO_STATUS_BLOCK ioStatus;
DBGKM_APIMSG ApiMsg;
if (Peb == nullptr) {
return STATUS_SUCCESS;
}
ULONG maxModuleMsgs = *g_pDbgkpMaxModuleMsgs;
__try {
Ldr = kDbgUtil::GetPEBLdr(Peb);
LdrHead = &Ldr->InLoadOrderModuleList;
ProbeForReadSmallStructure(LdrHead, sizeof(LIST_ENTRY), sizeof(UCHAR));
for (LdrNext = LdrHead->Flink, i = 0;
LdrNext != LdrHead && i < maxModuleMsgs;
LdrNext = LdrNext->Flink, i++) {
if (i > 1) {
//
// First image got send with process create message
//
RtlZeroMemory(&ApiMsg, sizeof(ApiMsg));
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
ProbeForReadSmallStructure(LdrEntry, sizeof(LDR_DATA_TABLE_ENTRY), sizeof(UCHAR));
ApiMsg.ApiNumber = DbgKmLoadDllApi;
ApiMsg.u.LoadDll.BaseOfDll = LdrEntry->DllBase;
ProbeForReadSmallStructure(ApiMsg.u.LoadDll.BaseOfDll, sizeof(IMAGE_DOS_HEADER), sizeof(UCHAR));
NtHeaders = RtlImageNtHeader(ApiMsg.u.LoadDll.BaseOfDll);
if (NtHeaders) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.LoadDll.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
status = kDbgUtil::g_pMmGetFileNameForAddress(NtHeaders, &Name);
if (NT_SUCCESS(status)) {
InitializeObjectAttributes(&attr, &Name,
OBJ_FORCE_ACCESS_CHECK | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
nullptr, nullptr);
status = ZwOpenFile(&ApiMsg.u.LoadDll.FileHandle,
GENERIC_READ | SYNCHRONIZE,
&attr,
&ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(status)) {
ApiMsg.u.LoadDll.FileHandle = nullptr;
}
ExFreePool(Name.Buffer);
}
if (DebugObject) {
status = DbgkpQueueMessage(
Process, Thread, &ApiMsg, DEBUG_EVENT_NOWAIT, DebugObject
);
}
else {
DbgkpSendApiMessage(0x3, &ApiMsg);
status = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(status) && ApiMsg.u.LoadDll.FileHandle != nullptr) {
ObCloseHandle(ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
}
ProbeForReadSmallStructure(LdrNext, sizeof(LIST_ENTRY), sizeof(UCHAR));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
#ifdef _WIN64
PWOW64_PROCESS Wow64Process = (PWOW64_PROCESS)kDbgUtil::GetProcessWow64Process(Process);
if (Wow64Process != nullptr && Wow64Process->Wow64 != nullptr) {
PPEB32 Peb32;
PPEB_LDR_DATA32 Ldr32;
PLIST_ENTRY32 LdrHead32, LdrNext32;
PLDR_DATA_TABLE_ENTRY32 LdrEntry32;
PWCHAR pSys;
Peb32 = (PPEB32)Wow64Process->Wow64;
__try {
Ldr32 = (PPEB_LDR_DATA32)ULongToPtr(Peb32->Ldr);
LdrHead32 = &Ldr32->InLoadOrderModuleList;
ProbeForReadSmallStructure(LdrHead32, sizeof(LIST_ENTRY32), sizeof(UCHAR));
for (LdrNext32 = (PLIST_ENTRY32)UlongToPtr(LdrHead32->Flink), i = 0;
LdrNext32 != LdrHead32 && i < maxModuleMsgs;
LdrNext32 = (PLIST_ENTRY32)UlongToPtr(LdrNext32->Flink), i++) {
if (i > 1) {
RtlZeroMemory(&ApiMsg, sizeof(ApiMsg));
LdrEntry32 = CONTAINING_RECORD(LdrNext32, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
ProbeForReadSmallStructure(LdrEntry32, sizeof(LDR_DATA_TABLE_ENTRY32), sizeof(UCHAR));
ApiMsg.ApiNumber = DbgKmLoadDllApi;
ApiMsg.u.LoadDll.BaseOfDll = UlongToPtr(LdrEntry32->DllBase);
ApiMsg.u.LoadDll.NamePointer = nullptr;
ProbeForReadSmallStructure(ApiMsg.u.LoadDll.BaseOfDll, sizeof(IMAGE_DOS_HEADER), sizeof(UCHAR));
NtHeaders = RtlImageNtHeader(ApiMsg.u.LoadDll.BaseOfDll);
if (NtHeaders) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
ApiMsg.u.LoadDll.DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
status = kDbgUtil::g_pMmGetFileNameForAddress(NtHeaders, &Name);
if (NT_SUCCESS(status)) {
ASSERT(sizeof(L"SYSTEM32") == sizeof(WOW64_SYSTEM_DIRECTORY_U));
pSys = wcsstr(Name.Buffer, L"\\SYSTEM32\\");
if (pSys != NULL) {
RtlCopyMemory(pSys + 1,
WOW64_SYSTEM_DIRECTORY_U,
sizeof(WOW64_SYSTEM_DIRECTORY_U) - sizeof(UNICODE_NULL));
}
InitializeObjectAttributes(&attr,
&Name,
OBJ_FORCE_ACCESS_CHECK | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
nullptr,
nullptr);
status = ZwOpenFile(&ApiMsg.u.LoadDll.FileHandle,
GENERIC_READ | SYNCHRONIZE,
&attr,
&ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(status)) {
ApiMsg.u.LoadDll.FileHandle = nullptr;
}
ExFreePool(Name.Buffer);
}
if (DebugObject) {
status = DbgkpQueueMessage(Process,
Thread,
&ApiMsg,
DEBUG_EVENT_NOWAIT,
DebugObject);
}
else {
status = DbgkpSendApiMessage(3, &ApiMsg);
status = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(status) && ApiMsg.u.LoadDll.FileHandle != NULL) {
ObCloseHandle(ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
}
ProbeForReadSmallStructure(LdrNext32, sizeof(LIST_ENTRY32), sizeof(UCHAR));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
}
#endif
return STATUS_SUCCESS;
}
VOID NewDbgkMapViewOfSection(
_In_ PEPROCESS Process,
_In_ PVOID SectionObject,
_In_ PVOID BaseAddress
)
/*++
Routine Description:
This function is called when the current process successfully
maps a view of an image section. If the process has an associated
debug port, then a load dll message is sent.
Arguments:
SectionObject - Supplies a pointer to the section mapped by the process.
BaseAddress - Supplies the base address of where the section is
mapped in the current process address space.
--*/
{
PVOID Port;
HANDLE hFile = nullptr;
DBGKM_APIMSG ApiMsg;
PEPROCESS CurrentProcess;
PETHREAD CurrentThread;
PIMAGE_NT_HEADERS pImageHeader;
PDBGKM_LOAD_DLL LoadDllArgs;
CurrentProcess = PsGetCurrentProcess();
CurrentThread = PsGetCurrentThread();
if (ExGetPreviousMode() == KernelMode){
return;
}
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(CurrentThread);
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
Port = nullptr;
}
else {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
Port = *pDebugObject;
}
if (!Port) {
return;
}
LoadDllArgs = &ApiMsg.u.LoadDll;
LoadDllArgs->FileHandle = kDbgUtil::g_pDbgkpSectionToFileHandle(SectionObject);
LoadDllArgs->BaseOfDll = BaseAddress;
LoadDllArgs->DebugInfoFileOffset = 0;
LoadDllArgs->DebugInfoSize = 0;
//
// The loader fills in the module name in this pointer before mapping
// the section. It's a very poor linkage.
//
PTEB Teb = (PTEB)PsGetCurrentThreadTeb();
LoadDllArgs->NamePointer = Teb->NtTib.ArbitraryUserPointer;
__try {
pImageHeader = RtlImageNtHeader(BaseAddress);
if (pImageHeader != nullptr) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = pImageHeader->FileHeader.PointerToSymbolTable;
ApiMsg.u.LoadDll.DebugInfoSize = pImageHeader->FileHeader.NumberOfSections;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ApiMsg.u.LoadDll.DebugInfoFileOffset = 0;
ApiMsg.u.LoadDll.DebugInfoSize = 0;
ApiMsg.u.LoadDll.NamePointer = nullptr;
}
DBGKM_FORMAT_API_MSG(ApiMsg, DbgKmLoadDllApi, sizeof(DBGKM_LOAD_DLL));
DbgkpSendApiMessage(0x1, &ApiMsg);
if (ApiMsg.u.LoadDll.FileHandle != nullptr) {
ObCloseHandle(ApiMsg.u.LoadDll.FileHandle, KernelMode);
}
}
VOID NewDbgkUnMapViewOfSection(
_In_ PEPROCESS Process,
_In_ PVOID BaseAddress
)/*++
Routine Description:
This function is called when the current process successfully
unmpas a view of an image section. If the process has an associated
debug port, then an "unmap view of section" message is sent.
--*/
{
PVOID Port;
DBGKM_APIMSG ApiMsg;
PEPROCESS CurrentProcess;
PETHREAD CurrentThread;
CurrentProcess = PsGetCurrentProcess();
CurrentThread = PsGetCurrentThread();
if (ExGetPreviousMode() == KernelMode) {
return;
}
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(CurrentThread);
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
Port = nullptr;
}
else {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
Port = *pDebugObject;
}
if (!Port) {
return;
}
PDBGKM_UNLOAD_DLL UnloadDllArgs;
UnloadDllArgs = &ApiMsg.u.UnloadDll;
UnloadDllArgs->BaseAddress = BaseAddress;
DBGKM_FORMAT_API_MSG(ApiMsg, DbgKmUnloadDllApi, sizeof(DBGKM_UNLOAD_DLL));
DbgkpSendApiMessage(0x1, &ApiMsg);
}
VOID NewDbgkCreateThread(
PETHREAD Thread
)
/*++
Routine Description:
This function is called when a new thread begins to execute. If the
thread has an associated DebugPort, then a message is sent thru the
port.
If this thread is the first thread in the process, then this event
is translated into a CreateProcessInfo message.
If a message is sent, then while the thread is waiting a reply,
all other threads in the process are suspended.
Arguments:
Thread - New thread just being started.
Return Value:
None.
--*/
{
PVOID Port;
DBGKM_APIMSG m;
PDBGKM_CREATE_THREAD CreateThreadArgs;
PDBGKM_CREATE_PROCESS CreateProcessArgs;
PEPROCESS Process;
PDBGKM_LOAD_DLL LoadDllArgs;
NTSTATUS status;
PIMAGE_NT_HEADERS NtHeaders = nullptr;
PTEB Teb;
ULONG OldFlags = 0;
PFILE_OBJECT FileObject;
OBJECT_ATTRIBUTES ioStatus;
PVOID SectionObject;
PETHREAD CurrentThread;
#if defined(_WIN64)
PVOID Wow64Process = nullptr;
#endif
CurrentThread = PsGetCurrentThread();
Process = kDbgUtil::GetThreadApcState(CurrentThread)->Process;
#if defined(_WIN64)
Wow64Process = kDbgUtil::GetProcessWow64Process(Process);
#endif
PULONG pFlags = kDbgUtil::GetProcessFlags(Process);
OldFlags = PS_TEST_SET_BITS(pFlags, PS_PROCESS_FLAGS_CREATE_REPORTED | PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE);
ULONG PspNotifyEnableMask = *g_pPspNotifyEnableMask;
if ((OldFlags & PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE) == 0 && (PspNotifyEnableMask & 1)) {
IMAGE_INFO_EX ImageInfoEx;
PUNICODE_STRING ImageName;
POBJECT_NAME_INFORMATION FileNameInfo;
//
// notification of main.exe
//
ImageInfoEx.ImageInfo.Properties = 0;
ImageInfoEx.ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
PVOID SectionBaseAddress = kDbgUtil::GetProcessSectionBaseAddress(Process);
ImageInfoEx.ImageInfo.ImageBase = SectionBaseAddress;
ImageInfoEx.ImageInfo.ImageSize = 0;
__try {
NtHeaders = RtlImageNtHeader(SectionBaseAddress);
if (NtHeaders) {
#ifdef _WIN64
if (Wow64Process != nullptr) {
ImageInfoEx.ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER((PIMAGE_NT_HEADERS32)NtHeaders, SizeOfImage);
}
else {
#endif
ImageInfoEx.ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER(NtHeaders, SizeOfImage);
#ifdef _WIN64
}
#endif
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ImageInfoEx.ImageInfo.ImageSize = 0;
}
ImageInfoEx.ImageInfo.ImageSelector = 0;
ImageInfoEx.ImageInfo.ImageSectionNumber = 0;
PsReferenceProcessFilePointer((PEPROCESS)Process, &FileObject);
status = SeLocateProcessImageName((PEPROCESS)Process, &ImageName);
if (!NT_SUCCESS(status)) {
ImageName = nullptr;
}
PsCallImageNotifyRoutines(ImageName, PsGetProcessId(Process), &ImageInfoEx, FileObject);
if (ImageName) {
//因为在SeLocateProcessImageName中为ImageName申请了内存,所以要在此处释放掉
ExFreePool(ImageName);
}
ObDereferenceObject(FileObject);
//
// system dll
//
for (int i = 0; i < 2; i++) {
PPS_SYSTEM_DLL_INFO info = kDbgUtil::g_pPsQuerySystemDllInfo(i);
if (info != nullptr) {
ImageInfoEx.ImageInfo.Properties = 0;
ImageInfoEx.ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
ImageInfoEx.ImageInfo.ImageBase = info->ImageBase;
ImageInfoEx.ImageInfo.ImageSize = 0;
__try {
NtHeaders = RtlImageNtHeader(info->ImageBase);
if (NtHeaders) {
#ifdef _WIN64
if (Wow64Process != nullptr) {
ImageInfoEx.ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER((PIMAGE_NT_HEADERS32)NtHeaders, SizeOfImage);
}
else {
#endif
ImageInfoEx.ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER(NtHeaders, SizeOfImage);
#ifdef _WIN64
}
#endif
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ImageInfoEx.ImageInfo.ImageSize = 0;
}
ImageInfoEx.ImageInfo.ImageSelector = 0;
ImageInfoEx.ImageInfo.ImageSectionNumber = 0;
PPS_SYSTEM_DLL sysDll = CONTAINING_RECORD(info, PS_SYSTEM_DLL, SystemDllInfo);
SectionObject = kDbgUtil::g_pObFastReferenceObject(&sysDll->SectionObjectFastRef);
if (SectionObject == nullptr) {
KeEnterCriticalRegion();
ExAcquirePushLockShared((PEX_PUSH_LOCK_S)&sysDll->PushLock);
SectionObject = kDbgUtil::g_pObFastReferenceObjectLocked(&sysDll->SectionObjectFastRef);
ExReleasePushLockShared((PEX_PUSH_LOCK_S)&sysDll->PushLock);
KeLeaveCriticalRegion();
}
PVOID SectionControlArea = kDbgUtil::g_pMiSectionControlArea(SectionObject);
FileObject = kDbgUtil::g_pMiReferenceControlAreaFile(SectionControlArea);
if (FileObject != nullptr) {
kDbgUtil::g_pObFastDereferenceObject(&sysDll->SectionObjectFastRef, SectionObject);
}
PsCallImageNotifyRoutines(&info->Ntdll32Path,
PsGetProcessId(Process),
&ImageInfoEx,
FileObject);
if(FileObject!=nullptr)
ObDereferenceObject(FileObject);
}
}
// 检测新创建的线程所在的进程是否正在被调试
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
if (*pDebugObject == nullptr) {
return;
}
// 判断是否调用过调试函数
if ((OldFlags & PS_PROCESS_FLAGS_CREATE_REPORTED) == 0) {
CreateThreadArgs = &m.u.CreateProcess.InitialThread;
CreateThreadArgs->SubSystemKey = 0;
CreateThreadArgs->StartAddress = nullptr;
CreateProcessArgs = &m.u.CreateProcess;
CreateProcessArgs->SubSystemKey = 0;
PVOID SectionObject = kDbgUtil::GetProcessSectionObject(Process);
CreateProcessArgs->FileHandle = kDbgUtil::g_pDbgkpSectionToFileHandle(SectionObject);
PVOID SectionBaseAddress = kDbgUtil::GetProcessSectionBaseAddress(Process);
CreateProcessArgs->BaseOfImage = SectionBaseAddress;
CreateProcessArgs->DebugInfoFileOffset = 0;
CreateProcessArgs->DebugInfoSize = 0;
__try {
NtHeaders = RtlImageNtHeader(SectionBaseAddress);
if (NtHeaders) {
CreateThreadArgs->StartAddress = (PVOID)(DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER(NtHeaders, AddressOfEntryPoint) +
DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER(NtHeaders, ImageBase));
CreateProcessArgs->DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
CreateProcessArgs->DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
CreateThreadArgs->StartAddress = nullptr;
CreateProcessArgs->DebugInfoFileOffset = 0;
CreateProcessArgs->DebugInfoSize = 0;
}
DBGKM_FORMAT_API_MSG(m, DbgKmCreateProcessApi, sizeof(*CreateProcessArgs));
DbgkpSendApiMessage(0, &m);
if (CreateProcessArgs->FileHandle != nullptr) {
ObCloseHandle(CreateProcessArgs->FileHandle, KernelMode);
}
DbgkSendSystemDllMessages(nullptr, nullptr, &m);
}
else {
CreateThreadArgs = &m.u.CreateThread;
CreateThreadArgs->SubSystemKey = 0;
CreateThreadArgs->StartAddress = kDbgUtil::GetThreadWin32StartAddress(Thread);
DBGKM_FORMAT_API_MSG(m, DbgKmCreateThreadApi, sizeof(*CreateThreadArgs));
DbgkpSendApiMessage(1, &m);
}
ULONG ClonedThread = kDbgUtil::GetThreadClonedThread(Thread);
if (ClonedThread) {
DbgkpPostModuleMessages(
Process,
Thread,
nullptr
);
}
}
}
NTSTATUS
DbgkpQueueMessage(
_In_ PEPROCESS Process,
_In_ PETHREAD Thread,
_Inout_ PDBGKM_APIMSG ApiMsg,
_In_ ULONG Flags,
_In_ PDEBUG_OBJECT TargetDebugObject
)
/*++
Routine Description:
Queues a debug message to the port for a user mode debugger to get.
Arguments:
Process - Process being debugged
Thread - Thread making call
ApiMsg - Message being sent and received
NoWait - Don't wait for a response. Buffer message and return.
TargetDebugObject - Port to queue nowait messages to
Return Value:
NTSTATUS - Status of call.
--*/
{
PDEBUG_EVENT DebugEvent;
DEBUG_EVENT StaticDebugEvent;
PDEBUG_OBJECT DebugObject = nullptr;
NTSTATUS status;
if (Flags & DEBUG_EVENT_NOWAIT) {
// NoWait: Don't wait for a response. Buffer message and return.
DebugEvent = (PDEBUG_EVENT)ExAllocatePoolWithQuotaTag(NonPagedPool, sizeof(*DebugEvent), 'ssap');
if (DebugEvent == nullptr) {
return STATUS_INSUFFICIENT_RESOURCES;
}
DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
ObReferenceObject(Process);
ObReferenceObject(Thread);
DebugEvent->BackoutThread = PsGetCurrentThread();
DebugObject = TargetDebugObject;
}
else {
DebugEvent = &StaticDebugEvent;
DebugEvent->Flags = Flags;
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
DebugObject = *pDebugObject;
PULONG pCrossThreadFlags = kDbgUtil::GetProcessFlags(Process);
ULONG CrossThreadFlags = *pCrossThreadFlags;
//
// See if this create message has already been sent.
//
if (ApiMsg->ApiNumber == DbgKmCreateProcessApi ||
ApiMsg->ApiNumber == DbgKmCreateProcessApi) {
if (CrossThreadFlags & PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG) {
DebugObject = nullptr;
}
}
if (ApiMsg->ApiNumber == DbgKmLoadDllApi &&
CrossThreadFlags & PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG &&
Flags & 0x40) {
DebugObject = nullptr;
}
//
// See if this exit message is for thread that never had a create
//
if (ApiMsg->ApiNumber == DbgKmExitThreadApi ||
ApiMsg->ApiNumber == DbgKmExitProcessApi) {
if (CrossThreadFlags & PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG) {
DebugObject = nullptr;
}
}
KeInitializeEvent(&DebugEvent->ContinueEvent, SynchronizationEvent, FALSE);
}
DebugEvent->Process = Process;
DebugEvent->Thread = Thread;
DebugEvent->ApiMsg = *ApiMsg;
DebugEvent->ClientId = kDbgUtil::GetThreadCid(Thread);
if (DebugObject == nullptr) {
status = STATUS_PORT_NOT_SET;
}
else {
//
// We must not use a debug port thats got no handles left
//
ExAcquireFastMutex(&DebugObject->Mutex);
//
// If the object is delete pending then don't use this object.
//
if ((DebugObject->Flags & DEBUG_OBJECT_DELETE_PENDING) == 0) {
InsertTailList(&DebugObject->EventList, &DebugEvent->EventList);
//
// Set the event to say there is an unread event in the object.
//
if ((Flags & DEBUG_EVENT_NOWAIT) == 0) {
// 通知调试器有消息要读取
KeSetEvent(&DebugObject->EventsPresent, 0, FALSE);
}
status = STATUS_SUCCESS;
}
else {
status = STATUS_DEBUGGER_INACTIVE;
}
ExReleaseFastMutex(&DebugObject->Mutex);
}
if ((Flags & DEBUG_EVENT_NOWAIT) == 0) {
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
if (NT_SUCCESS(status)) {
// 等待调试器的回复
KeWaitForSingleObject(
&DebugEvent->ContinueEvent,
Executive,
KernelMode,
FALSE,
nullptr
);
status = DebugEvent->Status;
*ApiMsg = DebugEvent->ApiMsg;
}
}
else {
if (!NT_SUCCESS(status)) {
ObDereferenceObject(Process);
ObDereferenceObject(Thread);
ExFreePool(DebugEvent);
}
}
return status;
}
NTSTATUS NewNtWaitForDebugEvent(
_In_ HANDLE DebugObjectHandle,
_In_ BOOLEAN Alertable,
_In_opt_ PLARGE_INTEGER Timeout,
_Out_ PDBGUI_WAIT_STATE_CHANGE WaitStateChange
)
/*++
Routine Description:
Waits for a debug event and returns it to the user if one arrives
Arguments:
DebugObjectHandle - Handle to a debug object
Alertable - TRUE is the wait to be alertable
Timeout - Operation timeout value
WaitStateChange - Returned debug event
Return Value:
Status of operation
--*/ {
NTSTATUS status;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
LARGE_INTEGER Tmo = { 0 };
LARGE_INTEGER StartTime = { 0 };
DBGUI_WAIT_STATE_CHANGE waitState{};
PEPROCESS Process;
PETHREAD Thread;
PreviousMode = ExGetPreviousMode();
__try {
if (ARGUMENT_PRESENT(Timeout)) {
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure(Timeout, sizeof(*Timeout), sizeof(UCHAR));
}
KeQuerySystemTime(&StartTime);
}
if (PreviousMode != KernelMode) {
ProbeForWriteSmallStructure(WaitStateChange, sizeof(*WaitStateChange), sizeof(UCHAR));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
status = ObReferenceObjectByHandle(DebugObjectHandle,
DEBUG_OBJECT_WAIT_STATE_CHANGE,
*DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
nullptr);
if (!NT_SUCCESS(status)) {
return status;
}
Process = nullptr;
Thread = nullptr;
BOOLEAN GotEvent;
PDEBUG_EVENT DebugEvent, DebugEvent2;
PLIST_ENTRY Entry, Entry2;
while (true) {
// 在调试对象有事件时产生
status = KeWaitForSingleObject(&DebugObject->EventsPresent,
Executive, PreviousMode, Alertable, Timeout);
if (!NT_SUCCESS(status) || status == STATUS_TIMEOUT
|| status == STATUS_ALERTED || status == STATUS_USER_APC) {
break;
}
GotEvent = FALSE;
DebugEvent = nullptr;
ExAcquireFastMutex(&DebugObject->Mutex);
//
// If the object is delete pending then return an error.
//
if ((DebugObject->Flags & DEBUG_OBJECT_DELETE_PENDING)==0) {
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
Entry = Entry->Flink) {
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
//
// If this event has not been given back to the user yet and is not
// inactive then pass it back.
// We check to see if we have any other outstanding messages for this
// thread as this confuses VC. You can only get multiple events
// for the same thread for the attach faked messages.
//
if ((DebugEvent->Flags & (DEBUG_EVENT_READ | DEBUG_EVENT_INACTIVE)) == 0) {
GotEvent = TRUE;
for (Entry2 = DebugObject->EventList.Flink;
Entry2 != Entry;
Entry2 = Entry2->Flink) {
DebugEvent2 = CONTAINING_RECORD(Entry2, DEBUG_EVENT, EventList);
if (DebugEvent->ClientId.UniqueProcess == DebugEvent2->ClientId.UniqueProcess) {
//
// This event has the same process as an earlier event. Mark it as inactive.
//
DebugEvent->Flags |= DEBUG_EVENT_INACTIVE;
DebugEvent->BackoutThread = nullptr;
GotEvent = FALSE;
break;
}
}
if (GotEvent) {
break;
}
}
}
if (GotEvent) {
Process = DebugEvent->Process;
Thread = DebugEvent->Thread;
ObReferenceObject(Thread);
ObReferenceObject(Process);
kDbgUtil::g_pDbgkpConvertKernelToUserStateChange(&waitState, DebugEvent);
DebugEvent->Flags |= DEBUG_EVENT_READ;
}
else {
//
// No unread events there. Clear the event.
KeClearEvent(&DebugObject->EventsPresent);
}
status = STATUS_SUCCESS;
}
else {
status = STATUS_DEBUGGER_INACTIVE;
}
ExReleaseFastMutex(&DebugObject->Mutex);
if (NT_SUCCESS(status)) {
//
// If we woke up and found nothing
//
if (GotEvent == FALSE) {
//
// If timeout is a delta time then adjust it for the wait so far.
//
if (Tmo.QuadPart < 0) {
LARGE_INTEGER NewTime;
KeQuerySystemTime(&NewTime);
Tmo.QuadPart = Tmo.QuadPart + (NewTime.QuadPart - StartTime.QuadPart);
StartTime = NewTime;
if (Tmo.QuadPart >= 0) {
status = STATUS_TIMEOUT;
break;
}
}
}
else {
//
// Fixup needed handles. The caller could have guessed the thread id etc by now and made the target thread
// continue. This isn't a problem as we won't do anything damaging to the system in this case. The caller
// won't get the correct results but they set out to break us.
//
kDbgUtil::g_pDbgkpOpenHandles(&waitState, Process, Thread);
ObDereferenceObject(Thread);
ObDereferenceObject(Process);
break;
}
}
else {
break;
}
}
ObDereferenceObject(DebugObject);
__try {
*WaitStateChange = waitState;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
return status;
}
NTSTATUS NewNtDebugContinue(
_In_ HANDLE DebugObjectHandle,
_In_ PCLIENT_ID AppClientId,
_In_ NTSTATUS ContinueStatus
)
/*++
Routine Description:
Continues a stalled debugged thread
Arguments:
DebugObjectHandle - Handle to a debug object
ClientId - ClientId of thread tro continue
ContinueStatus - Status of continue
Return Value:
Status of operation
--*/
{
NTSTATUS status;
PDEBUG_OBJECT DebugObject;
PDEBUG_EVENT DebugEvent, FoundDebugEvent;
KPROCESSOR_MODE PreviousMode;
PLIST_ENTRY Entry;
CLIENT_ID ClientId;
BOOLEAN GotEvent;
PreviousMode = ExGetPreviousMode();
__try {
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure(AppClientId, sizeof(*AppClientId), sizeof(UCHAR));
}
ClientId = *AppClientId;
}
__except (EXCEPTION_EXECUTE_HANDLER) {// If previous mode is kernel then don't handle the exception
return GetExceptionCode();
}
switch (ContinueStatus)
{
case DBG_EXCEPTION_HANDLED:
case DBG_CONTINUE:
case DBG_TERMINATE_PROCESS:
case DBG_TERMINATE_THREAD:
case DBG_EXCEPTION_NOT_HANDLED:
break;
default:
return STATUS_INVALID_PARAMETER;
}
status = ObReferenceObjectByHandle(DebugObjectHandle,
DEBUG_OBJECT_WAIT_STATE_CHANGE,
*DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
nullptr);
if (!NT_SUCCESS(status)) {
return status;
}
GotEvent = FALSE;
FoundDebugEvent = nullptr;
ExAcquireFastMutex(&DebugObject->Mutex);
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
Entry = Entry->Flink) {
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
//
// Make sure the client ID matches that the debugger saw all the events.
// We don't allow the caller to start a thread that it never saw a message for.
//
if (DebugEvent->ClientId.UniqueProcess == AppClientId->UniqueProcess) {
if (!GotEvent) {
if (DebugEvent->ClientId.UniqueThread == ClientId.UniqueThread &&
(DebugEvent->Flags & DEBUG_EVENT_READ) != 0) {
RemoveEntryList(Entry);
FoundDebugEvent = DebugEvent;
GotEvent = TRUE;
}
}
else {
//
// VC breaks if it sees more than one event at a time
// for the same process.
//
DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
KeSetEvent(&DebugObject->EventsPresent, 0, FALSE);
break;
}
}
}
ExReleaseFastMutex(&DebugObject->Mutex);
ObReferenceObject(DebugObject);
if (GotEvent) {
FoundDebugEvent->ApiMsg.ReturnedStatus = ContinueStatus;
FoundDebugEvent->Status = STATUS_SUCCESS;
kDbgUtil::g_pDbgkpWakeTarget(FoundDebugEvent);
}
else {
status = STATUS_INVALID_PARAMETER;
}
return status;
}
NTSTATUS NewNtRemoveProcessDebug(
_In_ HANDLE ProcessHandle,
_In_ HANDLE DebugObjectHandle
)
/*++
Routine Description:
Remove a debug object from a process.
Arguments:
ProcessHandle - Handle to a process currently being debugged
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS status;
PEPROCESS Process, CurrentProcess;
KPROCESSOR_MODE PreviousMode;
PDEBUG_OBJECT DebugObject;
PreviousMode = ExGetPreviousMode();
status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_SUSPEND_RESUME,
*PsProcessType,
PreviousMode,
(PVOID*)&Process,
nullptr);
if (!NT_SUCCESS(status)) {
return status;
}
if (PreviousMode == UserMode) {
CurrentProcess = PsGetCurrentProcess();
}
status = ObReferenceObjectByHandle(DebugObjectHandle,
DEBUG_OBJECT_KILL_ON_CLOSE,
*DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
nullptr);
if (!NT_SUCCESS(status)) {
ObDereferenceObject(Process);
return status;
}
status = NewDbgkClearProcessDebugObject((PEPROCESS)Process, DebugObject);
ObDereferenceObject(DebugObject);
ObDereferenceObject(Process);
return status;
}
VOID DbgkpResumeProcess(
_In_opt_ PEPROCESS Process
) {
if (Process == nullptr) {
kDbgUtil::g_pKeThawAllThreads();
}
}
NTSTATUS DbgkpSendApiMessage(
UCHAR Flags,
PDBGKM_APIMSG ApiMsg
)
/*++
Routine Description:
This function sends the specified API message over the specified
port. It is the caller's responsibility to format the API message
prior to calling this function.
If the SuspendProcess flag is supplied, then all threads in the calling
process are first suspended.
Upon receipt of the reply message, the threads,the threads are resumed.
--*/
{
NTSTATUS status;
BOOLEAN SuspendProcess;
PEPROCESS Process;
PETHREAD Thread;
SuspendProcess = FALSE;
if (Flags & 0x1) {
SuspendProcess = kDbgUtil::g_pDbgkpSuspendProcess();
}
ApiMsg->ReturnedStatus = STATUS_PENDING;
status = DbgkpQueueMessage(
PsGetCurrentProcess(),
KeGetCurrentThread(),
ApiMsg,
((Flags & 0x2) << 0x5),
nullptr
);
ZwFlushInstructionCache(ZwCurrentProcess(), nullptr, 0);
if (SuspendProcess) {
DbgkpResumeProcess(nullptr);
}
return status;
}
EX_FAST_REF
ExFastReference(
_Inout_ PEX_FAST_REF FastRef
) {
EX_FAST_REF OldRef, NewRef;
while (true) {
OldRef = ReadForWriteAccess(FastRef);
if (OldRef.RefCnt != 0) {
NewRef.Value = OldRef.Value - 1;
NewRef.Object = InterlockedCompareExchangePointerRelease(&FastRef->Object, NewRef.Object, OldRef.Object);
if (NewRef.Object != OldRef.Object) {
continue;
}
}
break;
}
return OldRef;
}
LOGICAL
ExFastRefAddAdditionalReferenceCounts(
_Inout_ PEX_FAST_REF FastRef,
_In_ PVOID Object,
_In_ ULONG RefsToAdd
) {
EX_FAST_REF OldRef, NewRef;
while (true) {
OldRef = ReadForWriteAccess(FastRef);
if (OldRef.RefCnt + RefsToAdd > MAX_FAST_REFS ||
(ULONG_PTR)Object != (OldRef.Value & ~MAX_FAST_REFS)) {
return FALSE;
}
NewRef.Value = OldRef.Value + RefsToAdd;
NewRef.Object = InterlockedCompareExchangePointerAcquire(&FastRef->Object,
NewRef.Object,
OldRef.Object);
if (NewRef.Object != OldRef.Object) {
continue;
}
break;
}
return TRUE;
}
VOID NewDbgkExitThread(
NTSTATUS ExitStatus
)
/*++
Routine Description:
This function is called when a new thread terminates. At this
point, the thread will no longer execute in user-mode. No other
exit processing has occured.
If a message is sent, then while the thread is awaiting a reply,
all other threads in the process are suspended.
Arguments:
ExitStatus - Supplies the ExitStatus of the exiting thread.
Return Value:
None.
--*/
{
DBGKM_APIMSG ApiMsg;
PEPROCESS Process;
PETHREAD CurrentThread;
PVOID Port;
PDBGKM_EXIT_THREAD args;
CurrentThread = PsGetCurrentThread();
Process = PsGetCurrentProcess();
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(CurrentThread);
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
Port = nullptr;
}
else {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
Port = *pDebugObject;
}
if (!Port) {
return;
}
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_DEADTHREAD) {
return;
}
args = &ApiMsg.u.ExitThread;
args->ExitStatus = ExitStatus;
DBGKM_FORMAT_API_MSG(ApiMsg, DbgKmExitThreadApi, sizeof(*args));
DbgkpSendApiMessage(FALSE, &ApiMsg);
BOOLEAN Frozen = kDbgUtil::g_pDbgkpSuspendProcess();
if (Frozen) {
DbgkpResumeProcess(nullptr);
}
}
VOID NewDbgkExitProcess(
NTSTATUS ExitStatus
)
/*++
Routine Description:
This function is called when a process terminates. The address
space of the process is still intact, but no threads exist in
the process.
Arguments:
ExitStatus - Supplies the ExitStatus of the exiting process.
Return Value:
None.
--*/
{
DBGKM_APIMSG ApiMsg;
PEPROCESS Process;
PETHREAD CurrentThread;
PVOID Port;
PDBGKM_EXIT_PROCESS args;
CurrentThread = PsGetCurrentThread();
Process = PsGetCurrentProcess();
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(CurrentThread);
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
Port = nullptr;
}
else {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
Port = *pDebugObject;
}
if (!Port) {
return;
}
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_DEADTHREAD) {
return;
}
//
// this ensures that other timed lockers of the process will bail
// since this call is done while holding the process lock, and lock duration
// is controlled by debugger
//
PLARGE_INTEGER pExitTime = kDbgUtil::GetProcessExitTime(Process);
KeQuerySystemTime(pExitTime);
args = &ApiMsg.u.ExitProcess;
args->ExitStatus = ExitStatus;
DBGKM_FORMAT_API_MSG(ApiMsg, DbgKmExitProcessApi, sizeof(*args));
DbgkpSendApiMessage(FALSE, &ApiMsg);
}
BOOLEAN NewDbgkForwardException(
_In_ PEXCEPTION_RECORD ExceptionRecord,
_In_ BOOLEAN DebugException,
_In_ BOOLEAN SecondChance
)
/*++
Routine Description:
This function is called forward an exception to the calling process's
debug or subsystem exception port.
Arguments:
ExceptionRecord - Supplies a pointer to an exception record.
DebugException - Supplies a boolean variable that specifies whether
this exception is to be forwarded to the process's
DebugPort(TRUE), or to its ExceptionPort(FALSE).
Return Value:
TRUE - The process has a DebugPort or an ExceptionPort, and the reply
received from the port indicated that the exception was handled.
FALSE - The process either does not have a DebugPort or
ExceptionPort, or the process has a port, but the reply received
from the port indicated that the exception was not handled.
--*/
{
NTSTATUS status;
PEPROCESS Process;
PVOID ExceptionPort = nullptr;
PDEBUG_OBJECT DebugObject = nullptr;
BOOLEAN LpcPort = FALSE;
DBGKM_APIMSG m;
PDBGKM_EXCEPTION args;
args = &m.u.Exception;
//
// Initialize the debug LPC message with default information
//
DBGKM_FORMAT_API_MSG(m, DbgKmExceptionApi, sizeof(*args));
//
// Get the address of the destination LPC port.
//
Process = PsGetCurrentProcess();
if (DebugException) {
PETHREAD CurrentThread = PsGetCurrentThread();
PULONG pCrossThreadFlags = kDbgUtil::GetThreadCrossThreadFlags(CurrentThread);
if (*pCrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
DebugObject = nullptr;
}
else {
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(Process);
DebugObject = *pDebugObject;
}
LpcPort = FALSE;
}
else {
ExceptionPort = kDbgUtil::g_pPsCaptureExceptionPort((PEPROCESS)Process);
m.h.u2.ZeroInit = LPC_EXCEPTION;
LpcPort = TRUE;
}
//
// If the destination LPC port address is NULL, then return FALSE.
//
if (DebugObject == nullptr) {
return FALSE;
}
//
// Fill in the reminder of the debug LPC message
//
args->ExceptionRecord = *ExceptionRecord;
args->FirstChance = !SecondChance;
//
// Send the debug message to the destination LPC port.
//
if (LpcPort) {
status = kDbgUtil::g_pDbgkpSendApiMessageLpc(&m, ExceptionPort, DebugException);
}
else{
status = DbgkpSendApiMessage(DebugException, &m);
}
//
// If the send was not successful, then return a FALSE indicating that
// the port did not handle the exception. Otherwise, if the debug port
// is specified, then look at the return status in the message.
//
if (!NT_SUCCESS(status) ||
(DebugException) &&
(m.ReturnedStatus == DBG_EXCEPTION_NOT_HANDLED || !NT_SUCCESS(m.ReturnedStatus))
) {
if (SecondChance) {
status = kDbgUtil::g_pDbgkpSendErrorMessage(ExceptionRecord, FALSE, &m);
return NT_SUCCESS(status);
}
return FALSE;
}
else {
return TRUE;
}
}
NTSTATUS NewDbgkClearProcessDebugObject(
_In_ PEPROCESS Process,
_In_ PDEBUG_OBJECT SourceDebugObject
)
/*++
Routine Description:
Remove a debug port from a process
Arguments:
Process - Process to be debugged
SourceDebugObject - Debug object to detach
Return Value:
NTSTATUS - Status of call.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEBUG_OBJECT DebugObject = nullptr;
PDEBUG_EVENT DebugEvent;
LIST_ENTRY TempList;
PLIST_ENTRY Entry;
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
PDEBUG_OBJECT* pDebugPort = kDbgUtil::GetProcessDebugPort(Process);
DebugObject = *pDebugPort;
if (DebugObject == nullptr || (DebugObject != SourceDebugObject && SourceDebugObject != nullptr)) {
DebugObject = nullptr;
status = STATUS_PORT_NOT_SET;
}
else {
*pDebugPort = nullptr;
status = STATUS_SUCCESS;
}
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
if (NT_SUCCESS(status)) {
DbgkpMarkProcessPeb(Process);
}
//
// Remove any events for this process and wake up the threads
//
if (DebugObject) {
//
// Remove any events and queue them to a tempory queue
//
InitializeListHead(&TempList);
ExAcquireFastMutex(&DebugObject->Mutex);
for (Entry = DebugObject->EventList.Flink;
Entry != &DebugObject->EventList;
) {
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
Entry = Entry->Flink;
// 一个调试器可以同时调试多个被调试进程
if (DebugEvent->Process == Process) {
RemoveEntryList(&DebugEvent->EventList);
InsertTailList(&TempList, &DebugEvent->EventList);
}
}
ExReleaseFastMutex(&DebugObject->Mutex);
ObDereferenceObject(DebugObject);
//
// Wake up all the removed threads.
//
while (!IsListEmpty(&TempList)) {
Entry = RemoveHeadList(&TempList);
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
kDbgUtil::g_pDbgkpWakeTarget(DebugEvent);
}
}
return status;
}
NTSTATUS NewNtSetInformationDebugObject(
_In_ HANDLE DebugObjectHandle,
_In_ DEBUG_OBJECT_INFORMATION_CLASS DebugObjectInformationClass,
_In_ PVOID DebugInformation,
_In_ ULONG DebugInformationLength,
_Out_opt_ PULONG ReturnLength
)
/*++
Routine Description:
This function sets the state of a debug object.
Arguments:
DebugObjectHandle - Supplies a handle to a process debug object
DebugObjectInformationClass - Supplies the class of information being
set.
DebugInformation - Supplies a pointer to a record that contains the information
to set.
ReturnLength - Supplies the length of the record that contains the information
to set.
Return Value:
NTSTATUS - Status of call
--*/
{
KPROCESSOR_MODE PreviousMode;
ULONG Flags;
NTSTATUS status;
PDEBUG_OBJECT DebugObject;
PreviousMode = ExGetPreviousMode();
__try {
if (PreviousMode != KernelMode) {
if (DebugInformationLength) {
ProbeForRead(DebugInformation, DebugInformationLength,
sizeof(ULONG));
if (ARGUMENT_PRESENT(ReturnLength)) {
ProbeForWriteUlong(ReturnLength);
}
}
}
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = 0;
}
switch (DebugObjectInformationClass) {
case DebugObjectFlagsInformation:
{
if (DebugInformationLength != sizeof(ULONG)) {
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = sizeof(ULONG);
}
return STATUS_INFO_LENGTH_MISMATCH;
}
Flags = *(PULONG)DebugInformation;
break;
}
default:
return STATUS_INVALID_PARAMETER;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
switch (DebugObjectInformationClass)
{
case DebugObjectFlagsInformation:
{
if (Flags & ~DEBUG_KILL_ON_CLOSE) {
return STATUS_INVALID_PARAMETER;
}
status = ObReferenceObjectByHandle(DebugObjectHandle,
DEBUG_OBJECT_SET_INFORMATION,
*DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
nullptr);
if (!NT_SUCCESS(status)) {
return status;
}
ExAcquireFastMutex(&DebugObject->Mutex);
if (Flags & DEBUG_KILL_ON_CLOSE) {
DebugObject->Flags |= DEBUG_OBJECT_KILL_ON_CLOSE;
}
else {
DebugObject->Flags &= ~DEBUG_OBJECT_KILL_ON_CLOSE;
}
ExReleaseFastMutex(&DebugObject->Mutex);
ObDereferenceObject(DebugObject);
}
}
return STATUS_SUCCESS;
}
NTSTATUS NewDbgkCopyProcessDebugPort(
_In_ PEPROCESS TargetProcess,
_In_ PEPROCESS SourceProcess,
_In_ PDEBUG_OBJECT DebugObject,
_Out_ PBOOLEAN bFlag
)
/*++
Routine Description:
Copies a debug port from one process to another.
Arguments:
TargetProcess - Process to move port to
SourceProcess - Process to move port from
--*/
{
PDEBUG_OBJECT* pDebugObject = kDbgUtil::GetProcessDebugPort(TargetProcess);
// New process. Needs no locks.
*pDebugObject = nullptr;
PDEBUG_OBJECT* pSourceDebugPort = kDbgUtil::GetProcessDebugPort(SourceProcess);
if (*pSourceDebugPort != nullptr) {
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
DebugObject = *pSourceDebugPort;
PULONG pFlags = kDbgUtil::GetProcessFlags(SourceProcess);
if (DebugObject != nullptr && (*pFlags & PS_PROCESS_FLAGS_NO_DEBUG_INHERIT) == 0){
//
// We must not propagate a debug port thats got no handles left.
//
ExAcquireFastMutex(&DebugObject->Mutex);
//
// If the object is delete pending then don't propagate this object.
//
if ((DebugObject->Flags & DEBUG_OBJECT_DELETE_PENDING) == 0) {
ObReferenceObject(DebugObject);
*pDebugObject = DebugObject;
}
ExReleaseFastMutex(&DebugObject->Mutex);
}
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
}
return STATUS_SUCCESS;
}
VOID DbgkpCloseObject(
_In_ PEPROCESS Process,
_In_ PVOID Object,
_In_ ACCESS_MASK GrantedAccess,
_In_ ULONG_PTR ProcessHandleCount,
_In_ ULONG_PTR SystemHandleCount
)
/*++
Routine Description:
Called by the object manager when handle is closed to the object.
Arguments:
Process - Process doing the close
Object - Debug object being deleted
GrantedAccess - Access granted for this handle
ProcessHandleCount - Unused and unmaintained by OB
SystemHandleCount - Current handle count for this object
Return Value:
None.
--*/
{
PDEBUG_OBJECT DebugObject = (PDEBUG_OBJECT)Object;
PDEBUG_EVENT DebugEvent;
PLIST_ENTRY Entry;
BOOLEAN Deref;
//
// If this isn't the last then do nothing
//
if (SystemHandleCount > 1) {
return;
}
ExAcquireFastMutex(&DebugObject->Mutex);
//
// Mark this object as going away and wake up any processes that are waiting.
//
DebugObject->Flags |= DEBUG_OBJECT_DELETE_PENDING;
//
// Remove any events and queue them to a temporary queue
//
Entry = DebugObject->EventList.Flink;
InitializeListHead(&DebugObject->EventList);
ExReleaseFastMutex(&DebugObject->Mutex);
//
// Wake anyone waiting. They need to leave this object alone now as its deleting
//
KeSetEvent(&DebugObject->EventsPresent, 0, FALSE);
//
// Loop over all processes and remove the debug port from any that still have it.
// Debug port propagation was disabled by setting the delete pending flag above so we only have to do this
// once. No more refs can appear now.
//
// 枚举系统内的所有进程,如果发现某个进程的DebugObject字段的值与要关闭的对象相同,则将其置为0
for (Process = kDbgUtil::g_pPsGetNextProcess(nullptr); Process != nullptr; Process = kDbgUtil::g_pPsGetNextProcess(Process)) {
PDEBUG_OBJECT* pDebugPort = kDbgUtil::GetProcessDebugPort(Process);
if (*pDebugPort == DebugObject) {
Deref = FALSE;
ExAcquireFastMutex(g_pDbgkpProcessDebugPortMutex);
if (*pDebugPort == DebugObject) {
*pDebugPort = nullptr;
Deref = TRUE;
}
ExReleaseFastMutex(g_pDbgkpProcessDebugPortMutex);
if (Deref) {
DbgkpMarkProcessPeb(Process);
//
// If the caller wanted process deletion on debugger dying (old interface) then kill off the process.
//
if (DebugObject->Flags & DEBUG_OBJECT_KILL_ON_CLOSE) {
kDbgUtil::g_pPsTerminateProcess(Process, STATUS_DEBUGGER_INACTIVE);
}
ObDereferenceObject(DebugObject);
}
}
}
//
// Wake up all removed threads
//
while (Entry != &DebugObject->EventList) {
DebugEvent = CONTAINING_RECORD(Entry, DEBUG_EVENT, EventList);
Entry = Entry->Flink;
DebugEvent->Status = STATUS_DEBUGGER_INACTIVE;
kDbgUtil::g_pDbgkpWakeTarget(DebugEvent);
}
}
VOID ExAcquirePushLockShared(_In_ PEX_PUSH_LOCK_S PushLock)
/*++
Routine Description:
Acquire a push lock shared
Arguments:
PushLock - Push lock to be acquired
--*/ {
EX_PUSH_LOCK_S OldValue, NewValue;
OldValue.Value = 0;
NewValue.Value = EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK;
if (InterlockedCompareExchangePointer(&PushLock->Ptr,
NewValue.Ptr, OldValue.Ptr) != OldValue.Ptr) {
kDbgUtil::g_pExfAcquirePushLockShared((PEX_PUSH_LOCK)PushLock);
}
}
VOID ExReleasePushLockShared(_In_ PEX_PUSH_LOCK_S PushLock)
/*++
Routine Description:
Release a push lock was acquired shared
Arguments:
PushLock - Push lock to be released
--*/
{
EX_PUSH_LOCK_S OldValue, NewValue;
OldValue = *PushLock;
OldValue.Value = EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK;
NewValue.Value = 0;
if (InterlockedCompareExchangePointer(&PushLock->Ptr,
NewValue.Ptr, OldValue.Ptr) != OldValue.Ptr) {
kDbgUtil::g_pExfReleasePushLockShared((PEX_PUSH_LOCK)PushLock);
}
}
VOID DbgkpDeleteObject(_In_ PDEBUG_OBJECT DebugObject) {
IsListEmpty(&DebugObject->EventList);
}