bed time c:

This commit is contained in:
lhodges1 2023-08-29 01:00:52 +10:00
parent a5822fb5cf
commit 013f5a9906
15 changed files with 455 additions and 390 deletions

View file

@ -132,7 +132,6 @@
<ClCompile Include="integrity.c" />
<ClCompile Include="ioctl.c" />
<ClCompile Include="modules.c" />
<ClCompile Include="nmi.c" />
<ClCompile Include="pool.c" />
<ClCompile Include="queue.c" />
</ItemGroup>
@ -144,7 +143,6 @@
<ClInclude Include="integrity.h" />
<ClInclude Include="ioctl.h" />
<ClInclude Include="modules.h" />
<ClInclude Include="nmi.h" />
<ClInclude Include="pool.h" />
<ClInclude Include="queue.h" />
</ItemGroup>

View file

@ -30,9 +30,6 @@
<ClCompile Include="ioctl.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="nmi.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="modules.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -62,9 +59,6 @@
<ClInclude Include="ioctl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="nmi.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="modules.h">
<Filter>Header Files</Filter>
</ClInclude>

View file

@ -44,7 +44,7 @@ NTSTATUS GetDriverImageSize(
return status;
}
NTSTATUS New_CopyDriverExecutableRegions(
NTSTATUS CopyDriverExecutableRegions(
_In_ PIRP Irp
)
{
@ -181,4 +181,20 @@ end:
ExFreePoolWithTag( buffer, POOL_TAG_INTEGRITY );
return status;
}
/*
* 1. map driver to memory
* 2. store executable sections in buffer
* 3. do the same with the in-memory module
* 4. hash both buffers with the current time or something
* 5. compare
*/
NTSTATUS PerformInMemoryIntegrityCheckVsDiskImage(
_In_ PIRP Irp
)
{
NTSTATUS status;
}

View file

@ -5,8 +5,6 @@
#define POOL_TAG_INTEGRITY 'intg'
VOID WalkKernelPageTables();
NTSTATUS CopyDriverExecutableRegions(
_In_ PIRP Irp
);
@ -15,10 +13,6 @@ NTSTATUS GetDriverImageSize(
_In_ PIRP Irp
);
NTSTATUS New_CopyDriverExecutableRegions(
_In_ PIRP Irp
);
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
#define IMAGE_SIZEOF_SHORT_NAME 8

View file

@ -2,7 +2,6 @@
#include "common.h"
#include "nmi.h"
#include "modules.h"
#include "driver.h"
#include "callbacks.h"
@ -156,7 +155,7 @@ NTSTATUS DeviceControl(
NULL,
NULL,
NULL,
New_CopyDriverExecutableRegions,
CopyDriverExecutableRegions,
Irp
);
@ -210,6 +209,15 @@ NTSTATUS DeviceControl(
break;
case IOCTL_SCAN_FOR_UNLINKED_PROCESS:
status = FindUnlinkedProcesses( Irp );
if ( !NT_SUCCESS( status ) )
DEBUG_ERROR( "FindUNlinekdProcesses failed with status %x", status );
break;
default:
DEBUG_ERROR( "Invalid IOCTL passed to driver" );
break;

View file

@ -5,8 +5,6 @@
#include <wdftypes.h>
#include <wdf.h>
#include "nmi.h"
#define IOCCTL_RUN_NMI_CALLBACKS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2001, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_VALIDATE_DRIVER_OBJECTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2002, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2004, METHOD_BUFFERED, FILE_ANY_ACCESS)
@ -16,6 +14,7 @@
#define IOCTL_RETRIEVE_MODULE_EXECUTABLE_REGIONS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2008, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_REQUEST_TOTAL_MODULE_SIZE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2009, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_NOTIFY_DRIVER_ON_PROCESS_TERMINATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2010, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SCAN_FOR_UNLINKED_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2011, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _DRIVER_INITIATION_INFORMATION
{

View file

@ -1,9 +1,14 @@
#include "modules.h"
#include "nmi.h"
#define WHITELISTED_MODULE_TAG 'whte'
PVOID nmi_callback_handle = NULL;
/* Global structure to hold pointers to required memory for the NMI's */
NMI_POOLS nmi_pools = { 0 };
#define NMI_DELAY 100 * 10000
#define WHITELISTED_MODULE_COUNT 3
#define MODULE_MAX_STRING_SIZE 256
@ -18,13 +23,6 @@ CHAR WHITELISTED_MODULES[ WHITELISTED_MODULE_COUNT ][ MODULE_MAX_STRING_SIZE ] =
"Wdf01000.sys",
};
typedef struct _WHITELISTED_REGIONS
{
UINT64 base;
UINT64 end;
}WHITELISTED_REGIONS, *PWHITELISTED_REGIONS;
PRTL_MODULE_EXTENDED_INFO FindSystemModuleByName(
_In_ LPCSTR ModuleName,
_In_ PSYSTEM_MODULES SystemModules
@ -546,5 +544,285 @@ NTSTATUS HandleValidateDriversIOCTL(
ExFreePoolWithTag( head, INVALID_DRIVER_LIST_HEAD_POOL );
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
return status;
}
NTSTATUS IsInstructionPointerInInvalidRegion(
_In_ UINT64 RIP,
_In_ PSYSTEM_MODULES SystemModules,
_Out_ PBOOLEAN Result
)
{
if ( !RIP || !SystemModules || !Result )
return STATUS_INVALID_PARAMETER;
/* Note that this does not check for HAL or PatchGuard Execution */
for ( INT i = 0; i < SystemModules->module_count; i++ )
{
PRTL_MODULE_EXTENDED_INFO system_module = ( PRTL_MODULE_EXTENDED_INFO )(
( uintptr_t )SystemModules->address + i * sizeof( RTL_MODULE_EXTENDED_INFO ) );
UINT64 base = ( UINT64 )system_module->ImageBase;
UINT64 end = base + system_module->ImageSize;
if ( RIP >= base && RIP <= end )
{
*Result = TRUE;
return STATUS_SUCCESS;;
}
}
*Result = FALSE;
return STATUS_SUCCESS;
}
NTSTATUS AnalyseNmiData(
_In_ INT NumCores,
_In_ PSYSTEM_MODULES SystemModules,
_In_ PIRP Irp
)
{
if ( !NumCores || !SystemModules )
return STATUS_INVALID_PARAMETER;
for ( INT core = 0; core < NumCores; core++ )
{
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )nmi_pools.nmi_context + core * sizeof( NMI_CONTEXT ) );
/* Make sure our NMIs were run */
if ( !context->nmi_callbacks_run )
{
NMI_CALLBACK_FAILURE report;
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = NULL;
report.invalid_rip = NULL;
report.were_nmis_disabled = TRUE;
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( NMI_CALLBACK_FAILURE )
);
return STATUS_SUCCESS;
}
PNMI_CALLBACK_DATA thread_data = ( PNMI_CALLBACK_DATA )(
( uintptr_t )nmi_pools.thread_data_pool + core * sizeof( NMI_CALLBACK_DATA ) );
DEBUG_LOG( "cpu number: %i callback count: %i", core, context->nmi_callbacks_run );
/* Walk the stack */
for ( INT frame = 0; frame < thread_data->num_frames_captured; frame++ )
{
BOOLEAN flag;
DWORD64 stack_frame = *( DWORD64* )(
( ( uintptr_t )nmi_pools.stack_frames + thread_data->stack_frames_offset + frame * sizeof( PVOID ) ) );
if ( !NT_SUCCESS( IsInstructionPointerInInvalidRegion( stack_frame, SystemModules, &flag ) ) )
{
DEBUG_ERROR( "errro checking RIP for current stack address" );
continue;
}
if ( flag == FALSE )
{
/*
* Note: for now, we only handle 1 report at a time so we stop the
* analysis once we receive a report since we only send a buffer
* large enough for 1 report. In the future this should be changed
* to a buffer that can hold atleast 4 reports (since the chance we
* get 4 reports with a single NMI would be impossible) so we can
* continue parsing the rest of the stack frames after receiving a
* single report.
*/
NMI_CALLBACK_FAILURE report;
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = thread_data->kthread_address;
report.invalid_rip = stack_frame;
report.were_nmis_disabled = FALSE;
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( NMI_CALLBACK_FAILURE )
);
return STATUS_SUCCESS;
}
}
}
return STATUS_SUCCESS;
}
BOOLEAN NmiCallback(
_In_ PVOID Context,
_In_ BOOLEAN Handled
)
{
UNREFERENCED_PARAMETER( Handled );
ULONG proc_num = KeGetCurrentProcessorNumber();
PVOID current_thread = KeGetCurrentThread();
NMI_CALLBACK_DATA thread_data = { 0 };
/*
* Cannot allocate pool in this function as it runs at IRQL >= dispatch level
* so ive just allocated a global pool with size equal to 0x200 * num_procs
*/
INT num_frames_captured = RtlCaptureStackBackTrace(
NULL,
STACK_FRAME_POOL_SIZE,
( uintptr_t )nmi_pools.stack_frames + proc_num * STACK_FRAME_POOL_SIZE,
NULL
);
/*
* This function is run in the context of the interrupted thread hence we can
* gather any and all information regarding the thread that may be useful for analysis
*/
thread_data.kthread_address = ( UINT64 )current_thread;
thread_data.kprocess_address = ( UINT64 )PsGetCurrentProcess();
thread_data.stack_base = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_BASE_OFFSET ) );
thread_data.stack_limit = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_LIMIT_OFFSET ) );
thread_data.start_address = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_START_ADDRESS_OFFSET ) );
thread_data.cr3 = __readcr3();
thread_data.stack_frames_offset = proc_num * STACK_FRAME_POOL_SIZE;
thread_data.num_frames_captured = num_frames_captured;
RtlCopyMemory(
( ( uintptr_t )nmi_pools.thread_data_pool ) + proc_num * sizeof( thread_data ),
&thread_data,
sizeof( thread_data )
);
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )Context + proc_num * sizeof( NMI_CONTEXT ) );
context->nmi_callbacks_run += 1;
DEBUG_LOG( "num nmis called: %i from addr: %llx", context->nmi_callbacks_run, ( uintptr_t )context );
return TRUE;
}
NTSTATUS LaunchNonMaskableInterrupt(
_In_ ULONG NumCores
)
{
if ( !NumCores )
return STATUS_INVALID_PARAMETER;
PKAFFINITY_EX ProcAffinityPool = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KAFFINITY_EX ), PROC_AFFINITY_POOL );
if ( !ProcAffinityPool )
return STATUS_ABANDONED;
nmi_pools.stack_frames = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL );
if ( !nmi_pools.stack_frames )
{
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
nmi_pools.thread_data_pool = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * sizeof( NMI_CALLBACK_DATA ), THREAD_DATA_POOL );
if ( !nmi_pools.thread_data_pool )
{
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
LARGE_INTEGER delay = { 0 };
delay.QuadPart -= NMI_DELAY;
for ( ULONG core = 0; core < NumCores; core++ )
{
KeInitializeAffinityEx( ProcAffinityPool );
KeAddProcessorAffinityEx( ProcAffinityPool, core );
DEBUG_LOG( "Sending NMI" );
HalSendNMI( ProcAffinityPool );
/*
* Only a single NMI can be active at any given time, so arbitrarily
* delay execution to allow time for the NMI to be processed
*/
KeDelayExecutionThread( KernelMode, FALSE, &delay );
}
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_SUCCESS;
}
NTSTATUS HandleNmiIOCTL(
_In_ PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
SYSTEM_MODULES system_modules = { 0 };
ULONG num_cores = KeQueryActiveProcessorCountEx( 0 );
/* Fix annoying visual studio linting error */
RtlZeroMemory( &system_modules, sizeof( SYSTEM_MODULES ) );
RtlZeroMemory( &nmi_pools, sizeof( NMI_POOLS ) );
nmi_pools.nmi_context = ExAllocatePool2( POOL_FLAG_NON_PAGED, num_cores * sizeof( NMI_CONTEXT ), NMI_CONTEXT_POOL );
if ( !nmi_pools.nmi_context )
{
DEBUG_ERROR( "nmi_context ExAllocatePool2 failed" );
return STATUS_ABANDONED;
}
/*
* We want to register and unregister our callback each time so it becomes harder
* for people to hook our callback and get up to some funny business
*/
nmi_callback_handle = KeRegisterNmiCallback( NmiCallback, nmi_pools.nmi_context );
if ( !nmi_callback_handle )
{
DEBUG_ERROR( "KeRegisterNmiCallback failed" );
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
return STATUS_ABANDONED;
}
/*
* We query the system modules each time since they can potentially
* change at any time
*/
status = GetSystemModuleInformation( &system_modules );
if ( !NT_SUCCESS( status ) )
{
DEBUG_ERROR( "Error retriving system module information" );
return status;
}
status = LaunchNonMaskableInterrupt( num_cores );
if ( !NT_SUCCESS( status ) )
{
DEBUG_ERROR( "Error running NMI callbacks" );
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
return status;
}
status = AnalyseNmiData( num_cores, &system_modules, Irp );
if ( !NT_SUCCESS( status ) )
DEBUG_ERROR( "Error analysing nmi data" );
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
ExFreePoolWithTag( nmi_pools.thread_data_pool, THREAD_DATA_POOL );
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
KeDeregisterNmiCallback( nmi_callback_handle );
return status;
}

View file

@ -13,6 +13,55 @@
#define REASON_NO_BACKING_MODULE 1
#define REASON_INVALID_IOCTL_DISPATCH 2
#define REPORT_NMI_CALLBACK_FAILURE 50
NTSTATUS HandleNmiIOCTL(
_In_ PIRP Irp
);
typedef struct _WHITELISTED_REGIONS
{
UINT64 base;
UINT64 end;
}WHITELISTED_REGIONS, * PWHITELISTED_REGIONS;
typedef struct _NMI_POOLS
{
PVOID thread_data_pool;
PVOID stack_frames;
PVOID nmi_context;
}NMI_POOLS, * PNMI_POOLS;
typedef struct NMI_CALLBACK_FAILURE
{
INT report_code;
INT were_nmis_disabled;
UINT64 kthread_address;
UINT64 invalid_rip;
}NMI_CALLBACK_FAILURE, * PNMI_CALLBACK_FAILURE;
typedef struct _NMI_CONTEXT
{
INT nmi_callbacks_run;
}NMI_CONTEXT, * PNMI_CONTEXT;
typedef struct _NMI_CALLBACK_DATA
{
UINT64 kthread_address;
UINT64 kprocess_address;
UINT64 start_address;
UINT64 stack_limit;
UINT64 stack_base;
uintptr_t stack_frames_offset;
INT num_frames_captured;
UINT64 cr3;
}NMI_CALLBACK_DATA, * PNMI_CALLBACK_DATA;
typedef struct _MODULE_VALIDATION_FAILURE_HEADER
{
INT module_count;

View file

@ -1,299 +0,0 @@
#include "nmi.h"
#include "modules.h"
#include "common.h"
#define NMI_DELAY 100 * 10000
typedef struct _NMI_POOLS
{
PVOID thread_data_pool;
PVOID stack_frames;
PVOID nmi_context;
}NMI_POOLS, * PNMI_POOLS;
PVOID nmi_callback_handle = NULL;
/* Global structure to hold pointers to required memory for the NMI's */
NMI_POOLS nmi_pools = { 0 };
NTSTATUS IsInstructionPointerInInvalidRegion(
_In_ UINT64 RIP,
_In_ PSYSTEM_MODULES SystemModules,
_Out_ PBOOLEAN Result
)
{
if ( !RIP || !SystemModules || !Result )
return STATUS_INVALID_PARAMETER;
/* Note that this does not check for HAL or PatchGuard Execution */
for ( INT i = 0; i < SystemModules->module_count; i++ )
{
PRTL_MODULE_EXTENDED_INFO system_module = ( PRTL_MODULE_EXTENDED_INFO )(
( uintptr_t )SystemModules->address + i * sizeof( RTL_MODULE_EXTENDED_INFO ) );
UINT64 base = ( UINT64 )system_module->ImageBase;
UINT64 end = base + system_module->ImageSize;
if ( RIP >= base && RIP <= end )
{
*Result = TRUE;
return STATUS_SUCCESS;;
}
}
*Result = FALSE;
return STATUS_SUCCESS;
}
NTSTATUS AnalyseNmiData(
_In_ INT NumCores,
_In_ PSYSTEM_MODULES SystemModules,
_In_ PIRP Irp
)
{
if ( !NumCores || !SystemModules )
return STATUS_INVALID_PARAMETER;
for ( INT core = 0; core < NumCores; core++ )
{
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )nmi_pools.nmi_context + core * sizeof( NMI_CONTEXT ) );
/* Make sure our NMIs were run */
if ( !context->nmi_callbacks_run )
{
NMI_CALLBACK_FAILURE report;
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = NULL;
report.invalid_rip = NULL;
report.were_nmis_disabled = TRUE;
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( NMI_CALLBACK_FAILURE )
);
return STATUS_SUCCESS;
}
PNMI_CALLBACK_DATA thread_data = ( PNMI_CALLBACK_DATA )(
( uintptr_t )nmi_pools.thread_data_pool + core * sizeof( NMI_CALLBACK_DATA ) );
DEBUG_LOG( "cpu number: %i callback count: %i", core, context->nmi_callbacks_run );
/* Walk the stack */
for ( INT frame = 0; frame < thread_data->num_frames_captured; frame++ )
{
BOOLEAN flag;
DWORD64 stack_frame = *( DWORD64* )(
( ( uintptr_t )nmi_pools.stack_frames + thread_data->stack_frames_offset + frame * sizeof( PVOID ) ) );
if ( !NT_SUCCESS( IsInstructionPointerInInvalidRegion( stack_frame, SystemModules, &flag ) ) )
{
DEBUG_ERROR( "errro checking RIP for current stack address" );
continue;
}
if ( flag == FALSE )
{
/*
* Note: for now, we only handle 1 report at a time so we stop the
* analysis once we receive a report since we only send a buffer
* large enough for 1 report. In the future this should be changed
* to a buffer that can hold atleast 4 reports (since the chance we
* get 4 reports with a single NMI would be impossible) so we can
* continue parsing the rest of the stack frames after receiving a
* single report.
*/
NMI_CALLBACK_FAILURE report;
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = thread_data->kthread_address;
report.invalid_rip = stack_frame;
report.were_nmis_disabled = FALSE;
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( NMI_CALLBACK_FAILURE )
);
return STATUS_SUCCESS;
}
}
}
return STATUS_SUCCESS;
}
BOOLEAN NmiCallback(
_In_ PVOID Context,
_In_ BOOLEAN Handled
)
{
UNREFERENCED_PARAMETER( Handled );
ULONG proc_num = KeGetCurrentProcessorNumber();
PVOID current_thread = KeGetCurrentThread();
NMI_CALLBACK_DATA thread_data = { 0 };
/*
* Cannot allocate pool in this function as it runs at IRQL >= dispatch level
* so ive just allocated a global pool with size equal to 0x200 * num_procs
*/
INT num_frames_captured = RtlCaptureStackBackTrace(
NULL,
STACK_FRAME_POOL_SIZE,
( uintptr_t )nmi_pools.stack_frames + proc_num * STACK_FRAME_POOL_SIZE,
NULL
);
/*
* This function is run in the context of the interrupted thread hence we can
* gather any and all information regarding the thread that may be useful for analysis
*/
thread_data.kthread_address = ( UINT64 )current_thread;
thread_data.kprocess_address = ( UINT64 )PsGetCurrentProcess();
thread_data.stack_base = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_BASE_OFFSET ) );
thread_data.stack_limit = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_LIMIT_OFFSET ) );
thread_data.start_address = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_START_ADDRESS_OFFSET ) );
thread_data.cr3 = __readcr3();
thread_data.stack_frames_offset = proc_num * STACK_FRAME_POOL_SIZE;
thread_data.num_frames_captured = num_frames_captured;
RtlCopyMemory(
( ( uintptr_t )nmi_pools.thread_data_pool ) + proc_num * sizeof( thread_data ),
&thread_data,
sizeof( thread_data )
);
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )Context + proc_num * sizeof( NMI_CONTEXT ) );
context->nmi_callbacks_run += 1;
DEBUG_LOG( "num nmis called: %i from addr: %llx", context->nmi_callbacks_run, ( uintptr_t )context );
return TRUE;
}
NTSTATUS LaunchNonMaskableInterrupt(
_In_ ULONG NumCores
)
{
if ( !NumCores )
return STATUS_INVALID_PARAMETER;
PKAFFINITY_EX ProcAffinityPool = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KAFFINITY_EX ), PROC_AFFINITY_POOL );
if ( !ProcAffinityPool )
return STATUS_ABANDONED;
nmi_pools.stack_frames = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL );
if ( !nmi_pools.stack_frames )
{
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
nmi_pools.thread_data_pool = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * sizeof( NMI_CALLBACK_DATA ), THREAD_DATA_POOL );
if ( !nmi_pools.thread_data_pool )
{
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
LARGE_INTEGER delay = { 0 };
delay.QuadPart -= NMI_DELAY;
for ( ULONG core = 0; core < NumCores; core++ )
{
KeInitializeAffinityEx( ProcAffinityPool );
KeAddProcessorAffinityEx( ProcAffinityPool, core );
DEBUG_LOG( "Sending NMI" );
HalSendNMI( ProcAffinityPool );
/*
* Only a single NMI can be active at any given time, so arbitrarily
* delay execution to allow time for the NMI to be processed
*/
KeDelayExecutionThread( KernelMode, FALSE, &delay );
}
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
return STATUS_SUCCESS;
}
NTSTATUS HandleNmiIOCTL(
_In_ PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
SYSTEM_MODULES system_modules = { 0 };
ULONG num_cores = KeQueryActiveProcessorCountEx( 0 );
/* Fix annoying visual studio linting error */
RtlZeroMemory( &system_modules, sizeof( SYSTEM_MODULES ) );
RtlZeroMemory( &nmi_pools, sizeof( NMI_POOLS ) );
nmi_pools.nmi_context = ExAllocatePool2( POOL_FLAG_NON_PAGED, num_cores * sizeof( NMI_CONTEXT ), NMI_CONTEXT_POOL );
if ( !nmi_pools.nmi_context )
{
DEBUG_ERROR( "nmi_context ExAllocatePool2 failed" );
return STATUS_ABANDONED;
}
/*
* We want to register and unregister our callback each time so it becomes harder
* for people to hook our callback and get up to some funny business
*/
nmi_callback_handle = KeRegisterNmiCallback( NmiCallback, nmi_pools.nmi_context );
if ( !nmi_callback_handle )
{
DEBUG_ERROR( "KeRegisterNmiCallback failed" );
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
return STATUS_ABANDONED;
}
/*
* We query the system modules each time since they can potentially
* change at any time
*/
status = GetSystemModuleInformation( &system_modules );
if ( !NT_SUCCESS( status ) )
{
DEBUG_ERROR( "Error retriving system module information" );
return status;
}
status = LaunchNonMaskableInterrupt( num_cores );
if ( !NT_SUCCESS( status ) )
{
DEBUG_ERROR( "Error running NMI callbacks" );
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
return status;
}
status = AnalyseNmiData( num_cores, &system_modules, Irp );
if ( !NT_SUCCESS( status ) )
DEBUG_ERROR( "Error analysing nmi data" );
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
ExFreePoolWithTag( nmi_pools.thread_data_pool, THREAD_DATA_POOL );
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
KeDeregisterNmiCallback( nmi_callback_handle );
return status;
}

View file

@ -1,41 +0,0 @@
#ifndef NMI_H
#define NMI_H
#include <ntifs.h>
#include <intrin.h>
#define REPORT_NMI_CALLBACK_FAILURE 50
NTSTATUS HandleNmiIOCTL(
_In_ PIRP Irp
);
typedef struct NMI_CALLBACK_FAILURE
{
INT report_code;
INT were_nmis_disabled;
UINT64 kthread_address;
UINT64 invalid_rip;
}NMI_CALLBACK_FAILURE, *PNMI_CALLBACK_FAILURE;
typedef struct _NMI_CONTEXT
{
INT nmi_callbacks_run;
}NMI_CONTEXT, * PNMI_CONTEXT;
typedef struct _NMI_CALLBACK_DATA
{
UINT64 kthread_address;
UINT64 kprocess_address;
UINT64 start_address;
UINT64 stack_limit;
UINT64 stack_base;
uintptr_t stack_frames_offset;
INT num_frames_captured;
UINT64 cr3;
}NMI_CALLBACK_DATA, * PNMI_CALLBACK_DATA;
#endif

View file

@ -16,18 +16,18 @@
#define INDEX_MUTANTS_POOL_TAG 4
#define INDEX_FILE_OBJECTS_POOL_TAG 5
#define INDEX_DRIVERS_POOL_TAG 6
#define INDEX_SYMBOLIC_LINKS_POOL_TAG7
#define INDEX_SYMBOLIC_LINKS_POOL_TAG 7
CHAR EXECUTIVE_OBJECT_POOL_TAGS[ EXECUTIVE_OBJECT_COUNT ][ POOL_TAG_LENGTH ] =
{
"\x50\x72\x6f\x63",
"\x54\x68\x72\x64",
"\x44\x65\x73\x6B",
"\x57\x69\x6E\x64",
"\x4D\x75\x74\x65",
"\x46\x69\x6C\x65",
"\x44\x72\x69\x76",
"\x4C\x69\x6E\x6B"
"\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 */
};
PVOID process_buffer = NULL;
@ -123,7 +123,7 @@ VOID ScanPageForKernelObjectAllocation(
PUINT64 address_list;
ULONG allocation_size;
if ( !PageBase || !PageSize)
if ( !PageBase || !PageSize )
return;
for ( INT offset = 0; offset <= PageSize - POOL_TAG_LENGTH; offset++ )
@ -152,7 +152,6 @@ VOID ScanPageForKernelObjectAllocation(
*
* more: https://www.imf-conference.org/imf2006/23_Schuster-PoolAllocations.pdf
*/
allocation_size = pool_header->BlockSize * CHUNK_SIZE - sizeof( POOL_HEADER );
if ( ( allocation_size == WIN_PROCESS_ALLOCATION_SIZE ||
@ -207,9 +206,6 @@ BOOLEAN IsPhysicalAddressInPhysicalMemoryRange(
UINT64 start_address = 0;
UINT64 end_address = 0;
if ( !PhysicalAddress || !PhysicalMemoryRanges )
return FALSE;
while ( PhysicalMemoryRanges[ page_index ].NumberOfBytes.QuadPart != NULL )
{
start_address = PhysicalMemoryRanges[ page_index ].BaseAddress.QuadPart;
@ -407,10 +403,12 @@ VOID CheckIfProcessAllocationIsInProcessList(
* 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 FindUnlinkedProcesses(
_In_ PIRP Irp
)
{
NTSTATUS status;
PUINT64 allocation_address;
PINVALID_PROCESS_ALLOCATION_REPORT report_buffer = NULL;
EnumerateProcessListWithCallbackFunction(
IncrementProcessCounter
@ -422,8 +420,6 @@ NTSTATUS FindUnlinkedProcesses()
return STATUS_ABANDONED;
}
DEBUG_LOG( "Proc count: %lx", process_count );
process_buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, process_count * 2 * sizeof( UINT64 ), PROCESS_ADDRESS_LIST_TAG );
if ( !process_buffer )
@ -448,11 +444,34 @@ NTSTATUS FindUnlinkedProcesses()
/* report / do some further analysis */
DEBUG_ERROR( "INVALID POOL proc OMGGG" );
report_buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( INVALID_PROCESS_ALLOCATION_REPORT ), INVALID_PROCESS_REPORT_TAG );
if ( !report_buffer )
goto end;
report_buffer->report_code = REPORT_INVALID_PROCESS_ALLOCATION;
RtlCopyMemory(
report_buffer->process,
allocation_address[i],
REPORT_INVALID_PROCESS_BUFFER_SIZE );
Irp->IoStatus.Information = sizeof( INVALID_PROCESS_ALLOCATION_REPORT );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
report_buffer,
sizeof( INVALID_PROCESS_ALLOCATION_REPORT ) );
}
DEBUG_LOG( "Finished pool memory xd" );
end:
ExFreePoolWithTag( process_buffer, PROCESS_ADDRESS_LIST_TAG );
if (report_buffer )
ExFreePoolWithTag( report_buffer, INVALID_PROCESS_REPORT_TAG );
if (process_buffer )
ExFreePoolWithTag( process_buffer, PROCESS_ADDRESS_LIST_TAG );
process_count = NULL;
process_buffer = NULL;

View file

@ -3,9 +3,14 @@
#include <ntifs.h>
#define REPORT_INVALID_PROCESS_ALLOCATION 80
#define REPORT_INVALID_PROCESS_BUFFER_SIZE 4096
#define POOL_DUMP_BLOCK_TAG 'dump'
#define POOL_DEBUGGER_DATA_TAG 'data'
#define PROCESS_ADDRESS_LIST_TAG 'addr'
#define ANALYSE_PROCESS_TAG 'anls'
#define INVALID_PROCESS_REPORT_TAG 'invd'
#define PML4_ENTRY_COUNT 512
#define PDPT_ENTRY_COUNT 512
@ -18,6 +23,7 @@
#define PROCESS_OBJECT_ALLOCATION_MARGIN 0x90
#define EPROCESS_VIRTUAL_SIZE_OFFSET 0x498
#define EPROCESS_IMAGE_NAME_OFFSET 0x5a8
/* SIZE_2 = first alloc + 0x10 */
#define WIN_PROCESS_ALLOCATION_SIZE 0xcf0
@ -25,7 +31,16 @@
#define CHUNK_SIZE 16
NTSTATUS FindUnlinkedProcesses();
typedef struct _INVALID_PROCESS_ALLOCATION_REPORT
{
INT report_code;
CHAR process[ REPORT_INVALID_PROCESS_BUFFER_SIZE ];
}INVALID_PROCESS_ALLOCATION_REPORT, *PINVALID_PROCESS_ALLOCATION_REPORT;
NTSTATUS FindUnlinkedProcesses(
_In_ PIRP Irp
);
/* creds: https://www.unknowncheats.me/forum/2602838-post2.html */

View file

@ -7,7 +7,7 @@
#include "pipe.h"
#include <TlHelp32.h>
#define REPORT_BUFFER_SIZE 1024
#define REPORT_BUFFER_SIZE 8192
#define SEND_BUFFER_SIZE 8192
#define MAX_SIGNATURE_SIZE 256
@ -21,6 +21,7 @@
#define REPORT_NMI_CALLBACK_FAILURE 50
#define REPORT_MODULE_VALIDATION_FAILURE 60
#define REPORT_ILLEGAL_HANDLE_OPERATION 70
#define REPORT_INVALID_PROCESS_ALLOCATION 80
enum REPORT_CODES
{
@ -146,6 +147,12 @@ namespace global
LONG desired_access;
CHAR process_name[ 64 ];
};
struct INVALID_PROCESS_ALLOCATION_REPORT
{
INT report_code;
CHAR process[ 4096 ];
};
}
}

View file

@ -343,6 +343,32 @@ end:
free( buffer );
}
VOID kernelmode::Driver::ScanForUnlinkedProcess()
{
BOOLEAN status;
DWORD bytes_returned;
global::report_structures::INVALID_PROCESS_ALLOCATION_REPORT report;
status = DeviceIoControl(
this->driver_handle,
IOCTL_SCAN_FOR_UNLINKED_PROCESS,
NULL,
NULL,
&report,
sizeof(report),
&bytes_returned,
NULL
);
if ( status == NULL || bytes_returned == NULL)
{
LOG_ERROR( "failed to scan for unlinked processes %x", GetLastError() );
return;
}
this->report_interface->ServerSend( &report, bytes_returned, SERVER_SEND_MODULE_INTEGRITY_CHECK );
}
ULONG kernelmode::Driver::RequestTotalModuleSize()
{
BOOLEAN status;

View file

@ -15,6 +15,7 @@
#define IOCTL_RETRIEVE_MODULE_EXECUTABLE_REGIONS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2008, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_REQUEST_TOTAL_MODULE_SIZE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2009, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_NOTIFY_DRIVER_ON_PROCESS_TERMINATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2010, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SCAN_FOR_UNLINKED_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2011, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define MAX_HANDLE_REPORTS_PER_IRP 10
@ -44,6 +45,7 @@ namespace kernelmode
VOID ValidateKPRCBThreads();
VOID CheckHandleTableEntries();
VOID RequestModuleExecutableRegions();
VOID ScanForUnlinkedProcess();
};
struct DRIVER_INITIATION_INFORMATION