- Fix an embarassing works-for-me but uncommited cast.
 - Add support for reading registers and memory from foreign threads. Highly experimental and nearly untested, use at your own risk.

svn path=/trunk/; revision=64156
This commit is contained in:
Jérôme Gardou 2014-09-14 22:50:10 +00:00
parent d4fd3a072f
commit 6087abc76f
4 changed files with 147 additions and 9 deletions

View file

@ -11,6 +11,8 @@
/* LOCALS *********************************************************************/ /* LOCALS *********************************************************************/
static HANDLE gdb_run_thread; static HANDLE gdb_run_thread;
/* We might have to attach to a process to read its memory */
static PEPROCESS AttachedProcess = NULL;
/* Keep track of where we are for qfThreadInfo/qsThreadInfo */ /* Keep track of where we are for qfThreadInfo/qsThreadInfo */
static LIST_ENTRY* CurrentProcessEntry; static LIST_ENTRY* CurrentProcessEntry;
static LIST_ENTRY* CurrentThreadEntry; static LIST_ENTRY* CurrentThreadEntry;
@ -316,6 +318,13 @@ ReadMemorySendHandler(
send_gdb_memory(MessageData->Buffer, MessageData->Length); send_gdb_memory(MessageData->Buffer, MessageData->Length);
KdpSendPacketHandler = NULL; KdpSendPacketHandler = NULL;
KdpManipulateStateHandler = NULL; KdpManipulateStateHandler = NULL;
/* Detach if we have to */
if (AttachedProcess != NULL)
{
KeDetachProcess();
AttachedProcess = NULL;
}
} }
static static
@ -323,7 +332,8 @@ KDSTATUS
handle_gdb_read_mem( handle_gdb_read_mem(
_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ DBGKD_MANIPULATE_STATE64* State,
_Out_ PSTRING MessageData, _Out_ PSTRING MessageData,
_Out_ PULONG MessageLength) _Out_ PULONG MessageLength,
_Inout_ PKD_CONTEXT KdContext)
{ {
State->ApiNumber = DbgKdReadVirtualMemoryApi; State->ApiNumber = DbgKdReadVirtualMemoryApi;
State->ReturnStatus = STATUS_SUCCESS; /* ? */ State->ReturnStatus = STATUS_SUCCESS; /* ? */
@ -333,6 +343,19 @@ handle_gdb_read_mem(
MessageData->Length = 0; MessageData->Length = 0;
*MessageLength = 0; *MessageLength = 0;
/* Attach to the debug process to read its memory */
if (gdb_dbg_process != PsGetCurrentProcessId())
{
NTSTATUS Status = PsLookupProcessByProcessId(gdb_dbg_process, &AttachedProcess);
if (!NT_SUCCESS(Status))
{
KDDBGPRINT("The current GDB debug thread is invalid!");
send_gdb_packet("E03");
return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
}
KeAttachProcess(&AttachedProcess->Pcb);
}
State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]); State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1); State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
@ -416,7 +439,7 @@ gdb_interpret_input(
handle_gdb_set_thread(); handle_gdb_set_thread();
break; break;
case 'm': case 'm':
return handle_gdb_read_mem(State, MessageData, MessageLength); return handle_gdb_read_mem(State, MessageData, MessageLength, KdContext);
case 'p': case 'p':
return gdb_send_register(State, MessageData, MessageLength, KdContext); return gdb_send_register(State, MessageData, MessageLength, KdContext);
case 'q': case 'q':

View file

@ -7,6 +7,8 @@
#include "kdgdb.h" #include "kdgdb.h"
#include <pstypes.h>
enum reg_name enum reg_name
{ {
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
@ -79,6 +81,44 @@ ctx_to_reg(CONTEXT* ctx, enum reg_name name, unsigned short* size)
return 0; return 0;
} }
static
void*
thread_to_reg(PETHREAD Thread, enum reg_name reg_name, unsigned short* size)
{
PKTRAP_FRAME TrapFrame = Thread->Tcb.TrapFrame;
/* See if the thread was actually scheduled */
if (TrapFrame == NULL)
{
return NULL;
}
*size = 4;
switch (reg_name)
{
case EAX: return &TrapFrame->Eax;
case ECX: return &TrapFrame->Ecx;
case EDX: return &TrapFrame->Edx;
case EBX: return &TrapFrame->Ebx;
case ESP: return (TrapFrame->PreviousPreviousMode == KernelMode) ?
&TrapFrame->TempEsp : &TrapFrame->HardwareEsp;
case EBP: return &TrapFrame->Ebp;
case ESI: return &TrapFrame->Esi;
case EDI: return &TrapFrame->Edi;
case EIP: return &TrapFrame->Eip;
case EFLAGS: return &TrapFrame->EFlags;
case CS: return &TrapFrame->SegCs;
case SS: return &TrapFrame->HardwareSegSs;
case DS: return &TrapFrame->SegDs;
case ES: return &TrapFrame->SegEs;
case FS: return &TrapFrame->SegFs;
case GS: return &TrapFrame->SegGs;
default:
KDDBGPRINT("Unhandled regname: %d.\n", reg_name);
}
return NULL;
}
KDSTATUS KDSTATUS
gdb_send_registers( gdb_send_registers(
_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ DBGKD_MANIPULATE_STATE64* State,
@ -86,15 +126,62 @@ gdb_send_registers(
_Out_ PULONG MessageLength, _Out_ PULONG MessageLength,
_Inout_ PKD_CONTEXT KdContext) _Inout_ PKD_CONTEXT KdContext)
{ {
ULONG32 Registers[16]; CHAR Registers[16*8 + 1];
UCHAR* RegisterPtr;
unsigned i; unsigned i;
unsigned short size; unsigned short size;
CHAR* ptr = Registers;
for(i=0; i < 16; i++) if (gdb_dbg_thread == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread))
{ {
Registers[i] = *(ULONG32*)ctx_to_reg(&CurrentContext, i, &size); for(i=0; i < 16; i++)
{
RegisterPtr = ctx_to_reg(&CurrentContext, i, &size);
*ptr++ = hex_chars[RegisterPtr[0] >> 4];
*ptr++ = hex_chars[RegisterPtr[0] & 0xF];
*ptr++ = hex_chars[RegisterPtr[1] >> 4];
*ptr++ = hex_chars[RegisterPtr[1] & 0xF];
*ptr++ = hex_chars[RegisterPtr[2] >> 4];
*ptr++ = hex_chars[RegisterPtr[2] & 0xF];
*ptr++ = hex_chars[RegisterPtr[3] >> 4];
*ptr++ = hex_chars[RegisterPtr[3] & 0xF];
}
} }
send_gdb_memory(Registers, sizeof(Registers)); else
{
PETHREAD DbgThread;
NTSTATUS Status;
Status = PsLookupThreadByThreadId(gdb_dbg_thread, &DbgThread);
if (!NT_SUCCESS(Status))
{
/* Thread is dead */
send_gdb_packet("E03");
return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
}
for(i=0; i < 16; i++)
{
RegisterPtr = thread_to_reg(DbgThread, i, &size);
if (RegisterPtr)
{
*ptr++ = hex_chars[RegisterPtr[0] >> 4];
*ptr++ = hex_chars[RegisterPtr[0] & 0xF];
*ptr++ = hex_chars[RegisterPtr[1] >> 4];
*ptr++ = hex_chars[RegisterPtr[1] & 0xF];
*ptr++ = hex_chars[RegisterPtr[2] >> 4];
*ptr++ = hex_chars[RegisterPtr[2] & 0xF];
*ptr++ = hex_chars[RegisterPtr[3] >> 4];
*ptr++ = hex_chars[RegisterPtr[3] & 0xF];
}
else
{
ptr += sprintf(ptr, "xxxxxxxx");
}
}
}
*ptr = '\0';
send_gdb_packet(Registers);
return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext); return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
} }
@ -112,7 +199,27 @@ gdb_send_register(
/* Get the GDB register name (gdb_input = "pXX") */ /* Get the GDB register name (gdb_input = "pXX") */
reg_name = (hex_value(gdb_input[1]) << 4) | hex_value(gdb_input[2]); reg_name = (hex_value(gdb_input[1]) << 4) | hex_value(gdb_input[2]);
ptr = ctx_to_reg(&CurrentContext, reg_name, &size); if (gdb_dbg_thread == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread))
{
/* We can get it from the context of the current exception */
ptr = ctx_to_reg(&CurrentContext, reg_name, &size);
}
else
{
PETHREAD DbgThread;
NTSTATUS Status;
Status = PsLookupThreadByThreadId(gdb_dbg_thread, &DbgThread);
if (!NT_SUCCESS(Status))
{
/* Thread is dead */
send_gdb_packet("E03");
return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
}
ptr = thread_to_reg(DbgThread, reg_name, &size);
}
if (!ptr) if (!ptr)
{ {
/* Undefined. Let's assume 32 bit register */ /* Undefined. Let's assume 32 bit register */
@ -122,5 +229,6 @@ gdb_send_register(
{ {
send_gdb_memory(ptr, size); send_gdb_memory(ptr, size);
} }
return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext); return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
} }

View file

@ -67,6 +67,7 @@ void send_gdb_memory(_In_ VOID* Buffer, size_t Length);
void gdb_send_debug_io(_In_ PSTRING String); void gdb_send_debug_io(_In_ PSTRING String);
void gdb_send_exception(void); void gdb_send_exception(void);
void send_gdb_ntstatus(_In_ NTSTATUS Status); void send_gdb_ntstatus(_In_ NTSTATUS Status);
extern const char hex_chars[];
/* kdcom.c */ /* kdcom.c */
KDSTATUS NTAPI KdpPollBreakIn(VOID); KDSTATUS NTAPI KdpPollBreakIn(VOID);

View file

@ -14,6 +14,7 @@ FirstSendHandler(
_In_ ULONG PacketType, _In_ ULONG PacketType,
_In_ PSTRING MessageHeader, _In_ PSTRING MessageHeader,
_In_ PSTRING MessageData); _In_ PSTRING MessageData);
static BOOLEAN CanSendData = FALSE;
/* GLOBALS ********************************************************************/ /* GLOBALS ********************************************************************/
DBGKD_GET_VERSION64 KdVersion; DBGKD_GET_VERSION64 KdVersion;
@ -141,11 +142,13 @@ send_kd_state_change(DBGKD_ANY_WAIT_STATE_CHANGE* StateChange)
/* Save current state for later GDB queries */ /* Save current state for later GDB queries */
CurrentStateChange = *StateChange; CurrentStateChange = *StateChange;
/* Unless GDB tells us otherwise, those are what we should have */ /* Unless GDB tells us otherwise, those are what we should have */
gdb_dbg_thread = PsGetThreadId((PETHREAD)StateChange->Thread); gdb_dbg_thread = PsGetThreadId((PETHREAD)(ULONG_PTR)StateChange->Thread);
gdb_dbg_process = PsGetThreadProcessId((PETHREAD)StateChange->Thread); gdb_dbg_process = PsGetThreadProcessId((PETHREAD)(ULONG_PTR)StateChange->Thread);
gdb_send_exception(); gdb_send_exception();
/* Next receive call will ask for the context */ /* Next receive call will ask for the context */
KdpManipulateStateHandler = GetContextManipulateHandler; KdpManipulateStateHandler = GetContextManipulateHandler;
/* We can now send data, since after this we will be connected to GDB */
CanSendData = TRUE;
break; break;
default: default:
/* FIXME */ /* FIXME */
@ -159,6 +162,9 @@ send_kd_debug_io(
_In_ DBGKD_DEBUG_IO* DebugIO, _In_ DBGKD_DEBUG_IO* DebugIO,
_In_ PSTRING String) _In_ PSTRING String)
{ {
if (!CanSendData)
return;
switch (DebugIO->ApiNumber) switch (DebugIO->ApiNumber)
{ {
case DbgKdPrintStringApi: case DbgKdPrintStringApi: