|
|
/*++
Copyright(c) 1999-2002 Microsoft Corporation
--*/
#include "pch.cpp"
#include "platform.h"
#include "nt4p.h"
//----------------------------------------------------------------------------
//
// NtWin32LiveSystemProvider.
//
//----------------------------------------------------------------------------
class NtWin32LiveSystemProvider : public Win32LiveSystemProvider { public: NtWin32LiveSystemProvider(ULONG BuildNumber); ~NtWin32LiveSystemProvider(void);
virtual HRESULT Initialize(void); virtual void Release(void); virtual HRESULT OpenThread(IN ULONG DesiredAccess, IN BOOL InheritHandle, IN ULONG ThreadId, OUT PHANDLE Handle); virtual HRESULT GetTeb(IN HANDLE Thread, OUT PULONG64 Offset, OUT PULONG Size); virtual HRESULT GetThreadInfo(IN HANDLE Process, IN HANDLE Thread, OUT PULONG64 Teb, OUT PULONG SizeOfTeb, OUT PULONG64 StackBase, OUT PULONG64 StackLimit, OUT PULONG64 StoreBase, OUT PULONG64 StoreLimit); virtual HRESULT GetPeb(IN HANDLE Process, OUT PULONG64 Offset, OUT PULONG Size); virtual HRESULT StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId); virtual HRESULT EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars); virtual HRESULT EnumFunctionTables(OUT PULONG64 MinAddress, OUT PULONG64 MaxAddress, OUT PULONG64 BaseAddress, OUT PULONG EntryCount, OUT PVOID RawTable, IN ULONG RawTableSize, OUT PVOID* RawEntryHandle); virtual HRESULT EnumFunctionTableEntries(IN PVOID RawTable, IN ULONG RawTableSize, IN PVOID RawEntryHandle, OUT PVOID RawEntries, IN ULONG RawEntriesSize); virtual HRESULT EnumFunctionTableEntryMemory(IN ULONG64 TableBase, IN PVOID RawEntries, IN ULONG Index, OUT PULONG64 Start, OUT PULONG Size); virtual HRESULT EnumUnloadedModules(OUT PWSTR Path, IN ULONG PathChars, OUT PULONG64 BaseOfModule, OUT PULONG SizeOfModule, OUT PULONG CheckSum, OUT PULONG TimeDateStamp); virtual void FinishProcessEnum(void); virtual HRESULT StartHandleEnum(IN HANDLE Process, IN ULONG ProcessId, OUT PULONG Count); virtual HRESULT EnumHandles(OUT PULONG64 Handle, OUT PULONG Attributes, OUT PULONG GrantedAccess, OUT PULONG HandleCount, OUT PULONG PointerCount, OUT PWSTR TypeName, IN ULONG TypeNameChars, OUT PWSTR ObjectName, IN ULONG ObjectNameChars); virtual void FinishHandleEnum(void); virtual HRESULT EnumPebMemory(IN HANDLE Process, IN ULONG64 PebOffset, IN ULONG PebSize, IN MiniDumpProviderCallbacks* Callback); virtual HRESULT EnumTebMemory(IN HANDLE Process, IN HANDLE Thread, IN ULONG64 TebOffset, IN ULONG TebSize, IN MiniDumpProviderCallbacks* Callback);
void EnumUnicodeString(IN PUNICODE_STRING String, IN MiniDumpProviderCallbacks* Callback) { if (String->Length > 0 && String->Buffer) { Callback->EnumMemory((LONG_PTR)String->Buffer, String->Length); } }
void TranslateNtPathName(IN OUT PWSTR Path); protected: HINSTANCE m_NtDll; NT_OPEN_THREAD m_NtOpenThread; NT_QUERY_SYSTEM_INFORMATION m_NtQuerySystemInformation; NT_QUERY_INFORMATION_PROCESS m_NtQueryInformationProcess; NT_QUERY_INFORMATION_THREAD m_NtQueryInformationThread; NT_QUERY_OBJECT m_NtQueryObject; RTL_FREE_HEAP m_RtlFreeHeap; RTL_GET_FUNCTION_TABLE_LIST_HEAD m_RtlGetFunctionTableListHead; RTL_GET_UNLOAD_EVENT_TRACE m_RtlGetUnloadEventTrace;
PLIST_ENTRY m_FuncTableHead; PLIST_ENTRY m_FuncTable; PRTL_UNLOAD_EVENT_TRACE m_Unloads; PRTL_UNLOAD_EVENT_TRACE m_Unload; ULONG m_UnloadArraySize; ULONG m_NumUnloads; ULONG m_Handle; };
NtWin32LiveSystemProvider::NtWin32LiveSystemProvider(ULONG BuildNumber) : Win32LiveSystemProvider(VER_PLATFORM_WIN32_NT, BuildNumber) { m_NtDll = NULL; m_NtOpenThread = NULL; m_NtQuerySystemInformation = NULL; m_NtQueryInformationProcess = NULL; m_NtQueryInformationThread = NULL; m_NtQueryObject = NULL; m_RtlFreeHeap = NULL; m_RtlGetFunctionTableListHead = NULL; m_RtlGetUnloadEventTrace = NULL;
m_Unloads = NULL; }
NtWin32LiveSystemProvider::~NtWin32LiveSystemProvider(void) { if (m_NtDll) { FreeLibrary(m_NtDll); } if (m_Unloads) { HeapFree(GetProcessHeap(), 0, m_Unloads); } }
HRESULT NtWin32LiveSystemProvider::Initialize(void) { HRESULT Status;
if ((Status = Win32LiveSystemProvider::Initialize()) != S_OK) { return Status; } m_NtDll = LoadLibrary("ntdll.dll"); if (m_NtDll) { m_NtOpenThread = (NT_OPEN_THREAD) GetProcAddress(m_NtDll, "NtOpenThread"); m_NtQuerySystemInformation = (NT_QUERY_SYSTEM_INFORMATION) GetProcAddress(m_NtDll, "NtQuerySystemInformation"); m_NtQueryInformationProcess = (NT_QUERY_INFORMATION_PROCESS) GetProcAddress(m_NtDll, "NtQueryInformationProcess"); m_NtQueryInformationThread = (NT_QUERY_INFORMATION_THREAD) GetProcAddress(m_NtDll, "NtQueryInformationThread"); m_NtQueryObject = (NT_QUERY_OBJECT) GetProcAddress(m_NtDll, "NtQueryObject"); m_RtlFreeHeap = (RTL_FREE_HEAP) GetProcAddress(m_NtDll, "RtlFreeHeap"); m_RtlGetFunctionTableListHead = (RTL_GET_FUNCTION_TABLE_LIST_HEAD) GetProcAddress(m_NtDll, "RtlGetFunctionTableListHead"); m_RtlGetUnloadEventTrace = (RTL_GET_UNLOAD_EVENT_TRACE) GetProcAddress(m_NtDll, "RtlGetUnloadEventTrace"); } return S_OK; }
void NtWin32LiveSystemProvider::Release(void) { delete this; }
HRESULT NtWin32LiveSystemProvider::OpenThread(IN ULONG DesiredAccess, IN BOOL InheritHandle, IN ULONG ThreadId, OUT PHANDLE Handle) { if (m_OpenThread) { // OS supports regular Win32 OpenThread, so try it.
*Handle = m_OpenThread(DesiredAccess, InheritHandle, ThreadId); if (*Handle) { return S_OK; } } if (!m_NtOpenThread) { return E_NOTIMPL; }
NTSTATUS Status; NT4_OBJECT_ATTRIBUTES Obja; NT4_CLIENT_ID ClientId;
ClientId.UniqueThread = (HANDLE)LongToHandle(ThreadId); ClientId.UniqueProcess = (HANDLE)NULL;
Nt4InitializeObjectAttributes(&Obja, NULL, (InheritHandle ? NT4_OBJ_INHERIT : 0), NULL, NULL);
Status = m_NtOpenThread(Handle, (ACCESS_MASK)DesiredAccess, (POBJECT_ATTRIBUTES)&Obja, (PCLIENT_ID)&ClientId); if (!NT_SUCCESS(Status)) { return HRESULT_FROM_NT(Status); }
return S_OK; }
HRESULT NtWin32LiveSystemProvider::GetTeb(IN HANDLE Thread, OUT PULONG64 Offset, OUT PULONG Size) { if (!m_NtQueryInformationThread) { return E_NOTIMPL; }
THREAD_BASIC_INFORMATION ThreadInformation; NTSTATUS NtStatus;
NtStatus = m_NtQueryInformationThread(Thread, ThreadBasicInformation, &ThreadInformation, sizeof(ThreadInformation), NULL); if (NT_SUCCESS(NtStatus)) { // The TEB is a little smaller than a page but
// save the entire page so that adjacent TEB
// pages get coalesced into a single region.
// As TEBs are normally adjacent this is a common case.
*Offset = (LONG_PTR)ThreadInformation.TebBaseAddress; *Size = PAGE_SIZE; return S_OK; } else { *Offset = 0; *Size = 0; return HRESULT_FROM_NT(NtStatus); } }
HRESULT NtWin32LiveSystemProvider::GetThreadInfo(IN HANDLE Process, IN HANDLE Thread, OUT PULONG64 Teb, OUT PULONG SizeOfTeb, OUT PULONG64 StackBase, OUT PULONG64 StackLimit, OUT PULONG64 StoreBase, OUT PULONG64 StoreLimit) { HRESULT Status;
if ((Status = GetTeb(Thread, Teb, SizeOfTeb)) != S_OK) { return Status; }
return TibGetThreadInfo(Process, *Teb, StackBase, StackLimit, StoreBase, StoreLimit); }
HRESULT NtWin32LiveSystemProvider::GetPeb(IN HANDLE Process, OUT PULONG64 Offset, OUT PULONG Size) { if (!m_NtQueryInformationProcess) { return E_NOTIMPL; } PROCESS_BASIC_INFORMATION Information; NTSTATUS NtStatus;
NtStatus = m_NtQueryInformationProcess(Process, ProcessBasicInformation, &Information, sizeof(Information), NULL); if (NT_SUCCESS(NtStatus)) { *Offset = (LONG_PTR)Information.PebBaseAddress; *Size = sizeof(PEB); return S_OK; } else { *Offset = 0; *Size = 0; return HRESULT_FROM_NT(NtStatus); } }
HRESULT NtWin32LiveSystemProvider::StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId) { HRESULT Status; SIZE_T Done;
if ((Status = Win32LiveSystemProvider:: StartProcessEnum(Process, ProcessId)) != S_OK) { return Status; }
//
// On systems that support dynamic function tables
// ntdll exports a function called RtlGetFunctionTableListHead
// to retrieve the head of a process's function table list.
// Currently this is always a global LIST_ENTRY in ntdll
// and so is at the same address in all processes since ntdll
// is mapped at the same address in every process. This
// means we can call it in our process and get a pointer
// that's valid in the process being dumped.
//
// We also use the presence of RGFTLH as a signal of
// whether dynamic function tables are supported or not.
//
m_FuncTableHead = NULL; m_FuncTable = NULL; if (m_RtlGetFunctionTableListHead) {
PLIST_ENTRY HeadAddr, HeadFirst; HeadAddr = m_RtlGetFunctionTableListHead(); if (ReadProcessMemory(Process, HeadAddr, &HeadFirst, sizeof(HeadFirst), &Done) && Done == sizeof(HeadFirst)) { m_FuncTableHead = HeadAddr; m_FuncTable = HeadFirst; } }
//
// On systems that support unload traces
// ntdll exports a function called RtlGetUnloadEventTrace
// to retrieve the base of an unload trace array.
// Currently this is always a global in ntdll
// and so is at the same address in all processes since ntdll
// is mapped at the same address in every process. This
// means we can call it in our process and get a pointer
// that's valid in the process being dumped.
//
// We also use the presence of RGUET as a signal of
// whether unload traces are supported or not.
//
m_Unloads = NULL; m_Unload = NULL; m_NumUnloads = 0; m_UnloadArraySize = 0; if (m_RtlGetUnloadEventTrace) { PRTL_UNLOAD_EVENT_TRACE TraceAddr; ULONG Entries;
TraceAddr = m_RtlGetUnloadEventTrace(); // Currently there are always 16 entries.
Entries = 16; m_UnloadArraySize = Entries;
m_Unloads = (PRTL_UNLOAD_EVENT_TRACE) HeapAlloc(GetProcessHeap(), 0, sizeof(*TraceAddr) * Entries); if (m_Unloads && ReadProcessMemory(Process, TraceAddr, m_Unloads, sizeof(*TraceAddr) * Entries, &Done) && Done == sizeof(*TraceAddr) * Entries) { PRTL_UNLOAD_EVENT_TRACE Oldest;
//
// Find the true number of entries in use and sort.
// The sequence numbers of the trace records increase with
// time and we want to have the head of the list be the
// most recent record, so sort by decreasing sequence number.
// We know that the array is a circular buffer, so things
// are already in order except there may be a transition
// of sequence after the newest record. Find that transition
// and sorting becomes trivial.
//
Oldest = m_Unloads; for (ULONG i = 0; i < Entries; i++) {
if (!m_Unloads[i].BaseAddress || !m_Unloads[i].SizeOfImage) { // Unused entry, no need to continue.
Entries = i; break; }
if (m_Unloads[i].Sequence < Oldest->Sequence) { Oldest = m_Unloads + i; } }
m_Unload = Oldest; m_NumUnloads = Entries; } }
return S_OK; }
HRESULT NtWin32LiveSystemProvider::EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars) { HRESULT Status; if ((Status = Win32LiveSystemProvider:: EnumModules(Base, Path, PathChars)) != S_OK) { return Status; }
TranslateNtPathName(Path); return S_OK; }
HRESULT NtWin32LiveSystemProvider::EnumFunctionTables(OUT PULONG64 MinAddress, OUT PULONG64 MaxAddress, OUT PULONG64 BaseAddress, OUT PULONG EntryCount, OUT PVOID RawTable, IN ULONG RawTableSize, OUT PVOID* RawEntryHandle) {
#if defined(_AMD64_) || defined(_IA64_)
if (RawTableSize != sizeof(DYNAMIC_FUNCTION_TABLE)) { return E_INVALIDARG; } Restart: if (!m_FuncTable || m_FuncTable == m_FuncTableHead) { return S_FALSE; }
PDYNAMIC_FUNCTION_TABLE TableAddr = #ifdef _AMD64_
CONTAINING_RECORD(m_FuncTable, DYNAMIC_FUNCTION_TABLE, ListEntry); #else
CONTAINING_RECORD(m_FuncTable, DYNAMIC_FUNCTION_TABLE, Links); #endif
PDYNAMIC_FUNCTION_TABLE Table = (PDYNAMIC_FUNCTION_TABLE)RawTable; SIZE_T Done;
if (!ReadProcessMemory(m_ProcessHandle, TableAddr, Table, sizeof(*Table), &Done)) { return WIN32_LAST_STATUS(); } if (Done != sizeof(*Table)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); }
#ifdef _AMD64_
m_FuncTable = Table->ListEntry.Flink; #else
m_FuncTable = Table->Links.Flink; #endif
*MinAddress = (LONG_PTR)Table->MinimumAddress; *MaxAddress = (LONG_PTR)Table->MaximumAddress; *BaseAddress = (LONG_PTR)Table->BaseAddress; *RawEntryHandle = NULL;
//
// AMD64 and IA64 support a type of function table
// where the data is retrieved via a callback rather
// than being is a plain data table. In order to
// get at the data from out-of-process the table
// must have an out-of-process access DLL registered.
//
if (Table->Type == RF_CALLBACK) {
WCHAR DllName[MAX_PATH]; HMODULE OopDll; POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK OopCb;
if (!m_RtlFreeHeap) { return E_NOTIMPL; } if (!Table->OutOfProcessCallbackDll) { // No out-of-process access is possible.
goto Restart; }
if (!ReadProcessMemory(m_ProcessHandle, Table->OutOfProcessCallbackDll, DllName, sizeof(DllName) - sizeof(WCHAR), &Done)) { goto Restart; }
DllName[Done / sizeof(WCHAR)] = 0;
OopDll = LoadLibraryW(DllName); if (!OopDll) { goto Restart; }
OopCb = (POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)GetProcAddress (OopDll, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME); if (OopCb == NULL) { FreeLibrary(OopDll); goto Restart; }
if (!NT_SUCCESS(OopCb(m_ProcessHandle, TableAddr, EntryCount, (PRUNTIME_FUNCTION*)RawEntryHandle))) { FreeLibrary(OopDll); goto Restart; }
FreeLibrary(OopDll); } else { *EntryCount = Table->EntryCount; }
return S_OK;
#else
return S_FALSE;
#endif
}
HRESULT NtWin32LiveSystemProvider::EnumFunctionTableEntries(IN PVOID RawTable, IN ULONG RawTableSize, IN PVOID RawEntryHandle, OUT PVOID RawEntries, IN ULONG RawEntriesSize) { #if defined(_AMD64_) || defined(_IA64_)
if (RawTableSize != sizeof(DYNAMIC_FUNCTION_TABLE)) { return E_INVALIDARG; } PDYNAMIC_FUNCTION_TABLE Table = (PDYNAMIC_FUNCTION_TABLE)RawTable;
if (Table->Type == RF_CALLBACK) { memcpy(RawEntries, RawEntryHandle, RawEntriesSize); m_RtlFreeHeap(RtlProcessHeap(), 0, RawEntryHandle); } else { SIZE_T Done; if (!ReadProcessMemory(m_ProcessHandle, Table->FunctionTable, RawEntries, RawEntriesSize, &Done)) { return WIN32_LAST_STATUS(); } if (Done != RawEntriesSize) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } }
return S_OK;
#else
return E_NOTIMPL; #endif
}
HRESULT NtWin32LiveSystemProvider::EnumFunctionTableEntryMemory(IN ULONG64 TableBase, IN PVOID RawEntries, IN ULONG Index, OUT PULONG64 Start, OUT PULONG Size) { #if defined(_IA64_) || defined(_AMD64_)
UNWIND_INFO Info; PRUNTIME_FUNCTION FuncEnt = (PRUNTIME_FUNCTION)RawEntries + Index; SIZE_T Done; #endif
#if defined(_IA64_)
*Start = TableBase + FuncEnt->UnwindInfoAddress; if (!ReadProcessMemory(m_ProcessHandle, (PVOID)(ULONG_PTR)*Start, &Info, sizeof(Info), &Done)) { return WIN32_LAST_STATUS(); } if (Done != sizeof(Info)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } *Size = sizeof(Info) + Info.DataLength * sizeof(ULONG64); #elif defined(_AMD64_)
*Start = TableBase + FuncEnt->UnwindData; if (!ReadProcessMemory(m_ProcessHandle, (PVOID)(ULONG_PTR)*Start, &Info, sizeof(Info), &Done)) { return WIN32_LAST_STATUS(); } if (Done != sizeof(Info)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } *Size = sizeof(Info) + (Info.CountOfCodes - 1) * sizeof(UNWIND_CODE); // An extra alignment code and pointer may be added on to handle
// the chained info case where the chain pointer is just
// beyond the end of the normal code array.
if ((Info.Flags & UNW_FLAG_CHAININFO) != 0) { if ((Info.CountOfCodes & 1) != 0) { (*Size) += sizeof(UNWIND_CODE); } (*Size) += sizeof(ULONG64); } #endif
#if defined(_IA64_) || defined(_AMD64_)
return S_OK; #else
return E_NOTIMPL; #endif
}
HRESULT NtWin32LiveSystemProvider::EnumUnloadedModules(OUT PWSTR Path, IN ULONG PathChars, OUT PULONG64 BaseOfModule, OUT PULONG SizeOfModule, OUT PULONG CheckSum, OUT PULONG TimeDateStamp) { if (m_NumUnloads == 0) { return S_FALSE; }
GenStrCopyNW(Path, m_Unload->ImageName, PathChars); *BaseOfModule = (LONG_PTR)m_Unload->BaseAddress; *SizeOfModule = (ULONG)m_Unload->SizeOfImage; *CheckSum = m_Unload->CheckSum; *TimeDateStamp = m_Unload->TimeDateStamp;
if (m_Unload == m_Unloads + (m_UnloadArraySize - 1)) { m_Unload = m_Unloads; } else { m_Unload++; } m_NumUnloads--;
return S_OK; }
void NtWin32LiveSystemProvider::FinishProcessEnum(void) { if (m_Unloads) { HeapFree(GetProcessHeap(), 0, m_Unloads); m_Unloads = NULL; } Win32LiveSystemProvider::FinishProcessEnum(); }
HRESULT NtWin32LiveSystemProvider::StartHandleEnum(IN HANDLE Process, IN ULONG ProcessId, OUT PULONG Count) { NTSTATUS NtStatus; if (!m_NtQueryInformationProcess || !m_NtQueryObject) { return E_NOTIMPL; } NtStatus = m_NtQueryInformationProcess(Process, ProcessHandleCount, Count, sizeof(*Count), NULL); if (!NT_SUCCESS(NtStatus)) { return HRESULT_FROM_NT(NtStatus); }
m_ProcessHandle = Process ; m_Handle = 4; return S_OK; }
HRESULT NtWin32LiveSystemProvider::EnumHandles(OUT PULONG64 Handle, OUT PULONG Attributes, OUT PULONG GrantedAccess, OUT PULONG HandleCount, OUT PULONG PointerCount, OUT PWSTR TypeName, IN ULONG TypeNameChars, OUT PWSTR ObjectName, IN ULONG ObjectNameChars) { #ifdef _WIN32_WCE
return E_NOTIMPL; #else
NTSTATUS NtStatus; ULONG64 Buffer[1024 / sizeof(ULONG64)]; POBJECT_TYPE_INFORMATION TypeInfo = (POBJECT_TYPE_INFORMATION)Buffer; POBJECT_NAME_INFORMATION NameInfo = (POBJECT_NAME_INFORMATION)Buffer; OBJECT_BASIC_INFORMATION BasicInfo; HANDLE Dup; ULONG Len;
for (;;) { if (m_Handle >= (1 << 24)) { return S_FALSE; } if (::DuplicateHandle(m_ProcessHandle, UlongToHandle(m_Handle), GetCurrentProcess(), &Dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
// If we can't get the basic info and type there isn't much
// point in writing anything out so skip the handle.
if (NT_SUCCESS(m_NtQueryObject(Dup, ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL)) && NT_SUCCESS(m_NtQueryObject(Dup, ObjectTypeInformation, TypeInfo, sizeof(Buffer), NULL))) { break; } ::CloseHandle(Dup); } m_Handle += 4; } Len = TypeInfo->TypeName.Length; if (Len > (TypeNameChars - 1) * sizeof(*TypeName)) { Len = (TypeNameChars - 1) * sizeof(*TypeName); } memcpy(TypeName, TypeInfo->TypeName.Buffer, Len); TypeName[Len / sizeof(*TypeName)] = 0;
// Don't get the name of file objects as it
// can cause deadlocks. If we fail getting the
// name just leave it out and don't consider it fatal.
if (GenStrCompareW(TypeName, L"File") && NT_SUCCESS(m_NtQueryObject(Dup, ObjectNameInformation, NameInfo, sizeof(Buffer), NULL)) && NameInfo->Name.Buffer != NULL) {
Len = NameInfo->Name.Length; if (Len > (ObjectNameChars - 1) * sizeof(*ObjectName)) { Len = (ObjectNameChars - 1) * sizeof(*ObjectName); } memcpy(ObjectName, NameInfo->Name.Buffer, Len); ObjectName[Len / sizeof(*ObjectName)] = 0; } else { ObjectName[0] = 0; }
*Handle = m_Handle; *Attributes = BasicInfo.Attributes; *GrantedAccess = BasicInfo.GrantedAccess; *HandleCount = BasicInfo.HandleCount; *PointerCount = BasicInfo.PointerCount;
::CloseHandle(Dup); m_Handle += 4; return S_OK; #endif // #ifdef _WIN32_WCE
}
void NtWin32LiveSystemProvider::FinishHandleEnum(void) { // Nothing to do.
}
HRESULT NtWin32LiveSystemProvider::EnumPebMemory(IN HANDLE Process, IN ULONG64 PebOffset, IN ULONG PebSize, IN MiniDumpProviderCallbacks* Callback) { HRESULT Status; PEB Peb;
if (PebSize > sizeof(Peb)) { PebSize = sizeof(Peb); } if ((Status = ReadAllVirtual(Process, PebOffset, &Peb, PebSize)) != S_OK) { return Status; } //
// Save the process parameters.
//
RTL_USER_PROCESS_PARAMETERS Params;
if (Peb.ProcessParameters && ReadAllVirtual(Process, (LONG_PTR)Peb.ProcessParameters, &Params, sizeof(Params)) == S_OK) {
Callback->EnumMemory((LONG_PTR)Peb.ProcessParameters, sizeof(Params));
EnumUnicodeString(&Params.CurrentDirectory.DosPath, Callback); EnumUnicodeString(&Params.DllPath, Callback); EnumUnicodeString(&Params.ImagePathName, Callback); EnumUnicodeString(&Params.CommandLine, Callback); // There's no indicator of how big the environment is,
// so just save an arbitrary amount.
Callback->EnumMemory((LONG_PTR)Params.Environment, 8192); EnumUnicodeString(&Params.WindowTitle, Callback); EnumUnicodeString(&Params.DesktopInfo, Callback); EnumUnicodeString(&Params.ShellInfo, Callback); EnumUnicodeString(&Params.RuntimeData, Callback); }
return S_OK; }
HRESULT NtWin32LiveSystemProvider::EnumTebMemory(IN HANDLE Process, IN HANDLE Thread, IN ULONG64 TebOffset, IN ULONG TebSize, IN MiniDumpProviderCallbacks* Callback) { HRESULT Status; TEB Teb; if (TebSize > sizeof(Teb)) { TebSize = sizeof(Teb); } if ((Status = ReadAllVirtual(Process, TebOffset, &Teb, TebSize)) != S_OK) { return Status; } //
// Save any TLS expansion.
//
if (m_BuildNumber >= NT_BUILD_WIN2K && Teb.TlsExpansionSlots) { Callback->EnumMemory((LONG_PTR)Teb.TlsExpansionSlots, TLS_EXPANSION_SLOTS * sizeof(ULONG_PTR)); }
//
// Save FLS data.
//
if (m_BuildNumber > NT_BUILD_XP && Teb.FlsData) { Callback->EnumMemory((LONG_PTR)Teb.FlsData, (FLS_MAXIMUM_AVAILABLE + 2) * sizeof(ULONG_PTR)); } return S_OK; }
void NtWin32LiveSystemProvider::TranslateNtPathName(IN OUT PWSTR Path) { if (Path[0] == L'\\' && Path[1] == L'?' && Path[2] == L'?' && Path[3] == L'\\') { ULONG Len = (GenStrLengthW(Path) + 1) * sizeof(*Path); if (Path[4] == L'U' && Path[5] == L'N' && Path[6] == L'C' && Path[7] == L'\\') { // Compress \??\UNC\ to \\.
memmove(Path + 1, Path + 7, Len - 7 * sizeof(*Path)); } else { // Remove \??\.
memmove(Path, Path + 4, Len - 4 * sizeof(*Path)); } } }
//----------------------------------------------------------------------------
//
// NtEnumModWin32LiveSystemProvider.
//
//----------------------------------------------------------------------------
class NtEnumModWin32LiveSystemProvider : public NtWin32LiveSystemProvider { public: NtEnumModWin32LiveSystemProvider(ULONG BuildNumber); ~NtEnumModWin32LiveSystemProvider(void);
virtual void Release(void); virtual HRESULT StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId); virtual HRESULT EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars); virtual void FinishProcessEnum(void); protected: HMODULE* m_ProcModules; ULONG m_NumProcModules; };
NtEnumModWin32LiveSystemProvider:: NtEnumModWin32LiveSystemProvider(ULONG BuildNumber) : NtWin32LiveSystemProvider(BuildNumber) { m_ProcModules = NULL; m_NumProcModules = 0; }
NtEnumModWin32LiveSystemProvider::~NtEnumModWin32LiveSystemProvider(void) { if (m_ProcModules) { HeapFree(GetProcessHeap(), 0, m_ProcModules); } }
void NtEnumModWin32LiveSystemProvider::Release(void) { delete this; }
HRESULT NtEnumModWin32LiveSystemProvider::StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId) { HRESULT Status;
if (!m_EnumProcessModules || !m_GetModuleFileNameExW) { return E_NOTIMPL; } if ((Status = NtWin32LiveSystemProvider:: StartProcessEnum(Process, ProcessId)) != S_OK) { return Status; }
ULONG ProcModSize = 16384;
m_ProcModules = (HMODULE*)HeapAlloc(GetProcessHeap(), 0, ProcModSize); if (!m_ProcModules) { NtWin32LiveSystemProvider::FinishProcessEnum(); return E_OUTOFMEMORY; }
//
// Walk module list, getting module information. Use PSAPI instead of
// toolhelp since it it does not exhibit the deadlock issues with
// the loader lock. ( on old versions of os )
//
ULONG Needed; if (!m_EnumProcessModules(Process, m_ProcModules, ProcModSize, &Needed)) { Status = WIN32_LAST_STATUS(); NtWin32LiveSystemProvider::FinishProcessEnum(); HeapFree(GetProcessHeap(), 0, m_ProcModules); m_ProcModules = NULL; return Status; }
m_NumProcModules = Needed / sizeof(m_ProcModules[0]); return S_OK; }
HRESULT NtEnumModWin32LiveSystemProvider::EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars) { HRESULT Status;
if (m_ModuleIndex >= m_NumProcModules) { return S_FALSE; }
*Base = (LONG_PTR)m_ProcModules[m_ModuleIndex]; if (!m_GetModuleFileNameExW(m_ProcessHandle, m_ProcModules[m_ModuleIndex], Path, PathChars)) { return WIN32_LAST_STATUS(); }
TranslateNtPathName(Path); m_ModuleIndex++; return S_OK; }
void NtEnumModWin32LiveSystemProvider::FinishProcessEnum(void) { HeapFree(GetProcessHeap(), 0, m_ProcModules); m_ProcModules = NULL; NtWin32LiveSystemProvider::FinishProcessEnum(); }
//----------------------------------------------------------------------------
//
// Nt4Win32LiveSystemProvider.
//
//----------------------------------------------------------------------------
class Nt4Win32LiveSystemProvider : public NtWin32LiveSystemProvider { public: Nt4Win32LiveSystemProvider(ULONG BuildNumber); ~Nt4Win32LiveSystemProvider(void);
virtual void Release(void); virtual HRESULT StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId); virtual HRESULT EnumThreads(OUT PULONG ThreadId); virtual HRESULT EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars); virtual void FinishProcessEnum(void); protected: PLIST_ENTRY m_LdrHead; PLIST_ENTRY m_LdrEntry; PNT4_SYSTEM_PROCESS_INFORMATION m_NtProcessInfo; PNT4_SYSTEM_THREAD_INFORMATION m_NtThread; ULONG m_NtThreads; };
Nt4Win32LiveSystemProvider:: Nt4Win32LiveSystemProvider(ULONG BuildNumber) : NtWin32LiveSystemProvider(BuildNumber) { m_NtProcessInfo = NULL; }
Nt4Win32LiveSystemProvider::~Nt4Win32LiveSystemProvider(void) { if (m_NtProcessInfo) { HeapFree(GetProcessHeap(), 0, m_NtProcessInfo); } }
void Nt4Win32LiveSystemProvider::Release(void) { delete this; }
HRESULT Nt4Win32LiveSystemProvider::StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId) { NTSTATUS NtStatus;
if (!m_NtQuerySystemInformation || !m_NtQueryInformationProcess) { return E_NOTIMPL; }
//
// Get the head of the loaded module list.
// Some system processes have no PEB and it's
// possible for a process to not have a loader list.
//
NT4_PROCESS_BASIC_INFORMATION BasicInfo; PNT4_PEB Peb; PNT4_PEB_LDR_DATA Ldr; SIZE_T Done;
NtStatus = m_NtQueryInformationProcess(Process, Nt4ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL); if (!NT_SUCCESS(NtStatus)) { return HRESULT_FROM_NT(NtStatus); }
m_LdrHead = NULL; m_LdrEntry = NULL; Peb = BasicInfo.PebBaseAddress; if (Peb) {
if (!ReadProcessMemory(Process, &Peb->Ldr, &Ldr, sizeof(Ldr), &Done)) { return WIN32_LAST_STATUS(); } if (Done < sizeof(Ldr)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); }
if (Ldr) { m_LdrHead = &Ldr->InMemoryOrderModuleList; if (!ReadProcessMemory(Process, &m_LdrHead->Flink, &m_LdrEntry, sizeof(m_LdrEntry), &Done)) { return WIN32_LAST_STATUS(); } if (Done < sizeof(m_LdrEntry)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } } } //
// Snap the set of threads in the process.
//
ULONG ProcInfoSize = 65536;
do {
if (m_NtProcessInfo) { HeapFree(GetProcessHeap(), 0, m_NtProcessInfo); } m_NtProcessInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) HeapAlloc(GetProcessHeap(), 0, ProcInfoSize); if (!m_NtProcessInfo) { return E_OUTOFMEMORY; } NtStatus = m_NtQuerySystemInformation(Nt4SystemProcessInformation, m_NtProcessInfo, ProcInfoSize, NULL); if (NT_SUCCESS(NtStatus)) { break; } else if (NtStatus != STATUS_INFO_LENGTH_MISMATCH) { HeapFree(GetProcessHeap(), 0, m_NtProcessInfo); m_NtProcessInfo = NULL; return HRESULT_FROM_NT(NtStatus); }
ProcInfoSize += 16384;
} while (NtStatus == STATUS_INFO_LENGTH_MISMATCH);
//
// Find the correct process in the process list.
//
PNT4_SYSTEM_PROCESS_INFORMATION ProcInfo = m_NtProcessInfo;
while (ProcInfo->NextEntryOffset && ProcInfo->UniqueProcessId != (HANDLE)(ULONG_PTR)ProcessId) {
ProcInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) ((ULONG_PTR)ProcInfo + ProcInfo->NextEntryOffset); }
if (ProcInfo->UniqueProcessId != (HANDLE)(ULONG_PTR)ProcessId) { // Could not find a matching process in the process list.
HeapFree(GetProcessHeap(), 0, m_NtProcessInfo); m_NtProcessInfo = NULL; return E_NOINTERFACE; } m_NtThread = (PNT4_SYSTEM_THREAD_INFORMATION)(ProcInfo + 1); m_NtThreads = ProcInfo->NumberOfThreads;
// Don't support function tables for NT4.
m_FuncTableHead = NULL; m_FuncTable = NULL;
// NT4 doesn't have an unloaded module list.
m_Unloads = NULL; m_Unload = NULL; m_NumUnloads = 0; m_UnloadArraySize = 0;
m_ProcessHandle = Process; m_ProcessId = ProcessId; return S_OK; }
HRESULT Nt4Win32LiveSystemProvider::EnumThreads(OUT PULONG ThreadId) { if (m_NtThreads == 0) { return S_FALSE; }
*ThreadId = (ULONG)(ULONG_PTR)m_NtThread->ClientId.UniqueThread; m_NtThread++; m_NtThreads--; return S_OK; }
HRESULT Nt4Win32LiveSystemProvider::EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars) { HRESULT Status;
if (!m_LdrEntry || m_LdrEntry == m_LdrHead) { return S_FALSE; }
PNT4_LDR_DATA_TABLE_ENTRY LdrEntry; NT4_LDR_DATA_TABLE_ENTRY LdrEntryData; SIZE_T Done;
LdrEntry = CONTAINING_RECORD(m_LdrEntry, NT4_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (!ReadProcessMemory(m_ProcessHandle, LdrEntry, &LdrEntryData, sizeof(LdrEntryData), &Done)) { return WIN32_LAST_STATUS(); } if (Done < sizeof(LdrEntryData)) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); }
*Base = (LONG_PTR)LdrEntryData.DllBase; if (PathChars) { ULONG Read = LdrEntryData.FullDllName.Length; PathChars = (PathChars - 1) * sizeof(Path[0]); if (Read > PathChars) { Read = PathChars; } if (Read) { if (!ReadProcessMemory(m_ProcessHandle, LdrEntryData.FullDllName.Buffer, Path, Read, &Done)) { return WIN32_LAST_STATUS(); } if (Done < Read) { return HRESULT_FROM_WIN32(ERROR_READ_FAULT); } } Path[Read / sizeof(Path[0])] = 0; } TranslateNtPathName(Path); m_LdrEntry = LdrEntryData.InMemoryOrderLinks.Flink; return S_OK; }
void Nt4Win32LiveSystemProvider::FinishProcessEnum(void) { HeapFree(GetProcessHeap(), 0, m_NtProcessInfo); m_NtProcessInfo = NULL; }
//----------------------------------------------------------------------------
//
// NewNtWin32LiveSystemProvider.
//
//----------------------------------------------------------------------------
Win32LiveSystemProvider* NewNtWin32LiveSystemProvider(ULONG BuildNumber) { if (BuildNumber < NT_BUILD_WIN2K) { return new Nt4Win32LiveSystemProvider(BuildNumber); } else if (BuildNumber < NT_BUILD_TH_MODULES) { return new NtEnumModWin32LiveSystemProvider(BuildNumber); } else { return new NtWin32LiveSystemProvider(BuildNumber); } }
|