Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5905 lines
149 KiB

//----------------------------------------------------------------------------
//
// IDebugDataSpaces implementations.
//
// Copyright (C) Microsoft Corporation, 1999-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
//----------------------------------------------------------------------------
//
// TargetInfo data space methods.
//
//----------------------------------------------------------------------------
void
TargetInfo::NearestDifferentlyValidOffsets(ULONG64 Offset,
PULONG64 NextOffset,
PULONG64 NextPage)
{
//
// In the default case we assume that address validity
// is controlled on a per-page basis so the next possibly
// valid page and offset are both the offset of the next
// page.
//
ULONG64 Page = NEXT_PAGE(m_Machine, Offset);
if (NextOffset != NULL)
{
*NextOffset = Page;
}
if (NextPage != NULL)
{
*NextPage = Page;
}
}
HRESULT
TargetInfo::ReadVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return ReadVirtual(Process, Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
TargetInfo::WriteVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
return WriteVirtual(Process, Offset, Buffer, BufferSize, BytesWritten);
}
// #define DBG_SEARCH
HRESULT
TargetInfo::SearchVirtual(
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN ULONG64 Length,
IN PVOID Pattern,
IN ULONG PatternSize,
IN ULONG PatternGranularity,
OUT PULONG64 MatchOffset
)
{
HRESULT Status;
ULONG64 SearchEnd;
UCHAR Buffer[4096];
PUCHAR Buf, Pat, BufEnd, PatEnd;
ULONG ReadLen;
ULONG64 BufOffset;
ULONG64 PatOffset;
ULONG64 StartOffset;
SearchEnd = Offset + Length;
Buf = Buffer;
BufEnd = Buffer;
Pat = (PUCHAR)Pattern;
PatEnd = Pat + PatternSize;
ReadLen = Length < sizeof(Buffer) ? (ULONG)Length : sizeof(Buffer);
BufOffset = Offset;
PatOffset = Offset;
StartOffset = Offset;
#ifdef DBG_SEARCH
g_NtDllCalls.DbgPrint("Search %d bytes from %I64X to %I64X, gran %X\n",
PatternSize, Offset, SearchEnd - 1,
Granularity);
#endif
for (;;)
{
#ifdef DBG_SEARCH_VERBOSE
g_NtDllCalls.DbgPrint(" %I64X: matched %d\n",
Offset + (Buf - Buffer),
(ULONG)(Pat - (PUCHAR)Pattern));
#endif
if (Pat == PatEnd)
{
// Made it to the end of the pattern so there's
// a match.
*MatchOffset = PatOffset;
Status = S_OK;
break;
}
if (Buf >= BufEnd)
{
ULONG Read;
// Ran out of buffered memory so get some more.
for (;;)
{
if (CheckUserInterrupt())
{
WarnOut("-- Memory search interrupted at %s\n",
FormatAddr64(Offset));
Status = HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT);
goto Exit;
}
if (Offset >= SearchEnd)
{
// Return a result code that's specific and
// consistent with the kernel version.
Status = HRESULT_FROM_NT(STATUS_NO_MORE_ENTRIES);
goto Exit;
}
Status = ReadVirtual(Process, Offset, Buffer, ReadLen, &Read);
#ifdef DBG_SEARCH
g_NtDllCalls.DbgPrint(" Read %X bytes at %I64X, ret %X:%X\n",
ReadLen, Offset,
Status, Read);
#endif
if (Status != S_OK)
{
// Skip to the start of the next page.
NearestDifferentlyValidOffsets(Offset, NULL, &Offset);
// Restart search due to the address discontinuity.
Pat = (PUCHAR)Pattern;
PatOffset = Offset;
}
else
{
break;
}
}
Buf = Buffer;
BufEnd = Buffer + Read;
BufOffset = Offset;
Offset += Read;
}
// If this is the first byte of the pattern it
// must match on a granularity boundary.
if (*Buf++ == *Pat &&
(Pat != (PUCHAR)Pattern ||
(((PatOffset - StartOffset) % PatternGranularity) == 0)))
{
Pat++;
}
else
{
Buf -= Pat - (PUCHAR)Pattern;
Pat = (PUCHAR)Pattern;
PatOffset = BufOffset + (Buf - Buffer);
}
}
Exit:
return Status;
}
ULONG
HammingDistance(ULONG64 Left, ULONG64 Right, ULONG WordLength)
{
ULONG64 Value;
ULONG Index;
ULONG Distance;
Value = Left ^ Right;
Distance = 0;
for (Index = 0; Index < 8 * WordLength; Index++)
{
if ((Value & 1))
{
Distance++;
}
Value >>= 1;
}
return Distance;
}
HRESULT
TargetInfo::PointerSearchPhysical(
IN ULONG64 Offset,
IN ULONG64 Length,
IN ULONG64 PointerMin,
IN ULONG64 PointerMax,
IN ULONG Flags,
OUT PULONG64 MatchOffsets,
IN ULONG MatchOffsetsSize,
OUT PULONG MatchOffsetsCount
)
{
HRESULT Status;
ULONG ReadSize;
if (Flags & PTR_SEARCH_PHYS_PTE)
{
ReadSize = m_TypeInfo.SizePte;
}
else
{
if (m_Machine->m_Ptr64)
{
ReadSize = 8;
}
else
{
ReadSize = 4;
PointerMin &= 0xffffffff;
PointerMax &= 0xffffffff;
}
}
// Make sure things are aligned properly.
if ((Offset & (ReadSize - 1)) ||
(Length & (ReadSize - 1)))
{
return E_INVALIDARG;
}
ULONG Hits;
Hits = 0;
Status = S_OK;
while (Length > 0)
{
ULONG64 Data;
BOOL Hit;
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT)
{
Status = HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT);
// Leave the interrupt flag on so that !search
// interrupts itself.
}
Data = 0;
if ((Status = ReadAllPhysical(Offset, &Data, ReadSize)) != S_OK)
{
return Status;
}
Hit = FALSE;
if (Flags & PTR_SEARCH_PHYS_PTE)
{
ULONG64 Pfn;
ULONG PteFlags;
m_Machine->DecodePte(Data, &Pfn, &PteFlags);
if (Pfn == PointerMin)
{
Hit = TRUE;
}
}
else
{
if ((Data >= PointerMin && Data <= PointerMax) ||
HammingDistance(Data, PointerMin, ReadSize) == 1)
{
Hit = TRUE;
}
}
if (Hit)
{
if (Hits < MatchOffsetsSize)
{
MatchOffsets[Hits] = Offset;
}
Hits++;
if (!(Flags & PTR_SEARCH_PHYS_ALL_HITS))
{
ULONG64 ToNextPage;
//
// The caller has asked for just the first
// hit per page, so we can skip to the next page.
//
ToNextPage = NEXT_PAGE(m_Machine, Offset);
if (ToNextPage == 0)
{
// Wrapped around, so we're done.
break;
}
ToNextPage -= Offset;
if (ToNextPage > Length)
{
ToNextPage = Length;
}
Offset += ToNextPage;
Length -= ToNextPage;
continue;
}
}
Offset += ReadSize;
Length -= ReadSize;
}
if (MatchOffsetsCount)
{
*MatchOffsetsCount = Hits;
}
return Status;
}
HRESULT
TargetInfo::ReadPhysicalUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesRead
)
{
return ReadPhysical(Offset, Buffer, BufferSize, Flags, BytesRead);
}
HRESULT
TargetInfo::WritePhysicalUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesWritten
)
{
return WritePhysical(Offset, Buffer, BufferSize, Flags, BytesWritten);
}
HRESULT
TargetInfo::ReadHandleData(
IN ProcessInfo* Process,
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
// Base implementation which silently fails for modes
// where there is no way to retrieve handle data.
return E_UNEXPECTED;
}
HRESULT
TargetInfo::FillVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled
)
{
HRESULT Status = S_OK;
PUCHAR Pat = (PUCHAR)Pattern;
PUCHAR PatEnd = Pat + PatternSize;
*Filled = 0;
while (Size-- > 0)
{
ULONG Done;
if (CheckUserInterrupt())
{
dprintf("User interrupt during fill - exiting.\n");
Status = HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT);
*Filled = 0;
break;
}
if ((Status = WriteVirtual(Process, Start, Pat, 1, &Done)) != S_OK)
{
break;
}
if (Done != 1)
{
Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
break;
}
Start++;
if (++Pat == PatEnd)
{
Pat = (PUCHAR)Pattern;
}
(*Filled)++;
}
// If nothing was filled return an error, otherwise
// consider it a success.
return *Filled > 0 ? S_OK : Status;
}
HRESULT
TargetInfo::FillPhysical(
THIS_
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled
)
{
HRESULT Status = S_OK;
PUCHAR Pat = (PUCHAR)Pattern;
PUCHAR PatEnd = Pat + PatternSize;
*Filled = 0;
while (Size-- > 0)
{
ULONG Done;
if (CheckUserInterrupt())
{
dprintf("User interrupt during fill - exiting.\n");
Status = HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT);
*Filled = 0;
break;
}
if ((Status = WritePhysical(Start, Pat, 1, PHYS_FLAG_DEFAULT,
&Done)) != S_OK)
{
break;
}
if (Done != 1)
{
Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
break;
}
Start++;
if (++Pat == PatEnd)
{
Pat = (PUCHAR)Pattern;
}
(*Filled)++;
}
// If nothing was filled return an error, otherwise
// consider it a success.
return *Filled > 0 ? S_OK : Status;
}
HRESULT
TargetInfo::GetProcessorId(ULONG Processor,
PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
// Base implementation which silently fails for modes
// where the ID cannot be retrieved.
return E_UNEXPECTED;
}
HRESULT
TargetInfo::GetProcessorSpeed(ULONG Processor,
PULONG Speed)
{
// Base implementation which silently fails for modes
// where the speed cannot be retrieved.
return E_UNEXPECTED;
}
HRESULT
TargetInfo::GetGenericProcessorFeatures(
ULONG Processor,
PULONG64 Features,
ULONG FeaturesSize,
PULONG Used
)
{
// Base implementation which silently fails for modes
// where the information cannot be retrieved.
return E_UNEXPECTED;
}
HRESULT
TargetInfo::GetSpecificProcessorFeatures(
ULONG Processor,
PULONG64 Features,
ULONG FeaturesSize,
PULONG Used
)
{
// Base implementation which silently fails for modes
// where the information cannot be retrieved.
return E_UNEXPECTED;
}
HRESULT
TargetInfo::GetTaggedBaseOffset(PULONG64 Offset)
{
// Base implementation silently fails for
// targets with no tagged data.
return E_NOINTERFACE;
}
HRESULT
TargetInfo::ReadTagged(ULONG64 Offset, PVOID Buffer, ULONG BufferSize)
{
// Base implementation silently fails for
// targets with no tagged data.
return E_NOINTERFACE;
}
HRESULT
TargetInfo::ReadPageFile(ULONG PfIndex, ULONG64 PfOffset,
PVOID Buffer, ULONG Size)
{
// Default implementation for targets which do not
// support reading the page file.
return HR_PAGE_NOT_AVAILABLE;
}
HRESULT
TargetInfo::GetUnloadedModuleListHead(ProcessInfo* Process, PULONG64 Head)
{
// Get the address of the dynamic function table list head which is the
// the same for all processes. This only has to be done once.
if (Process->m_RtlUnloadList)
{
*Head = Process->m_RtlUnloadList;
return S_OK;
}
GetOffsetFromSym(Process, "ntdll!RtlpUnloadEventTrace",
&Process->m_RtlUnloadList, NULL);
if (!Process->m_RtlUnloadList)
{
// No error message here as it's a common case when
// symbols are bad.
return E_NOINTERFACE;
}
*Head = Process->m_RtlUnloadList;
return S_OK;
}
HRESULT
TargetInfo::GetFunctionTableListHead(ProcessInfo* Process, PULONG64 Head)
{
// Get the address of the dynamic function table list head which is the
// the same for all processes. This only has to be done once.
if (Process->m_DynFuncTableList)
{
*Head = Process->m_DynFuncTableList;
return S_OK;
}
GetOffsetFromSym(Process, "ntdll!RtlpDynamicFunctionTable",
&Process->m_DynFuncTableList, NULL);
if (!Process->m_DynFuncTableList)
{
// No error message here as it's a common case when
// symbols are bad.
return E_NOINTERFACE;
}
*Head = Process->m_DynFuncTableList;
return S_OK;
}
// These procedures support dynamic function table entries for user-mode
// run-time code. Dynamic function tables are stored in a linked list
// inside ntdll. The address of the linked list head is returned by
// RtlGetFunctionTableListHead. Since dynamic function tables are
// only supported in user-mode the address of the list head will be
// the same in all processes. Dynamic function tables are very rare,
// so in most cases this the list will be unitialized and this routine
// will return NULL. dbghelp only calls this when it
// is unable to find a function entry in any of the images.
PVOID
TargetInfo::FindDynamicFunctionEntry(ProcessInfo* Process, ULONG64 Address)
{
ULONG64 ListHeadAddr;
LIST_ENTRY64 DynamicFunctionTableHead;
ULONG64 Entry;
if (GetFunctionTableListHead(Process, &ListHeadAddr) != S_OK)
{
return NULL;
}
// Read the dynamic function table list head
if (ReadListEntry(Process, m_Machine, ListHeadAddr,
&DynamicFunctionTableHead) != S_OK)
{
// This failure happens almost all the time in minidumps
// because the function table list symbol can be resolved
// but the memory isn't part of the minidump.
if (!IS_USER_MINI_DUMP(this))
{
ErrOut("Unable to read dynamic function table list head\n");
}
return NULL;
}
Entry = DynamicFunctionTableHead.Flink;
// The list head is initialized the first time it's used so check
// for an uninitialized pointers. This is the most common result.
if (Entry == 0)
{
return NULL;
}
// Loop through the dynamic function table list reading the headers.
// If the range of a dynamic function table contains Address then
// search the function table. Dynamic function table ranges are not
// mututally exclusive like those in images so an address may be
// in more than one range. However, there can be only one dynamic function
// entry that contains the address (if there are any at all).
while (Entry != ListHeadAddr)
{
ULONG64 Table, MinAddress, MaxAddress, BaseAddress, TableData;
ULONG TableSize;
WCHAR OutOfProcessDll[MAX_PATH];
CROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable;
PVOID FunctionTable;
PVOID FunctionEntry;
Table = Entry;
if (m_Machine->
ReadDynamicFunctionTable(Process, Table, &Entry,
&MinAddress, &MaxAddress,
&BaseAddress,
&TableData, &TableSize,
OutOfProcessDll,
&RawTable) != S_OK)
{
ErrOut("Unable to read dynamic function table entry\n");
continue;
}
if (Address >= MinAddress && Address < MaxAddress &&
(OutOfProcessDll[0] ||
(TableData && TableSize > 0)))
{
if (OutOfProcessDll[0])
{
if (ReadOutOfProcessDynamicFunctionTable
(Process, OutOfProcessDll, Table, &TableSize,
&FunctionTable) != S_OK)
{
ErrOut("Unable to read dynamic function table entries\n");
continue;
}
}
else
{
FunctionTable = malloc(TableSize);
if (FunctionTable == NULL)
{
ErrOut("Unable to allocate memory for "
"dynamic function table\n");
continue;
}
// Read the dynamic function table
if (ReadAllVirtual(Process, TableData, FunctionTable,
TableSize) != S_OK)
{
ErrOut("Unable to read dynamic function table entries\n");
free(FunctionTable);
continue;
}
}
FunctionEntry = m_Machine->
FindDynamicFunctionEntry(&RawTable, Address,
FunctionTable, TableSize);
free(FunctionTable);
if (FunctionEntry)
{
return FunctionEntry;
}
}
}
return NULL;
}
ULONG64
TargetInfo::GetDynamicFunctionTableBase(ProcessInfo* Process,
ULONG64 Address)
{
LIST_ENTRY64 ListHead;
ULONG64 Entry;
// If the process dynamic function table list head hasn't
// been looked up yet that means that no dynamic function
// table entry could be in use yet, so there's no need to look.
if (!Process->m_DynFuncTableList)
{
return 0;
}
if (ReadListEntry(Process, m_Machine,
Process->m_DynFuncTableList,
&ListHead) != S_OK)
{
return 0;
}
Entry = ListHead.Flink;
// The list head is initialized the first time it's used so check
// for an uninitialized pointers. This is the most common result.
if (Entry == 0)
{
return 0;
}
// Loop through the dynamic function table list reading the headers.
// If the range of a dynamic function table contains Address then
// return the function table's base.
while (Entry != Process->m_DynFuncTableList)
{
ULONG64 MinAddress, MaxAddress, BaseAddress, TableData;
ULONG TableSize;
WCHAR OutOfProcessDll[MAX_PATH];
CROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable;
if (m_Machine->
ReadDynamicFunctionTable(Process, Entry, &Entry,
&MinAddress, &MaxAddress,
&BaseAddress,
&TableData, &TableSize,
OutOfProcessDll,
&RawTable) == S_OK &&
Address >= MinAddress &&
Address < MaxAddress)
{
return BaseAddress;
}
}
return 0;
}
HRESULT
TargetInfo::ReadOutOfProcessDynamicFunctionTable(ProcessInfo* Process,
PWSTR Dll,
ULONG64 Table,
PULONG TableSize,
PVOID* TableData)
{
// Empty base implementation to avoid error messages
// that would be produced by an UNEXPECTED_HR implementation.
return E_UNEXPECTED;
}
PVOID CALLBACK
TargetInfo::DynamicFunctionTableCallback(HANDLE ProcessHandle,
ULONG64 Address,
ULONG64 Context)
{
ProcessInfo* Process = (ProcessInfo*)Context;
DBG_ASSERT(ProcessHandle == OS_HANDLE(Process->m_SysHandle));
return Process->m_Target->
FindDynamicFunctionEntry(Process, Address);
}
HRESULT
TargetInfo::EnumFunctionTables(IN ProcessInfo* Process,
IN OUT PULONG64 Start,
IN OUT PULONG64 Handle,
OUT PULONG64 MinAddress,
OUT PULONG64 MaxAddress,
OUT PULONG64 BaseAddress,
OUT PULONG EntryCount,
OUT PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable,
OUT PVOID* RawEntries)
{
HRESULT Status;
if (*Start == 0)
{
LIST_ENTRY64 DynamicFunctionTableHead;
if ((Status = GetFunctionTableListHead(Process, Start)) != S_OK)
{
return Status;
}
if ((Status = ReadListEntry(Process, m_Machine, *Start,
&DynamicFunctionTableHead)) != S_OK)
{
return Status;
}
*Handle = DynamicFunctionTableHead.Flink;
}
if (!*Handle || *Handle == *Start)
{
return S_FALSE;
}
ULONG64 Table, TableData;
ULONG TableSize;
WCHAR OutOfProcessDll[MAX_PATH];
Table = *Handle;
if ((Status = m_Machine->
ReadDynamicFunctionTable(Process, Table, Handle,
MinAddress, MaxAddress,
BaseAddress,
&TableData, &TableSize,
OutOfProcessDll,
RawTable)) != S_OK)
{
return Status;
}
if (OutOfProcessDll[0])
{
if ((Status = ReadOutOfProcessDynamicFunctionTable
(Process, OutOfProcessDll, Table, &TableSize,
RawEntries)) != S_OK)
{
return Status;
}
}
else
{
*RawEntries = malloc(TableSize);
if (!*RawEntries)
{
return E_OUTOFMEMORY;
}
// Read the dynamic function table
if ((Status = ReadAllVirtual(Process, TableData,
*RawEntries, TableSize)) != S_OK)
{
free(*RawEntries);
return Status;
}
}
*EntryCount = TableSize / m_TypeInfo.SizeRuntimeFunction;
return S_OK;
}
HRESULT
TargetInfo::QueryAddressInformation(ProcessInfo* Process,
ULONG64 Address, ULONG InSpace,
PULONG OutSpace, PULONG OutFlags)
{
// Default implementation which just returns the
// least restrictive settings.
*OutSpace = (Process && IS_KERNEL_TARGET(this)) ?
DBGKD_QUERY_MEMORY_KERNEL : DBGKD_QUERY_MEMORY_PROCESS;
*OutFlags =
DBGKD_QUERY_MEMORY_READ |
DBGKD_QUERY_MEMORY_WRITE |
DBGKD_QUERY_MEMORY_EXECUTE;
return S_OK;
}
HRESULT
TargetInfo::ReadPointer(
ProcessInfo* Process,
MachineInfo* Machine,
ULONG64 Address,
PULONG64 Pointer64
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToRead;
ULONG Pointer32;
ULONG64 Data;
if (Machine->m_Ptr64)
{
SizeToRead = sizeof(ULONG64);
Status = ReadVirtual(Process, Address, &Data, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(ULONG32);
Status = ReadVirtual(Process,
Address, &Pointer32, SizeToRead, &Result);
Data = EXTEND64(Pointer32);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToRead)
{
return E_FAIL;
}
*Pointer64 = Data;
return S_OK;
}
HRESULT
TargetInfo::WritePointer(
ProcessInfo* Process,
MachineInfo* Machine,
ULONG64 Address,
ULONG64 Pointer64
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToWrite;
ULONG Pointer32;
if (Machine->m_Ptr64)
{
SizeToWrite = sizeof(ULONG64);
Status = WriteVirtual(Process,
Address, &Pointer64, SizeToWrite, &Result);
}
else
{
SizeToWrite = sizeof(ULONG32);
Pointer32 = (ULONG)Pointer64;
Status = WriteVirtual(Process,
Address, &Pointer32, SizeToWrite, &Result);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToWrite)
{
return HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
}
return S_OK;
}
HRESULT
TargetInfo::ReadListEntry(
ProcessInfo* Process,
MachineInfo* Machine,
ULONG64 Address,
PLIST_ENTRY64 List64
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToRead;
LIST_ENTRY32 List32;
if (Machine->m_Ptr64)
{
SizeToRead = sizeof(LIST_ENTRY64);
Status = ReadVirtual(Process, Address, List64, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(LIST_ENTRY32);
Status = ReadVirtual(Process, Address, &List32, SizeToRead, &Result);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToRead)
{
return E_FAIL;
}
if (!Machine->m_Ptr64)
{
List64->Flink = EXTEND64(List32.Flink);
List64->Blink = EXTEND64(List32.Blink);
}
return S_OK;
}
void
ConvertLoaderEntry32To64(
PKLDR_DATA_TABLE_ENTRY32 b32,
PKLDR_DATA_TABLE_ENTRY64 b64
)
{
#define COPYSE2(p64,s32,f) p64->f = (ULONG64)(LONG64)(LONG)s32->f
COPYSE2(b64,b32,InLoadOrderLinks.Flink);
COPYSE2(b64,b32,InLoadOrderLinks.Blink);
COPYSE2(b64,b32,__Undefined1);
COPYSE2(b64,b32,__Undefined2);
COPYSE2(b64,b32,__Undefined3);
COPYSE2(b64,b32,NonPagedDebugInfo);
COPYSE2(b64,b32,DllBase);
COPYSE2(b64,b32,EntryPoint);
b64->SizeOfImage = b32->SizeOfImage;
b64->FullDllName.Length = b32->FullDllName.Length;
b64->FullDllName.MaximumLength = b32->FullDllName.MaximumLength;
COPYSE2(b64,b32,FullDllName.Buffer);
b64->BaseDllName.Length = b32->BaseDllName.Length;
b64->BaseDllName.MaximumLength = b32->BaseDllName.MaximumLength;
COPYSE2(b64,b32,BaseDllName.Buffer);
b64->Flags = b32->Flags;
b64->LoadCount = b32->LoadCount;
b64->__Undefined5 = b32->__Undefined5;
COPYSE2(b64,b32,__Undefined6);
b64->CheckSum = b32->CheckSum;
b64->TimeDateStamp = b32->TimeDateStamp;
#undef COPYSE2
return;
}
HRESULT
TargetInfo::ReadLoaderEntry(
ProcessInfo* Process,
MachineInfo* Machine,
ULONG64 Address,
PKLDR_DATA_TABLE_ENTRY64 Entry
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToRead;
KLDR_DATA_TABLE_ENTRY32 Ent32;
if (Machine->m_Ptr64)
{
SizeToRead = sizeof(KLDR_DATA_TABLE_ENTRY64);
Status = ReadVirtual(Process, Address, Entry, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(KLDR_DATA_TABLE_ENTRY32);
Status = ReadVirtual(Process, Address, &Ent32, SizeToRead, &Result);
ConvertLoaderEntry32To64(&Ent32, Entry);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToRead)
{
return E_FAIL;
}
return S_OK;
}
HRESULT
TargetInfo::ReadUnicodeString(ProcessInfo* Process,
MachineInfo* Machine,
ULONG64 Address, PUNICODE_STRING64 String)
{
HRESULT Status;
ULONG Result;
ULONG SizeToRead;
UNICODE_STRING32 Str32;
if (Machine->m_Ptr64)
{
SizeToRead = sizeof(UNICODE_STRING64);
Status = ReadVirtual(Process, Address, String, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(UNICODE_STRING32);
Status = ReadVirtual(Process, Address, &Str32, SizeToRead, &Result);
String->Length = Str32.Length;
String->MaximumLength = Str32.MaximumLength;
String->Buffer = EXTEND64(Str32.Buffer);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToRead)
{
return E_FAIL;
}
return S_OK;
}
// VS_VERSIONINFO has a variable format but in the case we
// care about it's fixed.
struct PARTIAL_VERSIONINFO
{
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey[17];
VS_FIXEDFILEINFO Value;
};
#define VER2_SIG ((ULONG)'X2EF')
HRESULT
TargetInfo::ReadImageVersionInfo(ProcessInfo* Process,
ULONG64 ImageBase,
PCSTR Item,
PVOID Buffer,
ULONG BufferSize,
PULONG VerInfoSize,
PIMAGE_DATA_DIRECTORY ResDataDir)
{
if (ResDataDir->VirtualAddress == 0 ||
ResDataDir->Size < sizeof(IMAGE_RESOURCE_DIRECTORY))
{
return E_NOINTERFACE;
}
HRESULT Status;
IMAGE_RESOURCE_DIRECTORY ResDir;
ULONG64 Offset, DirOffset;
Offset = ImageBase + ResDataDir->VirtualAddress;
if ((Status = ReadAllVirtual(Process,
Offset, &ResDir, sizeof(ResDir))) != S_OK)
{
return Status;
}
//
// Search for the resource directory entry named by VS_FILE_INFO.
//
IMAGE_RESOURCE_DIRECTORY_ENTRY DirEnt;
ULONG i;
DirOffset = Offset;
Offset += sizeof(ResDir) +
((ULONG64)ResDir.NumberOfNamedEntries * sizeof(DirEnt));
for (i = 0; i < (ULONG)ResDir.NumberOfIdEntries; i++)
{
if ((Status = ReadAllVirtual(Process,
Offset, &DirEnt, sizeof(DirEnt))) != S_OK)
{
return Status;
}
if (!DirEnt.NameIsString &&
MAKEINTRESOURCE(DirEnt.Id) == VS_FILE_INFO)
{
break;
}
Offset += sizeof(DirEnt);
}
if (i >= (ULONG)ResDir.NumberOfIdEntries ||
!DirEnt.DataIsDirectory)
{
return E_NOINTERFACE;
}
Offset = DirOffset + DirEnt.OffsetToDirectory;
if ((Status = ReadAllVirtual(Process,
Offset, &ResDir, sizeof(ResDir))) != S_OK)
{
return Status;
}
//
// Search for the resource directory entry named by VS_VERSION_INFO.
//
Offset += sizeof(ResDir) +
((ULONG64)ResDir.NumberOfNamedEntries * sizeof(DirEnt));
for (i = 0; i < (ULONG)ResDir.NumberOfIdEntries; i++)
{
if ((Status = ReadAllVirtual(Process,
Offset, &DirEnt, sizeof(DirEnt))) != S_OK)
{
return Status;
}
if (DirEnt.Name == VS_VERSION_INFO)
{
break;
}
Offset += sizeof(DirEnt);
}
if (i >= (ULONG)ResDir.NumberOfIdEntries ||
!DirEnt.DataIsDirectory)
{
return E_NOINTERFACE;
}
Offset = DirOffset + DirEnt.OffsetToDirectory;
if ((Status = ReadAllVirtual(Process,
Offset, &ResDir, sizeof(ResDir))) != S_OK)
{
return Status;
}
//
// We now have the VS_VERSION_INFO directory. Just take
// the first entry as we don't care about languages.
//
Offset += sizeof(ResDir);
if ((Status = ReadAllVirtual(Process,
Offset, &DirEnt, sizeof(DirEnt))) != S_OK)
{
return Status;
}
if (DirEnt.DataIsDirectory)
{
return E_NOINTERFACE;
}
IMAGE_RESOURCE_DATA_ENTRY DataEnt;
Offset = DirOffset + DirEnt.OffsetToData;
if ((Status = ReadAllVirtual(Process,
Offset, &DataEnt, sizeof(DataEnt))) != S_OK)
{
return Status;
}
if (DataEnt.Size < sizeof(PARTIAL_VERSIONINFO))
{
return E_NOINTERFACE;
}
PARTIAL_VERSIONINFO RawInfo;
Offset = ImageBase + DataEnt.OffsetToData;
if ((Status = ReadAllVirtual(Process,
Offset, &RawInfo, sizeof(RawInfo))) != S_OK)
{
return Status;
}
if (RawInfo.wLength < sizeof(RawInfo) ||
wcscmp(RawInfo.szKey, L"VS_VERSION_INFO") != 0)
{
return E_NOINTERFACE;
}
//
// VerQueryValueA needs extra data space for ANSI translations
// of version strings. VQVA assumes that this space is available
// at the end of the data block passed in. GetFileVersionInformationSize
// makes this work by returning a size that's big enough
// for the actual data plus space for ANSI translations. We
// need to do the same thing here so that we also provide
// the necessary translation area.
//
ULONG DataSize = (RawInfo.wLength + 3) & ~3;
PVOID VerData = malloc(DataSize * 2 + sizeof(ULONG));
if (VerData == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = ReadAllVirtual(Process,
Offset, VerData, RawInfo.wLength)) == S_OK)
{
// Stamp the buffer with the signature that indicates
// a full-size translation buffer is available after
// the raw data.
*(PULONG)((PUCHAR)VerData + DataSize) = VER2_SIG;
Status = QueryVersionDataBuffer(VerData, Item,
Buffer, BufferSize, VerInfoSize);
}
free(VerData);
return Status;
}
HRESULT
TargetInfo::ReadImageNtHeaders(ProcessInfo* Process,
ULONG64 ImageBase,
PIMAGE_NT_HEADERS64 Headers)
{
HRESULT Status;
IMAGE_DOS_HEADER DosHdr;
IMAGE_NT_HEADERS32 Hdr32;
if ((Status = ReadAllVirtual(Process, ImageBase,
&DosHdr, sizeof(DosHdr))) != S_OK)
{
return Status;
}
if (DosHdr.e_magic != IMAGE_DOS_SIGNATURE)
{
return E_INVALIDARG;
}
// Only read HEADERS32 worth of data first in case
// that's all the memory that's available.
if ((Status = ReadAllVirtual(Process, ImageBase + DosHdr.e_lfanew,
&Hdr32, sizeof(Hdr32))) != S_OK)
{
return Status;
}
if (Hdr32.Signature != IMAGE_NT_SIGNATURE)
{
return E_INVALIDARG;
}
switch(Hdr32.OptionalHeader.Magic)
{
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
ImageNtHdr32To64(&Hdr32, Headers);
Status = S_OK;
break;
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
// Read the remainder of the header.
memcpy(Headers, &Hdr32, sizeof(Hdr32));
Status = ReadAllVirtual(Process,
ImageBase + DosHdr.e_lfanew + sizeof(Hdr32),
(PUCHAR)Headers + sizeof(Hdr32),
sizeof(*Headers) - sizeof(Hdr32));
break;
default:
Status = E_INVALIDARG;
break;
}
return Status;
}
HRESULT
TargetInfo::ReadDirectoryTableBase(PULONG64 DirBase)
{
HRESULT Status;
ULONG64 CurProc;
// Retrieve the current EPROCESS's DirectoryTableBase[0] value.
Status = GetProcessInfoDataOffset(m_RegContextThread, 0, 0, &CurProc);
if (Status != S_OK)
{
return Status;
}
CurProc += m_KdDebuggerData.OffsetEprocessDirectoryTableBase;
return ReadPointer(m_ProcessHead, m_Machine, CurProc, DirBase);
}
HRESULT
TargetInfo::ReadSharedUserTimeDateN(PULONG64 TimeDate)
{
return ReadAllVirtual(m_ProcessHead, m_TypeInfo.SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, SystemTime),
TimeDate, sizeof(*TimeDate));
}
HRESULT
TargetInfo::ReadSharedUserUpTimeN(PULONG64 UpTime)
{
return ReadAllVirtual(m_ProcessHead, m_TypeInfo.SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTime),
UpTime, sizeof(*UpTime));
}
HRESULT
TargetInfo::ReadSharedUserProductInfo(PULONG ProductType, PULONG SuiteMask)
{
HRESULT Status;
BOOLEAN IsValid;
if ((Status =
ReadAllVirtual(m_ProcessHead, m_TypeInfo.SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, ProductTypeIsValid),
&IsValid, sizeof(IsValid))) != S_OK)
{
return Status;
}
if (!IsValid)
{
return E_NOTIMPL;
}
if ((Status =
ReadAllVirtual(m_ProcessHead, m_TypeInfo.SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, NtProductType),
ProductType, sizeof(*ProductType))) != S_OK)
{
return Status;
}
return ReadAllVirtual(m_ProcessHead, m_TypeInfo.SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, SuiteMask),
SuiteMask, sizeof(*SuiteMask));
}
//----------------------------------------------------------------------------
//
// LiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
LiveKernelTargetInfo::GetProcessorId
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
return m_Machine->ReadKernelProcessorId(Processor, Id);
}
HRESULT
LiveKernelTargetInfo::GetProcessorSpeed
(ULONG Processor, PULONG Speed)
{
HRESULT Status;
ULONG64 Prcb;
if ((Status =
GetProcessorSystemDataOffset(Processor, DEBUG_DATA_KPRCB_OFFSET,
&Prcb)) != S_OK)
{
return Status;
}
if (!m_KdDebuggerData.OffsetPrcbMhz)
{
return E_NOTIMPL;
}
return
ReadAllVirtual(m_ProcessHead,
Prcb + m_KdDebuggerData.OffsetPrcbMhz, Speed,
sizeof(ULONG));
}
//----------------------------------------------------------------------------
//
// ConnLiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
ConnLiveKernelTargetInfo::ReadVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
if (Process)
{
return Process->m_VirtualCache.
Read(Offset, Buffer, BufferSize, BytesRead);
}
else
{
return ReadVirtualUncached(Process,
Offset, Buffer, BufferSize, BytesRead);
}
}
HRESULT
ConnLiveKernelTargetInfo::WriteVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
if (Process)
{
Status = Process->m_VirtualCache.
Write(Offset, Buffer, BufferSize, BytesWritten);
}
else
{
Status = WriteVirtualUncached(Process, Offset, Buffer, BufferSize,
BytesWritten);
}
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::SearchVirtual(
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN ULONG64 Length,
IN PVOID Pattern,
IN ULONG PatternSize,
IN ULONG PatternGranularity,
OUT PULONG64 MatchOffset
)
{
// In NT 4.0, the search API is not supported at the kernel protocol
// level. Fall back to the default ReadMemory \ search.
//
HRESULT Status;
if (m_SystemVersion <= NT_SVER_NT4 ||
PatternGranularity != 1)
{
Status = TargetInfo::SearchVirtual(Process,
Offset, Length, (PUCHAR)Pattern,
PatternSize, PatternGranularity,
MatchOffset);
}
else
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_SEARCH_MEMORY a = &m.u.SearchMemory;
ULONG rc;
NTSTATUS st;
KdOut("Search called %s, length %I64x\n",
FormatAddr64(Offset), Length);
*MatchOffset = 0;
a->SearchAddress = Offset;
a->SearchLength = Length;
a->PatternLength = PatternSize;
m.ApiNumber = DbgKdSearchMemoryApi;
m.ReturnStatus = STATUS_PENDING;
//
// Send the message and data to write and then wait for reply
//
do
{
m_Transport->WritePacket(&m, sizeof(m),
PACKET_TYPE_KD_STATE_MANIPULATE,
Pattern, (USHORT)PatternSize);
rc = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
} while (rc != DBGKD_WAIT_PACKET ||
Reply->ApiNumber != DbgKdSearchMemoryApi);
st = Reply->ReturnStatus;
if (NT_SUCCESS(st))
{
if (m_Machine->m_Ptr64)
{
*MatchOffset = Reply->u.SearchMemory.FoundAddress;
}
else
{
*MatchOffset = EXTEND64(Reply->u.SearchMemory.FoundAddress);
}
}
KdOut("DbgKdSearchMemory %08lx\n", st);
Status = CONV_NT_STATUS(st);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::ReadVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
BOOL TranslateVirt = Process &&
Process->m_VirtualCache.m_ForceDecodePTEs;
ULONG Done;
ULONG Left;
HRESULT Status = S_OK;
if (!m_Machine->m_Ptr64 && EXTEND64((ULONG)Offset) != Offset)
{
ErrOut("ReadVirtual: %08x not properly sign extended\n",
(ULONG)Offset);
}
Restart:
Done = 0;
Left = BufferSize;
while (Left)
{
ULONG64 ReqOffs;
ULONG Req;
// Exit if the user is tired of waiting.
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT)
{
Done = 0;
Status = HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT);
break;
}
ReqOffs = Offset + Done;
Req = Left;
if (Req > MAX_MANIP_TRANSFER)
{
Req = MAX_MANIP_TRANSFER;
}
// Split all requests at page boundaries. This
// handles different translations per page in the
// case where the debugger is translating and also
// avoids failures in partial success cases where
// the lead page is valid but followers are not.
if (PAGE_ALIGN(m_Machine, ReqOffs) !=
PAGE_ALIGN(m_Machine, ReqOffs + Req - 1))
{
Req = (ULONG)
((ReqOffs | (m_Machine->m_PageSize - 1)) - ReqOffs + 1);
}
if (TranslateVirt)
{
Status = KdReadVirtualTranslated(ReqOffs, (PUCHAR)Buffer + Done,
Req, &Req);
}
else
{
NTSTATUS NtStatus =
KdReadVirtual(ReqOffs, (PUCHAR)Buffer + Done, Req, &Req);
Status = CONV_NT_STATUS(NtStatus);
}
if (Status != S_OK)
{
// If the target machine failed the write it
// may be because there's a page in transition
// which it didn't want to access. Try again
// with the debugger doing the translations so
// that it can override certain protections.
if (Status != HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT) &&
!TranslateVirt &&
Process && Process->m_VirtualCache.m_DecodePTEs)
{
TranslateVirt = TRUE;
goto Restart;
}
break;
}
else
{
Left -= Req;
Done += Req;
}
}
*BytesRead = Done;
return Done > 0 ? S_OK : Status;
}
HRESULT
ConnLiveKernelTargetInfo::WriteVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
BOOL TranslateVirt = Process &&
Process->m_VirtualCache.m_ForceDecodePTEs;
ULONG Done;
ULONG Left;
HRESULT Status = S_OK;
if (!m_Machine->m_Ptr64 && EXTEND64((ULONG)Offset) != Offset)
{
ErrOut("WriteVirtual: %08x not properly sign extended\n",
(ULONG)Offset);
}
// Restrict notifications to a single notify at the end.
g_EngNotify++;
Restart:
Done = 0;
Left = BufferSize;
while (Left)
{
ULONG64 ReqOffs;
ULONG Req;
ReqOffs = Offset + Done;
Req = Left;
if (Req > MAX_MANIP_TRANSFER)
{
Req = MAX_MANIP_TRANSFER;
}
// Split all requests at page boundaries. This
// handles different translations per page in the
// case where the debugger is translating and also
// avoids failures in partial success cases where
// the lead page is valid but followers are not.
if (PAGE_ALIGN(m_Machine, ReqOffs) !=
PAGE_ALIGN(m_Machine, ReqOffs + Req - 1))
{
Req = (ULONG)
((ReqOffs | (m_Machine->m_PageSize - 1)) - ReqOffs + 1);
}
if (TranslateVirt)
{
Status = KdWriteVirtualTranslated(ReqOffs, (PUCHAR)Buffer + Done,
Req, &Req);
}
else
{
NTSTATUS NtStatus =
KdWriteVirtual(ReqOffs, (PUCHAR)Buffer + Done, Req, &Req);
Status = CONV_NT_STATUS(NtStatus);
}
if (Status != S_OK)
{
// If the target machine failed the write it
// may be because there's a page in transition
// which it didn't want to access. Try again
// with the debugger doing the translations so
// that it can override certain protections.
if (Status != HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT) &&
!TranslateVirt &&
Process && Process->m_VirtualCache.m_DecodePTEs)
{
TranslateVirt = TRUE;
goto Restart;
}
break;
}
else
{
Left -= Req;
Done += Req;
}
}
*BytesWritten = Done;
Status = Done > 0 ? S_OK : Status;
g_EngNotify--;
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
NTSTATUS
ConnLiveKernelTargetInfo::KdReadVirtual(
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
ULONG rc;
//
// Initialize state manipulate message to read virtual memory.
//
m.ApiNumber = DbgKdReadVirtualMemoryApi;
m.u.ReadMemory.TargetBaseAddress = Offset;
m.u.ReadMemory.TransferCount = BufferSize;
//
// Send the read virtual message to the target system and wait for
// a 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 != DbgKdReadVirtualMemoryApi));
if (NT_SUCCESS(Reply->ReturnStatus))
{
if (Reply->u.ReadMemory.ActualBytesRead == 0)
{
Reply->ReturnStatus = STATUS_UNSUCCESSFUL;
}
else
{
if (Reply->u.ReadMemory.ActualBytesRead > BufferSize)
{
ErrOut("KdReadVirtual: Asked for %d, got %d\n",
BufferSize, Reply->u.ReadMemory.ActualBytesRead);
Reply->u.ReadMemory.ActualBytesRead = BufferSize;
}
memcpy(Buffer, Reply + 1, Reply->u.ReadMemory.ActualBytesRead);
}
}
*BytesRead = Reply->u.ReadMemory.ActualBytesRead;
KdOut("KdReadVirtual(%s, %x) returns %08lx, %x\n",
FormatAddr64(Offset), BufferSize,
Reply->ReturnStatus, *BytesRead);
return Reply->ReturnStatus;
}
NTSTATUS
ConnLiveKernelTargetInfo::KdWriteVirtual(
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesWritten
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
ULONG rc;
//
// Initialize state manipulate message to write virtual memory.
//
m.ApiNumber = DbgKdWriteVirtualMemoryApi;
m.u.WriteMemory.TargetBaseAddress = Offset;
m.u.WriteMemory.TransferCount = BufferSize;
//
// Send the write message and data to the target system and wait
// for a reply.
//
do
{
m_Transport->WritePacket(&m, sizeof(m),
PACKET_TYPE_KD_STATE_MANIPULATE,
Buffer, (USHORT)BufferSize);
rc = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
}
while ((rc != DBGKD_WAIT_PACKET) ||
(Reply->ApiNumber != DbgKdWriteVirtualMemoryApi));
KdOut("DbgKdWriteVirtualMemory returns %08lx\n", Reply->ReturnStatus);
*BytesWritten = Reply->u.WriteMemory.ActualBytesWritten;
KdOut("KdWriteVirtual(%s, %x) returns %08lx, %x\n",
FormatAddr64(Offset), BufferSize,
Reply->ReturnStatus, *BytesWritten);
return Reply->ReturnStatus;
}
HRESULT
ConnLiveKernelTargetInfo::KdReadVirtualTranslated(
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
//
// Virtual addresses are not necessarily contiguous
// after translation to physical addresses so do
// not allow transfers that cross a page boundary.
// Higher-level code is responsible for splitting
// up requests.
//
DBG_ASSERT(PAGE_ALIGN(m_Machine, Offset) ==
PAGE_ALIGN(m_Machine, Offset + BufferSize - 1));
ULONG64 TargetPhysicalAddress;
ULONG Levels;
ULONG PfIndex;
HRESULT Status;
BOOL ChangedSuspend;
// Allow the page table physical accesses to be cached.
ChangedSuspend = m_PhysicalCache.ChangeSuspend(TRUE);
Status = m_Machine->
GetVirtualTranslationPhysicalOffsets(m_ProcessHead->m_CurrentThread,
Offset, NULL, 0,
&Levels, &PfIndex,
&TargetPhysicalAddress);
if (ChangedSuspend)
{
m_PhysicalCache.ChangeSuspend(FALSE);
}
if (Status == S_OK)
{
Status = ReadPhysicalUncached(TargetPhysicalAddress,
Buffer, BufferSize,
PHYS_FLAG_DEFAULT,
BytesRead);
}
else if (Status == HR_PAGE_IN_PAGE_FILE ||
Status == HR_PAGE_NOT_AVAILABLE)
{
// Translate specific errors into generic memory
// failure errors.
Status = HRESULT_FROM_NT(STATUS_UNSUCCESSFUL);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::KdWriteVirtualTranslated(
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesWritten
)
{
//
// Virtual addresses are not necessarily contiguous
// after translation to physical addresses so do
// not allow transfers that cross a page boundary.
// Higher-level code is responsible for splitting
// up requests.
//
DBG_ASSERT(PAGE_ALIGN(m_Machine, Offset) ==
PAGE_ALIGN(m_Machine, Offset + BufferSize - 1));
ULONG64 TargetPhysicalAddress;
ULONG Levels;
ULONG PfIndex;
HRESULT Status;
BOOL ChangedSuspend;
// Allow the page table physical accesses to be cached.
ChangedSuspend = m_PhysicalCache.ChangeSuspend(TRUE);
Status = m_Machine->
GetVirtualTranslationPhysicalOffsets(m_ProcessHead->m_CurrentThread,
Offset, NULL, 0,
&Levels, &PfIndex,
&TargetPhysicalAddress);
if (ChangedSuspend)
{
m_PhysicalCache.ChangeSuspend(FALSE);
}
if (Status == S_OK)
{
Status = WritePhysicalUncached(TargetPhysicalAddress,
Buffer, BufferSize,
PHYS_FLAG_DEFAULT,
BytesWritten);
}
else if (Status == HR_PAGE_IN_PAGE_FILE ||
Status == HR_PAGE_NOT_AVAILABLE)
{
// Translate specific errors into generic memory
// failure errors.
Status = HRESULT_FROM_NT(STATUS_UNSUCCESSFUL);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::ReadPhysical(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesRead
)
{
if (!m_PhysicalCache.m_Suspend)
{
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
return m_PhysicalCache.
Read(Offset, Buffer, BufferSize, BytesRead);
}
else
{
return ReadPhysicalUncached(Offset, Buffer, BufferSize, Flags,
BytesRead);
}
}
HRESULT
ConnLiveKernelTargetInfo::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesWritten
)
{
if (!m_PhysicalCache.m_Suspend)
{
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
return m_PhysicalCache.
Write(Offset, Buffer, BufferSize, BytesWritten);
}
else
{
return WritePhysicalUncached(Offset, Buffer, BufferSize, Flags,
BytesWritten);
}
}
HRESULT
ConnLiveKernelTargetInfo::PointerSearchPhysical(
IN ULONG64 Offset,
IN ULONG64 Length,
IN ULONG64 PointerMin,
IN ULONG64 PointerMax,
IN ULONG Flags,
OUT PULONG64 MatchOffsets,
IN ULONG MatchOffsetsSize,
OUT PULONG MatchOffsetsCount
)
{
const ULONG SEARCH_SYMBOL_CHECK = 0xABCDDCBA;
HRESULT Status;
// The kernel stubs only handle page-based searches,
// so if the search isn't page aligned fall back on
// the base implementation.
// The kernel also will only return a single hit if
// the search range is greater than a page.
if ((Offset & ((1 << m_Machine->m_PageShift) - 1)) ||
(Length & ((1 << m_Machine->m_PageShift) - 1)) ||
((Flags & PTR_SEARCH_PHYS_ALL_HITS) &&
Length != m_Machine->m_PageSize))
{
return TargetInfo::PointerSearchPhysical(Offset, Length,
PointerMin, PointerMax,
Flags, MatchOffsets,
MatchOffsetsSize,
MatchOffsetsCount);
}
if (!m_KdpSearchInProgress)
{
if (!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchPageHits",
&m_KdpSearchPageHits, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchPageHitOffsets",
&m_KdpSearchPageHitOffsets, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchPageHitIndex",
&m_KdpSearchPageHitIndex, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchInProgress",
&m_KdpSearchInProgress, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchStartPageFrame",
&m_KdpSearchStartPageFrame, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchEndPageFrame",
&m_KdpSearchEndPageFrame, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchAddressRangeStart",
&m_KdpSearchAddressRangeStart, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchAddressRangeEnd",
&m_KdpSearchAddressRangeEnd, NULL) ||
!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchPfnValue",
&m_KdpSearchPfnValue, NULL))
{
m_KdpSearchInProgress = 0;
ErrOut("Search error: Unable to resolve symbols for kernel\n");
return E_INVALIDARG;
}
if (!(Flags & PTR_SEARCH_NO_SYMBOL_CHECK))
{
ULONG Check;
if (!GetOffsetFromSym(m_ProcessHead,
"nt!KdpSearchCheckPoint",
&m_KdpSearchCheckPoint, NULL) ||
ReadAllVirtual(m_ProcessHead, m_KdpSearchCheckPoint,
&Check, sizeof(Check)) != S_OK ||
Check != SEARCH_SYMBOL_CHECK)
{
m_KdpSearchInProgress = 0;
ErrOut("Search error: Incorrect symbols for kernel\n");
return E_INVALIDARG;
}
}
}
ULONG Ul;
//
// Perform some sanity checks on the values.
//
if (ReadAllVirtual(m_ProcessHead,
m_KdpSearchInProgress, &Ul, sizeof(Ul)) != S_OK ||
Ul != 0)
{
ErrOut("Search error: "
"Inconsistent value for nt!KdpSearchInProgress \n");
return E_INVALIDARG;
}
//
// Set up the search engine
//
if ((Status = WritePointer(m_ProcessHead, m_Machine,
m_KdpSearchAddressRangeStart,
PointerMin)) != S_OK ||
(Status = WritePointer(m_ProcessHead, m_Machine,
m_KdpSearchAddressRangeEnd,
PointerMax)) != S_OK ||
(Status = WritePointer(m_ProcessHead, m_Machine,
m_KdpSearchStartPageFrame,
Offset >> m_Machine->
m_PageShift)) != S_OK ||
(Status = WritePointer(m_ProcessHead, m_Machine,
m_KdpSearchEndPageFrame,
(Offset + Length - 1) >>
m_Machine->m_PageShift)) != S_OK)
{
return Status;
}
Ul = 0;
if ((Status = WriteAllVirtual(m_ProcessHead,
m_KdpSearchPageHitIndex,
&Ul, sizeof(Ul))) != S_OK ||
(Status = WriteAllVirtual(m_ProcessHead,
m_KdpSearchPageHits,
&Ul, sizeof(Ul))) != S_OK ||
(Status = WriteAllVirtual(m_ProcessHead,
m_KdpSearchPageHitOffsets,
&Ul, sizeof(Ul))) != S_OK)
{
return Status;
}
Ul = (Flags & PTR_SEARCH_PHYS_PTE) ? 1 : 0;
if ((Status = WriteAllVirtual(m_ProcessHead,
m_KdpSearchPfnValue,
&Ul, sizeof(Ul))) != S_OK)
{
return Status;
}
Ul = 1;
if ((Status = WriteAllVirtual(m_ProcessHead,
m_KdpSearchInProgress,
&Ul, sizeof(Ul))) != S_OK)
{
goto Exit;
}
// The kernel check-low-memory code checks the variables
// set above and automatically changes its behavior to
// the given search rather than the standard low-memory check.
if (FAILED(Status = CheckLowMemory()))
{
goto Exit;
}
ULONG Hits;
ULONG i;
if ((Status = ReadAllVirtual(m_ProcessHead,
m_KdpSearchPageHitIndex,
&Hits, sizeof(Hits))) != S_OK)
{
goto Exit;
}
if (MatchOffsetsCount)
{
*MatchOffsetsCount = Hits;
}
if (Hits > MatchOffsetsSize)
{
Hits = MatchOffsetsSize;
Status = S_FALSE;
}
for (i = 0; i < Hits; i++)
{
ULONG64 Pfn;
ULONG Offs;
HRESULT ReadStatus;
Pfn = 0;
if ((ReadStatus = ReadAllVirtual
(m_ProcessHead, m_KdpSearchPageHits +
i * m_TypeInfo.SizePageFrameNumber,
&Pfn,
m_TypeInfo.SizePageFrameNumber)) != S_OK ||
(ReadStatus = ReadAllVirtual
(m_ProcessHead,
m_KdpSearchPageHitOffsets + i * sizeof(ULONG),
&Offs, sizeof(Offs))) != S_OK)
{
Status = ReadStatus;
goto Exit;
}
MatchOffsets[i] = (Pfn << m_Machine->m_PageShift) +
(Offs & ((1 << m_Machine->m_PageShift) - 1));
}
Exit:
Ul = 0;
// Can't do much on failures.
WriteAllVirtual(m_ProcessHead,
m_KdpSearchPfnValue, &Ul, sizeof(Ul));
WriteAllVirtual(m_ProcessHead,
m_KdpSearchInProgress, &Ul, sizeof(Ul));
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::ReadPhysicalUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesRead
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_READ_MEMORY64 a;
NTSTATUS st;
ULONG rc;
ULONG cb, cb2;
ULONG OrigBufferSize = BufferSize;
cb2 = 0;
if (ARGUMENT_PRESENT(BytesRead))
{
*BytesRead = 0;
}
readmore:
cb = BufferSize;
if (cb > MAX_MANIP_TRANSFER)
{
cb = MAX_MANIP_TRANSFER;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdReadPhysicalMemoryApi;
m.ReturnStatus = STATUS_PENDING;
a = &m.u.ReadMemory;
a->TargetBaseAddress = Offset + cb2;
a->TransferCount = cb;
// The ActualBytes fields have been overloaded to
// allow passing in flags for the request. Previous
// debuggers passed zero so that should always be
// the default.
a->ActualBytesRead = Flags;
//
// 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 != DbgKdReadPhysicalMemoryApi);
st = Reply->ReturnStatus;
a = &Reply->u.ReadMemory;
DBG_ASSERT(a->ActualBytesRead <= cb);
//
// Return actual bytes read, and then transfer the bytes
//
if (ARGUMENT_PRESENT(BytesRead))
{
*BytesRead += a->ActualBytesRead;
}
//
// Since read response data follows message, Reply+1 should point
// at the data
//
if (NT_SUCCESS(st))
{
memcpy((PCHAR)((ULONG_PTR)Buffer + cb2), Reply + 1,
(int)a->ActualBytesRead);
BufferSize -= a->ActualBytesRead;
cb2 += a->ActualBytesRead;
if (BufferSize)
{
goto readmore;
}
}
KdOut("KdReadPhysical(%s, %x) returns %08lx, %x\n",
FormatAddr64(Offset), OrigBufferSize, st, cb2);
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::WritePhysicalUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesWritten
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_WRITE_MEMORY64 a;
NTSTATUS st;
ULONG rc;
ULONG cb, cb2;
ULONG OrigBufferSize = BufferSize;
InvalidateMemoryCaches(TRUE);
cb2 = 0;
if (ARGUMENT_PRESENT(BytesWritten))
{
*BytesWritten = 0;
}
writemore:
cb = BufferSize;
if (cb > MAX_MANIP_TRANSFER)
{
cb = MAX_MANIP_TRANSFER;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdWritePhysicalMemoryApi;
m.ReturnStatus = STATUS_PENDING;
a = &m.u.WriteMemory;
a->TargetBaseAddress = Offset + cb2;
a->TransferCount = cb;
// The ActualBytes fields have been overloaded to
// allow passing in flags for the request. Previous
// debuggers passed zero so that should always be
// the default.
a->ActualBytesWritten = Flags;
//
// Send the message and data to write and then wait for reply
//
do
{
m_Transport->WritePacket(&m, sizeof(m),
PACKET_TYPE_KD_STATE_MANIPULATE,
(PVOID)((ULONG_PTR)Buffer + cb2),
(USHORT)cb);
rc = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
} while (rc != DBGKD_WAIT_PACKET ||
Reply->ApiNumber != DbgKdWritePhysicalMemoryApi);
st = Reply->ReturnStatus;
a = &Reply->u.WriteMemory;
DBG_ASSERT(a->ActualBytesWritten <= cb);
//
// Return actual bytes written
//
if (ARGUMENT_PRESENT(BytesWritten))
{
*BytesWritten += a->ActualBytesWritten;
}
if (NT_SUCCESS(st))
{
BufferSize -= a->ActualBytesWritten;
cb2 += a->ActualBytesWritten;
if (BufferSize)
{
goto writemore;
}
}
KdOut("KdWritePhysical(%s, %x) returns %08lx, %x\n",
FormatAddr64(Offset), OrigBufferSize, st, cb2);
if (NT_SUCCESS(st))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_PHYSICAL);
}
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_READ_MEMORY64 a = &m.u.ReadMemory;
NTSTATUS st = STATUS_UNSUCCESSFUL;
ULONG rc;
if (BufferSize > MAX_MANIP_TRANSFER)
{
return E_INVALIDARG;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdReadControlSpaceApi;
m.ReturnStatus = STATUS_PENDING;
m.Processor = (SHORT)Processor;
a->TargetBaseAddress = Offset;
a->TransferCount = BufferSize;
a->ActualBytesRead = 0L;
//
// 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 != DbgKdReadControlSpaceApi);
st = Reply->ReturnStatus;
//
// Reset message address to reply.
//
a = &Reply->u.ReadMemory;
DBG_ASSERT(a->ActualBytesRead <= BufferSize);
//
// Return actual bytes read, and then transfer the bytes
//
if (ARGUMENT_PRESENT(BytesRead))
{
*BytesRead = a->ActualBytesRead;
}
//
// Since read response data follows message, Reply+1 should point
// at the data
//
memcpy(Buffer, Reply + 1, (int)a->ActualBytesRead);
KdOut("DbgKdReadControlSpace returns %08lx\n", st);
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_WRITE_MEMORY64 a = &m.u.WriteMemory;
NTSTATUS st;
ULONG rc;
if (BufferSize > MAX_MANIP_TRANSFER)
{
return E_INVALIDARG;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdWriteControlSpaceApi;
m.ReturnStatus = STATUS_PENDING;
m.Processor = (USHORT)Processor;
a->TargetBaseAddress = Offset;
a->TransferCount = BufferSize;
a->ActualBytesWritten = 0L;
//
// Send the message and data to write and then wait for reply
//
do
{
m_Transport->WritePacket(&m, sizeof(m),
PACKET_TYPE_KD_STATE_MANIPULATE,
Buffer, (USHORT)BufferSize);
rc = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
} while (rc != DBGKD_WAIT_PACKET ||
Reply->ApiNumber != DbgKdWriteControlSpaceApi);
st = Reply->ReturnStatus;
a = &Reply->u.WriteMemory;
DBG_ASSERT(a->ActualBytesWritten <= BufferSize);
//
// Return actual bytes written
//
*BytesWritten = a->ActualBytesWritten;
KdOut("DbgWriteControlSpace returns %08lx\n", st);
if (NT_SUCCESS(st))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_CONTROL);
}
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::ReadIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
NTSTATUS st;
ULONG rc;
ULONG DataValue;
switch(BufferSize)
{
case 1:
case 2:
case 4:
break;
default:
return E_INVALIDARG;
}
if (!(AddressSpace == 0 || AddressSpace == 1))
{
return E_INVALIDARG;
}
// Convert trivially extended I/O requests down into simple
// requests as not all platform support extended requests.
if (InterfaceType == Isa && BusNumber == 0 && AddressSpace == 1)
{
PDBGKD_READ_WRITE_IO64 a = &m.u.ReadWriteIo;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdReadIoSpaceApi;
m.ReturnStatus = STATUS_PENDING;
a->DataSize = BufferSize;
a->IoAddress = Offset;
//
// 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 != DbgKdReadIoSpaceApi);
st = Reply->ReturnStatus;
DataValue = Reply->u.ReadWriteIo.DataValue;
KdOut("DbgKdReadIoSpace returns %08lx\n", st);
}
else
{
PDBGKD_READ_WRITE_IO_EXTENDED64 a = &m.u.ReadWriteIoExtended;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdReadIoSpaceExtendedApi;
m.ReturnStatus = STATUS_PENDING;
a->DataSize = BufferSize;
a->IoAddress = Offset;
a->InterfaceType = InterfaceType;
a->BusNumber = BusNumber;
a->AddressSpace = AddressSpace;
//
// 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 != DbgKdReadIoSpaceExtendedApi);
st = Reply->ReturnStatus;
DataValue = Reply->u.ReadWriteIoExtended.DataValue;
KdOut("DbgKdReadIoSpaceExtended returns %08lx\n", st);
}
if (NT_SUCCESS(st))
{
switch(BufferSize)
{
case 1:
*(PUCHAR)Buffer = (UCHAR)DataValue;
break;
case 2:
*(PUSHORT)Buffer = (USHORT)DataValue;
break;
case 4:
*(PULONG)Buffer = DataValue;
break;
}
// I/O access currently can't successfully return anything
// other than the requested size.
if (BytesRead != NULL)
{
*BytesRead = BufferSize;
}
return S_OK;
}
else
{
return HRESULT_FROM_NT(st);
}
}
HRESULT
ConnLiveKernelTargetInfo::WriteIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
NTSTATUS st;
ULONG rc;
ULONG DataValue;
switch(BufferSize)
{
case 1:
DataValue = *(PUCHAR)Buffer;
break;
case 2:
DataValue = *(PUSHORT)Buffer;
break;
case 4:
DataValue = *(PULONG)Buffer;
break;
default:
return E_INVALIDARG;
}
if (!(AddressSpace == 0 || AddressSpace == 1))
{
return E_INVALIDARG;
}
// Convert trivially extended I/O requests down into simple
// requests as not all platform support extended requests.
if (InterfaceType == Isa && BusNumber == 0 && AddressSpace == 1)
{
PDBGKD_READ_WRITE_IO64 a = &m.u.ReadWriteIo;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdWriteIoSpaceApi;
m.ReturnStatus = STATUS_PENDING;
a->DataSize = BufferSize;
a->IoAddress = Offset;
a->DataValue = DataValue;
//
// 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 != DbgKdWriteIoSpaceApi);
st = Reply->ReturnStatus;
KdOut("DbgKdWriteIoSpace returns %08lx\n", st);
}
else
{
PDBGKD_READ_WRITE_IO_EXTENDED64 a = &m.u.ReadWriteIoExtended;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdWriteIoSpaceExtendedApi;
m.ReturnStatus = STATUS_PENDING;
a->DataSize = BufferSize;
a->IoAddress = Offset;
a->DataValue = DataValue;
a->InterfaceType = InterfaceType;
a->BusNumber = BusNumber;
a->AddressSpace = AddressSpace;
//
// 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 != DbgKdWriteIoSpaceExtendedApi);
st = Reply->ReturnStatus;
KdOut("DbgKdWriteIoSpaceExtended returns %08lx\n", st);
}
if (NT_SUCCESS(st))
{
// I/O access currently can't successfully return anything
// other than the requested size.
if (BytesWritten != NULL)
{
*BytesWritten = BufferSize;
}
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_IO);
return S_OK;
}
else
{
return HRESULT_FROM_NT(st);
}
}
HRESULT
ConnLiveKernelTargetInfo::ReadMsr(
THIS_
IN ULONG Msr,
OUT PULONG64 Value
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_READ_WRITE_MSR a = &m.u.ReadWriteMsr;
LARGE_INTEGER li;
NTSTATUS st;
ULONG rc;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdReadMachineSpecificRegister;
m.ReturnStatus = STATUS_PENDING;
a->Msr = Msr;
//
// 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 != DbgKdReadMachineSpecificRegister);
st = Reply->ReturnStatus;
a = &Reply->u.ReadWriteMsr;
li.LowPart = a->DataValueLow;
li.HighPart = a->DataValueHigh;
*Value = li.QuadPart;
KdOut("DbgKdReadMsr returns %08lx\n", st);
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_READ_WRITE_MSR a = &m.u.ReadWriteMsr;
LARGE_INTEGER li;
NTSTATUS st;
ULONG rc;
li.QuadPart = Value;
//
// Format state manipulate message
//
m.ApiNumber = DbgKdWriteMachineSpecificRegister;
m.ReturnStatus = STATUS_PENDING;
// Quiet PREfix warnings.
m.Processor = 0;
m.ProcessorLevel = 0;
a->Msr = Msr;
a->DataValueLow = li.LowPart;
a->DataValueHigh = li.HighPart;
//
// 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 != DbgKdWriteMachineSpecificRegister);
st = Reply->ReturnStatus;
KdOut("DbgKdWriteMsr returns %08lx\n", st);
if (NT_SUCCESS(st))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_MSR);
}
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::ReadBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_GET_SET_BUS_DATA a = &m.u.GetSetBusData;
NTSTATUS st;
ULONG rc;
//
// Check the buffer size.
//
if (BufferSize > MAX_MANIP_TRANSFER)
{
return E_INVALIDARG;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdGetBusDataApi;
m.ReturnStatus = STATUS_PENDING;
a->BusDataType = BusDataType;
a->BusNumber = BusNumber;
a->SlotNumber = SlotNumber;
a->Offset = Offset;
a->Length = BufferSize;
//
// 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 != DbgKdGetBusDataApi);
st = Reply->ReturnStatus;
if (NT_SUCCESS(st))
{
a = &Reply->u.GetSetBusData;
memcpy(Buffer, Reply + 1, a->Length);
if (BytesRead != NULL)
{
*BytesRead = a->Length;
}
}
KdOut("DbgKdGetBusData returns %08lx\n", st);
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::WriteBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
PDBGKD_GET_SET_BUS_DATA a = &m.u.GetSetBusData;
NTSTATUS st;
ULONG rc;
//
// Check the buffer size.
//
if (BufferSize > MAX_MANIP_TRANSFER)
{
return E_INVALIDARG;
}
//
// Format state manipulate message
//
m.ApiNumber = DbgKdSetBusDataApi;
m.ReturnStatus = STATUS_PENDING;
a->BusDataType = BusDataType;
a->BusNumber = BusNumber;
a->SlotNumber = SlotNumber;
a->Offset = Offset;
a->Length = BufferSize;
//
// Send the message and then wait for reply
//
do
{
m_Transport->WritePacket(&m, sizeof(m),
PACKET_TYPE_KD_STATE_MANIPULATE,
Buffer, (USHORT)BufferSize);
rc = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
} while (rc != DBGKD_WAIT_PACKET ||
Reply->ApiNumber != DbgKdSetBusDataApi);
st = Reply->ReturnStatus;
if (NT_SUCCESS(st) && BytesWritten != NULL)
{
a = &Reply->u.GetSetBusData;
*BytesWritten = a->Length;
}
KdOut("DbgKdSetBusData returns %08lx\n", st);
if (NT_SUCCESS(st))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_BUS_DATA);
}
return CONV_NT_STATUS(st);
}
HRESULT
ConnLiveKernelTargetInfo::CheckLowMemory(void)
{
DBGKD_MANIPULATE_STATE64 m;
PDBGKD_MANIPULATE_STATE64 Reply;
ULONG rc;
//
// Format state manipulate message
//
ZeroMemory(&m, sizeof(m));
m.ApiNumber = DbgKdCheckLowMemoryApi;
m.ReturnStatus = STATUS_PENDING;
//
// We wait for an answer from the kernel side.
//
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);
if (Reply->ReturnStatus != STATUS_SUCCESS)
{
ErrOut("Corrupted page with pfn %x\n", Reply->ReturnStatus);
}
KdOut("DbgKdCheckLowMemory 0x00000000\n");
return S_OK;
}
NTSTATUS
ConnLiveKernelTargetInfo::KdFillMemory(IN ULONG Flags,
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled)
{
ULONG NtStatus;
DBGKD_MANIPULATE_STATE64 Manip;
PDBGKD_MANIPULATE_STATE64 Reply;
NTSTATUS Status = STATUS_SUCCESS;
DBG_ASSERT(m_KdMaxManipulate > DbgKdFillMemoryApi &&
(Flags & 0xffff0000) == 0 &&
PatternSize <= MAX_MANIP_TRANSFER);
// Invalidate any cached memory.
if (Flags & DBGKD_FILL_MEMORY_VIRTUAL)
{
InvalidateMemoryCaches(TRUE);
}
else if (Flags & DBGKD_FILL_MEMORY_PHYSICAL)
{
m_PhysicalCache.Remove(Start, Size);
}
//
// Initialize state manipulate message to fill memory.
//
Manip.ApiNumber = DbgKdFillMemoryApi;
Manip.u.FillMemory.Address = Start;
Manip.u.FillMemory.Length = Size;
Manip.u.FillMemory.Flags = (USHORT)Flags;
Manip.u.FillMemory.PatternLength = (USHORT)PatternSize;
//
// Send the message and data to the target system and wait
// for a reply.
//
ULONG Recv;
do
{
m_Transport->WritePacket(&Manip, sizeof(Manip),
PACKET_TYPE_KD_STATE_MANIPULATE,
Pattern, (USHORT)PatternSize);
Recv = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
}
while ((Recv != DBGKD_WAIT_PACKET) ||
(Reply->ApiNumber != DbgKdFillMemoryApi));
NtStatus = Reply->ReturnStatus;
*Filled = Reply->u.FillMemory.Length;
KdOut("DbgKdFillMemory returns %08lx\n", NtStatus);
return NtStatus;
}
HRESULT
ConnLiveKernelTargetInfo::FillVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled
)
{
HRESULT Status;
if (m_KdMaxManipulate <= DbgKdFillMemoryApi ||
PatternSize > MAX_MANIP_TRANSFER)
{
Status = TargetInfo::FillVirtual(Process, Start, Size, Pattern,
PatternSize, Filled);
}
else
{
NTSTATUS NtStatus =
KdFillMemory(DBGKD_FILL_MEMORY_VIRTUAL, Start, Size,
Pattern, PatternSize, Filled);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::FillPhysical(
THIS_
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled
)
{
HRESULT Status;
if (m_KdMaxManipulate <= DbgKdFillMemoryApi ||
PatternSize > MAX_MANIP_TRANSFER)
{
Status = TargetInfo::FillPhysical(Start, Size, Pattern,
PatternSize, Filled);
}
else
{
NTSTATUS NtStatus =
KdFillMemory(DBGKD_FILL_MEMORY_PHYSICAL, Start, Size,
Pattern, PatternSize, Filled);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::QueryAddressInformation(ProcessInfo* Process,
ULONG64 Address,
ULONG InSpace,
PULONG OutSpace,
PULONG OutFlags)
{
HRESULT Status;
if (m_KdMaxManipulate <= DbgKdQueryMemoryApi)
{
Status = TargetInfo::QueryAddressInformation(Process, Address, InSpace,
OutSpace, OutFlags);
}
else
{
DBGKD_MANIPULATE_STATE64 Manip;
PDBGKD_MANIPULATE_STATE64 Reply;
NTSTATUS NtStatus;
//
// Initialize state manipulate message to query memory.
//
Manip.ApiNumber = DbgKdQueryMemoryApi;
Manip.u.QueryMemory.Address = Address;
Manip.u.QueryMemory.Reserved = 0;
Manip.u.QueryMemory.AddressSpace = InSpace;
Manip.u.QueryMemory.Flags = 0;
//
// Send the message and data to the target system and wait
// for a reply.
//
ULONG Recv;
do
{
m_Transport->WritePacket(&Manip, sizeof(Manip),
PACKET_TYPE_KD_STATE_MANIPULATE,
NULL, 0);
Recv = m_Transport->
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
}
while ((Recv != DBGKD_WAIT_PACKET) ||
(Reply->ApiNumber != DbgKdQueryMemoryApi));
NtStatus = Reply->ReturnStatus;
*OutSpace = Reply->u.QueryMemory.AddressSpace;
*OutFlags = Reply->u.QueryMemory.Flags;
KdOut("DbgKdQueryMemory returns %08lx\n", NtStatus);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
//----------------------------------------------------------------------------
//
// LocalLiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
LocalLiveKernelTargetInfo::ReadVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
SYSDBG_VIRTUAL Cmd;
NTSTATUS Status = STATUS_SUCCESS;
//
// The kernel only allows operations up to
// KDP_MESSAGE_BUFFER_SIZE, so break things up
// into chunks if necessary.
//
*BytesRead = 0;
Cmd.Address = (PVOID)(ULONG_PTR)Offset;
Cmd.Buffer = Buffer;
while (BufferSize > 0)
{
ULONG ChunkDone;
if (BufferSize > PACKET_MAX_SIZE)
{
Cmd.Request = PACKET_MAX_SIZE;
}
else
{
Cmd.Request = BufferSize;
}
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadWritePtr(Cmd.Buffer, Cmd.Request))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
ChunkDone = 0;
Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadVirtual,
&Cmd, sizeof(Cmd),
NULL, 0,
&ChunkDone);
if (!NT_SUCCESS(Status) && Status != STATUS_UNSUCCESSFUL)
{
break;
}
if (ChunkDone == 0)
{
// If some data was processed consider it a success.
Status = *BytesRead > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
break;
}
Cmd.Address = (PVOID)((PUCHAR)Cmd.Address + ChunkDone);
Cmd.Buffer = (PVOID)((PUCHAR)Cmd.Buffer + ChunkDone);
BufferSize -= ChunkDone;
*BytesRead += ChunkDone;
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WriteVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
SYSDBG_VIRTUAL Cmd;
NTSTATUS Status = STATUS_SUCCESS;
//
// The kernel only allows operations up to
// KDP_MESSAGE_BUFFER_SIZE, so break things up
// into chunks if necessary.
//
*BytesWritten = 0;
Cmd.Address = (PVOID)(ULONG_PTR)Offset;
Cmd.Buffer = Buffer;
while (BufferSize > 0)
{
ULONG ChunkDone;
if (BufferSize > PACKET_MAX_SIZE)
{
Cmd.Request = PACKET_MAX_SIZE;
}
else
{
Cmd.Request = BufferSize;
}
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadReadPtr(Cmd.Buffer, Cmd.Request))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
ChunkDone = 0;
Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWriteVirtual,
&Cmd, sizeof(Cmd),
NULL, 0,
&ChunkDone);
if (!NT_SUCCESS(Status) && Status != STATUS_UNSUCCESSFUL)
{
break;
}
if (ChunkDone == 0)
{
// If some data was processed consider it a success.
Status = *BytesWritten > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
break;
}
Cmd.Address = (PVOID)((PUCHAR)Cmd.Address + ChunkDone);
Cmd.Buffer = (PVOID)((PUCHAR)Cmd.Buffer + ChunkDone);
BufferSize -= ChunkDone;
*BytesWritten += ChunkDone;
}
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::ReadPhysical(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesRead
)
{
SYSDBG_PHYSICAL Cmd;
NTSTATUS Status = STATUS_SUCCESS;
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
//
// The kernel only allows operations up to
// KDP_MESSAGE_BUFFER_SIZE, so break things up
// into chunks if necessary.
//
*BytesRead = 0;
Cmd.Address.QuadPart = Offset;
Cmd.Buffer = Buffer;
while (BufferSize > 0)
{
ULONG ChunkDone;
if (BufferSize > PACKET_MAX_SIZE)
{
Cmd.Request = PACKET_MAX_SIZE;
}
else
{
Cmd.Request = BufferSize;
}
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadWritePtr(Cmd.Buffer, Cmd.Request))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
ChunkDone = 0;
Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadPhysical,
&Cmd, sizeof(Cmd),
NULL, 0,
&ChunkDone);
if (!NT_SUCCESS(Status) && Status != STATUS_UNSUCCESSFUL)
{
break;
}
if (ChunkDone == 0)
{
// If some data was processed consider it a success.
Status = *BytesRead > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
break;
}
Cmd.Address.QuadPart += ChunkDone;
Cmd.Buffer = (PVOID)((PUCHAR)Cmd.Buffer + ChunkDone);
BufferSize -= ChunkDone;
*BytesRead += ChunkDone;
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesWritten
)
{
SYSDBG_PHYSICAL Cmd;
NTSTATUS Status = STATUS_SUCCESS;
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
//
// The kernel only allows operations up to
// KDP_MESSAGE_BUFFER_SIZE, so break things up
// into chunks if necessary.
//
*BytesWritten = 0;
Cmd.Address.QuadPart = Offset;
Cmd.Buffer = Buffer;
while (BufferSize > 0)
{
ULONG ChunkDone;
if (BufferSize > PACKET_MAX_SIZE)
{
Cmd.Request = PACKET_MAX_SIZE;
}
else
{
Cmd.Request = BufferSize;
}
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadReadPtr(Cmd.Buffer, Cmd.Request))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
ChunkDone = 0;
Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWritePhysical,
&Cmd, sizeof(Cmd),
NULL, 0,
&ChunkDone);
if (!NT_SUCCESS(Status) && Status != STATUS_UNSUCCESSFUL)
{
break;
}
if (ChunkDone == 0)
{
// If some data was processed consider it a success.
Status = *BytesWritten > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
break;
}
Cmd.Address.QuadPart += ChunkDone;
Cmd.Buffer = (PVOID)((PUCHAR)Cmd.Buffer + ChunkDone);
BufferSize -= ChunkDone;
*BytesWritten += ChunkDone;
}
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_PHYSICAL);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadWritePtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_CONTROL_SPACE Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.Processor = Processor;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadControlSpace,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesRead);
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadReadPtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_CONTROL_SPACE Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.Processor = Processor;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWriteControlSpace,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesWritten);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_CONTROL);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::ReadIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadWritePtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_IO_SPACE Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.InterfaceType = (INTERFACE_TYPE)InterfaceType;
Cmd.BusNumber = BusNumber;
Cmd.AddressSpace = AddressSpace;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadIoSpace,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesRead);
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WriteIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadReadPtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_IO_SPACE Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.InterfaceType = (INTERFACE_TYPE)InterfaceType;
Cmd.BusNumber = BusNumber;
Cmd.AddressSpace = AddressSpace;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWriteIoSpace,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesWritten);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_IO);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::ReadMsr(
THIS_
IN ULONG Msr,
OUT PULONG64 Value
)
{
SYSDBG_MSR Cmd;
Cmd.Msr = Msr;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadMsr,
&Cmd, sizeof(Cmd),
&Cmd, sizeof(Cmd),
NULL);
if (NT_SUCCESS(Status))
{
*Value = Cmd.Data;
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
SYSDBG_MSR Cmd;
Cmd.Msr = Msr;
Cmd.Data = Value;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWriteMsr,
&Cmd, sizeof(Cmd),
NULL, 0,
NULL);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_MSR);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::ReadBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadWritePtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_BUS_DATA Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.BusDataType = (BUS_DATA_TYPE)BusDataType;
Cmd.BusNumber = BusNumber;
Cmd.SlotNumber = SlotNumber;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgReadBusData,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesRead);
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::WriteBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
// The kernel stubs avoid faults so all memory
// must be paged in ahead of time. There's
// still the possibility that something could
// get paged out after this but the assumption is
// that the vulnerability is small and it's much
// better than implementing dual code paths in
// the kernel.
if (IsBadReadPtr(Buffer, BufferSize))
{
return E_INVALIDARG;
}
SYSDBG_BUS_DATA Cmd;
Cmd.Address = Offset;
Cmd.Buffer = Buffer;
Cmd.Request = BufferSize;
Cmd.BusDataType = (BUS_DATA_TYPE)BusDataType;
Cmd.BusNumber = BusNumber;
Cmd.SlotNumber = SlotNumber;
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgWriteBusData,
&Cmd, sizeof(Cmd),
NULL, 0,
BytesWritten);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_BUS_DATA);
}
return CONV_NT_STATUS(Status);
}
HRESULT
LocalLiveKernelTargetInfo::CheckLowMemory(
)
{
NTSTATUS Status =
g_NtDllCalls.NtSystemDebugControl(SysDbgCheckLowMemory,
NULL, 0,
NULL, 0,
NULL);
return CONV_NT_STATUS(Status);
}
//----------------------------------------------------------------------------
//
// ExdiLiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
ExdiLiveKernelTargetInfo::ReadVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status = m_Server->
ReadVirtualMemory(Offset, BufferSize, 8, (PBYTE)Buffer, BytesRead);
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status = m_Server->
WriteVirtualMemory(Offset, BufferSize, 8, (PBYTE)Buffer, BytesWritten);
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::ReadPhysical(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesRead
)
{
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
HRESULT Status = m_Server->
ReadPhysicalMemoryOrPeriphIO(Offset, 0, BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesRead = BufferSize;
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
IN ULONG Flags,
OUT PULONG BytesWritten
)
{
if (Flags != PHYS_FLAG_DEFAULT)
{
return E_NOTIMPL;
}
HRESULT Status = m_Server->
WritePhysicalMemoryOrPeriphIO(Offset, 0, BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesWritten = BufferSize;
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_PHYSICAL);
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// No Ioctl defined for this.
return E_UNEXPECTED;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
// No Ioctl defined for this.
return E_UNEXPECTED;
}
HRESULT
ExdiLiveKernelTargetInfo::ReadIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status = m_Server->
ReadPhysicalMemoryOrPeriphIO(Offset, 1, BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesRead = BufferSize;
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status = m_Server->
WritePhysicalMemoryOrPeriphIO(Offset, 1, BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesWritten = BufferSize;
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_IO);
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::ReadMsr(
THIS_
IN ULONG Msr,
OUT PULONG64 Value
)
{
HRESULT Status;
if (DBGENG_EXDI_IOC_READ_MSR <= m_IoctlMin ||
DBGENG_EXDI_IOC_READ_MSR >= m_IoctlMax)
{
// Read MSR Ioctl not supported.
return E_NOTIMPL;
}
DBGENG_EXDI_IOCTL_MSR_IN IoctlIn;
DBGENG_EXDI_IOCTL_READ_MSR_OUT IoctlOut;
ULONG OutUsed;
IoctlIn.Code = DBGENG_EXDI_IOC_READ_MSR;
IoctlIn.Index = Msr;
if ((Status = m_Server->
Ioctl(sizeof(IoctlIn), (PBYTE)&IoctlIn,
sizeof(IoctlOut), &OutUsed, (PBYTE)&IoctlOut)) != S_OK)
{
return Status;
}
*Value = IoctlOut.Value;
return S_OK;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
HRESULT Status;
if (DBGENG_EXDI_IOC_WRITE_MSR <= m_IoctlMin ||
DBGENG_EXDI_IOC_WRITE_MSR >= m_IoctlMax)
{
// Write MSR Ioctl not supported.
return E_NOTIMPL;
}
DBGENG_EXDI_IOCTL_MSR_IN IoctlIn;
ULONG OutUsed;
IoctlIn.Code = DBGENG_EXDI_IOC_WRITE_MSR;
IoctlIn.Index = Msr;
IoctlIn.Value = Value;
Status = m_Server->Ioctl(sizeof(IoctlIn), (PBYTE)&IoctlIn,
0, &OutUsed, (PBYTE)&IoctlIn);
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_MSR);
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::ReadBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// No Ioctl defined for this.
return E_UNEXPECTED;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
// No Ioctl defined for this.
return E_UNEXPECTED;
}
HRESULT
ExdiLiveKernelTargetInfo::CheckLowMemory(
)
{
// XXX drewb - This doesn't have any meaning in
// the general case. What about when we know it's
// NT on the other side of the emulator?
return E_UNEXPECTED;
}
//----------------------------------------------------------------------------
//
// UserTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
LiveUserTargetInfo::ReadVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
if (m_Local)
{
return ReadVirtualUncached(Process,
Offset, Buffer, BufferSize, BytesRead);
}
else
{
return Process->m_VirtualCache.
Read(Offset, Buffer, BufferSize, BytesRead);
}
}
HRESULT
LiveUserTargetInfo::WriteVirtual(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
if (m_Local)
{
return WriteVirtualUncached(Process,
Offset, Buffer, BufferSize, BytesWritten);
}
else
{
return Process->m_VirtualCache.
Write(Offset, Buffer, BufferSize, BytesWritten);
}
}
HRESULT
LiveUserTargetInfo::ReadVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
// ReadProcessMemory will fail if any part of the
// region to read does not have read access. This
// routine attempts to read the largest valid prefix
// so it has to break up reads on page boundaries.
HRESULT Status = S_OK;
ULONG TotalBytesRead = 0;
ULONG Read;
ULONG ReadSize;
while (BufferSize > 0)
{
// Calculate bytes to read and don't let read cross
// a page boundary.
ReadSize = m_Machine->m_PageSize - (ULONG)
(Offset & (m_Machine->m_PageSize - 1));
ReadSize = min(BufferSize, ReadSize);
if ((Status = m_Services->
ReadVirtual(Process->m_SysHandle, Offset,
Buffer, ReadSize, &Read)) != S_OK)
{
if (TotalBytesRead != 0)
{
// If we've read something consider this a success.
Status = S_OK;
}
break;
}
TotalBytesRead += Read;
Offset += Read;
Buffer = (PVOID)((PUCHAR)Buffer + Read);
BufferSize -= (DWORD)Read;
}
if (Status == S_OK)
{
if (BytesRead != NULL)
{
*BytesRead = (DWORD)TotalBytesRead;
}
}
return Status;
}
HRESULT
LiveUserTargetInfo::WriteVirtualUncached(
THIS_
IN ProcessInfo* Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
ULONG RealBytesWritten;
HRESULT Status = m_Services->
WriteVirtual(Process->m_SysHandle, Offset, Buffer, BufferSize,
&RealBytesWritten);
*BytesWritten = (DWORD) RealBytesWritten;
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
HRESULT
LiveUserTargetInfo::GetUnloadedModuleListHead(ProcessInfo* Process,
PULONG64 Head)
{
// Get the address of the dynamic function table list head which is the
// the same for all processes. This only has to be done once.
if (Process->m_RtlUnloadList)
{
*Head = Process->m_RtlUnloadList;
return S_OK;
}
if (m_Services->
GetUnloadedModuleListHead(Process->m_SysHandle,
&Process->m_RtlUnloadList) == S_OK)
{
*Head = Process->m_RtlUnloadList;
return S_OK;
}
return TargetInfo::GetUnloadedModuleListHead(Process, Head);
}
HRESULT
LiveUserTargetInfo::GetFunctionTableListHead(ProcessInfo* Process, PULONG64 Head)
{
// Get the address of the dynamic function table list head which is the
// the same for all processes. This only has to be done once.
if (Process->m_DynFuncTableList)
{
*Head = Process->m_DynFuncTableList;
return S_OK;
}
if (m_Services->
GetFunctionTableListHead(Process->m_SysHandle,
&Process->m_DynFuncTableList) == S_OK)
{
*Head = Process->m_DynFuncTableList;
return S_OK;
}
return TargetInfo::GetFunctionTableListHead(Process, Head);
}
HRESULT
LiveUserTargetInfo::ReadOutOfProcessDynamicFunctionTable(ProcessInfo* Process,
PWSTR Dll,
ULONG64 Table,
PULONG RetTableSize,
PVOID* RetTableData)
{
HRESULT Status;
PVOID TableData;
ULONG TableSize;
// Allocate an initial buffer of a reasonable size to try
// and get the data in a single call.
TableSize = 65536;
for (;;)
{
TableData = malloc(TableSize);
if (TableData == NULL)
{
return E_OUTOFMEMORY;
}
//
// We assume that the function table data doesn't
// change between OOP calls as long as the debuggee isn't
// executing. To increase performance we cache loaded
// DLLs as long as things are halted.
//
OUT_OF_PROC_FUNC_TABLE_DLL* OopDll =
Process->FindOopFuncTableDll(Dll);
ULONG64 DllHandle;
Status = m_Services->
GetOutOfProcessFunctionTableW(Process->m_SysHandle,
Dll, OopDll ? OopDll->Handle : 0,
Table, TableData,
TableSize, &TableSize,
&DllHandle);
if (Status == S_OK)
{
// If we haven't cached this DLL yet do so now.
if (!OopDll)
{
// Failure to cache isn't critical, it
// just means more loads later.
if (Process->AddOopFuncTableDll(Dll, DllHandle) != S_OK)
{
m_Services->FreeLibrary(DllHandle);
}
}
break;
}
free(TableData);
if (Status == S_FALSE)
{
// Buffer was too small so loop and try again with
// the newly retrieved size.
}
else
{
return Status;
}
}
*RetTableSize = TableSize;
*RetTableData = TableData;
return S_OK;
}
HRESULT
LiveUserTargetInfo::QueryMemoryRegion(ProcessInfo* Process,
PULONG64 Handle,
BOOL HandleIsOffset,
PMEMORY_BASIC_INFORMATION64 Info)
{
MEMORY_BASIC_INFORMATION64 MemInfo;
HRESULT Status;
for (;;)
{
ULONG Used;
// The handle is always an offset in this mode so
// there's no need to check.
if ((Status = m_Services->
QueryVirtual(Process->m_SysHandle,
*Handle, &MemInfo, sizeof(MemInfo), &Used)) != S_OK)
{
return Status;
}
if (m_Machine->m_Ptr64)
{
if (Used != sizeof(MEMORY_BASIC_INFORMATION64))
{
return E_FAIL;
}
*Info = MemInfo;
}
else
{
if (Used != sizeof(MEMORY_BASIC_INFORMATION32))
{
return E_FAIL;
}
MemoryBasicInformation32To64((MEMORY_BASIC_INFORMATION32*)&MemInfo,
Info);
}
if (HandleIsOffset ||
!((Info->Protect & PAGE_GUARD) ||
(Info->Protect & PAGE_NOACCESS) ||
(Info->State & MEM_FREE) ||
(Info->State & MEM_RESERVE)))
{
break;
}
*Handle = Info->BaseAddress + Info->RegionSize;
}
*Handle = Info->BaseAddress + Info->RegionSize;
return S_OK;
}
HRESULT
LiveUserTargetInfo::ReadHandleData(
IN ProcessInfo* Process,
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
return m_Services->
ReadHandleData(Process->m_SysHandle,
Handle, DataType, Buffer, BufferSize,
DataSize);
}
HRESULT
LiveUserTargetInfo::GetProcessorId(
ULONG Processor,
PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id
)
{
ULONG Done;
return m_Services->GetProcessorId(Id, sizeof(*Id), &Done);
}
HRESULT
LiveUserTargetInfo::GetProcessorSpeed(
ULONG Processor,
PULONG Speed
)
{
return E_UNEXPECTED;
}
HRESULT
LiveUserTargetInfo::GetGenericProcessorFeatures(
ULONG Processor,
PULONG64 Features,
ULONG FeaturesSize,
PULONG Used
)
{
return m_Services->
GetGenericProcessorFeatures(Features, FeaturesSize, Used);
}
HRESULT
LiveUserTargetInfo::GetSpecificProcessorFeatures(
ULONG Processor,
PULONG64 Features,
ULONG FeaturesSize,
PULONG Used
)
{
return m_Services->
GetSpecificProcessorFeatures(Features, FeaturesSize, Used);
}
//----------------------------------------------------------------------------
//
// IDebugDataSpaces.
//
//----------------------------------------------------------------------------
STDMETHODIMP
DebugClient::ReadVirtual(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = CurReadVirtual(Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteVirtual(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->WriteVirtual(g_Process, Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SearchVirtual(
THIS_
IN ULONG64 Offset,
IN ULONG64 Length,
IN PVOID Pattern,
IN ULONG PatternSize,
IN ULONG PatternGranularity,
OUT PULONG64 MatchOffset
)
{
if (PatternGranularity == 0 ||
PatternSize % PatternGranularity)
{
return E_INVALIDARG;
}
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->SearchVirtual(g_Process, Offset, Length, Pattern,
PatternSize, PatternGranularity,
MatchOffset);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadVirtualUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
ReadVirtualUncached(g_Process, Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteVirtualUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
WriteVirtualUncached(g_Process, Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadPointersVirtual(
THIS_
IN ULONG Count,
IN ULONG64 Offset,
OUT /* size_is(Count) */ PULONG64 Ptrs
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = S_OK;
while (Count-- > 0)
{
if ((Status = g_Target->
ReadPointer(g_Process, g_Machine, Offset, Ptrs)) != S_OK)
{
break;
}
Offset += g_Machine->m_Ptr64 ? sizeof(ULONG64) : sizeof(ULONG);
Ptrs++;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WritePointersVirtual(
THIS_
IN ULONG Count,
IN ULONG64 Offset,
IN /* size_is(Count) */ PULONG64 Ptrs
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = S_OK;
while (Count-- > 0)
{
if ((Status = g_Target->
WritePointer(g_Process, g_Machine, Offset, *Ptrs)) != S_OK)
{
break;
}
Offset += g_Machine->m_Ptr64 ? sizeof(ULONG64) : sizeof(ULONG);
Ptrs++;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadPhysical(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
ReadPhysical(Offset, Buffer, BufferSize,
PHYS_FLAG_DEFAULT,
BytesRead != NULL ? BytesRead : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
WritePhysical(Offset, Buffer, BufferSize,
PHYS_FLAG_DEFAULT,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
// KSPECIAL_REGISTER content is kept in control space
// so accessing control space may touch data that's
// cached in the current machine KSPECIAL_REGISTERS.
// Flush the current machine to maintain consistency.
g_Target->FlushRegContext();
Status = g_Target->
ReadControl(Processor, Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
// KSPECIAL_REGISTER content is kept in control space
// so accessing control space may touch data that's
// cached in the current machine KSPECIAL_REGISTERS.
// Flush the current machine to maintain consistency.
g_Target->FlushRegContext();
Status = g_Target->
WriteControl(Processor, Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
ReadIo(InterfaceType, BusNumber, AddressSpace,
Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteIo(
THIS_
IN ULONG InterfaceType,
IN ULONG BusNumber,
IN ULONG AddressSpace,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
WriteIo(InterfaceType, BusNumber, AddressSpace,
Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadMsr(
THIS_
IN ULONG Msr,
OUT PULONG64 Value
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->ReadMsr(Msr, Value);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->WriteMsr(Msr, Value);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
ReadBusData(BusDataType, BusNumber, SlotNumber,
Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead : &BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteBusData(
THIS_
IN ULONG BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG BytesTemp;
Status = g_Target->
WriteBusData(BusDataType, BusNumber, SlotNumber,
Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::CheckLowMemory(
THIS
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->CheckLowMemory();
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadDebuggerData(
THIS_
IN ULONG Index,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
HRESULT Status;
ENTER_ENGINE();
// Wait till the machine is accessible because on dump files the
// debugger data block requires symbols to be loaded.
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
PVOID Data;
ULONG Size;
ULONG64 DataSpace;
if (Index < sizeof(g_Target->m_KdDebuggerData))
{
// Even though internally all of the debugger data is
// a single buffer that could be read arbitrarily we
// restrict access to the defined constants to
// preserve the abstraction that each constant refers
// to a separate piece of data.
Data = (PUCHAR)&g_Target->m_KdDebuggerData + Index;
Size = sizeof(ULONG64);
if ((Index >= DEBUG_DATA_OffsetKThreadNextProcessor) &&
(Index <= DEBUG_DATA_SizeEThread))
{
Size = sizeof(USHORT);
}
else if (Index & (sizeof(ULONG64) - 1))
{
Status = E_INVALIDARG;
goto Exit;
}
}
else
{
switch(Index)
{
case DEBUG_DATA_PaeEnabled:
DataSpace = (BOOLEAN)g_Target->m_KdDebuggerData.PaeEnabled;
Data = &DataSpace;
Size = sizeof(BOOLEAN);
break;
case DEBUG_DATA_SharedUserData:
DataSpace = g_Target->m_TypeInfo.SharedUserDataOffset;
Data = &DataSpace;
Size = sizeof(ULONG64);
break;
case DEBUG_DATA_ProductType:
Data = &g_Target->m_ProductType;
Size = sizeof(g_Target->m_ProductType);
break;
case DEBUG_DATA_SuiteMask:
Data = &g_Target->m_SuiteMask;
Size = sizeof(g_Target->m_SuiteMask);
break;
default:
Status = E_INVALIDARG;
goto Exit;
}
}
Status = FillDataBuffer(Data, Size, Buffer, BufferSize, DataSize);
Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadProcessorSystemData(
THIS_
IN ULONG Processor,
IN ULONG Index,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
HRESULT Status = S_OK;
PVOID Data;
ULONG Size;
ULONG64 DataSpace;
DEBUG_PROCESSOR_IDENTIFICATION_ALL AllId;
ENTER_ENGINE();
switch(Index)
{
case DEBUG_DATA_KPCR_OFFSET:
case DEBUG_DATA_KPRCB_OFFSET:
case DEBUG_DATA_KTHREAD_OFFSET:
if (!IS_MACHINE_SET(g_Target))
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->
GetProcessorSystemDataOffset(Processor, Index, &DataSpace);
Data = &DataSpace;
Size = sizeof(DataSpace);
}
break;
case DEBUG_DATA_BASE_TRANSLATION_VIRTUAL_OFFSET:
if (!IS_MACHINE_SET(g_Target))
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Machine->GetBaseTranslationVirtualOffset(&DataSpace);
Data = &DataSpace;
Size = sizeof(DataSpace);
}
break;
case DEBUG_DATA_PROCESSOR_IDENTIFICATION:
if (!g_Target)
{
Status = E_UNEXPECTED;
}
else
{
ZeroMemory(&AllId, sizeof(AllId));
Status = g_Target->GetProcessorId(Processor, &AllId);
Data = &AllId;
Size = sizeof(AllId);
}
break;
case DEBUG_DATA_PROCESSOR_SPEED:
if (!g_Target)
{
Status = E_UNEXPECTED;
}
else
{
DataSpace = 0;
Status = g_Target->
GetProcessorSpeed(Processor, (PULONG) &DataSpace);
Data = &DataSpace;
Size = sizeof(ULONG);
}
break;
default:
Status = E_INVALIDARG;
break;
}
if (Status == S_OK)
{
if (DataSize != NULL)
{
*DataSize = Size;
}
if (BufferSize < Size)
{
Status = S_FALSE;
Size = BufferSize;
}
memcpy(Buffer, Data, Size);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::VirtualToPhysical(
THIS_
IN ULONG64 Virtual,
OUT PULONG64 Physical
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG Levels;
ULONG PfIndex;
Status = g_Machine->
GetVirtualTranslationPhysicalOffsets(g_Thread, Virtual, NULL, 0,
&Levels, &PfIndex, Physical);
// GVTPO returns a special error code if the translation
// succeeded down to the level of the actual data but
// the data page itself is in the page file. This is used
// for the page file dump support. To an external caller,
// though, it's not useful so translate it into the standard
// page-not-available error.
if (Status == HR_PAGE_IN_PAGE_FILE)
{
Status = HR_PAGE_NOT_AVAILABLE;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetVirtualTranslationPhysicalOffsets(
THIS_
IN ULONG64 Virtual,
OUT OPTIONAL /* size_is(OffsetsSize) */ PULONG64 Offsets,
IN ULONG OffsetsSize,
OUT OPTIONAL PULONG Levels
)
{
HRESULT Status;
ENTER_ENGINE();
ULONG _Levels = 0;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG PfIndex;
ULONG64 LastPhys;
Status = g_Machine->
GetVirtualTranslationPhysicalOffsets(g_Thread, Virtual, Offsets,
OffsetsSize, &_Levels,
&PfIndex, &LastPhys);
// GVTPO returns a special error code if the translation
// succeeded down to the level of the actual data but
// the data page itself is in the page file. This is used
// for the page file dump support. To an external caller,
// though, it's not useful so translate it into the standard
// page-not-available error.
if (Status == HR_PAGE_IN_PAGE_FILE)
{
Status = HR_PAGE_NOT_AVAILABLE;
}
// If no translations occurred return the given failure.
// If there was a failure but translations occurred return
// S_FALSE to indicate the translation was incomplete.
if (_Levels > 0 && Status != S_OK)
{
Status = S_FALSE;
}
}
if (Levels != NULL)
{
*Levels = _Levels;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadHandleData(
THIS_
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!g_Process)
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->ReadHandleData(g_Process, Handle, DataType, Buffer,
BufferSize, DataSize);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::FillVirtual(
THIS_
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT OPTIONAL PULONG Filled
)
{
HRESULT Status;
ENTER_ENGINE();
if (PatternSize == 0)
{
Status = E_INVALIDARG;
}
else if (!g_Target || !g_Process)
{
Status = E_UNEXPECTED;
}
else
{
ULONG _Filled = 0;
Status = g_Target->FillVirtual(g_Process,
Start, Size, Pattern, PatternSize,
&_Filled);
if (Filled != NULL)
{
*Filled = _Filled;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::FillPhysical(
THIS_
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT OPTIONAL PULONG Filled
)
{
HRESULT Status;
ENTER_ENGINE();
if (PatternSize == 0)
{
Status = E_INVALIDARG;
}
else if (!g_Target)
{
Status = E_UNEXPECTED;
}
else
{
ULONG _Filled = 0;
Status = g_Target->
FillPhysical(Start, Size, Pattern, PatternSize, &_Filled);
if (Filled != NULL)
{
*Filled = _Filled;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::QueryVirtual(
THIS_
IN ULONG64 Offset,
OUT PMEMORY_BASIC_INFORMATION64 Info
)
{
HRESULT Status;
ENTER_ENGINE();
if (!g_Target || !g_Process)
{
Status = E_UNEXPECTED;
}
else if (!IS_USER_TARGET(g_Target))
{
return E_NOTIMPL;
}
else
{
ULONG64 Handle = Offset;
Status = g_Target->QueryMemoryRegion(g_Process, &Handle, TRUE, Info);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadImageNtHeaders(
THIS_
IN ULONG64 ImageBase,
OUT PIMAGE_NT_HEADERS64 Headers
)
{
HRESULT Status;
ENTER_ENGINE();
if (!g_Target || !g_Process)
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->ReadImageNtHeaders(g_Process, ImageBase, Headers);
}
LEAVE_ENGINE();
return Status;
}
HRESULT
GetFirstBlobHeaderOffset(PULONG64 OffsetRet)
{
HRESULT Status;
ULONG64 Offset;
DUMP_BLOB_FILE_HEADER FileHdr;
if ((Status = g_Target->
GetTaggedBaseOffset(&Offset)) != S_OK ||
(Status = g_Target->
ReadTagged(Offset, &FileHdr, sizeof(FileHdr))) != S_OK)
{
return Status;
}
if (FileHdr.Signature1 != DUMP_BLOB_SIGNATURE1 ||
FileHdr.Signature2 != DUMP_BLOB_SIGNATURE2)
{
// No blob data.
return E_NOINTERFACE;
}
if (FileHdr.HeaderSize != sizeof(FileHdr))
{
return HR_DATA_CORRUPT;
}
*OffsetRet = Offset + FileHdr.HeaderSize;
return S_OK;
}
HRESULT
ReadBlobHeaderAtOffset(ULONG64 Offset, PDUMP_BLOB_HEADER BlobHdr)
{
HRESULT Status;
if ((Status = g_Target->
ReadTagged(Offset, BlobHdr, sizeof(*BlobHdr))) != S_OK)
{
return Status;
}
if (BlobHdr->HeaderSize != sizeof(*BlobHdr))
{
return HR_DATA_CORRUPT;
}
return S_OK;
}
STDMETHODIMP
DebugClient::ReadTagged(
THIS_
IN LPGUID Tag,
IN ULONG Offset,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG TotalSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!g_Target)
{
Status = E_UNEXPECTED;
goto Exit;
}
ULONG64 HdrOffs;
DUMP_BLOB_HEADER BlobHdr;
if ((Status = GetFirstBlobHeaderOffset(&HdrOffs)) != S_OK)
{
goto Exit;
}
for (;;)
{
if ((Status = ReadBlobHeaderAtOffset(HdrOffs, &BlobHdr)) != S_OK)
{
// There's no way to know whether a blob should
// be present or not so all failures just turn
// into blob-not-present.
Status = E_NOINTERFACE;
goto Exit;
}
HdrOffs += BlobHdr.HeaderSize + BlobHdr.PrePad;
if (DbgIsEqualIID(*Tag, BlobHdr.Tag))
{
break;
}
HdrOffs += BlobHdr.DataSize + BlobHdr.PostPad;
}
if (Offset >= BlobHdr.DataSize)
{
Status = E_INVALIDARG;
goto Exit;
}
if (TotalSize)
{
*TotalSize = BlobHdr.DataSize;
}
if (Buffer)
{
if (BufferSize > BlobHdr.DataSize)
{
BufferSize = BlobHdr.DataSize;
}
Status = g_Target->ReadTagged(HdrOffs + Offset, Buffer, BufferSize);
}
else
{
Status = S_OK;
}
Exit:
LEAVE_ENGINE();
return Status;
}
struct TaggedEnum
{
ULONG64 Offset;
};
STDMETHODIMP
DebugClient::StartEnumTagged(
THIS_
OUT PULONG64 Handle
)
{
HRESULT Status;
ENTER_ENGINE();
TaggedEnum* Enum;
if (!g_Target)
{
Status = E_UNEXPECTED;
}
else if (!(Enum = new TaggedEnum))
{
Status = E_OUTOFMEMORY;
}
else
{
Status = GetFirstBlobHeaderOffset(&Enum->Offset);
if (Status == S_OK)
{
*Handle = (ULONG64)(ULONG_PTR)Enum;
}
else
{
delete Enum;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetNextTagged(
THIS_
IN ULONG64 Handle,
OUT LPGUID Tag,
OUT PULONG Size
)
{
HRESULT Status;
ENTER_ENGINE();
TaggedEnum* Enum = (TaggedEnum*)(ULONG_PTR)Handle;
DUMP_BLOB_HEADER BlobHdr;
if (!g_Target)
{
Status = E_UNEXPECTED;
}
else if ((Status = ReadBlobHeaderAtOffset(Enum->Offset,
&BlobHdr)) != S_OK)
{
// There's no way to know whether a blob should
// be present or not so all failures just turn
// into blob-not-present.
Status = S_FALSE;
ZeroMemory(Tag, sizeof(*Tag));
*Size = 0;
}
else
{
*Tag = BlobHdr.Tag;
*Size = BlobHdr.DataSize;
Enum->Offset += BlobHdr.HeaderSize + BlobHdr.PrePad +
BlobHdr.DataSize + BlobHdr.PostPad;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::EndEnumTagged(
THIS_
IN ULONG64 Handle
)
{
HRESULT Status;
ENTER_ENGINE();
if (!g_Target)
{
Status = E_UNEXPECTED;
}
else
{
delete (TaggedEnum*)(ULONG_PTR)Handle;
Status = S_OK;
}
LEAVE_ENGINE();
return Status;
}