//---------------------------------------------------------------------------- // // IDebugSystemObjects implementations. // // Copyright (C) Microsoft Corporation, 1999-2001. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" ULONG64 g_ImplicitThreadData; BOOL g_ImplicitThreadDataIsDefault = TRUE; ULONG64 g_ImplicitProcessData; BOOL g_ImplicitProcessDataIsDefault = TRUE; //---------------------------------------------------------------------------- // // Utility functions. // //---------------------------------------------------------------------------- HRESULT EmulateNtSelDescriptor(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? // switch(Selector) { case X86_KGDT_R3_TEB + 3: HRESULT Status; // In user mode fs points to the TEB so fake up // a selector for it. if ((Status = GetImplicitThreadDataTeb(&Desc->Base)) != S_OK) { return Status; } if (Machine != g_TargetMachine) { 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 = g_Target->ReadVirtual(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; break; case X86_KGDT_R3_DATA + 3: Desc->Base = 0; Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff; Type = 0x13; Gran = X86_DESC_GRANULARITY; break; default: 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); break; } Desc->Flags = Gran | X86_DESC_DEFAULT_BIG | X86_DESC_PRESENT | Type | (IS_USER_TARGET() ? (3 << X86_DESC_PRIVILEGE_SHIFT) : (Selector & 3)); return S_OK; } 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(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; ULONG Result; Status = g_Target->ReadVirtual(Base + Index, &X86Desc, sizeof(X86Desc), &Result); if (Status != S_OK) { return Status; } if (Result != sizeof(X86Desc)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } X86DescriptorToDescriptor64(&X86Desc, Desc); return S_OK; } //---------------------------------------------------------------------------- // // TargetInfo system object methods. // //---------------------------------------------------------------------------- HRESULT TargetInfo::GetProcessorSystemDataOffset( IN ULONG Processor, IN ULONG Index, OUT PULONG64 Offset ) { if (!IS_KERNEL_TARGET()) { return E_UNEXPECTED; } // XXX drewb - Temporary until different OS support is // sorted out. if (g_ActualSystemVersion < NT_SVER_START || g_ActualSystemVersion > NT_SVER_END) { return E_UNEXPECTED; } HRESULT Status; ULONG Read; if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) { DESCRIPTOR64 Entry; // // We always need the PCR address so go ahead and get it. // if (!IS_CONTEXT_ACCESSIBLE()) { 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, g_TargetMachine->m_OffsetSpecialRegisters + FIELD_OFFSET(X86_KSPECIAL_REGISTERS, Gdtr), &GdtDesc, sizeof(GdtDesc), &Read)) != S_OK || Read != sizeof(GdtDesc) || (Status = ReadX86Descriptor(X86_KGDT_R0_PCR, 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(g_TargetMachine, VIRTUAL_THREAD_HANDLE(Processor), X86_KGDT_R0_PCR, &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(g_TargetMachine, Entry.Base + FIELD_OFFSET(X86_KPCR, SelfPcr), 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(g_TargetMachine, Entry.Base + FIELD_OFFSET(X86_KPCR, Prcb), Offset); if (Status != S_OK) { return Status; } if (Index == DEBUG_DATA_KPRCB_OFFSET) { break; } Status = ReadPointer(g_TargetMachine, *Offset + KPRCB_CURRENT_THREAD_OFFSET_32, Offset); if (Status != S_OK) { return Status; } break; } } else { ULONG ReadSize = g_TargetMachine->m_Ptr64 ? sizeof(ULONG64) : sizeof(ULONG); ULONG64 Address; switch(g_TargetMachineType) { case IMAGE_FILE_MACHINE_ALPHA: case IMAGE_FILE_MACHINE_AXP64: switch(Index) { case DEBUG_DATA_KPCR_OFFSET: Index = ALPHA_DEBUG_CONTROL_SPACE_PCR; break; case DEBUG_DATA_KPRCB_OFFSET: Index = ALPHA_DEBUG_CONTROL_SPACE_PRCB; break; case DEBUG_DATA_KTHREAD_OFFSET: Index = ALPHA_DEBUG_CONTROL_SPACE_THREAD; break; } break; 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 (!g_TargetMachine->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), g_TargetMachine->m_OffsetSpecialRegisters, Special, g_TargetMachine->m_SizeKspecialRegisters, &Done); if (Status != S_OK) { return Status; } return Done == g_TargetMachine->m_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), g_TargetMachine->m_OffsetSpecialRegisters, Special, g_TargetMachine->m_SizeKspecialRegisters, &Done); if (Status != S_OK) { return Status; } return Done == g_TargetMachine->m_SizeKspecialRegisters ? S_OK : E_FAIL; } void TargetInfo::InvalidateTargetContext(void) { // Nothing to do. } HRESULT TargetInfo::GetContext( ULONG64 Thread, PCROSS_PLATFORM_CONTEXT Context ) { if (g_TargetMachine == NULL) { return E_UNEXPECTED; } HRESULT Status; CROSS_PLATFORM_CONTEXT TargetContextBuffer; PCROSS_PLATFORM_CONTEXT TargetContext; if (g_TargetMachine->m_SverCanonicalContext <= g_SystemVersion) { TargetContext = Context; } else { TargetContext = &TargetContextBuffer; g_TargetMachine->InitializeContextFlags(TargetContext, g_SystemVersion); } Status = GetTargetContext(Thread, TargetContext); if (Status == S_OK && TargetContext == &TargetContextBuffer) { Status = g_TargetMachine-> ConvertContextFrom(Context, g_SystemVersion, g_TargetMachine->m_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 (g_TargetMachine == NULL) { return E_UNEXPECTED; } CROSS_PLATFORM_CONTEXT TargetContextBuffer; PCROSS_PLATFORM_CONTEXT TargetContext; if (g_TargetMachine->m_SverCanonicalContext <= g_SystemVersion) { TargetContext = Context; } else { TargetContext = &TargetContextBuffer; HRESULT Status = g_TargetMachine-> ConvertContextTo(Context, g_SystemVersion, g_TargetMachine->m_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::KdGetThreadInfoDataOffset(PTHREAD_INFO Thread, ULONG64 ThreadHandle, PULONG64 Offset) { if (Thread != NULL && Thread->DataOffset != 0) { *Offset = Thread->DataOffset; return S_OK; } ULONG Processor; if (Thread != NULL) { ThreadHandle = Thread->Handle; } Processor = VIRTUAL_THREAD_INDEX(ThreadHandle); HRESULT Status; Status = GetProcessorSystemDataOffset(Processor, DEBUG_DATA_KTHREAD_OFFSET, Offset); if (Status == S_OK && Thread != NULL) { Thread->DataOffset = *Offset; } return Status; } HRESULT TargetInfo::KdGetProcessInfoDataOffset(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { // Process data offsets are not cached for kernel mode // since the only PROCESS_INFO 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 += g_TargetMachine->m_OffsetKThreadApcProcess; Status = ReadPointer(g_TargetMachine, ThreadData, &ProcessAddr); if (Status != S_OK) { ErrOut("Unable to read KTHREAD address %p\n", ThreadData); } else { *Offset = ProcessAddr; } return Status; } HRESULT TargetInfo::KdGetThreadInfoTeb(PTHREAD_INFO 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 += g_TargetMachine->m_OffsetKThreadTeb; Status = ReadPointer(g_TargetMachine, ThreadData, &TebAddr); if (Status != S_OK) { ErrOut("Could not read KTHREAD address %p\n", ThreadData); } else { *Offset = TebAddr; } return Status; } HRESULT TargetInfo::KdGetProcessInfoPeb(PTHREAD_INFO 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 += g_TargetMachine->m_OffsetEprocessPeb; Status = ReadPointer(g_TargetMachine, Process, &PebAddr); if (Status != S_OK) { ErrOut("Could not read KPROCESS address %p\n", Process); } else { *Offset = PebAddr; } return Status; } #define SELECTOR_CACHE_LENGTH 6 typedef struct _sc { struct _sc *nextYoungest; struct _sc *nextOldest; ULONG Processor; ULONG Selector; DESCRIPTOR64 Desc; } SELCACHEENTRY; SELCACHEENTRY SelectorCache[SELECTOR_CACHE_LENGTH], *selYoungest, *selOldest; void InitSelCache(void) { int i; for (i = 0; i < SELECTOR_CACHE_LENGTH; i++) { SelectorCache[i].nextYoungest = &SelectorCache[i+1]; SelectorCache[i].nextOldest = &SelectorCache[i-1]; SelectorCache[i].Processor = (LONG) -1; SelectorCache[i].Selector = 0; } SelectorCache[--i].nextYoungest = NULL; SelectorCache[0].nextOldest = NULL; selYoungest = &SelectorCache[i]; selOldest = &SelectorCache[0]; } BOOL FindSelector(ULONG Processor, ULONG Selector, PDESCRIPTOR64 Desc) { int i; for (i = 0; i < SELECTOR_CACHE_LENGTH; i++) { if (SelectorCache[i].Selector == Selector && SelectorCache[i].Processor == Processor) { *Desc = SelectorCache[i].Desc; return TRUE; } } return FALSE; } void PutSelector(ULONG Processor, ULONG Selector, PDESCRIPTOR64 Desc) { selOldest->Processor = Processor; selOldest->Selector = Selector; selOldest->Desc = *Desc; (selOldest->nextYoungest)->nextOldest = NULL; selOldest->nextOldest = selYoungest; selYoungest->nextYoungest = selOldest; selYoungest = selOldest; selOldest = selOldest->nextYoungest; } HRESULT TargetInfo::KdGetSelDescriptor(MachineInfo* Machine, ULONG64 Thread, ULONG Selector, PDESCRIPTOR64 Desc) { ULONG Processor = VIRTUAL_THREAD_INDEX(Thread); if (FindSelector(Processor, Selector, Desc)) { return S_OK; } PTHREAD_INFO CtxThread, ProcThread; ProcThread = FindThreadByHandle(g_CurrentProcess, Thread); if (ProcThread == NULL) { return E_INVALIDARG; } CtxThread = g_RegContextThread; ChangeRegContext(ProcThread); 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(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(PTHREAD_INFO Thread, ULONG64 ThreadHandle, PULONG64 Offset) { return KdGetThreadInfoDataOffset(Thread, ThreadHandle, Offset); } HRESULT LiveKernelTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { return KdGetProcessInfoDataOffset(Thread, Processor, ThreadData, Offset); } HRESULT LiveKernelTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread, ULONG ThreadIndex, ULONG64 ThreadData, PULONG64 Offset) { return KdGetThreadInfoTeb(Thread, ThreadIndex, ThreadData, Offset); } HRESULT LiveKernelTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { return KdGetProcessInfoPeb(Thread, Processor, ThreadData, Offset); } HRESULT LiveKernelTargetInfo::GetSelDescriptor(MachineInfo* Machine, ULONG64 Thread, ULONG Selector, PDESCRIPTOR64 Desc) { return KdGetSelDescriptor(Machine, Thread, Selector, Desc); } //---------------------------------------------------------------------------- // // ConnLiveKernelTargetInfo system object methods. // //---------------------------------------------------------------------------- HRESULT ConnLiveKernelTargetInfo::GetTargetContext( ULONG64 Thread, PVOID Context ) { NTSTATUS Status = DbgKdGetContext((USHORT)VIRTUAL_THREAD_INDEX(Thread), (PCROSS_PLATFORM_CONTEXT)Context); return CONV_NT_STATUS(Status); } HRESULT ConnLiveKernelTargetInfo::SetTargetContext( ULONG64 Thread, PVOID Context ) { NTSTATUS Status = DbgKdSetContext((USHORT)VIRTUAL_THREAD_INDEX(Thread), (PCROSS_PLATFORM_CONTEXT)Context); return CONV_NT_STATUS(Status); } //---------------------------------------------------------------------------- // // 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 || g_TargetMachineType != 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(g_TargetMachine, GsDesc.Base + FIELD_OFFSET(AMD64_KPCR, CurrentPrcb), Offset); if (Status != S_OK) { return Status; } if (Index == DEBUG_DATA_KPRCB_OFFSET) { break; } Status = ReadPointer(g_TargetMachine, *Offset + KPRCB_CURRENT_THREAD_OFFSET_64, 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 = g_TargetMachine-> GetExdiContext(m_Context, &m_ContextData)) != S_OK) { return Status; } m_ContextValid = TRUE; } g_TargetMachine->ConvertExdiContextToContext (&m_ContextData, (PCROSS_PLATFORM_CONTEXT)Context); return S_OK; } HRESULT ExdiLiveKernelTargetInfo::SetTargetContext( ULONG64 Thread, PVOID Context ) { if (!m_ContextValid) { HRESULT Status; if ((Status = g_TargetMachine-> GetExdiContext(m_Context, &m_ContextData)) != S_OK) { return Status; } m_ContextValid = TRUE; } g_TargetMachine->ConvertExdiContextFromContext ((PCROSS_PLATFORM_CONTEXT)Context, &m_ContextData); return g_TargetMachine->SetExdiContext(m_Context, &m_ContextData); } HRESULT ExdiLiveKernelTargetInfo::GetTargetSegRegDescriptors(ULONG64 Thread, ULONG Start, ULONG Count, PDESCRIPTOR64 Descs) { if (!m_ContextValid) { HRESULT Status; if ((Status = g_TargetMachine-> GetExdiContext(m_Context, &m_ContextData)) != S_OK) { return Status; } m_ContextValid = TRUE; } g_TargetMachine->ConvertExdiContextToSegDescs(&m_ContextData, 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 = g_TargetMachine-> GetExdiContext(m_Context, &m_ContextData)) != S_OK) { return Status; } m_ContextValid = TRUE; } g_TargetMachine->ConvertExdiContextToSpecial(&m_ContextData, Special); return S_OK; } HRESULT ExdiLiveKernelTargetInfo::SetTargetSpecialRegisters (ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special) { if (!m_ContextValid) { HRESULT Status; if ((Status = g_TargetMachine-> GetExdiContext(m_Context, &m_ContextData)) != S_OK) { return Status; } m_ContextValid = TRUE; } g_TargetMachine->ConvertExdiContextFromSpecial(Special, &m_ContextData); return g_TargetMachine->SetExdiContext(m_Context, &m_ContextData); } //---------------------------------------------------------------------------- // // UserTargetInfo system object methods. // //---------------------------------------------------------------------------- HRESULT UserTargetInfo::GetTargetContext( ULONG64 Thread, PVOID Context ) { return m_Services-> // g_TargetMachine should be used because of Wx86 debugging GetContext(Thread, *(PULONG)((PUCHAR)Context + g_TargetMachine->m_OffsetTargetContextFlags), g_TargetMachine->m_OffsetTargetContextFlags, Context, g_TargetMachine->m_SizeTargetContext, NULL); } HRESULT UserTargetInfo::SetTargetContext( ULONG64 Thread, PVOID Context ) { return m_Services->SetContext(Thread, Context, // g_TargetMachine should be used because of Wx86 debugging g_TargetMachine->m_SizeTargetContext, NULL); } HRESULT UserTargetInfo::GetThreadInfoDataOffset(PTHREAD_INFO Thread, ULONG64 ThreadHandle, PULONG64 Offset) { if (Thread != NULL && Thread->DataOffset != 0) { *Offset = Thread->DataOffset; return S_OK; } if (Thread != NULL) { ThreadHandle = Thread->Handle; } else if (ThreadHandle == NULL) { ThreadHandle = g_CurrentProcess->CurrentThread->Handle; } HRESULT Status = m_Services-> GetThreadDataOffset(ThreadHandle, Offset); if (Status == S_OK) { if (Thread != NULL) { Thread->DataOffset = *Offset; } } return Status; } HRESULT UserTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { HRESULT Status; PPROCESS_INFO Process; // Processor isn't any use so default to the current thread. if (Thread == NULL) { Process = g_CurrentProcess; } else { Process = Thread->Process; } if (ThreadData == 0 && Process->DataOffset != 0) { *Offset = Process->DataOffset; return S_OK; } if (ThreadData != 0) { if (g_DebuggerPlatformId == VER_PLATFORM_WIN32_NT) { ThreadData += g_TargetMachine->m_Ptr64 ? PEB_FROM_TEB64 : PEB_FROM_TEB32; Status = ReadPointer(g_TargetMachine, ThreadData, Offset); } else { Status = E_NOTIMPL; } } else { Status = m_Services->GetProcessDataOffset(Process->FullHandle, Offset); } if (Status == S_OK) { if (ThreadData == 0) { Process->DataOffset = *Offset; } } return Status; } HRESULT UserTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { return GetThreadInfoDataOffset(Thread, ThreadData, Offset); } HRESULT UserTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread, ULONG Processor, ULONG64 ThreadData, PULONG64 Offset) { // Thread data is not useful. return GetProcessInfoDataOffset(Thread, 0, 0, Offset); } HRESULT UserTargetInfo::GetSelDescriptor(MachineInfo* Machine, ULONG64 Thread, ULONG Selector, PDESCRIPTOR64 Desc) { HRESULT Status; ULONG Used; X86_LDT_ENTRY X86Desc; if ((Status = m_Services-> DescribeSelector(Thread, Selector, &X86Desc, sizeof(X86Desc), &Used)) != S_OK) { return Status; } if (Used != sizeof(X86Desc)) { return E_FAIL; } X86DescriptorToDescriptor64(&X86Desc, Desc); return S_OK; } //---------------------------------------------------------------------------- // // IDebugSystemObjects methods. // //---------------------------------------------------------------------------- STDMETHODIMP DebugClient::GetEventThread( THIS_ OUT PULONG Id ) { ENTER_ENGINE(); HRESULT Status; if (g_EventThread == NULL) { Status = E_UNEXPECTED; } else { *Id = g_EventThread->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->UserId; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentThreadId( THIS_ OUT PULONG Id ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL || g_CurrentProcess->CurrentThread == NULL) { Status = E_UNEXPECTED; } else { *Id = g_CurrentProcess->CurrentThread->UserId; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::SetCurrentThreadId( THIS_ IN ULONG Id ) { ENTER_ENGINE(); HRESULT Status; PTHREAD_INFO Thread = FindThreadByUserId(NULL, 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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *Id = g_CurrentProcess->UserId; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::SetCurrentProcessId( THIS_ IN ULONG Id ) { ENTER_ENGINE(); HRESULT Status; PPROCESS_INFO Process = FindProcessByUserId(Id); if (Process != NULL) { if (Process->CurrentThread == NULL) { Process->CurrentThread = Process->ThreadHead; } if (Process->CurrentThread == NULL) { Status = E_FAIL; } else { SetCurrentThread(Process->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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *Number = g_CurrentProcess->NumberThreads; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetTotalNumberThreads( THIS_ OUT PULONG Total, OUT PULONG LargestProcess ) { ENTER_ENGINE(); *Total = g_TotalNumberThreads; *LargestProcess = g_MaxThreadsInProcess; LEAVE_ENGINE(); return S_OK; } 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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { PTHREAD_INFO Thread; ULONG Index; if (Start >= g_CurrentProcess->NumberThreads || Start + Count > g_CurrentProcess->NumberThreads) { Status = E_INVALIDARG; } else { Index = 0; for (Thread = g_CurrentProcess->ThreadHead; Thread != NULL; Thread = Thread->Next) { if (Index >= Start && Index < Start + Count) { if (Ids != NULL) { *Ids++ = Thread->UserId; } if (SysIds != NULL) { *SysIds++ = Thread->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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { ULONG SysId; Status = g_Target->GetThreadIdByProcessor(Processor, &SysId); if (Status == S_OK) { PTHREAD_INFO Thread = FindThreadBySystemId(g_CurrentProcess, SysId); if (Thread != NULL) { *Id = Thread->UserId; } else { Status = E_NOINTERFACE; } } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentThreadDataOffset( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { Status = g_Target-> GetThreadInfoDataOffset(g_CurrentProcess->CurrentThread, 0, Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetThreadIdByDataOffset( THIS_ IN ULONG64 Offset, OUT PULONG Id ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { PTHREAD_INFO Thread; Status = E_NOINTERFACE; for (Thread = g_CurrentProcess->ThreadHead; Thread != NULL; Thread = Thread->Next) { HRESULT Status; ULONG64 DataOffset; Status = g_Target->GetThreadInfoDataOffset(Thread, 0, &DataOffset); if (Status != S_OK) { break; } if (DataOffset == Offset) { *Id = Thread->UserId; Status = S_OK; break; } } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentThreadTeb( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { Status = g_Target->GetThreadInfoTeb(g_CurrentProcess->CurrentThread, 0, 0, Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetThreadIdByTeb( THIS_ IN ULONG64 Offset, OUT PULONG Id ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { PTHREAD_INFO Thread; Status = E_NOINTERFACE; for (Thread = g_CurrentProcess->ThreadHead; Thread != NULL; Thread = Thread->Next) { HRESULT Status; ULONG64 Teb; Status = g_Target->GetThreadInfoTeb(Thread, 0, 0, &Teb); if (Status != S_OK) { break; } if (Teb == Offset) { *Id = Thread->UserId; Status = S_OK; break; } } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentThreadSystemId( THIS_ OUT PULONG SysId ) { if (IS_KERNEL_TARGET()) { return E_NOTIMPL; } HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *SysId = g_CurrentProcess->CurrentThread->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()) { Status = E_NOTIMPL; } else if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { PTHREAD_INFO Thread = FindThreadBySystemId(g_CurrentProcess, SysId); if (Thread != NULL) { *Id = Thread->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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *Handle = g_CurrentProcess->CurrentThread->Handle; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetThreadIdByHandle( THIS_ IN ULONG64 Handle, OUT PULONG Id ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { PTHREAD_INFO Thread = FindThreadByHandle(g_CurrentProcess, Handle); if (Thread != NULL) { *Id = Thread->UserId; Status = S_OK; } else { Status = E_NOINTERFACE; } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetNumberProcesses( THIS_ OUT PULONG Number ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } ENTER_ENGINE(); *Number = g_NumberProcesses; LEAVE_ENGINE(); return S_OK; } STDMETHODIMP DebugClient::GetProcessIdsByIndex( THIS_ IN ULONG Start, IN ULONG Count, OUT OPTIONAL /* size_is(Count) */ PULONG Ids, OUT OPTIONAL /* size_is(Count) */ PULONG SysIds ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } HRESULT Status; ENTER_ENGINE(); PPROCESS_INFO Process; ULONG Index; if (Start >= g_NumberProcesses || Start + Count > g_NumberProcesses) { Status = E_INVALIDARG; goto EH_Exit; } Index = 0; for (Process = g_ProcessHead; Process != NULL; Process = Process->Next) { if (Index >= Start && Index < Start + Count) { if (Ids != NULL) { *Ids++ = Process->UserId; } if (SysIds != NULL) { *SysIds++ = Process->SystemId; } } Index++; } Status = S_OK; EH_Exit: LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentProcessDataOffset( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { Status = g_Target-> GetProcessInfoDataOffset(g_CurrentProcess->CurrentThread, 0, 0, Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetProcessIdByDataOffset( THIS_ IN ULONG64 Offset, OUT PULONG Id ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } if (IS_KERNEL_TARGET()) { return E_NOTIMPL; } HRESULT Status; PPROCESS_INFO Process; ENTER_ENGINE(); Status = E_NOINTERFACE; for (Process = g_ProcessHead; Process != NULL; Process = Process->Next) { ULONG64 DataOffset; Status = g_Target-> GetProcessInfoDataOffset(Process->ThreadHead, 0, 0, &DataOffset); if (Status != S_OK) { break; } if (DataOffset == Offset) { *Id = Process->UserId; Status = S_OK; break; } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentProcessPeb( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { Status = g_Target->GetProcessInfoPeb(g_CurrentProcess->CurrentThread, 0, 0, Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetProcessIdByPeb( THIS_ IN ULONG64 Offset, OUT PULONG Id ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } if (IS_KERNEL_TARGET()) { return E_NOTIMPL; } HRESULT Status; PPROCESS_INFO Process; ENTER_ENGINE(); Status = E_NOINTERFACE; for (Process = g_ProcessHead; Process != NULL; Process = Process->Next) { ULONG64 Peb; Status = g_Target->GetProcessInfoPeb(Process->ThreadHead, 0, 0, &Peb); if (Status != S_OK) { break; } if (Peb == Offset) { *Id = Process->UserId; Status = S_OK; break; } } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentProcessSystemId( THIS_ OUT PULONG SysId ) { if (IS_KERNEL_TARGET()) { return E_NOTIMPL; } HRESULT Status; ENTER_ENGINE(); if (g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *SysId = g_CurrentProcess->SystemId; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetProcessIdBySystemId( THIS_ IN ULONG SysId, OUT PULONG Id ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } if (IS_KERNEL_TARGET()) { return E_NOTIMPL; } HRESULT Status; ENTER_ENGINE(); PPROCESS_INFO Process = FindProcessBySystemId(SysId); if (Process != NULL) { *Id = Process->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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { *Handle = (ULONG64)g_CurrentProcess->Handle; Status = S_OK; } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetProcessIdByHandle( THIS_ IN ULONG64 Handle, OUT PULONG Id ) { if (!IS_TARGET_SET()) { return E_UNEXPECTED; } HRESULT Status; ENTER_ENGINE(); PPROCESS_INFO Process = FindProcessByHandle(Handle); if (Process != NULL) { *Id = Process->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_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else if (g_CurrentProcess->ExecutableImage == NULL) { Status = E_NOINTERFACE; } else { Status = FillStringBuffer(g_CurrentProcess->ExecutableImage->ImagePath, 0, Buffer, BufferSize, ExeSize); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::GetCurrentProcessUpTime( THIS_ OUT PULONG UpTime ) { HRESULT Status; ENTER_ENGINE(); if (!IS_LIVE_USER_TARGET() || g_CurrentProcess == NULL) { Status = E_UNEXPECTED; } else { ULONG64 LongUpTime; LongUpTime = g_Target->GetProcessUpTimeN(g_CurrentProcess->FullHandle); if (LongUpTime == 0) { Status = E_NOINTERFACE; } else { *UpTime = FileTimeToTime(LongUpTime); Status = S_OK; } } LEAVE_ENGINE(); return Status; } HRESULT GetContextFromThreadStack( ULONG64 ThreadBase, PCROSS_PLATFORM_CONTEXT Context, PDEBUG_STACK_FRAME StkFrame, BOOL Verbose ) { DBG_ASSERT(ThreadBase && Context != NULL); if (!IS_KERNEL_TARGET()) { return E_UNEXPECTED; } HRESULT Status; CROSS_PLATFORM_THREAD ThreadContents; DEBUG_STACK_FRAME LocalStkFrame; ULONG Proc; if (!StkFrame) { StkFrame = &LocalStkFrame; } ZeroMemory(StkFrame, sizeof(*StkFrame)); if ((Status = g_Target->ReadAllVirtual (ThreadBase, &ThreadContents, g_TargetMachine->m_SizePartialKThread)) != S_OK) { { ErrOut("Cannot read thread contents @ %s, %s\n", FormatAddr64(ThreadBase), FormatStatusCode(Status)); } return Status; } if (*((PCHAR) &ThreadContents) != 6) { ErrOut("Invalid thread @ %s type - context unchanged.\n", FormatAddr64(ThreadBase)); return E_INVALIDARG; } Status = g_TargetMachine->GetContextFromThreadStack (ThreadBase, &ThreadContents, Context, StkFrame, &Proc); if (Status == S_FALSE) { // Get the processor context if it's a valid processor. if (Proc < g_TargetNumberProcessors) { // This get may be getting the context of the thread // currently cached by the register code. Make sure // the cache is flushed. FlushRegContext(); g_TargetMachine->InitializeContextFlags(Context, g_SystemVersion); if ((Status = g_Target->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; } } else if (Status != S_OK) { if (Verbose) { ErrOut("Can't retrieve thread context, %s\n", FormatStatusCode(Status)); } return Status; } return S_OK; } HRESULT SetContextFromThreadData(ULONG64 ThreadBase, BOOL Verbose) { if (!ThreadBase) { if (GetCurrentScopeContext()) { ResetCurrentScope(); } return S_OK; } HRESULT Status; DEBUG_STACK_FRAME StkFrame = {0}; CROSS_PLATFORM_CONTEXT Context = {0}; if ((Status = GetContextFromThreadStack(ThreadBase, &Context, &StkFrame, FALSE)) != S_OK) { return Status; } SetCurrentScope(&StkFrame, &Context, sizeof(Context)); if (StackTrace(StkFrame.FrameOffset, StkFrame.StackOffset, StkFrame.InstructionOffset, &StkFrame, 1, 0, 0, FALSE) != 1) { if (Verbose) { ErrOut("Scope can't be set for thread %s\n", FormatAddr64(ThreadBase)); } ResetCurrentScope(); return E_FAIL; } return S_OK; } HRESULT GetImplicitThreadData(PULONG64 Offset) { HRESULT Status; if (g_ImplicitThreadData == 0) { Status = SetImplicitThreadData(0, FALSE); } else { Status = S_OK; } *Offset = g_ImplicitThreadData; return Status; } HRESULT GetImplicitThreadDataTeb(PULONG64 Offset) { if (IS_USER_TARGET()) { // In user mode the thread data is the TEB. return GetImplicitThreadData(Offset); } else { return g_Target->ReadImplicitThreadInfoPointer (g_TargetMachine->m_OffsetKThreadTeb, Offset); } } HRESULT SetImplicitThreadData(ULONG64 Offset, BOOL Verbose) { HRESULT Status; BOOL Default = FALSE; if (Offset == 0) { if ((Status = g_Target-> GetThreadInfoDataOffset(g_CurrentProcess->CurrentThread, 0, &Offset)) != S_OK) { if (Verbose) { ErrOut("Unable to get the current thread data\n"); } return Status; } if (Offset == 0) { if (Verbose) { ErrOut("Current thread data is NULL\n"); } return E_FAIL; } Default = TRUE; } if (IS_KERNEL_TARGET() && (Status = SetContextFromThreadData(Default ? 0 : Offset, Verbose)) != S_OK) { return Status; } g_ImplicitThreadData = Offset; g_ImplicitThreadDataIsDefault = Default; return S_OK; } STDMETHODIMP DebugClient::GetImplicitThreadDataOffset( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (!IS_MACHINE_ACCESSIBLE()) { Status = E_UNEXPECTED; } else { Status = ::GetImplicitThreadData(Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::SetImplicitThreadDataOffset( THIS_ IN ULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (!IS_MACHINE_ACCESSIBLE()) { Status = E_UNEXPECTED; } else { Status = ::SetImplicitThreadData(Offset, FALSE); } LEAVE_ENGINE(); return Status; } HRESULT GetImplicitProcessData(PULONG64 Offset) { HRESULT Status; if (g_ImplicitProcessData == 0) { Status = SetImplicitProcessData(0, FALSE); } else { Status = S_OK; } *Offset = g_ImplicitProcessData; return Status; } HRESULT GetImplicitProcessDataPeb(PULONG64 Offset) { if (IS_USER_TARGET()) { // In user mode the process data is the PEB. return GetImplicitProcessData(Offset); } else { return g_Target->ReadImplicitProcessInfoPointer (g_TargetMachine->m_OffsetEprocessPeb, Offset); } } HRESULT SetImplicitProcessData(ULONG64 Offset, BOOL Verbose) { HRESULT Status; BOOL Default = FALSE; if (Offset == 0) { if ((Status = g_Target-> GetProcessInfoDataOffset(g_CurrentProcess->CurrentThread, 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 = g_ImplicitProcessData; BOOL OldDefault = g_ImplicitProcessDataIsDefault; g_ImplicitProcessData = Offset; g_ImplicitProcessDataIsDefault = Default; if (IS_KERNEL_TARGET() && (Status = g_TargetMachine-> SetDefaultPageDirectories(PAGE_DIR_ALL)) != S_OK) { g_ImplicitProcessData = Old; g_ImplicitProcessDataIsDefault = OldDefault; if (Verbose) { ErrOut("Process %s has invalid page directories\n", FormatAddr64(Offset)); } return Status; } return S_OK; } STDMETHODIMP DebugClient::GetImplicitProcessDataOffset( THIS_ OUT PULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (!IS_MACHINE_ACCESSIBLE()) { Status = E_UNEXPECTED; } else { Status = ::GetImplicitProcessData(Offset); } LEAVE_ENGINE(); return Status; } STDMETHODIMP DebugClient::SetImplicitProcessDataOffset( THIS_ IN ULONG64 Offset ) { HRESULT Status; ENTER_ENGINE(); if (!IS_MACHINE_ACCESSIBLE()) { Status = E_UNEXPECTED; } else { Status = ::SetImplicitProcessData(Offset, FALSE); } LEAVE_ENGINE(); return Status; } void ResetImplicitData(void) { g_ImplicitThreadData = 0; g_ImplicitThreadDataIsDefault = TRUE; g_ImplicitProcessData = 0; g_ImplicitProcessDataIsDefault = TRUE; } void ParseSetImplicitThread(void) { ULONG64 Base = 0; if (PeekChar() && *g_CurCmd != ';') { Base = GetExpression(); } if (SetImplicitThreadData(Base, TRUE) == S_OK) { dprintf("Implicit thread is now %s\n", FormatAddr64(g_ImplicitThreadData)); } } HRESULT KernelPageIn(ULONG64 Process, ULONG64 Data) { HRESULT Status; ULONG work; ULONG Size; ULONG64 ExpDebuggerProcessAttach = 0; ULONG64 ExpDebuggerPageIn = 0; ULONG64 ExpDebuggerWork = 0; if (!IS_LIVE_KERNEL_TARGET()) { ErrOut("This operation only works on live kernel debug sessions\n"); return E_NOTIMPL; } GetOffsetFromSym("nt!ExpDebuggerProcessAttach", &ExpDebuggerProcessAttach, NULL); GetOffsetFromSym("nt!ExpDebuggerPageIn", &ExpDebuggerPageIn, NULL); GetOffsetFromSym("nt!ExpDebuggerWork", &ExpDebuggerWork, NULL); if (!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->ReadVirtual(ExpDebuggerWork, &work, sizeof(work), &Size); if ((Status != S_OK) || Size != sizeof(work)) { 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); } Status = g_Target->WritePointer(g_Machine, ExpDebuggerProcessAttach, Process); if (Status == S_OK) { Status = g_Target->WritePointer(g_Machine, ExpDebuggerPageIn, Data); } if (Status == S_OK) { work = 1; Status = g_Target->WriteVirtual(ExpDebuggerWork, &work, sizeof(work), &Size); } if (Status != S_OK) { ErrOut("Could not queue operation to debugger worker thread\n"); } return Status; } void ParseSetImplicitProcess(void) { ULONG64 Base = 0; BOOL Ptes = FALSE; BOOL Invasive = FALSE; BOOL OldPtes = g_VirtualCache.m_ForceDecodePTEs; while (PeekChar() == '-' || *g_CurCmd == '/') { switch(*(++g_CurCmd)) { case 'p': Ptes = TRUE; break; case 'i': Invasive = 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)) { dprintf("You need to continue execution (press 'g' ) for " "the context to be switched. When the debugger breaks " "in again, you will be in the new process context.\n"); } return; } 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_VirtualCache.SetForceDecodePtes(FALSE); } if (SetImplicitProcessData(Base, TRUE) == S_OK) { dprintf("Implicit process is now %s\n", FormatAddr64(g_ImplicitProcessData)); if (Ptes) { if (Base) { g_VirtualCache.SetForceDecodePtes(TRUE); } dprintf(".cache %sforcedecodeptes done\n", Base != 0 ? "" : "no"); } if (Base && !g_VirtualCache.m_ForceDecodePTEs) { WarnOut("WARNING: .cache forcedecodeptes is not enabled\n"); } } else if (Ptes && !Base && OldPtes) { // Restore settings to the way they were. g_VirtualCache.SetForceDecodePtes(TRUE); } } void ParsePageIn(void) { 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; } // // Modify kernel state to do the pagein // if (S_OK != KernelPageIn(Process, Data)) { ErrOut("PageIn for address %s, process %s failed\n", FormatAddr64(Data), FormatAddr64(Process)); } else { dprintf("You need to continue execution (press 'g' ) for " "the pagein to be brought in. When the debugger breaks in " "again, the page will be present.\n"); } }