mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
oh yea
This commit is contained in:
parent
af7aa80a5c
commit
54a0e90405
3 changed files with 165 additions and 70 deletions
|
@ -5,6 +5,7 @@
|
|||
#include "callbacks.h"
|
||||
|
||||
#include "hv.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include "integrity.h"
|
||||
|
||||
|
@ -115,7 +116,7 @@ NTSTATUS DriverEntry(
|
|||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
WalkKernelPageTables,
|
||||
FindUnlinkedProcesses,
|
||||
NULL
|
||||
);
|
||||
|
||||
|
|
219
driver/pool.c
219
driver/pool.c
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
#define POOL_TAG_LENGTH 4
|
||||
|
@ -15,6 +17,9 @@ CHAR FILE_OBJECTS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x46\x69\x6C\x65";
|
|||
CHAR DRIVERS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x44\x72\x69\x76";
|
||||
CHAR SYMBOLIC_LINKS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x4C\x69\x6E\x6B";
|
||||
|
||||
PVOID process_buffer = NULL;
|
||||
ULONG process_count = NULL;
|
||||
|
||||
PKDDEBUGGER_DATA64 GetGlobalDebuggerData()
|
||||
{
|
||||
CONTEXT context = { 0 };
|
||||
|
@ -57,24 +62,57 @@ end:
|
|||
return debugger_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* For ~70% of EPROCESS structures the header layout is as follows:
|
||||
*
|
||||
* Pool base + 0x00 = ?? (not sure what structure lies here)
|
||||
* Pool base + 0x10 = OBJECT_HEADER_QUOTA_INFO
|
||||
* Pool base + 0x30 = OBJECT_HEADER_HANDLE_INFO
|
||||
* Pool base + 0x40 = OBJECT_HEADER
|
||||
* Pool base + 0x70 = EPROCESS
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
VOID ScanPageForKernelObjectAllocation(
|
||||
_In_ UINT64 PageBase,
|
||||
_In_ ULONG PageSize,
|
||||
_In_ LPCSTR ObjectTag
|
||||
_In_ LPCSTR ObjectTag,
|
||||
_In_ PVOID AddressBuffer
|
||||
)
|
||||
{
|
||||
INT length = 0;
|
||||
CHAR current_char;
|
||||
CHAR current_sig_byte;
|
||||
PPOOL_HEADER pool_header;
|
||||
PEPROCESS process;
|
||||
PEPROCESS process = NULL;
|
||||
LPCSTR process_name;
|
||||
PUINT64 address_list;
|
||||
ULONG allocation_size;
|
||||
|
||||
if ( !PageBase || !PageSize || !ObjectTag)
|
||||
return;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
for ( INT offset = 0; offset <= PageSize - POOL_TAG_LENGTH; offset++ )
|
||||
{
|
||||
for ( INT sig_index = 0; sig_index < POOL_TAG_LENGTH + 1; sig_index++ )
|
||||
|
@ -95,45 +133,41 @@ VOID ScanPageForKernelObjectAllocation(
|
|||
/*
|
||||
* This is some hard coded trash, need to figure out how we can differentiate different
|
||||
* types of objects since they would each have a varying number of headers, object sizes etc.
|
||||
*
|
||||
* For now we check 2 sizes, one of which is 0x10 smaller then the other (the unknown header?)
|
||||
* and make sure the pool is still allocated by checking the PoolType != 0.
|
||||
*
|
||||
* more: https://www.imf-conference.org/imf2006/23_Schuster-PoolAllocations.pdf
|
||||
*/
|
||||
if ( pool_header->BlockSize * CHUNK_SIZE - sizeof(POOL_HEADER) == WIN_PROCESS_ALLOCATION_SIZE )
|
||||
|
||||
allocation_size = pool_header->BlockSize * CHUNK_SIZE - sizeof( POOL_HEADER );
|
||||
|
||||
if ( ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE ||
|
||||
allocation_size == WIN_PROCESS_ALLOCATION_SIZE_2 ) &&
|
||||
pool_header->PoolType != NULL )
|
||||
{
|
||||
/*
|
||||
* For ~70% of EPROCESS structures the header layout is as follows:
|
||||
*
|
||||
* Pool base + 0x00 = ?? (not sure what structure lies here)
|
||||
* Pool base + 0x10 = OBJECT_HEADER_QUOTA_INFO
|
||||
* Pool base + 0x30 = OBJECT_HEADER_HANDLE_INFO
|
||||
* Pool base + 0x40 = OBJECT_HEADER
|
||||
* Pool base + 0x70 = EPROCESS
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
if ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE )
|
||||
process = process = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x70 );
|
||||
|
||||
process = (PEPROCESS)( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x70 );
|
||||
if ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE_2 )
|
||||
process = process = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x80 );
|
||||
|
||||
process_name = PsGetProcessImageFileName( process );
|
||||
if ( process == NULL )
|
||||
{
|
||||
DEBUG_LOG( "Process size is different to expected cos we hardcoded dis trash" );
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
DEBUG_LOG( "Found process: %s", process_name );
|
||||
address_list = ( PUINT64 )AddressBuffer;
|
||||
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
if ( address_list[ i ] == NULL )
|
||||
{
|
||||
address_list[ i ] = ( UINT64 )process;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -201,7 +235,7 @@ BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
|
|||
* and extract the physical address from the value at each virtual address page entry.
|
||||
*/
|
||||
|
||||
VOID WalkKernelPageTables()
|
||||
VOID WalkKernelPageTables(PVOID AddressBuffer)
|
||||
{
|
||||
CR3 cr3;
|
||||
PML4E pml4_base;
|
||||
|
@ -228,12 +262,6 @@ VOID WalkKernelPageTables()
|
|||
return;
|
||||
}
|
||||
|
||||
/* raise our irql to ensure we arent preempted by NOOB threads */
|
||||
KeRaiseIrql( DISPATCH_LEVEL, &irql );
|
||||
|
||||
/* disable interrupts to prevent any funny business occuring */
|
||||
_disable();
|
||||
|
||||
cr3.BitAddress = __readcr3();
|
||||
|
||||
physical.QuadPart = cr3.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
|
||||
|
@ -318,47 +346,104 @@ VOID WalkKernelPageTables()
|
|||
if ( base_virtual_page == NULL || !MmIsAddressValid( base_virtual_page ) )
|
||||
continue;
|
||||
|
||||
ScanPageForKernelObjectAllocation( base_virtual_page, PAGE_BASE_SIZE, (LPCSTR)PROCESS_POOL_TAG );
|
||||
ScanPageForKernelObjectAllocation(
|
||||
base_virtual_page,
|
||||
PAGE_BASE_SIZE,
|
||||
( LPCSTR )PROCESS_POOL_TAG,
|
||||
AddressBuffer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_enable();
|
||||
|
||||
KeLowerIrql( irql );
|
||||
|
||||
DEBUG_LOG( "Finished scanning memory" );
|
||||
}
|
||||
|
||||
VOID ScanNonPagedPoolForProcessTags()
|
||||
VOID IncrementProcessCounter()
|
||||
{
|
||||
process_count++;
|
||||
}
|
||||
|
||||
VOID CheckIfProcessAllocationIsInProcessList(
|
||||
_In_ PEPROCESS Process
|
||||
)
|
||||
{
|
||||
PUINT64 allocation_address;
|
||||
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
allocation_address = ( PUINT64 )process_buffer;
|
||||
|
||||
if ( ( UINT64 )Process >= allocation_address[ i ] - PROCESS_OBJECT_ALLOCATION_MARGIN &&
|
||||
( UINT64 )Process <= allocation_address[ i ] + PROCESS_OBJECT_ALLOCATION_MARGIN )
|
||||
{
|
||||
RtlZeroMemory( ( UINT64 )process_buffer + i * sizeof( UINT64 ), sizeof( UINT64 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Plan:
|
||||
* 1. Find number of running procs and allocate a pool equal to size 1.5 * num_procs.
|
||||
* 2. Walk the pages tables and store all found process allocation base addresses in this pool
|
||||
* 3. Enumerate the process allocation list and make sure that each allocation is within
|
||||
* 0x50 bytes of the EPROCESS base of one of the running processes.
|
||||
* 4. If there exists a process allocation that doesn't have a matching running process,
|
||||
* make sure it hasnt been deallocated
|
||||
* 5. If it hasn't been deallocated, search for the .exe via the long string file name
|
||||
* and report. Maybe do some further analysis can figure this out once we get there.
|
||||
*/
|
||||
NTSTATUS FindUnlinkedProcesses()
|
||||
{
|
||||
NTSTATUS status;
|
||||
PKDDEBUGGER_DATA64 debugger_data = NULL;
|
||||
UINT64 non_paged_pool_start = NULL;
|
||||
UINT64 non_paged_pool_end = NULL;
|
||||
PUINT64 allocation_address;
|
||||
|
||||
/* must free this */
|
||||
debugger_data = GetGlobalDebuggerData();
|
||||
EnumerateProcessListWithCallbackFunction(
|
||||
IncrementProcessCounter
|
||||
);
|
||||
|
||||
if ( debugger_data == NULL )
|
||||
if ( process_count == NULL )
|
||||
{
|
||||
DEBUG_ERROR( "Debugger data is null" );
|
||||
DEBUG_ERROR( "Faield to get process count " );
|
||||
return STATUS_ABANDONED;
|
||||
}
|
||||
|
||||
non_paged_pool_start = debugger_data->MmNonPagedPoolStart;
|
||||
non_paged_pool_end = debugger_data->MmNonPagedPoolEnd;
|
||||
DEBUG_LOG( "Proc count: %lx", process_count );
|
||||
|
||||
DEBUG_LOG( "NonPagedPool start: %llx, end %llx", non_paged_pool_start, non_paged_pool_end );
|
||||
process_buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, process_count * 2 * sizeof( UINT64 ), PROCESS_ADDRESS_LIST_TAG );
|
||||
|
||||
WalkKernelPageTables();
|
||||
if ( !process_buffer )
|
||||
return STATUS_ABANDONED;
|
||||
|
||||
/* for ( ; non_paged_pool_start <= non_paged_pool_end; non_paged_pool_start++ )
|
||||
WalkKernelPageTables( process_buffer );
|
||||
|
||||
EnumerateProcessListWithCallbackFunction(
|
||||
CheckIfProcessAllocationIsInProcessList
|
||||
);
|
||||
|
||||
allocation_address = ( PUINT64 )process_buffer;
|
||||
|
||||
DEBUG_LOG( "Allocation addr: %p", allocation_address );
|
||||
|
||||
for ( INT i = 0; i < process_count; i++ )
|
||||
{
|
||||
CHAR current_byte = *( CHAR* )non_paged_pool_start;
|
||||
DEBUG_LOG( "Current byte: %c", current_byte );
|
||||
*/
|
||||
if ( allocation_address[ i ] == NULL )
|
||||
continue;
|
||||
|
||||
ExFreePoolWithTag( debugger_data, POOL_DEBUGGER_DATA_TAG );
|
||||
/* process has been deallocated yet the pool header hasnt been updated? */
|
||||
if ( *( UINT8* )( allocation_address[ i ] + EPROCESS_VIRTUAL_SIZE_OFFSET ) == 0x0 )
|
||||
continue;
|
||||
|
||||
DEBUG_ERROR( "INVALID POOL proc OMGGG" );
|
||||
}
|
||||
|
||||
DEBUG_LOG( "Finished scaning xd" );
|
||||
|
||||
__debugbreak();
|
||||
|
||||
ExFreePoolWithTag( process_buffer, PROCESS_ADDRESS_LIST_TAG );
|
||||
process_count = NULL;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#define POOL_DUMP_BLOCK_TAG 'dump'
|
||||
#define POOL_DEBUGGER_DATA_TAG 'data'
|
||||
#define PROCESS_ADDRESS_LIST_TAG 'addr'
|
||||
|
||||
#define PML4_ENTRY_COUNT 512
|
||||
#define PDPT_ENTRY_COUNT 512
|
||||
|
@ -14,10 +15,18 @@
|
|||
#define PAGE_BASE_SIZE 0x1000
|
||||
#define POOL_TAG_SIZE 0x004
|
||||
|
||||
#define PROCESS_OBJECT_ALLOCATION_MARGIN 0x90
|
||||
|
||||
#define EPROCESS_VIRTUAL_SIZE_OFFSET 0x498
|
||||
|
||||
/* SIZE_2 = first alloc + 0x10 */
|
||||
#define WIN_PROCESS_ALLOCATION_SIZE 0xcf0
|
||||
#define WIN_PROCESS_ALLOCATION_SIZE_2 0xd00
|
||||
|
||||
#define CHUNK_SIZE 16
|
||||
|
||||
NTSTATUS FindUnlinkedProcesses();
|
||||
|
||||
/* creds: https://www.unknowncheats.me/forum/2602838-post2.html */
|
||||
|
||||
typedef struct _DBGKD_DEBUG_DATA_HEADER64
|
||||
|
|
Loading…
Reference in a new issue