Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3695 lines
88 KiB

//----------------------------------------------------------------------------
//
// IDebugDataSpaces implementations.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------
#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 = (Offset + g_TargetMachine->m_PageSize) &
~((ULONG64)g_TargetMachine->m_PageSize - 1);
if (NextOffset != NULL)
{
*NextOffset = Page;
}
if (NextPage != NULL)
{
*NextPage = Page;
}
}
HRESULT
TargetInfo::ReadVirtualUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return ReadVirtual(Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
TargetInfo::WriteVirtualUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
return WriteVirtual(Offset, Buffer, BufferSize, BytesWritten);
}
// #define DBG_SEARCH
HRESULT
TargetInfo::SearchVirtual(
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())
{
dprintf("User interrupt during memory search - "
"exiting.\n");
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(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;
}
HRESULT
TargetInfo::ReadPhysicalUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return ReadPhysical(Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
TargetInfo::WritePhysicalUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
return WritePhysical(Offset, Buffer, BufferSize, BytesWritten);
}
HRESULT
TargetInfo::FillVirtual(
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 = WriteVirtual(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, &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::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::GetFunctionTableListHead(void)
{
// 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 (g_CurrentProcess->DynFuncTableList)
{
return S_OK;
}
GetOffsetFromSym("ntdll!RtlpDynamicFunctionTable",
&g_CurrentProcess->DynFuncTableList, NULL);
if (!g_CurrentProcess->DynFuncTableList)
{
// No error message here as it's a common case when
// symbols are bad.
return E_NOINTERFACE;
}
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(MachineInfo* Machine, ULONG64 Address)
{
LIST_ENTRY64 DynamicFunctionTableHead;
ULONG64 Entry;
if (GetFunctionTableListHead() != S_OK)
{
return NULL;
}
// Read the dynamic function table list head
if (ReadListEntry(Machine,
g_CurrentProcess->DynFuncTableList,
&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())
{
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 != g_CurrentProcess->DynFuncTableList)
{
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 (Machine->ReadDynamicFunctionTable(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
(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(TableData, FunctionTable,
TableSize) != S_OK)
{
ErrOut("Unable to read dynamic function table entries\n");
free(FunctionTable);
continue;
}
}
FunctionEntry = Machine->
FindDynamicFunctionEntry(&RawTable, Address,
FunctionTable, TableSize);
free(FunctionTable);
if (FunctionEntry)
{
return FunctionEntry;
}
}
}
return NULL;
}
ULONG64
TargetInfo::GetDynamicFunctionTableBase(MachineInfo* Machine,
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 (!g_CurrentProcess->DynFuncTableList)
{
return 0;
}
if (ReadListEntry(Machine, g_CurrentProcess->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 != g_CurrentProcess->DynFuncTableList)
{
ULONG64 MinAddress, MaxAddress, BaseAddress, TableData;
ULONG TableSize;
WCHAR OutOfProcessDll[MAX_PATH];
CROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable;
if (Machine->ReadDynamicFunctionTable(Entry, &Entry,
&MinAddress, &MaxAddress,
&BaseAddress,
&TableData, &TableSize,
OutOfProcessDll,
&RawTable) == S_OK &&
Address >= MinAddress &&
Address < MaxAddress)
{
return BaseAddress;
}
}
return 0;
}
HRESULT
TargetInfo::ReadOutOfProcessDynamicFunctionTable(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 Process,
ULONG64 Address,
ULONG64 Context)
{
DBG_ASSERT(Process == g_CurrentProcess->Handle);
return g_Target->FindDynamicFunctionEntry((MachineInfo*)Context,
Address);
}
HRESULT
TargetInfo::QueryAddressInformation(ULONG64 Address, ULONG InSpace,
PULONG OutSpace, PULONG OutFlags)
{
// Default implementation which just returns the
// least restrictive settings.
*OutSpace = IS_KERNEL_TARGET() ?
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(
MachineInfo* Machine,
ULONG64 Address,
PULONG64 Pointer64
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToRead;
ULONG Pointer32;
if (Machine->m_Ptr64)
{
SizeToRead = sizeof(ULONG64);
Status = ReadVirtual(Address, Pointer64, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(ULONG32);
Status = ReadVirtual(Address, &Pointer32, SizeToRead, &Result);
*Pointer64 = EXTEND64(Pointer32);
}
if (Status != S_OK)
{
return Status;
}
if (Result != SizeToRead)
{
return E_FAIL;
}
return S_OK;
}
HRESULT
TargetInfo::WritePointer(
MachineInfo* Machine,
ULONG64 Address,
ULONG64 Pointer64
)
{
HRESULT Status;
ULONG Result;
ULONG SizeToWrite;
ULONG Pointer32;
if (Machine->m_Ptr64)
{
SizeToWrite = sizeof(ULONG64);
Status = WriteVirtual(Address, &Pointer64, SizeToWrite, &Result);
}
else
{
SizeToWrite = sizeof(ULONG32);
Pointer32 = (ULONG)Pointer64;
Status = WriteVirtual(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(
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(Address, List64, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(LIST_ENTRY32);
Status = ReadVirtual(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(
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(Address, Entry, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(KLDR_DATA_TABLE_ENTRY32);
Status = ReadVirtual(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(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(Address, String, SizeToRead, &Result);
}
else
{
SizeToRead = sizeof(UNICODE_STRING32);
Status = ReadVirtual(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;
}
HRESULT
TargetInfo::ReadDirectoryTableBase(PULONG64 DirBase)
{
HRESULT Status;
ULONG64 CurProc;
// Retrieve the current EPROCESS's DirectoryTableBase[0] value.
Status = GetProcessInfoDataOffset(g_CurrentProcess->CurrentThread,
0, 0, &CurProc);
if (Status != S_OK)
{
return Status;
}
CurProc += g_TargetMachine->m_OffsetEprocessDirectoryTableBase;
return ReadPointer(g_TargetMachine, CurProc, DirBase);
}
HRESULT
TargetInfo::ReadImplicitThreadInfoPointer(ULONG Offset, PULONG64 Ptr)
{
HRESULT Status;
ULONG64 CurThread;
// Retrieve the current ETHREAD.
if ((Status = GetImplicitThreadData(&CurThread)) != S_OK)
{
return Status;
}
return ReadPointer(g_TargetMachine, CurThread + Offset, Ptr);
}
HRESULT
TargetInfo::ReadImplicitProcessInfoPointer(ULONG Offset, PULONG64 Ptr)
{
HRESULT Status;
ULONG64 CurProc;
// Retrieve the current EPROCESS.
if ((Status = GetImplicitProcessData(&CurProc)) != S_OK)
{
return Status;
}
return ReadPointer(g_TargetMachine, CurProc + Offset, Ptr);
}
HRESULT
TargetInfo::ReadSharedUserTimeDateN(PULONG64 TimeDate)
{
HRESULT Status;
ULONG Done;
Status = ReadVirtual(g_TargetMachine->m_SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, SystemTime),
TimeDate, sizeof(*TimeDate), &Done);
if (Status != S_OK)
{
return Status;
}
if (Done != sizeof(*TimeDate))
{
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
}
return S_OK;
}
HRESULT
TargetInfo::ReadSharedUserUpTimeN(PULONG64 UpTime)
{
HRESULT Status;
ULONG Done;
Status = ReadVirtual(g_TargetMachine->m_SharedUserDataOffset +
FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTime),
UpTime, sizeof(*UpTime), &Done);
if (Status != S_OK)
{
return Status;
}
if (Done != sizeof(*UpTime))
{
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
}
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(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(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(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(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(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(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(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(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(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(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;
}
//----------------------------------------------------------------------------
//
// LiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
LiveKernelTargetInfo::GetProcessorId
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
return g_TargetMachine->ReadKernelProcessorId(Processor, Id);
}
//----------------------------------------------------------------------------
//
// ConnLiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
ConnLiveKernelTargetInfo::ReadVirtual(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return g_VirtualCache.Read(Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
ConnLiveKernelTargetInfo::WriteVirtual(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
HRESULT Status =
g_VirtualCache.Write(Offset, Buffer, BufferSize, BytesWritten);
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::SearchVirtual(
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 ReamMemory \ search.
//
HRESULT Status;
if (g_SystemVersion <= NT_SVER_NT4 || PatternGranularity != 1)
{
Status = TargetInfo::SearchVirtual(Offset, Length, (PUCHAR)Pattern,
PatternSize, PatternGranularity,
MatchOffset);
}
else
{
NTSTATUS NtStatus =
DbgKdSearchMemory(Offset, Length, (PUCHAR)Pattern,
PatternSize, MatchOffset);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::ReadVirtualUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
ULONG Length, Read;
NTSTATUS Status;
*BytesRead = 0;
while (BufferSize)
{
Length = BufferSize;
for (;;)
{
Status = DbgKdReadVirtualMemoryNow(Offset, Buffer, Length, &Read);
if (NT_SUCCESS(Status))
{
break;
}
if (Status == STATUS_CONTROL_C_EXIT)
{
return HRESULT_FROM_NT(Status);
}
if ((Offset & ~((ULONG64)g_TargetMachine->m_PageSize - 1)) !=
((Offset + Length - 1) & ~((ULONG64)g_TargetMachine->m_PageSize - 1)))
{
//
// Before accepting the error, make sure request
// didn't fail because it crossed multiple pages
//
Length = (ULONG)
((Offset | (g_TargetMachine->m_PageSize - 1)) -
Offset + 1);
}
else
{
if (Status == STATUS_UNSUCCESSFUL &&
g_VirtualCache.m_DecodePTEs &&
!g_VirtualCache.m_ForceDecodePTEs)
{
//
// Try getting the memory by looking up the physical
// location of the page
//
Status = DbgKdReadVirtualTranslatedMemory(Offset, Buffer,
Length, &Read);
if (NT_SUCCESS(Status))
{
break;
}
}
//
// Unable to get more memory. If we already read
// some return success, otherwise return error to
// the caller.
//
return *BytesRead > 0 ? S_OK : HRESULT_FROM_NT(Status);
}
}
BufferSize -= Read;
Offset += Read;
Buffer = (PVOID)((PUCHAR)Buffer + Read);
*BytesRead += Read;
}
return S_OK;
}
HRESULT
ConnLiveKernelTargetInfo::WriteVirtualUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
NTSTATUS Status =
DbgKdWriteVirtualMemoryNow(Offset, Buffer, BufferSize, BytesWritten);
if (Status == STATUS_UNSUCCESSFUL &&
g_VirtualCache.m_DecodePTEs &&
!g_VirtualCache.m_ForceDecodePTEs)
{
//
// Try getting the memory by looking up the physical
// location of the page
//
Status = DbgKdWriteVirtualTranslatedMemory(Offset, Buffer,
BufferSize, BytesWritten);
}
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::ReadPhysical(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
if (g_PhysicalCacheActive)
{
return g_PhysicalCache.Read(Offset, Buffer, BufferSize, BytesRead);
}
else
{
return ReadPhysicalUncached(Offset, Buffer, BufferSize, BytesRead);
}
}
HRESULT
ConnLiveKernelTargetInfo::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
if (g_PhysicalCacheActive)
{
return g_PhysicalCache.Write(Offset, Buffer, BufferSize,
BytesWritten);
}
else
{
return WritePhysicalUncached(Offset, Buffer, BufferSize,
BytesWritten);
}
}
HRESULT
ConnLiveKernelTargetInfo::ReadPhysicalUncached(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
NTSTATUS Status =
DbgKdReadPhysicalMemory(Offset, Buffer, BufferSize,
BytesRead);
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::WritePhysicalUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
NTSTATUS Status =
DbgKdWritePhysicalMemory(Offset, Buffer, BufferSize,
BytesWritten);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_PHYSICAL);
}
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
NTSTATUS Status =
DbgKdReadControlSpace((USHORT)Processor, (ULONG)Offset,
Buffer, BufferSize, BytesRead);
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
NTSTATUS Status =
DbgKdWriteControlSpace((USHORT)Processor, (ULONG)Offset,
Buffer, BufferSize,
BytesWritten);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_CONTROL);
}
return CONV_NT_STATUS(Status);
}
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
)
{
NTSTATUS Status;
// Convert trivially extended I/O requests down into simple
// requests as not all platform support extended requests.
if (InterfaceType == Isa && BusNumber == 0 && AddressSpace == 1)
{
Status = DbgKdReadIoSpace(Offset, Buffer, BufferSize);
}
else
{
Status = DbgKdReadIoSpaceExtended(Offset, Buffer, BufferSize,
(INTERFACE_TYPE)InterfaceType,
BusNumber, AddressSpace);
}
if (NT_SUCCESS(Status))
{
// I/O access currently can't successfully return anything
// than the requested size.
if (BytesRead != NULL)
{
*BytesRead = BufferSize;
}
return S_OK;
}
else
{
return HRESULT_FROM_NT(Status);
}
}
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
)
{
NTSTATUS Status;
// Convert trivially extended I/O requests down into simple
// requests as not all platform support extended requests.
if (InterfaceType == Isa && BusNumber == 0 && AddressSpace == 1)
{
Status = DbgKdWriteIoSpace(Offset, *(ULONG *)Buffer, BufferSize);
}
else
{
Status = DbgKdWriteIoSpaceExtended(Offset, *(ULONG *)Buffer,
BufferSize,
(INTERFACE_TYPE)InterfaceType,
BusNumber, AddressSpace);
}
if (NT_SUCCESS(Status))
{
// I/O access currently can't successfully return anything
// 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(Status);
}
}
HRESULT
ConnLiveKernelTargetInfo::ReadMsr(
THIS_
IN ULONG Msr,
OUT PULONG64 Value
)
{
NTSTATUS Status =
DbgKdReadMsr(Msr, Value);
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
NTSTATUS Status =
DbgKdWriteMsr(Msr, Value);
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_MSR);
}
return CONV_NT_STATUS(Status);
}
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
)
{
NTSTATUS Status =
DbgKdGetBusData(BusDataType, BusNumber, SlotNumber,
Buffer, Offset, &BufferSize);
if (NT_SUCCESS(Status) && BytesRead != NULL)
{
*BytesRead = BufferSize;
}
return CONV_NT_STATUS(Status);
}
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
)
{
NTSTATUS Status =
DbgKdSetBusData(BusDataType, BusNumber, SlotNumber,
Buffer, Offset, &BufferSize);
if (NT_SUCCESS(Status) && BytesWritten != NULL)
{
*BytesWritten = BufferSize;
}
if (NT_SUCCESS(Status))
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_BUS_DATA);
}
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::CheckLowMemory(
THIS
)
{
NTSTATUS Status =
DbgKdCheckLowMemory();
return CONV_NT_STATUS(Status);
}
HRESULT
ConnLiveKernelTargetInfo::FillVirtual(
THIS_
IN ULONG64 Start,
IN ULONG Size,
IN PVOID Pattern,
IN ULONG PatternSize,
OUT PULONG Filled
)
{
HRESULT Status;
if (g_KdMaxManipulate <= DbgKdFillMemoryApi ||
PatternSize > PACKET_MAX_SIZE)
{
Status = TargetInfo::FillVirtual(Start, Size, Pattern,
PatternSize, Filled);
}
else
{
NTSTATUS NtStatus =
DbgKdFillMemory(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 (g_KdMaxManipulate <= DbgKdFillMemoryApi ||
PatternSize > PACKET_MAX_SIZE)
{
Status = TargetInfo::FillPhysical(Start, Size, Pattern,
PatternSize, Filled);
}
else
{
NTSTATUS NtStatus =
DbgKdFillMemory(DBGKD_FILL_MEMORY_PHYSICAL, Start, Size,
Pattern, PatternSize, Filled);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
HRESULT
ConnLiveKernelTargetInfo::QueryAddressInformation(ULONG64 Address,
ULONG InSpace,
PULONG OutSpace,
PULONG OutFlags)
{
HRESULT Status;
if (g_KdMaxManipulate <= DbgKdQueryMemoryApi)
{
Status = TargetInfo::QueryAddressInformation(Address, InSpace,
OutSpace, OutFlags);
}
else
{
NTSTATUS NtStatus =
DbgKdQueryMemory(Address, InSpace, OutSpace, OutFlags);
Status = CONV_NT_STATUS(NtStatus);
}
return Status;
}
//----------------------------------------------------------------------------
//
// LocalLiveKernelTargetInfo data space methods.
//
//----------------------------------------------------------------------------
HRESULT
LocalLiveKernelTargetInfo::ReadVirtual(
THIS_
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 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,
OUT PULONG BytesRead
)
{
SYSDBG_PHYSICAL 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.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,
OUT PULONG BytesWritten
)
{
SYSDBG_PHYSICAL 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.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(
THIS
)
{
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 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 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,
OUT PULONG BytesRead
)
{
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,
OUT PULONG BytesWritten
)
{
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;
}
// XXX drewb - Guessing at how to implement these spaces.
#define EXDI_ADDR_CONTROL_SPACE 2
#define EXDI_ADDR_MSR 3
#define EXDI_ADDR_BUS_DATA 4
HRESULT
ExdiLiveKernelTargetInfo::ReadControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
ReadPhysicalMemoryOrPeriphIO(Offset, EXDI_ADDR_CONTROL_SPACE,
BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesRead = BufferSize;
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteControl(
THIS_
IN ULONG Processor,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
WritePhysicalMemoryOrPeriphIO(Offset, EXDI_ADDR_CONTROL_SPACE,
BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesWritten = BufferSize;
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_CONTROL);
}
return Status;
}
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
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
ReadPhysicalMemoryOrPeriphIO(Msr, EXDI_ADDR_MSR,
1, 64, (PBYTE)Value);
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
WritePhysicalMemoryOrPeriphIO(Msr, EXDI_ADDR_MSR,
1, 64, (PBYTE)&Value);
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
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
ReadPhysicalMemoryOrPeriphIO(Offset, EXDI_ADDR_BUS_DATA,
BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesRead = BufferSize;
}
return Status;
}
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
)
{
if (m_KdSupport != EXDI_KD_IOCTL)
{
return E_UNEXPECTED;
}
HRESULT Status = m_Server->
WritePhysicalMemoryOrPeriphIO(Offset, EXDI_ADDR_BUS_DATA,
BufferSize, 8, (PBYTE)Buffer);
if (Status == S_OK)
{
*BytesWritten = BufferSize;
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_BUS_DATA);
}
return Status;
}
HRESULT
ExdiLiveKernelTargetInfo::CheckLowMemory(
THIS
)
{
// 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
UserTargetInfo::ReadVirtualUncached(
THIS_
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 = g_TargetMachine->m_PageSize - (ULONG)
(Offset & (g_TargetMachine->m_PageSize - 1));
ReadSize = min(BufferSize, ReadSize);
if ((Status = m_Services->
ReadVirtual(g_CurrentProcess->FullHandle, 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
UserTargetInfo::WriteVirtualUncached(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
ULONG RealBytesWritten;
HRESULT Status =
m_Services->WriteVirtual(g_CurrentProcess->FullHandle,
Offset, Buffer, BufferSize,
&RealBytesWritten);
*BytesWritten = (DWORD) RealBytesWritten;
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_DATA, DEBUG_DATA_SPACE_VIRTUAL);
}
return Status;
}
HRESULT
UserTargetInfo::GetFunctionTableListHead(void)
{
// 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 (g_CurrentProcess->DynFuncTableList)
{
return S_OK;
}
if (m_Services->
GetFunctionTableListHead(g_CurrentProcess->FullHandle,
&g_CurrentProcess->DynFuncTableList) == S_OK)
{
return S_OK;
}
return TargetInfo::GetFunctionTableListHead();
}
HRESULT
UserTargetInfo::ReadOutOfProcessDynamicFunctionTable(PWSTR Dll,
ULONG64 Table,
PULONG RetTableSize,
PVOID* RetTableData)
{
HRESULT Status;
char DllA[MAX_PATH];
PVOID TableData;
ULONG TableSize;
if (!WideCharToMultiByte(CP_ACP, 0, Dll, -1,
DllA, sizeof(DllA), NULL, NULL))
{
return WIN32_LAST_STATUS();
}
// 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;
}
Status = m_Services->
GetOutOfProcessFunctionTable(g_CurrentProcess->FullHandle,
DllA, Table, TableData,
TableSize, &TableSize);
if (Status == S_OK)
{
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
UserTargetInfo::QueryMemoryRegion(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(g_CurrentProcess->FullHandle,
*Handle, &MemInfo, sizeof(MemInfo), &Used)) != S_OK)
{
return Status;
}
if (g_TargetMachine->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 (!((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
UserTargetInfo::ReadHandleData(
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
return m_Services->ReadHandleData(g_CurrentProcess->FullHandle,
Handle, DataType, Buffer, BufferSize,
DataSize);
}
HRESULT
UserTargetInfo::GetProcessorId
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
ULONG Done;
return m_Services->GetProcessorId(Id, sizeof(*Id), &Done);
}
HRESULT
LocalUserTargetInfo::ReadVirtual(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return ReadVirtualUncached(Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
LocalUserTargetInfo::WriteVirtual(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
return WriteVirtualUncached(Offset, Buffer, BufferSize, BytesWritten);
}
HRESULT
RemoteUserTargetInfo::ReadVirtual(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
return g_VirtualCache.Read(Offset, Buffer, BufferSize, BytesRead);
}
HRESULT
RemoteUserTargetInfo::WriteVirtual(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
return g_VirtualCache.Write(Offset, Buffer, BufferSize, BytesWritten);
}
//----------------------------------------------------------------------------
//
// IDebugDataSpaces.
//
//----------------------------------------------------------------------------
STDMETHODIMP
DebugClient::ReadVirtual(
THIS_
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesRead
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->ReadVirtual(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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->WriteVirtual(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;
}
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
HRESULT Status;
ENTER_ENGINE();
Status = g_Target->SearchVirtual(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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->ReadVirtualUncached(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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->WriteVirtualUncached(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_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG Done;
Status = S_OK;
while (Count-- > 0)
{
if ((Status = g_Target->
ReadPointer(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_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG Done;
Status = S_OK;
while (Count-- > 0)
{
if ((Status = g_Target->
WritePointer(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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->ReadPhysical(Offset, Buffer, BufferSize,
BytesRead != NULL ? BytesRead : &BytesTemp);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WritePhysical(
THIS_
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BytesWritten
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->WritePhysical(Offset, Buffer, BufferSize,
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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
// 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.
FlushRegContext();
ULONG BytesTemp;
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
// 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.
FlushRegContext();
ULONG BytesTemp;
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
HRESULT Status =
g_Target->ReadMsr(Msr, Value);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::WriteMsr(
THIS_
IN ULONG Msr,
IN ULONG64 Value
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT 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
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
ULONG BytesTemp;
HRESULT Status =
g_Target->WriteBusData(BusDataType, BusNumber, SlotNumber,
Offset, Buffer, BufferSize,
BytesWritten != NULL ? BytesWritten :
&BytesTemp);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::CheckLowMemory(
THIS
)
{
if (!IS_MACHINE_ACCESSIBLE())
{
return E_UNEXPECTED;
}
ENTER_ENGINE();
HRESULT 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_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
PVOID Data;
ULONG Size;
ULONG64 DataSpace;
if (Index < sizeof(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.
if (Index & (sizeof(ULONG64) - 1))
{
Status = E_INVALIDARG;
goto Exit;
}
Data = (PUCHAR)&KdDebuggerData + Index;
Size = sizeof(ULONG64);
}
else
{
switch(Index)
{
case DEBUG_DATA_PaeEnabled:
DataSpace = KdDebuggerData.PaeEnabled;
Data = &DataSpace;
Size = sizeof(BOOLEAN);
break;
case DEBUG_DATA_SharedUserData:
DataSpace = g_TargetMachine->m_SharedUserDataOffset;
Data = &DataSpace;
Size = sizeof(ULONG64);
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())
{
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())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Machine->GetBaseTranslationVirtualOffset(&DataSpace);
Data = &DataSpace;
Size = sizeof(DataSpace);
}
break;
case DEBUG_DATA_PROCESSOR_IDENTIFICATION:
if (!IS_TARGET_SET())
{
Status = E_UNEXPECTED;
}
else
{
ZeroMemory(&AllId, sizeof(AllId));
Status = g_Target->GetProcessorId(Processor, &AllId);
Data = &AllId;
Size = sizeof(AllId);
}
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_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG Levels;
ULONG PfIndex;
Status = g_Machine->
GetVirtualTranslationPhysicalOffsets(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_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG PfIndex;
ULONG64 LastPhys;
Status = g_Machine->
GetVirtualTranslationPhysicalOffsets(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 (!IS_TARGET_SET())
{
Status = E_UNEXPECTED;
}
else
{
Status = g_Target->ReadHandleData(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 (!IS_TARGET_SET())
{
Status = E_UNEXPECTED;
}
else
{
ULONG _Filled = 0;
Status = g_Target->FillVirtual(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 (!IS_TARGET_SET())
{
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 (!IS_TARGET_SET())
{
Status = E_UNEXPECTED;
}
else if (!IS_USER_TARGET())
{
return E_NOTIMPL;
}
else
{
ULONG64 Handle = Offset;
Status = g_Target->QueryMemoryRegion(&Handle, TRUE, Info);
}
LEAVE_ENGINE();
return Status;
}