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 "callbacks.h"
|
||||||
|
|
||||||
#include "hv.h"
|
#include "hv.h"
|
||||||
|
#include "pool.h"
|
||||||
|
|
||||||
#include "integrity.h"
|
#include "integrity.h"
|
||||||
|
|
||||||
|
@ -115,7 +116,7 @@ NTSTATUS DriverEntry(
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
WalkKernelPageTables,
|
FindUnlinkedProcesses,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
219
driver/pool.c
219
driver/pool.c
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include "callbacks.h"
|
||||||
|
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
|
||||||
#define POOL_TAG_LENGTH 4
|
#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 DRIVERS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x44\x72\x69\x76";
|
||||||
CHAR SYMBOLIC_LINKS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x4C\x69\x6E\x6B";
|
CHAR SYMBOLIC_LINKS_POOL_TAG[ POOL_TAG_LENGTH ] = "\x4C\x69\x6E\x6B";
|
||||||
|
|
||||||
|
PVOID process_buffer = NULL;
|
||||||
|
ULONG process_count = NULL;
|
||||||
|
|
||||||
PKDDEBUGGER_DATA64 GetGlobalDebuggerData()
|
PKDDEBUGGER_DATA64 GetGlobalDebuggerData()
|
||||||
{
|
{
|
||||||
CONTEXT context = { 0 };
|
CONTEXT context = { 0 };
|
||||||
|
@ -57,24 +62,57 @@ end:
|
||||||
return debugger_data;
|
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(
|
VOID ScanPageForKernelObjectAllocation(
|
||||||
_In_ UINT64 PageBase,
|
_In_ UINT64 PageBase,
|
||||||
_In_ ULONG PageSize,
|
_In_ ULONG PageSize,
|
||||||
_In_ LPCSTR ObjectTag
|
_In_ LPCSTR ObjectTag,
|
||||||
|
_In_ PVOID AddressBuffer
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
INT length = 0;
|
INT length = 0;
|
||||||
CHAR current_char;
|
CHAR current_char;
|
||||||
CHAR current_sig_byte;
|
CHAR current_sig_byte;
|
||||||
PPOOL_HEADER pool_header;
|
PPOOL_HEADER pool_header;
|
||||||
PEPROCESS process;
|
PEPROCESS process = NULL;
|
||||||
LPCSTR process_name;
|
LPCSTR process_name;
|
||||||
|
PUINT64 address_list;
|
||||||
|
ULONG allocation_size;
|
||||||
|
|
||||||
if ( !PageBase || !PageSize || !ObjectTag)
|
if ( !PageBase || !PageSize || !ObjectTag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PAGED_CODE();
|
|
||||||
|
|
||||||
for ( INT offset = 0; offset <= PageSize - POOL_TAG_LENGTH; offset++ )
|
for ( INT offset = 0; offset <= PageSize - POOL_TAG_LENGTH; offset++ )
|
||||||
{
|
{
|
||||||
for ( INT sig_index = 0; sig_index < POOL_TAG_LENGTH + 1; sig_index++ )
|
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
|
* 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.
|
* 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 )
|
||||||
{
|
{
|
||||||
/*
|
if ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE )
|
||||||
* For ~70% of EPROCESS structures the header layout is as follows:
|
process = process = ( PEPROCESS )( ( UINT64 )pool_header + sizeof( POOL_HEADER ) + 0x70 );
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
address_list = ( PUINT64 )AddressBuffer;
|
||||||
* 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
|
for ( INT i = 0; i < process_count; i++ )
|
||||||
* 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
|
if ( address_list[ i ] == NULL )
|
||||||
* 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?
|
address_list[ i ] = ( UINT64 )process;
|
||||||
*
|
break;
|
||||||
* 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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -201,7 +235,7 @@ BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
|
||||||
* and extract the physical address from the value at each virtual address page entry.
|
* and extract the physical address from the value at each virtual address page entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
VOID WalkKernelPageTables()
|
VOID WalkKernelPageTables(PVOID AddressBuffer)
|
||||||
{
|
{
|
||||||
CR3 cr3;
|
CR3 cr3;
|
||||||
PML4E pml4_base;
|
PML4E pml4_base;
|
||||||
|
@ -228,12 +262,6 @@ VOID WalkKernelPageTables()
|
||||||
return;
|
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();
|
cr3.BitAddress = __readcr3();
|
||||||
|
|
||||||
physical.QuadPart = cr3.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
|
physical.QuadPart = cr3.Bits.PhysicalAddress << PAGE_4KB_SHIFT;
|
||||||
|
@ -318,47 +346,104 @@ VOID WalkKernelPageTables()
|
||||||
if ( base_virtual_page == NULL || !MmIsAddressValid( base_virtual_page ) )
|
if ( base_virtual_page == NULL || !MmIsAddressValid( base_virtual_page ) )
|
||||||
continue;
|
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" );
|
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;
|
NTSTATUS status;
|
||||||
PKDDEBUGGER_DATA64 debugger_data = NULL;
|
PUINT64 allocation_address;
|
||||||
UINT64 non_paged_pool_start = NULL;
|
|
||||||
UINT64 non_paged_pool_end = NULL;
|
|
||||||
|
|
||||||
/* must free this */
|
EnumerateProcessListWithCallbackFunction(
|
||||||
debugger_data = GetGlobalDebuggerData();
|
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;
|
return STATUS_ABANDONED;
|
||||||
}
|
}
|
||||||
|
|
||||||
non_paged_pool_start = debugger_data->MmNonPagedPoolStart;
|
DEBUG_LOG( "Proc count: %lx", process_count );
|
||||||
non_paged_pool_end = debugger_data->MmNonPagedPoolEnd;
|
|
||||||
|
|
||||||
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;
|
if ( allocation_address[ i ] == NULL )
|
||||||
DEBUG_LOG( "Current byte: %c", current_byte );
|
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_DUMP_BLOCK_TAG 'dump'
|
||||||
#define POOL_DEBUGGER_DATA_TAG 'data'
|
#define POOL_DEBUGGER_DATA_TAG 'data'
|
||||||
|
#define PROCESS_ADDRESS_LIST_TAG 'addr'
|
||||||
|
|
||||||
#define PML4_ENTRY_COUNT 512
|
#define PML4_ENTRY_COUNT 512
|
||||||
#define PDPT_ENTRY_COUNT 512
|
#define PDPT_ENTRY_COUNT 512
|
||||||
|
@ -14,10 +15,18 @@
|
||||||
#define PAGE_BASE_SIZE 0x1000
|
#define PAGE_BASE_SIZE 0x1000
|
||||||
#define POOL_TAG_SIZE 0x004
|
#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 0xcf0
|
||||||
|
#define WIN_PROCESS_ALLOCATION_SIZE_2 0xd00
|
||||||
|
|
||||||
#define CHUNK_SIZE 16
|
#define CHUNK_SIZE 16
|
||||||
|
|
||||||
|
NTSTATUS FindUnlinkedProcesses();
|
||||||
|
|
||||||
/* creds: https://www.unknowncheats.me/forum/2602838-post2.html */
|
/* creds: https://www.unknowncheats.me/forum/2602838-post2.html */
|
||||||
|
|
||||||
typedef struct _DBGKD_DEBUG_DATA_HEADER64
|
typedef struct _DBGKD_DEBUG_DATA_HEADER64
|
||||||
|
|
Loading…
Reference in a new issue