mirror-ac/driver/pool.c

666 lines
18 KiB
C
Raw Normal View History

2023-08-26 14:07:06 +02:00
#include "pool.h"
2023-09-27 15:10:12 +02:00
#include <intrin.h>
2023-08-26 14:07:06 +02:00
2023-08-28 11:17:38 +02:00
#include "callbacks.h"
2023-09-18 05:15:26 +02:00
#include "queue.h"
2023-08-28 11:17:38 +02:00
2023-09-27 15:10:12 +02:00
#define PAGE_BASE_SIZE 0x1000
#define POOL_TAG_SIZE 0x004
#define PML4_ENTRY_COUNT 512
#define PDPT_ENTRY_COUNT 512
#define PD_ENTRY_COUNT 512
#define PT_ENTRY_COUNT 512
#define LARGE_PAGE_2MB_ENTRIES 512
#define LARGE_PAGE_1GB_ENTRIES 0x40000
#define CHUNK_SIZE 16
#define PROCESS_OBJECT_ALLOCATION_MARGIN 0x90
2023-08-26 14:07:06 +02:00
2023-08-27 16:34:21 +02:00
#define POOL_TAG_LENGTH 4
2023-08-28 13:10:07 +02:00
#define EXECUTIVE_OBJECT_COUNT 8
#define INDEX_PROCESS_POOL_TAG 0
#define INDEX_THREAD_POOL_TAG 1
#define INDEX_DESKTOP_POOL_TAG 2
#define INDEX_WINDOW_STATIONS_POOL_TAG 3
#define INDEX_MUTANTS_POOL_TAG 4
#define INDEX_FILE_OBJECTS_POOL_TAG 5
#define INDEX_DRIVERS_POOL_TAG 6
2023-08-28 17:00:52 +02:00
#define INDEX_SYMBOLIC_LINKS_POOL_TAG 7
2023-08-28 13:10:07 +02:00
2023-10-05 08:27:17 +02:00
CHAR EXECUTIVE_OBJECT_POOL_TAGS[EXECUTIVE_OBJECT_COUNT][POOL_TAG_LENGTH] =
2023-08-28 13:10:07 +02:00
{
2023-08-28 17:00:52 +02:00
"\x50\x72\x6f\x63", /* Process */
"\x54\x68\x72\x64", /* Thread */
"\x44\x65\x73\x6B", /* Desktop */
"\x57\x69\x6E\x64", /* Windows Station */
"\x4D\x75\x74\x65", /* Mutants i.e mutex etc. */
"\x46\x69\x6C\x65", /* File objects */
"\x44\x72\x69\x76", /* Drivers */
"\x4C\x69\x6E\x6B" /* Symbolic links */
2023-08-28 13:10:07 +02:00
};
2023-08-27 16:34:21 +02:00
2023-10-07 07:27:22 +02:00
typedef struct _PROCESS_SCAN_CONTEXT
{
ULONG process_count;
PVOID process_buffer;
}PROCESS_SCAN_CONTEXT, * PPROCESS_SCAN_CONTEXT;
2023-08-28 11:17:38 +02:00
2023-10-05 08:27:17 +02:00
PKDDEBUGGER_DATA64
2023-09-27 06:22:14 +02:00
GetGlobalDebuggerData()
2023-08-26 14:07:06 +02:00
{
CONTEXT context = { 0 };
PDUMP_HEADER dump_header = { 0 };
UINT64 thread_state;
PKDDEBUGGER_DATA64 debugger_data = NULL;
context.ContextFlags = CONTEXT_FULL;
2023-10-05 08:27:17 +02:00
RtlCaptureContext(&context);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
dump_header = ExAllocatePool2(POOL_FLAG_NON_PAGED, DUMP_BLOCK_SIZE, POOL_DUMP_BLOCK_TAG);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!dump_header)
2023-08-26 14:07:06 +02:00
goto end;
KeCapturePersistentThreadState(
&context,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
dump_header
);
2023-10-05 08:27:17 +02:00
debugger_data = (PKDDEBUGGER_DATA64)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDDEBUGGER_DATA64), POOL_DEBUGGER_DATA_TAG);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!debugger_data)
2023-08-26 14:07:06 +02:00
goto end;
2023-10-05 08:27:17 +02:00
RtlCopyMemory(debugger_data, dump_header->KdDebuggerDataBlock, sizeof(KDDEBUGGER_DATA64));
2023-08-26 14:07:06 +02:00
end:
2023-10-05 08:27:17 +02:00
if (dump_header)
ExFreePoolWithTag(dump_header, POOL_DUMP_BLOCK_TAG);
2023-08-26 14:07:06 +02:00
return debugger_data;
}
2023-10-05 08:27:17 +02:00
VOID
2023-09-27 06:22:14 +02:00
GetPsActiveProcessHead(
2023-08-30 11:19:41 +02:00
_In_ PUINT64 Address
)
{
PKDDEBUGGER_DATA64 debugger_data = GetGlobalDebuggerData();
2023-10-05 08:27:17 +02:00
*Address = *(UINT64*)(debugger_data->PsActiveProcessHead);
2023-08-30 11:19:41 +02:00
2023-10-05 08:27:17 +02:00
ExFreePoolWithTag(debugger_data, POOL_DEBUGGER_DATA_TAG);
2023-08-30 11:19:41 +02:00
}
2023-09-15 22:25:02 +02:00
/*
* Here we define a signature that can be used to find EPROCESS structures consistently across
* major windows versions. The fields we test have proven to be consistent in the following study:
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* https://www.cise.ufl.edu/~traynor/papers/ccs09b.pdf
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* Aswell as some of my own additional research and testing. The following signature is used:
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* PeakVirtualSize must be greater then 0 for any valid process:
* -> EPROCESS->PeakVirtualSize > 0
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* The DirectoryTableBase must be 0x20 aligned:
* -> EPROCESS->DirectoryTableBase % 20 == 0
2023-10-05 08:27:17 +02:00
*
* The pool allocation size must be greater then the size of an EPROCESS allocation and
2023-09-15 22:25:02 +02:00
* less then the size of a page. Allocation size can be found with the following formula:
* -> AllocationSize = POOL_HEADER->BlockSize * CHUNK_SIZE - sizeof(POOL_HEADER)
2023-10-05 08:27:17 +02:00
* -> AllocationSize > sizeof(EPROCESS)
2023-09-15 22:25:02 +02:00
* -> AllocationSize < PAGE_SIZE (4096)
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* Pool type must be non-null:
* -> POOL_HEADER->PoolType != NULL
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* The process PEB must be a usermode address and 0x1000 aligned:
* -> EPROCESS->Peb & 0x7ffd0000 == 0x7ffd0000 && EPROCESS->Peb % 0x1000 == 0
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* The object table must have the following properties and be 0x8 aligned:
* -> EPROCESS->ObjectTable & 0xe0000000 == 0xe0000000 && EPROCESS->ObjectTable % 0x8 == 0
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* The allocation size, when AND'd with 0xfff0 must not equal 0xfff0:
* -> AllocationSize & 0xfff0 != 0xfff0
2023-10-05 08:27:17 +02:00
*
2023-09-15 22:25:02 +02:00
* This signature will allow us to consistently and accurately determine if a given pool allocation is
* indeed an executive process allocation across major versions of Windows.
*/
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
BOOLEAN
2023-09-27 06:22:14 +02:00
ValidateIfAddressIsProcessStructure(
2023-09-15 22:25:02 +02:00
_In_ PVOID Address,
_In_ PPOOL_HEADER PoolHeader
)
{
UINT64 peak_virtual_size = NULL;
UINT64 dir_table_base = NULL;
UINT64 allocation_size = NULL;
UINT64 peb = NULL;
UINT64 object_table = NULL;
BOOLEAN peb_test = FALSE;
BOOLEAN object_table_test = FALSE;
UINT64 allocation_size_test = NULL;
2023-10-05 08:27:17 +02:00
if (MmIsAddressValid((UINT64)Address + KPROCESS_DIRECTORY_TABLE_BASE_OFFSET))
dir_table_base = *(UINT64*)((UINT64)Address + KPROCESS_DIRECTORY_TABLE_BASE_OFFSET);
2023-09-15 22:25:02 +02:00
2023-10-05 08:27:17 +02:00
if (MmIsAddressValid((UINT64)Address + EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET))
peak_virtual_size = *(UINT64*)((UINT64)Address + EPROCESS_PEAK_VIRTUAL_SIZE_OFFSET);
2023-09-17 05:14:02 +02:00
2023-10-05 08:27:17 +02:00
if (MmIsAddressValid((UINT64)PoolHeader + POOL_HEADER_BLOCK_SIZE_OFFSET))
allocation_size = PoolHeader->BlockSize * CHUNK_SIZE - sizeof(POOL_HEADER);
2023-09-15 22:25:02 +02:00
2023-10-05 08:27:17 +02:00
if (MmIsAddressValid((UINT64)Address + EPROCESS_PEB_OFFSET))
peb = *(UINT64*)((UINT64)Address + EPROCESS_PEB_OFFSET);
2023-09-15 22:25:02 +02:00
2023-10-05 08:27:17 +02:00
if (MmIsAddressValid((UINT64)Address + EPROCESS_OBJECT_TABLE_OFFSET))
object_table = *(UINT64*)((UINT64)Address + EPROCESS_OBJECT_TABLE_OFFSET);
2023-09-15 22:25:02 +02:00
2023-10-05 08:27:17 +02:00
peb_test = peb == NULL || (peb & 0x7ffd0000 == 0x7ffd0000 && peb % 0x1000 == NULL);
object_table_test = object_table == NULL || (object_table & 0xe0000000 == 0xe0000000 && object_table % 0x8 == 0);
2023-09-15 22:25:02 +02:00
allocation_size_test = allocation_size & 0xfff0;
2023-10-05 08:27:17 +02:00
if (peak_virtual_size > 0 && (dir_table_base & 0x20) == 0 &&
allocation_size > (EPROCESS_SIZE + OBJECT_HEADER_SIZE + sizeof(POOL_HEADER)) &&
PoolHeader->PoolType != NULL && !(allocation_size_test == 0xfff0) && !peb_test && !object_table_test)
2023-09-15 22:25:02 +02:00
{
return TRUE;
}
return FALSE;
}
2023-08-28 11:17:38 +02:00
/*
* OBJECT_HEADER->InfoMask is a bit mask that tells us which optional
* headers the object has. The bits are as follows:
*
* 0x1 = OBJECT_HEADER_CREATOR_INFO
* 0x2 = OBJECT_HEADER_NAME_INFO
* 0x4 = OBJECT_HEADER_HANDLE_INFO
* 0x8 = OBJECT_HEADER_QUOTA_INFO
* 0x10 = OBJECT_HEADER_PROCESS_INFO
* 0x20 = OBJECT_HEADER_AUDIT_INFO
* 0x40 = OBJECT_HEADER_HANDLE_REVOCATION_INFO
*/
/*
* Idea: since we don't know the number of headers or the exact memory layout of the object
* header section for these proc allocations, we can form an estimate address of base + 0x70
* and then iterate the loaded process list and if theres an address within say 0x50 of it we
* can assume that the process is legitmate. Then to find an unlinked process, it wouldn't
* exist in the loaded module list, check that it hasnt been deallocated and then focus on
* scanning it for name etc. Maybe scan for .exe extension?
*
* Also use the full name so we get the file extension and path not the 15 char long one
*/
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
VOID
2023-09-27 06:22:14 +02:00
ScanPageForKernelObjectAllocation(
2023-08-26 15:29:12 +02:00
_In_ UINT64 PageBase,
2023-08-27 16:34:21 +02:00
_In_ ULONG PageSize,
2023-08-28 13:10:07 +02:00
_In_ ULONG ObjectIndex,
2023-10-07 07:27:22 +02:00
_In_ PPROCESS_SCAN_CONTEXT Context
2023-08-26 15:29:12 +02:00
)
{
2023-08-27 16:34:21 +02:00
INT length = 0;
CHAR current_char;
CHAR current_sig_byte;
PPOOL_HEADER pool_header;
2023-08-28 11:17:38 +02:00
PEPROCESS process = NULL;
2023-09-15 22:25:02 +02:00
PEPROCESS process_size_one = NULL;
PEPROCESS process_size_two = NULL;
2023-09-17 05:14:02 +02:00
PEPROCESS test_process = NULL;
2023-08-27 16:34:21 +02:00
LPCSTR process_name;
2023-08-28 11:17:38 +02:00
PUINT64 address_list;
ULONG allocation_size;
2023-10-05 08:27:17 +02:00
ULONG minimum_process_allocation_size = EPROCESS_SIZE - sizeof(POOL_HEADER) - OBJECT_HEADER_SIZE;
2023-08-27 16:34:21 +02:00
2023-10-05 08:27:17 +02:00
if (!PageBase || !PageSize)
2023-08-26 19:19:26 +02:00
return;
2023-10-05 08:27:17 +02:00
for (INT offset = 0; offset <= PageSize - POOL_TAG_LENGTH - minimum_process_allocation_size; offset++)
2023-08-26 17:48:50 +02:00
{
2023-10-05 08:27:17 +02:00
for (INT sig_index = 0; sig_index < POOL_TAG_LENGTH + 1; sig_index++)
2023-08-26 17:48:50 +02:00
{
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(PageBase + offset + sig_index))
2023-08-26 17:48:50 +02:00
break;
2023-08-26 15:29:12 +02:00
2023-10-05 08:27:17 +02:00
current_char = *(PCHAR)(PageBase + offset + sig_index);
current_sig_byte = EXECUTIVE_OBJECT_POOL_TAGS[ObjectIndex][sig_index];
2023-08-26 15:29:12 +02:00
2023-10-05 08:27:17 +02:00
if (sig_index == POOL_TAG_LENGTH)
2023-08-26 21:25:57 +02:00
{
2023-10-05 08:27:17 +02:00
pool_header = (UINT64)PageBase + offset - POOL_HEADER_TAG_OFFSET;
2023-08-26 21:25:57 +02:00
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid((PVOID)pool_header))
2023-08-27 07:49:59 +02:00
break;
2023-08-26 21:25:57 +02:00
2023-10-05 08:27:17 +02:00
/*
2023-09-18 05:15:26 +02:00
* Since every executive allocation is required to have an _OBJECT_HEADER, we start
* iterating from the size of this object header, then jump up in blocks of 0x10 since
* every object header is divisible by 0x10. We iterate up to 0xb0 which is equal to the following:
2023-10-05 08:27:17 +02:00
*
2023-09-18 05:15:26 +02:00
* 0xb0 = sizeof(ALL_HEADER_OBJECTS) + 0x10 where the 0x10 is 16 bytes of padding.
*/
2023-10-05 08:27:17 +02:00
for (ULONG header_size = OBJECT_HEADER_SIZE; header_size < 0xb0; header_size += 0x10)
2023-09-15 22:25:02 +02:00
{
2023-10-05 08:27:17 +02:00
test_process = (PEPROCESS)((UINT64)pool_header + sizeof(POOL_HEADER) + header_size);
2023-09-17 05:14:02 +02:00
2023-10-05 08:27:17 +02:00
if (ValidateIfAddressIsProcessStructure(test_process, pool_header))
2023-09-17 05:14:02 +02:00
{
process = test_process;
2023-08-28 11:17:38 +02:00
break;
2023-09-17 05:14:02 +02:00
}
2023-09-15 22:25:02 +02:00
}
2023-08-28 11:17:38 +02:00
2023-10-05 08:27:17 +02:00
if (process == NULL)
2023-09-17 05:14:02 +02:00
break;
2023-10-05 08:27:17 +02:00
DEBUG_LOG("Process: %llx", (UINT64)process);
2023-09-17 05:14:02 +02:00
2023-10-07 07:27:22 +02:00
address_list = (PUINT64)Context->process_buffer;
2023-08-28 11:17:38 +02:00
2023-10-07 07:27:22 +02:00
for (INT i = 0; i < Context->process_count; i++)
2023-09-15 22:25:02 +02:00
{
2023-10-05 08:27:17 +02:00
if (address_list[i] == NULL)
2023-08-28 11:17:38 +02:00
{
2023-10-05 08:27:17 +02:00
address_list[i] = (UINT64)process;
2023-09-15 22:25:02 +02:00
break;
2023-08-28 11:17:38 +02:00
}
2023-08-27 07:49:59 +02:00
}
2023-08-26 17:48:50 +02:00
2023-08-27 07:49:59 +02:00
break;
2023-08-26 21:25:57 +02:00
}
2023-08-26 17:48:50 +02:00
2023-10-05 08:27:17 +02:00
if (current_char != current_sig_byte)
2023-08-27 07:49:59 +02:00
break;
2023-08-26 21:25:57 +02:00
}
2023-08-26 17:48:50 +02:00
}
}
2023-08-26 19:19:26 +02:00
/*
* Using MmGetPhysicalMemoryRangesEx2(), we can get a block of structures that
* describe the physical memory layout. With each physical page base we are going
2023-09-15 22:25:02 +02:00
* to enumerate, we want to make sure it lies within an appropriate region of
2023-08-26 19:19:26 +02:00
* physical memory, so this function is to check for exactly that.
*/
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
BOOLEAN
2023-09-27 06:22:14 +02:00
IsPhysicalAddressInPhysicalMemoryRange(
2023-08-26 17:48:50 +02:00
_In_ UINT64 PhysicalAddress,
_In_ PPHYSICAL_MEMORY_RANGE PhysicalMemoryRanges
)
{
ULONG page_index = 0;
2023-08-27 16:34:21 +02:00
UINT64 start_address = 0;
UINT64 end_address = 0;
2023-10-05 08:27:17 +02:00
while (PhysicalMemoryRanges[page_index].NumberOfBytes.QuadPart != NULL)
2023-08-26 15:29:12 +02:00
{
2023-10-05 08:27:17 +02:00
start_address = PhysicalMemoryRanges[page_index].BaseAddress.QuadPart;
end_address = start_address + PhysicalMemoryRanges[page_index].NumberOfBytes.QuadPart;
2023-08-26 15:29:12 +02:00
2023-10-05 08:27:17 +02:00
if (PhysicalAddress >= start_address && PhysicalAddress <= end_address)
2023-08-26 17:48:50 +02:00
return TRUE;
2023-08-26 19:19:26 +02:00
page_index++;
2023-08-26 15:29:12 +02:00
}
2023-08-26 17:48:50 +02:00
return FALSE;
2023-08-26 15:29:12 +02:00
}
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
VOID
2023-09-27 06:22:14 +02:00
EnumerateKernelLargePages(
2023-09-15 22:25:02 +02:00
_In_ UINT64 PageBase,
_In_ ULONG PageSize,
2023-10-07 07:27:22 +02:00
_In_ PPROCESS_SCAN_CONTEXT Context,
2023-09-15 22:25:02 +02:00
_In_ ULONG ObjectIndex
)
{
2023-09-18 05:15:26 +02:00
/*
* Split the large pages up into blocks of 0x1000 and scan each block
*/
2023-10-05 08:27:17 +02:00
for (UINT64 page_index = 0; page_index < PageSize; page_index++)
2023-09-15 22:25:02 +02:00
{
ScanPageForKernelObjectAllocation(
2023-10-05 08:27:17 +02:00
PageBase + (page_index * PAGE_SIZE),
2023-09-15 22:25:02 +02:00
PAGE_SIZE,
ObjectIndex,
2023-10-07 07:27:22 +02:00
Context
2023-09-15 22:25:02 +02:00
);
}
}
2023-08-26 15:29:12 +02:00
/*
* This is your basic page table walk function. On intel systems, paging has 4 levels,
* each table holds 512 entries with a total size of 0x1000 (512 * sizeof(QWORD)). Each entry
* in each table contains a value with a subset bitfield containing the physical address
* of the base of the next table in the structure. So for example, a PML4 entry contains
* a physical address that points to the base of the PDPT table, it is the same for a PDPT
* entry -> PD base and so on.
2023-09-15 22:25:02 +02:00
*
* However, as with all good things Windows has implemented security features meaning
* we cannot use functions such as MmCopyMemory or MmMapIoSpace on paging structures,
* so we must find another way to walk the pages. Luckily for us, there exists
2023-08-26 15:29:12 +02:00
* MmGetVirtualForPhysical. This function is self explanatory and returns the corresponding
* virtual address given a physical address. What this means is that we can extract a page
* entry physical address, pass it to MmGetVirtualForPhysical which returns us the virtual
2023-09-15 22:25:02 +02:00
* address of the base of the next page structure. This is because page tables are still
2023-08-26 15:29:12 +02:00
* mapped by the kernel and exist in virtual memory just like everything else and hence
2023-09-15 22:25:02 +02:00
* reading the value at all 512 entries from the virtual base will give us the equivalent
2023-08-26 15:29:12 +02:00
* value as directly reading the physical address.
2023-09-15 22:25:02 +02:00
*
2023-08-26 15:29:12 +02:00
* Using this, we essentially walk the page tables as any regular translation would
* except instead of simply reading the physical we translate it to a virtual address
* and extract the physical address from the value at each virtual address page entry.
*/
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
VOID
2023-10-07 07:27:22 +02:00
WalkKernelPageTables(
_In_ PPROCESS_SCAN_CONTEXT Context
)
2023-08-26 14:07:06 +02:00
{
CR3 cr3;
PML4E pml4_base;
PML4E pml4_entry;
2023-08-27 16:34:21 +02:00
UINT64 pdpt_base;
UINT64 pd_base;
UINT64 pt_base;
2023-08-26 14:07:06 +02:00
PDPTE pdpt_entry;
PDPTE_LARGE pdpt_large_entry;
PDE pd_entry;
PDE_LARGE pd_large_entry;
PTE pt_entry;
UINT64 base_physical_page;
2023-08-26 15:29:12 +02:00
UINT64 base_virtual_page;
2023-09-03 19:33:27 +02:00
UINT64 base_2mb_virtual_page;
UINT64 base_1gb_virtual_page;
2023-08-26 14:07:06 +02:00
PHYSICAL_ADDRESS physical;
2023-08-26 19:19:26 +02:00
PPHYSICAL_MEMORY_RANGE physical_memory_ranges;
2023-09-15 22:25:02 +02:00
KIRQL irql;
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
physical_memory_ranges = MmGetPhysicalMemoryRangesEx2(NULL, NULL);
2023-08-26 17:48:50 +02:00
2023-10-05 08:27:17 +02:00
if (physical_memory_ranges == NULL)
2023-08-26 17:48:50 +02:00
{
2023-10-05 08:27:17 +02:00
DEBUG_ERROR("LOL stupid cunt not working");
2023-08-26 17:48:50 +02:00
return;
}
2023-08-27 08:01:36 +02:00
cr3.BitAddress = __readcr3();
2023-08-26 15:29:12 +02:00
physical.QuadPart = cr3.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
pml4_base.BitAddress = MmGetVirtualForPhysical(physical);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pml4_base.BitAddress) || !pml4_base.BitAddress)
2023-08-26 14:07:06 +02:00
return;
2023-10-05 08:27:17 +02:00
for (INT pml4_index = 0; pml4_index < PML4_ENTRY_COUNT; pml4_index++)
2023-08-26 14:07:06 +02:00
{
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pml4_base.BitAddress + pml4_index * sizeof(UINT64)))
2023-08-30 15:38:12 +02:00
continue;
2023-10-05 08:27:17 +02:00
pml4_entry.BitAddress = *(UINT64*)(pml4_base.BitAddress + pml4_index * sizeof(UINT64));
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (pml4_entry.Bits.Present == NULL)
2023-08-26 14:07:06 +02:00
continue;
2023-08-26 15:29:12 +02:00
physical.QuadPart = pml4_entry.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
pdpt_base = MmGetVirtualForPhysical(physical);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!pdpt_base || !MmIsAddressValid(pdpt_base))
2023-08-26 14:07:06 +02:00
continue;
2023-10-05 08:27:17 +02:00
for (INT pdpt_index = 0; pdpt_index < PDPT_ENTRY_COUNT; pdpt_index++)
2023-08-26 14:07:06 +02:00
{
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pdpt_base + pdpt_index * sizeof(UINT64)))
2023-08-30 15:38:12 +02:00
continue;
2023-10-05 08:27:17 +02:00
pdpt_entry.BitAddress = *(UINT64*)(pdpt_base + pdpt_index * sizeof(UINT64));
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (pdpt_entry.Bits.Present == NULL)
2023-08-26 14:07:06 +02:00
continue;
2023-10-05 08:27:17 +02:00
if (IS_LARGE_PAGE(pdpt_entry.BitAddress))
2023-08-26 14:07:06 +02:00
{
2023-09-03 19:33:27 +02:00
/* 1gb size page */
2023-08-26 14:07:06 +02:00
pdpt_large_entry.BitAddress = pdpt_entry.BitAddress;
2023-09-03 19:33:27 +02:00
physical.QuadPart = pdpt_large_entry.Bits.PhysicalAddress << PAGE_1GB_SHIFT;
2023-10-05 08:27:17 +02:00
if (IsPhysicalAddressInPhysicalMemoryRange(physical.QuadPart, physical_memory_ranges) == FALSE)
2023-09-03 19:33:27 +02:00
continue;
2023-10-05 08:27:17 +02:00
base_1gb_virtual_page = MmGetVirtualForPhysical(physical);
2023-09-03 19:33:27 +02:00
2023-10-05 08:27:17 +02:00
if (!base_1gb_virtual_page || !MmIsAddressValid(base_1gb_virtual_page))
2023-09-03 19:33:27 +02:00
continue;
EnumerateKernelLargePages(
base_1gb_virtual_page,
LARGE_PAGE_1GB_ENTRIES,
2023-10-07 07:27:22 +02:00
Context,
2023-09-15 22:25:02 +02:00
INDEX_PROCESS_POOL_TAG
2023-09-03 19:33:27 +02:00
);
2023-08-26 14:07:06 +02:00
continue;
}
2023-08-26 15:29:12 +02:00
physical.QuadPart = pdpt_entry.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
pd_base = MmGetVirtualForPhysical(physical);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!pd_base || !MmIsAddressValid(pd_base))
2023-08-26 14:07:06 +02:00
continue;
2023-10-05 08:27:17 +02:00
for (INT pd_index = 0; pd_index < PD_ENTRY_COUNT; pd_index++)
2023-08-26 14:07:06 +02:00
{
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pd_base + pd_index * sizeof(UINT64)))
2023-08-30 15:38:12 +02:00
continue;
2023-10-05 08:27:17 +02:00
pd_entry.BitAddress = *(UINT64*)(pd_base + pd_index * sizeof(UINT64));
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (pd_entry.Bits.Present == NULL)
2023-08-26 14:07:06 +02:00
continue;
2023-10-05 08:27:17 +02:00
if (IS_LARGE_PAGE(pd_entry.BitAddress))
2023-08-26 14:07:06 +02:00
{
/* 2MB size page */
pd_large_entry.BitAddress = pd_entry.BitAddress;
2023-09-03 19:33:27 +02:00
physical.QuadPart = pd_large_entry.Bits.PhysicalAddress << PAGE_2MB_SHIFT;
2023-10-05 08:27:17 +02:00
if (IsPhysicalAddressInPhysicalMemoryRange(physical.QuadPart, physical_memory_ranges) == FALSE)
2023-09-03 19:33:27 +02:00
continue;
2023-10-05 08:27:17 +02:00
base_2mb_virtual_page = MmGetVirtualForPhysical(physical);
2023-09-03 19:33:27 +02:00
2023-10-05 08:27:17 +02:00
if (!base_2mb_virtual_page || !MmIsAddressValid(base_2mb_virtual_page))
2023-09-03 19:33:27 +02:00
continue;
EnumerateKernelLargePages(
base_2mb_virtual_page,
LARGE_PAGE_2MB_ENTRIES,
2023-10-07 07:27:22 +02:00
Context,
2023-09-15 22:25:02 +02:00
INDEX_PROCESS_POOL_TAG
2023-09-03 19:33:27 +02:00
);
2023-08-26 14:07:06 +02:00
continue;
}
2023-08-26 15:29:12 +02:00
physical.QuadPart = pd_entry.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pd_base + pd_index * sizeof(UINT64)))
2023-09-17 05:14:02 +02:00
continue;
2023-10-05 08:27:17 +02:00
pt_base = MmGetVirtualForPhysical(physical);
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (!pt_base || !MmIsAddressValid(pt_base))
2023-08-26 14:07:06 +02:00
continue;
2023-10-05 08:27:17 +02:00
for (INT pt_index = 0; pt_index < PT_ENTRY_COUNT; pt_index++)
2023-08-26 14:07:06 +02:00
{
2023-10-05 08:27:17 +02:00
if (!MmIsAddressValid(pt_base + pt_index * sizeof(UINT64)))
2023-08-30 15:38:12 +02:00
continue;
2023-10-05 08:27:17 +02:00
pt_entry.BitAddress = *(UINT64*)(pt_base + pt_index * sizeof(UINT64));
2023-08-26 14:07:06 +02:00
2023-10-05 08:27:17 +02:00
if (pt_entry.Bits.Present == NULL)
2023-08-26 14:07:06 +02:00
continue;
2023-08-26 15:29:12 +02:00
physical.QuadPart = pt_entry.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
2023-08-26 14:07:06 +02:00
2023-08-26 19:19:26 +02:00
/* if the page base isnt in a legit region, go next */
2023-10-05 08:27:17 +02:00
if (IsPhysicalAddressInPhysicalMemoryRange(physical.QuadPart, physical_memory_ranges) == FALSE)
2023-09-03 19:33:27 +02:00
continue;
2023-08-26 19:19:26 +02:00
2023-10-05 08:27:17 +02:00
base_virtual_page = MmGetVirtualForPhysical(physical);
2023-08-26 15:29:12 +02:00
2023-08-26 17:48:50 +02:00
/* stupid fucking intellisense error GO AWAY! */
2023-10-05 08:27:17 +02:00
if (base_virtual_page == NULL || !MmIsAddressValid(base_virtual_page))
2023-09-03 19:33:27 +02:00
continue;
2023-08-26 17:48:50 +02:00
2023-08-28 11:17:38 +02:00
ScanPageForKernelObjectAllocation(
base_virtual_page,
PAGE_BASE_SIZE,
2023-08-28 13:10:07 +02:00
INDEX_PROCESS_POOL_TAG,
2023-10-07 07:27:22 +02:00
Context
2023-08-28 11:17:38 +02:00
);
2023-08-26 14:07:06 +02:00
}
}
}
}
2023-10-05 08:27:17 +02:00
DEBUG_LOG("Finished scanning memory");
2023-08-28 11:17:38 +02:00
}
2023-08-27 07:49:59 +02:00
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
VOID
2023-10-07 07:27:22 +02:00
IncrementProcessCounter(
_In_ PEPROCESS Process,
_In_opt_ PVOID Context
)
2023-08-28 11:17:38 +02:00
{
2023-10-07 07:27:22 +02:00
PPROCESS_SCAN_CONTEXT context = (PPROCESS_SCAN_CONTEXT)Context;
if (!context)
return;
context->process_count++;
2023-08-28 11:17:38 +02:00
}
2023-08-27 07:49:59 +02:00
2023-09-27 06:22:14 +02:00
STATIC
2023-10-05 08:27:17 +02:00
VOID
2023-09-27 06:22:14 +02:00
CheckIfProcessAllocationIsInProcessList(
2023-10-07 07:27:22 +02:00
_In_ PEPROCESS Process,
_In_opt_ PVOID Context
2023-08-28 11:17:38 +02:00
)
{
PUINT64 allocation_address;
2023-10-07 07:27:22 +02:00
PPROCESS_SCAN_CONTEXT context = (PPROCESS_SCAN_CONTEXT)Context;
if (!context)
return;
2023-09-13 12:25:32 +02:00
2023-10-07 07:27:22 +02:00
for (INT i = 0; i < context->process_count; i++)
2023-08-28 11:17:38 +02:00
{
2023-10-07 07:27:22 +02:00
allocation_address = (PUINT64)context->process_buffer;
2023-08-28 11:17:38 +02:00
2023-10-05 08:27:17 +02:00
if ((UINT64)Process >= allocation_address[i] - PROCESS_OBJECT_ALLOCATION_MARGIN &&
(UINT64)Process <= allocation_address[i] + PROCESS_OBJECT_ALLOCATION_MARGIN)
2023-08-28 11:17:38 +02:00
{
2023-10-07 07:27:22 +02:00
RtlZeroMemory((UINT64)context->process_buffer + i * sizeof(UINT64), sizeof(UINT64));
2023-08-28 11:17:38 +02:00
}
}
2023-08-26 14:07:06 +02:00
}
2023-10-05 08:27:17 +02:00
NTSTATUS
2023-09-27 06:22:14 +02:00
FindUnlinkedProcesses(
2023-08-28 17:00:52 +02:00
_In_ PIRP Irp
)
2023-08-26 14:07:06 +02:00
{
2023-08-28 11:17:38 +02:00
PUINT64 allocation_address;
2023-10-07 07:27:22 +02:00
PROCESS_SCAN_CONTEXT context = { 0 };
2023-08-28 17:00:52 +02:00
PINVALID_PROCESS_ALLOCATION_REPORT report_buffer = NULL;
2023-08-26 14:07:06 +02:00
2023-08-28 11:17:38 +02:00
EnumerateProcessListWithCallbackFunction(
2023-09-13 12:06:25 +02:00
IncrementProcessCounter,
2023-10-07 07:27:22 +02:00
&context
2023-08-28 11:17:38 +02:00
);
2023-08-26 14:07:06 +02:00
2023-10-07 07:27:22 +02:00
if (context.process_count == NULL)
2023-08-26 14:07:06 +02:00
{
2023-10-07 07:27:22 +02:00
DEBUG_ERROR("Failed to get process count");
2023-08-26 14:07:06 +02:00
return STATUS_ABANDONED;
}
2023-10-07 07:27:22 +02:00
context.process_buffer =
ExAllocatePool2(POOL_FLAG_NON_PAGED, context.process_count * 2 * sizeof(UINT64), PROCESS_ADDRESS_LIST_TAG);
2023-08-28 11:17:38 +02:00
2023-10-07 07:27:22 +02:00
if (!context.process_buffer)
2023-09-15 22:25:02 +02:00
return STATUS_ABANDONED;
2023-08-28 11:17:38 +02:00
2023-10-07 07:27:22 +02:00
WalkKernelPageTables(&context);
2023-08-28 11:17:38 +02:00
EnumerateProcessListWithCallbackFunction(
2023-09-13 12:06:25 +02:00
CheckIfProcessAllocationIsInProcessList,
2023-09-15 22:25:02 +02:00
NULL
2023-08-28 11:17:38 +02:00
);
2023-08-26 14:07:06 +02:00
2023-10-07 07:27:22 +02:00
allocation_address = (PUINT64)context.process_buffer;
2023-08-26 14:07:06 +02:00
2023-10-07 07:27:22 +02:00
for (INT i = 0; i < context.process_count; i++)
2023-08-26 14:07:06 +02:00
{
2023-10-05 08:27:17 +02:00
if (allocation_address[i] == NULL)
2023-08-28 11:17:38 +02:00
continue;
2023-09-23 14:27:02 +02:00
/*
* It's important to remember that at this point it is still not guaranteed that we have found
* an unlinked process allocation. It is better to have a few false positives that can be later
* analysed rather then enforce a strict signature and potentially miss a real unlinked process.
*/
2023-10-05 08:27:17 +02:00
DEBUG_ERROR("INVALID POOL proc OMGGG");
2023-08-28 17:00:52 +02:00
2023-10-07 07:27:22 +02:00
report_buffer =
ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(INVALID_PROCESS_ALLOCATION_REPORT), REPORT_POOL_TAG);
2023-08-28 17:00:52 +02:00
2023-10-05 08:27:17 +02:00
if (!report_buffer)
2023-08-28 17:00:52 +02:00
goto end;
2023-09-18 05:15:26 +02:00
report_buffer->report_code = REPORT_INVALID_PROCESS_ALLOCATION;
2023-08-28 17:00:52 +02:00
2023-09-18 05:15:26 +02:00
RtlCopyMemory(
report_buffer->process,
2023-10-05 08:27:17 +02:00
(UINT64)allocation_address[i] - OBJECT_HEADER_SIZE,
REPORT_INVALID_PROCESS_BUFFER_SIZE
2023-09-18 05:15:26 +02:00
);
2023-08-28 17:00:52 +02:00
2023-10-05 08:27:17 +02:00
InsertReportToQueue(report_buffer);
2023-08-28 11:17:38 +02:00
}
2023-08-28 17:00:52 +02:00
end:
2023-10-07 07:27:22 +02:00
if (context.process_buffer)
ExFreePoolWithTag(context.process_buffer, PROCESS_ADDRESS_LIST_TAG);
2023-08-26 14:07:06 +02:00
2023-08-28 11:17:38 +02:00
return STATUS_SUCCESS;
2023-08-26 14:07:06 +02:00
}