Implement the WinDBG Debugger Worker Thread, which is created inside a target process whenever one uses the ".process /i <addr>" (invasive debugging) command using WinDBG in kernel-mode debugging.
See https://www.microsoftpressstore.com/articles/article.aspx?p=2201303&seqNum=2 section "Switching the Current Process Context" (from the book "Inside Windows Debugging" by Tarik Soulami, pp.101-102) for more details.
The names of the variables must be kept as they are, because WinDBG (more exactly, dbgeng.dll) searches for them within the kernel symbols and retrieves their addresses (since those variables are not publicly exported).

svn path=/trunk/; revision=68851
This commit is contained in:
Hermès Bélusca-Maïto 2015-08-29 02:03:56 +00:00
parent a0a1967e8e
commit dd088b28fc
4 changed files with 157 additions and 2 deletions

View file

@ -12,8 +12,132 @@
#define NDEBUG
#include <debug.h>
/* DATA **********************************************************************/
// #ifdef _WINKD_
/*
* WinDBG Debugger Worker Thread data
*/
WORK_QUEUE_ITEM ExpDebuggerWorkItem;
/*
* The following global variables must be visible through all the kernel
* because WinDBG explicitely search for them inside our symbols.
*/
WINKD_WORKER_STATE ExpDebuggerWork;
PEPROCESS ExpDebuggerProcessAttach;
PEPROCESS ExpDebuggerProcessKill;
ULONG_PTR ExpDebuggerPageIn;
// #endif /* _WINKD_ */
/* FUNCTIONS *****************************************************************/
// #ifdef _WINKD_
/*
* WinDBG Debugger Worker Thread
*
* A worker thread is queued whenever WinDBG wants to attach or kill a user-mode
* process from within live kernel-mode session, and/or page-in an address region.
*
* WinDBG commands:
* .process /i <addr> (where <addr> is the address of the EPROCESS block for this process)
* .kill <addr> ( " " " " )
* .pagein <addr> (where <addr> is the address to page in)
*
* The implementation is very naive because the same data is reused, so that if
* the worker thread has not started before WinDBG sends fresh new data again,
* then only the latest data is taken into account.
*/
VOID
NTAPI
ExpDebuggerWorker(IN PVOID Context)
{
PEPROCESS ProcessToAttach, ProcessToKill;
ULONG_PTR PageInAddress;
PEPROCESS Process;
KAPC_STATE ApcState;
UNREFERENCED_PARAMETER(Context);
/* Be sure we were started in an initialized state */
ASSERTMSG("ExpDebuggerWorker being entered with state != 2\n",
ExpDebuggerWork == WinKdWorkerInitialized);
if (ExpDebuggerWork != WinKdWorkerInitialized) return;
/* Reset the worker flag to the disabled state */
ExpDebuggerWork = WinKdWorkerDisabled;
/* Get the processes to be attached or killed, and the address to page in */
ProcessToAttach = ExpDebuggerProcessAttach;
ProcessToKill = ExpDebuggerProcessKill;
PageInAddress = ExpDebuggerPageIn;
/* Reset to their default values */
ExpDebuggerProcessAttach = NULL;
ExpDebuggerProcessKill = NULL;
ExpDebuggerPageIn = (ULONG_PTR)NULL;
/* Default to the current process if we don't find the process to be attached or killed */
Process = NULL;
/* Check if we need to attach or kill some process */
if (ProcessToAttach != NULL || ProcessToKill != NULL)
{
/* Find the process in the list */
Process = PsGetNextProcess(Process);
while (Process)
{
/* Is this the process we want to attach to? */
if (Process == ProcessToAttach)
{
/* Yes, attach ourselves to it */
KeStackAttachProcess(&Process->Pcb, &ApcState);
break;
}
/* Or is this the process we want to kill? */
else if (Process == ProcessToKill)
{
/* Yes, kill and dereference it, then return */
PsTerminateProcess(Process, DBG_TERMINATE_PROCESS);
ObDereferenceObject(Process);
return;
}
/* Get the next process */
Process = PsGetNextProcess(Process);
}
/* We either have found a process, or we default to the current process */
}
/* If we have an address to page in... */
if (PageInAddress)
{
/* ... try to do it by attempting to read at this address */
_SEH2_TRY
{
ProbeForReadUchar(PageInAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("Failed to page in address 0x%p, Status 0x%08lx\n", PageInAddress, _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Break into the process (or the current one if Process == NULL) */
DbgBreakPointWithStatus(DBG_STATUS_WORKER);
/* If we are attached to a process, not the current one... */
if (Process)
{
/* ... we can detach from the process */
KeUnstackDetachProcess(&ApcState);
/* Dereference the process which was referenced for us by PsGetNextProcess */
ObDereferenceObject(Process);
}
}
// #endif /* _WINKD_ */
/*++
* @name NtSystemDebugControl
* @implemented

View file

@ -485,6 +485,19 @@ ExpWorkerThreadBalanceManager(IN PVOID Context)
ObDereferenceObject(ExpLastWorkerThread);
PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
}
// #ifdef _WINKD_
/*
* If WinDBG wants to attach or kill a user-mode process, and/or
* page-in an address region, queue a debugger worker thread.
*/
if (ExpDebuggerWork == WinKdWorkerActivate)
{
ExInitializeWorkItem(&ExpDebuggerWorkItem, ExpDebuggerWorker, NULL);
ExpDebuggerWork = WinKdWorkerInitialized;
ExQueueWorkItem(&ExpDebuggerWorkItem, DelayedWorkQueue);
}
// #endif /* _WINKD_ */
}
}
@ -744,4 +757,3 @@ ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem,
}
/* EOF */

View file

@ -141,7 +141,7 @@ DbgkOpenProcessDebugPort(
IN KPROCESSOR_MODE PreviousMode,
OUT HANDLE *DebugHandle
);
extern ULONG DbgkpTraceLevel;
extern POBJECT_TYPE DbgkDebugObjectType;

View file

@ -42,6 +42,25 @@ extern UNICODE_STRING CmVersionString;
extern UNICODE_STRING CmCSDVersionString;
extern CHAR NtBuildLab[];
// #ifdef _WINKD_
/*
* WinDBG Debugger Worker Thread data (see dbgctrl.c)
*/
typedef enum _WINKD_WORKER_STATE
{
WinKdWorkerDisabled = 0,
WinKdWorkerActivate,
WinKdWorkerInitialized
} WINKD_WORKER_STATE;
extern WORK_QUEUE_ITEM ExpDebuggerWorkItem;
extern WINKD_WORKER_STATE ExpDebuggerWork;
extern PEPROCESS ExpDebuggerProcessAttach;
extern PEPROCESS ExpDebuggerProcessKill;
extern ULONG_PTR ExpDebuggerPageIn;
VOID NTAPI ExpDebuggerWorker(IN PVOID Context);
// #endif /* _WINKD_ */
#ifdef _WIN64
#define HANDLE_LOW_BITS (PAGE_SHIFT - 4)
#define HANDLE_HIGH_BITS (PAGE_SHIFT - 3)