//---------------------------------------------------------------------------- // // 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; }