576 lines
13 KiB
C
576 lines
13 KiB
C
/*++
|
|
|
|
Copyright (c) 1999-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
nt4.c
|
|
|
|
Abstract:
|
|
|
|
NT 4 specific routines.
|
|
|
|
The following routine are exported from this file:
|
|
|
|
o Nt4OpenThread
|
|
|
|
o Nt4GetProcessInfo
|
|
|
|
o Nt4EnumProcessModules
|
|
|
|
o Nt4GetModuleFileNameExW
|
|
|
|
Author:
|
|
|
|
Matthew D Hendel (math) 10-Sept-1999
|
|
|
|
Revision History:
|
|
|
|
|
|
Environment:
|
|
|
|
NT 4.0 only.
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include "ntx.h"
|
|
#include "nt4.h"
|
|
#include "nt4p.h"
|
|
#include "impl.h"
|
|
|
|
BOOL
|
|
WINAPI
|
|
Nt4EnumProcessModules(
|
|
HANDLE hProcess,
|
|
HMODULE *lphModule,
|
|
DWORD cb,
|
|
LPDWORD lpcbNeeded
|
|
);
|
|
|
|
|
|
HANDLE
|
|
WINAPI
|
|
Nt4OpenThread(
|
|
DWORD dwDesiredAccess,
|
|
BOOL bInheritHandle,
|
|
DWORD dwThreadId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
NT4_OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
NT4_CLIENT_ID ClientId;
|
|
|
|
ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId);
|
|
ClientId.UniqueProcess = (HANDLE)NULL;
|
|
|
|
Nt4InitializeObjectAttributes(
|
|
&Obja,
|
|
NULL,
|
|
(bInheritHandle ? NT4_OBJ_INHERIT : 0),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenThread(
|
|
&Handle,
|
|
(ACCESS_MASK)dwDesiredAccess,
|
|
(POBJECT_ATTRIBUTES)&Obja,
|
|
(PCLIENT_ID)&ClientId
|
|
);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
return Handle;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
Nt4GetProcessInfo(
|
|
IN HANDLE hProcess,
|
|
IN ULONG ProcessId,
|
|
IN ULONG DumpType,
|
|
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
|
|
IN PVOID CallbackParam,
|
|
OUT PINTERNAL_PROCESS * ProcessRet
|
|
)
|
|
{
|
|
BOOL Succ;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
ULONG BufferSize;
|
|
LPVOID Buffer;
|
|
PNT4_SYSTEM_PROCESS_INFORMATION ProcessInfo;
|
|
PNT4_SYSTEM_THREAD_INFORMATION ThreadInfo;
|
|
PINTERNAL_THREAD Thread;
|
|
PINTERNAL_MODULE Module;
|
|
PINTERNAL_PROCESS Process;
|
|
HMODULE Modules [ 512 ];
|
|
ULONG ModulesSize;
|
|
ULONG NumberOfModules;
|
|
ULONG_PTR Next;
|
|
|
|
|
|
BufferSize = 64 * KBYTE;
|
|
Buffer = NULL;
|
|
|
|
do {
|
|
|
|
if (Buffer) {
|
|
FreeMemory (Buffer);
|
|
}
|
|
Buffer = AllocMemory ( BufferSize );
|
|
|
|
if ( Buffer == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtQuerySystemInformation (
|
|
Nt4SystemProcessInformation,
|
|
Buffer,
|
|
BufferSize,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status) && Status != STATUS_INFO_LENGTH_MISMATCH) {
|
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|
return FALSE;
|
|
}
|
|
|
|
BufferSize += (8 * KBYTE);
|
|
|
|
} while (Status == STATUS_INFO_LENGTH_MISMATCH);
|
|
|
|
//
|
|
// Find the correct process in the process list.
|
|
//
|
|
|
|
ProcessInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) Buffer;
|
|
|
|
while (ProcessInfo->NextEntryOffset &&
|
|
ProcessInfo->UniqueProcessId != (HANDLE) ProcessId) {
|
|
|
|
Next = ((ULONG_PTR)ProcessInfo + ProcessInfo->NextEntryOffset);
|
|
ProcessInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) Next;
|
|
}
|
|
|
|
//
|
|
// Could not find a matching process in the process list.
|
|
//
|
|
|
|
if (ProcessInfo->UniqueProcessId != (HANDLE) ProcessId) {
|
|
Succ = FALSE;
|
|
GenAccumulateStatus(MDSTATUS_INTERNAL_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Create an INTERNAL_PROCESS object and copy the process information
|
|
// into it.
|
|
//
|
|
|
|
Process = GenAllocateProcessObject ( hProcess, ProcessId );
|
|
|
|
if ( Process == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Walk the thread list for this process, copying thread information
|
|
// for each thread. Walking the thread list also suspends all the threads
|
|
// in the process. This should be done before walking the module list
|
|
// to minimize the number of race conditions.
|
|
//
|
|
|
|
ThreadInfo = (PNT4_SYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
|
|
Process->NumberOfThreads = 0;
|
|
|
|
for (i = 0; i < ProcessInfo->NumberOfThreads; i++) {
|
|
|
|
ULONG WriteFlags;
|
|
|
|
if (!GenExecuteIncludeThreadCallback(hProcess,
|
|
ProcessId,
|
|
DumpType,
|
|
(ULONG)ThreadInfo->ClientId.UniqueThread,
|
|
CallbackRoutine,
|
|
CallbackParam,
|
|
&WriteFlags) ||
|
|
IsFlagClear(WriteFlags, ThreadWriteThread)) {
|
|
ThreadInfo++;
|
|
continue;
|
|
}
|
|
|
|
Status = GenAllocateThreadObject (
|
|
Process,
|
|
hProcess,
|
|
(ULONG) ThreadInfo->ClientId.UniqueThread,
|
|
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) {
|
|
InsertTailList (&Process->ThreadList, &Thread->ThreadsLink);
|
|
Process->NumberOfThreads++;
|
|
}
|
|
|
|
ThreadInfo++;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the module information. Use PSAPI since it actually works.
|
|
//
|
|
|
|
ModulesSize = 0;
|
|
Succ = Nt4EnumProcessModules (
|
|
Process->ProcessHandle,
|
|
Modules,
|
|
sizeof (Modules),
|
|
&ModulesSize
|
|
);
|
|
|
|
if ( !Succ ) {
|
|
GenAccumulateStatus(MDSTATUS_CALL_FAILED);
|
|
goto Exit;
|
|
}
|
|
|
|
NumberOfModules = ModulesSize / sizeof (HMODULE);
|
|
for (i = 0; i < NumberOfModules; i++) {
|
|
ULONG WriteFlags;
|
|
|
|
if (!GenExecuteIncludeModuleCallback(hProcess,
|
|
ProcessId,
|
|
DumpType,
|
|
(LONG_PTR)Modules[i],
|
|
CallbackRoutine,
|
|
CallbackParam,
|
|
&WriteFlags) ||
|
|
IsFlagClear(WriteFlags, ModuleWriteModule)) {
|
|
continue;
|
|
}
|
|
|
|
Module = NtxAllocateModuleObject (
|
|
Process,
|
|
Process->ProcessHandle,
|
|
(LONG_PTR) Modules [ i ],
|
|
DumpType,
|
|
WriteFlags,
|
|
NULL
|
|
);
|
|
|
|
if ( Module == NULL ) {
|
|
Succ = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
InsertTailList (&Process->ModuleList, &Module->ModulesLink);
|
|
}
|
|
|
|
Process->NumberOfModules = NumberOfModules;
|
|
|
|
Succ = TRUE;
|
|
|
|
Exit:
|
|
|
|
if ( Buffer ) {
|
|
FreeMemory ( Buffer );
|
|
Buffer = NULL;
|
|
}
|
|
|
|
if ( !Succ && Process != NULL ) {
|
|
GenFreeProcessObject ( Process );
|
|
Process = NULL;
|
|
}
|
|
|
|
*ProcessRet = Process;
|
|
|
|
return Succ;
|
|
}
|
|
|
|
|
|
//
|
|
// From PSAPI
|
|
//
|
|
|
|
BOOL
|
|
Nt4FindModule(
|
|
IN HANDLE hProcess,
|
|
IN HMODULE hModule,
|
|
OUT PNT4_LDR_DATA_TABLE_ENTRY LdrEntryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the loader table entry for the specified
|
|
module. The function copies the entry into the buffer pointed to
|
|
by the LdrEntryData parameter.
|
|
|
|
Arguments:
|
|
|
|
hProcess - Supplies the target process.
|
|
|
|
hModule - Identifies the module whose loader entry is being
|
|
requested. A value of NULL references the module handle
|
|
associated with the image file that was used to create the
|
|
process.
|
|
|
|
LdrEntryData - Returns the requested table entry.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a matching entry was found.
|
|
|
|
--*/
|
|
|
|
{
|
|
NT4_PROCESS_BASIC_INFORMATION BasicInfo;
|
|
NTSTATUS Status;
|
|
PNT4_PEB Peb;
|
|
PNT4_PEB_LDR_DATA Ldr;
|
|
PLIST_ENTRY LdrHead;
|
|
PLIST_ENTRY LdrNext;
|
|
|
|
Status = NtQueryInformationProcess(
|
|
hProcess,
|
|
Nt4ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
Peb = BasicInfo.PebBaseAddress;
|
|
|
|
|
|
if ( hModule == NULL ) {
|
|
if (!ReadProcessMemory(hProcess, &Peb->ImageBaseAddress, &hModule, sizeof(hModule), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ldr = Peb->Ldr
|
|
//
|
|
|
|
if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!Ldr) {
|
|
// Ldr might be null (for instance, if the process hasn't started yet).
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
LdrHead = &Ldr->InMemoryOrderModuleList;
|
|
|
|
//
|
|
// LdrNext = Head->Flink;
|
|
//
|
|
|
|
if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
while (LdrNext != LdrHead) {
|
|
|
|
PNT4_LDR_DATA_TABLE_ENTRY LdrEntry;
|
|
|
|
LdrEntry = CONTAINING_RECORD(
|
|
LdrNext,
|
|
NT4_LDR_DATA_TABLE_ENTRY,
|
|
InMemoryOrderLinks);
|
|
|
|
if (!ReadProcessMemory(hProcess, LdrEntry, LdrEntryData, sizeof(*LdrEntryData), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if ((HMODULE) LdrEntryData->DllBase == hModule) {
|
|
return(TRUE);
|
|
}
|
|
|
|
LdrNext = LdrEntryData->InMemoryOrderLinks.Flink;
|
|
}
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
Nt4EnumProcessModules(
|
|
HANDLE hProcess,
|
|
HMODULE *lphModule,
|
|
DWORD cb,
|
|
LPDWORD lpcbNeeded
|
|
)
|
|
{
|
|
NT4_PROCESS_BASIC_INFORMATION BasicInfo;
|
|
NTSTATUS Status;
|
|
PNT4_PEB Peb;
|
|
PNT4_PEB_LDR_DATA Ldr;
|
|
PLIST_ENTRY LdrHead;
|
|
PLIST_ENTRY LdrNext;
|
|
DWORD chMax;
|
|
DWORD ch;
|
|
|
|
Status = NtQueryInformationProcess(
|
|
hProcess,
|
|
Nt4ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
Peb = BasicInfo.PebBaseAddress;
|
|
|
|
//
|
|
// Ldr = Peb->Ldr
|
|
//
|
|
|
|
if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
LdrHead = &Ldr->InMemoryOrderModuleList;
|
|
|
|
//
|
|
// LdrNext = Head->Flink;
|
|
//
|
|
|
|
if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
chMax = cb / sizeof(HMODULE);
|
|
ch = 0;
|
|
|
|
while (LdrNext != LdrHead) {
|
|
PNT4_LDR_DATA_TABLE_ENTRY LdrEntry;
|
|
NT4_LDR_DATA_TABLE_ENTRY LdrEntryData;
|
|
|
|
LdrEntry = CONTAINING_RECORD(
|
|
LdrNext,
|
|
NT4_LDR_DATA_TABLE_ENTRY,
|
|
InMemoryOrderLinks);
|
|
|
|
if (!ReadProcessMemory(hProcess, LdrEntry, &LdrEntryData, sizeof(LdrEntryData), NULL)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (ch < chMax) {
|
|
try {
|
|
lphModule[ch] = (HMODULE) LdrEntryData.DllBase;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
ch++;
|
|
|
|
LdrNext = LdrEntryData.InMemoryOrderLinks.Flink;
|
|
}
|
|
|
|
try {
|
|
*lpcbNeeded = ch * sizeof(HMODULE);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
Nt4GetModuleFileNameExW(
|
|
HANDLE hProcess,
|
|
HMODULE hModule,
|
|
LPWSTR lpFilename,
|
|
DWORD nSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the full pathname of the executable file
|
|
from which the specified module was loaded. The function copies the
|
|
null-terminated filename into the buffer pointed to by the
|
|
lpFilename parameter.
|
|
|
|
Routine Description:
|
|
|
|
hModule - Identifies the module whose executable file name is being
|
|
requested. A value of NULL references the module handle
|
|
associated with the image file that was used to create the
|
|
process.
|
|
|
|
lpFilename - Points to the buffer that is to receive the filename.
|
|
|
|
nSize - Specifies the maximum number of characters to copy. If the
|
|
filename is longer than the maximum number of characters
|
|
specified by the nSize parameter, it is truncated.
|
|
|
|
Return Value:
|
|
|
|
The return value specifies the actual length of the string copied to
|
|
the buffer. A return value of zero indicates an error and extended
|
|
error status is available using the GetLastError function.
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
|
|
{
|
|
NT4_LDR_DATA_TABLE_ENTRY LdrEntryData;
|
|
DWORD cb;
|
|
|
|
if (!Nt4FindModule(hProcess, hModule, &LdrEntryData)) {
|
|
return(0);
|
|
}
|
|
|
|
nSize *= sizeof(WCHAR);
|
|
|
|
cb = LdrEntryData.FullDllName.MaximumLength;
|
|
if ( nSize < cb ) {
|
|
cb = nSize;
|
|
}
|
|
|
|
if (!ReadProcessMemory(hProcess, LdrEntryData.FullDllName.Buffer, lpFilename, cb, NULL)) {
|
|
return(0);
|
|
}
|
|
|
|
if (cb == LdrEntryData.FullDllName.MaximumLength) {
|
|
cb -= sizeof(WCHAR);
|
|
}
|
|
|
|
return(cb / sizeof(WCHAR));
|
|
}
|