Windows-Server-2003/sdktools/debuggers/ntsd64/modinfo.cpp

1120 lines
31 KiB
C++

//----------------------------------------------------------------------------
//
// Module list abstraction.
//
// Copyright (C) Microsoft Corporation, 2001-2002.
//
//----------------------------------------------------------------------------
#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));
//----------------------------------------------------------------------------
//
// Module list abstraction.
//
//----------------------------------------------------------------------------
void
ModuleInfo::ReadImageHeaderInfo(PMODULE_INFO_ENTRY Entry)
{
HRESULT Status;
UCHAR SectorBuffer[ 1024 ];
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 = m_Target->ReadVirtual(m_Process, 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;
Entry->MajorImageVersion = ((PIMAGE_NT_HEADERS32)NtHeaders)->
OptionalHeader.MajorImageVersion;
Entry->MinorImageVersion = ((PIMAGE_NT_HEADERS32)NtHeaders)->
OptionalHeader.MinorImageVersion;
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;
Entry->MajorImageVersion =
NtHeaders->OptionalHeader.MajorImageVersion;
Entry->MinorImageVersion =
NtHeaders->OptionalHeader.MinorImageVersion;
break;
}
Entry->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
Entry->MachineType = NtHeaders->FileHeader.Machine;
Entry->ImageInfoValid = 1;
Entry->ImageVersionValid = 1;
Entry->ImageMachineTypeValid = 1;
}
}
}
void
ModuleInfo::InitSource(ThreadInfo* Thread)
{
m_Thread = Thread;
m_Process = m_Thread->m_Process;
m_Target = m_Process->m_Target;
m_Machine = m_Target->m_Machine;
m_InfoLevel = MODULE_INFO_ALL;
}
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 = m_Target->
ReadLoaderEntry(m_Process, 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;
if (m_InfoLevel > MODULE_INFO_BASE_SIZE)
{
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 (m_Target->m_SystemVersion >= NT_SVER_W2K &&
Length != 0 && Buffer != 0 &&
Length < (MAX_IMAGE_PATH * sizeof(WCHAR)))
{
Status = m_Target->ReadVirtual(m_Process, 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 = m_Target->ReadVirtual(m_Process, 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->UnicodeNamePtr = 1;
Entry->NameLength = Length;
}
Entry->Base = LdrEntry.DllBase;
Entry->Size = LdrEntry.SizeOfImage;
Entry->CheckSum = LdrEntry.CheckSum;
Entry->TimeDateStamp = LdrEntry.TimeDateStamp;
//
// Update the image information, such as timestamp and real image size,
// directly from the image header
//
if (m_InfoLevel > MODULE_INFO_BASE_SIZE)
{
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 (m_InfoLevel > MODULE_INFO_BASE_SIZE &&
(LdrEntry.Flags & LDRP_NON_PAGED_DEBUG_INFO))
{
NON_PAGED_DEBUG_INFO di;
Status = m_Target->ReadVirtual(m_Process,
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->MachineType = di.Machine;
Entry->ImageInfoPartial = 1;
Entry->ImageInfoValid = 1;
Entry->ImageMachineTypeValid = 1;
if (di.Flags == 1)
{
Entry->DebugHeader = malloc(di.Size - sizeof(di));
if (Entry->DebugHeader)
{
Status = m_Target->ReadVirtual(m_Process,
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(ThreadInfo* Thread)
{
HRESULT Status;
LIST_ENTRY64 List64;
InitSource(Thread);
if ((m_Head = m_Target->m_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 = m_Target->ReadListEntry(m_Process, m_Machine, m_Head, &List64);
if (Status != S_OK)
{
// PsLoadedModuleList is a global kernel variable, so if
// it isn't around the kernel must not be mapped and
// we're in a very weird state.
ErrOut("Unable to read PsLoadedModuleList\n");
return S_FALSE;
}
if (!List64.Flink)
{
ULONG64 LoaderBlock;
IMAGE_NT_HEADERS64 ImageHdr;
//
// In live debug sessions, the debugger connects before Mm creates
// the actual module list. If PsLoadedModuleList is
// uninitialized, try to load symbols from the loader
// block module list.
//
// If there is no loader block module list but we know
// the kernel base address and can read the image headers
// we fake a single entry for the kernel so that kernel
// symbols will load even without any module lists.
//
if (m_Target->m_KdDebuggerData.KeLoaderBlock &&
m_Target->ReadPointer(m_Process, m_Machine,
m_Target->m_KdDebuggerData.KeLoaderBlock,
&LoaderBlock) == S_OK &&
LoaderBlock &&
m_Target->ReadListEntry(m_Process, m_Machine, LoaderBlock,
&List64) == S_OK &&
List64.Flink)
{
m_Head = LoaderBlock;
}
else if (m_Target->m_KdDebuggerData.KernBase &&
m_Target->ReadImageNtHeaders(m_Process, m_Target->
m_KdDebuggerData.KernBase,
&ImageHdr) == S_OK)
{
m_Head = m_Target->m_KdDebuggerData.KernBase;
List64.Flink = m_Target->m_KdDebuggerData.KernBase;
}
else
{
dprintf("No module list information. Delay kernel load.\n");
return S_FALSE;
}
}
m_Cur = List64.Flink;
return S_OK;
}
HRESULT
NtKernelModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
HRESULT Status;
if (m_Head && m_Head == m_Target->m_KdDebuggerData.KernBase)
{
//
// We weren't able to locate any actual module list
// information but we do have a kernel base and valid
// image at that address. Fake up a kernel module entry.
//
wcscpy((PWSTR)Entry->Buffer, L"kd_ntoskrnl");
Entry->NamePtr = &(Entry->Buffer[0]);
Entry->UnicodeNamePtr = 1;
Entry->NameLength = wcslen((PWSTR)Entry->NamePtr) * sizeof(WCHAR);
Entry->Base = m_Head;
ReadImageHeaderInfo(Entry);
m_Head = 0;
m_Cur = 0;
Status = S_OK;
}
else
{
Status = NtModuleInfo::GetEntry(Entry);
}
// We know that all kernel modules must be
// native modules so force the machine type
// if it isn't already set.
if (Status == S_OK && !Entry->ImageMachineTypeValid)
{
Entry->MachineType = m_Machine->m_ExecTypes[0];
Entry->ImageMachineTypeValid = 1;
}
return Status;
}
NtKernelModuleInfo g_NtKernelModuleIterator;
HRESULT
NtUserModuleInfo::Initialize(ThreadInfo* Thread)
{
if (Thread)
{
InitSource(Thread);
}
return GetUserModuleListAddress(m_Thread, m_Machine, m_Peb, FALSE,
&m_Head, &m_Cur) ? S_OK : S_FALSE;
}
HRESULT
NtUserModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
HRESULT Status = NtModuleInfo::GetEntry(Entry);
if (Status == S_OK)
{
Entry->UserMode = TRUE;
}
return Status;
}
HRESULT
NtTargetUserModuleInfo::Initialize(ThreadInfo* Thread)
{
m_Peb = 0;
return NtUserModuleInfo::Initialize(Thread);
}
NtTargetUserModuleInfo g_NtTargetUserModuleIterator;
HRESULT
NtWow64UserModuleInfo::Initialize(ThreadInfo* Thread)
{
HRESULT Status;
InitSource(Thread);
if (m_Target->m_Machine->m_NumExecTypes < 2)
{
return E_UNEXPECTED;
}
m_Machine = MachineTypeInfo(m_Target, m_Target->m_Machine->m_ExecTypes[1]);
if ((Status = GetPeb32(&m_Peb)) != S_OK)
{
return Status;
}
return NtUserModuleInfo::Initialize(NULL);
}
HRESULT
NtWow64UserModuleInfo::GetPeb32(PULONG64 Peb32)
{
ULONG64 Teb;
ULONG64 Teb32;
HRESULT Status;
if ((Status = m_Process->GetImplicitThreadDataTeb(m_Thread, &Teb)) == S_OK)
{
if ((Status = m_Target->
ReadPointer(m_Process, m_Machine, Teb, &Teb32)) == S_OK)
{
if (!Teb32)
{
return E_UNEXPECTED;
}
ULONG RawPeb32;
Status = m_Target->
ReadAllVirtual(m_Process, 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(ThreadInfo* Thread)
{
InitSource(Thread);
m_Image = m_Process->m_ImageHead;
return S_OK;
}
HRESULT
DebuggerModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
if (m_Image == NULL)
{
return S_FALSE;
}
Entry->NamePtr = m_Image->m_ImagePath;
Entry->UnicodeNamePtr = 0;
Entry->NameLength = strlen(Entry->NamePtr);
Entry->ModuleName = m_Image->m_ModuleName;
Entry->File = m_Image->m_File;
Entry->Base = m_Image->m_BaseOfImage;
Entry->Size = m_Image->m_SizeOfImage;
Entry->CheckSum = m_Image->m_CheckSum;
Entry->TimeDateStamp = m_Image->m_TimeDateStamp;
Entry->MachineType = m_Image->GetMachineType();
Entry->ImageInfoValid = TRUE;
Entry->ImageMachineTypeValid =
Entry->MachineType != IMAGE_FILE_MACHINE_UNKNOWN ? TRUE : FALSE;
Entry->UserMode = m_Image->m_UserMode;
m_Image = m_Image->m_Next;
return S_OK;
}
DebuggerModuleInfo g_DebuggerModuleIterator;
void
UnloadedModuleInfo::InitSource(ThreadInfo* Thread)
{
m_Thread = Thread;
m_Process = m_Thread->m_Process;
m_Target = m_Process->m_Target;
m_Machine = m_Target->m_Machine;
}
HRESULT
NtKernelUnloadedModuleInfo::Initialize(ThreadInfo* Thread)
{
// Make sure that the kernel dump size doesn't exceed
// the generic size limit.
C_ASSERT((MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR)) + 1 <=
MAX_INFO_UNLOADED_NAME);
InitSource(Thread);
if (m_Target->m_KdDebuggerData.MmUnloadedDrivers == 0 ||
m_Target->m_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 = m_Target->ReadPointer(m_Process, m_Machine,
m_Target->
m_KdDebuggerData.MmUnloadedDrivers,
&m_Base)) != S_OK ||
(Status = m_Target->ReadVirtual(m_Process, m_Target->
m_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 (m_Target->m_Machine->m_Ptr64)
{
UNLOADED_DRIVERS64 Entry;
Offset = m_Base + m_Index * sizeof(Entry);
if ((Status = m_Target->
ReadAllVirtual(m_Process, Offset, &Entry, sizeof(Entry))) != S_OK)
{
return Status;
}
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 = m_Target->
ReadAllVirtual(m_Process, Offset, &Entry, sizeof(Entry))) != S_OK)
{
return Status;
}
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 = m_Target->
ReadVirtual(m_Process, 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
NtUserUnloadedModuleInfo::Initialize(ThreadInfo* Thread)
{
HRESULT Status;
InitSource(Thread);
if ((Status = m_Target->
GetUnloadedModuleListHead(m_Process, &m_Base)) != S_OK)
{
return Status;
}
m_Index = 0;
return S_OK;
}
HRESULT
NtUserUnloadedModuleInfo::GetEntry(PSTR Name,
PDEBUG_MODULE_PARAMETERS Params)
{
if (m_Index >= RTL_UNLOAD_EVENT_TRACE_NUMBER)
{
return S_FALSE;
}
ULONG64 Offset;
HRESULT Status;
PWSTR NameArray;
RTL_UNLOAD_EVENT_TRACE32 Entry32;
RTL_UNLOAD_EVENT_TRACE64 Entry64;
// Make sure that the RTL record size doesn't exceed
// the generic size limit.
C_ASSERT(DIMA(Entry32.ImageName) <= MAX_INFO_UNLOADED_NAME);
ZeroMemory(Params, sizeof(*Params));
Params->Flags |= DEBUG_MODULE_UNLOADED | DEBUG_MODULE_USER_MODE;
if (m_Machine->m_Ptr64)
{
Offset = m_Base + m_Index * sizeof(Entry64);
if ((Status = m_Target->
ReadAllVirtual(m_Process, Offset,
&Entry64, sizeof(Entry64))) != S_OK)
{
return Status;
}
if (Entry64.BaseAddress == 0)
{
m_Index = RTL_UNLOAD_EVENT_TRACE_NUMBER;
return S_FALSE;
}
Params->Base = Entry64.BaseAddress;
Params->Size = (ULONG)Entry64.SizeOfImage;
Params->TimeDateStamp = Entry64.TimeDateStamp;
Params->Checksum = Entry64.CheckSum;
NameArray = Entry64.ImageName;
}
else
{
Offset = m_Base + m_Index * sizeof(Entry32);
if ((Status = m_Target->
ReadAllVirtual(m_Process, Offset,
&Entry32, sizeof(Entry32))) != S_OK)
{
return Status;
}
if (Entry32.BaseAddress == 0)
{
m_Index = RTL_UNLOAD_EVENT_TRACE_NUMBER;
return S_FALSE;
}
Params->Base = EXTEND64(Entry32.BaseAddress);
Params->Size = (ULONG)Entry32.SizeOfImage;
Params->TimeDateStamp = Entry32.TimeDateStamp;
Params->Checksum = Entry32.CheckSum;
NameArray = Entry32.ImageName;
}
if (Name != NULL)
{
NameArray[DIMA(Entry32.ImageName) - 1] = 0;
if (WideCharToMultiByte(CP_ACP, 0,
NameArray, -1,
Name,
MAX_INFO_UNLOADED_NAME,
NULL, NULL) == 0)
{
return WIN32_LAST_STATUS();
}
Name[MAX_INFO_UNLOADED_NAME - 1] = 0;
}
m_Index++;
return S_OK;
}
NtUserUnloadedModuleInfo g_NtUserUnloadedModuleIterator;
HRESULT
ToolHelpModuleInfo::Initialize(ThreadInfo* Thread)
{
InitSource(Thread);
m_Snap = g_Kernel32Calls.
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
m_Process->m_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
ToolHelpModuleInfo::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;
CopyString(Entry->Buffer, Mod.szModule, DIMA(Entry->Buffer));
Entry->NamePtr = Entry->Buffer;
Entry->UnicodeNamePtr = 0;
Entry->NameLength = strlen(Entry->NamePtr);
Entry->Base = EXTEND64((ULONG_PTR)Mod.modBaseAddr);
Entry->Size = Mod.modBaseSize;
// Toolhelp only enumerates user-mode modules.
Entry->UserMode = TRUE;
//
// Update the image informaion, such as timestamp and real image size,
// Directly from the image header
//
ReadImageHeaderInfo(Entry);
return S_OK;
}
ToolHelpModuleInfo g_ToolHelpModuleIterator;
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
BOOL
GetUserModuleListAddress(
ThreadInfo* Thread,
MachineInfo* Machine,
ULONG64 Peb,
BOOL Quiet,
PULONG64 OrderModuleListStart,
PULONG64 FirstEntry
)
{
ULONG64 PebLdrOffset;
ULONG64 ModuleListOffset;
ULONG64 PebLdr = 0;
*OrderModuleListStart = 0;
*FirstEntry = 0;
if (!Thread || !Machine)
{
return FALSE;
}
//
// Triage dumps have no user mode information.
// User-mode minidumps don't have a loader list.
//
if (IS_KERNEL_TRIAGE_DUMP(Machine->m_Target) ||
IS_USER_MINI_DUMP(Machine->m_Target))
{
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 (Thread->m_Process->m_Target->
GetImplicitProcessDataPeb(Thread, &Peb) != S_OK)
{
if (!Quiet)
{
ErrOut("Unable to get PEB pointer\n");
}
return FALSE;
}
if (!Peb)
{
// This is a common error as the idle and system process has no
// user address space. So only print the error if we really
// expected to find a user mode address space:
// The Idle and system process have a NULL parent client id - all
// other threads have a valid ID.
//
ULONG64 Pcid;
if ((Thread->m_Process->m_Target->
GetImplicitProcessDataParentCID(Thread, &Pcid) != S_OK) ||
Pcid)
{
if (!Quiet)
{
ErrOut("PEB address is NULL !\n");
}
}
return FALSE;
}
}
//
// Read address the PEB Ldr data from the PEB structure
//
Peb += PebLdrOffset;
if ( (Machine->m_Target->
ReadPointer(Thread->m_Process, Machine, Peb, &PebLdr) != S_OK) ||
(PebLdr == 0) )
{
if (!Quiet)
{
ErrOut("PEB is paged out (Peb = %s). "
"Type \".hh dbgerr001\" for details\n",
FormatMachineAddr64(Machine, Peb));
}
return FALSE;
}
//
// Read address of the user mode module list from the PEB Ldr Data.
//
PebLdr += ModuleListOffset;
*OrderModuleListStart = PebLdr;
if ( (Machine->m_Target->
ReadPointer(Thread->m_Process, 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(
ThreadInfo* Thread,
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(Thread, Machine, Peb, TRUE,
&ModList, &List))
{
return FALSE;
}
while (List != ModList)
{
Status = Machine->m_Target->
ReadLoaderEntry(Thread->m_Process, 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 = Machine->m_Target->
ReadVirtual(Thread->m_Process, 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;
}