thread validation check

This commit is contained in:
lhodges1 2023-08-30 21:15:57 +10:00
parent 40d8a39aa8
commit 67a8bdf170
9 changed files with 117 additions and 48 deletions

View file

@ -120,6 +120,8 @@ NTSTATUS DriverEntry(
NULL
);
ZwClose( handle );
status = IoCreateDevice(
DriverObject,
NULL,
@ -158,10 +160,10 @@ NTSTATUS DriverEntry(
IoDeleteSymbolicLink( &DEVICE_SYMBOLIC_LINK );
IoDeleteDevice( DriverObject->DeviceObject );
return STATUS_FAILED_DRIVER_ENTRY;
}
}
DEBUG_LOG( "DonnaAC Driver Entry Complete. type: %lx", DriverObject->DeviceObject->DeviceType );
DEBUG_LOG( "DonnaAC Driver Entry Complete" );
return status;
return STATUS_SUCCESS;
}

View file

@ -7,6 +7,7 @@
#include "callbacks.h"
#include "pool.h"
#include "integrity.h"
#include "thread.h"
#include "hv.h"
@ -218,6 +219,12 @@ NTSTATUS DeviceControl(
break;
case IOCTL_VALIDATE_KPRCB_CURRENT_THREAD:
ValidateKPCRBThreads( Irp );
break;
default:
DEBUG_ERROR( "Invalid IOCTL passed to driver" );
break;

View file

@ -15,6 +15,7 @@
#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 IOCTL_VALIDATE_KPRCB_CURRENT_THREAD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2012, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _DRIVER_INITIATION_INFORMATION
{

View file

@ -716,16 +716,16 @@ NTSTATUS LaunchNonMaskableInterrupt(
if ( !NumCores )
return STATUS_INVALID_PARAMETER;
PKAFFINITY_EX ProcAffinityPool = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KAFFINITY_EX ), PROC_AFFINITY_POOL );
PKAFFINITY_EX proc_affinity = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KAFFINITY_EX ), PROC_AFFINITY_POOL );
if ( !ProcAffinityPool )
if ( !proc_affinity )
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 );
ExFreePoolWithTag( proc_affinity, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
@ -734,7 +734,7 @@ NTSTATUS LaunchNonMaskableInterrupt(
if ( !nmi_pools.thread_data_pool )
{
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
ExFreePoolWithTag( proc_affinity, PROC_AFFINITY_POOL );
return STATUS_ABANDONED;
}
@ -743,11 +743,11 @@ NTSTATUS LaunchNonMaskableInterrupt(
for ( ULONG core = 0; core < NumCores; core++ )
{
KeInitializeAffinityEx( ProcAffinityPool );
KeAddProcessorAffinityEx( ProcAffinityPool, core );
KeInitializeAffinityEx( proc_affinity );
KeAddProcessorAffinityEx( proc_affinity, core );
DEBUG_LOG( "Sending NMI" );
HalSendNMI( ProcAffinityPool );
HalSendNMI( proc_affinity );
/*
* Only a single NMI can be active at any given time, so arbitrarily
@ -756,7 +756,7 @@ NTSTATUS LaunchNonMaskableInterrupt(
KeDelayExecutionThread( KernelMode, FALSE, &delay );
}
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
ExFreePoolWithTag( proc_affinity, PROC_AFFINITY_POOL );
return STATUS_SUCCESS;
}

View file

@ -5,34 +5,6 @@
#include <intrin.h>
/*
* How this will work:
*
* 1. The KPCRB (processor control block) contains 3 pointers to 3 threads:
*
* +0x008 CurrentThread : Ptr64 _KTHREAD
* +0x010 NextThread : Ptr64 _KTHREAD
* +0x018 IdleThread : Ptr64 _KTHREAD
*
* 2. These threads are stored in a list that is seperate to the KTHREADs linked list.
* We know this because if you unlink a process, the threads are still scheduled by
* the OS, meaning the OS has a seperate list that it uses to schedule these threads.
*
* 3. Now from here, we can get thread ID and pass it to PsLookupThreadByThreadId which
* takes the thread ID and returns a pointer to the ETHREAD structure. It does this
* by indexing the PspCidTable using the TID we pass in.
*
* What we can potentially observer is that any threads that have been removed from the
* PspCidTable will return a null ptr from PsLookupThreadById. In addition to this, we
* can also check if the KTHREAD address referenced in the KPCRB is not in the KTHREAD
* linked list. Allowing us to find threads removed from both the PspCidTable and the
* KTHREAD linked list.
*/
/*
* IDEA: we can run a thread on each core to maximise the search, so it would be 3 * num procs
*/
UINT8 thread_found_in_pspcidtable = FALSE;
UINT8 thread_found_in_kthreadlist = FALSE;
BOOLEAN finished = FALSE;
@ -76,8 +48,30 @@ VOID ProcessEnumerationCallback(
}
}
NTSTATUS ValidateKPCRBThreads(
//_In_ PIRP Irp
/*
* How this will work:
*
* 1. The KPCRB (processor control block) contains 3 pointers to 3 threads:
*
* +0x008 CurrentThread : Ptr64 _KTHREAD
* +0x010 NextThread : Ptr64 _KTHREAD
* +0x018 IdleThread : Ptr64 _KTHREAD
*
* 2. These threads are stored in a list that is seperate to the KTHREADs linked list.
* We know this because if you unlink a process, the threads are still scheduled by
* the OS, meaning the OS has a seperate list that it uses to schedule these threads.
*
* 3. From here we can firstly check if the KTHREAD is within the KTHREAD linked list,
* if it is we can then use this to check if its in the PspCidTable by passing it
* to PsGetThreadId which returns the thread id by enumerating the PspCidTable and
* finding the corresponding object pointer. If the thread id is not found, we know
* that it's been removed from the PspCidTable, and if the thread is not in any
* process' thread list , we know it's been removed from the KTHREAD linked list.
*
*/
VOID ValidateKPCRBThreads(
_In_ PIRP Irp
)
{
NTSTATUS status;
@ -93,17 +87,30 @@ NTSTATUS ValidateKPCRBThreads(
kprcb = kpcr + KPRCB_OFFSET_FROM_GS_BASE;
current_kpcrb_thread = *( UINT64* )( kprcb + KPCRB_CURRENT_THREAD );
DEBUG_LOG( "Current processor: %lx, current kprcb: %llx, current thread: %llx", KeGetCurrentProcessorNumber(), kprcb, current_kpcrb_thread );
EnumerateProcessListWithCallbackFunction(
ProcessEnumerationCallback
);
DEBUG_LOG( "Thread in psp: %i, thread in list: %i", thread_found_in_pspcidtable, thread_found_in_kthreadlist );
if ( thread_found_in_kthreadlist == FALSE || thread_found_in_pspcidtable == FALSE )
{
Irp->IoStatus.Information = sizeof( HIDDEN_SYSTEM_THREAD_REPORT );
HIDDEN_SYSTEM_THREAD_REPORT report;
report.report_code = REPORT_HIDDEN_SYSTEM_THREAD;
report.found_in_kthreadlist = thread_found_in_kthreadlist;
report.found_in_pspcidtable = thread_found_in_pspcidtable;
report.thread_id = PsGetThreadId( current_kpcrb_thread );
report.thread_address = current_kpcrb_thread;
RtlCopyMemory(
report.thread,
current_kpcrb_thread,
sizeof( report.thread ));
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( HIDDEN_SYSTEM_THREAD_REPORT ) );
}
current_kpcrb_thread = NULL;
@ -113,5 +120,4 @@ NTSTATUS ValidateKPCRBThreads(
KeRevertToUserAffinityThreadEx( old_affinity );
}
}

View file

@ -11,8 +11,21 @@
#define KPROCESS_THREADLIST_OFFSET 0x030
#define KTHREAD_THREADLIST_OFFSET 0x2f8
NTSTATUS ValidateKPCRBThreads(
//_In_ PIRP Irp
#define REPORT_HIDDEN_SYSTEM_THREAD 90
VOID ValidateKPCRBThreads(
_In_ PIRP Irp
);
typedef struct _HIDDEN_SYSTEM_THREAD_REPORT
{
INT report_code;
INT found_in_kthreadlist;
INT found_in_pspcidtable;
UINT64 thread_address;
LONG thread_id;
CHAR thread[ 4096 ];
}HIDDEN_SYSTEM_THREAD_REPORT, *PHIDDEN_SYSTEM_THREAD_REPORT;
#endif

View file

@ -22,6 +22,7 @@
#define REPORT_MODULE_VALIDATION_FAILURE 60
#define REPORT_ILLEGAL_HANDLE_OPERATION 70
#define REPORT_INVALID_PROCESS_ALLOCATION 80
#define REPORT_HIDDEN_SYSTEM_THREAD 90
enum REPORT_CODES
{
@ -153,6 +154,20 @@ namespace global
INT report_code;
CHAR process[ 4096 ];
};
/*
* No point copying data from the start address here
* since people can easily change it.
*/
struct HIDDEN_SYSTEM_THREAD_REPORT
{
INT report_code;
INT found_in_kthreadlist;
INT found_in_pspcidtable;
UINT64 thread_address;
LONG thread_id;
CHAR thread[ 4096 ];
};
}
}

View file

@ -413,7 +413,31 @@ VOID kernelmode::Driver::NotifyDriverOnProcessTermination()
VOID kernelmode::Driver::ValidateKPRCBThreads()
{
BOOLEAN status;
DWORD bytes_returned;
global::report_structures::HIDDEN_SYSTEM_THREAD_REPORT report;
status = DeviceIoControl(
this->driver_handle,
IOCTL_VALIDATE_KPRCB_CURRENT_THREAD,
NULL,
NULL,
&report,
sizeof( report ),
&bytes_returned,
NULL
);
if ( status == NULL)
{
LOG_ERROR( "failed to validate kpcrb threads with status %x", GetLastError() );
return;
}
if ( bytes_returned == NULL )
return;
this->report_interface->ServerSend( &report, bytes_returned, SERVER_SEND_MODULE_INTEGRITY_CHECK );
}
VOID kernelmode::Driver::CheckDriverHeartbeat()

View file

@ -16,6 +16,7 @@
#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 IOCTL_VALIDATE_KPRCB_CURRENT_THREAD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2012, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define MAX_HANDLE_REPORTS_PER_IRP 10