/*
* KProcessHacker
*
* Copyright (C) 2016 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 .
*/
#include
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphFreeCapturedUnicodeString)
#pragma alloc_text(PAGE, KphCaptureUnicodeString)
#pragma alloc_text(PAGE, KphEnumerateSystemModules)
#pragma alloc_text(PAGE, KphValidateAddressForSystemModules)
#pragma alloc_text(PAGE, KphGetProcessMappedFileName)
#endif
VOID KphFreeCapturedUnicodeString(
__in PUNICODE_STRING CapturedUnicodeString
)
{
PAGED_CODE();
if (CapturedUnicodeString->Buffer)
ExFreePoolWithTag(CapturedUnicodeString->Buffer, 'UhpK');
}
NTSTATUS KphCaptureUnicodeString(
__in PUNICODE_STRING UnicodeString,
__out PUNICODE_STRING CapturedUnicodeString
)
{
UNICODE_STRING unicodeString;
PWCHAR userBuffer;
PAGED_CODE();
__try
{
ProbeForRead(UnicodeString, sizeof(UNICODE_STRING), sizeof(ULONG));
unicodeString.Length = UnicodeString->Length;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = NULL;
userBuffer = UnicodeString->Buffer;
ProbeForRead(userBuffer, unicodeString.Length, sizeof(WCHAR));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
if (unicodeString.Length & 1)
{
return STATUS_INVALID_PARAMETER;
}
if (unicodeString.Length != 0)
{
unicodeString.Buffer = ExAllocatePoolWithTag(
PagedPool,
unicodeString.Length,
'UhpK'
);
if (!unicodeString.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
__try
{
memcpy(
unicodeString.Buffer,
userBuffer,
unicodeString.Length
);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphFreeCapturedUnicodeString(&unicodeString);
return GetExceptionCode();
}
}
*CapturedUnicodeString = unicodeString;
return STATUS_SUCCESS;
}
/**
* Enumerates the modules loaded by the kernel.
*
* \param Modules A variable which receives a pointer to a structure containing information about
* the kernel modules. The structure must be freed with the tag 'ThpK'.
*/
NTSTATUS KphEnumerateSystemModules(
__out PRTL_PROCESS_MODULES *Modules
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize;
ULONG attempts;
PAGED_CODE();
bufferSize = 2048;
attempts = 8;
do
{
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
status = ZwQuerySystemInformation(
SystemModuleInformation,
buffer,
bufferSize,
&bufferSize
);
if (NT_SUCCESS(status))
{
*Modules = buffer;
return status;
}
ExFreePoolWithTag(buffer, 'ThpK');
if (status != STATUS_INFO_LENGTH_MISMATCH)
{
break;
}
} while (--attempts);
return status;
}
/**
* Checks if an address range lies within a kernel module.
*
* \param Address The beginning of the address range.
* \param Length The number of bytes in the address range.
*/
NTSTATUS KphValidateAddressForSystemModules(
__in PVOID Address,
__in SIZE_T Length
)
{
NTSTATUS status;
PRTL_PROCESS_MODULES modules;
ULONG i;
BOOLEAN valid;
PAGED_CODE();
status = KphEnumerateSystemModules(&modules);
if (!NT_SUCCESS(status))
return status;
valid = FALSE;
for (i = 0; i < modules->NumberOfModules; i++)
{
if (
(ULONG_PTR)Address + Length >= (ULONG_PTR)Address &&
(ULONG_PTR)Address >= (ULONG_PTR)modules->Modules[i].ImageBase &&
(ULONG_PTR)Address + Length <= (ULONG_PTR)modules->Modules[i].ImageBase + modules->Modules[i].ImageSize
)
{
dprintf("Validated address 0x%Ix in %s\n", Address, modules->Modules[i].FullPathName);
valid = TRUE;
break;
}
}
ExFreePoolWithTag(modules, 'ThpK');
if (valid)
status = STATUS_SUCCESS;
else
status = STATUS_ACCESS_VIOLATION;
return status;
}
/**
* 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 Modules A variable which receives a pointer to a string containing the file name of the
* section. The structure must be freed with the tag 'ThpK'.
*/
NTSTATUS KphGetProcessMappedFileName(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out PUNICODE_STRING *FileName
)
{
NTSTATUS status;
PVOID buffer;
SIZE_T bufferSize;
SIZE_T returnLength;
PAGED_CODE();
bufferSize = 0x100;
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
return STATUS_INSUFFICIENT_RESOURCES;
status = ZwQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
if (status == STATUS_BUFFER_OVERFLOW)
{
ExFreePoolWithTag(buffer, 'ThpK');
bufferSize = returnLength;
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
return STATUS_INSUFFICIENT_RESOURCES;
status = ZwQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
}
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(buffer, 'ThpK');
return status;
}
*FileName = buffer;
return status;
}