Files
mirror-processhacker/2.x/trunk/ProcessHacker/native.c
T
wj32 a8560f08b1 completed run-as GUI; process token stealing not implemented yet
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2917 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2010-03-06 09:39:40 +00:00

4314 lines
108 KiB
C

/*
* Process Hacker -
* native support functions
*
* Copyright (C) 2009-2010 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ph.h>
#include <kph.h>
#include <symprvp.h>
typedef BOOLEAN (NTAPI *PPHP_ENUM_PROCESS_MODULES_CALLBACK)(
__in HANDLE ProcessHandle,
__in PLDR_DATA_TABLE_ENTRY Entry,
__in PVOID AddressOfEntry,
__in PVOID Context1,
__in PVOID Context2
);
PWSTR PhDosDeviceNames[26];
PH_QUEUED_LOCK PhDosDeviceNamesLock;
/**
* Opens a process.
*
* \param ProcessHandle A variable which receives a handle to the process.
* \param DesiredAccess The desired access to the process.
* \param ProcessId The ID of the process.
*/
NTSTATUS PhOpenProcess(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in HANDLE ProcessId
)
{
OBJECT_ATTRIBUTES objectAttributes = { 0 };
CLIENT_ID clientId;
if (PhKphHandle)
{
return KphOpenProcess(
PhKphHandle,
ProcessHandle,
ProcessId,
DesiredAccess
);
}
else
{
clientId.UniqueProcess = ProcessId;
clientId.UniqueThread = NULL;
return NtOpenProcess(
ProcessHandle,
DesiredAccess,
&objectAttributes,
&clientId
);
}
}
/**
* Opens a thread.
*
* \param ThreadHandle A variable which receives a handle to the thread.
* \param DesiredAccess The desired access to the thread.
* \param ThreadId The ID of the thread.
*/
NTSTATUS PhOpenThread(
__out PHANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in HANDLE ThreadId
)
{
OBJECT_ATTRIBUTES objectAttributes = { 0 };
CLIENT_ID clientId;
if (PhKphHandle)
{
return KphOpenThread(
PhKphHandle,
ThreadHandle,
ThreadId,
DesiredAccess
);
}
else
{
clientId.UniqueProcess = NULL;
clientId.UniqueThread = ThreadId;
return NtOpenThread(
ThreadHandle,
DesiredAccess,
&objectAttributes,
&clientId
);
}
}
/**
* Opens a process token.
*
* \param TokenHandle A variable which receives a handle to the token.
* \param DesiredAccess The desired access to the token.
* \param ProcessHandle A handle to a process.
*/
NTSTATUS PhOpenProcessToken(
__out PHANDLE TokenHandle,
__in ACCESS_MASK DesiredAccess,
__in HANDLE ProcessHandle
)
{
if (PhKphHandle)
{
return KphOpenProcessToken(
PhKphHandle,
TokenHandle,
ProcessHandle,
DesiredAccess
);
}
else
{
return NtOpenProcessToken(
ProcessHandle,
DesiredAccess,
TokenHandle
);
}
}
/**
* Opens a thread token.
*
* \param TokenHandle A variable which receives a handle to the token.
* \param DesiredAccess The desired access to the token.
* \param ThreadHandle A handle to a thread.
* \param OpenAsSelf TRUE to use the primary token for access checks,
* FALSE to use the impersonation token.
*/
NTSTATUS PhOpenThreadToken(
__out PHANDLE TokenHandle,
__in ACCESS_MASK DesiredAccess,
__in HANDLE ThreadHandle,
__in BOOLEAN OpenAsSelf
)
{
return NtOpenThreadToken(
ThreadHandle,
DesiredAccess,
OpenAsSelf,
TokenHandle
);
}
NTSTATUS PhGetObjectSecurity(
__in HANDLE Handle,
__in SECURITY_INFORMATION SecurityInformation,
__out PSECURITY_DESCRIPTOR *SecurityDescriptor
)
{
NTSTATUS status;
ULONG bufferSize;
PVOID buffer;
bufferSize = 0x100;
buffer = PhAllocate(bufferSize);
status = NtQuerySecurityObject(
Handle,
SecurityInformation,
buffer,
bufferSize,
&bufferSize
);
if (status == STATUS_BUFFER_TOO_SMALL)
{
PhFree(buffer);
buffer = PhAllocate(bufferSize);
status = NtQuerySecurityObject(
Handle,
SecurityInformation,
buffer,
bufferSize,
&bufferSize
);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
*SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer;
return status;
}
NTSTATUS PhSetObjectSecurity(
__in HANDLE Handle,
__in SECURITY_INFORMATION SecurityInformation,
__in PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
return NtSetSecurityObject(
Handle,
SecurityInformation,
SecurityDescriptor
);
}
/**
* Terminates a process.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_TERMINATE access.
* \param ExitStatus A status value that indicates why the
* process is being terminated.
*/
NTSTATUS PhTerminateProcess(
__in HANDLE ProcessHandle,
__in NTSTATUS ExitStatus
)
{
if (PhKphHandle)
{
return KphTerminateProcess(
PhKphHandle,
ProcessHandle,
ExitStatus
);
}
else
{
return NtTerminateProcess(
ProcessHandle,
ExitStatus
);
}
}
/**
* Suspends a process' threads.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_SUSPEND_RESUME access.
*/
NTSTATUS PhSuspendProcess(
__in HANDLE ProcessHandle
)
{
if (PhKphHandle && WINDOWS_HAS_PSSUSPENDRESUMEPROCESS)
{
return KphSuspendProcess(PhKphHandle, ProcessHandle);
}
else
{
return NtSuspendProcess(ProcessHandle);
}
}
/**
* Resumes a process' threads.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_SUSPEND_RESUME access.
*/
NTSTATUS PhResumeProcess(
__in HANDLE ProcessHandle
)
{
if (PhKphHandle && WINDOWS_HAS_PSSUSPENDRESUMEPROCESS)
{
return KphResumeProcess(PhKphHandle, ProcessHandle);
}
else
{
return NtResumeProcess(ProcessHandle);
}
}
/**
* Terminates a thread.
*
* \param ThreadHandle A handle to a thread. The handle must
* have THREAD_TERMINATE access.
* \param ExitStatus A status value that indicates why the
* thread is being terminated.
*/
NTSTATUS PhTerminateThread(
__in HANDLE ThreadHandle,
__in NTSTATUS ExitStatus
)
{
if (PhKphHandle)
{
return KphTerminateThread(
PhKphHandle,
ThreadHandle,
ExitStatus
);
}
else
{
return NtTerminateThread(
ThreadHandle,
ExitStatus
);
}
}
/**
* Suspends a thread.
*
* \param ThreadHandle A handle to a thread. The handle must
* have THREAD_SUSPEND_RESUME access.
* \param PreviousSuspendCount A variable which receives the
* number of times the thread had been suspended.
*/
NTSTATUS PhSuspendThread(
__in HANDLE ThreadHandle,
__out_opt PULONG PreviousSuspendCount
)
{
return NtSuspendThread(ThreadHandle, PreviousSuspendCount);
}
/**
* Resumes a thread.
*
* \param ThreadHandle A handle to a thread. The handle must
* have THREAD_SUSPEND_RESUME access.
* \param PreviousSuspendCount A variable which receives the
* number of times the thread had been suspended.
*/
NTSTATUS PhResumeThread(
__in HANDLE ThreadHandle,
__out_opt PULONG PreviousSuspendCount
)
{
return NtResumeThread(ThreadHandle, PreviousSuspendCount);
}
/**
* Gets the processor context of a thread.
*
* \param ThreadHandle A handle to a thread. The handle must
* have THREAD_GET_CONTEXT access.
* \param Context A variable which receives the context
* structure.
*/
NTSTATUS PhGetThreadContext(
__in HANDLE ThreadHandle,
__inout PCONTEXT Context
)
{
if (PhKphHandle)
{
return KphGetContextThread(PhKphHandle, ThreadHandle, Context);
}
else
{
return NtGetContextThread(ThreadHandle, Context);
}
}
/**
* Sets the processor context of a thread.
*
* \param ThreadHandle A handle to a thread. The handle must
* have THREAD_SET_CONTEXT access.
* \param Context The new context structure.
*/
NTSTATUS PhSetThreadContext(
__in HANDLE ThreadHandle,
__in PCONTEXT Context
)
{
if (PhKphHandle)
{
return KphSetContextThread(PhKphHandle, ThreadHandle, Context);
}
else
{
return NtSetContextThread(ThreadHandle, Context);
}
}
/**
* Copies memory from another process into the current process.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_VM_READ access.
* \param BaseAddress The address from which memory is to be copied.
* \param Buffer A buffer which receives the copied memory.
* \param BufferSize The number of bytes to copy.
* \param NumberOfBytesRead A variable which receives the number
* of bytes copied to the buffer.
*/
NTSTATUS PhReadVirtualMemory(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out_bcount(BufferSize) PVOID Buffer,
__in SIZE_T BufferSize,
__out_opt PSIZE_T NumberOfBytesRead
)
{
// KphReadVirtualMemory is much slower than
// NtReadVirtualMemory, so we'll stick to
// the using the original system call.
return NtReadVirtualMemory(
ProcessHandle,
BaseAddress,
Buffer,
BufferSize,
NumberOfBytesRead
);
}
/**
* Copies memory from the current process into another process.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_VM_WRITE access.
* \param BaseAddress The address to which memory is to be copied.
* \param Buffer A buffer which contains the memory to copy.
* \param BufferSize The number of bytes to copy.
* \param NumberOfBytesWritten A variable which receives the number
* of bytes copied from the buffer.
*/
NTSTATUS PhWriteVirtualMemory(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__in_bcount(BufferSize) PVOID Buffer,
__in SIZE_T BufferSize,
__out_opt PSIZE_T NumberOfBytesWritten
)
{
return NtWriteVirtualMemory(
ProcessHandle,
BaseAddress,
Buffer,
BufferSize,
NumberOfBytesWritten
);
}
/**
* Queries variable-sized information for a process.
* The function allocates a buffer to contain the information.
*
* \param ProcessHandle A handle to a process. The access required
* depends on the information class specified.
* \param ProcessInformationClass The information class to retrieve.
* \param Buffer A variable which receives a pointer to a buffer
* containing the information. You must free the buffer using
* PhFree() when you no longer need it.
*/
NTSTATUS PhpQueryProcessVariableSize(
__in HANDLE ProcessHandle,
__in PROCESS_INFORMATION_CLASS ProcessInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG returnLength = 0;
NtQueryInformationProcess(
ProcessHandle,
ProcessInformationClass,
NULL,
0,
&returnLength
);
buffer = PhAllocate(returnLength);
status = NtQueryInformationProcess(
ProcessHandle,
ProcessInformationClass,
buffer,
returnLength,
&returnLength
);
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
/**
* Gets basic information for a process.
*
* \param ProcessHandle A handle to a process. The handle must have
* PROCESS_QUERY_LIMITED_INFORMATION access.
* \param BasicInformation A variable which receives the information.
*/
NTSTATUS PhGetProcessBasicInformation(
__in HANDLE ProcessHandle,
__out PPROCESS_BASIC_INFORMATION BasicInformation
)
{
return NtQueryInformationProcess(
ProcessHandle,
ProcessBasicInformation,
BasicInformation,
sizeof(PROCESS_BASIC_INFORMATION),
NULL
);
}
/**
* Gets the file name of the process' image.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_QUERY_LIMITED_INFORMATION access.
* \param FileName A variable which receives a pointer to a
* string containing the file name. You must free the string
* using PhDereferenceObject() when you no longer need it.
*/
NTSTATUS PhGetProcessImageFileName(
__in HANDLE ProcessHandle,
__out PPH_STRING *FileName
)
{
NTSTATUS status;
PUNICODE_STRING fileName;
status = PhpQueryProcessVariableSize(
ProcessHandle,
ProcessImageFileName,
&fileName
);
if (!NT_SUCCESS(status))
return status;
*FileName = PhCreateStringEx(fileName->Buffer, fileName->Length);
PhFree(fileName);
return status;
}
/**
* Gets a string stored in a process' parameters structure.
*
* \param ProcessHandle A handle to a process. The handle must
* have PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ
* access.
* \param Offset The string to retrieve.
* \param String A variable which receives a pointer to the
* requested string. You must free the string using
* PhDereferenceObject() when you no longer need it.
*
* \retval STATUS_INVALID_PARAMETER_2 An invalid value was
* specified in the Offset parameter.
*/
NTSTATUS PhGetProcessPebString(
__in HANDLE ProcessHandle,
__in PH_PEB_OFFSET Offset,
__out PPH_STRING *String
)
{
NTSTATUS status;
PPH_STRING string;
ULONG offset;
PROCESS_BASIC_INFORMATION basicInfo;
PVOID processParameters;
UNICODE_STRING unicodeString;
switch (Offset)
{
case PhpoCurrentDirectory:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, CurrentDirectory);
break;
case PhpoDllPath:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, DllPath);
break;
case PhpoImagePathName:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, ImagePathName);
break;
case PhpoCommandLine:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, CommandLine);
break;
case PhpoWindowTitle:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, WindowTitle);
break;
case PhpoDesktopName:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, DesktopInfo);
break;
case PhpoShellInfo:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, ShellInfo);
break;
case PhpoRuntimeData:
offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, RuntimeData);
break;
default:
return STATUS_INVALID_PARAMETER_2;
}
// Get the PEB address.
if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo)))
return status;
// Read the address of the process parameters.
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)),
&processParameters,
sizeof(PVOID),
NULL
)))
return status;
// Read the string structure.
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(processParameters, offset),
&unicodeString,
sizeof(UNICODE_STRING),
NULL
)))
return status;
string = PhCreateStringEx(NULL, unicodeString.Length);
// Read the string contents.
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
unicodeString.Buffer,
string->Buffer,
string->Length,
NULL
)))
{
PhDereferenceObject(string);
return status;
}
*String = string;
return status;
}
/**
* Gets a process' session ID.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION access.
* \param SessionId A variable which receives the
* process' session ID.
*/
NTSTATUS PhGetProcessSessionId(
__in HANDLE ProcessHandle,
__out PULONG SessionId
)
{
NTSTATUS status;
PROCESS_SESSION_INFORMATION sessionInfo;
status = NtQueryInformationProcess(
ProcessHandle,
ProcessSessionInformation,
&sessionInfo,
sizeof(PROCESS_SESSION_INFORMATION),
NULL
);
if (NT_SUCCESS(status))
{
*SessionId = sessionInfo.SessionId;
}
return status;
}
/**
* Gets a process' no-execute status.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION access.
* \param ExecuteFlags A variable which receives the
* no-execute flags.
*/
NTSTATUS PhGetProcessExecuteFlags(
__in HANDLE ProcessHandle,
__out PULONG ExecuteFlags
)
{
return NtQueryInformationProcess(
ProcessHandle,
ProcessExecuteFlags,
ExecuteFlags,
sizeof(ULONG),
NULL
);
}
/**
* Gets whether a process is running under 32-bit
* emulation.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION access.
* \param IsWow64 A variable which receives a boolean
* indicating whether the process is 32-bit.
*
* \remarks Do not use this function under a 32-bit
* environment.
*/
NTSTATUS PhGetProcessIsWow64(
__in HANDLE ProcessHandle,
__out PBOOLEAN IsWow64
)
{
NTSTATUS status;
PVOID wow64;
status = NtQueryInformationProcess(
ProcessHandle,
ProcessWow64Information,
&wow64,
sizeof(PVOID),
NULL
);
if (NT_SUCCESS(status))
{
*IsWow64 = !!wow64;
}
return status;
}
/**
* Gets whether a process is being debugged.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION access.
* \param IsBeingDebugged A variable which receives a boolean
* indicating whether the process is being debugged.
*/
NTSTATUS PhGetProcessIsBeingDebugged(
__in HANDLE ProcessHandle,
__out PBOOLEAN IsBeingDebugged
)
{
NTSTATUS status;
PVOID debugPort;
status = NtQueryInformationProcess(
ProcessHandle,
ProcessDebugPort,
&debugPort,
sizeof(PVOID),
NULL
);
if (NT_SUCCESS(status))
{
*IsBeingDebugged = !!debugPort;
}
return status;
}
/**
* Gets a handle to a process' debug object.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION access.
* \param DebugObjectHandle A variable which receives a
* handle to the debug object associated with the process.
* You must close the handle when you no longer need it.
*
* \retval STATUS_PORT_NOT_SET The process is not being
* debugged and has no associated debug object.
*/
NTSTATUS PhGetProcessDebugObject(
__in HANDLE ProcessHandle,
__out PHANDLE DebugObjectHandle
)
{
return NtQueryInformationProcess(
ProcessHandle,
ProcessDebugObjectHandle,
DebugObjectHandle,
sizeof(HANDLE),
NULL
);
}
/**
* Gets a process' I/O priority.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION access.
* \param IoPriority A variable which receives the I/O
* priority of the process.
*/
NTSTATUS PhGetProcessIoPriority(
__in HANDLE ProcessHandle,
__out PULONG IoPriority
)
{
return NtQueryInformationProcess(
ProcessHandle,
ProcessIoPriority,
IoPriority,
sizeof(ULONG),
NULL
);
}
/**
* Gets a process' page priority.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION access.
* \param PagePriority A variable which receives the page
* priority of the process.
*/
NTSTATUS PhGetProcessPagePriority(
__in HANDLE ProcessHandle,
__out PULONG PagePriority
)
{
return NtQueryInformationProcess(
ProcessHandle,
ProcessPagePriority,
PagePriority,
sizeof(ULONG),
NULL
);
}
/**
* Gets whether the process is running under the POSIX
* subsystem.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION and
* PROCESS_VM_READ access.
* \param IsPosix A variable which receives a boolean
* indicating whether the process is running under the
* POSIX subsystem.
*/
NTSTATUS PhGetProcessIsPosix(
__in HANDLE ProcessHandle,
__out PBOOLEAN IsPosix
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
ULONG imageSubsystem;
status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo);
if (!NT_SUCCESS(status))
return status;
status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageSubsystem)),
&imageSubsystem,
sizeof(ULONG),
NULL
);
if (NT_SUCCESS(status))
{
*IsPosix = imageSubsystem == IMAGE_SUBSYSTEM_POSIX_CUI;
}
return status;
}
/**
* Gets a process' cycle count.
*
* \param ProcessHandle A handle to a process. The handle must have
* PROCESS_QUERY_LIMITED_INFORMATION access.
* \param CycleTime A variable which receives the 64-bit cycle
* time.
*/
NTSTATUS PhGetProcessCycleTime(
__in HANDLE ProcessHandle,
__out PULONG64 CycleTime
)
{
NTSTATUS status;
PROCESS_CYCLE_TIME_INFORMATION cycleTimeInfo;
status = NtQueryInformationProcess(
ProcessHandle,
ProcessCycleTime,
&cycleTimeInfo,
sizeof(PROCESS_CYCLE_TIME_INFORMATION),
NULL
);
if (!NT_SUCCESS(status))
return status;
*CycleTime = cycleTimeInfo.AccumulatedCycles.QuadPart;
return status;
}
/**
* Gets the POSIX command line of a process.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION and
* PROCESS_VM_READ access.
* \param CommandLine A variable which receives a pointer
* to a string containing the POSIX command line. You must
* free the string using PhDereferenceObject() when you no
* longer need it.
*
* \retval STATUS_UNSUCCESSFUL The command line of the
* process could not be retrieved because it is too large.
*
* \remarks Do not use this function on a non-POSIX process.
* Use the PhGetProcessIsPosix() function to determine
* whether a process is running under the POSIX subsystem.
*/
NTSTATUS PhGetProcessPosixCommandLine(
__in HANDLE ProcessHandle,
__out PPH_STRING *CommandLine
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
PVOID processParameters;
UNICODE_STRING commandLine;
status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo);
if (!NT_SUCCESS(status))
return status;
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)),
&processParameters,
sizeof(PVOID),
NULL
)))
return status;
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, CommandLine)),
&commandLine,
sizeof(UNICODE_STRING),
NULL
)))
return status;
// See ProcessHandle.cs from PH 1.x for how POSIX command lines work.
{
PVOID pointer = NULL;
PVOID firstPointer = NULL;
PVOID lastPointer = NULL;
BOOLEAN zeroReached = FALSE;
ULONG i;
ULONG commandLineChunkSize;
PCHAR commandLineChunk;
i = 0;
// Read the first command line pointer + the first environment pointer.
while (i < sizeof(PVOID) * 100) // reasonable limit
{
PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(commandLine.Buffer, i),
&pointer,
sizeof(PVOID),
NULL
);
if (pointer && !firstPointer)
firstPointer = pointer;
if (zeroReached)
lastPointer = pointer;
i += sizeof(PVOID);
if (zeroReached)
break;
else if (!pointer)
zeroReached = TRUE;
pointer = NULL;
}
commandLineChunkSize = (ULONG)((PBYTE)lastPointer - (PBYTE)firstPointer);
// Set a limit on how much we're going to read.
if (commandLineChunkSize > 0x1000)
return STATUS_UNSUCCESSFUL;
commandLineChunk = PhAllocate(commandLineChunkSize);
// Read the chunk.
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
firstPointer,
commandLineChunk,
commandLineChunkSize,
NULL
)))
{
PhFree(commandLineChunk);
return status;
}
// Replace the nulls in the chunk with spaces.
for (i = 0; i < commandLineChunkSize; i++)
{
if (commandLineChunk[i] == 0)
{
commandLineChunk[i] = ' ';
// Trim the last null/space.
if (i == commandLineChunkSize - 1)
{
commandLineChunkSize--;
break;
}
}
}
*CommandLine = PhCreateStringFromAnsiEx(
commandLineChunk,
commandLineChunkSize
);
PhFree(commandLineChunk);
return status;
}
}
/**
* Gets a process' environment variables.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION and PROCESS_VM_READ
* access.
* \param Variables A variable which will receive a pointer
* to an array of \ref PH_ENVIRONMENT_VARIABLE structures, one
* for each variable. You must free the array using
* PhFreeProcessEnvironmentVariables() when you no longer
* need it.
* \param NumberOfVariables A variable which will receive
* the number of environment variables returned in the
* array.
*/
NTSTATUS PhGetProcessEnvironmentVariables(
__in HANDLE ProcessHandle,
__out PPH_ENVIRONMENT_VARIABLE *Variables,
__out PULONG NumberOfVariables
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
PVOID processParameters;
PVOID environment;
ULONG environmentLength;
PWSTR buffer;
PPH_LIST pairsList;
ULONG i;
PPH_ENVIRONMENT_VARIABLE variables;
status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo);
if (!NT_SUCCESS(status))
return status;
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)),
&processParameters,
sizeof(PVOID),
NULL
)))
return status;
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment)),
&environment,
sizeof(PVOID),
NULL
)))
return status;
{
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQueryEx(
ProcessHandle,
environment,
&mbi,
sizeof(MEMORY_BASIC_INFORMATION)
))
return NTSTATUS_FROM_WIN32(GetLastError());
environmentLength = (ULONG)(mbi.RegionSize -
((ULONG_PTR)environment - (ULONG_PTR)mbi.BaseAddress));
}
// Read in the entire region of memory.
buffer = PhAllocate(environmentLength);
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
environment,
buffer,
environmentLength,
NULL
)))
{
PhFree(buffer);
return status;
}
// Create a list of pairs.
pairsList = PhCreateList(20);
i = 0;
environmentLength /= 2;
while (TRUE)
{
ULONG oldIndex;
// Go through the buffer and stop when we reach
// the end of the pair string.
oldIndex = i;
while (i < environmentLength && buffer[i] != 0)
i++;
// Check if the environment block has ended
// (the last string is zero-length) or if we
// overran the buffer.
if (i == oldIndex || i >= environmentLength)
break;
// Save a pointer to the current pair string.
PhAddListItem(pairsList, &buffer[oldIndex]);
i++; // skip the null terminator
}
// Create the output array.
variables = PhAllocate(sizeof(PH_ENVIRONMENT_VARIABLE) * pairsList->Count);
for (i = 0; i < pairsList->Count; i++)
{
PWSTR pairPointer;
PWSTR valuePointer;
pairPointer = (PWSTR)pairsList->Items[i];
valuePointer = wcschr(pairPointer, '=');
variables[i].Name = PhCreateStringEx(pairPointer, (valuePointer - pairPointer) * 2);
variables[i].Value = PhCreateString(valuePointer + 1);
}
*Variables = variables;
*NumberOfVariables = pairsList->Count;
PhDereferenceObject(pairsList);
PhFree(buffer);
return STATUS_SUCCESS;
}
/**
* Frees an array of environment variables returned
* by PhGetProcessEnvironmentVariables().
*
* \param Variables A pointer to an array of
* environment variables.
* \param NumberOfVariables The number of environment
* variables in the array.
*/
VOID PhFreeProcessEnvironmentVariables(
__in PPH_ENVIRONMENT_VARIABLE Variables,
__in ULONG NumberOfVariables
)
{
ULONG i;
for (i = 0; i < NumberOfVariables; i++)
{
PhDereferenceObject(Variables[i].Name);
PhDereferenceObject(Variables[i].Value);
}
PhFree(Variables);
}
/**
* Gets the file name of a mapped section.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION access.
* \param BaseAddress The base address of the section view.
* \param FileName A variable which receives a pointer to
* a string containing the file name of the section. You
* must free the string using PhDereferenceObject() when
* you no longer need it.
*/
NTSTATUS PhGetProcessMappedFileName(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out PPH_STRING *FileName
)
{
NTSTATUS status;
PVOID buffer;
SIZE_T bufferSize;
SIZE_T returnLength;
PUNICODE_STRING unicodeString;
bufferSize = 0x100;
buffer = PhAllocate(bufferSize);
status = NtQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
if (status == STATUS_BUFFER_OVERFLOW)
{
PhFree(buffer);
bufferSize = returnLength;
buffer = PhAllocate(bufferSize);
status = NtQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
unicodeString = (PUNICODE_STRING)buffer;
*FileName = PhCreateStringEx(
unicodeString->Buffer,
unicodeString->Length
);
PhFree(buffer);
return status;
}
/**
* Gets working set information for a process.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION and PROCESS_VM_READ
* access.
* \param WorkingSetInformation A variable which receives a
* pointer to the information. You must free the buffer using
* PhFree() when you no longer need it.
*/
NTSTATUS PhGetProcessWorkingSetInformation(
__in HANDLE ProcessHandle,
__out PMEMORY_WORKING_SET_INFORMATION *WorkingSetInformation
)
{
NTSTATUS status;
PVOID buffer;
SIZE_T bufferSize;
bufferSize = 0x8000;
buffer = PhAllocate(bufferSize);
while ((status = NtQueryVirtualMemory(
ProcessHandle,
NULL,
MemoryWorkingSetInformation,
buffer,
bufferSize,
NULL
)) == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(buffer);
bufferSize *= 2;
// Fail if we're resizing the buffer to over
// 16 MB.
if (bufferSize > PH_LARGE_BUFFER_SIZE)
return STATUS_INSUFFICIENT_RESOURCES;
buffer = PhAllocate(bufferSize);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
*WorkingSetInformation = (PMEMORY_WORKING_SET_INFORMATION)buffer;
return status;
}
/**
* Gets working set counters for a process.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_INFORMATION and PROCESS_VM_READ
* access.
* \param WsCounters A variable which receives the
* counters.
*/
NTSTATUS PhGetProcessWsCounters(
__in HANDLE ProcessHandle,
__out PPH_PROCESS_WS_COUNTERS WsCounters
)
{
NTSTATUS status;
PMEMORY_WORKING_SET_INFORMATION wsInfo;
PH_PROCESS_WS_COUNTERS wsCounters;
ULONG i;
if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation(
ProcessHandle,
&wsInfo
)))
return status;
memset(&wsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS));
for (i = 0; i < wsInfo->NumberOfEntries; i++)
{
wsCounters.NumberOfPages++;
if (wsInfo->WorkingSetInfo[i].ShareCount > 1)
wsCounters.NumberOfSharedPages++;
if (wsInfo->WorkingSetInfo[i].ShareCount == 0)
wsCounters.NumberOfPrivatePages++;
if (wsInfo->WorkingSetInfo[i].Shared)
wsCounters.NumberOfShareablePages++;
}
PhFree(wsInfo);
*WsCounters = wsCounters;
return status;
}
/**
* Sets a process' I/O priority.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_SET_INFORMATION access.
* \param IoPriority The new I/O priority.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhSetProcessIoPriority(
__in HANDLE ProcessHandle,
__in ULONG IoPriority
)
{
return KphSetInformationProcess(
PhKphHandle,
ProcessHandle,
ProcessIoPriority,
&IoPriority,
sizeof(ULONG)
);
}
/**
* Sets a process' no-execute status.
*
* \param ProcessHandle A handle to a process.
* \param ExecuteFlags The new no-execute flags.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhSetProcessExecuteFlags(
__in HANDLE ProcessHandle,
__in ULONG ExecuteFlags
)
{
return KphSetExecuteOptions(
PhKphHandle,
ProcessHandle,
ExecuteFlags
);
}
/**
* Causes a process to load a DLL.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION
* and PROCESS_VM_WRITE access.
* \param FileName The file name of the DLL to inject.
* \param Timeout The timeout, in milliseconds, for the
* process to load the DLL.
*
* \remarks If the process does not load the DLL before
* the timeout expires it may crash. Choose the timeout
* value carefully.
*/
NTSTATUS PhInjectDllProcess(
__in HANDLE ProcessHandle,
__in PWSTR FileName,
__in ULONG Timeout
)
{
NTSTATUS status;
PVOID baseAddress = NULL;
SIZE_T stringSize;
SIZE_T allocSize;
HANDLE threadHandle;
stringSize = (wcslen(FileName) + 1) * 2;
allocSize = stringSize;
if (!NT_SUCCESS(status = NtAllocateVirtualMemory(
ProcessHandle,
&baseAddress,
0,
&allocSize,
MEM_COMMIT,
PAGE_READWRITE
)))
return status;
if (!NT_SUCCESS(status = PhWriteVirtualMemory(
ProcessHandle,
baseAddress,
FileName,
stringSize,
NULL
)))
goto FreeExit;
// Vista seems to support native threads better than XP.
if (WindowsVersion >= WINDOWS_VISTA)
{
if (!NT_SUCCESS(status = RtlCreateUserThread(
ProcessHandle,
NULL,
FALSE,
0,
0,
0,
(PUSER_THREAD_START_ROUTINE)PhGetProcAddress(L"kernel32.dll", "LoadLibraryW"),
baseAddress,
&threadHandle,
NULL
)))
goto FreeExit;
}
else
{
if (!CreateRemoteThread(
ProcessHandle,
NULL,
0,
(PTHREAD_START_ROUTINE)PhGetProcAddress(L"kernel32.dll", "LoadLibraryW"),
baseAddress,
0,
NULL
))
{
status = NTSTATUS_FROM_WIN32(GetLastError());
goto FreeExit;
}
}
// Wait for the thread to finish.
WaitForSingleObject(threadHandle, Timeout);
NtClose(threadHandle);
FreeExit:
// Size needs to be zero if we're freeing.
allocSize = 0;
NtFreeVirtualMemory(
ProcessHandle,
&baseAddress,
&allocSize,
MEM_RELEASE
);
return status;
}
/**
* Causes a process to unload a DLL.
*
* \param ProcessHandle A handle to a process. The handle
* must have PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD,
* PROCESS_VM_OPERATION, PROCESS_VM_READ and PROCESS_VM_WRITE access.
* \param BaseAddress The base address of the DLL to unload.
* \param Timeout The timeout, in milliseconds, for the
* process to unload the DLL.
*/
NTSTATUS PhUnloadDllProcess(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__in ULONG Timeout
)
{
NTSTATUS status;
HANDLE threadHandle;
THREAD_BASIC_INFORMATION basicInfo;
status = PhSetProcessModuleLoadCount(
ProcessHandle,
BaseAddress,
1
);
if (!NT_SUCCESS(status))
return status;
if (WindowsVersion >= WINDOWS_VISTA)
{
status = RtlCreateUserThread(
ProcessHandle,
NULL,
FALSE,
0,
0,
0,
(PUSER_THREAD_START_ROUTINE)PhGetProcAddress(L"ntdll.dll", "LdrUnloadDll"),
BaseAddress,
&threadHandle,
NULL
);
}
else
{
if (!(threadHandle = CreateRemoteThread(
ProcessHandle,
NULL,
0,
(PTHREAD_START_ROUTINE)PhGetProcAddress(L"kernel32.dll", "FreeLibrary"),
BaseAddress,
0,
NULL
)))
{
status = NTSTATUS_FROM_WIN32(GetLastError());
}
}
if (!NT_SUCCESS(status))
return status;
if (WaitForSingleObject(threadHandle, Timeout) == WAIT_OBJECT_0)
{
status = PhGetThreadBasicInformation(threadHandle, &basicInfo);
if (NT_SUCCESS(status))
{
status = basicInfo.ExitStatus;
}
}
else
{
status = STATUS_TIMEOUT;
}
NtClose(threadHandle);
return status;
}
/**
* Gets basic information for a thread.
*
* \param ThreadHandle A handle to a thread. The handle must have
* THREAD_QUERY_LIMITED_INFORMATION access.
* \param BasicInformation A variable which receives the information.
*/
NTSTATUS PhGetThreadBasicInformation(
__in HANDLE ThreadHandle,
__out PTHREAD_BASIC_INFORMATION BasicInformation
)
{
return NtQueryInformationThread(
ThreadHandle,
ThreadBasicInformation,
BasicInformation,
sizeof(THREAD_BASIC_INFORMATION),
NULL
);
}
/**
* Gets a thread's I/O priority.
*
* \param ThreadHandle A handle to a thread. The handle
* must have THREAD_QUERY_LIMITED_INFORMATION access.
* \param IoPriority A variable which receives the I/O
* priority of the thread.
*/
NTSTATUS PhGetThreadIoPriority(
__in HANDLE ThreadHandle,
__out PULONG IoPriority
)
{
return NtQueryInformationThread(
ThreadHandle,
ThreadIoPriority,
IoPriority,
sizeof(ULONG),
NULL
);
}
/**
* Gets a thread's page priority.
*
* \param ThreadHandle A handle to a thread. The handle
* must have THREAD_QUERY_LIMITED_INFORMATION access.
* \param PagePriority A variable which receives the page
* priority of the thread.
*/
NTSTATUS PhGetThreadPagePriority(
__in HANDLE ThreadHandle,
__out PULONG PagePriority
)
{
return NtQueryInformationThread(
ThreadHandle,
ThreadPagePriority,
PagePriority,
sizeof(ULONG),
NULL
);
}
/**
* Gets a thread's cycle count.
*
* \param ThreadHandle A handle to a thread. The handle must have
* THREAD_QUERY_LIMITED_INFORMATION access.
* \param CycleTime A variable which receives the 64-bit cycle
* time.
*/
NTSTATUS PhGetThreadCycleTime(
__in HANDLE ThreadHandle,
__out PULONG64 CycleTime
)
{
NTSTATUS status;
THREAD_CYCLE_TIME_INFORMATION cycleTimeInfo;
status = NtQueryInformationThread(
ThreadHandle,
ThreadCycleTime,
&cycleTimeInfo,
sizeof(THREAD_CYCLE_TIME_INFORMATION),
NULL
);
if (!NT_SUCCESS(status))
return status;
*CycleTime = cycleTimeInfo.AccumulatedCycles.QuadPart;
return status;
}
/**
* Sets a thread's I/O priority.
*
* \param ThreadHandle A handle to a thread. The handle
* must have THREAD_SET_LIMITED_INFORMATION access.
* \param IoPriority The new I/O priority.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhSetThreadIoPriority(
__in HANDLE ThreadHandle,
__in ULONG IoPriority
)
{
return KphSetInformationThread(
PhKphHandle,
ThreadHandle,
ThreadIoPriority,
&IoPriority,
sizeof(ULONG)
);
}
/**
* Converts a STACKFRAME64 structure to a
* PH_THREAD_STACK_FRAME structure.
*
* \param StackFrame64 A pointer to the STACKFRAME64 structure
* to convert.
* \param ThreadStackFrame A pointer to the resulting
* PH_THREAD_STACK_FRAME structure.
*/
VOID PhpConvertStackFrame(
__in STACKFRAME64 *StackFrame64,
__out PPH_THREAD_STACK_FRAME ThreadStackFrame
)
{
ULONG i;
ThreadStackFrame->PcAddress = (PVOID)StackFrame64->AddrPC.Offset;
ThreadStackFrame->ReturnAddress = (PVOID)StackFrame64->AddrReturn.Offset;
ThreadStackFrame->FrameAddress = (PVOID)StackFrame64->AddrFrame.Offset;
ThreadStackFrame->StackAddress = (PVOID)StackFrame64->AddrStack.Offset;
ThreadStackFrame->BStoreAddress = (PVOID)StackFrame64->AddrBStore.Offset;
for (i = 0; i < 4; i++)
ThreadStackFrame->Params[i] = (PVOID)StackFrame64->Params[i];
}
/**
* Walks a thread's stack.
*
* \param ThreadHandle A handle to a thread. The handle
* must have THREAD_GET_CONTEXT and THREAD_SUSPEND_RESUME
* access. The handle can have any access for kernel stack
* walking.
* \param ProcessHandle A handle to the thread's parent
* process. The handle must have PROCESS_QUERY_INFORMATION
* and PROCESS_VM_READ access. If a symbol provider is
* being used, pass its process handle.
* \param Flags A combination of flags.
* \li \c PH_WALK_I386_STACK Walks the x86 stack. On AMD64
* systems this flag walks the WOW64 stack.
* \li \c PH_WALK_AMD64_STACK Walks the AMD64 stack. On x86
* systems this flag is ignored.
* \li \c PH_WALK_KERNEL_STACK Walks the kernel stack. This
* flag is ignored if there is no active KProcessHacker
* connection.
* \param Callback A callback function which is executed
* for each stack frame.
* \param Context A user-defined value to pass to the
* callback function.
*/
NTSTATUS PhWalkThreadStack(
__in HANDLE ThreadHandle,
__in_opt HANDLE ProcessHandle,
__in ULONG Flags,
__in PPH_WALK_THREAD_STACK_CALLBACK Callback,
__in PVOID Context
)
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN suspended = FALSE;
BOOLEAN processOpened = FALSE;
// Open a handle to the process if we weren't given one.
if (!ProcessHandle)
{
if (PhKphHandle)
{
if (!NT_SUCCESS(status = KphOpenThreadProcess(
PhKphHandle,
&ProcessHandle,
ThreadHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
)))
return status;
}
else
{
THREAD_BASIC_INFORMATION basicInfo;
if (!NT_SUCCESS(status = PhGetThreadBasicInformation(
ThreadHandle,
&basicInfo
)))
return status;
if (!NT_SUCCESS(status = PhOpenProcess(
&ProcessHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
basicInfo.ClientId.UniqueProcess
)))
return status;
}
processOpened = TRUE;
}
// Suspend the thread to avoid inaccurate results.
if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL)))
suspended = TRUE;
// Kernel stack walk.
if ((Flags & PH_WALK_KERNEL_STACK) && PhKphHandle)
{
PVOID stack[62 - 1]; // 62 limit for XP and Server 2003.
ULONG capturedFrames;
ULONG i;
if (NT_SUCCESS(KphCaptureStackBackTraceThread(
PhKphHandle,
ThreadHandle,
1,
sizeof(stack) / sizeof(PVOID),
stack,
&capturedFrames,
NULL
)))
{
PH_THREAD_STACK_FRAME threadStackFrame;
memset(&threadStackFrame, 0, sizeof(PH_THREAD_STACK_FRAME));
for (i = 0; i < capturedFrames; i++)
{
threadStackFrame.PcAddress = stack[i];
if (!Callback(&threadStackFrame, Context))
{
goto ResumeExit;
}
}
}
}
#ifdef _M_X64
if (Flags & PH_WALK_AMD64_STACK)
{
STACKFRAME64 stackFrame;
PH_THREAD_STACK_FRAME threadStackFrame;
CONTEXT context;
context.ContextFlags = CONTEXT_ALL;
if (!NT_SUCCESS(status = PhGetThreadContext(
ThreadHandle,
&context
)))
goto SkipAmd64Stack;
memset(&stackFrame, 0, sizeof(STACKFRAME64));
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrPC.Offset = context.Rip;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context.Rsp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context.Rbp;
while (TRUE)
{
if (!PhStackWalk(
IMAGE_FILE_MACHINE_AMD64,
ProcessHandle,
ThreadHandle,
&stackFrame,
&context,
NULL,
NULL,
NULL,
NULL
))
break;
// If we have an invalid instruction pointer, break.
if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1)
break;
// Convert the stack frame and execute the callback.
PhpConvertStackFrame(&stackFrame, &threadStackFrame);
if (!Callback(&threadStackFrame, Context))
goto ResumeExit;
}
}
SkipAmd64Stack:
#endif
// x86/WOW64 stack walk.
if (Flags & PH_WALK_I386_STACK)
{
STACKFRAME64 stackFrame;
PH_THREAD_STACK_FRAME threadStackFrame;
#ifndef _M_X64
CONTEXT context;
context.ContextFlags = CONTEXT_ALL;
if (!NT_SUCCESS(status = PhGetThreadContext(
ThreadHandle,
&context
)))
goto SkipI386Stack;
#else
WOW64_CONTEXT context;
context.ContextFlags = WOW64_CONTEXT_ALL;
if (!NT_SUCCESS(status = NtQueryInformationThread(
ThreadHandle,
ThreadWow64Context,
&context,
sizeof(WOW64_CONTEXT),
NULL
)))
goto SkipI386Stack;
#endif
memset(&stackFrame, 0, sizeof(STACKFRAME64));
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrPC.Offset = context.Eip;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context.Esp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context.Ebp;
while (TRUE)
{
if (!PhStackWalk(
IMAGE_FILE_MACHINE_I386,
ProcessHandle,
ThreadHandle,
&stackFrame,
&context,
NULL,
NULL,
NULL,
NULL
))
break;
// If we have an invalid instruction pointer, break.
if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1)
break;
// Convert the stack frame and execute the callback.
PhpConvertStackFrame(&stackFrame, &threadStackFrame);
if (!Callback(&threadStackFrame, Context))
goto ResumeExit;
}
}
SkipI386Stack:
ResumeExit:
if (suspended)
NtResumeThread(ThreadHandle, NULL);
if (processOpened)
NtClose(ProcessHandle);
return status;
}
/**
* Queries variable-sized information for a token.
* The function allocates a buffer to contain the information.
*
* \param TokenHandle A handle to a token. The access required
* depends on the information class specified.
* \param TokenInformationClass The information class to retrieve.
* \param Buffer A variable which receives a pointer to a buffer
* containing the information. You must free the buffer using
* PhFree() when you no longer need it.
*/
NTSTATUS PhpQueryTokenVariableSize(
__in HANDLE TokenHandle,
__in TOKEN_INFORMATION_CLASS TokenInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG returnLength = 0;
NtQueryInformationToken(
TokenHandle,
TokenInformationClass,
NULL,
0,
&returnLength
);
buffer = PhAllocate(returnLength);
status = NtQueryInformationToken(
TokenHandle,
TokenInformationClass,
buffer,
returnLength,
&returnLength
);
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
/**
* Gets a token's user.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param User A variable which receives a pointer to
* a structure containing the token's user. You must
* free the structure using PhFree() when you no longer
* need it.
*/
NTSTATUS PhGetTokenUser(
__in HANDLE TokenHandle,
__out PTOKEN_USER *User
)
{
return PhpQueryTokenVariableSize(
TokenHandle,
TokenUser,
User
);
}
/**
* Gets a token's session ID.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param SessionId A variable which receives the
* session ID.
*/
NTSTATUS PhGetTokenSessionId(
__in HANDLE TokenHandle,
__out PULONG SessionId
)
{
ULONG returnLength;
return NtQueryInformationToken(
TokenHandle,
TokenSessionId,
SessionId,
sizeof(ULONG),
&returnLength
);
}
/**
* Gets a token's elevation type.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param ElevationType A variable which receives the
* elevation type.
*/
NTSTATUS PhGetTokenElevationType(
__in HANDLE TokenHandle,
__out PTOKEN_ELEVATION_TYPE ElevationType
)
{
ULONG returnLength;
return NtQueryInformationToken(
TokenHandle,
TokenElevationType,
ElevationType,
sizeof(TOKEN_ELEVATION_TYPE),
&returnLength
);
}
/**
* Gets whether a token is elevated.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param Elevated A variable which receives a
* boolean indicating whether the token is elevated.
*/
NTSTATUS PhGetTokenIsElevated(
__in HANDLE TokenHandle,
__out PBOOLEAN Elevated
)
{
NTSTATUS status;
TOKEN_ELEVATION elevation;
ULONG returnLength;
status = NtQueryInformationToken(
TokenHandle,
TokenElevation,
&elevation,
sizeof(TOKEN_ELEVATION),
&returnLength
);
if (NT_SUCCESS(status))
{
*Elevated = !!elevation.TokenIsElevated;
}
return status;
}
/**
* Gets a token's statistics.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param Statistics A variable which receives the
* token's statistics.
*/
NTSTATUS PhGetTokenStatistics(
__in HANDLE TokenHandle,
__out PTOKEN_STATISTICS Statistics
)
{
ULONG returnLength;
return NtQueryInformationToken(
TokenHandle,
TokenStatistics,
Statistics,
sizeof(TOKEN_STATISTICS),
&returnLength
);
}
/**
* Gets a token's groups.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param Groups A variable which receives a pointer to
* a structure containing the token's groups. You must
* free the structure using PhFree() when you no longer
* need it.
*/
NTSTATUS PhGetTokenGroups(
__in HANDLE TokenHandle,
__out PTOKEN_GROUPS *Groups
)
{
return PhpQueryTokenVariableSize(
TokenHandle,
TokenGroups,
Groups
);
}
/**
* Gets a token's privileges.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param Privileges A variable which receives a pointer to
* a structure containing the token's privileges. You must
* free the structure using PhFree() when you no longer
* need it.
*/
NTSTATUS PhGetTokenPrivileges(
__in HANDLE TokenHandle,
__out PTOKEN_PRIVILEGES *Privileges
)
{
return PhpQueryTokenVariableSize(
TokenHandle,
TokenPrivileges,
Privileges
);
}
/**
* Gets whether virtualization is allowed for a token.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param IsVirtualizationAllowed A variable which receives
* a boolean indicating whether virtualization is allowed
* for the token.
*/
NTSTATUS PhGetTokenIsVirtualizationAllowed(
__in HANDLE TokenHandle,
__out PBOOLEAN IsVirtualizationAllowed
)
{
NTSTATUS status;
ULONG returnLength;
ULONG virtualizationAllowed;
status = NtQueryInformationToken(
TokenHandle,
TokenVirtualizationAllowed,
&virtualizationAllowed,
sizeof(ULONG),
&returnLength
);
if (!NT_SUCCESS(status))
return status;
*IsVirtualizationAllowed = !!virtualizationAllowed;
return status;
}
/**
* Gets whether virtualization is enabled for a token.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param IsVirtualizationEnabled A variable which receives
* a boolean indicating whether virtualization is enabled
* for the token.
*/
NTSTATUS PhGetTokenIsVirtualizationEnabled(
__in HANDLE TokenHandle,
__out PBOOLEAN IsVirtualizationEnabled
)
{
NTSTATUS status;
ULONG returnLength;
ULONG virtualizationEnabled;
status = NtQueryInformationToken(
TokenHandle,
TokenVirtualizationEnabled,
&virtualizationEnabled,
sizeof(ULONG),
&returnLength
);
if (!NT_SUCCESS(status))
return status;
*IsVirtualizationEnabled = !!virtualizationEnabled;
return status;
}
NTSTATUS PhSetTokenSessionId(
__in HANDLE TokenHandle,
__in ULONG SessionId
)
{
return NtSetInformationToken(
TokenHandle,
TokenSessionId,
&SessionId,
sizeof(ULONG)
);
}
/**
* Modifies a token privilege.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_ADJUST_PRIVILEGES access.
* \param PrivilegeName The name of the privilege to
* modify. If this parameter is NULL, you must specify
* a LUID in the \a PrivilegeLuid parameter.
* \param PrivilegeLuid The LUID of the privilege to
* modify. If this parameter is NULL, you must specify
* a name in the \a PrivilegeName parameter.
* \param Attributes The new attributes of the privilege.
*/
BOOLEAN PhSetTokenPrivilege(
__in HANDLE TokenHandle,
__in_opt PWSTR PrivilegeName,
__in_opt PLUID PrivilegeLuid,
__in ULONG Attributes
)
{
TOKEN_PRIVILEGES privileges = { 0 };
privileges.PrivilegeCount = 1;
privileges.Privileges[0].Attributes = Attributes;
if (PrivilegeLuid)
{
privileges.Privileges[0].Luid = *PrivilegeLuid;
}
else if (PrivilegeName)
{
if (!PhLookupPrivilegeValue(
PrivilegeName,
&privileges.Privileges[0].Luid
))
return FALSE;
}
else
{
return FALSE;
}
if (!AdjustTokenPrivileges(
TokenHandle,
FALSE,
&privileges,
0,
NULL,
NULL
))
return FALSE;
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
return FALSE;
return TRUE;
}
/**
* Sets whether virtualization is enabled for a token.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_WRITE access.
* \param IsVirtualizationEnabled A boolean indicating
* whether virtualization is to be enabled for the token.
*/
NTSTATUS PhSetTokenIsVirtualizationEnabled(
__in HANDLE TokenHandle,
__in BOOLEAN IsVirtualizationEnabled
)
{
ULONG virtualizationEnabled;
virtualizationEnabled = IsVirtualizationEnabled;
return NtSetInformationToken(
TokenHandle,
TokenVirtualizationEnabled,
&virtualizationEnabled,
sizeof(ULONG)
);
}
/**
* Gets a token's integrity level.
*
* \param TokenHandle A handle to a token. The handle
* must have TOKEN_QUERY access.
* \param IntegrityLevel A variable which receives
* the integrity level of the token.
* \param IntegrityString A variable which receives a
* pointer to a string containing a string representation
* of the integrity level. You must free the string
* using PhDereferenceObject() when you no longer need it.
*/
NTSTATUS PhGetTokenIntegrityLevel(
__in HANDLE TokenHandle,
__out_opt PPH_INTEGRITY IntegrityLevel,
__out_opt PPH_STRING *IntegrityString
)
{
NTSTATUS status;
PTOKEN_GROUPS groups;
ULONG i;
PPH_STRING sidName = NULL;
PH_INTEGRITY integrityLevel;
PPH_STRING integrityString;
status = PhGetTokenGroups(TokenHandle, &groups);
if (!NT_SUCCESS(status))
return status;
// Look for an integrity SID.
for (i = 0; i < groups->GroupCount; i++)
{
if (groups->Groups[i].Attributes & SE_GROUP_INTEGRITY_ENABLED)
{
PhLookupSid(groups->Groups[i].Sid, &sidName, NULL, NULL);
break;
}
}
PhFree(groups);
// Did we get the SID name successfully?
if (!sidName)
return STATUS_UNSUCCESSFUL;
// Look for " Mandatory Level".
i = PhStringIndexOfString(sidName, 0, L" Mandatory Level");
if (i == -1)
{
PhDereferenceObject(sidName);
return STATUS_UNSUCCESSFUL;
}
// Get the string before the suffix.
integrityString = PhSubstring(sidName, 0, i);
PhDereferenceObject(sidName);
// Compute the integer integrity level.
if (PhStringEquals2(integrityString, L"Untrusted", FALSE))
integrityLevel = PiUntrusted;
else if (PhStringEquals2(integrityString, L"Low", FALSE))
integrityLevel = PiLow;
else if (PhStringEquals2(integrityString, L"Medium", FALSE))
integrityLevel = PiMedium;
else if (PhStringEquals2(integrityString, L"High", FALSE))
integrityLevel = PiHigh;
else if (PhStringEquals2(integrityString, L"System", FALSE))
integrityLevel = PiSystem;
else if (PhStringEquals2(integrityString, L"Installer", FALSE))
integrityLevel = PiInstaller;
else
integrityLevel = -1;
if (integrityLevel == -1)
{
PhDereferenceObject(integrityString);
return STATUS_UNSUCCESSFUL;
}
if (IntegrityLevel)
{
*IntegrityLevel = integrityLevel;
}
if (IntegrityString)
{
*IntegrityString = integrityString;
}
else
{
PhDereferenceObject(integrityString);
}
return status;
}
NTSTATUS PhGetFileSize(
__in HANDLE FileHandle,
__out PLARGE_INTEGER Size
)
{
NTSTATUS status;
FILE_STANDARD_INFORMATION standardInfo;
IO_STATUS_BLOCK isb;
status = NtQueryInformationFile(
FileHandle,
&isb,
&standardInfo,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation
);
if (!NT_SUCCESS(status))
return status;
*Size = standardInfo.EndOfFile;
return status;
}
NTSTATUS PhpQueryTransactionManagerVariableSize(
__in HANDLE TransactionManagerHandle,
__in TRANSACTIONMANAGER_INFORMATION_CLASS TransactionManagerInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 0x100;
if (!NtQueryInformationTransactionManager)
return STATUS_NOT_SUPPORTED;
buffer = PhAllocate(bufferSize);
while (TRUE)
{
status = NtQueryInformationTransactionManager(
TransactionManagerHandle,
TransactionManagerInformationClass,
buffer,
bufferSize,
NULL
);
if (status == STATUS_BUFFER_OVERFLOW)
{
PhFree(buffer);
bufferSize *= 2;
if (bufferSize > 1 * 1024 * 1024)
return STATUS_INSUFFICIENT_RESOURCES;
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
NTSTATUS PhGetTransactionManagerBasicInformation(
__in HANDLE TransactionManagerHandle,
__out PTRANSACTIONMANAGER_BASIC_INFORMATION BasicInformation
)
{
if (NtQueryInformationTransactionManager)
{
return NtQueryInformationTransactionManager(
TransactionManagerHandle,
TransactionManagerBasicInformation,
BasicInformation,
sizeof(TRANSACTIONMANAGER_BASIC_INFORMATION),
NULL
);
}
else
{
return STATUS_NOT_SUPPORTED;
}
}
NTSTATUS PhGetTransactionManagerLogFileName(
__in HANDLE TransactionManagerHandle,
__out PPH_STRING *LogFileName
)
{
NTSTATUS status;
PTRANSACTIONMANAGER_LOGPATH_INFORMATION logPathInfo;
status = PhpQueryTransactionManagerVariableSize(
TransactionManagerHandle,
TransactionManagerLogPathInformation,
&logPathInfo
);
if (!NT_SUCCESS(status))
return status;
*LogFileName = PhCreateStringEx(
logPathInfo->LogPath,
logPathInfo->LogPathLength
);
PhFree(logPathInfo);
return status;
}
NTSTATUS PhpQueryTransactionVariableSize(
__in HANDLE TransactionHandle,
__in TRANSACTION_INFORMATION_CLASS TransactionInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 0x100;
if (!NtQueryInformationTransaction)
return STATUS_NOT_SUPPORTED;
buffer = PhAllocate(bufferSize);
while (TRUE)
{
status = NtQueryInformationTransaction(
TransactionHandle,
TransactionInformationClass,
buffer,
bufferSize,
NULL
);
if (status == STATUS_BUFFER_OVERFLOW)
{
PhFree(buffer);
bufferSize *= 2;
if (bufferSize > 1 * 1024 * 1024)
return STATUS_INSUFFICIENT_RESOURCES;
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
NTSTATUS PhGetTransactionBasicInformation(
__in HANDLE TransactionHandle,
__out PTRANSACTION_BASIC_INFORMATION BasicInformation
)
{
if (NtQueryInformationTransaction)
{
return NtQueryInformationTransaction(
TransactionHandle,
TransactionBasicInformation,
BasicInformation,
sizeof(TRANSACTION_BASIC_INFORMATION),
NULL
);
}
else
{
return STATUS_NOT_SUPPORTED;
}
}
NTSTATUS PhGetTransactionPropertiesInformation(
__in HANDLE TransactionHandle,
__out_opt PLARGE_INTEGER Timeout,
__out_opt TRANSACTION_OUTCOME *Outcome,
__out_opt PPH_STRING *Description
)
{
NTSTATUS status;
PTRANSACTION_PROPERTIES_INFORMATION propertiesInfo;
status = PhpQueryTransactionVariableSize(
TransactionHandle,
TransactionPropertiesInformation,
&propertiesInfo
);
if (!NT_SUCCESS(status))
return status;
if (Timeout)
{
*Timeout = propertiesInfo->Timeout;
}
if (Outcome)
{
*Outcome = propertiesInfo->Outcome;
}
if (Description)
{
*Description = PhCreateStringEx(
propertiesInfo->Description,
propertiesInfo->DescriptionLength
);
}
PhFree(propertiesInfo);
return status;
}
NTSTATUS PhpQueryResourceManagerVariableSize(
__in HANDLE ResourceManagerHandle,
__in RESOURCEMANAGER_INFORMATION_CLASS ResourceManagerInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 0x100;
if (!NtQueryInformationResourceManager)
return STATUS_NOT_SUPPORTED;
buffer = PhAllocate(bufferSize);
while (TRUE)
{
status = NtQueryInformationResourceManager(
ResourceManagerHandle,
ResourceManagerInformationClass,
buffer,
bufferSize,
NULL
);
if (status == STATUS_BUFFER_OVERFLOW)
{
PhFree(buffer);
bufferSize *= 2;
if (bufferSize > 1 * 1024 * 1024)
return STATUS_INSUFFICIENT_RESOURCES;
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
NTSTATUS PhGetResourceManagerBasicInformation(
__in HANDLE ResourceManagerHandle,
__out_opt PGUID Guid,
__out_opt PPH_STRING *Description
)
{
NTSTATUS status;
PRESOURCEMANAGER_BASIC_INFORMATION basicInfo;
status = PhpQueryResourceManagerVariableSize(
ResourceManagerHandle,
ResourceManagerBasicInformation,
&basicInfo
);
if (!NT_SUCCESS(status))
return status;
if (Guid)
{
*Guid = basicInfo->ResourceManagerId;
}
if (Description)
{
*Description = PhCreateStringEx(
basicInfo->Description,
basicInfo->DescriptionLength
);
}
PhFree(basicInfo);
return status;
}
NTSTATUS PhGetEnlistmentBasicInformation(
__in HANDLE EnlistmentHandle,
__out PENLISTMENT_BASIC_INFORMATION BasicInformation
)
{
if (NtQueryInformationEnlistment)
{
return NtQueryInformationEnlistment(
EnlistmentHandle,
EnlistmentBasicInformation,
BasicInformation,
sizeof(ENLISTMENT_BASIC_INFORMATION),
NULL
);
}
else
{
return STATUS_NOT_SUPPORTED;
}
}
typedef struct _OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT
{
NTSTATUS Status;
PVOID BaseAddress;
HANDLE DriverHandle;
} OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT, *POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT;
BOOLEAN NTAPI PhpOpenDriverByBaseAddressCallback(
__in PPH_STRING Name,
__in PPH_STRING TypeName,
__in PVOID Context
)
{
NTSTATUS status;
POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context = Context;
PPH_STRING driverName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE driverHandle;
DRIVER_BASIC_INFORMATION basicInfo;
driverName = PhConcatStrings2(L"\\Driver\\", Name->Buffer);
InitializeObjectAttributes(
&objectAttributes,
&driverName->us,
0,
NULL,
NULL
);
status = KphOpenDriver(PhKphHandle, &driverHandle, &objectAttributes);
PhDereferenceObject(driverName);
if (!NT_SUCCESS(status))
return TRUE;
status = KphQueryInformationDriver(
PhKphHandle,
driverHandle,
DriverBasicInformation,
&basicInfo,
sizeof(DRIVER_BASIC_INFORMATION),
NULL
);
if (NT_SUCCESS(status))
{
if (basicInfo.DriverStart == context->BaseAddress)
{
context->Status = STATUS_SUCCESS;
context->DriverHandle = driverHandle;
return FALSE;
}
}
NtClose(driverHandle);
return TRUE;
}
/**
* Opens a driver object using a base address.
*
* \param DriverHandle A variable which receives a
* handle to the driver object.
* \param BaseAddress The base address of the driver
* to open.
*
* \retval STATUS_OBJECT_NAME_NOT_FOUND The driver could
* not be found.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhOpenDriverByBaseAddress(
__out PHANDLE DriverHandle,
__in PVOID BaseAddress
)
{
NTSTATUS status;
UNICODE_STRING driverDirectoryName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE driverDirectoryHandle;
OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context;
RtlInitUnicodeString(
&driverDirectoryName,
L"\\Driver"
);
InitializeObjectAttributes(
&objectAttributes,
&driverDirectoryName,
0,
NULL,
NULL
);
if (!NT_SUCCESS(status = NtOpenDirectoryObject(
&driverDirectoryHandle,
DIRECTORY_QUERY,
&objectAttributes
)))
return status;
context.Status = STATUS_OBJECT_NAME_NOT_FOUND;
context.BaseAddress = BaseAddress;
status = PhEnumDirectoryObjects(
driverDirectoryHandle,
PhpOpenDriverByBaseAddressCallback,
&context
);
NtClose(driverDirectoryHandle);
if (!NT_SUCCESS(status) && !NT_SUCCESS(context.Status))
return status;
if (NT_SUCCESS(context.Status))
{
*DriverHandle = context.DriverHandle;
}
return context.Status;
}
/**
* Queries variable-sized information for a driver.
* The function allocates a buffer to contain the information.
*
* \param DriverHandle A handle to a driver. The access required
* depends on the information class specified.
* \param DriverInformationClass The information class to retrieve.
* \param Buffer A variable which receives a pointer to a buffer
* containing the information. You must free the buffer using
* PhFree() when you no longer need it.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhpQueryDriverVariableSize(
__in HANDLE DriverHandle,
__in DRIVER_INFORMATION_CLASS DriverInformationClass,
__out PPVOID Buffer
)
{
NTSTATUS status;
PVOID buffer;
ULONG returnLength = 0;
KphQueryInformationDriver(
PhKphHandle,
DriverHandle,
DriverInformationClass,
NULL,
0,
&returnLength
);
buffer = PhAllocate(returnLength);
status = KphQueryInformationDriver(
PhKphHandle,
DriverHandle,
DriverInformationClass,
buffer,
returnLength,
&returnLength
);
if (NT_SUCCESS(status))
{
*Buffer = buffer;
}
else
{
PhFree(buffer);
}
return status;
}
/**
* Gets the service key name of a driver.
*
* \param DriverHandle A handle to a driver.
* \param ServiceKeyName A variable which receives a pointer
* to a string containing the service key name. You must
* free the string using PhDereferenceObject() when you no
* longer need it.
*
* \remarks This function requires a valid KProcessHacker
* handle.
*/
NTSTATUS PhGetDriverServiceKeyName(
__in HANDLE DriverHandle,
__out PPH_STRING *ServiceKeyName
)
{
NTSTATUS status;
PUNICODE_STRING unicodeString;
if (!NT_SUCCESS(status = PhpQueryDriverVariableSize(
DriverHandle,
DriverServiceKeyNameInformation,
&unicodeString
)))
return status;
*ServiceKeyName = PhCreateStringEx(
unicodeString->Buffer,
unicodeString->Length
);
PhFree(unicodeString);
return status;
}
NTSTATUS PhpUnloadDriver(
__in PPH_STRING ServiceKeyName
)
{
NTSTATUS status;
ULONG win32Result;
HKEY servicesKeyHandle;
HKEY serviceKeyHandle;
ULONG disposition;
PPH_STRING servicePath;
if ((win32Result = RegCreateKey(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services",
&servicesKeyHandle
)) != ERROR_SUCCESS)
{
return NTSTATUS_FROM_WIN32(win32Result);
}
if ((win32Result = RegCreateKeyEx(
servicesKeyHandle,
ServiceKeyName->Buffer,
0,
NULL,
0,
KEY_WRITE,
NULL,
&serviceKeyHandle,
&disposition
)) != ERROR_SUCCESS)
{
RegCloseKey(servicesKeyHandle);
return NTSTATUS_FROM_WIN32(win32Result);
}
if (disposition == REG_CREATED_NEW_KEY)
{
ULONG dword;
PPH_STRING string;
// Set up the required values.
dword = 1;
RegSetValueEx(serviceKeyHandle, L"ErrorControl", 0, REG_DWORD, (PBYTE)&dword, sizeof(ULONG));
RegSetValueEx(serviceKeyHandle, L"Start", 0, REG_DWORD, (PBYTE)&dword, sizeof(ULONG));
RegSetValueEx(serviceKeyHandle, L"Type", 0, REG_DWORD, (PBYTE)&dword, sizeof(ULONG));
// Use a bogus name.
string = PhCreateString(L"\\SystemRoot\\system32\\drivers\\ntfs.sys");
RegSetValueEx(serviceKeyHandle, L"ImagePath", 0, REG_SZ, (PBYTE)string->Buffer, string->Length + 2);
PhDereferenceObject(string);
}
servicePath = PhConcatStrings2(
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\",
ServiceKeyName->Buffer
);
status = NtUnloadDriver(&servicePath->us);
PhDereferenceObject(servicePath);
if (disposition == REG_CREATED_NEW_KEY)
{
RegDeleteKey(servicesKeyHandle, ServiceKeyName->Buffer);
}
RegCloseKey(serviceKeyHandle);
RegCloseKey(servicesKeyHandle);
return status;
}
/**
* Unloads a driver.
*
* \param BaseAddress The base address of the driver.
* This parameter can be NULL if a value is specified
* in \c Name.
* \param Name The base name of the driver. This
* parameter can be NULL if a value is specified in
* \c BaseAddress and KProcessHacker is loaded.
*
* \retval STATUS_INVALID_PARAMETER_MIX Both
* \c BaseAddress and \c Name were null, or \c Name
* was not specified and KProcessHacker is not loaded.
* \retval STATUS_OBJECT_NAME_NOT_FOUND The driver
* could not be found.
*/
NTSTATUS PhUnloadDriver(
__in_opt PVOID BaseAddress,
__in_opt PWSTR Name
)
{
NTSTATUS status;
HANDLE driverHandle;
PPH_STRING serviceKeyName = NULL;
if (!BaseAddress && !Name)
return STATUS_INVALID_PARAMETER_MIX;
if (!Name && !PhKphHandle)
return STATUS_INVALID_PARAMETER_MIX;
// Try to get the service key name by scanning the
// Driver directory.
if (PhKphHandle && BaseAddress)
{
if (NT_SUCCESS(PhOpenDriverByBaseAddress(
&driverHandle,
BaseAddress
)))
{
PhGetDriverServiceKeyName(driverHandle, &serviceKeyName);
NtClose(driverHandle);
}
}
// Use the base name if we didn't get the service
// key name.
if (!serviceKeyName && Name)
{
PPH_STRING name;
name = PhCreateString(Name);
// Remove the extension if it is present.
if (PhStringEndsWith2(name, L".sys", TRUE))
{
serviceKeyName = PhSubstring(name, 0, name->Length / 2 - 4);
PhDereferenceObject(name);
}
else
{
serviceKeyName = name;
}
}
if (!serviceKeyName)
return STATUS_OBJECT_NAME_NOT_FOUND;
status = PhpUnloadDriver(serviceKeyName);
PhDereferenceObject(serviceKeyName);
return status;
}
/**
* Duplicates a handle.
*
* \param SourceProcessHandle A handle to the source
* process. The handle must have PROCESS_DUP_HANDLE
* access.
* \param SourceHandle The handle to duplicate from
* the source process.
* \param TargetProcessHandle A handle to the target
* process. If DUPLICATE_CLOSE_SOURCE is specified
* in the \a Options parameter, this parameter can be
* NULL.
* \param TargetHandle A variable which receives
* the new handle in the target process. If
* DUPLICATE_CLOSE_SOURCE is specified in the \a Options
* parameter, this parameter can be NULL.
* \param DesiredAccess The desired access to the
* object referenced by the source handle.
* \param HandleAttributes The attributes to apply
* to the new handle.
* \param Options The options to use when duplicating
* the handle.
*/
NTSTATUS PhDuplicateObject(
__in HANDLE SourceProcessHandle,
__in HANDLE SourceHandle,
__in_opt HANDLE TargetProcessHandle,
__out_opt PHANDLE TargetHandle,
__in ACCESS_MASK DesiredAccess,
__in ULONG HandleAttributes,
__in ULONG Options
)
{
if (PhKphHandle)
{
return KphDuplicateObject(
PhKphHandle,
SourceProcessHandle,
SourceHandle,
TargetProcessHandle,
TargetHandle,
DesiredAccess,
HandleAttributes,
Options
);
}
else
{
return NtDuplicateObject(
SourceProcessHandle,
SourceHandle,
TargetProcessHandle,
TargetHandle,
DesiredAccess,
HandleAttributes,
Options
);
}
}
NTSTATUS PhpEnumProcessModules(
__in HANDLE ProcessHandle,
__in PPHP_ENUM_PROCESS_MODULES_CALLBACK Callback,
__in PVOID Context1,
__in PVOID Context2
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
PVOID ldr;
PEB_LDR_DATA pebLdrData;
PLIST_ENTRY startLink;
PLIST_ENTRY currentLink;
LDR_DATA_TABLE_ENTRY currentEntry;
ULONG i;
// Get the PEB address.
status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo);
if (!NT_SUCCESS(status))
return status;
// Read the address of the loader data.
status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, Ldr)),
&ldr,
sizeof(PVOID),
NULL
);
if (!NT_SUCCESS(status))
return status;
// Read the loader data.
status = PhReadVirtualMemory(
ProcessHandle,
ldr,
&pebLdrData,
sizeof(PEB_LDR_DATA),
NULL
);
if (!NT_SUCCESS(status))
return status;
if (!pebLdrData.Initialized)
return STATUS_UNSUCCESSFUL;
// Traverse the linked list (in load order).
i = 0;
startLink = PTR_ADD_OFFSET(ldr, FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModuleList));
currentLink = pebLdrData.InLoadOrderModuleList.Flink;
while (
currentLink != startLink &&
i <= PH_ENUM_PROCESS_MODULES_ITERS
)
{
PVOID addressOfEntry;
addressOfEntry = CONTAINING_RECORD(currentLink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
status = PhReadVirtualMemory(
ProcessHandle,
addressOfEntry,
&currentEntry,
LDR_DATA_TABLE_ENTRY_SIZE,
NULL
);
if (!NT_SUCCESS(status))
return status;
// Make sure the entry is valid.
if (currentEntry.DllBase)
{
// Execute the callback.
if (!Callback(
ProcessHandle,
&currentEntry,
addressOfEntry,
Context1,
Context2
))
break;
}
currentLink = currentEntry.InLoadOrderLinks.Flink;
i++;
}
return status;
}
BOOLEAN NTAPI PhpEnumProcessModulesCallback(
__in HANDLE ProcessHandle,
__in PLDR_DATA_TABLE_ENTRY Entry,
__in PVOID AddressOfEntry,
__in PVOID Context1,
__in PVOID Context2
)
{
BOOLEAN cont;
PWSTR baseDllNameBuffer;
PWSTR fullDllNameBuffer;
// Read the base DLL name string and add a null terminator.
baseDllNameBuffer = PhAllocate(Entry->BaseDllName.Length + 2);
if (NT_SUCCESS(PhReadVirtualMemory(
ProcessHandle,
Entry->BaseDllName.Buffer,
baseDllNameBuffer,
Entry->BaseDllName.Length,
NULL
)))
{
baseDllNameBuffer[Entry->BaseDllName.Length / 2] = 0;
Entry->BaseDllName.Buffer = baseDllNameBuffer;
}
// Read the full DLL name string and add a null terminator.
fullDllNameBuffer = PhAllocate(Entry->FullDllName.Length + 2);
if (NT_SUCCESS(PhReadVirtualMemory(
ProcessHandle,
Entry->FullDllName.Buffer,
fullDllNameBuffer,
Entry->FullDllName.Length,
NULL
)))
{
fullDllNameBuffer[Entry->FullDllName.Length / 2] = 0;
Entry->FullDllName.Buffer = fullDllNameBuffer;
}
// Execute the callback.
cont = ((PPH_ENUM_PROCESS_MODULES_CALLBACK)Context1)(Entry, Context2);
PhFree(baseDllNameBuffer);
PhFree(fullDllNameBuffer);
return cont;
}
/**
* Enumerates the modules loaded by a process.
*
* \param ProcessHandle A handle to a process. The
* handle must have PROCESS_QUERY_LIMITED_INFORMATION
* and PROCESS_VM_READ access.
* \param Callback A callback function which is
* executed for each process module.
* \param Context A user-defined value to pass to the
* callback function.
*/
NTSTATUS PhEnumProcessModules(
__in HANDLE ProcessHandle,
__in PPH_ENUM_PROCESS_MODULES_CALLBACK Callback,
__in PVOID Context
)
{
return PhpEnumProcessModules(
ProcessHandle,
PhpEnumProcessModulesCallback,
Callback,
Context
);
}
typedef struct _SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT
{
NTSTATUS Status;
PVOID BaseAddress;
USHORT LoadCount;
} SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT, *PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT;
BOOLEAN NTAPI PhpSetProcessModuleLoadCountCallback(
__in HANDLE ProcessHandle,
__in PLDR_DATA_TABLE_ENTRY Entry,
__in PVOID AddressOfEntry,
__in PVOID Context1,
__in PVOID Context2
)
{
PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context = Context1;
if (Entry->DllBase == context->BaseAddress)
{
Entry->LoadCount = context->LoadCount;
context->Status = PhWriteVirtualMemory(
ProcessHandle,
AddressOfEntry,
Entry,
LDR_DATA_TABLE_ENTRY_SIZE,
NULL
);
return FALSE;
}
return TRUE;
}
/**
* Sets the load count of a process module.
*
* \param ProcessHandle A handle to a process. The
* handle must have PROCESS_QUERY_LIMITED_INFORMATION,
* PROCESS_VM_READ and PROCESS_VM_WRITE access.
* \param BaseAddress The base address of a module.
* \param LoadCount The new load count of the module.
*
* \retval STATUS_DLL_NOT_FOUND The module was not found.
*/
NTSTATUS PhSetProcessModuleLoadCount(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__in USHORT LoadCount
)
{
NTSTATUS status;
SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context;
context.Status = STATUS_DLL_NOT_FOUND;
context.BaseAddress = BaseAddress;
context.LoadCount = LoadCount;
status = PhpEnumProcessModules(
ProcessHandle,
PhpSetProcessModuleLoadCountCallback,
&context,
NULL
);
if (!NT_SUCCESS(status))
return status;
return context.Status;
}
/**
* Enumerates the modules loaded by the kernel.
*
* \param Modules A variable which receives a pointer
* to a structure containing information about
* the kernel modules. You must free the structure
* using PhFree() when you no longer need it.
*/
NTSTATUS PhEnumKernelModules(
__out PRTL_PROCESS_MODULES *Modules
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 2048;
buffer = PhAllocate(bufferSize);
status = NtQuerySystemInformation(
SystemModuleInformation,
buffer,
bufferSize,
&bufferSize
);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(buffer);
buffer = PhAllocate(bufferSize);
status = NtQuerySystemInformation(
SystemModuleInformation,
buffer,
bufferSize,
&bufferSize
);
}
if (!NT_SUCCESS(status))
return status;
*Modules = buffer;
return status;
}
/**
* Gets the file name of the kernel image.
*
* \return A pointer to a string containing the
* kernel image file name. You must free the string
* using PhDereferenceObject() when you no longer
* need it.
*/
PPH_STRING PhGetKernelFileName()
{
PRTL_PROCESS_MODULES modules;
PPH_STRING fileName = NULL;
if (!NT_SUCCESS(PhEnumKernelModules(&modules)))
return NULL;
if (modules->NumberOfModules >= 1)
{
fileName = PhCreateStringFromAnsi(modules->Modules[0].FullPathName);
}
PhFree(modules);
return fileName;
}
/**
* Enumerates the running processes.
*
* \param Processes A variable which receives a
* pointer to a buffer containing process
* information. You must free the buffer using
* PhFree() when you no longer need it.
*
* \remarks You can use the \ref PH_FIRST_PROCESS
* and \ref PH_NEXT_PROCESS macros to process the
* information contained in the buffer.
*/
NTSTATUS PhEnumProcesses(
__out PPVOID Processes
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 2048;
buffer = PhAllocate(bufferSize);
while (TRUE)
{
status = NtQuerySystemInformation(
SystemProcessInformation,
buffer,
bufferSize,
&bufferSize
);
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(buffer);
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
*Processes = buffer;
return status;
}
/**
* Finds the process information structure for a
* specific process.
*
* \param Processes A pointer to a buffer returned
* by PhEnumProcesses().
* \param ProcessId The ID of the process.
*
* \return A pointer to the process information
* structure for the specified process, or NULL if
* the structure could not be found.
*/
PSYSTEM_PROCESS_INFORMATION PhFindProcessInformation(
__in PVOID Processes,
__in HANDLE ProcessId
)
{
PSYSTEM_PROCESS_INFORMATION process;
process = PH_FIRST_PROCESS(Processes);
do
{
if (process->UniqueProcessId == ProcessId)
return process;
} while (process = PH_NEXT_PROCESS(process));
return NULL;
}
/**
* Enumerates all open handles.
*
* \param Handles A variable which receives a pointer
* to a structure containing information about all
* opened handles. You must free the structure using
* PhFree() when you no longer need it.
*
* \retval STATUS_INSUFFICIENT_RESOURCES The
* handle information returned by the kernel is too
* large.
*/
NTSTATUS PhEnumHandles(
__out PSYSTEM_HANDLE_INFORMATION *Handles
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize = 0x1000;
buffer = PhAllocate(bufferSize);
while ((status = NtQuerySystemInformation(
SystemHandleInformation,
buffer,
bufferSize,
NULL
)) == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(buffer);
bufferSize *= 2;
// Fail if we're resizing the buffer to something
// very large.
if (bufferSize > PH_LARGE_BUFFER_SIZE)
return STATUS_INSUFFICIENT_RESOURCES;
buffer = PhAllocate(bufferSize);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
*Handles = (PSYSTEM_HANDLE_INFORMATION)buffer;
return status;
}
/**
* Enumerates the objects in a directory object.
*
* \param DirectoryHandle A handle to a directory. The
* handle must have DIRECTORY_QUERY access.
* \param Callback A callback function which is
* executed for each object.
* \param Context A user-defined value to pass to the
* callback function.
*/
NTSTATUS PhEnumDirectoryObjects(
__in HANDLE DirectoryHandle,
__in PPH_ENUM_DIRECTORY_OBJECTS Callback,
__in PVOID Context
)
{
NTSTATUS status;
ULONG context = 0;
BOOLEAN firstTime = TRUE;
ULONG bufferSize;
POBJECT_DIRECTORY_INFORMATION buffer;
ULONG i;
BOOLEAN cont;
bufferSize = 0x200;
buffer = PhAllocate(bufferSize);
while (TRUE)
{
// Get a batch of entries.
while ((status = NtQueryDirectoryObject(
DirectoryHandle,
buffer,
bufferSize,
FALSE,
firstTime,
&context,
NULL
)) == STATUS_MORE_ENTRIES)
{
// Check if we have at least one entry. If not,
// we'll double the buffer size and try again.
if (buffer[0].Name.Buffer)
break;
// Make sure we don't use too much memory.
if (bufferSize > PH_LARGE_BUFFER_SIZE)
{
PhFree(buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
PhFree(buffer);
bufferSize *= 2;
buffer = PhAllocate(bufferSize);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
// Read the batch and execute the callback function
// for each object.
i = 0;
cont = TRUE;
while (TRUE)
{
POBJECT_DIRECTORY_INFORMATION info;
PPH_STRING name;
PPH_STRING typeName;
info = &buffer[i];
if (!info->Name.Buffer)
break;
name = PhCreateStringEx(info->Name.Buffer, info->Name.Length);
typeName = PhCreateStringEx(info->TypeName.Buffer, info->TypeName.Length);
cont = Callback(name, typeName, Context);
PhDereferenceObject(name);
PhDereferenceObject(typeName);
if (!cont)
break;
i++;
}
if (!cont)
break;
if (status != STATUS_MORE_ENTRIES)
break;
firstTime = FALSE;
}
PhFree(buffer);
return STATUS_SUCCESS;
}
/**
* Initializes the DOS device names module.
*/
VOID PhInitializeDosDeviceNames()
{
ULONG i;
for (i = 0; i < 26; i++)
PhDosDeviceNames[i] = PhAllocate(64 * sizeof(WCHAR));
PhInitializeQueuedLock(&PhDosDeviceNamesLock);
}
/**
* Refreshes the DOS device names array.
*/
VOID PhRefreshDosDeviceNames()
{
WCHAR deviceName[3];
ULONG i;
deviceName[1] = ':';
deviceName[2] = 0;
for (i = 0; i < 26; i++)
{
deviceName[0] = (WCHAR)('A' + i);
PhAcquireQueuedLockExclusiveFast(&PhDosDeviceNamesLock);
if (!QueryDosDevice(deviceName, PhDosDeviceNames[i], 64))
PhDosDeviceNames[i][0] = 0;
PhReleaseQueuedLockExclusiveFast(&PhDosDeviceNamesLock);
}
}
/**
* Resolves a NT path into a DOS path.
*
* \param Name A string containing the path to resolve.
*
* \return A pointer to a string containing the DOS
* path. You must free the string using PhDereferenceObject()
* when you no longer need it.
*/
PPH_STRING PhResolveDevicePrefix(
__in PPH_STRING Name
)
{
ULONG i;
PPH_STRING newName = NULL;
// Go through the DOS devices and try to find a matching prefix.
for (i = 0; i < 26; i++)
{
PWSTR prefix;
ULONG prefixLength;
BOOLEAN isPrefix = FALSE;
PhAcquireQueuedLockSharedFast(&PhDosDeviceNamesLock);
prefix = PhDosDeviceNames[i];
prefixLength = (ULONG)wcslen(prefix);
if (prefixLength > 0)
isPrefix = PhStringStartsWith2(Name, prefix, TRUE);
PhReleaseQueuedLockSharedFast(&PhDosDeviceNamesLock);
if (isPrefix)
{
newName = PhCreateStringEx(NULL, 4 + Name->Length - prefixLength * 2);
newName->Buffer[0] = (WCHAR)('A' + i);
newName->Buffer[1] = ':';
memcpy(
&newName->Buffer[2],
&Name->Buffer[prefixLength],
Name->Length - prefixLength * 2
);
break;
}
}
return newName;
}
/**
* Converts a file name into DOS format.
*
* \param FileName A string containing a file name.
*
* \return A pointer to a string containing the DOS
* file name. You must free the string using
* PhDereferenceObject() when you no longer need it.
*
* \remarks This function may convert NT object
* name paths to invalid ones. If the path to be
* converted is not necessarily a file name, use
* PhResolveDevicePrefix().
*/
PPH_STRING PhGetFileName(
__in PPH_STRING FileName
)
{
PPH_STRING newFileName;
newFileName = FileName;
// "\??\" refers to \GLOBAL??. Just remove it.
if (PhStringStartsWith2(FileName, L"\\??\\", FALSE))
{
newFileName = PhCreateStringEx(NULL, FileName->Length - 8);
memcpy(newFileName->Buffer, &FileName->Buffer[4], FileName->Length - 8);
}
// "\SystemRoot" means "C:\Windows".
else if (PhStringStartsWith2(FileName, L"\\SystemRoot", TRUE))
{
PPH_STRING systemDirectory = PhGetSystemDirectory();
if (systemDirectory)
{
ULONG indexOfLastBackslash = PhStringLastIndexOfChar(systemDirectory, 0, '\\');
newFileName = PhCreateStringEx(NULL, indexOfLastBackslash * 2 + FileName->Length - 22);
memcpy(newFileName->Buffer, systemDirectory->Buffer, indexOfLastBackslash * 2);
memcpy(&newFileName->Buffer[indexOfLastBackslash], &FileName->Buffer[11], FileName->Length - 22);
PhDereferenceObject(systemDirectory);
}
}
else
{
PPH_STRING resolvedName;
resolvedName = PhResolveDevicePrefix(FileName);
if (resolvedName)
{
newFileName = resolvedName;
}
else
{
// We didn't find a match.
// If the file name starts with a backslash, prepend the system drive.
if (PhStringStartsWith2(newFileName, L"\\Windows", TRUE))
{
PPH_STRING systemDirectory = PhGetSystemDirectory();
newFileName = PhCreateStringEx(NULL, FileName->Length + 4);
newFileName->Buffer[0] = systemDirectory->Buffer[0];
newFileName->Buffer[1] = ':';
memcpy(&newFileName->Buffer[2], FileName->Buffer, FileName->Length);
PhDereferenceObject(systemDirectory);
}
else
{
// Just return the supplied file name. Note that we need
// to add a reference.
PhReferenceObject(newFileName);
}
}
}
return newFileName;
}
typedef struct _ENUM_GENERIC_PROCESS_MODULES_CONTEXT
{
PPH_ENUM_GENERIC_MODULES_CALLBACK Callback;
PVOID Context;
PPH_LIST BaseAddressList;
} ENUM_GENERIC_PROCESS_MODULES_CONTEXT, *PENUM_GENERIC_PROCESS_MODULES_CONTEXT;
static BOOLEAN EnumGenericProcessModulesCallback(
__in PLDR_DATA_TABLE_ENTRY Module,
__in PVOID Context
)
{
PENUM_GENERIC_PROCESS_MODULES_CONTEXT context;
PH_MODULE_INFO moduleInfo;
PPH_STRING fileName;
BOOLEAN cont;
context = (PENUM_GENERIC_PROCESS_MODULES_CONTEXT)Context;
// Check if we have a duplicate base address.
if (PhIndexOfListItem(context->BaseAddressList, Module->DllBase) != -1)
{
return TRUE;
}
else
{
PhAddListItem(context->BaseAddressList, Module->DllBase);
}
fileName = PhCreateStringEx(
Module->FullDllName.Buffer,
Module->FullDllName.Length
);
moduleInfo.BaseAddress = Module->DllBase;
moduleInfo.Size = Module->SizeOfImage;
moduleInfo.EntryPoint = Module->EntryPoint;
moduleInfo.Flags = Module->Flags;
moduleInfo.Name = PhCreateStringEx(
Module->BaseDllName.Buffer,
Module->BaseDllName.Length
);
moduleInfo.FileName = PhGetFileName(fileName);
PhDereferenceObject(fileName);
cont = context->Callback(&moduleInfo, context->Context);
PhDereferenceObject(moduleInfo.Name);
PhDereferenceObject(moduleInfo.FileName);
return cont;
}
VOID PhpRtlModulesToGenericModules(
__in PRTL_PROCESS_MODULES Modules,
__in PPH_ENUM_GENERIC_MODULES_CALLBACK Callback,
__in PVOID Context,
__in PPH_LIST BaseAddressList
)
{
PRTL_PROCESS_MODULE_INFORMATION module;
ULONG i;
PH_MODULE_INFO moduleInfo;
BOOLEAN cont;
for (i = 0; i < Modules->NumberOfModules; i++)
{
PPH_STRING fileName;
module = &Modules->Modules[i];
// Check if we have a duplicate base address.
if (PhIndexOfListItem(BaseAddressList, module->ImageBase) != -1)
{
continue;
}
else
{
PhAddListItem(BaseAddressList, module->ImageBase);
}
fileName = PhCreateStringFromAnsi(module->FullPathName);
moduleInfo.BaseAddress = module->ImageBase;
moduleInfo.Size = module->ImageSize;
moduleInfo.EntryPoint = NULL;
moduleInfo.Flags = module->Flags;
moduleInfo.Name = PhCreateStringFromAnsi(&module->FullPathName[module->OffsetToFileName]);
moduleInfo.FileName = PhGetFileName(fileName); // convert to DOS file name
PhDereferenceObject(fileName);
cont = Callback(&moduleInfo, Context);
PhDereferenceObject(moduleInfo.Name);
PhDereferenceObject(moduleInfo.FileName);
if (!cont)
break;
}
}
VOID PhpEnumGenericMappedFiles(
__in HANDLE ProcessHandle,
__in PPH_ENUM_GENERIC_MODULES_CALLBACK Callback,
__in PVOID Context,
__in PPH_LIST BaseAddressList
)
{
PVOID baseAddress;
MEMORY_BASIC_INFORMATION basicInfo;
baseAddress = (PVOID)0;
while (VirtualQueryEx(
ProcessHandle,
baseAddress,
&basicInfo,
sizeof(MEMORY_BASIC_INFORMATION)
))
{
if (basicInfo.Type == MEM_MAPPED)
{
PPH_STRING fileName;
PPH_STRING newFileName;
PH_MODULE_INFO moduleInfo;
BOOLEAN cont;
// Check if we have a duplicate base address.
if (PhIndexOfListItem(BaseAddressList, baseAddress) != -1)
{
goto ContinueLoop;
}
else
{
PhAddListItem(BaseAddressList, baseAddress);
}
if (!NT_SUCCESS(PhGetProcessMappedFileName(
ProcessHandle,
baseAddress,
&fileName
)))
goto ContinueLoop;
// Get the DOS file name and then get the base name.
newFileName = PhGetFileName(fileName);
PhDereferenceObject(fileName);
moduleInfo.BaseAddress = baseAddress;
moduleInfo.Size = (ULONG)basicInfo.RegionSize;
moduleInfo.EntryPoint = NULL;
moduleInfo.Flags = 0;
moduleInfo.FileName = newFileName;
moduleInfo.Name = PhGetBaseName(newFileName);
cont = Callback(&moduleInfo, Context);
PhDereferenceObject(moduleInfo.FileName);
PhDereferenceObject(moduleInfo.Name);
if (!cont)
break;
}
ContinueLoop:
baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize);
}
}
/**
* Enumerates the modules loaded by a process.
*
* \param ProcessId The ID of a process. If
* \ref SYSTEM_PROCESS_ID is specified the function
* enumerates the kernel modules.
* \param ProcessHandle A handle to the process.
* \param Flags Flags controlling the information
* to retrieve.
* \li \c PH_ENUM_GENERIC_MAPPED_FILES Enumerate mapped
* files.
* \param Callback A callback function which is executed
* for each module.
* \param Context A user-defined value to pass
* to the callback function.
*/
NTSTATUS PhEnumGenericModules(
__in HANDLE ProcessId,
__in_opt HANDLE ProcessHandle,
__in ULONG Flags,
__in PPH_ENUM_GENERIC_MODULES_CALLBACK Callback,
__in PVOID Context
)
{
NTSTATUS status;
PPH_LIST baseAddressList;
baseAddressList = PhCreateList(20);
if (ProcessId == SYSTEM_PROCESS_ID)
{
// Kernel modules
PRTL_PROCESS_MODULES modules;
if (!NT_SUCCESS(status = PhEnumKernelModules(&modules)))
{
goto CleanupExit;
}
PhpRtlModulesToGenericModules(
modules,
Callback,
Context,
baseAddressList
);
PhFree(modules);
}
else
{
// 32-bit process modules
BOOLEAN opened = FALSE;
BOOLEAN isWow64 = FALSE;
ENUM_GENERIC_PROCESS_MODULES_CONTEXT context;
if (!ProcessHandle)
{
if (!NT_SUCCESS(status = PhOpenProcess(
&ProcessHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
ProcessId
)))
{
if (!NT_SUCCESS(status = PhOpenProcess(
&ProcessHandle,
ProcessQueryAccess | PROCESS_VM_READ,
ProcessId
)))
{
goto CleanupExit;
}
}
opened = TRUE;
}
context.Callback = Callback;
context.Context = Context;
context.BaseAddressList = baseAddressList;
status = PhEnumProcessModules(
ProcessHandle,
EnumGenericProcessModulesCallback,
&context
);
// Mapped files
if (Flags & PH_ENUM_GENERIC_MAPPED_FILES)
{
PhpEnumGenericMappedFiles(
ProcessHandle,
Callback,
Context,
baseAddressList
);
}
#ifdef _M_X64
// And just before we close the process handle, find out
// if the process is running under WOW64.
PhGetProcessIsWow64(ProcessHandle, &isWow64);
#endif
if (opened)
NtClose(ProcessHandle);
#ifdef _M_X64
// 64-bit process modules
if (isWow64)
{
PRTL_DEBUG_INFORMATION debugBuffer;
debugBuffer = RtlCreateQueryDebugBuffer(0, FALSE);
if (debugBuffer)
{
if (NT_SUCCESS(RtlQueryProcessDebugInformation(
ProcessId,
RTL_QUERY_PROCESS_MODULES32 | RTL_QUERY_PROCESS_NONINVASIVE,
debugBuffer
)))
{
PhpRtlModulesToGenericModules(
debugBuffer->Modules,
Callback,
Context,
baseAddressList
);
}
RtlDestroyQueryDebugBuffer(debugBuffer);
}
}
#endif
}
CleanupExit:
PhDereferenceObject(baseAddressList);
return status;
}