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
|
||
}
|