mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
58a686c945
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2567 21ef857c-d57f-4fe0-8362-d861dc6d29cd
1675 lines
40 KiB
C
1675 lines
40 KiB
C
/*
|
|
* Process Hacker -
|
|
* process 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>
|
|
|
|
static PWSTR PhDosDeviceNames[26];
|
|
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
NTSTATUS PhTerminateProcess(
|
|
__in HANDLE ProcessHandle,
|
|
__in NTSTATUS ExitStatus
|
|
)
|
|
{
|
|
if (PhKphHandle)
|
|
{
|
|
return KphTerminateProcess(
|
|
PhKphHandle,
|
|
ProcessHandle,
|
|
ExitStatus
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return NtTerminateProcess(
|
|
ProcessHandle,
|
|
ExitStatus
|
|
);
|
|
}
|
|
}
|
|
|
|
NTSTATUS PhTerminateThread(
|
|
__in HANDLE ThreadHandle,
|
|
__in NTSTATUS ExitStatus
|
|
)
|
|
{
|
|
if (PhKphHandle)
|
|
{
|
|
return KphTerminateThread(
|
|
PhKphHandle,
|
|
ThreadHandle,
|
|
ExitStatus
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return NtTerminateThread(
|
|
ThreadHandle,
|
|
ExitStatus
|
|
);
|
|
}
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhpQueryProcessVariableSize(
|
|
__in HANDLE ProcessHandle,
|
|
__in PROCESS_INFORMATION_CLASS ProcessInformationClass,
|
|
__out PPVOID Buffer
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
ULONG returnLength;
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS PhGetProcessBasicInformation(
|
|
__in HANDLE ProcessHandle,
|
|
__out PPROCESS_BASIC_INFORMATION BasicInformation
|
|
)
|
|
{
|
|
return NtQueryInformationProcess(
|
|
ProcessHandle,
|
|
ProcessBasicInformation,
|
|
BasicInformation,
|
|
sizeof(PROCESS_BASIC_INFORMATION),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhGetProcessImageFileName(
|
|
__in HANDLE ProcessHandle,
|
|
__out PPH_STRING *FileName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
PUNICODE_STRING fileName;
|
|
|
|
status = PhpQueryProcessVariableSize(
|
|
ProcessHandle,
|
|
ProcessImageFileName,
|
|
&buffer
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
fileName = (PUNICODE_STRING)buffer;
|
|
*FileName = PhCreateStringEx(fileName->Buffer, fileName->Length);
|
|
PhFree(buffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS PhGetProcessExecuteFlags(
|
|
__in HANDLE ProcessHandle,
|
|
__out PULONG ExecuteFlags
|
|
)
|
|
{
|
|
return NtQueryInformationProcess(
|
|
ProcessHandle,
|
|
ProcessExecuteFlags,
|
|
ExecuteFlags,
|
|
sizeof(ULONG),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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_UNSUCCESSFUL;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS PhSetProcessExecuteFlags(
|
|
__in HANDLE ProcessHandle,
|
|
__in ULONG ExecuteFlags
|
|
)
|
|
{
|
|
return KphSetExecuteOptions(
|
|
PhKphHandle,
|
|
ProcessHandle,
|
|
ExecuteFlags
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhpQueryTokenVariableSize(
|
|
__in HANDLE TokenHandle,
|
|
__in TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
__out PPVOID Buffer
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
ULONG returnLength;
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS PhGetTokenUser(
|
|
__in HANDLE TokenHandle,
|
|
__out PTOKEN_USER *User
|
|
)
|
|
{
|
|
return PhpQueryTokenVariableSize(
|
|
TokenHandle,
|
|
TokenUser,
|
|
User
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhGetTokenElevationType(
|
|
__in HANDLE TokenHandle,
|
|
__out PTOKEN_ELEVATION_TYPE ElevationType
|
|
)
|
|
{
|
|
return NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenElevationType,
|
|
ElevationType,
|
|
sizeof(TOKEN_ELEVATION_TYPE),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhGetTokenIsElevated(
|
|
__in HANDLE TokenHandle,
|
|
__out PBOOLEAN Elevated
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
TOKEN_ELEVATION elevation;
|
|
|
|
status = NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenElevation,
|
|
&elevation,
|
|
sizeof(TOKEN_ELEVATION),
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*Elevated = !!elevation.TokenIsElevated;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS PhGetTokenGroups(
|
|
__in HANDLE TokenHandle,
|
|
__out PTOKEN_GROUPS *Groups
|
|
)
|
|
{
|
|
return PhpQueryTokenVariableSize(
|
|
TokenHandle,
|
|
TokenGroups,
|
|
Groups
|
|
);
|
|
}
|
|
|
|
NTSTATUS PhGetTokenPrivileges(
|
|
__in HANDLE TokenHandle,
|
|
__out PTOKEN_PRIVILEGES *Privileges
|
|
)
|
|
{
|
|
return PhpQueryTokenVariableSize(
|
|
TokenHandle,
|
|
TokenPrivileges,
|
|
Privileges
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
BOOLEAN PhLookupPrivilegeName(
|
|
__in PLUID PrivilegeValue,
|
|
__out PPH_STRING *PrivilegeName
|
|
)
|
|
{
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
|
|
bufferSize = 0x10;
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
if (!LookupPrivilegeName(NULL, PrivilegeValue, buffer, &bufferSize))
|
|
{
|
|
PhFree(buffer);
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
if (!LookupPrivilegeName(NULL, PrivilegeValue, buffer, &bufferSize))
|
|
{
|
|
PhFree(buffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*PrivilegeName = PhCreateString(buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN PhLookupPrivilegeDisplayName(
|
|
__in PWSTR PrivilegeName,
|
|
__out PPH_STRING *PrivilegeDisplayName
|
|
)
|
|
{
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
ULONG languageId;
|
|
|
|
bufferSize = 0x40;
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
if (!LookupPrivilegeDisplayName(NULL, PrivilegeName, buffer, &bufferSize, &languageId))
|
|
{
|
|
PhFree(buffer);
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
if (!LookupPrivilegeDisplayName(NULL, PrivilegeName, buffer, &bufferSize, &languageId))
|
|
{
|
|
PhFree(buffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*PrivilegeDisplayName = PhCreateString(buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN PhLookupPrivilegeValue(
|
|
__in PWSTR PrivilegeName,
|
|
__out PLUID PrivilegeValue
|
|
)
|
|
{
|
|
return !!LookupPrivilegeValue(
|
|
NULL,
|
|
PrivilegeName,
|
|
PrivilegeValue
|
|
);
|
|
}
|
|
|
|
BOOLEAN PhLookupSid(
|
|
__in PSID Sid,
|
|
__out_opt PPH_STRING *Name,
|
|
__out_opt PPH_STRING *DomainName,
|
|
__out_opt PSID_NAME_USE NameUse
|
|
)
|
|
{
|
|
PVOID nameBuffer;
|
|
ULONG nameBufferSize;
|
|
PVOID domainNameBuffer;
|
|
ULONG domainNameBufferSize;
|
|
SID_NAME_USE nameUse;
|
|
|
|
nameBufferSize = 0x40;
|
|
nameBuffer = PhAllocate(nameBufferSize * 2);
|
|
domainNameBufferSize = 0x40;
|
|
domainNameBuffer = PhAllocate(domainNameBufferSize * 2);
|
|
|
|
if (!LookupAccountSid(
|
|
NULL,
|
|
Sid,
|
|
nameBuffer,
|
|
&nameBufferSize,
|
|
domainNameBuffer,
|
|
&domainNameBufferSize,
|
|
&nameUse
|
|
))
|
|
{
|
|
PhFree(nameBuffer);
|
|
nameBuffer = PhAllocate(nameBufferSize * 2);
|
|
PhFree(domainNameBuffer);
|
|
domainNameBuffer = PhAllocate(domainNameBufferSize * 2);
|
|
|
|
if (!LookupAccountSid(
|
|
NULL,
|
|
Sid,
|
|
nameBuffer,
|
|
&nameBufferSize,
|
|
domainNameBuffer,
|
|
&domainNameBufferSize,
|
|
&nameUse
|
|
))
|
|
{
|
|
PhFree(nameBuffer);
|
|
PhFree(domainNameBuffer);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Name)
|
|
*Name = PhCreateString(nameBuffer);
|
|
if (DomainName)
|
|
*DomainName = PhCreateString(domainNameBuffer);
|
|
if (NameUse)
|
|
*NameUse = nameUse;
|
|
|
|
PhFree(nameBuffer);
|
|
PhFree(domainNameBuffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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 PhEnumProcessModules(
|
|
__in HANDLE ProcessHandle,
|
|
__in PPH_ENUM_PROCESS_MODULES_CALLBACK Callback,
|
|
__in PVOID Context
|
|
)
|
|
{
|
|
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
|
|
)
|
|
{
|
|
PWSTR baseDllNameBuffer;
|
|
PWSTR fullDllNameBuffer;
|
|
BOOLEAN cont;
|
|
|
|
status = PhReadVirtualMemory(
|
|
ProcessHandle,
|
|
CONTAINING_RECORD(currentLink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks),
|
|
¤tEntry,
|
|
sizeof(LDR_DATA_TABLE_ENTRY),
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
// Make sure the entry is valid.
|
|
if (currentEntry.DllBase)
|
|
{
|
|
// Read the base DLL name string and add a null terminator.
|
|
|
|
baseDllNameBuffer = PhAllocate(currentEntry.BaseDllName.Length + 2);
|
|
|
|
if (NT_SUCCESS(PhReadVirtualMemory(
|
|
ProcessHandle,
|
|
currentEntry.BaseDllName.Buffer,
|
|
baseDllNameBuffer,
|
|
currentEntry.BaseDllName.Length,
|
|
NULL
|
|
)))
|
|
{
|
|
baseDllNameBuffer[currentEntry.BaseDllName.Length / 2] = 0;
|
|
currentEntry.BaseDllName.Buffer = baseDllNameBuffer;
|
|
}
|
|
|
|
// Read the full DLL name string and add a null terminator.
|
|
|
|
fullDllNameBuffer = PhAllocate(currentEntry.FullDllName.Length + 2);
|
|
|
|
if (NT_SUCCESS(PhReadVirtualMemory(
|
|
ProcessHandle,
|
|
currentEntry.FullDllName.Buffer,
|
|
fullDllNameBuffer,
|
|
currentEntry.FullDllName.Length,
|
|
NULL
|
|
)))
|
|
{
|
|
fullDllNameBuffer[currentEntry.FullDllName.Length / 2] = 0;
|
|
currentEntry.FullDllName.Buffer = fullDllNameBuffer;
|
|
}
|
|
|
|
// Execute the callback.
|
|
cont = Callback(¤tEntry, Context);
|
|
|
|
PhFree(baseDllNameBuffer);
|
|
PhFree(fullDllNameBuffer);
|
|
|
|
if (!cont)
|
|
break;
|
|
}
|
|
|
|
currentLink = currentEntry.InLoadOrderLinks.Flink;
|
|
i++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS PhEnumKernelModules(
|
|
__out PRTL_PROCESS_MODULES *Modules
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
static 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS PhEnumProcesses(
|
|
__out PPVOID Processes
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
static ULONG bufferSize = 2048; // keep the last successful buffer size between calls
|
|
|
|
buffer = PhAllocate(bufferSize);
|
|
|
|
while (TRUE)
|
|
{
|
|
if (!buffer)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
status = NtQuerySystemInformation(
|
|
SystemProcessInformation,
|
|
buffer,
|
|
bufferSize,
|
|
&bufferSize
|
|
);
|
|
|
|
if (NT_SUCCESS(status))
|
|
break;
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
PhFree(buffer);
|
|
buffer = PhAllocate(bufferSize);
|
|
}
|
|
else
|
|
{
|
|
PhFree(buffer);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
*Processes = buffer;
|
|
|
|
return status;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
VOID PhInitializeDosDeviceNames()
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < 26; i++)
|
|
PhDosDeviceNames[i] = PhAllocate(64 * sizeof(WCHAR));
|
|
}
|
|
|
|
VOID PhRefreshDosDeviceNames()
|
|
{
|
|
WCHAR deviceName[3];
|
|
ULONG i;
|
|
|
|
deviceName[1] = ':';
|
|
deviceName[2] = 0;
|
|
|
|
for (i = 0; i < 26; i++)
|
|
{
|
|
deviceName[0] = (WCHAR)('A' + i);
|
|
|
|
if (!QueryDosDevice(deviceName, PhDosDeviceNames[i], 64))
|
|
PhDosDeviceNames[i][0] = 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
// Go through the DOS devices and try to find a matching prefix.
|
|
else
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < 26; i++)
|
|
{
|
|
PWSTR prefix = PhDosDeviceNames[i];
|
|
ULONG prefixLength = (ULONG)wcslen(prefix);
|
|
|
|
if (prefixLength > 0)
|
|
{
|
|
if (PhStringStartsWith2(FileName, prefix, TRUE))
|
|
{
|
|
newFileName = PhCreateStringEx(NULL, 4 + FileName->Length - prefixLength * 2);
|
|
newFileName->Buffer[0] = (WCHAR)('A' + i);
|
|
newFileName->Buffer[1] = ':';
|
|
memcpy(&newFileName->Buffer[2], &FileName->Buffer[prefixLength], FileName->Length - prefixLength * 2);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newFileName == FileName)
|
|
{
|
|
// We didn't find a match. If the file name starts with
|
|
// a backslash, prepend the system drive.
|
|
if (newFileName->Buffer[0] == '\\')
|
|
{
|
|
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 = 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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
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
|
|
);
|
|
}
|
|
|
|
if (opened)
|
|
CloseHandle(ProcessHandle);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto CleanupExit;
|
|
}
|
|
|
|
#ifndef _M_X64
|
|
// 64-bit process modules
|
|
{
|
|
PRTL_DEBUG_INFORMATION debugBuffer;
|
|
|
|
debugBuffer = RtlCreateQueryDebugBuffer(0, TRUE);
|
|
|
|
if (debugBuffer)
|
|
{
|
|
if (NT_SUCCESS(RtlQueryProcessDebugInformation(
|
|
ProcessId,
|
|
RTL_QUERY_PROCESS_MODULES32,
|
|
debugBuffer
|
|
)))
|
|
{
|
|
PhpRtlModulesToGenericModules(
|
|
debugBuffer->Modules,
|
|
Callback,
|
|
Context,
|
|
baseAddressList
|
|
);
|
|
}
|
|
|
|
RtlDestroyQueryDebugBuffer(debugBuffer);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CleanupExit:
|
|
PhDereferenceObject(baseAddressList);
|
|
|
|
return status;
|
|
}
|