You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1327 lines
42 KiB
1327 lines
42 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
}
|