3197 lines
84 KiB
C++
3197 lines
84 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// Abstraction of target-specific information.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1999-2001.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
//
|
|
// Note by olegk
|
|
// We using KLDR_DATA_TABLE_ENTRY64 in some places like
|
|
// GetModNameFromLoaderList) instead of LDR_DATA_TABLE_ENTRY assuming that
|
|
// most important fields are the same in these structures.
|
|
// So I add some asserts for quick notification if anything will change
|
|
// (these are not fullproof checks just a basics)
|
|
//
|
|
C_ASSERT(&(((PLDR_DATA_TABLE_ENTRY64)0)->InLoadOrderLinks) ==
|
|
&(((PKLDR_DATA_TABLE_ENTRY64)0)->InLoadOrderLinks));
|
|
|
|
C_ASSERT(&(((PLDR_DATA_TABLE_ENTRY64)0)->DllBase) ==
|
|
&(((PKLDR_DATA_TABLE_ENTRY64)0)->DllBase));
|
|
|
|
C_ASSERT(&(((PLDR_DATA_TABLE_ENTRY64)0)->FullDllName) ==
|
|
&(((PKLDR_DATA_TABLE_ENTRY64)0)->FullDllName));
|
|
|
|
DBGKD_GET_VERSION64 g_KdVersion;
|
|
|
|
PCSTR g_NtSverNames[] =
|
|
{
|
|
"Windows NT 4", "Windows 2000 RC3", "Windows 2000", "Windows XP",
|
|
};
|
|
PCSTR g_W9xSverNames[] =
|
|
{
|
|
"Windows 95", "Windows 98", "Windows 98 SE", "Windows ME",
|
|
};
|
|
PCSTR g_XBoxSverNames[] =
|
|
{
|
|
"XBox",
|
|
};
|
|
PCSTR g_BigSverNames[] =
|
|
{
|
|
"BIG KD Emulation",
|
|
};
|
|
PCSTR g_ExdiSverNames[] =
|
|
{
|
|
"eXDI Device",
|
|
};
|
|
PCSTR g_NtBdSverNames[] =
|
|
{
|
|
"Windows Boot Debugger",
|
|
};
|
|
PCSTR g_EfiSverNames[] =
|
|
{
|
|
"EFI KD Emulation",
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Support functions.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONG
|
|
NtBuildToSystemVersion(ULONG Build)
|
|
{
|
|
if (Build > 2195)
|
|
{
|
|
return NT_SVER_W2K_WHISTLER;
|
|
}
|
|
else if (Build > 2183)
|
|
{
|
|
return NT_SVER_W2K;
|
|
}
|
|
else if (Build > 1381)
|
|
{
|
|
return NT_SVER_W2K_RC3;
|
|
}
|
|
else
|
|
{
|
|
return NT_SVER_NT4;
|
|
}
|
|
}
|
|
|
|
// Taken from http://kbinternal/kb/articles/q158/2/38.htm
|
|
//
|
|
// Release Version File dates
|
|
// ---------------------------------------------------------------------
|
|
// Windows 95 retail, OEM 4.00.950 7/11/95
|
|
// Windows 95 retail SP1 4.00.950A 7/11/95
|
|
// OEM Service Release 1 4.00.950A 7/11/95
|
|
// OEM Service Release 2 4.00.1111* (4.00.950B) 8/24/96
|
|
// OEM Service Release 2.1 4.03.1212-1214* (4.00.950B) 8/24/96-8/27/97
|
|
// OEM Service Release 2.5 4.03.1214* (4.00.950C) 8/24/96-11/18/97
|
|
// Windows 98 retail, OEM 4.10.1998 5/11/98
|
|
// Windows 98 Second Edition 4.10.2222A 4/23/99
|
|
|
|
ULONG
|
|
Win9xBuildToSystemVersion(ULONG Build)
|
|
{
|
|
if (Build > 2222)
|
|
{
|
|
return W9X_SVER_WME;
|
|
}
|
|
else if (Build > 1998)
|
|
{
|
|
return W9X_SVER_W98SE;
|
|
}
|
|
else if (Build > 950)
|
|
{
|
|
return W9X_SVER_W98;
|
|
}
|
|
else
|
|
{
|
|
return W9X_SVER_W95;
|
|
}
|
|
}
|
|
|
|
void
|
|
SetTargetSystemVersionAndBuild(ULONG Build, ULONG PlatformId)
|
|
{
|
|
if (PlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
g_ActualSystemVersion = NtBuildToSystemVersion(Build);
|
|
g_SystemVersion = g_ActualSystemVersion;
|
|
}
|
|
else
|
|
{
|
|
// Win9x puts the major and minor versions in the high word
|
|
// of the build number so mask them off.
|
|
Build &= 0xffff;
|
|
g_ActualSystemVersion = Win9xBuildToSystemVersion(Build);
|
|
// Win98SE was the first Win9x version to support
|
|
// the extended registers thread context flag.
|
|
if (g_ActualSystemVersion >= W9X_SVER_W98SE)
|
|
{
|
|
g_SystemVersion = NT_SVER_W2K;
|
|
}
|
|
else
|
|
{
|
|
g_SystemVersion = NT_SVER_NT4;
|
|
}
|
|
}
|
|
|
|
g_TargetBuildNumber = Build;
|
|
}
|
|
|
|
PCSTR
|
|
SystemVersionName(ULONG Sver)
|
|
{
|
|
if (Sver > NT_SVER_START && Sver < NT_SVER_END)
|
|
{
|
|
return g_NtSverNames[Sver - NT_SVER_START - 1];
|
|
}
|
|
else if (Sver > W9X_SVER_START && Sver < W9X_SVER_END)
|
|
{
|
|
return g_W9xSverNames[Sver - W9X_SVER_START - 1];
|
|
}
|
|
else if (Sver > XBOX_SVER_START && Sver < XBOX_SVER_END)
|
|
{
|
|
return g_XBoxSverNames[Sver - XBOX_SVER_START - 1];
|
|
}
|
|
else if (Sver > BIG_SVER_START && Sver < BIG_SVER_END)
|
|
{
|
|
return g_BigSverNames[Sver - BIG_SVER_START - 1];
|
|
}
|
|
else if (Sver > EXDI_SVER_START && Sver < EXDI_SVER_END)
|
|
{
|
|
return g_ExdiSverNames[Sver - EXDI_SVER_START - 1];
|
|
}
|
|
else if (Sver > NTBD_SVER_START && Sver < NTBD_SVER_END)
|
|
{
|
|
return g_NtBdSverNames[Sver - NTBD_SVER_START - 1];
|
|
}
|
|
else if (Sver > EFI_SVER_START && Sver < EFI_SVER_END)
|
|
{
|
|
return g_EfiSverNames[Sver - EFI_SVER_START - 1];
|
|
}
|
|
|
|
return "Unknown System";
|
|
}
|
|
|
|
BOOL
|
|
GetUserModuleListAddress(
|
|
MachineInfo* Machine,
|
|
ULONG64 Peb,
|
|
BOOL Quiet,
|
|
PULONG64 OrderModuleListStart,
|
|
PULONG64 FirstEntry
|
|
)
|
|
{
|
|
ULONG64 PebLdrOffset;
|
|
ULONG64 ModuleListOffset;
|
|
ULONG64 PebAddr;
|
|
ULONG64 PebLdr = 0;
|
|
|
|
*OrderModuleListStart = 0;
|
|
*FirstEntry = 0;
|
|
|
|
//
|
|
// Triage dumps have no user mode information.
|
|
// User-mode minidumps don't have a loader list.
|
|
//
|
|
|
|
if (IS_KERNEL_TRIAGE_DUMP() || IS_USER_MINI_DUMP())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Machine->m_Ptr64)
|
|
{
|
|
PebLdrOffset = PEBLDR_FROM_PEB64;
|
|
ModuleListOffset = MODULE_LIST_FROM_PEBLDR64;
|
|
}
|
|
else
|
|
{
|
|
PebLdrOffset = PEBLDR_FROM_PEB32;
|
|
ModuleListOffset = MODULE_LIST_FROM_PEBLDR32;
|
|
}
|
|
|
|
if (!Peb)
|
|
{
|
|
if (GetImplicitProcessDataPeb(&Peb) != S_OK)
|
|
{
|
|
if (!Quiet)
|
|
{
|
|
ErrOut("Unable to read KPROCESS\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (Peb == 0) )
|
|
{
|
|
// This is a common error as the system process has no
|
|
// user address space.
|
|
if (!Quiet)
|
|
{
|
|
ErrOut("Unable to retrieve the PEB address. "
|
|
"This is usually caused\n");
|
|
ErrOut("by being in the wrong process context or by paging\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read address the PEB Ldr data from the PEB structure
|
|
//
|
|
|
|
Peb += PebLdrOffset;
|
|
|
|
if ( (g_Target->ReadPointer(Machine, Peb, &PebLdr) != S_OK) ||
|
|
(PebLdr == 0) )
|
|
{
|
|
if (!Quiet)
|
|
{
|
|
ErrOut("PPEB_LDR_DATA is NULL (Peb = %s)\n",
|
|
FormatMachineAddr64(Machine, Peb));
|
|
ErrOut("This is usually caused by being in the wrong process\n");
|
|
ErrOut("context or by paging\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Read address of the user mode module list from the PEB Ldr Data.
|
|
//
|
|
|
|
PebLdr += ModuleListOffset;
|
|
*OrderModuleListStart = PebLdr;
|
|
|
|
if ( (g_Target->ReadPointer(Machine, PebLdr, FirstEntry) != S_OK) ||
|
|
(*FirstEntry == 0) )
|
|
{
|
|
if (!Quiet)
|
|
{
|
|
ErrOut("UserMode Module List Address is NULL (Addr= %s)\n",
|
|
FormatMachineAddr64(Machine, PebLdr));
|
|
ErrOut("This is usually caused by being in the wrong process\n");
|
|
ErrOut("context or by paging\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
GetModNameFromLoaderList(
|
|
MachineInfo* Machine,
|
|
ULONG64 Peb,
|
|
ULONG64 ModuleBase,
|
|
PSTR NameBuffer,
|
|
ULONG BufferSize,
|
|
BOOL FullPath
|
|
)
|
|
{
|
|
ULONG64 ModList;
|
|
ULONG64 List;
|
|
HRESULT Status;
|
|
KLDR_DATA_TABLE_ENTRY64 Entry;
|
|
WCHAR UnicodeBuffer[MAX_IMAGE_PATH];
|
|
ULONG Read;
|
|
|
|
if (!GetUserModuleListAddress(Machine, Peb, TRUE, &ModList, &List))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while (List != ModList)
|
|
{
|
|
Status = g_Target->ReadLoaderEntry(Machine, List, &Entry);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Unable to read LDR_DATA_TABLE_ENTRY at %s - %s\n",
|
|
FormatMachineAddr64(Machine, List),
|
|
FormatStatusCode(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
List = Entry.InLoadOrderLinks.Flink;
|
|
|
|
if (Entry.DllBase == ModuleBase)
|
|
{
|
|
UNICODE_STRING64 Name;
|
|
|
|
//
|
|
// We found a matching entry. Try to get the name.
|
|
//
|
|
if (FullPath)
|
|
{
|
|
Name = Entry.FullDllName;
|
|
}
|
|
else
|
|
{
|
|
Name = Entry.BaseDllName;
|
|
}
|
|
if (Name.Length == 0 ||
|
|
Name.Buffer == 0 ||
|
|
Name.Length >= sizeof(UnicodeBuffer) - sizeof(WCHAR))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Status = g_Target->ReadVirtual(Name.Buffer, UnicodeBuffer,
|
|
Name.Length, &Read);
|
|
if (Status != S_OK || Read < Name.Length)
|
|
{
|
|
ErrOut("Unable to read name string at %s - %s\n",
|
|
FormatMachineAddr64(Machine, Name.Buffer),
|
|
FormatStatusCode(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
UnicodeBuffer[Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
if (!WideCharToMultiByte(CP_ACP, 0, UnicodeBuffer,
|
|
Name.Length / sizeof(WCHAR) + 1,
|
|
NameBuffer, BufferSize,
|
|
NULL, NULL))
|
|
{
|
|
ErrOut("Unable to convert Unicode string %ls to ANSI\n",
|
|
UnicodeBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
SetTargetNtCsdVersion(ULONG CsdVersion)
|
|
{
|
|
g_TargetServicePackNumber = CsdVersion;
|
|
|
|
if (CsdVersion == 0)
|
|
{
|
|
g_TargetServicePackString[0] = 0;
|
|
return;
|
|
}
|
|
|
|
PSTR Str = g_TargetServicePackString;
|
|
*Str = 0;
|
|
|
|
if (CsdVersion & 0xFFFF)
|
|
{
|
|
sprintf(Str, "Service Pack %u", (CsdVersion & 0xFF00) >> 8);
|
|
Str += strlen(Str);
|
|
if (CsdVersion & 0xFF)
|
|
{
|
|
*Str++ = 'A' + (char)(CsdVersion & 0xFF) - 1;
|
|
*Str = 0;
|
|
}
|
|
}
|
|
|
|
if (CsdVersion & 0xFFFF0000)
|
|
{
|
|
if (CsdVersion & 0xFFFF)
|
|
{
|
|
strcpy(Str, ", ");
|
|
Str += strlen(Str);
|
|
}
|
|
sprintf(Str, "RC %u", (CsdVersion >> 24) & 0xFF);
|
|
Str += strlen(Str);
|
|
if (CsdVersion & 0x00FF0000)
|
|
{
|
|
sprintf(Str, ".%u", (CsdVersion >> 16) & 0xFF);
|
|
Str += strlen(Str);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Module list abstraction.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
ModuleInfo::ReadImageHeaderInfo(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
HRESULT Status;
|
|
UCHAR SectorBuffer[ 512 ];
|
|
PIMAGE_NT_HEADERS64 NtHeaders;
|
|
ULONG Result;
|
|
|
|
if (Entry->ImageInfoValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// For live debugging of both user mode and kernel mode, we have
|
|
// to go load the checksum timestamp directly out of the image header
|
|
// because someone decided to overwrite these fields in the OS
|
|
// module list - Argh !
|
|
//
|
|
|
|
Entry->CheckSum = UNKNOWN_CHECKSUM;
|
|
Entry->TimeDateStamp = UNKNOWN_TIMESTAMP;
|
|
|
|
Status = g_Target->ReadVirtual(Entry->Base, SectorBuffer,
|
|
sizeof(SectorBuffer), &Result);
|
|
if (Status == S_OK && Result >= sizeof(SectorBuffer))
|
|
{
|
|
NtHeaders = (PIMAGE_NT_HEADERS64)ImageNtHeader(SectorBuffer);
|
|
if (NtHeaders != NULL)
|
|
{
|
|
switch (NtHeaders->OptionalHeader.Magic)
|
|
{
|
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
Entry->CheckSum = ((PIMAGE_NT_HEADERS32)NtHeaders)->
|
|
OptionalHeader.CheckSum;
|
|
Entry->Size = ((PIMAGE_NT_HEADERS32)NtHeaders)->
|
|
OptionalHeader.SizeOfImage;
|
|
Entry->SizeOfCode = ((PIMAGE_NT_HEADERS32)NtHeaders)->
|
|
OptionalHeader.SizeOfCode;
|
|
Entry->SizeOfData = ((PIMAGE_NT_HEADERS32)NtHeaders)->
|
|
OptionalHeader.SizeOfInitializedData;
|
|
break;
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
Entry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
|
|
Entry->Size = NtHeaders->OptionalHeader.SizeOfImage;
|
|
Entry->SizeOfCode = NtHeaders->OptionalHeader.SizeOfCode;
|
|
Entry->SizeOfData =
|
|
NtHeaders->OptionalHeader.SizeOfInitializedData;
|
|
break;
|
|
}
|
|
Entry->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
|
|
|
Entry->ImageInfoValid = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
NtModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Result = 0;
|
|
ULONG Length;
|
|
ULONG64 Buffer;
|
|
|
|
if (m_Cur == m_Head)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
KLDR_DATA_TABLE_ENTRY64 LdrEntry;
|
|
|
|
Status = g_Target->ReadLoaderEntry(m_Machine, m_Cur, &LdrEntry);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Unable to read KLDR_DATA_TABLE_ENTRY at %s - %s\n",
|
|
FormatAddr64(m_Cur), FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
m_Cur = LdrEntry.InLoadOrderLinks.Flink;
|
|
|
|
//
|
|
// Get the image path if possible, otherwise
|
|
// just use the image base name.
|
|
//
|
|
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
|
|
Length = (ULONG)(ULONG_PTR) LdrEntry.FullDllName.Length;
|
|
Buffer = LdrEntry.FullDllName.Buffer;
|
|
|
|
// In the NT4 dumps that we have the long name may
|
|
// point to valid memory but the memory content is
|
|
// rarely the correct name, so just don't bother
|
|
// trying to read the long name on NT4.
|
|
if (g_SystemVersion >= NT_SVER_W2K &&
|
|
Length != 0 && Buffer != 0 &&
|
|
Length < (MAX_IMAGE_PATH * sizeof(WCHAR)))
|
|
{
|
|
Status = g_Target->ReadVirtual(Buffer,
|
|
Entry->Buffer,
|
|
Length,
|
|
&Result);
|
|
|
|
if (Status != S_OK || (Result < Length))
|
|
{
|
|
// Make this a verbose message since it's possible the
|
|
// name is simply paged out.
|
|
VerbOut("Unable to read NT module Full Name string at %s - %s\n",
|
|
FormatAddr64(Buffer), FormatStatusCode(Status));
|
|
Result = 0;
|
|
}
|
|
}
|
|
|
|
if (!Result)
|
|
{
|
|
Length = (ULONG)(ULONG_PTR) LdrEntry.BaseDllName.Length;
|
|
Buffer = LdrEntry.BaseDllName.Buffer;
|
|
|
|
if (Length != 0 && Buffer != 0 &&
|
|
Length < (MAX_IMAGE_PATH * sizeof(WCHAR)))
|
|
{
|
|
Status = g_Target->ReadVirtual(Buffer,
|
|
Entry->Buffer,
|
|
Length,
|
|
&Result);
|
|
|
|
if (Status != S_OK || (Result < Length))
|
|
{
|
|
WarnOut("Unable to read NT module Base Name "
|
|
"string at %s - %s\n",
|
|
FormatAddr64(Buffer), FormatStatusCode(Status));
|
|
Result = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Result)
|
|
{
|
|
// We did not get any name - just return.
|
|
return S_OK;
|
|
}
|
|
|
|
*(PWCHAR)(Entry->Buffer + Length) = UNICODE_NULL;
|
|
|
|
Entry->NamePtr = &(Entry->Buffer[0]);
|
|
Entry->NameLength = Length;
|
|
Entry->UnicodeNamePtr = 1;
|
|
Entry->Base = LdrEntry.DllBase;
|
|
Entry->Size = LdrEntry.SizeOfImage;
|
|
Entry->CheckSum = LdrEntry.CheckSum;
|
|
Entry->TimeDateStamp = LdrEntry.TimeDateStamp;
|
|
|
|
//
|
|
// Update the image informaion, such as timestamp and real image size,
|
|
// Directly from the image header
|
|
//
|
|
|
|
ReadImageHeaderInfo(Entry);
|
|
|
|
//
|
|
// For newer NT builds, we also have an alternate entry in the
|
|
// LdrDataTable to store image information in case the actual header
|
|
// is paged out. We do this for session space images only right now.
|
|
//
|
|
|
|
if (LdrEntry.Flags & LDRP_NON_PAGED_DEBUG_INFO)
|
|
{
|
|
NON_PAGED_DEBUG_INFO di;
|
|
|
|
Status = g_Target->ReadVirtual(LdrEntry.NonPagedDebugInfo,
|
|
&di,
|
|
sizeof(di), // Only read the base struct
|
|
&Result);
|
|
|
|
if (Status != S_OK || (Result < sizeof(di)))
|
|
{
|
|
WarnOut("Unable to read NonPagedDebugInfo at %s - %s\n",
|
|
FormatAddr64(LdrEntry.NonPagedDebugInfo),
|
|
FormatStatusCode(Status));
|
|
return S_OK;
|
|
}
|
|
|
|
Entry->TimeDateStamp = di.TimeDateStamp;
|
|
Entry->CheckSum = di.CheckSum;
|
|
Entry->Size = di.SizeOfImage;
|
|
|
|
Entry->ImageInfoPartial = 1;
|
|
Entry->ImageInfoValid = 1;
|
|
|
|
if (di.Flags == 1)
|
|
{
|
|
Entry->DebugHeader = malloc(di.Size - sizeof(di));
|
|
|
|
if (Entry->DebugHeader)
|
|
{
|
|
Status = g_Target->ReadVirtual(LdrEntry.NonPagedDebugInfo +
|
|
sizeof(di),
|
|
Entry->DebugHeader,
|
|
di.Size - sizeof(di),
|
|
&Result);
|
|
|
|
if (Status != S_OK || (Result < di.Size - sizeof(di)))
|
|
{
|
|
WarnOut("Unable to read NonPagedDebugInfo data at %s - %s\n",
|
|
FormatAddr64(LdrEntry.NonPagedDebugInfo + sizeof(di)),
|
|
FormatStatusCode(Status));
|
|
return S_OK;
|
|
}
|
|
|
|
Entry->ImageDebugHeader = 1;
|
|
Entry->SizeOfDebugHeader = di.Size - sizeof(di);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
NtKernelModuleInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
LIST_ENTRY64 List64;
|
|
|
|
m_Machine = g_TargetMachine;
|
|
|
|
if ((m_Head = KdDebuggerData.PsLoadedModuleList) == 0)
|
|
{
|
|
//
|
|
// This field is ALWAYS set in NT 5 targets.
|
|
//
|
|
// We will only fail here if someone changed the debugger code
|
|
// and did not "make up" this structure properly for NT 4 or
|
|
// dump targets..
|
|
//
|
|
|
|
ErrOut("Module List address is NULL - "
|
|
"debugger not initialized properly.\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
Status = g_Target->ReadListEntry(m_Machine, m_Head, &List64);
|
|
|
|
//
|
|
// In live debug sessions, the debugger connects before Mm creates
|
|
// the actual module list.
|
|
// If we are this early on, try to load symbols from the loader
|
|
// block module list (NT 6). Otherwise, just fail and we will
|
|
// get called back later by the ntoskrnl.exe module load call.
|
|
//
|
|
// For dumps, (not user mode or triage) the data should always
|
|
// be initialized.
|
|
//
|
|
|
|
if ( (Status == S_OK) && (List64.Flink) )
|
|
{
|
|
m_Cur = List64.Flink;
|
|
}
|
|
else
|
|
{
|
|
dprintf("PsLoadedModuleList not initialized yet. "
|
|
"Delay kernel load.\n");
|
|
return S_FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
NtKernelModuleInfo g_NtKernelModuleIterator;
|
|
|
|
HRESULT
|
|
NtUserModuleInfo::Initialize(void)
|
|
{
|
|
return GetUserModuleListAddress(m_Machine, m_Peb, FALSE, &m_Head, &m_Cur) ?
|
|
S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
NtTargetUserModuleInfo::Initialize(void)
|
|
{
|
|
m_Machine = g_TargetMachine;
|
|
m_Peb = 0;
|
|
return NtUserModuleInfo::Initialize();
|
|
}
|
|
|
|
NtTargetUserModuleInfo g_NtTargetUserModuleIterator;
|
|
|
|
HRESULT
|
|
NtWow64UserModuleInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (g_TargetMachine->m_NumExecTypes < 2)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
m_Machine = MachineTypeInfo(g_TargetMachine->m_ExecTypes[1]);
|
|
|
|
if ((Status = GetPeb32(&m_Peb)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
return NtUserModuleInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
NtWow64UserModuleInfo::GetPeb32(PULONG64 Peb32)
|
|
{
|
|
ULONG64 Teb;
|
|
ULONG64 Teb32;
|
|
ULONG Result;
|
|
HRESULT Status;
|
|
|
|
if ((Status = GetImplicitThreadDataTeb(&Teb)) == S_OK)
|
|
{
|
|
if ((Status = g_Target->ReadPointer(g_TargetMachine,
|
|
Teb, &Teb32)) == S_OK)
|
|
{
|
|
if (!Teb32)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
ULONG RawPeb32;
|
|
|
|
Status = g_Target->
|
|
ReadAllVirtual(Teb32 + PEB_FROM_TEB32, &RawPeb32,
|
|
sizeof(RawPeb32));
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Cannot read PEB32 from WOW64 TEB32 %s - %s\n",
|
|
FormatAddr64(Teb32), FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
*Peb32 = EXTEND64(RawPeb32);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NtWow64UserModuleInfo g_NtWow64UserModuleIterator;
|
|
|
|
HRESULT
|
|
DebuggerModuleInfo::Initialize(void)
|
|
{
|
|
m_Image = g_CurrentProcess->ImageHead;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DebuggerModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
if (m_Image == NULL)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
Entry->NamePtr = m_Image->ImagePath;
|
|
Entry->UnicodeNamePtr = 0;
|
|
Entry->ModuleName = m_Image->ModuleName;
|
|
Entry->File = m_Image->File;
|
|
Entry->Base = m_Image->BaseOfImage;
|
|
Entry->Size = m_Image->SizeOfImage;
|
|
Entry->CheckSum = m_Image->CheckSum;
|
|
Entry->TimeDateStamp = m_Image->TimeDateStamp;
|
|
|
|
Entry->ImageInfoValid = 1;
|
|
|
|
m_Image = m_Image->Next;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DebuggerModuleInfo g_DebuggerModuleIterator;
|
|
|
|
HRESULT
|
|
NtKernelUnloadedModuleInfo::Initialize(void)
|
|
{
|
|
if (KdDebuggerData.MmUnloadedDrivers == 0 ||
|
|
KdDebuggerData.MmLastUnloadedDriver == 0)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// If this is the initial module load we need to be
|
|
// careful because much of the system isn't initialized
|
|
// yet. Some versions of the OS can crash when scanning
|
|
// the unloaded module list, plus at this point we can
|
|
// safely assume there are no unloaded modules, so just
|
|
// don't enumerate anything.
|
|
if (g_EngStatus & ENG_STATUS_AT_INITIAL_MODULE_LOAD)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT Status;
|
|
ULONG Read;
|
|
|
|
if ((Status = g_Target->ReadPointer(g_TargetMachine,
|
|
KdDebuggerData.MmUnloadedDrivers,
|
|
&m_Base)) != S_OK ||
|
|
(Status = g_Target->ReadVirtual(KdDebuggerData.MmLastUnloadedDriver,
|
|
&m_Index, sizeof(m_Index),
|
|
&Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Read != sizeof(m_Index))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
m_Count = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
NtKernelUnloadedModuleInfo::GetEntry(PSTR Name,
|
|
PDEBUG_MODULE_PARAMETERS Params)
|
|
{
|
|
if (m_Count == MI_UNLOADED_DRIVERS)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (m_Index == 0)
|
|
{
|
|
m_Index = MI_UNLOADED_DRIVERS - 1;
|
|
}
|
|
else
|
|
{
|
|
m_Index--;
|
|
}
|
|
|
|
ULONG64 Offset;
|
|
ULONG Read;
|
|
HRESULT Status;
|
|
ULONG64 WideName;
|
|
ULONG NameLen;
|
|
|
|
ZeroMemory(Params, sizeof(*Params));
|
|
Params->Flags |= DEBUG_MODULE_UNLOADED;
|
|
|
|
if (g_TargetMachine->m_Ptr64)
|
|
{
|
|
UNLOADED_DRIVERS64 Entry;
|
|
|
|
Offset = m_Base + m_Index * sizeof(Entry);
|
|
if ((Status = g_Target->ReadVirtual(Offset, &Entry, sizeof(Entry),
|
|
&Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Read != sizeof(Entry))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
if (Entry.Name.Buffer == 0)
|
|
{
|
|
m_Count = MI_UNLOADED_DRIVERS;
|
|
return S_FALSE;
|
|
}
|
|
|
|
Params->Base = Entry.StartAddress;
|
|
Params->Size = (ULONG)(Entry.EndAddress - Entry.StartAddress);
|
|
Params->TimeDateStamp =
|
|
FileTimeToTimeDateStamp(Entry.CurrentTime.QuadPart);
|
|
WideName = Entry.Name.Buffer;
|
|
NameLen = Entry.Name.Length;
|
|
}
|
|
else
|
|
{
|
|
UNLOADED_DRIVERS32 Entry;
|
|
|
|
Offset = m_Base + m_Index * sizeof(Entry);
|
|
if ((Status = g_Target->ReadVirtual(Offset, &Entry, sizeof(Entry),
|
|
&Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Read != sizeof(Entry))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
if (Entry.Name.Buffer == 0)
|
|
{
|
|
m_Count = MI_UNLOADED_DRIVERS;
|
|
return S_FALSE;
|
|
}
|
|
|
|
Params->Base = EXTEND64(Entry.StartAddress);
|
|
Params->Size = Entry.EndAddress - Entry.StartAddress;
|
|
Params->TimeDateStamp =
|
|
FileTimeToTimeDateStamp(Entry.CurrentTime.QuadPart);
|
|
WideName = EXTEND64(Entry.Name.Buffer);
|
|
NameLen = Entry.Name.Length;
|
|
}
|
|
|
|
if (Name != NULL)
|
|
{
|
|
//
|
|
// This size restriction is in force for minidumps only.
|
|
// For kernel dumps, just truncate the name for now ...
|
|
//
|
|
|
|
if (NameLen > MAX_UNLOADED_NAME_LENGTH)
|
|
{
|
|
NameLen = MAX_UNLOADED_NAME_LENGTH;
|
|
}
|
|
|
|
WCHAR WideBuf[MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR) + 1];
|
|
|
|
if ((Status = g_Target->ReadVirtual(WideName, WideBuf, NameLen,
|
|
&Read)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
WideBuf[NameLen / sizeof(WCHAR)] = 0;
|
|
if (WideCharToMultiByte(CP_ACP, 0,
|
|
WideBuf, NameLen / sizeof(WCHAR) + 1,
|
|
Name,
|
|
MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR) + 1,
|
|
NULL, NULL) == 0)
|
|
{
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
Name[MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR)] = 0;
|
|
}
|
|
|
|
m_Count++;
|
|
return S_OK;
|
|
}
|
|
|
|
NtKernelUnloadedModuleInfo g_NtKernelUnloadedModuleIterator;
|
|
|
|
HRESULT
|
|
W9xModuleInfo::Initialize(void)
|
|
{
|
|
m_Snap = g_Kernel32Calls.
|
|
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
|
|
g_CurrentProcess->SystemId);
|
|
if (m_Snap == INVALID_HANDLE_VALUE)
|
|
{
|
|
m_Snap = NULL;
|
|
ErrOut("Can't create snapshot\n");
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
m_First = TRUE;
|
|
m_LastId = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
W9xModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
if (m_Snap == NULL)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
BOOL Succ;
|
|
MODULEENTRY32 Mod;
|
|
|
|
Mod.dwSize = sizeof(Mod);
|
|
if (m_First)
|
|
{
|
|
Succ = g_Kernel32Calls.Module32First(m_Snap, &Mod);
|
|
m_First = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Win9x seems to require that this module ID be saved
|
|
// between calls so stick it back in to keep Win9x happy.
|
|
Mod.th32ModuleID = m_LastId;
|
|
Succ = g_Kernel32Calls.Module32Next(m_Snap, &Mod);
|
|
}
|
|
if (!Succ)
|
|
{
|
|
CloseHandle(m_Snap);
|
|
m_Snap = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
m_LastId = Mod.th32ModuleID;
|
|
strncat(Entry->Buffer, Mod.szModule, sizeof(Entry->Buffer) - 1);
|
|
Entry->NamePtr = Entry->Buffer;
|
|
Entry->UnicodeNamePtr = 0;
|
|
Entry->Base = EXTEND64((ULONG_PTR)Mod.modBaseAddr);
|
|
Entry->Size = Mod.modBaseSize;
|
|
|
|
//
|
|
// Update the image informaion, such as timestamp and real image size,
|
|
// Directly from the image header
|
|
//
|
|
|
|
ReadImageHeaderInfo(Entry);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
W9xModuleInfo g_W9xModuleIterator;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Target configuration information.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONG g_SystemVersion;
|
|
ULONG g_ActualSystemVersion;
|
|
|
|
ULONG g_TargetCheckedBuild;
|
|
ULONG g_TargetBuildNumber;
|
|
BOOL g_MachineInitialized;
|
|
ULONG g_TargetMachineType = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
ULONG g_TargetExecMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
ULONG g_TargetPlatformId;
|
|
char g_TargetServicePackString[_MAX_PATH];
|
|
ULONG g_TargetServicePackNumber;
|
|
char g_TargetBuildLabName[272];
|
|
|
|
ULONG g_TargetNumberProcessors;
|
|
ULONG g_TargetClass = DEBUG_CLASS_UNINITIALIZED;
|
|
ULONG g_TargetClassQualifier;
|
|
|
|
ConnLiveKernelTargetInfo g_ConnLiveKernelTarget;
|
|
LocalLiveKernelTargetInfo g_LocalLiveKernelTarget;
|
|
ExdiLiveKernelTargetInfo g_ExdiLiveKernelTarget;
|
|
LocalUserTargetInfo g_LocalUserTarget;
|
|
RemoteUserTargetInfo g_RemoteUserTarget;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// TargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Used in convenience macros.
|
|
ULONG g_TmpCount;
|
|
|
|
TargetInfo* g_Target = &g_UnexpectedTarget;
|
|
|
|
void
|
|
TargetInfo::Uninitialize(void)
|
|
{
|
|
// Placeholder.
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::ThreadInitialize(void)
|
|
{
|
|
// Placeholder.
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
TargetInfo::ThreadUninitialize(void)
|
|
{
|
|
// Placeholder.
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::OutputTime(void)
|
|
{
|
|
ULONG64 TimeDateN = GetCurrentTimeDateN();
|
|
if (TimeDateN)
|
|
{
|
|
dprintf("Debug session time: %s\n",
|
|
TimeToStr(FileTimeToTimeDateStamp(TimeDateN)));
|
|
}
|
|
|
|
ULONG64 UpTimeN = GetCurrentSystemUpTimeN();
|
|
if (UpTimeN)
|
|
{
|
|
ULONG seconds = FileTimeToTime(UpTimeN);
|
|
ULONG minutes = seconds / 60;
|
|
ULONG hours = minutes / 60;
|
|
ULONG days = hours / 24;
|
|
|
|
dprintf("System Uptime: %d days %d:%02d:%02d \n",
|
|
days, hours%24, minutes%60, seconds%60);
|
|
}
|
|
else
|
|
{
|
|
dprintf("System Uptime: not available\n");
|
|
}
|
|
|
|
if (IS_LIVE_USER_TARGET())
|
|
{
|
|
ULONG64 UpTimeProcessN = GetProcessUpTimeN(g_CurrentProcess->FullHandle);
|
|
if (UpTimeN)
|
|
{
|
|
ULONG seconds = FileTimeToTime(UpTimeProcessN);
|
|
ULONG minutes = seconds / 60;
|
|
ULONG hours = minutes / 60;
|
|
ULONG days = hours / 24;
|
|
|
|
dprintf("Process Uptime: %d days %d:%02d:%02d \n",
|
|
days, hours%24, minutes%60, seconds%60);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Process Uptime: not available\n");
|
|
}
|
|
}
|
|
|
|
return (TimeDateN && UpTimeN) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::OutputVersion(void)
|
|
{
|
|
BOOL MpMachine;
|
|
|
|
if (!IS_TARGET_SET())
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
else if (IS_USER_TARGET())
|
|
{
|
|
dprintf("%s ", SystemVersionName(g_ActualSystemVersion));
|
|
}
|
|
else
|
|
{
|
|
dprintf("%s Kernel ", SystemVersionName(g_ActualSystemVersion));
|
|
}
|
|
|
|
dprintf("Version %u", g_TargetBuildNumber);
|
|
|
|
// Win9x seems to set the CSD string to a space which isn't
|
|
// very interesting so ignore it.
|
|
if (g_TargetServicePackString[0] &&
|
|
strcmp(g_TargetServicePackString, " ") != 0)
|
|
{
|
|
dprintf(" (%s)", g_TargetServicePackString);
|
|
}
|
|
|
|
MpMachine = IS_LIVE_KERNEL_TARGET() ?
|
|
((g_KdVersion.Flags & DBGKD_VERS_FLAG_MP) ? 1 : 0) :
|
|
(g_TargetNumberProcessors > 1);
|
|
|
|
dprintf(" %s ", MpMachine ? "MP" : "UP");
|
|
|
|
if (MpMachine)
|
|
{
|
|
dprintf("(%d procs) ", g_TargetNumberProcessors);
|
|
}
|
|
|
|
dprintf("%s %s\n",
|
|
g_TargetCheckedBuild == 0xC ? "Checked" : "Free",
|
|
g_TargetMachine != NULL ? g_TargetMachine->m_FullName : "");
|
|
|
|
if (g_Wow64exts != NULL)
|
|
{
|
|
dprintf("WOW64 extensions loaded\n");
|
|
}
|
|
|
|
if (g_TargetBuildLabName[0])
|
|
{
|
|
dprintf("%s\n", g_TargetBuildLabName);
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET())
|
|
{
|
|
dprintf("Kernel base = 0x%s PsLoadedModuleList = 0x%s\n",
|
|
FormatAddr64(KdDebuggerData.KernBase),
|
|
FormatAddr64(KdDebuggerData.PsLoadedModuleList));
|
|
}
|
|
|
|
OutputTime();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
ModuleInfo*
|
|
TargetInfo::GetModuleInfo(BOOL UserMode)
|
|
{
|
|
if (UserMode)
|
|
{
|
|
switch(g_TargetPlatformId)
|
|
{
|
|
case VER_PLATFORM_WIN32_NT:
|
|
return &g_NtTargetUserModuleIterator;
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
return &g_W9xModuleIterator;
|
|
default:
|
|
ErrOut("System module info not available\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_TargetPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ErrOut("System module info only available on Windows NT/2000\n");
|
|
return NULL;
|
|
}
|
|
|
|
DBG_ASSERT(IS_KERNEL_TARGET());
|
|
return &g_NtKernelModuleIterator;
|
|
}
|
|
}
|
|
|
|
UnloadedModuleInfo*
|
|
TargetInfo::GetUnloadedModuleInfo(void)
|
|
{
|
|
if (g_TargetPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ErrOut("System unloaded module info only available on "
|
|
"Windows NT/2000\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET())
|
|
{
|
|
return &g_NtKernelUnloadedModuleIterator;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetImageVersionInformation(PCSTR ImagePath,
|
|
ULONG64 ImageBase,
|
|
PCSTR Item,
|
|
PVOID Buffer, ULONG BufferSize,
|
|
PULONG VerInfoSize)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::Reload(
|
|
IN PCSTR args
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
CHAR AnsiString[MAX_IMAGE_PATH];
|
|
LPSTR SpecificModule = NULL;
|
|
ULONG64 Address = 0;
|
|
ULONG ImageSize = 0;
|
|
PCHAR p;
|
|
ULONG ModCount;
|
|
BOOL IgnoreSignature = FALSE;
|
|
ULONG ReloadSymOptions;
|
|
BOOL UnloadOnly = FALSE;
|
|
BOOL ReallyVerbose = FALSE;
|
|
BOOL LoadUserSymbols = TRUE;
|
|
BOOL UserModeList = IS_USER_TARGET();
|
|
BOOL ForceSymbolLoad = FALSE;
|
|
BOOL PrintImageListOnly = FALSE;
|
|
BOOL rc;
|
|
BOOL AddrLoad = FALSE;
|
|
BOOL UseDebuggerModuleList;
|
|
BOOL SkipPathChecks = FALSE;
|
|
ModuleInfo* ModIter;
|
|
BOOL Wow64ModLoaded = FALSE;
|
|
HRESULT RetStatus = S_OK;
|
|
MODULE_INFO_ENTRY ModEntry = {0};
|
|
|
|
|
|
if (!IS_TARGET_SET() ||
|
|
g_CurrentProcess == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// Historically, live user-mode reload has always
|
|
// just used the internal module list so preserve that.
|
|
UseDebuggerModuleList = IS_USER_TARGET() && !IS_DUMP_TARGET();
|
|
|
|
while (*args)
|
|
{
|
|
while (*args && *args <= ' ')
|
|
{
|
|
args++;
|
|
}
|
|
|
|
if (*args == '/' || *args == '-')
|
|
{
|
|
args++;
|
|
while (*args > ' ')
|
|
{
|
|
switch (*args++)
|
|
{
|
|
case 'a':
|
|
// for internal use only: loads whatever is found at the
|
|
// passed address
|
|
AddrLoad = TRUE;
|
|
break;
|
|
|
|
case 'd':
|
|
UseDebuggerModuleList = TRUE;
|
|
break;
|
|
|
|
case 'f':
|
|
ForceSymbolLoad = TRUE;
|
|
break;
|
|
|
|
case 'i':
|
|
IgnoreSignature = TRUE;
|
|
break;
|
|
|
|
case 'l':
|
|
PrintImageListOnly = TRUE;
|
|
break;
|
|
|
|
case 'n':
|
|
LoadUserSymbols = FALSE;
|
|
break;
|
|
|
|
case 'P':
|
|
// Internal-only switch.
|
|
SkipPathChecks = TRUE;
|
|
break;
|
|
|
|
case 's':
|
|
UseDebuggerModuleList = FALSE;
|
|
break;
|
|
|
|
case 'u':
|
|
if (!_strcmpi(args, "ser"))
|
|
{
|
|
UserModeList = TRUE;
|
|
args += 3;
|
|
}
|
|
else
|
|
{
|
|
UnloadOnly = TRUE;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
ReallyVerbose = TRUE;
|
|
break;
|
|
|
|
default:
|
|
dprintf("Reload: Unknown option '%c'\n", args[-1]);
|
|
dprintf("Usage: .reload [flags] [module] ...\n");
|
|
dprintf(" Flags: /d Use the debugger's module list\n");
|
|
dprintf(" Default for live user-mode "
|
|
"sessions\n");
|
|
dprintf(" /f Force immediate symbol load "
|
|
"instead of deferred\n");
|
|
dprintf(" /i Force symbol load by ignoring "
|
|
"mismatches in the pdb signature (requires /f as well)\n");
|
|
dprintf(" /l Just list the modules. "
|
|
"Kernel output same as !drivers\n");
|
|
dprintf(" /n Do not load from user-mode list "
|
|
"in kernel sessions\n");
|
|
dprintf(" /s Use the system's module list\n");
|
|
dprintf(" Default for dump and kernel sessions\n");
|
|
dprintf(" /u Unload symbols, no reload\n");
|
|
dprintf(" /user Load only user-mode symbols "
|
|
"in kernel sessions\n");
|
|
dprintf(" /v Verbose\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (*args && *args <= ' ')
|
|
{
|
|
args++;
|
|
}
|
|
|
|
if (!(*args == '/' || *args == '-') || AddrLoad)
|
|
{
|
|
AnsiString[ 0 ] = '\0';
|
|
Address = 0;
|
|
|
|
if (!sscanf(args, "%s", AnsiString) ||
|
|
!strlen(AnsiString))
|
|
{
|
|
AddrLoad = FALSE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
p = 0;
|
|
args += strlen( AnsiString );
|
|
|
|
//
|
|
// Support !reload pri_kern.exe=0x80400000,size
|
|
//
|
|
|
|
if (p = strchr(AnsiString, '='))
|
|
{
|
|
*p++ = 0;
|
|
|
|
if (sscanf(p, "%I64x", &Address) != 1)
|
|
{
|
|
ErrOut("Invalid address %s\n", p);
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!g_TargetMachine->m_Ptr64)
|
|
{
|
|
Address = EXTEND64(Address);
|
|
}
|
|
|
|
if (p = strchr(p, ','))
|
|
{
|
|
p++;
|
|
if (sscanf(p, "%x", &ImageSize) != 1)
|
|
{
|
|
ErrOut("Invalid ImageSize %s\n", p);
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UnloadOnly)
|
|
{
|
|
BOOL Deleted;
|
|
|
|
Deleted = DelImageByName(g_CurrentProcess, AnsiString,
|
|
INAME_MODULE);
|
|
if (!Deleted)
|
|
{
|
|
// The user might have given an image name
|
|
// instead of a module name so try that.
|
|
Deleted = DelImageByName(g_CurrentProcess,
|
|
PathTail(AnsiString),
|
|
INAME_IMAGE_PATH_TAIL);
|
|
}
|
|
if (Deleted)
|
|
{
|
|
dprintf("Unloaded %s\n", AnsiString);
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unable to find module '%s'\n", AnsiString);
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: This seems unnecessary as AddImage is going to
|
|
// check for the renamed image anyway.
|
|
//
|
|
|
|
SpecificModule = _strdup( AnsiString );
|
|
if (!SpecificModule)
|
|
{
|
|
return S_OK;
|
|
}
|
|
if (IS_KERNEL_TARGET() &&
|
|
_stricmp( AnsiString, KERNEL_MODULE_NAME ) == 0)
|
|
{
|
|
ForceSymbolLoad = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (AddrLoad)
|
|
{
|
|
free(SpecificModule);
|
|
SpecificModule = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ignore signature will only work if we load the symbols imediately.
|
|
if (ForceSymbolLoad == FALSE)
|
|
{
|
|
IgnoreSignature = FALSE;
|
|
}
|
|
|
|
if (!PrintImageListOnly && !SkipPathChecks)
|
|
{
|
|
if (g_SymbolSearchPath == NULL ||
|
|
*g_SymbolSearchPath == NULL)
|
|
{
|
|
dprintf("*********************************************************************\n");
|
|
dprintf("* Symbols can not be loaded because symbol path is not initialized. *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* The Symbol Path can be set by: *\n");
|
|
dprintf("* using the _NT_SYMBOL_PATH environment variable. *\n");
|
|
dprintf("* using the -y <symbol_path> argument when starting the debugger. *\n");
|
|
dprintf("* using .sympath and .sympath+ *\n");
|
|
dprintf("*********************************************************************\n");
|
|
RetStatus = E_INVALIDARG;
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
if (IS_DUMP_WITH_MAPPED_IMAGES() &&
|
|
(g_ExecutableImageSearchPath == NULL ||
|
|
*g_ExecutableImageSearchPath == NULL))
|
|
{
|
|
dprintf("*********************************************************************\n");
|
|
dprintf("* Analyzing Minidumps requires access to the actual executable *\n");
|
|
dprintf("* images for the crashed system *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* The Executable Image Path can be set by: *\n");
|
|
dprintf("* using the _NT_EXECUTABLE_IMAGE_PATH environment variable. *\n");
|
|
dprintf("* using the -i <image_path> argument when starting the debugger. *\n");
|
|
dprintf("* using .exepath and .exepath+ *\n");
|
|
dprintf("*********************************************************************\n");
|
|
RetStatus = E_INVALIDARG;
|
|
goto FreeSpecMod;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If both the module name and the address are specified, then just load
|
|
// the module right now, as this is only used when normal symbol loading
|
|
// would have failed in the first place.
|
|
//
|
|
|
|
if (SpecificModule && Address)
|
|
{
|
|
if (IgnoreSignature)
|
|
{
|
|
ReloadSymOptions = SymGetOptions();
|
|
SymSetOptions(ReloadSymOptions | SYMOPT_LOAD_ANYTHING);
|
|
}
|
|
|
|
ModEntry.NamePtr = SpecificModule,
|
|
ModEntry.Base = Address;
|
|
ModEntry.Size = ImageSize;
|
|
ModEntry.CheckSum = -1;
|
|
|
|
if (AddImage(&ModEntry, TRUE) == NULL)
|
|
{
|
|
ErrOut("Unable to add module at %s\n", FormatAddr64(Address));
|
|
RetStatus = E_FAIL;
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
SymSetOptions(ReloadSymOptions);
|
|
}
|
|
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
//
|
|
// Don't unload and reset things if we are looking for a specific module
|
|
// or if we're going to use the existing module list.
|
|
//
|
|
|
|
if (SpecificModule == NULL)
|
|
{
|
|
if (!PrintImageListOnly &&
|
|
(!UseDebuggerModuleList || UnloadOnly))
|
|
{
|
|
DelImages(g_CurrentProcess);
|
|
}
|
|
|
|
if (UnloadOnly)
|
|
{
|
|
dprintf("Unloaded all modules\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if (!IS_USER_TARGET() && !UseDebuggerModuleList)
|
|
{
|
|
if (!IS_DUMP_TARGET())
|
|
{
|
|
GetKdVersion();
|
|
}
|
|
|
|
VerifyKernelBase(TRUE);
|
|
}
|
|
|
|
//
|
|
// Print out the correct statement based on the type of output we
|
|
// want to provide
|
|
//
|
|
|
|
if (PrintImageListOnly)
|
|
{
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Debugger Module List Summary\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("System %s Summary\n",
|
|
IS_USER_TARGET() ? "Image" : "Driver and Image");
|
|
}
|
|
|
|
dprintf("Base ");
|
|
if (g_TargetMachine->m_Ptr64)
|
|
{
|
|
dprintf(" ");
|
|
}
|
|
#if 0
|
|
if (Flags & 1)
|
|
{
|
|
dprintf("Code Size Data Size Resident "
|
|
"Standby Driver Name\n");
|
|
}
|
|
else if (Flags & 2)
|
|
{
|
|
dprintf("Code Data Locked Resident Standby "
|
|
"Loader Entry Driver Name\n");
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Image Size "
|
|
"Image Name Creation Time\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Code Size Data Size "
|
|
"Image Name Creation Time\n");
|
|
}
|
|
}
|
|
else if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Reloading current modules\n");
|
|
}
|
|
else if (!IS_USER_TARGET())
|
|
{
|
|
dprintf("Loading %s Symbols\n",
|
|
UserModeList ? "User" : "Kernel");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the beginning of the module list.
|
|
//
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
ModIter = &g_DebuggerModuleIterator;
|
|
}
|
|
else
|
|
{
|
|
ModIter = GetModuleInfo(UserModeList);
|
|
}
|
|
|
|
if (ModIter == NULL)
|
|
{
|
|
// Error messages already printed.
|
|
RetStatus = E_UNEXPECTED;
|
|
goto FreeSpecMod;
|
|
}
|
|
if ((Status = ModIter->Initialize()) != S_OK)
|
|
{
|
|
// Error messages already printed.
|
|
// Fold unprepared-to-reload S_FALSE into S_OK.
|
|
RetStatus = SUCCEEDED(Status) ? S_OK : Status;
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
ReloadSymOptions = SymGetOptions();
|
|
SymSetOptions(ReloadSymOptions | SYMOPT_LOAD_ANYTHING);
|
|
}
|
|
|
|
// Suppress notifications until everything is done.
|
|
g_EngNotify++;
|
|
|
|
LoadLoop:
|
|
for (ModCount=0; ; ModCount++)
|
|
{
|
|
// Flush regularly so the user knows something is
|
|
// happening during the reload.
|
|
FlushCallbacks();
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ModCount > 1000)
|
|
{
|
|
ErrOut("ModuleList is corrupt - walked over 1000 module entries\n");
|
|
break;
|
|
}
|
|
|
|
if (ModEntry.DebugHeader)
|
|
{
|
|
free(ModEntry.DebugHeader);
|
|
}
|
|
|
|
ZeroMemory(&ModEntry, sizeof(ModEntry));
|
|
if ((Status = ModIter->GetEntry(&ModEntry)) != S_OK)
|
|
{
|
|
// Error message already printed in error case.
|
|
// Works for end-of-list case also.
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Warn if not all the information was gathered
|
|
//
|
|
|
|
if (!ModEntry.ImageInfoValid)
|
|
{
|
|
VerbOut("Unable to read image header for");
|
|
|
|
if (ModEntry.UnicodeNamePtr)
|
|
{
|
|
VerbOut(" %ls", ModEntry.NamePtr);
|
|
}
|
|
else
|
|
{
|
|
VerbOut(" %s", ModEntry.NamePtr);
|
|
}
|
|
|
|
VerbOut(" at %s\n",
|
|
FormatAddr64(ModEntry.Base));
|
|
}
|
|
|
|
|
|
if (ModEntry.NameLength > (MAX_IMAGE_PATH - 1) *
|
|
(ModEntry.UnicodeNamePtr ? sizeof(WCHAR) : sizeof(CHAR)))
|
|
{
|
|
ErrOut("Module list is corrupt.");
|
|
if (IS_KERNEL_TARGET())
|
|
{
|
|
ErrOut(" Check your kernel symbols.\n");
|
|
}
|
|
else
|
|
{
|
|
ErrOut(" Loader list may be invalid\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
// If this entry has no name just skip it.
|
|
if ((ModEntry.NamePtr == NULL) ||
|
|
(ModEntry.UnicodeNamePtr && ModEntry.NameLength == 0))
|
|
{
|
|
ErrOut(" Module List has empty entry in it - skipping\n");
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Are we looking for a module at a specific address ?
|
|
//
|
|
|
|
if (AddrLoad)
|
|
{
|
|
if (Address < ModEntry.Base ||
|
|
Address >= ModEntry.Base + ModEntry.Size)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ModEntry.UnicodeNamePtr)
|
|
{
|
|
if (!WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
(PWSTR)ModEntry.NamePtr,
|
|
ModEntry.NameLength / sizeof(WCHAR),
|
|
AnsiString,
|
|
sizeof(AnsiString),
|
|
NULL,
|
|
NULL))
|
|
{
|
|
WarnOut("Unable to convert Unicode string %ls to Ansi\n",
|
|
ModEntry.NamePtr);
|
|
continue;
|
|
}
|
|
|
|
ModEntry.NamePtr = AnsiString;
|
|
ModEntry.UnicodeNamePtr = 0;
|
|
AnsiString[ModEntry.NameLength / sizeof(WCHAR)] = 0;
|
|
}
|
|
|
|
//
|
|
// If we are loading a specific module:
|
|
//
|
|
// If the Module is NT, we take the first module in the list as it is
|
|
// guaranteed to be the kernel. Reset the Base address if it was
|
|
// not set.
|
|
//
|
|
// Otherwise, actually compare the strings and continue if they don't
|
|
// match
|
|
//
|
|
|
|
if (SpecificModule)
|
|
{
|
|
if (!UserModeList && _stricmp( SpecificModule, KERNEL_MODULE_NAME ) == 0)
|
|
{
|
|
if (!KdDebuggerData.KernBase)
|
|
{
|
|
KdDebuggerData.KernBase = ModEntry.Base;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!MatchPathTails(SpecificModule, ModEntry.NamePtr))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
PCSTR NamePtrTail = PathTail(ModEntry.NamePtr);
|
|
|
|
if (PrintImageListOnly)
|
|
{
|
|
PCHAR Time;
|
|
|
|
//
|
|
// The timestamp in minidumps was corrupt until NT5 RC3
|
|
// The timestamp could also be invalid because it was paged out
|
|
// in which case it's value is UNKNOWN_TIMESTAMP.
|
|
|
|
if (IS_KERNEL_TRIAGE_DUMP() &&
|
|
(g_ActualSystemVersion > NT_SVER_START &&
|
|
g_ActualSystemVersion <= NT_SVER_W2K_RC3))
|
|
{
|
|
Time = "";
|
|
}
|
|
|
|
Time = TimeToStr(ModEntry.TimeDateStamp);
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("%s %6lx (%4ld k) %12s %s\n",
|
|
FormatAddr64(ModEntry.Base), ModEntry.Size,
|
|
KBYTES(ModEntry.Size), NamePtrTail,
|
|
Time);
|
|
}
|
|
else
|
|
{
|
|
dprintf("%s %6lx (%4ld k) %5lx (%3ld k) %12s %s\n",
|
|
FormatAddr64(ModEntry.Base),
|
|
ModEntry.SizeOfCode, KBYTES(ModEntry.SizeOfCode),
|
|
ModEntry.SizeOfData, KBYTES(ModEntry.SizeOfData),
|
|
NamePtrTail, Time);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't bother reloading the kernel if we are not specifically
|
|
// asked since we know those symbols we reloaded by the
|
|
// VerifyKernel call.
|
|
//
|
|
|
|
if (!SpecificModule && !UserModeList &&
|
|
KdDebuggerData.KernBase == ModEntry.Base)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (ReallyVerbose)
|
|
{
|
|
dprintf("AddImage: %s\n DllBase = %s\n Size = %08x\n "
|
|
"Checksum = %08x\n TimeDateStamp = %08x\n",
|
|
ModEntry.NamePtr, FormatAddr64(ModEntry.Base),
|
|
ModEntry.Size, ModEntry.CheckSum,
|
|
ModEntry.TimeDateStamp);
|
|
}
|
|
else
|
|
{
|
|
if (!SpecificModule)
|
|
{
|
|
dprintf(".");
|
|
}
|
|
}
|
|
|
|
if (Address)
|
|
{
|
|
ModEntry.Base = Address;
|
|
}
|
|
|
|
if (AddImage(&ModEntry, ForceSymbolLoad) == NULL)
|
|
{
|
|
RetStatus = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (SpecificModule)
|
|
{
|
|
free( SpecificModule );
|
|
goto Notify;
|
|
}
|
|
|
|
if (AddrLoad)
|
|
{
|
|
goto Notify;
|
|
}
|
|
}
|
|
|
|
if (UseDebuggerModuleList || IS_KERNEL_TARGET() || UserModeList)
|
|
{
|
|
// print newline after all the '.'
|
|
dprintf("\n");
|
|
}
|
|
|
|
if (!UseDebuggerModuleList && !UserModeList && SpecificModule == NULL)
|
|
{
|
|
// If we just reloaded the kernel modules
|
|
// go through the unloaded module list.
|
|
if (!PrintImageListOnly)
|
|
{
|
|
dprintf("Loading unloaded module list\n");
|
|
}
|
|
ListUnloadedModules(PrintImageListOnly ?
|
|
LUM_OUTPUT : LUM_OUTPUT_TERSE, NULL);
|
|
}
|
|
|
|
//
|
|
// If we got to the end of the kernel symbols, try to load the
|
|
// user mode symbols for the current process.
|
|
//
|
|
|
|
if (!UseDebuggerModuleList &&
|
|
(UserModeList == FALSE) &&
|
|
(LoadUserSymbols == TRUE) &&
|
|
SUCCEEDED(Status))
|
|
{
|
|
if (!AddrLoad && !SpecificModule)
|
|
{
|
|
dprintf("Loading User Symbols\n");
|
|
}
|
|
|
|
UserModeList = TRUE;
|
|
ModIter = GetModuleInfo(UserModeList);
|
|
if (ModIter != NULL && ModIter->Initialize() == S_OK)
|
|
{
|
|
goto LoadLoop;
|
|
}
|
|
}
|
|
|
|
if (!SpecificModule && !Wow64ModLoaded)
|
|
{
|
|
ModIter = &g_NtWow64UserModuleIterator;
|
|
|
|
Wow64ModLoaded = TRUE;
|
|
if (ModIter->Initialize() == S_OK)
|
|
{
|
|
dprintf("Loading Wow64 Symbols\n");
|
|
goto LoadLoop;
|
|
}
|
|
}
|
|
|
|
// In the multiple load situation we always return OK
|
|
// since an error wouldn't tell you much about what
|
|
// actually occurred.
|
|
// Specific loads that haven't already been handled are checked
|
|
// right after this.
|
|
RetStatus = S_OK;
|
|
|
|
//
|
|
// If we still have not managed to load a named file, just pass the name
|
|
// and the address and hope for the best.
|
|
//
|
|
|
|
if (SpecificModule)
|
|
{
|
|
WarnOut("\nModule \"%s\" was not found in the module list.\n",
|
|
SpecificModule);
|
|
WarnOut("Debugger will attempt to load module \"%s\" "
|
|
"by guessing the base address.\n\n", SpecificModule);
|
|
WarnOut("Please provide the full image name, including the "
|
|
"extension (i.e. kernel32.dll) for more reliable results.\n");
|
|
|
|
ZeroMemory(&ModEntry, sizeof(ModEntry));
|
|
|
|
ModEntry.NamePtr = SpecificModule,
|
|
ModEntry.Base = Address;
|
|
ModEntry.Size = ImageSize;
|
|
|
|
if (AddImage(&ModEntry, TRUE) == NULL)
|
|
{
|
|
ErrOut("Unable to load module at %s\n", FormatAddr64(Address));
|
|
RetStatus = E_FAIL;
|
|
}
|
|
|
|
free(SpecificModule);
|
|
}
|
|
|
|
Notify:
|
|
// If we've gotten this far we've done one or more reloads
|
|
// and postponed notifications. Do them now that all the work
|
|
// has been done.
|
|
g_EngNotify--;
|
|
if (SUCCEEDED(RetStatus))
|
|
{
|
|
NotifyChangeSymbolState(DEBUG_CSS_LOADS | DEBUG_CSS_UNLOADS, 0,
|
|
g_CurrentProcess);
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
SymSetOptions(ReloadSymOptions);
|
|
}
|
|
|
|
if (ModEntry.DebugHeader)
|
|
{
|
|
free(ModEntry.DebugHeader);
|
|
}
|
|
|
|
return RetStatus;
|
|
|
|
FreeSpecMod:
|
|
free(SpecificModule);
|
|
return RetStatus;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetProcessUpTimeN(ULONG64 Process)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
TargetInfo::GetKdVersion(void)
|
|
{
|
|
BOOL Ptr64;
|
|
|
|
if (GetTargetKdVersion(&g_KdVersion) != S_OK)
|
|
{
|
|
ErrOut("Debugger can't get KD version information\n");
|
|
memset(&g_KdVersion, 0, sizeof(g_KdVersion));
|
|
return;
|
|
}
|
|
|
|
if ((g_KdVersion.MajorVersion >> 8) >= KD_MAJOR_COUNT)
|
|
{
|
|
ErrOut("KD version has unknown kernel type\n");
|
|
memset(&g_KdVersion, 0, sizeof(g_KdVersion));
|
|
return;
|
|
}
|
|
|
|
if (MachineTypeIndex(g_KdVersion.MachineType) == MACHIDX_COUNT)
|
|
{
|
|
ErrOut("KD version has unknown processor architecture\n");
|
|
memset(&g_KdVersion, 0, sizeof(g_KdVersion));
|
|
return;
|
|
}
|
|
|
|
Ptr64 =
|
|
((g_KdVersion.Flags & DBGKD_VERS_FLAG_PTR64) == DBGKD_VERS_FLAG_PTR64);
|
|
if (g_KdVersion.Flags & DBGKD_VERS_FLAG_NOMM)
|
|
{
|
|
g_VirtualCache.m_DecodePTEs = FALSE;
|
|
}
|
|
|
|
// Reloads cause the version to be retrieved but
|
|
// we don't want to completely reinitialize machines
|
|
// in that case as some settings can be lost. Only
|
|
// reinitialize if there's a need to do so.
|
|
BOOL MustInitializeMachines =
|
|
g_TargetMachineType != g_KdVersion.MachineType ||
|
|
g_TargetMachine == NULL;
|
|
|
|
g_TargetMachineType = g_KdVersion.MachineType;
|
|
g_TargetBuildNumber = g_KdVersion.MinorVersion;
|
|
g_TargetCheckedBuild = g_KdVersion.MajorVersion & 0xFF;
|
|
|
|
if ((g_TargetMachineType == IMAGE_FILE_MACHINE_ALPHA) && Ptr64)
|
|
{
|
|
g_TargetMachineType = IMAGE_FILE_MACHINE_AXP64;
|
|
g_KdVersion.MachineType = IMAGE_FILE_MACHINE_AXP64;
|
|
}
|
|
|
|
//
|
|
// Determine the OS running.
|
|
//
|
|
|
|
switch(g_KdVersion.MajorVersion >> 8)
|
|
{
|
|
case KD_MAJOR_NT:
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = NtBuildToSystemVersion(g_TargetBuildNumber);
|
|
g_SystemVersion = g_ActualSystemVersion;
|
|
break;
|
|
|
|
case KD_MAJOR_XBOX:
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = XBOX_SVER_1;
|
|
g_SystemVersion = NT_SVER_W2K;
|
|
break;
|
|
|
|
case KD_MAJOR_BIG:
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = BIG_SVER_1;
|
|
g_SystemVersion = NT_SVER_W2K;
|
|
break;
|
|
|
|
case KD_MAJOR_EXDI:
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = EXDI_SVER_1;
|
|
g_SystemVersion = NT_SVER_W2K;
|
|
break;
|
|
|
|
case KD_MAJOR_NTBD:
|
|
// Special mode for the NT boot debugger where
|
|
// the full system hasn't started yet.
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = NTBD_SVER_W2K_WHISTLER;
|
|
g_SystemVersion = NtBuildToSystemVersion(g_TargetBuildNumber);
|
|
break;
|
|
|
|
case KD_MAJOR_EFI:
|
|
g_TargetPlatformId = VER_PLATFORM_WIN32_NT;
|
|
g_ActualSystemVersion = EFI_SVER_1;
|
|
g_SystemVersion = NT_SVER_W2K_WHISTLER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Pre-Whistler kernels didn't set these values so
|
|
// default them appropriately.
|
|
//
|
|
|
|
g_KdMaxPacketType = g_KdVersion.MaxPacketType;
|
|
if (g_SystemVersion < NT_SVER_W2K_WHISTLER ||
|
|
g_KdMaxPacketType == 0 ||
|
|
g_KdMaxPacketType > PACKET_TYPE_MAX)
|
|
{
|
|
g_KdMaxPacketType = PACKET_TYPE_KD_CONTROL_REQUEST + 1;
|
|
}
|
|
|
|
g_KdMaxStateChange = g_KdVersion.MaxStateChange +
|
|
DbgKdMinimumStateChange;
|
|
if (g_SystemVersion < NT_SVER_W2K_WHISTLER ||
|
|
g_KdMaxStateChange == DbgKdMinimumStateChange ||
|
|
g_KdMaxStateChange > DbgKdMaximumStateChange)
|
|
{
|
|
g_KdMaxStateChange = DbgKdLoadSymbolsStateChange + 1;
|
|
}
|
|
|
|
g_KdMaxManipulate = g_KdVersion.MaxManipulate +
|
|
DbgKdMinimumManipulate;
|
|
if (g_SystemVersion < NT_SVER_W2K_WHISTLER ||
|
|
g_KdMaxManipulate == DbgKdMinimumManipulate ||
|
|
g_KdMaxManipulate > DbgKdMaximumManipulate)
|
|
{
|
|
g_KdMaxManipulate = DbgKdCheckLowMemoryApi + 1;
|
|
}
|
|
|
|
if (MustInitializeMachines)
|
|
{
|
|
InitializeMachines(g_TargetMachineType);
|
|
}
|
|
|
|
KdOut("Target MajorVersion %08lx\n", g_KdVersion.MajorVersion);
|
|
KdOut("Target MinorVersion %08lx\n", g_KdVersion.MinorVersion);
|
|
KdOut("Target ProtocolVersion %08lx\n", g_KdVersion.ProtocolVersion);
|
|
KdOut("Target Flags %08lx\n", g_KdVersion.Flags);
|
|
KdOut("Target MachineType %08lx\n", g_KdVersion.MachineType);
|
|
KdOut("Target MaxPacketType %x\n", g_KdVersion.MaxPacketType);
|
|
KdOut("Target MaxStateChange %x\n", g_KdVersion.MaxStateChange);
|
|
KdOut("Target MaxManipulate %x\n", g_KdVersion.MaxManipulate);
|
|
KdOut("Target KernBase %s\n",
|
|
FormatAddr64(g_KdVersion.KernBase));
|
|
KdOut("Target PsLoadedModuleList %s\n",
|
|
FormatAddr64(g_KdVersion.PsLoadedModuleList));
|
|
KdOut("Target DebuggerDataList %s\n",
|
|
FormatAddr64(g_KdVersion.DebuggerDataList));
|
|
|
|
dprintf("Connected to %s %d %s target, ptr64 %s\n",
|
|
SystemVersionName(g_ActualSystemVersion), g_TargetBuildNumber,
|
|
g_TargetMachine->m_FullName,
|
|
g_TargetMachine->m_Ptr64 ? "TRUE" : "FALSE");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
InitSelCache();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
|
|
{
|
|
ULONG64 BugCheckData;
|
|
ULONG64 Data[5];
|
|
HRESULT Status;
|
|
ULONG Read;
|
|
|
|
if (!(BugCheckData = KdDebuggerData.KiBugcheckData))
|
|
{
|
|
if (!GetOffsetFromSym("nt!KiBugCheckData", &BugCheckData, NULL) ||
|
|
!BugCheckData)
|
|
{
|
|
ErrOut("Unable to resolve nt!KiBugCheckData\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
if (g_TargetMachine->m_Ptr64)
|
|
{
|
|
Status = ReadVirtual(BugCheckData, Data, sizeof(Data), &Read);
|
|
}
|
|
else
|
|
{
|
|
ULONG i;
|
|
ULONG Data32[5];
|
|
|
|
Status = ReadVirtual(BugCheckData, Data32, sizeof(Data32), &Read);
|
|
Read *= 2;
|
|
for (i = 0; i < DIMA(Data); i++)
|
|
{
|
|
Data[i] = EXTEND64(Data32[i]);
|
|
}
|
|
}
|
|
|
|
if (Status != S_OK || Read != sizeof(Data))
|
|
{
|
|
ErrOut("Unable to read KiBugCheckData\n");
|
|
return Status == S_OK ? E_FAIL : Status;
|
|
}
|
|
|
|
*Code = (ULONG)Data[0];
|
|
memcpy(Args, Data + 1, sizeof(Data) - sizeof(ULONG64));
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG64
|
|
LiveKernelTargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
ULONG64 TimeDate;
|
|
|
|
if (ReadSharedUserTimeDateN(&TimeDate) == S_OK)
|
|
{
|
|
return TimeDate;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
LiveKernelTargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (ReadSharedUserUpTimeN(&UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ConnLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status = DbgKdConnectAndInitialize(m_ConnectOptions);
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
Status = LiveKernelTargetInfo::Initialize();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
ConnLiveKernelTargetInfo::Uninitialize(void)
|
|
{
|
|
if (g_DbgKdTransport != NULL)
|
|
{
|
|
g_DbgKdTransport->Uninitialize();
|
|
g_DbgKdTransport = NULL;
|
|
}
|
|
|
|
LiveKernelTargetInfo::Uninitialize();
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
NTSTATUS Status = DbgKdGetVersion(Version);
|
|
return CONV_NT_STATUS(Status);
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::RequestBreakIn(void)
|
|
{
|
|
// Tell the waiting thread to break in.
|
|
g_DbgKdTransport->m_BreakIn = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::Reboot(void)
|
|
{
|
|
DbgKdReboot();
|
|
ResetConnection(DEBUG_SESSION_REBOOT);
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LocalLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
DBGKD_GET_VERSION64 Version;
|
|
|
|
// Do a quick check to see if this kernel even
|
|
// supports the necessary debug services.
|
|
if (!NT_SUCCESS(g_NtDllCalls.
|
|
NtSystemDebugControl(SysDbgQueryVersion, NULL, 0,
|
|
&Version, sizeof(Version), NULL)))
|
|
{
|
|
ErrOut("The system does not support local kernel debugging.\n");
|
|
ErrOut("Local kernel debugging requires Windows XP, Administrative\n"
|
|
"privileges, and is not supported by WOW64.\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// Set this right here since we know kernel debugging only works on
|
|
// recent systems using the 64 bit protocol.
|
|
//
|
|
|
|
DbgKdApi64 = TRUE;
|
|
|
|
return LiveKernelTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
NTSTATUS Status = g_NtDllCalls.
|
|
NtSystemDebugControl(SysDbgQueryVersion, NULL, 0,
|
|
Version, sizeof(*Version), NULL);
|
|
return CONV_NT_STATUS(Status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ExdiLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
ExdiNotifyRunChange::Initialize(void)
|
|
{
|
|
m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (m_Event == NULL)
|
|
{
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
ExdiNotifyRunChange::Uninitialize(void)
|
|
{
|
|
if (m_Event != NULL)
|
|
{
|
|
CloseHandle(m_Event);
|
|
m_Event = NULL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
ExdiNotifyRunChange::QueryInterface(
|
|
THIS_
|
|
IN REFIID InterfaceId,
|
|
OUT PVOID* Interface
|
|
)
|
|
{
|
|
if (DbgIsEqualIID(IID_IUnknown, InterfaceId) ||
|
|
DbgIsEqualIID(__uuidof(IeXdiClientNotifyRunChg), InterfaceId))
|
|
{
|
|
*Interface = this;
|
|
return S_OK;
|
|
}
|
|
|
|
*Interface = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
ExdiNotifyRunChange::AddRef(
|
|
THIS
|
|
)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
ExdiNotifyRunChange::Release(
|
|
THIS
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
ExdiNotifyRunChange::NotifyRunStateChange(RUN_STATUS_TYPE ersCurrent,
|
|
HALT_REASON_TYPE ehrCurrent,
|
|
ADDRESS_TYPE CurrentExecAddress,
|
|
DWORD dwExceptionCode)
|
|
{
|
|
if (ersCurrent == rsRunning)
|
|
{
|
|
// We're waiting for things to stop so ignore this.
|
|
return S_OK;
|
|
}
|
|
|
|
m_HaltReason = ehrCurrent;
|
|
m_ExecAddress = CurrentExecAddress;
|
|
m_ExceptionCode = dwExceptionCode;
|
|
SetEvent(m_Event);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
class ExdiParams : public ParameterStringParser
|
|
{
|
|
public:
|
|
virtual ULONG GetNumberParameters(void)
|
|
{
|
|
// No need to get.
|
|
return 0;
|
|
}
|
|
virtual void GetParameter(ULONG Index, PSTR Name, PSTR Value)
|
|
{
|
|
}
|
|
|
|
virtual void ResetParameters(void);
|
|
virtual BOOL SetParameter(PCSTR Name, PCSTR Value);
|
|
|
|
CLSID m_Clsid;
|
|
EXDI_KD_SUPPORT m_KdSupport;
|
|
BOOL m_ForceX86;
|
|
};
|
|
|
|
void
|
|
ExdiParams::ResetParameters(void)
|
|
{
|
|
ZeroMemory(&m_Clsid, sizeof(m_Clsid));
|
|
m_KdSupport = EXDI_KD_NONE;
|
|
m_ForceX86 = FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ScanExdiDriverList(PCSTR Name, LPCLSID Clsid)
|
|
{
|
|
char Pattern[MAX_PARAM_VALUE];
|
|
|
|
strncpy(Pattern, Name, sizeof(Pattern));
|
|
Pattern[MAX_PARAM_VALUE-1] = 0;
|
|
|
|
_strupr(Pattern);
|
|
|
|
HKEY ListKey;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"Software\\Microsoft\\eXdi\\DriverList", 0,
|
|
KEY_ALL_ACCESS, &ListKey) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG Index = 0;
|
|
BOOL Status = FALSE;
|
|
char ValName[MAX_PARAM_VALUE];
|
|
WCHAR WideValName[MAX_PARAM_VALUE];
|
|
ULONG NameLen, ValLen;
|
|
ULONG Type;
|
|
char Value[MAX_PARAM_VALUE];
|
|
|
|
for (;;)
|
|
{
|
|
NameLen = sizeof(ValName);
|
|
ValLen = sizeof(Value);
|
|
if (RegEnumValue(ListKey, Index, ValName, &NameLen, NULL,
|
|
&Type, (PBYTE)Value, &ValLen) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Type == REG_SZ &&
|
|
MatchPattern(Value, Pattern) &&
|
|
MultiByteToWideChar(CP_ACP, 0, ValName, -1, WideValName,
|
|
sizeof(WideValName) / sizeof(WCHAR)) > 0 &&
|
|
g_Ole32Calls.CLSIDFromString(WideValName, Clsid) == S_OK)
|
|
{
|
|
Status = TRUE;
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
RegCloseKey(ListKey);
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
ExdiParams::SetParameter(PCSTR Name, PCSTR Value)
|
|
{
|
|
if (!_strcmpi(Name, "CLSID"))
|
|
{
|
|
WCHAR WideValue[MAX_PARAM_VALUE];
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, Value, -1, WideValue,
|
|
sizeof(WideValue) / sizeof(WCHAR)) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return g_Ole32Calls.CLSIDFromString(WideValue, &m_Clsid) == S_OK;
|
|
}
|
|
else if (!_strcmpi(Name, "Desc"))
|
|
{
|
|
return ScanExdiDriverList(Value, &m_Clsid);
|
|
}
|
|
else if (!_strcmpi(Name, "ForceX86"))
|
|
{
|
|
m_ForceX86 = TRUE;
|
|
}
|
|
else if (!_strcmpi(Name, "Kd"))
|
|
{
|
|
if (!Value)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_strcmpi(Value, "Ioctl"))
|
|
{
|
|
m_KdSupport = EXDI_KD_IOCTL;
|
|
}
|
|
else if (!_strcmpi(Value, "GsPcr"))
|
|
{
|
|
m_KdSupport = EXDI_KD_GS_PCR;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCSTR g_ExdiGroupNames[] =
|
|
{
|
|
"exdi",
|
|
};
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// Load ole32.dll so we can call CoCreateInstance.
|
|
if ((Status = InitDynamicCalls(&g_Ole32CallsDesc)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
ULONG Group;
|
|
|
|
Group = ParameterStringParser::
|
|
GetParser(m_ConnectOptions, DIMA(g_ExdiGroupNames), g_ExdiGroupNames);
|
|
if (Group == PARSER_INVALID)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ExdiParams Params;
|
|
|
|
Params.ResetParameters();
|
|
if (!Params.ParseParameters(m_ConnectOptions))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
m_KdSupport = Params.m_KdSupport;
|
|
|
|
if ((Status = g_Ole32Calls.
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if ((Status = g_Ole32Calls.CoCreateInstance(Params.m_Clsid, NULL,
|
|
CLSCTX_LOCAL_SERVER,
|
|
__uuidof(IeXdiServer),
|
|
(PVOID*)&m_Server)) != S_OK)
|
|
{
|
|
goto EH_CoInit;
|
|
}
|
|
|
|
if ((Status = m_Server->GetTargetInfo(&m_GlobalInfo)) != S_OK)
|
|
{
|
|
goto EH_Server;
|
|
}
|
|
|
|
if (Params.m_ForceX86 ||
|
|
m_GlobalInfo.TargetProcessorFamily == PROCESSOR_FAMILY_X86)
|
|
{
|
|
if (!Params.m_ForceX86 &&
|
|
(Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiX86_64Context),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_AMD64;
|
|
}
|
|
else if ((Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiX86Context),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
else
|
|
{
|
|
goto EH_Server;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
goto EH_Server;
|
|
}
|
|
|
|
DWORD HwCode, SwCode;
|
|
|
|
if ((Status = m_Server->GetNbCodeBpAvail(&HwCode, &SwCode)) != S_OK)
|
|
{
|
|
goto EH_Context;
|
|
}
|
|
|
|
// We'd prefer to use software code breakpoints for our
|
|
// software code breakpoints so that hardware resources
|
|
// aren't consumed for a breakpoint we don't need to
|
|
// use hardware for. However, some servers, such as
|
|
// the x86-64 SimNow implementation, do not support
|
|
// software breakpoints.
|
|
// Also, if the number of hardware breakpoints is
|
|
// unbounded, go ahead and let the server choose.
|
|
// SimNow advertises -1 -1 for some reason and
|
|
// this is necessary to get things to work.
|
|
|
|
if (SwCode > 0 && HwCode != (DWORD)-1)
|
|
{
|
|
m_CodeBpType = cbptSW;
|
|
}
|
|
else
|
|
{
|
|
m_CodeBpType = cbptAlgo;
|
|
}
|
|
|
|
if ((Status = m_RunChange.Initialize()) != S_OK)
|
|
{
|
|
goto EH_Context;
|
|
}
|
|
|
|
if ((Status = LiveKernelTargetInfo::Initialize()) != S_OK)
|
|
{
|
|
goto EH_RunChange;
|
|
}
|
|
|
|
m_ContextValid = 0;
|
|
return S_OK;
|
|
|
|
EH_RunChange:
|
|
m_RunChange.Uninitialize();
|
|
EH_Context:
|
|
RELEASE(m_Context);
|
|
EH_Server:
|
|
RELEASE(m_Server);
|
|
EH_CoInit:
|
|
g_Ole32Calls.CoUninitialize();
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::ThreadInitialize(void)
|
|
{
|
|
return g_Ole32Calls.CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
}
|
|
|
|
void
|
|
ExdiLiveKernelTargetInfo::ThreadUninitialize(void)
|
|
{
|
|
g_Ole32Calls.CoUninitialize();
|
|
}
|
|
|
|
void
|
|
ExdiLiveKernelTargetInfo::Uninitialize(void)
|
|
{
|
|
m_RunChange.Uninitialize();
|
|
RELEASE(m_Context);
|
|
RELEASE(m_Server);
|
|
g_Ole32Calls.CoUninitialize();
|
|
}
|
|
|
|
#define EXDI_IOCTL_GET_KD_VERSION ((ULONG)'VDKG')
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
switch(m_KdSupport)
|
|
{
|
|
case EXDI_KD_IOCTL:
|
|
//
|
|
// User has indicated the target supports the
|
|
// KD version ioctl.
|
|
//
|
|
|
|
ULONG Command;
|
|
ULONG Retrieved;
|
|
HRESULT Status;
|
|
|
|
Command = EXDI_IOCTL_GET_KD_VERSION;
|
|
if ((Status = m_Server->Ioctl(sizeof(Command), (PBYTE)&Command,
|
|
sizeof(*Version), &Retrieved,
|
|
(PBYTE)Version)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Retrieved != sizeof(*Version))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// This mode implies a recent kernel so we can
|
|
// assume 64-bit kd.
|
|
DbgKdApi64 = TRUE;
|
|
break;
|
|
|
|
case EXDI_KD_GS_PCR:
|
|
//
|
|
// User has indicated that a version of NT
|
|
// is running and that the PCR can be accessed
|
|
// through GS. Look up the version block from
|
|
// the PCR.
|
|
//
|
|
|
|
if (m_ExpectedMachine == IMAGE_FILE_MACHINE_AMD64)
|
|
{
|
|
ULONG64 KdVer;
|
|
ULONG Done;
|
|
|
|
if ((Status = g_Amd64Machine.
|
|
GetExdiContext(m_Context, &m_ContextData)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if ((Status = m_Server->
|
|
ReadVirtualMemory(m_ContextData.Amd64Context.
|
|
DescriptorGs.SegBase +
|
|
FIELD_OFFSET(AMD64_KPCR, KdVersionBlock),
|
|
sizeof(KdVer), 8, (PBYTE)&KdVer,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(KdVer))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
if ((Status = m_Server->
|
|
ReadVirtualMemory(KdVer, sizeof(*Version), 8, (PBYTE)Version,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(*Version))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
// This mode implies a recent kernel so we can
|
|
// assume 64-bit kd.
|
|
DbgKdApi64 = TRUE;
|
|
|
|
// Update the version block's Simulation field to
|
|
// indicate that this is a simulated execution.
|
|
Version->Simulation = DBGKD_SIMULATION_EXDI;
|
|
if ((Status = m_Server->
|
|
WriteVirtualMemory(KdVer, sizeof(*Version), 8, (PBYTE)Version,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(*Version))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case EXDI_KD_NONE:
|
|
//
|
|
// Fake up a version structure.
|
|
//
|
|
|
|
Version->MajorVersion = KD_MAJOR_EXDI << 8;
|
|
Version->ProtocolVersion = 0;
|
|
Version->Flags = DBGKD_VERS_FLAG_PTR64 | DBGKD_VERS_FLAG_NOMM;
|
|
Version->MachineType = (USHORT)m_ExpectedMachine;
|
|
Version->KernBase = 0;
|
|
Version->PsLoadedModuleList = 0;
|
|
Version->DebuggerDataList = 0;
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::RequestBreakIn(void)
|
|
{
|
|
return m_Server->Halt();
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::Reboot(void)
|
|
{
|
|
HRESULT Status = m_Server->Reboot();
|
|
if (Status == S_OK)
|
|
{
|
|
ResetConnection(DEBUG_SESSION_REBOOT);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// UserTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
UserTargetInfo::Initialize(void)
|
|
{
|
|
// Nothing to do right now.
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
UserTargetInfo::Uninitialize(void)
|
|
{
|
|
RELEASE(m_Services);
|
|
}
|
|
|
|
HRESULT
|
|
UserTargetInfo::GetImageVersionInformation(PCSTR ImagePath,
|
|
ULONG64 ImageBase,
|
|
PCSTR Item,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
PULONG VerInfoSize)
|
|
{
|
|
return m_Services->
|
|
GetFileVersionInformation(ImagePath, Item,
|
|
Buffer, BufferSize, VerInfoSize);
|
|
}
|
|
|
|
ULONG64
|
|
UserTargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
ULONG64 TimeDate;
|
|
|
|
if (m_Services->GetCurrentTimeDateN(&TimeDate) == S_OK)
|
|
{
|
|
return TimeDate;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
UserTargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (m_Services->GetCurrentSystemUpTimeN(&UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
UserTargetInfo::GetProcessUpTimeN(ULONG64 Process)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (m_Services->GetProcessUpTimeN(Process, &UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserTargetInfo::RequestBreakIn(void)
|
|
{
|
|
PPROCESS_INFO Process = g_CurrentProcess;
|
|
if (Process == NULL)
|
|
{
|
|
Process = g_ProcessHead;
|
|
if (Process == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
return m_Services->RequestBreakIn(Process->FullHandle);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Base TargetInfo methods that trivially fail.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define UNEXPECTED_VOID(Class, Method, Args) \
|
|
void \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
}
|
|
|
|
#define UNEXPECTED_HR(Class, Method, Args) \
|
|
HRESULT \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
return E_UNEXPECTED; \
|
|
}
|
|
|
|
#define UNEXPECTED_ULONG64(Class, Method, Val, Args) \
|
|
ULONG64 \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
return Val; \
|
|
}
|
|
|
|
TargetInfo g_UnexpectedTarget;
|
|
|
|
UNEXPECTED_HR(TargetInfo, Initialize, (void))
|
|
UNEXPECTED_HR(TargetInfo, ReadVirtual, (
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteVirtual, (
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadPhysical, (
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WritePhysical, (
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadControl, (
|
|
IN ULONG Processor,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteControl, (
|
|
IN ULONG Processor,
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadIo, (
|
|
IN ULONG InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG AddressSpace,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteIo, (
|
|
IN ULONG InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG AddressSpace,
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadMsr, (
|
|
IN ULONG Msr,
|
|
OUT PULONG64 Value
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteMsr, (
|
|
IN ULONG Msr,
|
|
IN ULONG64 Value
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadBusData, (
|
|
IN ULONG BusDataType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN ULONG Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteBusData, (
|
|
IN ULONG BusDataType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN ULONG Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, CheckLowMemory, (
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadHandleData, (
|
|
IN ULONG64 Handle,
|
|
IN ULONG DataType,
|
|
OUT OPTIONAL PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG DataSize
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetTargetContext, (
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, SetTargetContext, (
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadIdByProcessor, (
|
|
IN ULONG Processor,
|
|
OUT PULONG Id
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadInfoDataOffset, (
|
|
PTHREAD_INFO Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetProcessInfoDataOffset, (
|
|
PTHREAD_INFO Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadInfoTeb, (
|
|
PTHREAD_INFO Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetProcessInfoPeb, (
|
|
PTHREAD_INFO Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetSelDescriptor, (
|
|
MachineInfo* Machine,
|
|
ULONG64 Thread,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc))
|
|
UNEXPECTED_HR(TargetInfo, GetTargetKdVersion, (
|
|
PDBGKD_GET_VERSION64 Version))
|
|
UNEXPECTED_HR(TargetInfo, ReadBugCheckData, (
|
|
PULONG Code, ULONG64 Args[4]))
|
|
UNEXPECTED_HR(TargetInfo, GetExceptionContext, (
|
|
PCROSS_PLATFORM_CONTEXT Context))
|
|
UNEXPECTED_VOID(TargetInfo, InitializeWatchTrace, (
|
|
void))
|
|
UNEXPECTED_VOID(TargetInfo, ProcessWatchTraceEvent, (
|
|
PDBGKD_TRACE_DATA TraceData,
|
|
ADDR PcAddr))
|
|
UNEXPECTED_HR(TargetInfo, WaitForEvent, (
|
|
ULONG Flags,
|
|
ULONG Timeout))
|
|
UNEXPECTED_HR(TargetInfo, RequestBreakIn, (void))
|
|
UNEXPECTED_HR(TargetInfo, Reboot, (void))
|
|
UNEXPECTED_HR(TargetInfo, InsertCodeBreakpoint, (
|
|
PPROCESS_INFO Process,
|
|
MachineInfo* Machine,
|
|
PADDR Addr,
|
|
PUCHAR StorageSpace))
|
|
UNEXPECTED_HR(TargetInfo, RemoveCodeBreakpoint, (
|
|
PPROCESS_INFO Process,
|
|
MachineInfo* Machine,
|
|
PADDR Addr,
|
|
PUCHAR StorageSpace))
|
|
UNEXPECTED_HR(TargetInfo, QueryMemoryRegion, (
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info))
|