2200 lines
54 KiB
C
2200 lines
54 KiB
C
/*++
|
||
|
||
Copyright (c) 1999-2002 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
gen.c
|
||
|
||
Abstract:
|
||
|
||
Generic routins for minidump that work on both NT and Win9x.
|
||
|
||
Author:
|
||
|
||
Matthew D Hendel (math) 10-Sep-1999
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
#include <limits.h>
|
||
|
||
#include "nt4.h"
|
||
#include "win.h"
|
||
#include "ntx.h"
|
||
#include "wce.h"
|
||
|
||
#include "impl.h"
|
||
|
||
#define REASONABLE_NB11_RECORD_SIZE (10 * KBYTE)
|
||
#define REASONABLE_MISC_RECORD_SIZE (10 * KBYTE)
|
||
|
||
ULONG g_MiniDumpStatus;
|
||
|
||
#if defined (i386)
|
||
|
||
//
|
||
// For FPO frames on x86 we access bytes outside of the ESP - StackBase range.
|
||
// This variable determines how many extra bytes we need to add for this
|
||
// case.
|
||
//
|
||
|
||
#define X86_STACK_FRAME_EXTRA_FPO_BYTES 4
|
||
|
||
#endif
|
||
|
||
|
||
LPVOID
|
||
AllocMemory(
|
||
SIZE_T Size
|
||
)
|
||
{
|
||
LPVOID Mem = HeapAlloc ( GetProcessHeap (), HEAP_ZERO_MEMORY, Size );
|
||
if (!Mem) {
|
||
// Handle marking the no-memory state for all allocations.
|
||
GenAccumulateStatus(MDSTATUS_OUT_OF_MEMORY);
|
||
}
|
||
return Mem;
|
||
}
|
||
|
||
VOID
|
||
FreeMemory(
|
||
IN LPVOID Memory
|
||
)
|
||
{
|
||
if ( Memory ) {
|
||
HeapFree ( GetProcessHeap (), 0, Memory );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
PVOID
|
||
ReAllocMemory(
|
||
IN LPVOID Memory,
|
||
IN SIZE_T Size
|
||
)
|
||
{
|
||
LPVOID Mem = HeapReAlloc ( GetProcessHeap (), HEAP_ZERO_MEMORY, Memory, Size);
|
||
if (!Mem) {
|
||
// Handle marking the no-memory state for all allocations.
|
||
GenAccumulateStatus(MDSTATUS_OUT_OF_MEMORY);
|
||
}
|
||
return Mem;
|
||
}
|
||
|
||
|
||
BOOL
|
||
ProcessThread32Next(
|
||
HANDLE hSnapshot,
|
||
DWORD dwProcessID,
|
||
THREADENTRY32 * ThreadInfo
|
||
)
|
||
{
|
||
BOOL succ;
|
||
|
||
//
|
||
// NB: Toolhelp says nothing about the order of the threads will be
|
||
// returned in (i.e., if they are grouped by process or not). If they
|
||
// are groupled by process -- which they emperically seem to be -- there
|
||
// is a more efficient algorithm than simple brute force.
|
||
//
|
||
|
||
do {
|
||
ThreadInfo->dwSize = sizeof (*ThreadInfo);
|
||
succ = Thread32Next (hSnapshot, ThreadInfo);
|
||
|
||
} while (succ && ThreadInfo->th32OwnerProcessID != dwProcessID);
|
||
|
||
return succ;
|
||
}
|
||
|
||
BOOL
|
||
ProcessThread32First(
|
||
HANDLE hSnapshot,
|
||
DWORD dwProcessID,
|
||
THREADENTRY32 * ThreadInfo
|
||
)
|
||
{
|
||
BOOL succ;
|
||
|
||
ThreadInfo->dwSize = sizeof (*ThreadInfo);
|
||
succ = Thread32First (hSnapshot, ThreadInfo);
|
||
|
||
if (succ && ThreadInfo->th32OwnerProcessID != dwProcessID) {
|
||
succ = ProcessThread32Next (hSnapshot, dwProcessID, ThreadInfo);
|
||
}
|
||
|
||
return succ;
|
||
}
|
||
|
||
|
||
ULONG
|
||
GenGetAccumulatedStatus(
|
||
void
|
||
)
|
||
{
|
||
return g_MiniDumpStatus;
|
||
}
|
||
|
||
void
|
||
GenAccumulateStatus(
|
||
IN ULONG Status
|
||
)
|
||
{
|
||
g_MiniDumpStatus |= Status;
|
||
}
|
||
|
||
void
|
||
GenClearAccumulatedStatus(
|
||
void
|
||
)
|
||
{
|
||
g_MiniDumpStatus = 0;
|
||
}
|
||
|
||
|
||
VOID
|
||
GenGetDefaultWriteFlags(
|
||
IN ULONG DumpType,
|
||
OUT PULONG ModuleWriteFlags,
|
||
OUT PULONG ThreadWriteFlags
|
||
)
|
||
{
|
||
*ModuleWriteFlags = ModuleWriteModule | ModuleWriteMiscRecord |
|
||
ModuleWriteCvRecord;
|
||
if (DumpType & MiniDumpWithDataSegs) {
|
||
*ModuleWriteFlags |= ModuleWriteDataSeg;
|
||
}
|
||
|
||
*ThreadWriteFlags = ThreadWriteThread | ThreadWriteContext;
|
||
if (!(DumpType & MiniDumpWithFullMemory)) {
|
||
*ThreadWriteFlags |= ThreadWriteStack | ThreadWriteInstructionWindow;
|
||
#if defined (DUMP_BACKING_STORE)
|
||
*ThreadWriteFlags |= ThreadWriteBackingStore;
|
||
#endif
|
||
}
|
||
if (DumpType & MiniDumpWithProcessThreadData) {
|
||
*ThreadWriteFlags |= ThreadWriteThreadData;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
GenExecuteIncludeThreadCallback(
|
||
IN HANDLE hProcess,
|
||
IN DWORD ProcessId,
|
||
IN ULONG DumpType,
|
||
IN ULONG ThreadId,
|
||
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
|
||
IN PVOID CallbackParam,
|
||
OUT PULONG WriteFlags
|
||
)
|
||
{
|
||
BOOL Succ;
|
||
MINIDUMP_CALLBACK_INPUT CallbackInput;
|
||
MINIDUMP_CALLBACK_OUTPUT CallbackOutput;
|
||
|
||
|
||
// Initialize the default write flags.
|
||
GenGetDefaultWriteFlags(DumpType, &CallbackOutput.ModuleWriteFlags,
|
||
WriteFlags);
|
||
|
||
//
|
||
// If there are no callbacks to call, then we are done.
|
||
//
|
||
|
||
if ( CallbackRoutine == NULL ) {
|
||
return TRUE;
|
||
}
|
||
|
||
CallbackInput.ProcessHandle = hProcess;
|
||
CallbackInput.ProcessId = ProcessId;
|
||
CallbackInput.CallbackType = IncludeThreadCallback;
|
||
|
||
CallbackInput.IncludeThread.ThreadId = ThreadId;
|
||
|
||
CallbackOutput.ThreadWriteFlags = *WriteFlags;
|
||
|
||
Succ = CallbackRoutine (CallbackParam,
|
||
&CallbackInput,
|
||
&CallbackOutput);
|
||
|
||
//
|
||
// If the callback returned FALSE, quit now.
|
||
//
|
||
|
||
if ( !Succ ) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Limit the flags that can be added.
|
||
*WriteFlags &= CallbackOutput.ThreadWriteFlags;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
GenExecuteIncludeModuleCallback(
|
||
IN HANDLE hProcess,
|
||
IN DWORD ProcessId,
|
||
IN ULONG DumpType,
|
||
IN ULONG64 BaseOfImage,
|
||
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
|
||
IN PVOID CallbackParam,
|
||
OUT PULONG WriteFlags
|
||
)
|
||
{
|
||
BOOL Succ;
|
||
MINIDUMP_CALLBACK_INPUT CallbackInput;
|
||
MINIDUMP_CALLBACK_OUTPUT CallbackOutput;
|
||
|
||
|
||
// Initialize the default write flags.
|
||
GenGetDefaultWriteFlags(DumpType, WriteFlags,
|
||
&CallbackOutput.ThreadWriteFlags);
|
||
|
||
//
|
||
// If there are no callbacks to call, then we are done.
|
||
//
|
||
|
||
if ( CallbackRoutine == NULL ) {
|
||
return TRUE;
|
||
}
|
||
|
||
CallbackInput.ProcessHandle = hProcess;
|
||
CallbackInput.ProcessId = ProcessId;
|
||
CallbackInput.CallbackType = IncludeModuleCallback;
|
||
|
||
CallbackInput.IncludeModule.BaseOfImage = BaseOfImage;
|
||
|
||
CallbackOutput.ModuleWriteFlags = *WriteFlags;
|
||
|
||
Succ = CallbackRoutine (CallbackParam,
|
||
&CallbackInput,
|
||
&CallbackOutput);
|
||
|
||
//
|
||
// If the callback returned FALSE, quit now.
|
||
//
|
||
|
||
if ( !Succ ) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Limit the flags that can be added.
|
||
*WriteFlags = (*WriteFlags | ModuleReferencedByMemory) &
|
||
CallbackOutput.ModuleWriteFlags;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
GenGetVersionInfo(
|
||
IN PWSTR FullPath,
|
||
OUT VS_FIXEDFILEINFO * VersionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the VS_FIXEDFILEINFO for the module described by FullPath.
|
||
|
||
Arguments:
|
||
|
||
FullPath - FullPath to the module.
|
||
|
||
VersionInfo - Buffer to copy the Version information.
|
||
|
||
Return Values:
|
||
|
||
TRUE - Success.
|
||
|
||
FALSE - Failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL Succ;
|
||
ULONG unused;
|
||
ULONG Size;
|
||
UINT VerSize;
|
||
PVOID VersionBlock;
|
||
PVOID VersionData;
|
||
CHAR FullPathA [ MAX_PATH + 10 ];
|
||
BOOL UseAnsi = FALSE;
|
||
|
||
//
|
||
// Get the version information.
|
||
//
|
||
|
||
Size = GetFileVersionInfoSizeW (FullPath, &unused);
|
||
|
||
if (Size == 0 &&
|
||
GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
|
||
|
||
// We're on an OS that doesn't support Unicode
|
||
// file operations. Convert to ANSI and see if
|
||
// that helps.
|
||
|
||
if (WideCharToMultiByte (CP_ACP,
|
||
0,
|
||
FullPath,
|
||
-1,
|
||
FullPathA,
|
||
sizeof (FullPathA),
|
||
0,
|
||
0
|
||
) > 0) {
|
||
|
||
Size = GetFileVersionInfoSizeA(FullPathA, &unused);
|
||
UseAnsi = TRUE;
|
||
}
|
||
}
|
||
|
||
if (Size) {
|
||
VersionBlock = AllocMemory (Size);
|
||
|
||
if (VersionBlock) {
|
||
if (UseAnsi) {
|
||
Succ = GetFileVersionInfoA(FullPathA,
|
||
0,
|
||
Size,
|
||
VersionBlock);
|
||
} else {
|
||
Succ = GetFileVersionInfoW(FullPath,
|
||
0,
|
||
Size,
|
||
VersionBlock);
|
||
}
|
||
|
||
if (Succ)
|
||
{
|
||
//
|
||
// Get the VS_FIXEDFILEINFO from the image.
|
||
//
|
||
|
||
VerSize = 0; // ?? sizeof (Module->VersionInfo);
|
||
Succ = VerQueryValue(VersionBlock,
|
||
"\\",
|
||
&VersionData,
|
||
&VerSize);
|
||
|
||
if ( Succ && (VerSize == sizeof (VS_FIXEDFILEINFO)) ) {
|
||
CopyMemory (VersionInfo, VersionData, sizeof (*VersionInfo));
|
||
FreeMemory(VersionBlock);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
FreeMemory (VersionBlock);
|
||
}
|
||
}
|
||
|
||
// Files don't have to have version information
|
||
// so don't accumulate status for this failure.
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
GenGetDebugRecord(
|
||
IN PVOID Base,
|
||
IN ULONG MappedSize,
|
||
IN PIMAGE_NT_HEADERS NtHeaders,
|
||
IN ULONG DebugRecordType,
|
||
IN ULONG DebugRecordMaxSize,
|
||
OUT PVOID * DebugInfo,
|
||
OUT ULONG * SizeOfDebugInfo
|
||
)
|
||
{
|
||
ULONG i;
|
||
ULONG Size;
|
||
ULONG NumberOfDebugDirectories;
|
||
IMAGE_DEBUG_DIRECTORY UNALIGNED* DebugDirectories;
|
||
|
||
|
||
Size = 0;
|
||
|
||
//
|
||
// Find the debug directory and copy the memory into the.
|
||
//
|
||
|
||
DebugDirectories = (IMAGE_DEBUG_DIRECTORY UNALIGNED *)
|
||
ImageDirectoryEntryToData (Base,
|
||
FALSE,
|
||
IMAGE_DIRECTORY_ENTRY_DEBUG,
|
||
&Size);
|
||
|
||
//
|
||
// Check that we got a valid record.
|
||
//
|
||
|
||
if (DebugDirectories &&
|
||
((Size % sizeof (IMAGE_DEBUG_DIRECTORY)) == 0) &&
|
||
(ULONG_PTR)DebugDirectories - (ULONG_PTR)Base + Size <= MappedSize)
|
||
{
|
||
NumberOfDebugDirectories = Size / sizeof (IMAGE_DEBUG_DIRECTORY);
|
||
|
||
for (i = 0 ; i < NumberOfDebugDirectories; i++)
|
||
{
|
||
//
|
||
// We should check if it's a NB10 or something record.
|
||
//
|
||
|
||
if ((DebugDirectories[ i ].Type == DebugRecordType) &&
|
||
(DebugDirectories[ i ].SizeOfData < DebugRecordMaxSize))
|
||
{
|
||
if (DebugDirectories[i].PointerToRawData +
|
||
DebugDirectories[i].SizeOfData > MappedSize)
|
||
{
|
||
break;
|
||
}
|
||
|
||
*SizeOfDebugInfo = DebugDirectories [ i ].SizeOfData;
|
||
*DebugInfo = AllocMemory ( *SizeOfDebugInfo );
|
||
|
||
if (!(*DebugInfo))
|
||
{
|
||
break;
|
||
}
|
||
|
||
CopyMemory(*DebugInfo,
|
||
((PBYTE) Base) +
|
||
DebugDirectories [ i ].PointerToRawData,
|
||
*SizeOfDebugInfo);
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
PVOID
|
||
GenOpenMapping(
|
||
IN PCWSTR FilePath,
|
||
OUT PULONG Size,
|
||
OUT PWSTR LongPath,
|
||
IN ULONG LongPathChars
|
||
)
|
||
{
|
||
HANDLE hFile;
|
||
HANDLE hMappedFile;
|
||
PVOID MappedFile;
|
||
DWORD Chars;
|
||
|
||
//
|
||
// The module may be loaded with a short name. Open
|
||
// the mapping with the name given, but also determine
|
||
// the long name if possible. This is done here as
|
||
// the ANSI/Unicode issues are already being handled here.
|
||
//
|
||
|
||
hFile = CreateFileW(
|
||
FilePath,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
|
||
|
||
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
|
||
|
||
// We're on an OS that doesn't support Unicode
|
||
// file operations. Convert to ANSI and see if
|
||
// that helps.
|
||
|
||
CHAR FilePathA [ MAX_PATH + 10 ];
|
||
|
||
if (WideCharToMultiByte (CP_ACP,
|
||
0,
|
||
FilePath,
|
||
-1,
|
||
FilePathA,
|
||
sizeof (FilePathA),
|
||
0,
|
||
0
|
||
) > 0) {
|
||
|
||
hFile = CreateFileA(FilePathA,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
Chars = GetLongPathNameA(FilePathA, FilePathA,
|
||
ARRAY_COUNT(FilePathA));
|
||
if (Chars == 0 || Chars >= ARRAY_COUNT(FilePathA) ||
|
||
MultiByteToWideChar(CP_ACP, 0, FilePathA, -1,
|
||
LongPath, LongPathChars) == 0) {
|
||
// Couldn't get the long path, just use the
|
||
// given path.
|
||
lstrcpynW(LongPath, FilePath, LongPathChars);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
return NULL;
|
||
}
|
||
} else {
|
||
Chars = GetLongPathNameW(FilePath, LongPath, LongPathChars);
|
||
if (Chars == 0 || Chars >= LongPathChars) {
|
||
// Couldn't get the long path, just use the given path.
|
||
lstrcpynW(LongPath, FilePath, LongPathChars);
|
||
}
|
||
}
|
||
|
||
*Size = GetFileSize(hFile, NULL);
|
||
if (*Size == -1) {
|
||
CloseHandle( hFile );
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
return NULL;
|
||
}
|
||
|
||
hMappedFile = CreateFileMapping (
|
||
hFile,
|
||
NULL,
|
||
PAGE_READONLY,
|
||
0,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if ( !hMappedFile ) {
|
||
CloseHandle ( hFile );
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
return NULL;
|
||
}
|
||
|
||
MappedFile = MapViewOfFile (
|
||
hMappedFile,
|
||
FILE_MAP_READ,
|
||
0,
|
||
0,
|
||
0
|
||
);
|
||
|
||
CloseHandle (hMappedFile);
|
||
CloseHandle (hFile);
|
||
|
||
if (!MappedFile) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
}
|
||
|
||
return MappedFile;
|
||
}
|
||
|
||
BOOL
|
||
GenGetDataContributors(
|
||
IN OUT PINTERNAL_PROCESS Process,
|
||
IN PINTERNAL_MODULE Module
|
||
)
|
||
{
|
||
ULONG i;
|
||
PIMAGE_SECTION_HEADER NtSection;
|
||
BOOL Succ = TRUE;
|
||
PVOID MappedBase;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
ULONG MappedSize;
|
||
BOOL AnsiApi;
|
||
|
||
MappedBase = GenOpenMapping ( Module->FullPath, &MappedSize, NULL, 0 );
|
||
if ( MappedBase == NULL ) {
|
||
return FALSE;
|
||
}
|
||
|
||
NtHeaders = ImageNtHeader ( MappedBase );
|
||
NtSection = IMAGE_FIRST_SECTION ( NtHeaders );
|
||
|
||
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
|
||
|
||
if ( (NtSection[ i ].Characteristics & IMAGE_SCN_MEM_WRITE) &&
|
||
(NtSection[ i ].Characteristics & IMAGE_SCN_MEM_READ) &&
|
||
( (NtSection[ i ].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) ||
|
||
(NtSection[ i ].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) )
|
||
) {
|
||
|
||
if (!GenAddMemoryBlock(Process, MEMBLOCK_DATA_SEG,
|
||
SIGN_EXTEND (NtSection[i].VirtualAddress + Module->BaseOfImage),
|
||
NtSection[i].Misc.VirtualSize)) {
|
||
Succ = FALSE;
|
||
} else {
|
||
#if 0
|
||
printf ("Section: %8.8s Addr: %08x Size: %08x Raw Size: %08x\n",
|
||
NtSection[ i ].Name,
|
||
(ULONG)(NtSection[ i ].VirtualAddress + Module->BaseOfImage),
|
||
NtSection[ i ].Misc.VirtualSize,
|
||
NtSection[ i ].SizeOfRawData
|
||
);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
UnmapViewOfFile(MappedBase);
|
||
return Succ;
|
||
}
|
||
|
||
|
||
HANDLE
|
||
GenOpenThread(
|
||
DWORD dwDesiredAccess,
|
||
BOOL bInheritHandle,
|
||
DWORD dwThreadId
|
||
)
|
||
{
|
||
ULONG Type;
|
||
ULONG Major;
|
||
HANDLE hThread;
|
||
|
||
//
|
||
// First try the OpenThred call in the system, if one exists. This is
|
||
// thunked to return NULL via the delay-load import mechanism if it
|
||
// doesn't exist.
|
||
//
|
||
|
||
hThread = OpenThread (dwDesiredAccess,
|
||
bInheritHandle,
|
||
dwThreadId
|
||
);
|
||
|
||
|
||
if ( hThread != NULL ) {
|
||
return hThread;
|
||
}
|
||
|
||
//
|
||
// Did not succeed. Try alternate methods.
|
||
//
|
||
|
||
GenGetSystemType ( &Type, &Major, NULL, NULL, NULL );
|
||
|
||
if ( Type == WinNt && Major == 4 ) {
|
||
|
||
hThread = Nt4OpenThread (
|
||
dwDesiredAccess,
|
||
bInheritHandle,
|
||
dwThreadId
|
||
);
|
||
|
||
|
||
} else if ( Type == Win9x ) {
|
||
|
||
//
|
||
// The Access and Inheritable parameters are ignored on Win9x.
|
||
//
|
||
|
||
hThread = WinOpenThread (
|
||
dwDesiredAccess,
|
||
bInheritHandle,
|
||
dwThreadId
|
||
);
|
||
} else {
|
||
|
||
hThread = NULL;
|
||
}
|
||
|
||
// Errors are sometimes expected due to
|
||
// thread instability during initial suspension,
|
||
// so do not accumulate status here.
|
||
|
||
return hThread;
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
GenAllocateThreadObject(
|
||
IN struct _INTERNAL_PROCESS* Process,
|
||
IN HANDLE ProcessHandle,
|
||
IN ULONG ThreadId,
|
||
IN ULONG DumpType,
|
||
IN ULONG WriteFlags,
|
||
PINTERNAL_THREAD* ThreadRet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocate and initialize an INTERNAL_THREAD structure.
|
||
|
||
Return Values:
|
||
|
||
S_OK on success.
|
||
|
||
S_FALSE if the thread can't be opened.
|
||
|
||
Errors on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
HRESULT Succ;
|
||
PINTERNAL_THREAD Thread;
|
||
ULONG64 StackEnd;
|
||
ULONG64 StackLimit;
|
||
ULONG64 StoreLimit;
|
||
|
||
ASSERT ( ProcessHandle );
|
||
|
||
Thread = (PINTERNAL_THREAD) AllocMemory ( sizeof (INTERNAL_THREAD) );
|
||
|
||
if (Thread == NULL) {
|
||
return E_OUTOFMEMORY;
|
||
}
|
||
|
||
*ThreadRet = Thread;
|
||
|
||
Thread->ThreadId = ThreadId;
|
||
Thread->ThreadHandle = GenOpenThread (
|
||
THREAD_ALL_ACCESS,
|
||
FALSE,
|
||
Thread->ThreadId);
|
||
|
||
if ( Thread->ThreadHandle == NULL ) {
|
||
// The thread may have exited before we got around
|
||
// to trying to open it. If the open fails with
|
||
// a not-found code return an alternate success to
|
||
// indicate that it's not a critical failure.
|
||
Succ = HRESULT_FROM_WIN32(GetLastError());
|
||
if (Succ == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) ||
|
||
Succ == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
|
||
Succ = S_FALSE;
|
||
} else if (SUCCEEDED(Succ)) {
|
||
Succ = E_FAIL;
|
||
}
|
||
if (FAILED(Succ)) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
}
|
||
goto Exit;
|
||
}
|
||
|
||
// If the current thread is dumping itself we can't
|
||
// suspend. We can also assume the suspend count must
|
||
// be zero since the thread is running.
|
||
if (Thread->ThreadId == GetCurrentThreadId()) {
|
||
Thread->SuspendCount = 0;
|
||
} else {
|
||
Thread->SuspendCount = SuspendThread ( Thread->ThreadHandle );
|
||
}
|
||
Thread->WriteFlags = WriteFlags;
|
||
|
||
//
|
||
// Add this if we ever need it
|
||
//
|
||
|
||
Thread->PriorityClass = 0;
|
||
Thread->Priority = 0;
|
||
|
||
//
|
||
// Initialize the thread context.
|
||
//
|
||
|
||
Thread->Context.ContextFlags = ALL_REGISTERS;
|
||
|
||
Succ = GetThreadContext (Thread->ThreadHandle,
|
||
&Thread->Context) ? S_OK : E_FAIL;
|
||
if ( Succ != S_OK ) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
goto Exit;
|
||
}
|
||
|
||
Thread->SizeOfContext = sizeof (CONTEXT);
|
||
|
||
|
||
Succ = GenGetThreadInfo(ProcessHandle,
|
||
Thread->ThreadHandle,
|
||
&Thread->Teb,
|
||
&Thread->SizeOfTeb,
|
||
&Thread->StackBase,
|
||
&StackLimit,
|
||
&Thread->BackingStoreBase,
|
||
&StoreLimit);
|
||
if (Succ != S_OK) {
|
||
goto Exit;
|
||
}
|
||
|
||
//
|
||
// If the stack pointer (SP) is within the range of the stack
|
||
// region (as allocated by the OS), only take memory from
|
||
// the stack region up to the SP. Otherwise, assume the program
|
||
// has blown it's SP -- purposefully, or not -- and copy
|
||
// the entire stack as known by the OS.
|
||
//
|
||
|
||
StackEnd = SIGN_EXTEND (STACK_POINTER (&Thread->Context));
|
||
|
||
#if defined (i386)
|
||
|
||
//
|
||
// Note: for FPO frames on x86 we access bytes outside of the
|
||
// ESP - StackBase range. Add a couple of bytes extra here so we
|
||
// don't fail these cases.
|
||
//
|
||
|
||
StackEnd -= X86_STACK_FRAME_EXTRA_FPO_BYTES;
|
||
#endif
|
||
|
||
#ifdef DUMP_BACKING_STORE
|
||
Thread->BackingStoreSize =
|
||
(ULONG)(SIGN_EXTEND(BSTORE_POINTER(&Thread->Context)) -
|
||
Thread->BackingStoreBase);
|
||
#else
|
||
Thread->BackingStoreSize = 0;
|
||
#endif
|
||
|
||
if (StackLimit <= StackEnd && StackEnd < Thread->StackBase) {
|
||
Thread->StackEnd = StackEnd;
|
||
} else {
|
||
Thread->StackEnd = StackLimit;
|
||
}
|
||
|
||
if ((ULONG)(Thread->StackBase - Thread->StackEnd) >
|
||
Process->MaxStackOrStoreSize) {
|
||
Process->MaxStackOrStoreSize =
|
||
(ULONG)(Thread->StackBase - Thread->StackEnd);
|
||
}
|
||
if (Thread->BackingStoreSize > Process->MaxStackOrStoreSize) {
|
||
Process->MaxStackOrStoreSize = Thread->BackingStoreSize;
|
||
}
|
||
|
||
Exit:
|
||
|
||
if ( Succ != S_OK ) {
|
||
FreeMemory ( Thread );
|
||
}
|
||
|
||
return Succ;
|
||
}
|
||
|
||
VOID
|
||
GenFreeThreadObject(
|
||
IN PINTERNAL_THREAD Thread
|
||
)
|
||
{
|
||
if (Thread->SuspendCount != -1 &&
|
||
Thread->ThreadId != GetCurrentThreadId()) {
|
||
ResumeThread (Thread->ThreadHandle);
|
||
Thread->SuspendCount = -1;
|
||
}
|
||
CloseHandle (Thread->ThreadHandle);
|
||
Thread->ThreadHandle = NULL;
|
||
FreeMemory ( Thread );
|
||
Thread = NULL;
|
||
}
|
||
|
||
BOOL
|
||
GenGetThreadInstructionWindow(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN PINTERNAL_THREAD Thread
|
||
)
|
||
{
|
||
PVOID MemBuf;
|
||
PUCHAR InstrStart;
|
||
ULONG InstrSize;
|
||
SIZE_T BytesRead;
|
||
BOOL Succ = FALSE;
|
||
|
||
//
|
||
// Store a window of the instruction stream around
|
||
// the current program counter. This allows some
|
||
// instruction context to be given even when images
|
||
// can't be mapped. It also allows instruction
|
||
// context to be given for generated code where
|
||
// no image contains the necessary instructions.
|
||
//
|
||
|
||
InstrStart = (PUCHAR)PROGRAM_COUNTER(&Thread->Context) -
|
||
INSTRUCTION_WINDOW_SIZE / 2;
|
||
InstrSize = INSTRUCTION_WINDOW_SIZE;
|
||
|
||
MemBuf = AllocMemory(InstrSize);
|
||
if (!MemBuf) {
|
||
return FALSE;
|
||
}
|
||
|
||
for (;;) {
|
||
// If we can read the instructions through the
|
||
// current program counter we'll say that's
|
||
// good enough.
|
||
if (ReadProcessMemory(Process->ProcessHandle,
|
||
InstrStart,
|
||
MemBuf,
|
||
InstrSize,
|
||
&BytesRead) &&
|
||
InstrStart + BytesRead >
|
||
(PUCHAR)PROGRAM_COUNTER(&Thread->Context)) {
|
||
Succ = GenAddMemoryBlock(Process, MEMBLOCK_INSTR_WINDOW,
|
||
SIGN_EXTEND(InstrStart),
|
||
(ULONG)BytesRead) != NULL;
|
||
break;
|
||
}
|
||
|
||
// We couldn't read up to the program counter.
|
||
// If the start address is on the previous page
|
||
// move it up to the same page.
|
||
if (((ULONG_PTR)InstrStart & ~(PAGE_SIZE - 1)) !=
|
||
(PROGRAM_COUNTER(&Thread->Context) & ~(PAGE_SIZE - 1))) {
|
||
ULONG Fraction = PAGE_SIZE -
|
||
(ULONG)(ULONG_PTR)InstrStart & (PAGE_SIZE - 1);
|
||
InstrSize -= Fraction;
|
||
InstrStart += Fraction;
|
||
} else {
|
||
// The start and PC were on the same page so
|
||
// we just can't read memory. There may have been
|
||
// a jump to a bad address or something, so this
|
||
// doesn't constitute an unexpected failure.
|
||
break;
|
||
}
|
||
}
|
||
|
||
FreeMemory(MemBuf);
|
||
return Succ;
|
||
}
|
||
|
||
|
||
PINTERNAL_MODULE
|
||
GenAllocateModuleObject(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN PWSTR FullPathW,
|
||
IN ULONG_PTR BaseOfModule,
|
||
IN ULONG DumpType,
|
||
IN ULONG WriteFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given the full-path to the module and the base of the module, create and
|
||
initialize an INTERNAL_MODULE object, and return it.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL Succ;
|
||
PVOID MappedBase;
|
||
ULONG MappedSize;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
PINTERNAL_MODULE Module;
|
||
ULONG Chars;
|
||
BOOL AnsiApi;
|
||
|
||
ASSERT (FullPathW);
|
||
ASSERT (BaseOfModule);
|
||
|
||
Module = (PINTERNAL_MODULE) AllocMemory ( sizeof (INTERNAL_MODULE) );
|
||
|
||
if (Module == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
MappedBase = GenOpenMapping ( FullPathW, &MappedSize,
|
||
Module->FullPath,
|
||
ARRAY_COUNT(Module->FullPath) );
|
||
|
||
if ( MappedBase == NULL ) {
|
||
FreeMemory(Module);
|
||
return NULL;
|
||
}
|
||
|
||
if (IsFlagSet(DumpType, MiniDumpFilterModulePaths)) {
|
||
Module->SavePath = Module->FullPath + lstrlenW(Module->FullPath);
|
||
while (Module->SavePath > Module->FullPath) {
|
||
Module->SavePath--;
|
||
if (*Module->SavePath == '\\' ||
|
||
*Module->SavePath == '/' ||
|
||
*Module->SavePath == ':') {
|
||
Module->SavePath++;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
Module->SavePath = Module->FullPath;
|
||
}
|
||
|
||
//
|
||
// Cull information from the image header.
|
||
//
|
||
|
||
NtHeaders = ImageNtHeader ( MappedBase );
|
||
|
||
Module->BaseOfImage = SIGN_EXTEND (BaseOfModule);
|
||
Module->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
|
||
Module->CheckSum = NtHeaders->OptionalHeader.CheckSum;
|
||
Module->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
||
Module->WriteFlags = WriteFlags;
|
||
|
||
//
|
||
// Get the version information for the module.
|
||
//
|
||
|
||
Succ = GenGetVersionInfo (
|
||
FullPathW,
|
||
&Module->VersionInfo
|
||
);
|
||
|
||
|
||
if ( !Succ ) {
|
||
Module->VersionInfo.dwSignature = 0;
|
||
}
|
||
|
||
//
|
||
// Get the CV record from the debug directory.
|
||
//
|
||
|
||
if (IsFlagSet(Module->WriteFlags, ModuleWriteCvRecord)) {
|
||
Succ = GenGetDebugRecord(MappedBase,
|
||
MappedSize,
|
||
NtHeaders,
|
||
IMAGE_DEBUG_TYPE_CODEVIEW,
|
||
REASONABLE_NB11_RECORD_SIZE,
|
||
&Module->CvRecord,
|
||
&Module->SizeOfCvRecord);
|
||
if ( !Succ ) {
|
||
Module->CvRecord = NULL;
|
||
Module->SizeOfCvRecord = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the MISC record from the debug directory.
|
||
//
|
||
|
||
if (IsFlagSet(Module->WriteFlags, ModuleWriteMiscRecord)) {
|
||
Succ = GenGetDebugRecord(MappedBase,
|
||
MappedSize,
|
||
NtHeaders,
|
||
IMAGE_DEBUG_TYPE_MISC,
|
||
REASONABLE_MISC_RECORD_SIZE,
|
||
&Module->MiscRecord,
|
||
&Module->SizeOfMiscRecord);
|
||
if ( !Succ ) {
|
||
Module->MiscRecord = NULL;
|
||
Module->SizeOfMiscRecord = 0;
|
||
}
|
||
}
|
||
|
||
UnmapViewOfFile ( MappedBase );
|
||
return Module;
|
||
}
|
||
|
||
VOID
|
||
GenFreeModuleObject(
|
||
IN PINTERNAL_MODULE Module
|
||
)
|
||
{
|
||
FreeMemory ( Module->CvRecord );
|
||
Module->CvRecord = NULL;
|
||
|
||
FreeMemory ( Module->MiscRecord );
|
||
Module->MiscRecord = NULL;
|
||
|
||
FreeMemory ( Module );
|
||
Module = NULL;
|
||
}
|
||
|
||
PINTERNAL_UNLOADED_MODULE
|
||
GenAllocateUnloadedModuleObject(
|
||
IN PWSTR Path,
|
||
IN ULONG_PTR BaseOfModule,
|
||
IN ULONG SizeOfModule,
|
||
IN ULONG CheckSum,
|
||
IN ULONG TimeDateStamp
|
||
)
|
||
{
|
||
PINTERNAL_UNLOADED_MODULE Module;
|
||
|
||
Module = (PINTERNAL_UNLOADED_MODULE)
|
||
AllocMemory ( sizeof (*Module) );
|
||
if (Module == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
lstrcpynW (Module->Path, Path, ARRAY_COUNT(Module->Path));
|
||
|
||
Module->BaseOfImage = SIGN_EXTEND (BaseOfModule);
|
||
Module->SizeOfImage = SizeOfModule;
|
||
Module->CheckSum = CheckSum;
|
||
Module->TimeDateStamp = TimeDateStamp;
|
||
|
||
return Module;
|
||
}
|
||
|
||
VOID
|
||
GenFreeUnloadedModuleObject(
|
||
IN PINTERNAL_UNLOADED_MODULE Module
|
||
)
|
||
{
|
||
FreeMemory ( Module );
|
||
Module = NULL;
|
||
}
|
||
|
||
typedef BOOL (WINAPI* FN_GetProcessTimes)(
|
||
IN HANDLE hProcess,
|
||
OUT LPFILETIME lpCreationTime,
|
||
OUT LPFILETIME lpExitTime,
|
||
OUT LPFILETIME lpKernelTime,
|
||
OUT LPFILETIME lpUserTime
|
||
);
|
||
|
||
PINTERNAL_PROCESS
|
||
GenAllocateProcessObject(
|
||
IN HANDLE hProcess,
|
||
IN ULONG ProcessId
|
||
)
|
||
{
|
||
PINTERNAL_PROCESS Process;
|
||
FN_GetProcessTimes GetProcTimes;
|
||
LPVOID Peb;
|
||
|
||
Process = (PINTERNAL_PROCESS) AllocMemory ( sizeof (INTERNAL_PROCESS) );
|
||
if (!Process) {
|
||
return NULL;
|
||
}
|
||
|
||
Process->ProcessId = ProcessId;
|
||
Process->ProcessHandle = hProcess;
|
||
Process->NumberOfThreads = 0;
|
||
Process->NumberOfModules = 0;
|
||
Process->NumberOfFunctionTables = 0;
|
||
InitializeListHead (&Process->ThreadList);
|
||
InitializeListHead (&Process->ModuleList);
|
||
InitializeListHead (&Process->UnloadedModuleList);
|
||
InitializeListHead (&Process->FunctionTableList);
|
||
InitializeListHead (&Process->MemoryBlocks);
|
||
|
||
GetProcTimes = (FN_GetProcessTimes)
|
||
GetProcAddress(GetModuleHandle("kernel32.dll"),
|
||
"GetProcessTimes");
|
||
if (GetProcTimes) {
|
||
FILETIME Create, Exit, User, Kernel;
|
||
|
||
if (GetProcTimes(hProcess, &Create, &Exit, &User, &Kernel)) {
|
||
Process->TimesValid = TRUE;
|
||
Process->CreateTime = FileTimeToTimeDate(&Create);
|
||
Process->UserTime = FileTimeToSeconds(&User);
|
||
Process->KernelTime = FileTimeToSeconds(&Kernel);
|
||
} else {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
}
|
||
}
|
||
|
||
Peb = GenGetPebAddress(hProcess, &Process->SizeOfPeb);
|
||
Process->Peb = SIGN_EXTEND(Peb);
|
||
|
||
return Process;
|
||
}
|
||
|
||
BOOL
|
||
GenFreeProcessObject(
|
||
IN PINTERNAL_PROCESS Process
|
||
)
|
||
{
|
||
PINTERNAL_MODULE Module;
|
||
PINTERNAL_UNLOADED_MODULE UnlModule;
|
||
PINTERNAL_THREAD Thread;
|
||
PINTERNAL_FUNCTION_TABLE Table;
|
||
PVA_RANGE Range;
|
||
PLIST_ENTRY Entry;
|
||
|
||
Thread = NULL;
|
||
Module = NULL;
|
||
|
||
Entry = Process->ModuleList.Flink;
|
||
while ( Entry != &Process->ModuleList ) {
|
||
|
||
Module = CONTAINING_RECORD (Entry, INTERNAL_MODULE, ModulesLink);
|
||
Entry = Entry->Flink;
|
||
|
||
GenFreeModuleObject ( Module );
|
||
Module = NULL;
|
||
}
|
||
|
||
Entry = Process->UnloadedModuleList.Flink;
|
||
while ( Entry != &Process->UnloadedModuleList ) {
|
||
|
||
UnlModule = CONTAINING_RECORD (Entry, INTERNAL_UNLOADED_MODULE,
|
||
ModulesLink);
|
||
Entry = Entry->Flink;
|
||
|
||
GenFreeUnloadedModuleObject ( UnlModule );
|
||
UnlModule = NULL;
|
||
}
|
||
|
||
Entry = Process->ThreadList.Flink;
|
||
while ( Entry != &Process->ThreadList ) {
|
||
|
||
Thread = CONTAINING_RECORD (Entry, INTERNAL_THREAD, ThreadsLink);
|
||
Entry = Entry->Flink;
|
||
|
||
if (Thread->SuspendCount != -1) {
|
||
GenFreeThreadObject ( Thread );
|
||
Thread = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
Entry = Process->FunctionTableList.Flink;
|
||
while ( Entry != &Process->FunctionTableList ) {
|
||
|
||
Table = CONTAINING_RECORD (Entry, INTERNAL_FUNCTION_TABLE, TableLink);
|
||
Entry = Entry->Flink;
|
||
|
||
GenFreeFunctionTableObject ( Table );
|
||
|
||
}
|
||
|
||
Entry = Process->MemoryBlocks.Flink;
|
||
while (Entry != &Process->MemoryBlocks) {
|
||
Range = CONTAINING_RECORD(Entry, VA_RANGE, NextLink);
|
||
Entry = Entry->Flink;
|
||
FreeMemory(Range);
|
||
}
|
||
|
||
FreeMemory ( Process );
|
||
Process = NULL;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
struct _INTERNAL_FUNCTION_TABLE*
|
||
GenAllocateFunctionTableObject(
|
||
IN ULONG64 MinAddress,
|
||
IN ULONG64 MaxAddress,
|
||
IN ULONG64 BaseAddress,
|
||
IN ULONG EntryCount,
|
||
IN PDYNAMIC_FUNCTION_TABLE RawTable
|
||
)
|
||
{
|
||
PINTERNAL_FUNCTION_TABLE Table;
|
||
|
||
Table = (PINTERNAL_FUNCTION_TABLE)
|
||
AllocMemory ( sizeof (INTERNAL_FUNCTION_TABLE) );
|
||
if (Table) {
|
||
Table->RawEntries = AllocMemory(sizeof(RUNTIME_FUNCTION) * EntryCount);
|
||
if (Table->RawEntries) {
|
||
Table->MinimumAddress = MinAddress;
|
||
Table->MaximumAddress = MaxAddress;
|
||
Table->BaseAddress = BaseAddress;
|
||
Table->EntryCount = EntryCount;
|
||
Table->RawTable = *RawTable;
|
||
// RawEntries will be filled out afterwards.
|
||
} else {
|
||
FreeMemory(Table);
|
||
Table = NULL;
|
||
}
|
||
}
|
||
|
||
return Table;
|
||
}
|
||
|
||
VOID
|
||
GenFreeFunctionTableObject(
|
||
IN struct _INTERNAL_FUNCTION_TABLE* Table
|
||
)
|
||
{
|
||
if (Table->RawEntries) {
|
||
FreeMemory(Table->RawEntries);
|
||
}
|
||
|
||
FreeMemory(Table);
|
||
}
|
||
|
||
BOOL
|
||
GenIncludeUnwindInfoMemory(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN ULONG DumpType,
|
||
IN struct _INTERNAL_FUNCTION_TABLE* Table
|
||
)
|
||
{
|
||
ULONG i;
|
||
PRUNTIME_FUNCTION FuncEnt;
|
||
|
||
if (DumpType & MiniDumpWithFullMemory) {
|
||
// Memory will be included by default.
|
||
return TRUE;
|
||
}
|
||
|
||
// This code only needs to scan IA64 and AMD64 tables.
|
||
#if !defined(_IA64_) && !defined(_AMD64_)
|
||
return TRUE;
|
||
#endif
|
||
|
||
FuncEnt = (PRUNTIME_FUNCTION)Table->RawEntries;
|
||
for (i = 0; i < Table->EntryCount; i++) {
|
||
|
||
#if defined(_IA64_) || defined(_AMD64_)
|
||
SIZE_T Done;
|
||
UNWIND_INFO Info;
|
||
ULONG64 Start;
|
||
ULONG Size;
|
||
#endif
|
||
|
||
#if defined(_IA64_)
|
||
|
||
Start = Table->BaseAddress + FuncEnt->UnwindInfoAddress;
|
||
if (!ReadProcessMemory(Process->ProcessHandle, (PVOID)Start,
|
||
&Info, sizeof(Info), &Done) ||
|
||
Done != sizeof(Info)) {
|
||
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
||
return FALSE;
|
||
}
|
||
Size = sizeof(Info) + Info.DataLength * sizeof(ULONG64);
|
||
|
||
#elif defined(_AMD64_)
|
||
|
||
Start = Table->BaseAddress + FuncEnt->UnwindData;
|
||
if (!ReadProcessMemory(Process->ProcessHandle, (PVOID)Start,
|
||
&Info, sizeof(Info), &Done) ||
|
||
Done != sizeof(Info)) {
|
||
GenAccumulateStatus(MDSTATUS_UNABLE_TO_READ_MEMORY);
|
||
return FALSE;
|
||
}
|
||
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_)
|
||
if (!GenAddMemoryBlock(Process, MEMBLOCK_UNWIND_INFO, Start, Size)) {
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
FuncEnt++;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
PVOID
|
||
GenGetTebAddress(
|
||
IN HANDLE Thread,
|
||
OUT PULONG SizeOfTeb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the TIB (or TEB, if you prefer) address for the thread identified
|
||
by ThreadHandle.
|
||
|
||
Arguments:
|
||
|
||
Thread - A handle for a thread that has THRED_QUERY_CONTEXT and
|
||
THREAD_QUERY_INFORMATION privileges.
|
||
|
||
Return Values:
|
||
|
||
Linear address of the Tib (Teb) on success.
|
||
|
||
NULL on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPVOID TebAddress;
|
||
ULONG Type;
|
||
ULONG Major;
|
||
|
||
GenGetSystemType (&Type, &Major, NULL, NULL, NULL);
|
||
|
||
if ( Type == WinNt ) {
|
||
|
||
TebAddress = NtxGetTebAddress (Thread, SizeOfTeb);
|
||
|
||
} else if ( Type != Win9x ) {
|
||
|
||
// WinCE doesn't have a TIB.
|
||
TebAddress = NULL;
|
||
*SizeOfTeb = 0;
|
||
|
||
} else {
|
||
|
||
#ifdef _X86_
|
||
|
||
BOOL Succ;
|
||
ULONG Addr;
|
||
LDT_ENTRY Ldt;
|
||
CONTEXT Context;
|
||
|
||
Context.ContextFlags = CONTEXT_SEGMENTS;
|
||
|
||
Succ = GetThreadContext (Thread, &Context);
|
||
|
||
if ( !Succ ) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
return NULL;
|
||
}
|
||
|
||
Succ = GetThreadSelectorEntry (Thread,
|
||
Context.SegFs,
|
||
&Ldt
|
||
);
|
||
|
||
if ( !Succ ) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
return NULL;
|
||
}
|
||
|
||
Addr = (Ldt.HighWord.Bytes.BaseHi << 24) |
|
||
(Ldt.HighWord.Bytes.BaseMid << 16) |
|
||
(Ldt.BaseLow);
|
||
|
||
TebAddress = (LPVOID) Addr;
|
||
*SizeOfTeb = sizeof(NT_TIB);
|
||
|
||
#else
|
||
|
||
TebAddress = NULL;
|
||
*SizeOfTeb = 0;
|
||
|
||
#endif // _X86_
|
||
}
|
||
|
||
return TebAddress;
|
||
}
|
||
|
||
PVOID
|
||
GenGetPebAddress(
|
||
IN HANDLE Process,
|
||
OUT PULONG SizeOfPeb
|
||
)
|
||
{
|
||
LPVOID PebAddress;
|
||
ULONG Type;
|
||
ULONG Major;
|
||
|
||
GenGetSystemType (&Type, &Major, NULL, NULL, NULL);
|
||
|
||
if ( Type == WinNt ) {
|
||
|
||
PebAddress = NtxGetPebAddress (Process, SizeOfPeb);
|
||
|
||
} else if ( Type == WinCe ) {
|
||
|
||
PebAddress = WceGetPebAddress (Process, SizeOfPeb);
|
||
|
||
} else {
|
||
|
||
// No process data.
|
||
PebAddress = NULL;
|
||
*SizeOfPeb = 0;
|
||
|
||
}
|
||
|
||
return PebAddress;
|
||
}
|
||
|
||
HRESULT
|
||
GenGetThreadInfo(
|
||
IN HANDLE Process,
|
||
IN HANDLE Thread,
|
||
OUT PULONG64 Teb,
|
||
OUT PULONG SizeOfTeb,
|
||
OUT PULONG64 StackBase,
|
||
OUT PULONG64 StackLimit,
|
||
OUT PULONG64 StoreBase,
|
||
OUT PULONG64 StoreLimit
|
||
)
|
||
{
|
||
ULONG Type;
|
||
|
||
GenGetSystemType (&Type, NULL, NULL, NULL, NULL);
|
||
|
||
if ( Type == WinCe ) {
|
||
|
||
return WceGetThreadInfo(Process, Thread,
|
||
Teb, SizeOfTeb,
|
||
StackBase, StackLimit,
|
||
StoreBase, StoreLimit);
|
||
|
||
} else {
|
||
|
||
LPVOID TebAddress = GenGetTebAddress (Thread, SizeOfTeb);
|
||
if (!TebAddress) {
|
||
return E_FAIL;
|
||
}
|
||
|
||
*Teb = SIGN_EXTEND((LONG_PTR)TebAddress);
|
||
return TibGetThreadInfo(Process, TebAddress,
|
||
StackBase, StackLimit,
|
||
StoreBase, StoreLimit);
|
||
|
||
}
|
||
}
|
||
|
||
void
|
||
GenRemoveMemoryBlock(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN PVA_RANGE Block
|
||
)
|
||
{
|
||
RemoveEntryList(&Block->NextLink);
|
||
Process->NumberOfMemoryBlocks--;
|
||
Process->SizeOfMemoryBlocks -= Block->Size;
|
||
}
|
||
|
||
PVA_RANGE
|
||
GenAddMemoryBlock(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN MEMBLOCK_TYPE Type,
|
||
IN ULONG64 Start,
|
||
IN ULONG Size
|
||
)
|
||
{
|
||
ULONG64 End;
|
||
PLIST_ENTRY ScanEntry;
|
||
PVA_RANGE Scan;
|
||
ULONG64 ScanEnd;
|
||
PVA_RANGE New = NULL;
|
||
SIZE_T Done;
|
||
UCHAR Byte;
|
||
|
||
// Do not use Size after this to avoid ULONG overflows.
|
||
End = Start + Size;
|
||
if (End < Start) {
|
||
End = (ULONG64)-1;
|
||
}
|
||
|
||
if (Start == End) {
|
||
// Nothing to add.
|
||
return NULL;
|
||
}
|
||
|
||
if ((End - Start) > ULONG_MAX - Process->SizeOfMemoryBlocks) {
|
||
// Overflow.
|
||
GenAccumulateStatus(MDSTATUS_INTERNAL_ERROR);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// First trim the range down to memory that can actually
|
||
// be accessed.
|
||
//
|
||
|
||
while (Start < End) {
|
||
if (ReadProcessMemory(Process->ProcessHandle,
|
||
(PVOID)(ULONG_PTR)Start,
|
||
&Byte, sizeof(Byte), &Done) && Done) {
|
||
break;
|
||
}
|
||
|
||
// Move up to the next page.
|
||
Start = (Start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||
if (!Start) {
|
||
// Wrapped around.
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
if (Start >= End) {
|
||
// No valid memory.
|
||
return NULL;
|
||
}
|
||
|
||
ScanEnd = (Start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||
for (;;) {
|
||
if (ScanEnd >= End) {
|
||
break;
|
||
}
|
||
|
||
if (!ReadProcessMemory(Process->ProcessHandle,
|
||
(PVOID)(ULONG_PTR)ScanEnd,
|
||
&Byte, sizeof(Byte), &Done) || !Done) {
|
||
End = ScanEnd;
|
||
break;
|
||
}
|
||
|
||
// Move up to the next page.
|
||
ScanEnd = (ScanEnd + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||
if (!ScanEnd) {
|
||
ScanEnd--;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// When adding memory to the list of memory to be saved
|
||
// we want to avoid overlaps and also coalesce adjacent regions
|
||
// so that the list has the largest possible non-adjacent
|
||
// blocks. In order to accomplish this we make a pass over
|
||
// the list and merge all listed blocks that overlap or abut the
|
||
// incoming range with the incoming range, then remove the
|
||
// merged entries from the list. After this pass we have
|
||
// a region which is guaranteed not to overlap or abut anything in
|
||
// the list.
|
||
//
|
||
|
||
ScanEntry = Process->MemoryBlocks.Flink;
|
||
while (ScanEntry != &Process->MemoryBlocks) {
|
||
Scan = CONTAINING_RECORD(ScanEntry, VA_RANGE, NextLink);
|
||
ScanEnd = Scan->Start + Scan->Size;
|
||
ScanEntry = Scan->NextLink.Flink;
|
||
|
||
if (Scan->Start > End || ScanEnd < Start) {
|
||
// No overlap or adjacency.
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Compute the union of the incoming range and
|
||
// the scan block, then remove the scan block.
|
||
//
|
||
|
||
if (Scan->Start < Start) {
|
||
Start = Scan->Start;
|
||
}
|
||
if (ScanEnd > End) {
|
||
End = ScanEnd;
|
||
}
|
||
|
||
// We've lost the specific type. This is not a problem
|
||
// right now but if specific types must be preserved
|
||
// all the way through in the future it will be necessary
|
||
// to avoid merging.
|
||
Type = MEMBLOCK_MERGED;
|
||
|
||
GenRemoveMemoryBlock(Process, Scan);
|
||
|
||
if (!New) {
|
||
// Save memory for reuse.
|
||
New = Scan;
|
||
} else {
|
||
FreeMemory(Scan);
|
||
}
|
||
}
|
||
|
||
if (!New) {
|
||
New = (PVA_RANGE)AllocMemory(sizeof(*New));
|
||
if (!New) {
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
New->Start = Start;
|
||
// Overflow is extremely unlikely, so don't do anything
|
||
// fancy to handle it.
|
||
if (End - Start > ULONG_MAX) {
|
||
New->Size = ULONG_MAX;
|
||
} else {
|
||
New->Size = (ULONG)(End - Start);
|
||
}
|
||
New->Type = Type;
|
||
InsertTailList(&Process->MemoryBlocks, &New->NextLink);
|
||
Process->NumberOfMemoryBlocks++;
|
||
Process->SizeOfMemoryBlocks += New->Size;
|
||
|
||
return New;
|
||
}
|
||
|
||
void
|
||
GenRemoveMemoryRange(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN ULONG64 Start,
|
||
IN ULONG Size
|
||
)
|
||
{
|
||
ULONG64 End = Start + Size;
|
||
PLIST_ENTRY ScanEntry;
|
||
PVA_RANGE Scan;
|
||
ULONG64 ScanEnd;
|
||
|
||
Restart:
|
||
ScanEntry = Process->MemoryBlocks.Flink;
|
||
while (ScanEntry != &Process->MemoryBlocks) {
|
||
Scan = CONTAINING_RECORD(ScanEntry, VA_RANGE, NextLink);
|
||
ScanEnd = Scan->Start + Scan->Size;
|
||
ScanEntry = Scan->NextLink.Flink;
|
||
|
||
if (Scan->Start >= End || ScanEnd <= Start) {
|
||
// No overlap.
|
||
continue;
|
||
}
|
||
|
||
if (Scan->Start < Start) {
|
||
// Trim block to non-overlapping pre-Start section.
|
||
Scan->Size = (ULONG)(Start - Scan->Start);
|
||
if (ScanEnd > End) {
|
||
// There's also a non-overlapping section post-End.
|
||
// We need to add a new block.
|
||
GenAddMemoryBlock(Process, Scan->Type,
|
||
End, (ULONG)(ScanEnd - End));
|
||
// The list has changed so restart.
|
||
goto Restart;
|
||
}
|
||
} else if (ScanEnd > End) {
|
||
// Trim block to non-overlapping post-End section.
|
||
Scan->Start = End;
|
||
Scan->Size = (ULONG)(ScanEnd - End);
|
||
} else {
|
||
// Scan is completely contained.
|
||
GenRemoveMemoryBlock(Process, Scan);
|
||
FreeMemory(Scan);
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
WINAPI
|
||
GenGetSystemType(
|
||
OUT ULONG * Type, OPTIONAL
|
||
OUT ULONG * Major, OPTIONAL
|
||
OUT ULONG * Minor, OPTIONAL
|
||
OUT ULONG * ServicePack, OPTIONAL
|
||
OUT ULONG * BuildNumber OPTIONAL
|
||
)
|
||
{
|
||
OSVERSIONINFO Version;
|
||
|
||
Version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
||
GetVersionEx (&Version);
|
||
|
||
if (Type) {
|
||
if (Version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
|
||
*Type = Win9x;
|
||
} else if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
||
*Type = WinNt;
|
||
} else if (Version.dwPlatformId == VER_PLATFORM_WIN32_CE) {
|
||
*Type = WinCe;
|
||
} else {
|
||
*Type = Unknown;
|
||
}
|
||
}
|
||
|
||
if (Major) {
|
||
if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT ||
|
||
Version.dwPlatformId == VER_PLATFORM_WIN32_CE) {
|
||
*Major = Version.dwMajorVersion;
|
||
} else {
|
||
if (Version.dwMinorVersion == 0) {
|
||
*Major = 95;
|
||
} else {
|
||
*Major = 98;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Minor) {
|
||
if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT ||
|
||
Version.dwPlatformId == VER_PLATFORM_WIN32_CE) {
|
||
*Minor = Version.dwMinorVersion;
|
||
} else {
|
||
*Minor = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// TODO: Derive this from known build numbers only if it's necessary
|
||
// for external stuff.
|
||
//
|
||
|
||
if (ServicePack) {
|
||
*ServicePack = 0;
|
||
}
|
||
|
||
if (BuildNumber) {
|
||
*BuildNumber = Version.dwBuildNumber;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
GenScanAddressSpace(
|
||
IN PINTERNAL_PROCESS Process,
|
||
IN ULONG DumpType
|
||
)
|
||
{
|
||
ULONG ProtectMask = 0, TypeMask = 0;
|
||
BOOL Succ;
|
||
ULONG_PTR Offset;
|
||
MEMORY_BASIC_INFORMATION Info;
|
||
|
||
if (DumpType & MiniDumpWithPrivateReadWriteMemory) {
|
||
ProtectMask |= PAGE_READWRITE;
|
||
TypeMask |= MEM_PRIVATE;
|
||
}
|
||
|
||
if (!ProtectMask || !TypeMask) {
|
||
// Nothing to scan for.
|
||
return TRUE;
|
||
}
|
||
|
||
Succ = TRUE;
|
||
|
||
Offset = 0;
|
||
for (;;) {
|
||
if (!VirtualQueryEx(Process->ProcessHandle, (LPVOID)Offset,
|
||
&Info, sizeof(Info))) {
|
||
break;
|
||
}
|
||
|
||
Offset = (ULONG_PTR)Info.BaseAddress + Info.RegionSize;
|
||
|
||
if (Info.State == MEM_COMMIT &&
|
||
(Info.Protect & ProtectMask) &&
|
||
(Info.Type & TypeMask)) {
|
||
|
||
while (Info.RegionSize > 0) {
|
||
ULONG BlockSize;
|
||
|
||
if (Info.RegionSize > ULONG_MAX / 2) {
|
||
BlockSize = ULONG_MAX / 2;
|
||
} else {
|
||
BlockSize = (ULONG)Info.RegionSize;
|
||
}
|
||
|
||
if (!GenAddMemoryBlock(Process, MEMBLOCK_PRIVATE_RW,
|
||
SIGN_EXTEND(Info.BaseAddress),
|
||
BlockSize)) {
|
||
Succ = FALSE;
|
||
}
|
||
|
||
Info.BaseAddress = (PVOID)
|
||
((ULONG_PTR)Info.BaseAddress + BlockSize);
|
||
Info.RegionSize -= BlockSize;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Succ;
|
||
}
|
||
|
||
|
||
BOOL
|
||
GenGetProcessInfo(
|
||
IN HANDLE hProcess,
|
||
IN ULONG ProcessId,
|
||
IN ULONG DumpType,
|
||
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
|
||
IN PVOID CallbackParam,
|
||
OUT PINTERNAL_PROCESS * ProcessRet
|
||
)
|
||
{
|
||
BOOL Succ;
|
||
ULONG Type;
|
||
ULONG Major;
|
||
|
||
GenGetSystemType (&Type, &Major, NULL, NULL, NULL);
|
||
|
||
if ( Type == WinNt && Major > 4 ) {
|
||
|
||
Succ = NtxGetProcessInfo (hProcess, ProcessId, DumpType,
|
||
CallbackRoutine, CallbackParam,
|
||
ProcessRet);
|
||
|
||
} else if ( Type == WinNt && Major == 4 ) {
|
||
|
||
Succ = Nt4GetProcessInfo (hProcess, ProcessId, DumpType,
|
||
CallbackRoutine, CallbackParam,
|
||
ProcessRet);
|
||
|
||
} else if ( Type == Win9x || Type == WinCe ) {
|
||
|
||
Succ = ThGetProcessInfo (hProcess, ProcessId, DumpType,
|
||
CallbackRoutine, CallbackParam,
|
||
ProcessRet);
|
||
|
||
} else {
|
||
|
||
Succ = FALSE;
|
||
}
|
||
|
||
if (Succ) {
|
||
// We don't consider a failure here to be a critical
|
||
// failure. The dump won't contain all of the
|
||
// requested information but it'll still have
|
||
// the basic thread information, which could be
|
||
// valuable on its own.
|
||
GenScanAddressSpace(*ProcessRet, DumpType);
|
||
}
|
||
|
||
return Succ;
|
||
}
|
||
|
||
|
||
BOOL
|
||
GenWriteHandleData(
|
||
IN HANDLE ProcessHandle,
|
||
IN HANDLE hFile,
|
||
IN struct _MINIDUMP_STREAM_INFO * StreamInfo
|
||
)
|
||
{
|
||
BOOL Succ;
|
||
ULONG Type;
|
||
ULONG Major;
|
||
|
||
GenGetSystemType(&Type, &Major, NULL, NULL, NULL);
|
||
|
||
if ( Type == WinNt ) {
|
||
|
||
Succ = NtxWriteHandleData(ProcessHandle, hFile, StreamInfo);
|
||
|
||
} else {
|
||
|
||
Succ = FALSE;
|
||
|
||
}
|
||
|
||
return Succ;
|
||
}
|
||
|
||
|
||
ULONG
|
||
CheckSum (
|
||
IN ULONG PartialSum,
|
||
IN PVOID SourceVa,
|
||
IN ULONG_PTR Length
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Computes a checksum for the supplied virtual address and length
|
||
|
||
This function comes from Dr. Dobbs Journal, May 1992
|
||
|
||
Arguments:
|
||
|
||
PartialSum - The previous partial checksum
|
||
|
||
SourceVa - Starting address
|
||
|
||
Length - Length, in bytes, of the range
|
||
|
||
Return Value:
|
||
|
||
The checksum value
|
||
|
||
--*/
|
||
{
|
||
|
||
PUSHORT Source;
|
||
|
||
Source = (PUSHORT) SourceVa;
|
||
Length = Length / 2;
|
||
|
||
while (Length--) {
|
||
PartialSum += *Source++;
|
||
PartialSum = (PartialSum >> 16) + (PartialSum & 0xFFFF);
|
||
}
|
||
|
||
return PartialSum;
|
||
}
|
||
|
||
BOOL
|
||
ThGetProcessInfo(
|
||
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.
|
||
As toolhelp provides an abstraction for retrieval, this
|
||
code is "generic" and can run on any platform supporting toolhelp.
|
||
|
||
Return Values:
|
||
|
||
TRUE - Success.
|
||
|
||
FALSE - Failure:
|
||
|
||
Environment:
|
||
|
||
Any platform that supports toolhelp.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL Succ;
|
||
BOOL MoreThreads;
|
||
BOOL MoreModules;
|
||
HANDLE Snapshot;
|
||
MODULEENTRY32 ModuleInfo;
|
||
THREADENTRY32 ThreadInfo;
|
||
PINTERNAL_THREAD Thread;
|
||
PINTERNAL_PROCESS Process;
|
||
PINTERNAL_MODULE Module;
|
||
WCHAR UnicodePath [ MAX_PATH + 10 ];
|
||
|
||
ASSERT ( hProcess );
|
||
ASSERT ( ProcessId != 0 );
|
||
ASSERT ( ProcessRet );
|
||
|
||
Process = NULL;
|
||
Thread = NULL;
|
||
Module = NULL;
|
||
Snapshot = NULL;
|
||
ModuleInfo.dwSize = sizeof (MODULEENTRY32);
|
||
ThreadInfo.dwSize = sizeof (THREADENTRY32);
|
||
|
||
Process = GenAllocateProcessObject ( hProcess, ProcessId );
|
||
|
||
if ( Process == NULL ) {
|
||
return FALSE;
|
||
}
|
||
|
||
Snapshot = CreateToolhelp32Snapshot (
|
||
TH32CS_SNAPMODULE | TH32CS_SNAPTHREAD,
|
||
ProcessId
|
||
);
|
||
|
||
if ( Snapshot == (HANDLE) -1 ) {
|
||
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
||
Succ = FALSE;
|
||
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);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Walk module list, getting module information.
|
||
//
|
||
|
||
for (MoreModules = Module32First ( Snapshot, &ModuleInfo );
|
||
MoreModules;
|
||
MoreModules = Module32Next ( Snapshot, &ModuleInfo ) ) {
|
||
ULONG WriteFlags;
|
||
|
||
ASSERT ( (ULONG_PTR)ModuleInfo.modBaseAddr == (ULONG_PTR)ModuleInfo.hModule );
|
||
|
||
if (!GenExecuteIncludeModuleCallback(hProcess,
|
||
ProcessId,
|
||
DumpType,
|
||
(LONG_PTR)ModuleInfo.modBaseAddr,
|
||
CallbackRoutine,
|
||
CallbackParam,
|
||
&WriteFlags) ||
|
||
IsFlagClear(WriteFlags, ModuleWriteModule)) {
|
||
continue;
|
||
}
|
||
|
||
MultiByteToWideChar (CP_ACP,
|
||
0,
|
||
ModuleInfo.szExePath,
|
||
-1,
|
||
UnicodePath,
|
||
ARRAY_COUNT(UnicodePath)
|
||
);
|
||
|
||
|
||
Module = GenAllocateModuleObject (
|
||
Process,
|
||
UnicodePath,
|
||
(LONG_PTR) ModuleInfo.modBaseAddr,
|
||
DumpType,
|
||
WriteFlags
|
||
);
|
||
|
||
if ( Module == NULL ) {
|
||
Succ = FALSE;
|
||
goto Exit;
|
||
}
|
||
|
||
Process->NumberOfModules++;
|
||
InsertTailList (&Process->ModuleList, &Module->ModulesLink);
|
||
}
|
||
|
||
Succ = TRUE;
|
||
|
||
Exit:
|
||
|
||
if ( Snapshot ) {
|
||
CloseHandle ( Snapshot );
|
||
Snapshot = NULL;
|
||
}
|
||
|
||
if ( !Succ && Process != NULL ) {
|
||
GenFreeProcessObject ( Process );
|
||
Process = NULL;
|
||
}
|
||
|
||
*ProcessRet = Process;
|
||
|
||
return Succ;
|
||
}
|