You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4302 lines
102 KiB
4302 lines
102 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// IDebugSystemObjects implementations.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1999-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Utility functions.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
X86DescriptorToDescriptor64(PX86_LDT_ENTRY X86Desc,
|
|
PDESCRIPTOR64 Desc64)
|
|
{
|
|
Desc64->Base = EXTEND64((ULONG)X86Desc->BaseLow +
|
|
((ULONG)X86Desc->HighWord.Bits.BaseMid << 16) +
|
|
((ULONG)X86Desc->HighWord.Bytes.BaseHi << 24));
|
|
Desc64->Limit = (ULONG)X86Desc->LimitLow +
|
|
((ULONG)X86Desc->HighWord.Bits.LimitHi << 16);
|
|
if (X86Desc->HighWord.Bits.Granularity)
|
|
{
|
|
Desc64->Limit = (Desc64->Limit << X86_PAGE_SHIFT) +
|
|
((1 << X86_PAGE_SHIFT) - 1);
|
|
}
|
|
Desc64->Flags = (ULONG)X86Desc->HighWord.Bytes.Flags1 +
|
|
(((ULONG)X86Desc->HighWord.Bytes.Flags2 >> 4) << 8);
|
|
}
|
|
|
|
HRESULT
|
|
ReadX86Descriptor(TargetInfo* Target, ProcessInfo* Process,
|
|
ULONG Selector, ULONG64 Base, ULONG Limit,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
ULONG Index;
|
|
|
|
// Mask off irrelevant bits
|
|
Index = Selector & ~0x7;
|
|
|
|
// Check to make sure that the selector is within the table bounds
|
|
if (Index > Limit)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT Status;
|
|
X86_LDT_ENTRY X86Desc;
|
|
|
|
Status = Target->
|
|
ReadAllVirtual(Process, Base + Index, &X86Desc, sizeof(X86Desc));
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
X86DescriptorToDescriptor64(&X86Desc, Desc);
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// TargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
TargetInfo::GetProcessorSystemDataOffset(
|
|
IN ULONG Processor,
|
|
IN ULONG Index,
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
ThreadInfo* Thread;
|
|
|
|
if (!IS_KERNEL_TARGET(this))
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// XXX drewb - Temporary until different OS support is
|
|
// sorted out.
|
|
if (m_ActualSystemVersion < NT_SVER_START ||
|
|
m_ActualSystemVersion > NT_SVER_END)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
Thread = m_ProcessHead->
|
|
FindThreadByHandle(VIRTUAL_THREAD_HANDLE(Processor));
|
|
if (!Thread)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT Status;
|
|
ULONG Read;
|
|
|
|
if (m_MachineType == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
DESCRIPTOR64 Entry;
|
|
|
|
//
|
|
// We always need the PCR address so go ahead and get it.
|
|
//
|
|
|
|
if (!IS_CONTEXT_POSSIBLE(this))
|
|
{
|
|
X86_DESCRIPTOR GdtDesc;
|
|
|
|
// We can't go through the normal context segreg mapping
|
|
// but all we really need is an entry from the
|
|
// kernel GDT that should always be present and
|
|
// constant while the system is initialized. We
|
|
// can get the GDT information from the x86 control
|
|
// space so do that.
|
|
if ((Status = ReadControl
|
|
(Processor,
|
|
m_TypeInfo.OffsetSpecialRegisters +
|
|
FIELD_OFFSET(X86_KSPECIAL_REGISTERS, Gdtr),
|
|
&GdtDesc, sizeof(GdtDesc), &Read)) != S_OK ||
|
|
Read != sizeof(GdtDesc) ||
|
|
(Status = ReadX86Descriptor(this, m_ProcessHead,
|
|
m_KdDebuggerData.GdtR0Pcr,
|
|
EXTEND64(GdtDesc.Base),
|
|
GdtDesc.Limit, &Entry)) != S_OK)
|
|
{
|
|
ErrOut("Unable to read selector for PCR for processor %u\n",
|
|
Processor);
|
|
return Status != S_OK ?
|
|
Status : HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((Status = GetSelDescriptor(Thread, m_Machine,
|
|
m_KdDebuggerData.GdtR0Pcr,
|
|
&Entry)) != S_OK)
|
|
{
|
|
ErrOut("Unable to read selector for PCR for processor %u\n",
|
|
Processor);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
switch(Index)
|
|
{
|
|
case DEBUG_DATA_KPCR_OFFSET:
|
|
|
|
Status = ReadPointer(m_ProcessHead, m_Machine,
|
|
Entry.Base +
|
|
m_KdDebuggerData.OffsetPcrSelfPcr,
|
|
Offset);
|
|
if ((Status != S_OK) || Entry.Base != *Offset)
|
|
{
|
|
ErrOut("KPCR is corrupted !");
|
|
}
|
|
|
|
*Offset = Entry.Base;
|
|
break;
|
|
|
|
case DEBUG_DATA_KPRCB_OFFSET:
|
|
case DEBUG_DATA_KTHREAD_OFFSET:
|
|
Status = ReadPointer(m_ProcessHead, m_Machine,
|
|
Entry.Base +
|
|
m_KdDebuggerData.OffsetPcrCurrentPrcb,
|
|
Offset);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (Index == DEBUG_DATA_KPRCB_OFFSET)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = ReadPointer(m_ProcessHead, m_Machine,
|
|
*Offset +
|
|
m_KdDebuggerData.OffsetPrcbCurrentThread,
|
|
Offset);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG ReadSize = m_Machine->m_Ptr64 ?
|
|
sizeof(ULONG64) : sizeof(ULONG);
|
|
ULONG64 Address;
|
|
|
|
switch(m_MachineType)
|
|
{
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
switch(Index)
|
|
{
|
|
case DEBUG_DATA_KPCR_OFFSET:
|
|
Index = IA64_DEBUG_CONTROL_SPACE_PCR;
|
|
break;
|
|
case DEBUG_DATA_KPRCB_OFFSET:
|
|
Index = IA64_DEBUG_CONTROL_SPACE_PRCB;
|
|
break;
|
|
case DEBUG_DATA_KTHREAD_OFFSET:
|
|
Index = IA64_DEBUG_CONTROL_SPACE_THREAD;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
switch(Index)
|
|
{
|
|
case DEBUG_DATA_KPCR_OFFSET:
|
|
Index = AMD64_DEBUG_CONTROL_SPACE_PCR;
|
|
break;
|
|
case DEBUG_DATA_KPRCB_OFFSET:
|
|
Index = AMD64_DEBUG_CONTROL_SPACE_PRCB;
|
|
break;
|
|
case DEBUG_DATA_KTHREAD_OFFSET:
|
|
Index = AMD64_DEBUG_CONTROL_SPACE_THREAD;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Status = ReadControl(Processor, Index, &Address, ReadSize, &Read);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
else if (Read != ReadSize)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
if (!m_Machine->m_Ptr64)
|
|
{
|
|
Address = EXTEND64(Address);
|
|
}
|
|
|
|
*Offset = Address;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetTargetSegRegDescriptors(ULONG64 Thread,
|
|
ULONG Start, ULONG Count,
|
|
PDESCRIPTOR64 Descs)
|
|
{
|
|
while (Count-- > 0)
|
|
{
|
|
Descs->Flags = SEGDESC_INVALID;
|
|
Descs++;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetTargetSpecialRegisters
|
|
(ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Done;
|
|
|
|
Status = ReadControl(VIRTUAL_THREAD_INDEX(Thread),
|
|
m_TypeInfo.OffsetSpecialRegisters,
|
|
Special,
|
|
m_TypeInfo.SizeKspecialRegisters,
|
|
&Done);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
return Done == m_TypeInfo.SizeKspecialRegisters ?
|
|
S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::SetTargetSpecialRegisters
|
|
(ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Done;
|
|
|
|
Status = WriteControl(VIRTUAL_THREAD_INDEX(Thread),
|
|
m_TypeInfo.OffsetSpecialRegisters,
|
|
Special,
|
|
m_TypeInfo.SizeKspecialRegisters,
|
|
&Done);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
return Done == m_TypeInfo.SizeKspecialRegisters ?
|
|
S_OK : E_FAIL;
|
|
}
|
|
|
|
void
|
|
TargetInfo::InvalidateTargetContext(void)
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetThreadStartOffset(ThreadInfo* Thread,
|
|
PULONG64 StartOffset)
|
|
{
|
|
// Base implementation to indicate no information available.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
void
|
|
TargetInfo::SuspendThreads(void)
|
|
{
|
|
// Base implementation for targets that can't suspend threads.
|
|
}
|
|
|
|
BOOL
|
|
TargetInfo::ResumeThreads(void)
|
|
{
|
|
ThreadInfo* Thread;
|
|
|
|
// Base implementation for targets that can't suspend threads.
|
|
//
|
|
// Wipe out all cached thread data offsets in
|
|
// case operations after the resumption invalidates
|
|
// the cached values.
|
|
for (Thread = m_ProcessHead ? m_ProcessHead->m_ThreadHead : NULL;
|
|
Thread != NULL;
|
|
Thread = Thread->m_Next)
|
|
{
|
|
Thread->m_DataOffset = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetContext(
|
|
ULONG64 Thread,
|
|
PCROSS_PLATFORM_CONTEXT Context
|
|
)
|
|
{
|
|
if (m_Machine == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT Status;
|
|
CROSS_PLATFORM_CONTEXT TargetContextBuffer;
|
|
PCROSS_PLATFORM_CONTEXT TargetContext;
|
|
|
|
if (m_Machine->m_SverCanonicalContext <=
|
|
m_SystemVersion)
|
|
{
|
|
TargetContext = Context;
|
|
}
|
|
else
|
|
{
|
|
TargetContext = &TargetContextBuffer;
|
|
m_Machine->
|
|
InitializeContextFlags(TargetContext,
|
|
m_SystemVersion);
|
|
}
|
|
|
|
Status = GetTargetContext(Thread, TargetContext);
|
|
|
|
if (Status == S_OK && TargetContext == &TargetContextBuffer)
|
|
{
|
|
Status = m_Machine->
|
|
ConvertContextFrom(Context, m_SystemVersion,
|
|
m_TypeInfo.SizeTargetContext,
|
|
TargetContext);
|
|
// Conversion should always succeed since the size is
|
|
// known to be valid.
|
|
DBG_ASSERT(Status == S_OK);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::SetContext(
|
|
ULONG64 Thread,
|
|
PCROSS_PLATFORM_CONTEXT Context
|
|
)
|
|
{
|
|
if (m_Machine == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
CROSS_PLATFORM_CONTEXT TargetContextBuffer;
|
|
PCROSS_PLATFORM_CONTEXT TargetContext;
|
|
|
|
if (m_Machine->m_SverCanonicalContext <=
|
|
m_SystemVersion)
|
|
{
|
|
TargetContext = Context;
|
|
}
|
|
else
|
|
{
|
|
TargetContext = &TargetContextBuffer;
|
|
HRESULT Status = m_Machine->
|
|
ConvertContextTo(Context, m_SystemVersion,
|
|
m_TypeInfo.SizeTargetContext,
|
|
TargetContext);
|
|
// Conversion should always succeed since the size is
|
|
// known to be valid.
|
|
DBG_ASSERT(Status == S_OK);
|
|
}
|
|
|
|
return SetTargetContext(Thread, TargetContext);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetContextFromThreadStack(ULONG64 ThreadBase,
|
|
PCROSS_PLATFORM_CONTEXT Context,
|
|
BOOL Verbose)
|
|
{
|
|
DBG_ASSERT(ThreadBase && Context != NULL);
|
|
|
|
if (!IS_KERNEL_TARGET(this))
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT Status;
|
|
UCHAR Type;
|
|
ULONG Proc = 0;
|
|
UCHAR State;
|
|
ULONG64 Stack;
|
|
|
|
Status = ReadAllVirtual(m_ProcessHead, ThreadBase, &Type, sizeof(Type));
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Cannot read thread type from thread %s, %s\n",
|
|
FormatMachineAddr64(m_Machine, ThreadBase),
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
if (Type != 6)
|
|
{
|
|
ErrOut("Invalid thread @ %s type - context unchanged.\n",
|
|
FormatMachineAddr64(m_Machine, ThreadBase));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Check to see if the thread is currently running.
|
|
//
|
|
|
|
Status = ReadAllVirtual(m_ProcessHead,
|
|
ThreadBase + m_KdDebuggerData.OffsetKThreadState,
|
|
&State, sizeof(State));
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Cannot read thread stack from thread %s, %s\n",
|
|
FormatMachineAddr64(m_Machine, ThreadBase),
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
if (State != 2)
|
|
{
|
|
// thread is not running.
|
|
|
|
Status = ReadPointer(m_ProcessHead, m_Machine,
|
|
ThreadBase +
|
|
m_KdDebuggerData.OffsetKThreadKernelStack,
|
|
&Stack);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Cannot read thread stack from thread %s, %s\n",
|
|
FormatMachineAddr64(m_Machine, ThreadBase),
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = m_Machine->GetContextFromThreadStack
|
|
(ThreadBase, Context, Stack);
|
|
}
|
|
else
|
|
{
|
|
if ((Status =
|
|
ReadAllVirtual(m_ProcessHead,
|
|
ThreadBase +
|
|
m_KdDebuggerData.OffsetKThreadNextProcessor,
|
|
&Proc, 1)) != S_OK)
|
|
{
|
|
ErrOut("Cannot read processor number from thread %s, %s\n",
|
|
FormatMachineAddr64(m_Machine, ThreadBase),
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
// Get the processor context if it's a valid processor.
|
|
if (Proc < m_NumProcessors)
|
|
{
|
|
// This get may be getting the context of the thread
|
|
// currently cached by the register code. Make sure
|
|
// the cache is flushed.
|
|
FlushRegContext();
|
|
|
|
m_Machine->
|
|
InitializeContextFlags(Context, m_SystemVersion);
|
|
if ((Status = GetContext(VIRTUAL_THREAD_HANDLE(Proc),
|
|
Context)) != S_OK)
|
|
{
|
|
ErrOut("Unable to get context for thread "
|
|
"running on processor %d, %s\n",
|
|
Proc, FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Thread running on invalid processor %d\n", Proc);
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Can't retrieve thread context, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::StartAttachProcess(ULONG ProcessId,
|
|
ULONG AttachFlags,
|
|
PPENDING_PROCESS* Pending)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::StartCreateProcess(PWSTR CommandLine,
|
|
ULONG CreateFlags,
|
|
PBOOL InheritHandles,
|
|
PWSTR CurrentDir,
|
|
PPENDING_PROCESS* Pending)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::TerminateProcesses(void)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::DetachProcesses(void)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::EmulateNtX86SelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
// Only emulate on platforms that support segments.
|
|
if (Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_I386 &&
|
|
Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_AMD64)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
ULONG Type, Gran;
|
|
|
|
//
|
|
// For user mode and triage dumps, we can hardcode the selector
|
|
// since we do not have it anywhere.
|
|
// XXX drewb - How many should we fake? There are quite
|
|
// a few KGDT entries. Which ones are valid in user mode and
|
|
// which are valid for a triage dump?
|
|
//
|
|
|
|
if (Selector == m_KdDebuggerData.GdtR3Teb)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// In user mode fs points to the TEB so fake up
|
|
// a selector for it.
|
|
if ((Status = Thread->m_Process->
|
|
GetImplicitThreadDataTeb(Thread, &Desc->Base)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (Machine != m_Machine)
|
|
{
|
|
ULONG Read;
|
|
|
|
// We're asking for an emulated machine's TEB.
|
|
// The only case we currently handle is x86-on-IA64
|
|
// for Wow64, where the 32-bit TEB pointer is
|
|
// stored in NT_TIB.ExceptionList.
|
|
// Conveniently, this is the very first entry.
|
|
if ((Status = ReadVirtual(Thread->m_Process,
|
|
Desc->Base, &Desc->Base,
|
|
sizeof(ULONG), &Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Read != sizeof(ULONG))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
Desc->Base = EXTEND64(Desc->Base);
|
|
}
|
|
|
|
Desc->Limit = Machine->m_PageSize - 1;
|
|
Type = 0x13;
|
|
Gran = 0;
|
|
}
|
|
else if (Selector == m_KdDebuggerData.GdtR3Data)
|
|
{
|
|
Desc->Base = 0;
|
|
Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
|
|
Type = 0x13;
|
|
Gran = X86_DESC_GRANULARITY;
|
|
}
|
|
else
|
|
{
|
|
// Assume it's a code segment.
|
|
Desc->Base = 0;
|
|
Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
|
|
Type = 0x1b;
|
|
Gran = X86_DESC_GRANULARITY |
|
|
(Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64 ?
|
|
X86_DESC_LONG_MODE : 0);
|
|
}
|
|
|
|
Desc->Flags = Gran | X86_DESC_DEFAULT_BIG | X86_DESC_PRESENT | Type |
|
|
(IS_USER_TARGET(this) ?
|
|
(3 << X86_DESC_PRIVILEGE_SHIFT) : (Selector & 3));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::EmulateNtAmd64SelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
if (Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_AMD64)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
ULONG Type, Gran;
|
|
|
|
//
|
|
// XXX drewb - How many should we fake? There are quite
|
|
// a few KGDT64 entries. Which ones are valid in user mode and
|
|
// which are valid for a triage dump?
|
|
//
|
|
|
|
if (Selector == m_KdDebuggerData.Gdt64R3CmTeb)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// In user mode fs points to the TEB so fake up
|
|
// a selector for it.
|
|
if ((Status = Thread->m_Process->
|
|
GetImplicitThreadDataTeb(Thread, &Desc->Base)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (Machine != m_Machine)
|
|
{
|
|
ULONG Read;
|
|
|
|
// We're asking for an emulated machine's TEB.
|
|
// The only case we currently handle is x86-on-IA64
|
|
// for Wow64, where the 32-bit TEB pointer is
|
|
// stored in NT_TIB.ExceptionList.
|
|
// Conveniently, this is the very first entry.
|
|
if ((Status = ReadVirtual(Thread->m_Process,
|
|
Desc->Base, &Desc->Base,
|
|
sizeof(ULONG), &Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Read != sizeof(ULONG))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
Desc->Base = EXTEND64(Desc->Base);
|
|
}
|
|
|
|
Desc->Limit = Machine->m_PageSize - 1;
|
|
Type = 0x13;
|
|
Gran = 0;
|
|
}
|
|
else if (Selector == m_KdDebuggerData.GdtR3Data)
|
|
{
|
|
Desc->Base = 0;
|
|
Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
|
|
Type = 0x13;
|
|
Gran = X86_DESC_GRANULARITY;
|
|
}
|
|
else if (Selector == m_KdDebuggerData.Gdt64R3CmCode)
|
|
{
|
|
Desc->Base = 0;
|
|
Desc->Limit = 0xffffffff;
|
|
Type = 0x1b;
|
|
Gran = X86_DESC_GRANULARITY;
|
|
}
|
|
else
|
|
{
|
|
// Assume it's a code segment.
|
|
Desc->Base = 0;
|
|
Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
|
|
Type = 0x1b;
|
|
Gran = X86_DESC_GRANULARITY |
|
|
(Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64 ?
|
|
X86_DESC_LONG_MODE : 0);
|
|
}
|
|
|
|
Desc->Flags = Gran | X86_DESC_DEFAULT_BIG | X86_DESC_PRESENT | Type |
|
|
(IS_USER_TARGET(this) ?
|
|
(3 << X86_DESC_PRIVILEGE_SHIFT) : (Selector & 3));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::EmulateNtSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
switch(Machine->m_ExecTypes[0])
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
return EmulateNtX86SelDescriptor(Thread, Machine, Selector, Desc);
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
return EmulateNtAmd64SelDescriptor(Thread, Machine, Selector, Desc);
|
|
default:
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetImplicitProcessData(ThreadInfo* Thread, PULONG64 Offset)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (m_ImplicitProcessData == 0 ||
|
|
(m_ImplicitProcessDataIsDefault &&
|
|
Thread != m_ImplicitProcessDataThread))
|
|
{
|
|
Status = SetImplicitProcessData(Thread, 0, FALSE);
|
|
}
|
|
else
|
|
{
|
|
Status = S_OK;
|
|
}
|
|
|
|
*Offset = m_ImplicitProcessData;
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetImplicitProcessDataPeb(ThreadInfo* Thread, PULONG64 Peb)
|
|
{
|
|
if (IS_USER_TARGET(this))
|
|
{
|
|
// In user mode the process data is the PEB.
|
|
return GetImplicitProcessData(Thread, Peb);
|
|
}
|
|
else if (IS_KERNEL_TARGET(this))
|
|
{
|
|
return ReadImplicitProcessInfoPointer
|
|
(Thread, m_KdDebuggerData.OffsetEprocessPeb, Peb);
|
|
}
|
|
else
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetImplicitProcessDataParentCID(ThreadInfo* Thread, PULONG64 Pcid)
|
|
{
|
|
if (!IS_KERNEL_TARGET(this))
|
|
{
|
|
// In user mode we don't need the parent process ...
|
|
return E_NOTIMPL;
|
|
}
|
|
else
|
|
{
|
|
return ReadImplicitProcessInfoPointer
|
|
(Thread, m_KdDebuggerData.OffsetEprocessParentCID, Pcid);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::SetImplicitProcessData(ThreadInfo* Thread,
|
|
ULONG64 Offset, BOOL Verbose)
|
|
{
|
|
HRESULT Status;
|
|
BOOL Default = FALSE;
|
|
|
|
if (Offset == 0)
|
|
{
|
|
if (!Thread || Thread->m_Process->m_Target != this)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Unable to get the current thread data\n");
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if ((Status = GetProcessInfoDataOffset(Thread, 0, 0, &Offset)) != S_OK)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Unable to get the current process data\n");
|
|
}
|
|
return Status;
|
|
}
|
|
if (Offset == 0)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Current process data is NULL\n");
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
Default = TRUE;
|
|
}
|
|
|
|
ULONG64 Old = m_ImplicitProcessData;
|
|
BOOL OldDefault = m_ImplicitProcessDataIsDefault;
|
|
ThreadInfo* OldThread = m_ImplicitProcessDataThread;
|
|
|
|
m_ImplicitProcessData = Offset;
|
|
m_ImplicitProcessDataIsDefault = Default;
|
|
m_ImplicitProcessDataThread = Thread;
|
|
if (IS_KERNEL_TARGET(this) &&
|
|
!IS_KERNEL_TRIAGE_DUMP(this) &&
|
|
(Status = m_Machine->
|
|
SetDefaultPageDirectories(Thread, PAGE_DIR_ALL)) != S_OK)
|
|
{
|
|
m_ImplicitProcessData = Old;
|
|
m_ImplicitProcessDataIsDefault = OldDefault;
|
|
m_ImplicitProcessDataThread = OldThread;
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Process %s has invalid page directories\n",
|
|
FormatMachineAddr64(m_Machine, Offset));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::ReadImplicitProcessInfoPointer(ThreadInfo* Thread,
|
|
ULONG Offset, PULONG64 Ptr)
|
|
{
|
|
HRESULT Status;
|
|
ULONG64 CurProc;
|
|
|
|
// Retrieve the current EPROCESS.
|
|
if ((Status = GetImplicitProcessData(Thread, &CurProc)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
return ReadPointer(Thread->m_Process, m_Machine, CurProc + Offset, Ptr);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::KdGetThreadInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset)
|
|
{
|
|
if (Thread != NULL && Thread->m_DataOffset != 0)
|
|
{
|
|
*Offset = Thread->m_DataOffset;
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG Processor;
|
|
|
|
if (Thread != NULL)
|
|
{
|
|
ThreadHandle = Thread->m_Handle;
|
|
}
|
|
Processor = VIRTUAL_THREAD_INDEX(ThreadHandle);
|
|
|
|
HRESULT Status;
|
|
|
|
Status = GetProcessorSystemDataOffset(Processor,
|
|
DEBUG_DATA_KTHREAD_OFFSET,
|
|
Offset);
|
|
|
|
if (Status == S_OK && Thread != NULL)
|
|
{
|
|
Thread->m_DataOffset = *Offset;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::KdGetProcessInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
// Process data offsets are not cached for kernel mode
|
|
// since the only ProcessInfo is for kernel space.
|
|
|
|
ULONG64 ProcessAddr;
|
|
HRESULT Status;
|
|
|
|
if (ThreadData == 0)
|
|
{
|
|
Status = GetThreadInfoDataOffset(Thread,
|
|
VIRTUAL_THREAD_HANDLE(Processor),
|
|
&ThreadData);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
ThreadData += m_KdDebuggerData.OffsetKThreadApcProcess;
|
|
|
|
Status = ReadPointer(m_ProcessHead,
|
|
m_Machine, ThreadData, &ProcessAddr);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Unable to read KTHREAD address %p\n", ThreadData);
|
|
}
|
|
else
|
|
{
|
|
*Offset = ProcessAddr;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::KdGetThreadInfoTeb(ThreadInfo* Thread,
|
|
ULONG ThreadIndex,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
ULONG64 TebAddr;
|
|
HRESULT Status;
|
|
|
|
if (ThreadData == 0)
|
|
{
|
|
Status = GetThreadInfoDataOffset(Thread,
|
|
VIRTUAL_THREAD_HANDLE(ThreadIndex),
|
|
&ThreadData);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
ThreadData += m_KdDebuggerData.OffsetKThreadTeb;
|
|
|
|
Status = ReadPointer(m_ProcessHead,
|
|
m_Machine, ThreadData, &TebAddr);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Could not read KTHREAD address %p\n", ThreadData);
|
|
}
|
|
else
|
|
{
|
|
*Offset = TebAddr;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::KdGetProcessInfoPeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
HRESULT Status;
|
|
ULONG64 Process, PebAddr;
|
|
|
|
Status = GetProcessInfoDataOffset(Thread, Processor,
|
|
ThreadData, &Process);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Process += m_KdDebuggerData.OffsetEprocessPeb;
|
|
|
|
Status = ReadPointer(m_ProcessHead,
|
|
m_Machine, Process, &PebAddr);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Could not read KPROCESS address %p\n", Process);
|
|
}
|
|
else
|
|
{
|
|
*Offset = PebAddr;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
TargetInfo::FlushSelectorCache(void)
|
|
{
|
|
for (ULONG i = 0; i < SELECTOR_CACHE_LENGTH; i++)
|
|
{
|
|
m_SelectorCache[i].Younger = &m_SelectorCache[i + 1];
|
|
m_SelectorCache[i].Older = &m_SelectorCache[i - 1];
|
|
m_SelectorCache[i].Processor = (ULONG)-1;
|
|
m_SelectorCache[i].Selector = 0;
|
|
}
|
|
m_SelectorCache[--i].Younger = NULL;
|
|
m_SelectorCache[0].Older = NULL;
|
|
m_YoungestSel = &m_SelectorCache[i];
|
|
m_OldestSel = &m_SelectorCache[0];
|
|
}
|
|
|
|
BOOL
|
|
TargetInfo::FindSelector(ULONG Processor, ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SELECTOR_CACHE_LENGTH; i++)
|
|
{
|
|
if (m_SelectorCache[i].Selector == Selector &&
|
|
m_SelectorCache[i].Processor == Processor)
|
|
{
|
|
*Desc = m_SelectorCache[i].Desc;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
TargetInfo::PutSelector(ULONG Processor, ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
m_OldestSel->Processor = Processor;
|
|
m_OldestSel->Selector = Selector;
|
|
m_OldestSel->Desc = *Desc;
|
|
(m_OldestSel->Younger)->Older = NULL;
|
|
m_OldestSel->Older = m_YoungestSel;
|
|
m_YoungestSel->Younger = m_OldestSel;
|
|
m_YoungestSel = m_OldestSel;
|
|
m_OldestSel = m_OldestSel->Younger;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::KdGetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
if (!Thread || !Machine)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ULONG Processor = VIRTUAL_THREAD_INDEX(Thread->m_Handle);
|
|
|
|
if (FindSelector(Processor, Selector, Desc))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
ThreadInfo* CtxThread;
|
|
|
|
CtxThread = m_RegContextThread;
|
|
ChangeRegContext(Thread);
|
|
|
|
ULONG TableReg;
|
|
DESCRIPTOR64 Table;
|
|
HRESULT Status;
|
|
|
|
// Find out whether this is a GDT or LDT selector
|
|
if (Selector & 0x4)
|
|
{
|
|
TableReg = SEGREG_LDT;
|
|
}
|
|
else
|
|
{
|
|
TableReg = SEGREG_GDT;
|
|
}
|
|
|
|
//
|
|
// Fetch the address and limit of the appropriate descriptor table,
|
|
// then look up the selector entry.
|
|
//
|
|
|
|
if ((Status = Machine->GetSegRegDescriptor(TableReg, &Table)) != S_OK)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Status = ReadX86Descriptor(this, Thread->m_Process,
|
|
Selector, Table.Base, (ULONG)Table.Limit, Desc);
|
|
if (Status == S_OK)
|
|
{
|
|
PutSelector(Processor, Selector, Desc);
|
|
}
|
|
|
|
Exit:
|
|
ChangeRegContext(CtxThread);
|
|
return Status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LiveKernelTargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetThreadIdByProcessor(
|
|
IN ULONG Processor,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
*Id = VIRTUAL_THREAD_ID(Processor);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetThreadInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetThreadInfoDataOffset(Thread, ThreadHandle, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetProcessInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetProcessInfoDataOffset(Thread, Processor, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetThreadInfoTeb(ThreadInfo* Thread,
|
|
ULONG ThreadIndex,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetThreadInfoTeb(Thread, ThreadIndex, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetProcessInfoPeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetProcessInfoPeb(Thread, Processor, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::GetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
return KdGetSelDescriptor(Thread, Machine, Selector, Desc);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ConnLiveKernelTargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
DBGKD_MANIPULATE_STATE64 m;
|
|
PDBGKD_MANIPULATE_STATE64 Reply;
|
|
PDBGKD_GET_CONTEXT a = &m.u.GetContext;
|
|
NTSTATUS st;
|
|
ULONG rc;
|
|
|
|
if (m_Machine == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//
|
|
// Format state manipulate message
|
|
//
|
|
|
|
m.ApiNumber = DbgKdGetContextApi;
|
|
m.ReturnStatus = STATUS_PENDING;
|
|
m.Processor = (USHORT)VIRTUAL_THREAD_INDEX(Thread);
|
|
|
|
//
|
|
// Send the message and then wait for reply
|
|
//
|
|
|
|
do
|
|
{
|
|
m_Transport->WritePacket(&m, sizeof(m),
|
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|
NULL, 0);
|
|
rc = m_Transport->
|
|
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
|
|
} while (rc != DBGKD_WAIT_PACKET ||
|
|
Reply->ApiNumber != DbgKdGetContextApi);
|
|
|
|
st = Reply->ReturnStatus;
|
|
|
|
//
|
|
// Since get context response data follows message, Reply+1 should point
|
|
// at the data
|
|
//
|
|
|
|
memcpy(Context, Reply + 1, m_TypeInfo.SizeTargetContext);
|
|
|
|
KdOut("DbgKdGetContext returns %08lx\n", st);
|
|
return CONV_NT_STATUS(st);
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::SetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
DBGKD_MANIPULATE_STATE64 m;
|
|
PDBGKD_MANIPULATE_STATE64 Reply;
|
|
PDBGKD_SET_CONTEXT a = &m.u.SetContext;
|
|
NTSTATUS st;
|
|
ULONG rc;
|
|
|
|
if (m_Machine == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//
|
|
// Format state manipulate message
|
|
//
|
|
|
|
m.ApiNumber = DbgKdSetContextApi;
|
|
m.ReturnStatus = STATUS_PENDING;
|
|
m.Processor = (USHORT)VIRTUAL_THREAD_INDEX(Thread);
|
|
|
|
//
|
|
// Send the message and context and then wait for reply
|
|
//
|
|
|
|
do
|
|
{
|
|
m_Transport->WritePacket(&m, sizeof(m),
|
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|
Context,
|
|
(USHORT)
|
|
m_TypeInfo.SizeTargetContext);
|
|
rc = m_Transport->
|
|
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
|
|
} while (rc != DBGKD_WAIT_PACKET ||
|
|
Reply->ApiNumber != DbgKdSetContextApi);
|
|
|
|
st = Reply->ReturnStatus;
|
|
|
|
KdOut("DbgKdSetContext returns %08lx\n", st);
|
|
return CONV_NT_STATUS(st);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LocalLiveKernelTargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
// There really isn't any way to make
|
|
// this work in a meaningful way unless the system
|
|
// is paused.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::SetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
// There really isn't any way to make
|
|
// this work in a meaningful way unless the system
|
|
// is paused.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ExdiLiveKernelTargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetProcessorSystemDataOffset(
|
|
IN ULONG Processor,
|
|
IN ULONG Index,
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
if (m_KdSupport != EXDI_KD_GS_PCR ||
|
|
m_MachineType != IMAGE_FILE_MACHINE_AMD64)
|
|
{
|
|
return LiveKernelTargetInfo::
|
|
GetProcessorSystemDataOffset(Processor, Index, Offset);
|
|
}
|
|
|
|
HRESULT Status;
|
|
DESCRIPTOR64 GsDesc;
|
|
|
|
if ((Status =
|
|
GetTargetSegRegDescriptors(0, SEGREG_GS, 1, &GsDesc)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
switch(Index)
|
|
{
|
|
case DEBUG_DATA_KPCR_OFFSET:
|
|
*Offset = GsDesc.Base;
|
|
break;
|
|
|
|
case DEBUG_DATA_KPRCB_OFFSET:
|
|
case DEBUG_DATA_KTHREAD_OFFSET:
|
|
Status = ReadPointer(m_ProcessHead,
|
|
m_Machine,
|
|
GsDesc.Base +
|
|
m_KdDebuggerData.OffsetPcrCurrentPrcb,
|
|
Offset);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (Index == DEBUG_DATA_KPRCB_OFFSET)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = ReadPointer(m_ProcessHead,
|
|
m_Machine,
|
|
*Offset +
|
|
m_KdDebuggerData.OffsetPrcbCurrentThread,
|
|
Offset);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
if (!m_ContextValid)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = m_Machine->
|
|
GetExdiContext(m_Context, &m_ContextData, m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_ContextValid = NULL;
|
|
}
|
|
|
|
m_Machine->ConvertExdiContextToContext
|
|
(&m_ContextData, m_ContextType, (PCROSS_PLATFORM_CONTEXT)Context);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::SetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
if (!m_ContextValid)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = m_Machine->
|
|
GetExdiContext(m_Context, &m_ContextData, m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_ContextValid = TRUE;
|
|
}
|
|
|
|
m_Machine->ConvertExdiContextFromContext
|
|
((PCROSS_PLATFORM_CONTEXT)Context, &m_ContextData, m_ContextType);
|
|
return m_Machine->SetExdiContext(m_Context, &m_ContextData,
|
|
m_ContextType);
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetTargetSegRegDescriptors(ULONG64 Thread,
|
|
ULONG Start, ULONG Count,
|
|
PDESCRIPTOR64 Descs)
|
|
{
|
|
if (!m_ContextValid)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = m_Machine->
|
|
GetExdiContext(m_Context, &m_ContextData, m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_ContextValid = TRUE;
|
|
}
|
|
|
|
m_Machine->
|
|
ConvertExdiContextToSegDescs(&m_ContextData, m_ContextType,
|
|
Start, Count, Descs);
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
ExdiLiveKernelTargetInfo::InvalidateTargetContext(void)
|
|
{
|
|
m_ContextValid = FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetTargetSpecialRegisters
|
|
(ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
|
|
{
|
|
if (!m_ContextValid)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = m_Machine->
|
|
GetExdiContext(m_Context, &m_ContextData, m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_ContextValid = TRUE;
|
|
}
|
|
|
|
m_Machine->
|
|
ConvertExdiContextToSpecial(&m_ContextData, m_ContextType, Special);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::SetTargetSpecialRegisters
|
|
(ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
|
|
{
|
|
if (!m_ContextValid)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = m_Machine->
|
|
GetExdiContext(m_Context, &m_ContextData, m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_ContextValid = TRUE;
|
|
}
|
|
|
|
m_Machine->
|
|
ConvertExdiContextFromSpecial(Special, &m_ContextData, m_ContextType);
|
|
return m_Machine->SetExdiContext(m_Context, &m_ContextData,
|
|
m_ContextType);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LiveUserTargetInfo system object methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
return m_Services->
|
|
GetContext(Thread,
|
|
*(PULONG)((PUCHAR)Context +
|
|
m_TypeInfo.
|
|
OffsetTargetContextFlags),
|
|
m_TypeInfo.OffsetTargetContextFlags,
|
|
Context,
|
|
m_TypeInfo.SizeTargetContext, NULL);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::SetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
return m_Services->
|
|
SetContext(Thread, Context,
|
|
m_TypeInfo.SizeTargetContext,
|
|
NULL);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetThreadStartOffset(ThreadInfo* Thread,
|
|
PULONG64 StartOffset)
|
|
{
|
|
return m_Services->
|
|
GetThreadStartAddress(Thread->m_Handle, StartOffset);
|
|
}
|
|
|
|
#define BUFFER_THREADS 64
|
|
|
|
void
|
|
LiveUserTargetInfo::SuspendThreads(void)
|
|
{
|
|
HRESULT Status;
|
|
ProcessInfo* Process;
|
|
ThreadInfo* Thread;
|
|
ULONG64 Threads[BUFFER_THREADS];
|
|
ULONG Counts[BUFFER_THREADS];
|
|
PULONG StoreCounts[BUFFER_THREADS];
|
|
ULONG Buffered;
|
|
ULONG i;
|
|
|
|
Buffered = 0;
|
|
Process = m_ProcessHead;
|
|
while (Process != NULL)
|
|
{
|
|
Thread = (Process->m_Flags & ENG_PROC_NO_SUSPEND_RESUME) ?
|
|
NULL : Process->m_ThreadHead;
|
|
while (Thread != NULL)
|
|
{
|
|
if (!Process->m_Exited &&
|
|
!Thread->m_Exited &&
|
|
Thread->m_Handle != 0)
|
|
{
|
|
#ifdef DBG_SUSPEND
|
|
dprintf("** suspending thread id: %x handle: %I64x\n",
|
|
Thread->m_SystemId, Thread->m_Handle);
|
|
#endif
|
|
|
|
if (Thread->m_InternalFreezeCount > 0)
|
|
{
|
|
Thread->m_InternalFreezeCount--;
|
|
}
|
|
else if (Thread->m_FreezeCount > 0)
|
|
{
|
|
dprintf("thread %d can execute\n", Thread->m_UserId);
|
|
Thread->m_FreezeCount--;
|
|
}
|
|
else
|
|
{
|
|
if (Buffered == BUFFER_THREADS)
|
|
{
|
|
if ((Status = m_Services->
|
|
SuspendThreads(Buffered, Threads,
|
|
Counts)) != S_OK)
|
|
{
|
|
WarnOut("SuspendThread failed, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
for (i = 0; i < Buffered; i++)
|
|
{
|
|
*StoreCounts[i] = Counts[i];
|
|
}
|
|
|
|
Buffered = 0;
|
|
}
|
|
|
|
Threads[Buffered] = Thread->m_Handle;
|
|
StoreCounts[Buffered] = &Thread->m_SuspendCount;
|
|
Buffered++;
|
|
}
|
|
}
|
|
|
|
Thread = Thread->m_Next;
|
|
}
|
|
|
|
Process = Process->m_Next;
|
|
}
|
|
|
|
if (Buffered > 0)
|
|
{
|
|
if ((Status = m_Services->
|
|
SuspendThreads(Buffered, Threads, Counts)) != S_OK)
|
|
{
|
|
WarnOut("SuspendThread failed, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
for (i = 0; i < Buffered; i++)
|
|
{
|
|
*StoreCounts[i] = Counts[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
LiveUserTargetInfo::ResumeThreads(void)
|
|
{
|
|
ProcessInfo* Process;
|
|
ThreadInfo* Thread;
|
|
HRESULT Status;
|
|
BOOL EventActive = FALSE;
|
|
BOOL EventAlive = FALSE;
|
|
ULONG64 Threads[BUFFER_THREADS];
|
|
ULONG Counts[BUFFER_THREADS];
|
|
PULONG StoreCounts[BUFFER_THREADS];
|
|
ULONG Buffered;
|
|
ULONG i;
|
|
|
|
Buffered = 0;
|
|
Process = m_ProcessHead;
|
|
while (Process != NULL)
|
|
{
|
|
if (Process->m_Flags & ENG_PROC_NO_SUSPEND_RESUME)
|
|
{
|
|
Thread = NULL;
|
|
// Suppress any possible warning message under
|
|
// the assumption that sessions where the caller
|
|
// is managing suspension do not need warnings.
|
|
EventActive = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Thread = Process->m_ThreadHead;
|
|
}
|
|
while (Thread != NULL)
|
|
{
|
|
if (!Process->m_Exited &&
|
|
!Thread->m_Exited &&
|
|
Thread->m_Handle != 0)
|
|
{
|
|
if (Process == g_EventProcess)
|
|
{
|
|
EventAlive = TRUE;
|
|
}
|
|
|
|
#ifdef DBG_SUSPEND
|
|
dprintf("** resuming thread id: %x handle: %I64x\n",
|
|
Thread->m_SystemId, Thread->m_Handle);
|
|
#endif
|
|
|
|
if ((g_EngStatus & ENG_STATUS_STOP_SESSION) == 0 &&
|
|
!ThreadWillResume(Thread))
|
|
{
|
|
if (!IsSelectedExecutionThread(Thread,
|
|
SELTHREAD_INTERNAL_THREAD))
|
|
{
|
|
dprintf("thread %d not executing\n", Thread->m_UserId);
|
|
Thread->m_FreezeCount++;
|
|
}
|
|
else
|
|
{
|
|
Thread->m_InternalFreezeCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Process == g_EventProcess)
|
|
{
|
|
EventActive = TRUE;
|
|
}
|
|
|
|
if (Buffered == BUFFER_THREADS)
|
|
{
|
|
if ((Status = m_Services->
|
|
ResumeThreads(Buffered, Threads,
|
|
Counts)) != S_OK)
|
|
{
|
|
WarnOut("ResumeThread failed, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
for (i = 0; i < Buffered; i++)
|
|
{
|
|
*StoreCounts[i] = Counts[i];
|
|
}
|
|
|
|
Buffered = 0;
|
|
}
|
|
|
|
Threads[Buffered] = Thread->m_Handle;
|
|
StoreCounts[Buffered] = &Thread->m_SuspendCount;
|
|
Buffered++;
|
|
}
|
|
}
|
|
|
|
Thread = Thread->m_Next;
|
|
}
|
|
|
|
Process = Process->m_Next;
|
|
}
|
|
|
|
if (Buffered > 0)
|
|
{
|
|
if ((Status = m_Services->
|
|
ResumeThreads(Buffered, Threads, Counts)) != S_OK)
|
|
{
|
|
WarnOut("ResumeThread failed, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
for (i = 0; i < Buffered; i++)
|
|
{
|
|
*StoreCounts[i] = Counts[i];
|
|
}
|
|
}
|
|
|
|
if (EventAlive && !EventActive)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetThreadInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset)
|
|
{
|
|
if (Thread && Thread->m_DataOffset)
|
|
{
|
|
*Offset = Thread->m_DataOffset;
|
|
return S_OK;
|
|
}
|
|
|
|
if (Thread)
|
|
{
|
|
ThreadHandle = Thread->m_Handle;
|
|
}
|
|
else if (!ThreadHandle)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT Status = m_Services->
|
|
GetThreadDataOffset(ThreadHandle, Offset);
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
if (Thread)
|
|
{
|
|
Thread->m_DataOffset = *Offset;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetProcessInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// Even if an arbitrary thread data pointer is given
|
|
// we still require a thread in order to know what
|
|
// process to read from.
|
|
// Processor isn't any use so fail if no thread is given.
|
|
if (!Thread)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if (ThreadData != 0)
|
|
{
|
|
if (g_DebuggerPlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ThreadData += m_Machine->m_Ptr64 ?
|
|
PEB_FROM_TEB64 : PEB_FROM_TEB32;
|
|
Status = ReadPointer(Thread->m_Process, m_Machine,
|
|
ThreadData, Offset);
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process = Thread->m_Process;
|
|
if (Process->m_DataOffset != 0)
|
|
{
|
|
*Offset = Process->m_DataOffset;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = m_Services->
|
|
GetProcessDataOffset(Process->m_SysHandle, Offset);
|
|
}
|
|
}
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
if (!ThreadData)
|
|
{
|
|
Thread->m_Process->m_DataOffset = *Offset;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetThreadInfoTeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return GetThreadInfoDataOffset(Thread, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetProcessInfoPeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
// Thread data is not useful.
|
|
return GetProcessInfoDataOffset(Thread, 0, 0, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Used;
|
|
X86_LDT_ENTRY X86Desc;
|
|
|
|
if ((Status = m_Services->
|
|
DescribeSelector(Thread->m_Handle, Selector,
|
|
&X86Desc, sizeof(X86Desc),
|
|
&Used)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Used != sizeof(X86Desc))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
X86DescriptorToDescriptor64(&X86Desc, Desc);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::StartAttachProcess(ULONG ProcessId,
|
|
ULONG AttachFlags,
|
|
PPENDING_PROCESS* Pending)
|
|
{
|
|
HRESULT Status;
|
|
PPENDING_PROCESS Pend;
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
ErrOut("SECURE: Process attach disallowed\n");
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
if (m_Local &&
|
|
ProcessId == GetCurrentProcessId() &&
|
|
!(AttachFlags & DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND))
|
|
{
|
|
ErrOut("Can't debug the current process\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Pend = new PENDING_PROCESS;
|
|
if (Pend == NULL)
|
|
{
|
|
ErrOut("Unable to allocate memory\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ((AttachFlags & DEBUG_ATTACH_NONINVASIVE) == 0)
|
|
{
|
|
if ((Status = m_Services->
|
|
AttachProcess(ProcessId, AttachFlags,
|
|
&Pend->Handle, &Pend->Options)) != S_OK)
|
|
{
|
|
ErrOut("Cannot debug pid %ld, %s\n \"%s\"\n",
|
|
ProcessId, FormatStatusCode(Status), FormatStatus(Status));
|
|
delete Pend;
|
|
return Status;
|
|
}
|
|
|
|
Pend->Flags = ENG_PROC_ATTACHED;
|
|
if (AttachFlags & DEBUG_ATTACH_EXISTING)
|
|
{
|
|
Pend->Flags |= ENG_PROC_ATTACH_EXISTING;
|
|
}
|
|
if (AttachFlags & DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK)
|
|
{
|
|
Pend->Flags |= ENG_PROC_NO_INITIAL_BREAK;
|
|
}
|
|
if (AttachFlags & DEBUG_ATTACH_INVASIVE_RESUME_PROCESS)
|
|
{
|
|
Pend->Flags |= ENG_PROC_RESUME_AT_ATTACH;
|
|
}
|
|
|
|
if (ProcessId == CSRSS_PROCESS_ID)
|
|
{
|
|
if (m_Local)
|
|
{
|
|
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS |
|
|
DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION;
|
|
g_EngOptions &= ~DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
|
|
}
|
|
|
|
Pend->Flags |= ENG_PROC_SYSTEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Pend->Handle = 0;
|
|
Pend->Flags = ENG_PROC_EXAMINED;
|
|
if (AttachFlags & DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
|
|
{
|
|
Pend->Flags |= ENG_PROC_NO_SUSPEND_RESUME;
|
|
}
|
|
Pend->Options = DEBUG_PROCESS_ONLY_THIS_PROCESS;
|
|
}
|
|
|
|
Pend->Id = ProcessId;
|
|
Pend->InitialThreadId = 0;
|
|
Pend->InitialThreadHandle = 0;
|
|
AddPendingProcess(Pend);
|
|
*Pending = Pend;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::StartCreateProcess(PWSTR CommandLine,
|
|
ULONG CreateFlags,
|
|
PBOOL InheritHandles,
|
|
PWSTR CurrentDir,
|
|
PPENDING_PROCESS* Pending)
|
|
{
|
|
HRESULT Status;
|
|
PPENDING_PROCESS Pend;
|
|
|
|
if ((CreateFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) == 0)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
ErrOut("SECURE: Process creation disallowed\n");
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
Pend = new PENDING_PROCESS;
|
|
if (Pend == NULL)
|
|
{
|
|
ErrOut("Unable to allocate memory\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
dprintf("CommandLine: %ws\n", CommandLine);
|
|
|
|
//
|
|
// Pick up incoming inherit and curdir settings
|
|
// and apply defaults if necessary.
|
|
//
|
|
|
|
BOOL FinalInheritHandles;
|
|
|
|
if (InheritHandles)
|
|
{
|
|
FinalInheritHandles = *InheritHandles;
|
|
}
|
|
else
|
|
{
|
|
FinalInheritHandles = TRUE;
|
|
}
|
|
|
|
if (!CurrentDir)
|
|
{
|
|
CurrentDir = g_StartProcessDir;
|
|
}
|
|
if (CurrentDir)
|
|
{
|
|
dprintf("Starting directory: %ws\n", CurrentDir);
|
|
}
|
|
|
|
//
|
|
// Create the process.
|
|
//
|
|
|
|
if ((Status = m_Services->
|
|
CreateProcessW(CommandLine, CreateFlags,
|
|
FinalInheritHandles, CurrentDir,
|
|
&Pend->Id, &Pend->InitialThreadId,
|
|
&Pend->Handle, &Pend->InitialThreadHandle)) != S_OK)
|
|
{
|
|
ErrOut("Cannot execute '%ws', %s\n \"%s\"\n",
|
|
CommandLine, FormatStatusCode(Status),
|
|
FormatStatusArgs(Status, NULL));
|
|
delete Pend;
|
|
}
|
|
else
|
|
{
|
|
Pend->Flags = ENG_PROC_CREATED;
|
|
Pend->Options = (CreateFlags & DEBUG_ONLY_THIS_PROCESS) ?
|
|
DEBUG_PROCESS_ONLY_THIS_PROCESS : 0;
|
|
AddPendingProcess(Pend);
|
|
*Pending = Pend;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::TerminateProcesses(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
ErrOut("SECURE: Process termination disallowed\n");
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
ProcessInfo* Process;
|
|
ULONG AllExamined = ENG_PROC_EXAMINED;
|
|
|
|
ForTargetProcesses(this)
|
|
{
|
|
// The all-examined flag will get turned off if any
|
|
// process is not examined.
|
|
AllExamined &= Process->m_Flags;
|
|
|
|
if ((Status = Process->Terminate()) != S_OK)
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (m_DeferContinueEvent)
|
|
{
|
|
// The event's process may just have been terminated so don't
|
|
// check for failures.
|
|
m_Services->ContinueEvent(DBG_CONTINUE);
|
|
m_DeferContinueEvent = FALSE;
|
|
}
|
|
|
|
DEBUG_EVENT64 Event;
|
|
ULONG EventUsed;
|
|
BOOL AnyLeft;
|
|
BOOL AnyExited;
|
|
|
|
for (;;)
|
|
{
|
|
while (!AllExamined &&
|
|
m_Services->WaitForEvent(0, &Event, sizeof(Event),
|
|
&EventUsed) == S_OK)
|
|
{
|
|
// Check for process exit events so we can
|
|
// mark the process infos as exited.
|
|
if (EventUsed == sizeof(DEBUG_EVENT32))
|
|
{
|
|
DEBUG_EVENT32 Event32 = *(DEBUG_EVENT32*)&Event;
|
|
DebugEvent32To64(&Event32, &Event);
|
|
}
|
|
else if (EventUsed != sizeof(DEBUG_EVENT64))
|
|
{
|
|
ErrOut("Event data corrupt\n");
|
|
Status = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
if (Event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
|
|
{
|
|
Process = FindProcessBySystemId(Event.dwProcessId);
|
|
if (Process != NULL)
|
|
{
|
|
Process->MarkExited();
|
|
}
|
|
}
|
|
|
|
m_Services->ContinueEvent(DBG_CONTINUE);
|
|
}
|
|
|
|
AnyLeft = FALSE;
|
|
AnyExited = FALSE;
|
|
|
|
ForTargetProcesses(this)
|
|
{
|
|
if (!Process->m_Exited)
|
|
{
|
|
ULONG Code;
|
|
|
|
if ((Status = m_Services->
|
|
GetProcessExitCode(Process->m_SysHandle, &Code)) == S_OK)
|
|
{
|
|
Process->MarkExited();
|
|
AnyExited = TRUE;
|
|
}
|
|
else if (FAILED(Status))
|
|
{
|
|
ErrOut("Unable to wait for process to terminate, %s\n",
|
|
FormatStatusCode(Status));
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
AnyLeft = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AnyLeft)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AnyExited)
|
|
{
|
|
// Give things time to run and exit.
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
// We've terminated everything so it's safe to assume
|
|
// we're no longer debugging any system processes.
|
|
// We do this now rather than wait for DeleteProcess
|
|
// so that shutdown can query the value immediately.
|
|
m_AllProcessFlags &= ~ENG_PROC_SYSTEM;
|
|
|
|
//
|
|
// Drain off any remaining events.
|
|
//
|
|
|
|
if (!AllExamined)
|
|
{
|
|
while (m_Services->
|
|
WaitForEvent(10, &Event, sizeof(Event), NULL) == S_OK)
|
|
{
|
|
m_Services->ContinueEvent(DBG_CONTINUE);
|
|
}
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::DetachProcesses(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
ErrOut("SECURE: Process detach disallowed\n");
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
if (m_DeferContinueEvent)
|
|
{
|
|
if ((Status = m_Services->ContinueEvent(DBG_CONTINUE)) != S_OK)
|
|
{
|
|
ErrOut("Unable to continue terminated process, %s\n",
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
m_DeferContinueEvent = FALSE;
|
|
}
|
|
|
|
ProcessInfo* Process;
|
|
ULONG AllExamined = ENG_PROC_EXAMINED;
|
|
|
|
ForTargetProcesses(this)
|
|
{
|
|
// The all-examined flag will get turned off if any
|
|
// process is not examined.
|
|
AllExamined &= Process->m_Flags;
|
|
|
|
Process->Detach();
|
|
}
|
|
|
|
// We've terminated everything so it's safe to assume
|
|
// we're no longer debugging any system processes.
|
|
// We do this now rather than wait for DeleteProcess
|
|
// so that shutdown can query the value immediately.
|
|
m_AllProcessFlags &= ~ENG_PROC_SYSTEM;
|
|
|
|
//
|
|
// Drain off any remaining events.
|
|
//
|
|
|
|
if (!AllExamined)
|
|
{
|
|
DEBUG_EVENT64 Event;
|
|
|
|
while (m_Services->
|
|
WaitForEvent(10, &Event, sizeof(Event), NULL) == S_OK)
|
|
{
|
|
m_Services->ContinueEvent(DBG_CONTINUE);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
LiveUserTargetInfo::AddPendingProcess(PPENDING_PROCESS Pending)
|
|
{
|
|
Pending->Next = m_ProcessPending;
|
|
m_ProcessPending = Pending;
|
|
m_AllPendingFlags |= Pending->Flags;
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::RemovePendingProcess(PPENDING_PROCESS Pending)
|
|
{
|
|
PPENDING_PROCESS Prev, Cur;
|
|
ULONG AllFlags = 0;
|
|
|
|
Prev = NULL;
|
|
for (Cur = m_ProcessPending; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
if (Cur == Pending)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Cur;
|
|
AllFlags |= Cur->Flags;
|
|
}
|
|
|
|
if (Cur == NULL)
|
|
{
|
|
DBG_ASSERT(Cur != NULL);
|
|
return;
|
|
}
|
|
|
|
Cur = Cur->Next;
|
|
if (Prev == NULL)
|
|
{
|
|
m_ProcessPending = Cur;
|
|
}
|
|
else
|
|
{
|
|
Prev->Next = Cur;
|
|
}
|
|
DiscardPendingProcess(Pending);
|
|
|
|
while (Cur != NULL)
|
|
{
|
|
AllFlags |= Cur->Flags;
|
|
Cur = Cur->Next;
|
|
}
|
|
m_AllPendingFlags = AllFlags;
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::DiscardPendingProcess(PPENDING_PROCESS Pending)
|
|
{
|
|
if (Pending->InitialThreadHandle)
|
|
{
|
|
m_Services->CloseHandle(Pending->InitialThreadHandle);
|
|
}
|
|
if (Pending->Handle)
|
|
{
|
|
m_Services->CloseHandle(Pending->Handle);
|
|
}
|
|
delete Pending;
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::DiscardPendingProcesses(void)
|
|
{
|
|
while (m_ProcessPending != NULL)
|
|
{
|
|
PPENDING_PROCESS Next = m_ProcessPending->Next;
|
|
DiscardPendingProcess(m_ProcessPending);
|
|
m_ProcessPending = Next;
|
|
}
|
|
|
|
m_AllPendingFlags = 0;
|
|
}
|
|
|
|
PPENDING_PROCESS
|
|
LiveUserTargetInfo::FindPendingProcessByFlags(ULONG Flags)
|
|
{
|
|
PPENDING_PROCESS Cur;
|
|
|
|
for (Cur = m_ProcessPending; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
if (Cur->Flags & Flags)
|
|
{
|
|
return Cur;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PPENDING_PROCESS
|
|
LiveUserTargetInfo::FindPendingProcessById(ULONG Id)
|
|
{
|
|
PPENDING_PROCESS Cur;
|
|
|
|
for (Cur = m_ProcessPending; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
if (Cur->Id == Id)
|
|
{
|
|
return Cur;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::VerifyPendingProcesses(void)
|
|
{
|
|
PPENDING_PROCESS Cur;
|
|
|
|
Restart:
|
|
for (Cur = m_ProcessPending; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
ULONG ExitCode;
|
|
|
|
if (Cur->Handle &&
|
|
m_Services->GetProcessExitCode(Cur->Handle, &ExitCode) == S_OK)
|
|
{
|
|
ErrOut("Process %d exited before attach completed\n", Cur->Id);
|
|
RemovePendingProcess(Cur);
|
|
goto Restart;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::AddExamineToPendingAttach(void)
|
|
{
|
|
PPENDING_PROCESS Cur;
|
|
|
|
for (Cur = m_ProcessPending; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
if (Cur->Flags & ENG_PROC_ATTACHED)
|
|
{
|
|
Cur->Flags |= ENG_PROC_EXAMINED;
|
|
m_AllPendingFlags |= ENG_PROC_EXAMINED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LiveUserTargetInfo::SuspendResumeThreads(ProcessInfo* Process,
|
|
BOOL Susp,
|
|
ThreadInfo* Match)
|
|
{
|
|
ThreadInfo* Thrd;
|
|
|
|
for (Thrd = Process->m_ThreadHead; Thrd; Thrd = Thrd->m_Next)
|
|
{
|
|
if (Match != NULL && Match != Thrd)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
HRESULT Status;
|
|
ULONG Count;
|
|
|
|
if (Susp)
|
|
{
|
|
Status = m_Services->
|
|
SuspendThreads(1, &Thrd->m_Handle, &Count);
|
|
}
|
|
else
|
|
{
|
|
Status = m_Services->
|
|
ResumeThreads(1, &Thrd->m_Handle, &Count);
|
|
}
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Operation failed for thread %d, 0x%X\n",
|
|
Thrd->m_UserId, Status);
|
|
}
|
|
else
|
|
{
|
|
Thrd->m_SuspendCount = Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// IDebugSystemObjects methods.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetEventThread(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
ENTER_ENGINE();
|
|
|
|
HRESULT Status;
|
|
|
|
if (g_EventThread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_EventThread->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetEventProcess(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
ENTER_ENGINE();
|
|
|
|
HRESULT Status;
|
|
|
|
if (g_EventProcess == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_EventProcess->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentThreadId(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_Thread->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::SetCurrentThreadId(
|
|
THIS_
|
|
IN ULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_RUNNING(g_CmdState))
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread = FindAnyThreadByUserId(Id);
|
|
if (Thread != NULL)
|
|
{
|
|
SetCurrentThread(Thread, FALSE);
|
|
ResetCurrentScopeLazy();
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessId(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_Process->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::SetCurrentProcessId(
|
|
THIS_
|
|
IN ULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_RUNNING(g_CmdState))
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process = FindAnyProcessByUserId(Id);
|
|
if (Process != NULL)
|
|
{
|
|
if (Process->m_CurrentThread == NULL)
|
|
{
|
|
Process->m_CurrentThread = Process->m_ThreadHead;
|
|
}
|
|
if (Process->m_CurrentThread == NULL)
|
|
{
|
|
Status = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
SetCurrentThread(Process->m_CurrentThread, FALSE);
|
|
ResetCurrentScopeLazy();
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetNumberThreads(
|
|
THIS_
|
|
OUT PULONG Number
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Number = g_Process->m_NumThreads;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetTotalNumberThreads(
|
|
THIS_
|
|
OUT PULONG Total,
|
|
OUT PULONG LargestProcess
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Total = g_Target->m_TotalNumberThreads;
|
|
*LargestProcess = g_Target->m_MaxThreadsInProcess;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdsByIndex(
|
|
THIS_
|
|
IN ULONG Start,
|
|
IN ULONG Count,
|
|
OUT OPTIONAL /* size_is(Count) */ PULONG Ids,
|
|
OUT OPTIONAL /* size_is(Count) */ PULONG SysIds
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread;
|
|
ULONG Index;
|
|
|
|
if (Start >= g_Process->m_NumThreads ||
|
|
Start + Count > g_Process->m_NumThreads)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
Index = 0;
|
|
for (Thread = g_Process->m_ThreadHead;
|
|
Thread != NULL;
|
|
Thread = Thread->m_Next)
|
|
{
|
|
if (Index >= Start && Index < Start + Count)
|
|
{
|
|
if (Ids != NULL)
|
|
{
|
|
*Ids++ = Thread->m_UserId;
|
|
}
|
|
if (SysIds != NULL)
|
|
{
|
|
*SysIds++ = Thread->m_SystemId;
|
|
}
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdByProcessor(
|
|
THIS_
|
|
IN ULONG Processor,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ULONG SysId;
|
|
|
|
Status = g_Target->GetThreadIdByProcessor(Processor, &SysId);
|
|
if (Status == S_OK)
|
|
{
|
|
ThreadInfo* Thread =
|
|
g_Process->FindThreadBySystemId(SysId);
|
|
if (Thread != NULL)
|
|
{
|
|
*Id = Thread->m_UserId;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentThreadDataOffset(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->
|
|
GetThreadInfoDataOffset(g_Thread, 0, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdByDataOffset(
|
|
THIS_
|
|
IN ULONG64 Offset,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread;
|
|
|
|
Status = E_NOINTERFACE;
|
|
for (Thread = g_Process->m_ThreadHead;
|
|
Thread != NULL;
|
|
Thread = Thread->m_Next)
|
|
{
|
|
ULONG64 DataOffset;
|
|
|
|
Status = g_Target->GetThreadInfoDataOffset(Thread, 0, &DataOffset);
|
|
if (Status != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (DataOffset == Offset)
|
|
{
|
|
*Id = Thread->m_UserId;
|
|
Status = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentThreadTeb(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->GetThreadInfoTeb(g_Thread, 0, 0, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdByTeb(
|
|
THIS_
|
|
IN ULONG64 Offset,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread;
|
|
|
|
Status = E_NOINTERFACE;
|
|
for (Thread = g_Process->m_ThreadHead;
|
|
Thread != NULL;
|
|
Thread = Thread->m_Next)
|
|
{
|
|
ULONG64 Teb;
|
|
|
|
Status = g_Target->GetThreadInfoTeb(Thread, 0, 0, &Teb);
|
|
if (Status != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Teb == Offset)
|
|
{
|
|
*Id = Thread->m_UserId;
|
|
Status = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentThreadSystemId(
|
|
THIS_
|
|
OUT PULONG SysId
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*SysId = g_Thread->m_SystemId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdBySystemId(
|
|
THIS_
|
|
IN ULONG SysId,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread = g_Process->FindThreadBySystemId(SysId);
|
|
if (Thread != NULL)
|
|
{
|
|
*Id = Thread->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentThreadHandle(
|
|
THIS_
|
|
OUT PULONG64 Handle
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Handle = g_Thread->m_Handle;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetThreadIdByHandle(
|
|
THIS_
|
|
IN ULONG64 Handle,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ThreadInfo* Thread = g_Process->FindThreadByHandle(Handle);
|
|
if (Thread != NULL)
|
|
{
|
|
*Id = Thread->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetNumberProcesses(
|
|
THIS_
|
|
OUT PULONG Number
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Number = g_Target->m_NumProcesses;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetProcessIdsByIndex(
|
|
THIS_
|
|
IN ULONG Start,
|
|
IN ULONG Count,
|
|
OUT OPTIONAL /* size_is(Count) */ PULONG Ids,
|
|
OUT OPTIONAL /* size_is(Count) */ PULONG SysIds
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
goto EH_Exit;
|
|
}
|
|
|
|
ProcessInfo* Process;
|
|
ULONG Index;
|
|
|
|
if (Start >= g_Target->m_NumProcesses ||
|
|
Start + Count > g_Target->m_NumProcesses)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
goto EH_Exit;
|
|
}
|
|
|
|
Index = 0;
|
|
for (Process = g_Target->m_ProcessHead;
|
|
Process != NULL;
|
|
Process = Process->m_Next)
|
|
{
|
|
if (Index >= Start && Index < Start + Count)
|
|
{
|
|
if (Ids != NULL)
|
|
{
|
|
*Ids++ = Process->m_UserId;
|
|
}
|
|
if (SysIds != NULL)
|
|
{
|
|
*SysIds++ = Process->m_SystemId;
|
|
}
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
EH_Exit:
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessDataOffset(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->
|
|
GetProcessInfoDataOffset(g_Thread, 0, 0, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetProcessIdByDataOffset(
|
|
THIS_
|
|
IN ULONG64 Offset,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process;
|
|
|
|
Status = E_NOINTERFACE;
|
|
for (Process = g_Target->m_ProcessHead;
|
|
Process != NULL;
|
|
Process = Process->m_Next)
|
|
{
|
|
ULONG64 DataOffset;
|
|
|
|
Status = g_Target->
|
|
GetProcessInfoDataOffset(Process->m_ThreadHead,
|
|
0, 0, &DataOffset);
|
|
if (Status != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (DataOffset == Offset)
|
|
{
|
|
*Id = Process->m_UserId;
|
|
Status = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessPeb(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Thread == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->GetProcessInfoPeb(g_Thread, 0, 0, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetProcessIdByPeb(
|
|
THIS_
|
|
IN ULONG64 Offset,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process;
|
|
|
|
Status = E_NOINTERFACE;
|
|
for (Process = g_Target->m_ProcessHead;
|
|
Process != NULL;
|
|
Process = Process->m_Next)
|
|
{
|
|
ULONG64 Peb;
|
|
|
|
Status = g_Target->GetProcessInfoPeb(Process->m_ThreadHead,
|
|
0, 0, &Peb);
|
|
if (Status != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Peb == Offset)
|
|
{
|
|
*Id = Process->m_UserId;
|
|
Status = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessSystemId(
|
|
THIS_
|
|
OUT PULONG SysId
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*SysId = g_Process->m_SystemId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetProcessIdBySystemId(
|
|
THIS_
|
|
IN ULONG SysId,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Status = E_NOTIMPL;
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process = g_Target->FindProcessBySystemId(SysId);
|
|
if (Process != NULL)
|
|
{
|
|
*Id = Process->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessHandle(
|
|
THIS_
|
|
OUT PULONG64 Handle
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Handle = g_Process->m_SysHandle;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetProcessIdByHandle(
|
|
THIS_
|
|
IN ULONG64 Handle,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ProcessInfo* Process = g_Target->FindProcessByHandle(Handle);
|
|
if (Process != NULL)
|
|
{
|
|
*Id = Process->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessExecutableName(
|
|
THIS_
|
|
OUT OPTIONAL PSTR Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG ExeSize
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = FillStringBuffer(g_Process->GetExecutableImageName(), 0,
|
|
Buffer, BufferSize, ExeSize);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentProcessUpTime(
|
|
THIS_
|
|
OUT PULONG UpTime
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!IS_USER_TARGET(g_Target) || g_Process == NULL)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
ULONG64 LongUpTime;
|
|
|
|
LongUpTime = g_Target->GetProcessUpTimeN(g_Process);
|
|
if (LongUpTime == 0)
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
else
|
|
{
|
|
*UpTime = FileTimeToTime(LongUpTime);
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetImplicitThreadDataOffset(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Process->GetImplicitThreadData(g_Thread, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::SetImplicitThreadDataOffset(
|
|
THIS_
|
|
IN ULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Process->SetImplicitThreadData(g_Thread, Offset, FALSE);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetImplicitProcessDataOffset(
|
|
THIS_
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->GetImplicitProcessData(g_Thread, Offset);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::SetImplicitProcessDataOffset(
|
|
THIS_
|
|
IN ULONG64 Offset
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->SetImplicitProcessData(g_Thread, Offset, FALSE);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
ResetImplicitData(void)
|
|
{
|
|
if (g_Process)
|
|
{
|
|
g_Process->ResetImplicitData();
|
|
}
|
|
if (g_Target)
|
|
{
|
|
g_Target->ResetImplicitData();
|
|
}
|
|
}
|
|
|
|
void
|
|
SetImplicitProcessAndCache(ULONG64 Base, BOOL Ptes, BOOL ReloadUser)
|
|
{
|
|
BOOL OldPtes = g_Process->m_VirtualCache.m_ForceDecodePTEs;
|
|
|
|
if (Ptes && !Base)
|
|
{
|
|
// If the user has requested a reset to the default
|
|
// process with no translations we need to turn
|
|
// off translations immediately so that any
|
|
// existing base doesn't interfere with determining
|
|
// the default process.
|
|
g_Process->m_VirtualCache.SetForceDecodePtes(FALSE, g_Target);
|
|
}
|
|
|
|
if (g_Target->SetImplicitProcessData(g_Thread, Base, TRUE) == S_OK)
|
|
{
|
|
dprintf("Implicit process is now %s\n",
|
|
FormatAddr64(g_Target->m_ImplicitProcessData));
|
|
|
|
if (Ptes)
|
|
{
|
|
if (Base)
|
|
{
|
|
g_Process->m_VirtualCache.SetForceDecodePtes(TRUE, g_Target);
|
|
}
|
|
if (IS_REMOTE_KERNEL_TARGET(g_Target))
|
|
{
|
|
dprintf(".cache %sforcedecodeptes done\n",
|
|
Base != 0 ? "" : "no");
|
|
}
|
|
|
|
if (ReloadUser)
|
|
{
|
|
PCSTR ArgsRet;
|
|
|
|
g_Target->Reload(g_Thread, "/user", &ArgsRet);
|
|
}
|
|
}
|
|
|
|
if (Base && !g_Process->m_VirtualCache.m_ForceDecodePTEs &&
|
|
IS_REMOTE_KERNEL_TARGET(g_Target))
|
|
{
|
|
WarnOut("WARNING: .cache forcedecodeptes is not enabled\n");
|
|
}
|
|
}
|
|
else if (Ptes && !Base && OldPtes)
|
|
{
|
|
// Restore settings to the way they were.
|
|
g_Process->m_VirtualCache.SetForceDecodePtes(TRUE, g_Target);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
SetScopeContextFromThreadData(ULONG64 ThreadBase, BOOL Verbose)
|
|
{
|
|
if (!ThreadBase)
|
|
{
|
|
if (GetCurrentScopeContext())
|
|
{
|
|
ResetCurrentScope();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT Status;
|
|
DEBUG_STACK_FRAME StkFrame;
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
|
|
if ((Status = g_Target->
|
|
GetContextFromThreadStack(ThreadBase, &Context, Verbose)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
g_Machine->GetScopeFrameFromContext(&Context, &StkFrame);
|
|
SetCurrentScope(&StkFrame, &Context, sizeof(Context));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
DotThread(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Base = 0;
|
|
BOOL Ptes = FALSE;
|
|
BOOL ReloadUser = FALSE;
|
|
|
|
if (!g_Thread)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*(++g_CurCmd))
|
|
{
|
|
case 'p':
|
|
Ptes = TRUE;
|
|
break;
|
|
case 'r':
|
|
ReloadUser = TRUE;
|
|
break;
|
|
default:
|
|
dprintf("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Base = GetExpression();
|
|
}
|
|
|
|
// Save the current setting in case things fail and
|
|
// it needs to be restored.
|
|
IMPLICIT_THREAD_SAVE Save;
|
|
g_Process->SaveImplicitThread(&Save);
|
|
|
|
if (g_Process->SetImplicitThreadData(g_Thread, Base, TRUE) == S_OK &&
|
|
(!IS_KERNEL_TARGET(g_Target) ||
|
|
SetScopeContextFromThreadData(Base, TRUE) == S_OK))
|
|
{
|
|
dprintf("Implicit thread is now %s\n",
|
|
FormatAddr64(g_Process->m_ImplicitThreadData));
|
|
|
|
if (IS_KERNEL_TARGET(g_Target) &&
|
|
g_Process->m_ImplicitThreadData &&
|
|
Ptes)
|
|
{
|
|
ULONG64 Process;
|
|
|
|
if (!Base || g_Target->
|
|
GetProcessInfoDataOffset(NULL, 0,
|
|
g_Process->m_ImplicitThreadData,
|
|
&Process) == S_OK)
|
|
{
|
|
SetImplicitProcessAndCache(Base ? Process : 0, Ptes,
|
|
ReloadUser);
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Unable to get process of implicit thread\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_Process->RestoreImplicitThread(&Save);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
KernelPageIn(ULONG64 Process, ULONG64 Data, BOOL Kill)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Work;
|
|
|
|
ULONG64 ExpDebuggerProcessKill = 0;
|
|
ULONG64 ExpDebuggerProcessAttach = 0;
|
|
ULONG64 ExpDebuggerPageIn = 0;
|
|
ULONG64 ExpDebuggerWork = 0;
|
|
|
|
if (!IS_LIVE_KERNEL_TARGET(g_Target))
|
|
{
|
|
ErrOut("This operation only works on live kernel debug sessions\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!ExpDebuggerProcessKill", &ExpDebuggerProcessKill,
|
|
NULL);
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!ExpDebuggerProcessAttach", &ExpDebuggerProcessAttach,
|
|
NULL);
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!ExpDebuggerPageIn", &ExpDebuggerPageIn, NULL);
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!ExpDebuggerWork", &ExpDebuggerWork, NULL);
|
|
|
|
if ((Kill && !ExpDebuggerProcessKill) ||
|
|
!ExpDebuggerProcessAttach ||
|
|
!ExpDebuggerPageIn ||
|
|
!ExpDebuggerWork)
|
|
{
|
|
ErrOut("Symbols are wrong or this version of the operating system "
|
|
"does not support this command\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
Status = g_Target->ReadAllVirtual(g_Process,
|
|
ExpDebuggerWork, &Work, sizeof(Work));
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Could not determine status or debugger worker thread\n");
|
|
return HRESULT_FROM_WIN32(ERROR_BUSY);
|
|
}
|
|
else if (Work > 1)
|
|
{
|
|
ErrOut("Debugger worker thread has pending command\n");
|
|
return HRESULT_FROM_WIN32(ERROR_BUSY);
|
|
}
|
|
|
|
if (Kill)
|
|
{
|
|
Status = g_Target->WritePointer(g_Process, g_Machine,
|
|
ExpDebuggerProcessKill,
|
|
Process);
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->WritePointer(g_Process, g_Machine,
|
|
ExpDebuggerProcessAttach,
|
|
Process);
|
|
if (Status == S_OK)
|
|
{
|
|
Status = g_Target->WritePointer(g_Process, g_Machine,
|
|
ExpDebuggerPageIn, Data);
|
|
}
|
|
}
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
Work = 1;
|
|
Status = g_Target->
|
|
WriteAllVirtual(g_Process, ExpDebuggerWork, &Work, sizeof(Work));
|
|
}
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Could not queue operation to debugger worker thread\n");
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
DotProcess(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_Thread)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
ULONG64 Base = 0;
|
|
BOOL Ptes = FALSE;
|
|
BOOL Invasive = FALSE;
|
|
BOOL ReloadUser = FALSE;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*(++g_CurCmd))
|
|
{
|
|
case 'i':
|
|
Invasive = TRUE;
|
|
break;
|
|
case 'p':
|
|
Ptes = TRUE;
|
|
break;
|
|
case 'r':
|
|
ReloadUser = TRUE;
|
|
break;
|
|
default:
|
|
dprintf("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Base = GetExpression();
|
|
}
|
|
|
|
if (Invasive)
|
|
{
|
|
if (S_OK == KernelPageIn(Base, 0, FALSE))
|
|
{
|
|
dprintf("You need to continue execution (press 'g' <enter>) "
|
|
"for the context\nto be switched. When the debugger "
|
|
"breaks in again, you will be in\nthe new process "
|
|
"context.\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
SetImplicitProcessAndCache(Base, Ptes, ReloadUser);
|
|
}
|
|
|
|
void
|
|
DotFiber(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Base = 0;
|
|
|
|
if (!g_Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Base = GetExpression();
|
|
}
|
|
|
|
if (Base)
|
|
{
|
|
HRESULT Status;
|
|
DEBUG_STACK_FRAME Frame;
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
|
|
if ((Status = g_Machine->GetContextFromFiber(g_Process, Base, &Context,
|
|
TRUE)) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_Machine->GetScopeFrameFromContext(&Context, &Frame);
|
|
SetCurrentScope(&Frame, &Context, sizeof(Context));
|
|
if (StackTrace(Client,
|
|
Frame.FrameOffset, Frame.StackOffset,
|
|
Frame.InstructionOffset, STACK_NO_DEFAULT,
|
|
&Frame, 1, 0, 0, FALSE) != 1)
|
|
{
|
|
ErrOut("Unable to walk fiber stack\n");
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
else if (GetCurrentScopeContext())
|
|
{
|
|
dprintf("Resetting default context\n");
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotKernelKill(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Process = 0;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Process = GetExpression();
|
|
}
|
|
|
|
if (Process)
|
|
{
|
|
KernelPageIn(Process, 0, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotPageIn(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Process = 0;
|
|
ULONG64 Data = 0;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*(++g_CurCmd))
|
|
{
|
|
case 'p':
|
|
g_CurCmd++;
|
|
Process = GetExpression();
|
|
break;
|
|
default:
|
|
g_CurCmd++;
|
|
dprintf("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Data = GetExpression();
|
|
}
|
|
|
|
if (!Data)
|
|
{
|
|
ErrOut("Pagein requires an address to be specified\n");
|
|
return;
|
|
}
|
|
|
|
if (Data > g_Target->m_SystemRangeStart)
|
|
{
|
|
ErrOut("Pagein operations are only supported for user mode"
|
|
" addresses due to limitations in the memory manager\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Modify kernel state to do the pagein
|
|
//
|
|
|
|
if (S_OK != KernelPageIn(Process, Data, FALSE))
|
|
{
|
|
ErrOut("PageIn for address %s, process %s failed\n",
|
|
FormatAddr64(Data),
|
|
FormatAddr64(Process));
|
|
}
|
|
else
|
|
{
|
|
dprintf("You need to continue execution (press 'g' <enter>) for "
|
|
"the pagein to be brought in. When the debugger breaks in "
|
|
"again, the page will be present.\n");
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetEventSystem(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
ENTER_ENGINE();
|
|
|
|
HRESULT Status;
|
|
|
|
if (!g_EventTarget)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_EventTarget->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentSystemId(
|
|
THIS_
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
ENTER_ENGINE();
|
|
|
|
HRESULT Status;
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Id = g_Target->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::SetCurrentSystemId(
|
|
THIS_
|
|
IN ULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (IS_RUNNING(g_CmdState))
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
TargetInfo* Target = FindTargetByUserId(Id);
|
|
if (Target && Target == g_Target)
|
|
{
|
|
// Requested system is already current.
|
|
Status = S_OK;
|
|
}
|
|
else if (Target)
|
|
{
|
|
ThreadInfo* Thread = NULL;
|
|
|
|
//
|
|
// Systems can exist without processes and threads
|
|
// so allow such a system to be current even
|
|
// if there is no process or thread.
|
|
//
|
|
if (Target->m_CurrentProcess == NULL)
|
|
{
|
|
Target->m_CurrentProcess = Target->m_ProcessHead;
|
|
}
|
|
if (Target->m_CurrentProcess)
|
|
{
|
|
if (Target->m_CurrentProcess->m_CurrentThread == NULL)
|
|
{
|
|
Target->m_CurrentProcess->m_CurrentThread =
|
|
Target->m_CurrentProcess->m_ThreadHead;
|
|
}
|
|
Thread = Target->m_CurrentProcess->m_CurrentThread;
|
|
}
|
|
|
|
if (Thread)
|
|
{
|
|
Status = Target->SwitchToTarget(g_Target);
|
|
if (Status == S_OK)
|
|
{
|
|
SetCurrentThread(Thread, FALSE);
|
|
ResetCurrentScopeLazy();
|
|
}
|
|
else if (Status == S_FALSE)
|
|
{
|
|
// The switch requires a wait.
|
|
Status = RawWaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLayersFromTarget(Target);
|
|
// Notify that there is no current thread.
|
|
NotifyChangeEngineState(DEBUG_CES_CURRENT_THREAD,
|
|
DEBUG_ANY_ID, TRUE);
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetNumberSystems(
|
|
THIS_
|
|
OUT PULONG Number
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
*Number = g_NumberTargets;
|
|
Status = S_OK;
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetSystemIdsByIndex(
|
|
THIS_
|
|
IN ULONG Start,
|
|
IN ULONG Count,
|
|
OUT /* size_is(Count) */ PULONG Ids
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
TargetInfo* Target;
|
|
ULONG Index;
|
|
|
|
if (Start >= g_NumberTargets ||
|
|
Start + Count > g_NumberTargets)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
goto EH_Exit;
|
|
}
|
|
|
|
Index = 0;
|
|
ForAllLayersToTarget()
|
|
{
|
|
if (Index >= Start && Index < Start + Count)
|
|
{
|
|
if (Ids != NULL)
|
|
{
|
|
*Ids++ = Target->m_UserId;
|
|
}
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
EH_Exit:
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetTotalNumberThreadsAndProcesses(
|
|
THIS_
|
|
OUT PULONG TotalThreads,
|
|
OUT PULONG TotalProcesses,
|
|
OUT PULONG LargestProcessThreads,
|
|
OUT PULONG LargestSystemThreads,
|
|
OUT PULONG LargestSystemProcesses
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
ULONG TotThreads = 0;
|
|
ULONG TotProcs = 0;
|
|
ULONG LargePt = 0;
|
|
ULONG LargeSt = 0;
|
|
ULONG LargeSp = 0;
|
|
TargetInfo* Target;
|
|
|
|
ForAllLayersToTarget()
|
|
{
|
|
TotThreads += Target->m_TotalNumberThreads;
|
|
TotProcs += Target->m_NumProcesses;
|
|
if (Target->m_MaxThreadsInProcess > LargePt)
|
|
{
|
|
LargePt = Target->m_MaxThreadsInProcess;
|
|
}
|
|
if (Target->m_TotalNumberThreads > LargeSt)
|
|
{
|
|
LargeSt = Target->m_TotalNumberThreads;
|
|
}
|
|
if (Target->m_NumProcesses > LargeSp)
|
|
{
|
|
LargeSp = Target->m_NumProcesses;
|
|
}
|
|
}
|
|
|
|
*TotalThreads = TotThreads;
|
|
*TotalProcesses = TotProcs;
|
|
*LargestProcessThreads = LargePt;
|
|
*LargestSystemThreads = LargeSt;
|
|
*LargestSystemProcesses = LargeSp;
|
|
|
|
Status = S_OK;
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentSystemServer(
|
|
THIS_
|
|
OUT PULONG64 Server
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
*Server = (!IS_LIVE_USER_TARGET(g_Target) ||
|
|
((LiveUserTargetInfo*)g_Target)->m_Local) ?
|
|
0 : (ULONG64)((LiveUserTargetInfo*)g_Target)->m_Services;
|
|
Status = S_OK;
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetSystemByServer(
|
|
THIS_
|
|
IN ULONG64 Server,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
TargetInfo* Target = FindTargetByServer(Server);
|
|
if (Target)
|
|
{
|
|
*Id = Target->m_UserId;
|
|
Status = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DebugClient::GetCurrentSystemServerName(
|
|
THIS_
|
|
OUT OPTIONAL PSTR Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG NameSize
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
ENTER_ENGINE();
|
|
|
|
if (!g_Target)
|
|
{
|
|
Status = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
Status = g_Target->
|
|
GetDescription(Buffer, BufferSize, NameSize);
|
|
}
|
|
|
|
LEAVE_ENGINE();
|
|
return Status;
|
|
}
|