912 lines
26 KiB
C
912 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright(c) 1999-2002 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ntx.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Minidump user-mode crashdump NT specific functions. These routines work on
|
|||
|
NT-based operating systems from NT5 on.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Matthew D Hendel (math) 20-Aug-1999
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
#include "impl.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PINTERNAL_MODULE
|
|||
|
NtxAllocateModuleObject(
|
|||
|
IN PINTERNAL_PROCESS Process,
|
|||
|
IN HANDLE ProcessHandle,
|
|||
|
IN ULONG_PTR BaseOfModule,
|
|||
|
IN ULONG DumpType,
|
|||
|
IN ULONG WriteFlags,
|
|||
|
IN PWSTR ModuleName OPTIONAL
|
|||
|
)
|
|||
|
{
|
|||
|
WCHAR FullPath [ MAX_PATH + 10 ];
|
|||
|
|
|||
|
//
|
|||
|
// The basic LdrQueryProcessModule API that toolhelp uses
|
|||
|
// always returns ANSI strings for module paths. This
|
|||
|
// means that even if you use the wide toolhelp calls
|
|||
|
// you still lose Unicode information because the original
|
|||
|
// Unicode path was converted to ANSI and then back to Unicode.
|
|||
|
// To avoid this problem, always try and look up the true
|
|||
|
// Unicode path first. This doesn't work for 32-bit modules
|
|||
|
// in WOW64, though, so if there's a failure just use the
|
|||
|
// incoming string.
|
|||
|
//
|
|||
|
|
|||
|
if (GetModuleFileNameExW(ProcessHandle,
|
|||
|
(HMODULE) BaseOfModule,
|
|||
|
FullPath,
|
|||
|
sizeof (FullPath))) {
|
|||
|
ModuleName = FullPath;
|
|||
|
} else if (!ModuleName) {
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Translate funky \??\... module name.
|
|||
|
//
|
|||
|
|
|||
|
return GenAllocateModuleObject (Process, ModuleName, BaseOfModule,
|
|||
|
DumpType, WriteFlags);
|
|||
|
}
|
|||
|
|
|||
|
typedef
|
|||
|
PLIST_ENTRY
|
|||
|
(*FN_RtlGetFunctionTableListHead) (
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
NtxGetFunctionTables(
|
|||
|
IN HANDLE hProcess,
|
|||
|
IN PINTERNAL_PROCESS Process,
|
|||
|
IN ULONG DumpType
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef _WIN32_WCE
|
|||
|
return FALSE;
|
|||
|
#else
|
|||
|
HMODULE NtDll;
|
|||
|
FN_RtlGetFunctionTableListHead GetHead = NULL;
|
|||
|
PLIST_ENTRY HeadAddr;
|
|||
|
LIST_ENTRY Head;
|
|||
|
PVOID Next;
|
|||
|
SIZE_T Done;
|
|||
|
DYNAMIC_FUNCTION_TABLE Table;
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
NtDll = GetModuleHandle("ntdll");
|
|||
|
if (NtDll) {
|
|||
|
GetHead = (FN_RtlGetFunctionTableListHead)
|
|||
|
GetProcAddress(NtDll, "RtlGetFunctionTableListHead");
|
|||
|
}
|
|||
|
if (!GetHead) {
|
|||
|
// Dynamic function tables are not supported.
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
HeadAddr = GetHead();
|
|||
|
if (!ReadProcessMemory(hProcess, HeadAddr, &Head, sizeof(Head),
|
|||
|
&Done) || Done != sizeof(Head)) {
|
|||
|
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Next = Head.Flink;
|
|||
|
while (Next && Next != HeadAddr) {
|
|||
|
|
|||
|
PINTERNAL_FUNCTION_TABLE IntTable;
|
|||
|
PVOID HeapEntries;
|
|||
|
PVOID TableAddr;
|
|||
|
ULONG EntryCount;
|
|||
|
|
|||
|
TableAddr = Next;
|
|||
|
|
|||
|
if (!ReadProcessMemory(hProcess, TableAddr, &Table, sizeof(Table),
|
|||
|
&Done) || Done != sizeof(Table)) {
|
|||
|
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
#ifdef _AMD64_
|
|||
|
Next = Table.ListEntry.Flink;
|
|||
|
#else
|
|||
|
Next = Table.Links.Flink;
|
|||
|
#endif
|
|||
|
|
|||
|
HeapEntries = NULL;
|
|||
|
|
|||
|
#if defined(_AMD64_) || defined(_IA64_)
|
|||
|
|
|||
|
//
|
|||
|
// 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 (!Table.OutOfProcessCallbackDll) {
|
|||
|
// No out-of-process access is possible.
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (!ReadProcessMemory(hProcess, Table.OutOfProcessCallbackDll,
|
|||
|
DllName, sizeof(DllName) - sizeof(WCHAR),
|
|||
|
&Done)) {
|
|||
|
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
DllName[Done / sizeof(WCHAR)] = 0;
|
|||
|
|
|||
|
OopDll = LoadLibraryW(DllName);
|
|||
|
if (!OopDll) {
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
OopCb = (POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)GetProcAddress
|
|||
|
(OopDll, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME);
|
|||
|
if (OopCb == NULL) {
|
|||
|
FreeLibrary(OopDll);
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(OopCb(hProcess, TableAddr,
|
|||
|
&EntryCount,
|
|||
|
(PRUNTIME_FUNCTION*)&HeapEntries))) {
|
|||
|
FreeLibrary(OopDll);
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FreeLibrary(OopDll);
|
|||
|
} else {
|
|||
|
EntryCount = Table.EntryCount;
|
|||
|
}
|
|||
|
|
|||
|
#else
|
|||
|
EntryCount = Table.EntryCount;
|
|||
|
#endif
|
|||
|
|
|||
|
IntTable = GenAllocateFunctionTableObject(Table.MinimumAddress,
|
|||
|
Table.MaximumAddress,
|
|||
|
#ifdef _ALPHA_
|
|||
|
Table.MinimumAddress,
|
|||
|
#else
|
|||
|
Table.BaseAddress,
|
|||
|
#endif
|
|||
|
EntryCount,
|
|||
|
&Table);
|
|||
|
if (IntTable) {
|
|||
|
|
|||
|
#if defined(_AMD64_) || defined(_IA64_)
|
|||
|
|
|||
|
if (Table.Type == RF_CALLBACK) {
|
|||
|
memcpy(IntTable->RawEntries, HeapEntries,
|
|||
|
EntryCount * sizeof(RUNTIME_FUNCTION));
|
|||
|
} else
|
|||
|
#endif
|
|||
|
{
|
|||
|
if (!ReadProcessMemory(hProcess, Table.FunctionTable,
|
|||
|
IntTable->RawEntries,
|
|||
|
EntryCount * sizeof(RUNTIME_FUNCTION),
|
|||
|
&Done) ||
|
|||
|
Done != EntryCount * sizeof(RUNTIME_FUNCTION)) {
|
|||
|
GenFreeFunctionTableObject(IntTable);
|
|||
|
IntTable = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (HeapEntries) {
|
|||
|
RtlFreeHeap(RtlProcessHeap(), 0, HeapEntries);
|
|||
|
}
|
|||
|
|
|||
|
if (!IntTable) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!GenIncludeUnwindInfoMemory(hProcess, DumpType, IntTable)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Process->NumberOfFunctionTables++;
|
|||
|
InsertTailList(&Process->FunctionTableList, &IntTable->TableLink);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
#endif // _WIN32_WCE
|
|||
|
}
|
|||
|
|
|||
|
#ifdef RTL_UNLOAD_EVENT_TRACE_NUMBER
|
|||
|
|
|||
|
typedef
|
|||
|
PRTL_UNLOAD_EVENT_TRACE
|
|||
|
(*FN_RtlGetUnloadEventTrace) (
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
BOOL
|
|||
|
NtxGetUnloadedModules(
|
|||
|
IN HANDLE hProcess,
|
|||
|
IN PINTERNAL_PROCESS Process,
|
|||
|
IN ULONG DumpType
|
|||
|
)
|
|||
|
{
|
|||
|
#if defined(_WIN32_WCE) || !defined(RTL_UNLOAD_EVENT_TRACE_NUMBER)
|
|||
|
return FALSE;
|
|||
|
#else
|
|||
|
HMODULE NtDll;
|
|||
|
FN_RtlGetUnloadEventTrace GetTrace = NULL;
|
|||
|
PRTL_UNLOAD_EVENT_TRACE TraceAddr;
|
|||
|
PRTL_UNLOAD_EVENT_TRACE TraceArray;
|
|||
|
SIZE_T Done;
|
|||
|
ULONG Entries;
|
|||
|
PRTL_UNLOAD_EVENT_TRACE Oldest;
|
|||
|
ULONG i;
|
|||
|
PINTERNAL_UNLOADED_MODULE IntModule;
|
|||
|
|
|||
|
if (!(DumpType & MiniDumpWithUnloadedModules)) {
|
|||
|
// No unloaded module info requested.
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
NtDll = GetModuleHandle("ntdll");
|
|||
|
if (NtDll) {
|
|||
|
GetTrace = (FN_RtlGetUnloadEventTrace)
|
|||
|
GetProcAddress(NtDll, "RtlGetUnloadEventTrace");
|
|||
|
}
|
|||
|
if (!GetTrace) {
|
|||
|
// Unload traces are not supported.
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
TraceAddr = GetTrace();
|
|||
|
|
|||
|
// Currently there are always 16 entries.
|
|||
|
Entries = 16;
|
|||
|
|
|||
|
TraceArray = (PRTL_UNLOAD_EVENT_TRACE)
|
|||
|
AllocMemory(sizeof(*TraceArray) * Entries);
|
|||
|
if (!TraceArray) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!ReadProcessMemory(hProcess, TraceAddr,
|
|||
|
TraceArray, sizeof(*TraceArray) * Entries,
|
|||
|
&Done) ||
|
|||
|
Done != sizeof(*TraceArray) * Entries) {
|
|||
|
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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 = TraceArray;
|
|||
|
for (i = 0; i < Entries; i++) {
|
|||
|
|
|||
|
if (!TraceArray[i].BaseAddress || !TraceArray[i].SizeOfImage) {
|
|||
|
// Unused entry, no need to continue.
|
|||
|
Entries = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (TraceArray[i].Sequence < Oldest->Sequence) {
|
|||
|
Oldest = TraceArray + i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now push the entries on from the oldest to the youngest.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < Entries; i++) {
|
|||
|
IntModule =
|
|||
|
GenAllocateUnloadedModuleObject(Oldest->ImageName,
|
|||
|
(ULONG_PTR)Oldest->BaseAddress,
|
|||
|
(ULONG)Oldest->SizeOfImage,
|
|||
|
Oldest->CheckSum,
|
|||
|
Oldest->TimeDateStamp);
|
|||
|
if (!IntModule) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Process->NumberOfUnloadedModules++;
|
|||
|
|
|||
|
InsertHeadList(&Process->UnloadedModuleList, &IntModule->ModulesLink);
|
|||
|
|
|||
|
if (Oldest == TraceArray + (Entries - 1)) {
|
|||
|
Oldest = TraceArray;
|
|||
|
} else {
|
|||
|
Oldest++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
#endif // _WIN32_WCE || !RTL_UNLOAD_EVENT_TRACE_NUMBER
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
NtxGetProcessInfo(
|
|||
|
IN HANDLE hProcess,
|
|||
|
IN ULONG ProcessId,
|
|||
|
IN ULONG DumpType,
|
|||
|
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
|
|||
|
IN PVOID CallbackParam,
|
|||
|
OUT PINTERNAL_PROCESS * ProcessRet
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Using toolhelp, obtain the process information for this process.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
TRUE - Success.
|
|||
|
|
|||
|
FALSE - Failure:
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Win9x/Win2k+ only.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOL Succ;
|
|||
|
ULONG i;
|
|||
|
BOOL MoreThreads;
|
|||
|
HANDLE Snapshot, ModuleSnapshot = INVALID_HANDLE_VALUE;
|
|||
|
THREADENTRY32 ThreadInfo;
|
|||
|
PINTERNAL_THREAD Thread;
|
|||
|
PINTERNAL_PROCESS Process;
|
|||
|
PINTERNAL_MODULE Module;
|
|||
|
HMODULE Modules [ 512 ];
|
|||
|
ULONG ModulesSize;
|
|||
|
ULONG NumberOfModules;
|
|||
|
ULONG BuildNumber;
|
|||
|
|
|||
|
ASSERT ( hProcess );
|
|||
|
ASSERT ( ProcessId != 0 );
|
|||
|
ASSERT ( ProcessRet );
|
|||
|
|
|||
|
Process = NULL;
|
|||
|
Thread = NULL;
|
|||
|
Module = NULL;
|
|||
|
Snapshot = NULL;
|
|||
|
ThreadInfo.dwSize = sizeof (THREADENTRY32);
|
|||
|
|
|||
|
Process = GenAllocateProcessObject ( hProcess, ProcessId );
|
|||
|
|
|||
|
if ( Process == NULL ) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Snapshot = CreateToolhelp32Snapshot (
|
|||
|
TH32CS_SNAPTHREAD,
|
|||
|
ProcessId
|
|||
|
);
|
|||
|
|
|||
|
if ( Snapshot == INVALID_HANDLE_VALUE ) {
|
|||
|
Succ = FALSE;
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Walk thread list, suspending all threads and getting thread info.
|
|||
|
//
|
|||
|
|
|||
|
for (MoreThreads = ProcessThread32First (Snapshot, ProcessId, &ThreadInfo );
|
|||
|
MoreThreads;
|
|||
|
MoreThreads = ProcessThread32Next ( Snapshot, ProcessId, &ThreadInfo ) ) {
|
|||
|
HRESULT Status;
|
|||
|
ULONG WriteFlags;
|
|||
|
|
|||
|
if (!GenExecuteIncludeThreadCallback(hProcess,
|
|||
|
ProcessId,
|
|||
|
DumpType,
|
|||
|
ThreadInfo.th32ThreadID,
|
|||
|
CallbackRoutine,
|
|||
|
CallbackParam,
|
|||
|
&WriteFlags) ||
|
|||
|
IsFlagClear(WriteFlags, ThreadWriteThread)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
Status = GenAllocateThreadObject (
|
|||
|
Process,
|
|||
|
hProcess,
|
|||
|
ThreadInfo.th32ThreadID,
|
|||
|
DumpType,
|
|||
|
WriteFlags,
|
|||
|
&Thread
|
|||
|
);
|
|||
|
|
|||
|
if ( FAILED(Status) ) {
|
|||
|
Succ = FALSE;
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
|
|||
|
// If Status is S_FALSE it means that the thread
|
|||
|
// couldn't be opened and probably exited before
|
|||
|
// we got to it. Just continue on.
|
|||
|
if (Status == S_OK) {
|
|||
|
Process->NumberOfThreads++;
|
|||
|
InsertTailList (&Process->ThreadList, &Thread->ThreadsLink);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
GenGetSystemType (NULL, NULL, NULL, NULL, &BuildNumber);
|
|||
|
if (BuildNumber > 2468) {
|
|||
|
|
|||
|
//
|
|||
|
// toolhelp had been changed to perform noninvasive
|
|||
|
// module enumeration
|
|||
|
//
|
|||
|
|
|||
|
MODULEENTRY32W ModuleEntry;
|
|||
|
BOOL ModuleFound;
|
|||
|
|
|||
|
NumberOfModules = 0;
|
|||
|
Succ = TRUE;
|
|||
|
|
|||
|
ModuleSnapshot = CreateToolhelp32Snapshot(
|
|||
|
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
|
|||
|
ProcessId
|
|||
|
);
|
|||
|
|
|||
|
if (ModuleSnapshot == INVALID_HANDLE_VALUE) {
|
|||
|
Succ = FALSE;
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory(&ModuleEntry, sizeof(ModuleEntry));
|
|||
|
ModuleEntry.dwSize = sizeof(ModuleEntry);
|
|||
|
|
|||
|
ModuleFound = Module32FirstW(ModuleSnapshot, &ModuleEntry);
|
|||
|
while (ModuleFound) {
|
|||
|
ULONG WriteFlags;
|
|||
|
|
|||
|
if (GenExecuteIncludeModuleCallback(hProcess,
|
|||
|
ProcessId,
|
|||
|
DumpType,
|
|||
|
(LONG_PTR)ModuleEntry.modBaseAddr,
|
|||
|
CallbackRoutine,
|
|||
|
CallbackParam,
|
|||
|
&WriteFlags) &&
|
|||
|
IsFlagSet(WriteFlags, ModuleWriteModule)) {
|
|||
|
|
|||
|
Module = NtxAllocateModuleObject (Process,
|
|||
|
Process->ProcessHandle,
|
|||
|
(LONG_PTR)ModuleEntry.modBaseAddr,
|
|||
|
DumpType,
|
|||
|
WriteFlags,
|
|||
|
ModuleEntry.szExePath);
|
|||
|
if ( Module == NULL ) {
|
|||
|
Succ = FALSE;
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
|
|||
|
InsertTailList (&Process->ModuleList, &Module->ModulesLink);
|
|||
|
++NumberOfModules;
|
|||
|
}
|
|||
|
|
|||
|
ModuleFound = Module32NextW(ModuleSnapshot, &ModuleEntry);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// 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 )
|
|||
|
//
|
|||
|
|
|||
|
ModulesSize = 0;
|
|||
|
Succ = EnumProcessModules (
|
|||
|
Process->ProcessHandle,
|
|||
|
Modules,
|
|||
|
sizeof (Modules),
|
|||
|
&ModulesSize
|
|||
|
);
|
|||
|
|
|||
|
if ( !Succ ) {
|
|||
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
|
|||
|
NumberOfModules = ModulesSize / sizeof (HMODULE);
|
|||
|
for (i = 0; i < NumberOfModules; i++) {
|
|||
|
ULONG WriteFlags;
|
|||
|
|
|||
|
if (!GenExecuteIncludeModuleCallback(hProcess,
|
|||
|
ProcessId,
|
|||
|
DumpType,
|
|||
|
(LONG_PTR)Modules[i],
|
|||
|
CallbackRoutine,
|
|||
|
CallbackParam,
|
|||
|
&WriteFlags) ||
|
|||
|
IsFlagClear(WriteFlags, ModuleWriteModule)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
Module = NtxAllocateModuleObject (
|
|||
|
Process,
|
|||
|
Process->ProcessHandle,
|
|||
|
(LONG_PTR) Modules [ i ],
|
|||
|
DumpType,
|
|||
|
WriteFlags,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if ( Module == NULL ) {
|
|||
|
Succ = FALSE;
|
|||
|
goto Exit;
|
|||
|
}
|
|||
|
InsertTailList (&Process->ModuleList, &Module->ModulesLink);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Process->NumberOfModules = NumberOfModules;
|
|||
|
|
|||
|
Succ = NtxGetFunctionTables(hProcess, Process, DumpType);
|
|||
|
|
|||
|
// If we can't get unloaded modules that's not a critical problem.
|
|||
|
NtxGetUnloadedModules(hProcess, Process, DumpType);
|
|||
|
|
|||
|
Exit:
|
|||
|
|
|||
|
if ( Snapshot && (Snapshot != INVALID_HANDLE_VALUE) ) {
|
|||
|
CloseHandle ( Snapshot );
|
|||
|
Snapshot = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if ( ModuleSnapshot && (ModuleSnapshot != INVALID_HANDLE_VALUE) ) {
|
|||
|
CloseHandle ( ModuleSnapshot );
|
|||
|
ModuleSnapshot = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if ( !Succ && Process != NULL ) {
|
|||
|
GenFreeProcessObject ( Process );
|
|||
|
Process = NULL;
|
|||
|
}
|
|||
|
|
|||
|
*ProcessRet = Process;
|
|||
|
|
|||
|
return Succ;
|
|||
|
}
|
|||
|
|
|||
|
LPVOID
|
|||
|
NtxGetTebAddress(
|
|||
|
IN HANDLE Thread,
|
|||
|
OUT PULONG SizeOfTeb
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef _WIN32_WCE
|
|||
|
*SizeOfTeb = 0;
|
|||
|
return NULL;
|
|||
|
#else
|
|||
|
THREAD_BASIC_INFORMATION ThreadInformation;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = 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.
|
|||
|
*SizeOfTeb = PAGE_SIZE;
|
|||
|
return ThreadInformation.TebBaseAddress;
|
|||
|
} else {
|
|||
|
*SizeOfTeb = 0;
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
HRESULT
|
|||
|
TibGetThreadInfo(
|
|||
|
IN HANDLE Process,
|
|||
|
IN LPVOID TibBase,
|
|||
|
OUT PULONG64 StackBase,
|
|||
|
OUT PULONG64 StackLimit,
|
|||
|
OUT PULONG64 StoreBase,
|
|||
|
OUT PULONG64 StoreLimit
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef _WIN32_WCE
|
|||
|
return E_NOTIMPL;
|
|||
|
#else
|
|||
|
TEB Teb;
|
|||
|
HRESULT Succ;
|
|||
|
SIZE_T BytesRead;
|
|||
|
|
|||
|
#if defined (DUMP_BACKING_STORE)
|
|||
|
|
|||
|
Succ = ReadProcessMemory(Process,
|
|||
|
TibBase,
|
|||
|
&Teb,
|
|||
|
sizeof (Teb),
|
|||
|
&BytesRead) ? S_OK : E_FAIL;
|
|||
|
if ( Succ != S_OK || BytesRead != sizeof (Teb) ) {
|
|||
|
return E_FAIL;
|
|||
|
}
|
|||
|
|
|||
|
*StoreBase = SIGN_EXTEND(BSTORE_BASE(&Teb));
|
|||
|
*StoreLimit = SIGN_EXTEND(BSTORE_LIMIT(&Teb));
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
Succ = ReadProcessMemory(Process,
|
|||
|
TibBase,
|
|||
|
&Teb,
|
|||
|
sizeof (Teb.NtTib),
|
|||
|
&BytesRead) ? S_OK : E_FAIL;
|
|||
|
if ( Succ != S_OK || BytesRead != sizeof (Teb.NtTib) ) {
|
|||
|
return E_FAIL;
|
|||
|
}
|
|||
|
|
|||
|
*StoreBase = 0;
|
|||
|
*StoreLimit = 0;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
*StackBase = SIGN_EXTEND((LONG_PTR)Teb.NtTib.StackBase);
|
|||
|
*StackLimit = SIGN_EXTEND((LONG_PTR)Teb.NtTib.StackLimit);
|
|||
|
|
|||
|
return S_OK;
|
|||
|
#endif // #ifdef _WIN32_WCE
|
|||
|
}
|
|||
|
|
|||
|
LPVOID
|
|||
|
NtxGetPebAddress(
|
|||
|
IN HANDLE Process,
|
|||
|
OUT PULONG SizeOfPeb
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef _WIN32_WCE
|
|||
|
*SizeOfPeb = 0;
|
|||
|
return NULL;
|
|||
|
#else
|
|||
|
PROCESS_BASIC_INFORMATION Information;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = NtQueryInformationProcess(Process,
|
|||
|
ProcessBasicInformation,
|
|||
|
&Information,
|
|||
|
sizeof(Information),
|
|||
|
NULL);
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
*SizeOfPeb = sizeof(PEB);
|
|||
|
return Information.PebBaseAddress;
|
|||
|
} else {
|
|||
|
*SizeOfPeb = 0;
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
NtxWriteHandleData(
|
|||
|
IN HANDLE ProcessHandle,
|
|||
|
IN HANDLE hFile,
|
|||
|
IN struct _MINIDUMP_STREAM_INFO * StreamInfo
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef _WIN32_WCE
|
|||
|
return FALSE;
|
|||
|
#else
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ULONG HandleCount;
|
|||
|
ULONG Hits;
|
|||
|
ULONG Handle;
|
|||
|
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;
|
|||
|
PMINIDUMP_HANDLE_DESCRIPTOR Descs, Desc;
|
|||
|
RVA Rva;
|
|||
|
ULONG32 Len;
|
|||
|
ULONG Done;
|
|||
|
MINIDUMP_HANDLE_DATA_STREAM DataStream;
|
|||
|
BOOL Succ;
|
|||
|
|
|||
|
NtStatus = NtQueryInformationProcess(ProcessHandle,
|
|||
|
ProcessHandleCount,
|
|||
|
&HandleCount,
|
|||
|
sizeof(HandleCount),
|
|||
|
NULL);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Descs = AllocMemory(HandleCount * sizeof(*Desc));
|
|||
|
if (Descs == NULL) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Hits = 0;
|
|||
|
Handle = 0;
|
|||
|
Desc = Descs;
|
|||
|
Rva = StreamInfo->RvaOfHandleData;
|
|||
|
|
|||
|
while (Hits < HandleCount && Handle < (1 << 24)) {
|
|||
|
if (!DuplicateHandle(ProcessHandle, (HANDLE)(ULONG_PTR)Handle,
|
|||
|
GetCurrentProcess(), &Dup,
|
|||
|
0, FALSE, DUPLICATE_SAME_ACCESS)) {
|
|||
|
Handle += 4;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// Successfully got a handle, so consider this a hit.
|
|||
|
Hits++;
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtQueryObject(Dup, ObjectBasicInformation,
|
|||
|
&BasicInfo, sizeof(BasicInfo), NULL)) ||
|
|||
|
!NT_SUCCESS(NtQueryObject(Dup, ObjectTypeInformation,
|
|||
|
TypeInfo, sizeof(Buffer), NULL))) {
|
|||
|
// If we can't get the basic info and type there isn't much
|
|||
|
// point in writing anything out so skip the handle.
|
|||
|
goto CloseDup;
|
|||
|
}
|
|||
|
|
|||
|
Len = TypeInfo->TypeName.Length;
|
|||
|
TypeInfo->TypeName.Buffer[Len / sizeof(WCHAR)] = 0;
|
|||
|
|
|||
|
Desc->TypeNameRva = Rva;
|
|||
|
|
|||
|
if (!WriteFile(hFile, &Len, sizeof(Len), &Done, NULL) ||
|
|||
|
Done != sizeof(Len)) {
|
|||
|
goto ExitCloseDup;
|
|||
|
}
|
|||
|
|
|||
|
Len += sizeof(WCHAR);
|
|||
|
if (!WriteFile(hFile, TypeInfo->TypeName.Buffer, Len, &Done, NULL) ||
|
|||
|
Done != Len) {
|
|||
|
goto ExitCloseDup;
|
|||
|
}
|
|||
|
|
|||
|
Rva += Len + sizeof(Len);
|
|||
|
|
|||
|
// 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 (lstrcmpW(TypeInfo->TypeName.Buffer, L"File") &&
|
|||
|
NT_SUCCESS(NtQueryObject(Dup, ObjectNameInformation,
|
|||
|
NameInfo, sizeof(Buffer), NULL)) &&
|
|||
|
NameInfo->Name.Buffer != NULL) {
|
|||
|
|
|||
|
Len = NameInfo->Name.Length;
|
|||
|
NameInfo->Name.Buffer[Len / sizeof(WCHAR)] = 0;
|
|||
|
|
|||
|
Desc->ObjectNameRva = Rva;
|
|||
|
|
|||
|
if (!WriteFile(hFile, &Len, sizeof(Len), &Done, NULL) ||
|
|||
|
Done != sizeof(Len)) {
|
|||
|
goto ExitCloseDup;
|
|||
|
}
|
|||
|
|
|||
|
Len += sizeof(WCHAR);
|
|||
|
if (!WriteFile(hFile, NameInfo->Name.Buffer, Len, &Done, NULL) ||
|
|||
|
Done != Len) {
|
|||
|
goto ExitCloseDup;
|
|||
|
}
|
|||
|
|
|||
|
Rva += Len + sizeof(Len);
|
|||
|
|
|||
|
} else {
|
|||
|
Desc->ObjectNameRva = 0;
|
|||
|
}
|
|||
|
|
|||
|
Desc->Handle = Handle;
|
|||
|
Desc->Attributes = BasicInfo.Attributes;
|
|||
|
Desc->GrantedAccess = BasicInfo.GrantedAccess;
|
|||
|
Desc->HandleCount = BasicInfo.HandleCount;
|
|||
|
Desc->PointerCount = BasicInfo.PointerCount;
|
|||
|
|
|||
|
Desc++;
|
|||
|
|
|||
|
CloseDup:
|
|||
|
CloseHandle(Dup);
|
|||
|
Handle += 4;
|
|||
|
}
|
|||
|
|
|||
|
DataStream.SizeOfHeader = sizeof(DataStream);
|
|||
|
DataStream.SizeOfDescriptor = sizeof(*Descs);
|
|||
|
DataStream.NumberOfDescriptors = (ULONG)(Desc - Descs);
|
|||
|
DataStream.Reserved = 0;
|
|||
|
|
|||
|
StreamInfo->RvaOfHandleData = Rva;
|
|||
|
StreamInfo->SizeOfHandleData = sizeof(DataStream) +
|
|||
|
DataStream.NumberOfDescriptors * sizeof(*Descs);
|
|||
|
|
|||
|
Succ =
|
|||
|
WriteFile(hFile, &DataStream, sizeof(DataStream), &Done, NULL) &&
|
|||
|
Done == sizeof(DataStream) &&
|
|||
|
WriteFile(hFile, Descs, DataStream.NumberOfDescriptors *
|
|||
|
sizeof(*Descs), &Done, NULL) &&
|
|||
|
Done == DataStream.NumberOfDescriptors * sizeof(*Descs);
|
|||
|
|
|||
|
FreeMemory(Descs);
|
|||
|
return Succ;
|
|||
|
|
|||
|
ExitCloseDup:
|
|||
|
CloseHandle(Dup);
|
|||
|
FreeMemory(Descs);
|
|||
|
return FALSE;
|
|||
|
#endif // #ifdef _WIN32_WCE
|
|||
|
}
|