mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2199 lines
54 KiB
2199 lines
54 KiB
/*++
|
|
|
|
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;
|
|
}
|