diff --git a/driver/asm.asm b/driver/asm.asm index 0e57177..5d3f581 100644 --- a/driver/asm.asm +++ b/driver/asm.asm @@ -16,7 +16,7 @@ TestINVDEmulation PROC pushfq cli push 1 ; push some dummy data onto the stack which will exist in writeback memory - wbinvd ; flush the internal cpu caches and write back all modified caches + wbinvd ; flush the internal cpu caches and write back all modified cache ; lines to main memory mov byte ptr [rsp], 0 ; set our dummy value to 0, this takes place inside writeback memory invd ; flush the internal caches, however this instruction will not write diff --git a/driver/callbacks.c b/driver/callbacks.c index a2710c3..dbb9aaf 100644 --- a/driver/callbacks.c +++ b/driver/callbacks.c @@ -17,6 +17,8 @@ QUEUE_HEAD head = { 0 }; */ KGUARDED_MUTEX mutex; +UNICODE_STRING OBJECT_TYPE_PROCESS = RTL_CONSTANT_STRING( L"Process" ); + VOID InitCallbackReportQueue( _In_ PBOOLEAN Status ) @@ -129,23 +131,18 @@ OB_PREOP_CALLBACK_STATUS ObPreOpCallbackRoutine( */ PEPROCESS process_creator = PsGetCurrentProcess(); PEPROCESS target_process = ( PEPROCESS )OperationInformation->Object; - LONG target_process_id = PsGetProcessId( target_process ); LONG process_creator_id = PsGetProcessId( process_creator ); - LONG protected_process_id; LONG parent_process_id; + LPCSTR process_creator_name; + LPCSTR target_process_name; GetProtectedProcessId( &protected_process_id ); GetProtectedProcessParentId( &parent_process_id ); - LPCSTR process_creator_name = PsGetProcessImageFileName( process_creator ); - LPCSTR target_process_name = PsGetProcessImageFileName( target_process ); - - /* - * NOTE for whatever fukin reason this shit prevent notepad rfom launching need - * 2 fix lol - */ + process_creator_name = PsGetProcessImageFileName( process_creator ); + target_process_name = PsGetProcessImageFileName( target_process ); if ( !strcmp( "notepad.exe", target_process_name) ) { @@ -177,11 +174,11 @@ OB_PREOP_CALLBACK_STATUS ObPreOpCallbackRoutine( goto end; report->report_code = REPORT_ILLEGAL_HANDLE_OPERATION; - report->desired_access = OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; + report->access = OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; report->is_kernel_handle = OperationInformation->KernelHandle; report->process_id = process_creator_id; report->thread_id = PsGetCurrentThreadId(); - memcpy( report->process_name, process_creator_name, HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH ); + RtlCopyMemory( report->process_name, process_creator_name, HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH ); InsertReportToQueue( report ); } @@ -202,6 +199,7 @@ VOID ProcessCreateNotifyRoutine( PEPROCESS parent_process; PEPROCESS target_process; LONG parent_process_id; + LONG target_process_id; LPCSTR target_process_name = NULL; LPCSTR parent_process_name = NULL; @@ -227,8 +225,244 @@ VOID ProcessCreateNotifyRoutine( if ( !strcmp( target_process_name, "notepad.exe") ) { - parent_process_id = PsGetProcessId( target_process ); - UpdateProtectedProcessId( parent_process_id ); + parent_process_id = PsGetProcessId( parent_process ); + UpdateProtectedProcessParentId( parent_process_id ); + + target_process_id = PsGetProcessId( target_process ); + UpdateProtectedProcessId( target_process_id ); + DEBUG_LOG( "Protected process parent proc id: %lx", parent_process_id ); } +} + +/* stolen from ReactOS xD */ +VOID NTAPI ExUnlockHandleTableEntry( + IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY HandleTableEntry +) +{ + LONG_PTR old_value; + PAGED_CODE(); + + /* Set the lock bit and make sure it wasn't earlier */ + old_value = InterlockedOr( ( PLONG )&HandleTableEntry->VolatileLowValue, 1 ); + + /* Unblock any waiters */ + ExfUnblockPushLock( &HandleTable->HandleContentionEvent, NULL ); +} + +BOOLEAN EnumHandleCallback( + _In_ PHANDLE_TABLE HandleTable, + _In_ PHANDLE_TABLE_ENTRY Entry, + _In_ HANDLE Handle, + _In_ PVOID Context +) +{ + PVOID object; + PVOID object_header; + POBJECT_TYPE object_type; + PEPROCESS process; + PEPROCESS protected_process; + LPCSTR process_name; + LPCSTR protected_process_name; + LONG protected_process_id; + ACCESS_MASK handle_access_mask; + + object_header = GET_OBJECT_HEADER_FROM_HANDLE( Entry->ObjectPointerBits ); + + /* Object header is the first 30 bytes of the object */ + object = ( uintptr_t )object_header + OBJECT_HEADER_SIZE; + + object_type = ObGetObjectType( object ); + + /* TODO: check for threads aswell */ + if ( !RtlCompareUnicodeString( &object_type->Name, &OBJECT_TYPE_PROCESS, TRUE ) ) + { + process = ( PEPROCESS )object; + process_name = PsGetProcessImageFileName( process ); + + GetProtectedProcessId( &protected_process_id ); + PsLookupProcessByProcessId( protected_process_id, &protected_process ); + protected_process_name = PsGetProcessImageFileName( protected_process ); + + if ( strcmp( process_name, protected_process_name ) ) + goto end; + + DEBUG_LOG( "Handle references our protected process with access mask: %lx", ( ACCESS_MASK )Entry->GrantedAccessBits ); + + handle_access_mask = ( ACCESS_MASK )Entry->GrantedAccessBits; + + /* These permissions can be stripped from every process including CSRSS and LSASS */ + if ( handle_access_mask & PROCESS_CREATE_PROCESS ) + { + Entry->GrantedAccessBits &= ~PROCESS_CREATE_PROCESS; + DEBUG_LOG( "Stripped PROCESS_CREATE_PROCESS" ); + } + + if ( handle_access_mask & PROCESS_CREATE_THREAD ) + { + Entry->GrantedAccessBits &= ~PROCESS_CREATE_THREAD; + DEBUG_LOG( "Stripped PROCESS_CREATE_THREAD" ); + } + + if ( handle_access_mask & PROCESS_DUP_HANDLE ) + { + Entry->GrantedAccessBits &= ~PROCESS_DUP_HANDLE; + DEBUG_LOG( "Stripped PROCESS_DUP_HANDLE" ); + } + + if ( handle_access_mask & PROCESS_QUERY_INFORMATION ) + { + Entry->GrantedAccessBits &= ~PROCESS_QUERY_INFORMATION; + DEBUG_LOG( "Stripped PROCESS_QUERY_INFORMATION" ); + } + + if ( handle_access_mask & PROCESS_QUERY_LIMITED_INFORMATION ) + { + Entry->GrantedAccessBits &= ~PROCESS_QUERY_LIMITED_INFORMATION; + DEBUG_LOG( "Stripped PROCESS_QUERY_LIMITED_INFORMATION" ); + } + + if ( handle_access_mask & PROCESS_VM_READ ) + { + Entry->GrantedAccessBits &= ~PROCESS_VM_READ; + DEBUG_LOG( "Stripped PROCESS_VM_READ" ); + } + + if ( !strcmp( process_name, "csrss.exe" ) || !strcmp( process_name, "lsass.exe" ) ) + { + DEBUG_LOG( "Required system process allowed, only stripping some permissions" ); + goto end; + } + + /* Permissions beyond here can only be stripped from non critical processes */ + if ( handle_access_mask & PROCESS_SET_INFORMATION ) + { + Entry->GrantedAccessBits &= ~PROCESS_SET_INFORMATION; + DEBUG_LOG( "Stripped PROCESS_SET_INFORMATION" ); + } + + if ( handle_access_mask & PROCESS_SET_QUOTA ) + { + Entry->GrantedAccessBits &= ~PROCESS_SET_QUOTA; + DEBUG_LOG( "Stripped PROCESS_SET_QUOTA" ); + } + + if ( handle_access_mask & PROCESS_SUSPEND_RESUME ) + { + Entry->GrantedAccessBits &= ~PROCESS_SUSPEND_RESUME; + DEBUG_LOG( "Stripped PROCESS_SUSPEND_RESUME " ); + } + + if ( handle_access_mask & PROCESS_TERMINATE ) + { + Entry->GrantedAccessBits &= ~PROCESS_TERMINATE; + DEBUG_LOG( "Stripped PROCESS_TERMINATE" ); + } + + if ( handle_access_mask & PROCESS_VM_OPERATION ) + { + Entry->GrantedAccessBits &= ~PROCESS_VM_OPERATION; + DEBUG_LOG( "Stripped PROCESS_VM_OPERATION" ); + } + + if ( handle_access_mask & PROCESS_VM_WRITE ) + { + Entry->GrantedAccessBits &= ~PROCESS_VM_WRITE; + DEBUG_LOG( "Stripped PROCESS_VM_WRITE" ); + } + + POPEN_HANDLE_FAILURE_REPORT report = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( OPEN_HANDLE_FAILURE_REPORT ), REPORT_POOL_TAG ); + + if ( !report ) + goto end; + + /* + * Using the same report structure as the ObRegisterCallbacks report + * since both of these reports are closely related by the fact they are + * triggered by a process either opening a handle to our protected process + * or have a valid open handle to it. I also don't think its worth creating + * another queue specifically for open handle reports since they will be + * rare. + */ + report->report_code = REPORT_ILLEGAL_HANDLE_OPERATION; + report->is_kernel_handle = NULL; + report->process_id = PsGetProcessId( process ); + report->thread_id = NULL; + report->access = handle_access_mask; + RtlCopyMemory( report->process_name, protected_process_name, HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH ); + + InsertReportToQueue( report ); + } + +end: + ExUnlockHandleTableEntry( HandleTable, Entry ); + return FALSE; +} + +NTSTATUS EnumerateProcessHandles( + _In_ PEPROCESS Process +) +{ + /* Handles are paged out so we need to be at an IRQL that allows paging */ + PAGED_CODE(); + + if ( !Process ) + return STATUS_INVALID_PARAMETER; + + //if ( Process == PsInitialSystemProcess ) + // return STATUS_SUCCESS; + + PHANDLE_TABLE handle_table = *( PHANDLE_TABLE* )( ( uintptr_t )Process + EPROCESS_HANDLE_TABLE_OFFSET ); + + if ( !handle_table ) + return STATUS_ABANDONED; + + if ( !MmIsAddressValid( handle_table ) ) + return STATUS_ABANDONED; + +#pragma warning(push) +#pragma warning(suppress : 6387) + + BOOLEAN result = ExEnumHandleTable( + handle_table, + EnumHandleCallback, + NULL, + NULL + ); + +#pragma warning(pop) + + return STATUS_SUCCESS; +} + +/* +* I dont think this way of enumerating processes is valid for something like an anti +* cheat which is mass deployed and needs to ensure that it won't crash the system. +* Since we have no access to the process structure locks it is definitely not +* mass deployment safe lol. +*/ +VOID EnumerateProcessListWithCallbackFunction( + _In_ PVOID Function +) +{ + if ( !Function ) + return; + + PEPROCESS base_process = PsInitialSystemProcess; + + if ( !base_process ) + return; + + PEPROCESS current_process = base_process; + + do + { + VOID( *callback_function_ptr )( PEPROCESS ) = Function; + ( *callback_function_ptr )( current_process ); + + PLIST_ENTRY list = ( PLIST_ENTRY )( ( uintptr_t )current_process + EPROCESS_PLIST_ENTRY_OFFSET ); + current_process = ( PEPROCESS )( ( uintptr_t )list->Flink - EPROCESS_PLIST_ENTRY_OFFSET ); + + } while ( current_process != base_process || !current_process ); } \ No newline at end of file diff --git a/driver/callbacks.h b/driver/callbacks.h index 14ba8ec..a944193 100644 --- a/driver/callbacks.h +++ b/driver/callbacks.h @@ -25,7 +25,7 @@ typedef struct _OPEN_HANDLE_FAILURE_REPORT INT is_kernel_handle; LONG process_id; LONG thread_id; - LONG desired_access; + LONG access; CHAR process_name[ HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH ]; }OPEN_HANDLE_FAILURE_REPORT, *POPEN_HANDLE_FAILURE_REPORT; @@ -45,6 +45,14 @@ typedef struct _OPEN_HANDLE_FAILURE_REPORT #define PROCESS_VM_READ 0x0010 #define PROCESS_VM_WRITE 0x0020 +//https://www.sysnative.com/forums/threads/object-headers-handles-and-types.34987/ +#define GET_OBJECT_HEADER_FROM_HANDLE(x) ((x << 4) | 0xffff000000000000) + +static const uintptr_t EPROCESS_IMAGE_FILE_NAME_OFFSET = 0x5a8; +static const uintptr_t EPROCESS_HANDLE_TABLE_OFFSET = 0x570; +static const uintptr_t OBJECT_HEADER_SIZE = 0x30; +static const uintptr_t EPROCESS_PLIST_ENTRY_OFFSET = 0x448; + VOID ObPostOpCallbackRoutine( _In_ PVOID RegistrationContext, _In_ POB_POST_OPERATION_INFORMATION OperationInformation @@ -71,4 +79,12 @@ VOID ProcessCreateNotifyRoutine( VOID FreeQueueObjectsAndCleanup(); +VOID EnumerateProcessListWithCallbackFunction( + _In_ PVOID Function +); + +NTSTATUS EnumerateProcessHandles( + _In_ PEPROCESS Process +); + #endif diff --git a/driver/driver.c b/driver/driver.c index 39cedd4..3400339 100644 --- a/driver/driver.c +++ b/driver/driver.c @@ -11,6 +11,7 @@ PVOID callback_registration_handle; LONG protected_process_id; LONG protected_process_parent_id; + KGUARDED_MUTEX mutex; UNICODE_STRING DEVICE_NAME = RTL_CONSTANT_STRING( L"\\Device\\DonnaAC" ); diff --git a/driver/hv.c b/driver/hv.c index 25920bf..0465922 100644 --- a/driver/hv.c +++ b/driver/hv.c @@ -13,7 +13,7 @@ * to an instruction such as FYL2XP1 (source: secret.club) which has an average * execution time slightly higher then the CPUID instruction then compare the two. * If the average time for the CPUID instruction is higher then the average time -* then the FYL2XP1 instruction it is a dead giveaway we are running on a +* for the FYL2XP1 instruction it is a dead giveaway we are running on a * virtualized system. * * source: https://secret.club/2020/01/12/battleye-hypervisor-detection.html @@ -34,7 +34,7 @@ INT APERFMsrTimingCheck() old_affinity = KeSetSystemAffinityThreadEx( new_affinity ); /* - * Once we've locked our thread to the current core, we saved the old irql + * Once we've locked our thread to the current core, we save the old irql * and raise to HIGH_LEVEL to ensure the chance our thread is preempted * by a thread with a higher IRQL is extremely low. */ diff --git a/driver/ioctl.c b/driver/ioctl.c index 0212c06..da5c16a 100644 --- a/driver/ioctl.c +++ b/driver/ioctl.c @@ -114,6 +114,15 @@ NTSTATUS DeviceControl( break; + case IOCTL_ENUMERATE_HANDLE_TABLES: + + /* can maybe implement this better so we can extract a status value */ + EnumerateProcessListWithCallbackFunction( + EnumerateProcessHandles + ); + + break; + default: DEBUG_ERROR( "Invalid IOCTL passed to driver" ); break; diff --git a/driver/ioctl.h b/driver/ioctl.h index a0ac205..8522f1d 100644 --- a/driver/ioctl.h +++ b/driver/ioctl.h @@ -12,6 +12,7 @@ #define IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2004, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2005, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_PERFORM_VIRTUALIZATION_CHECK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2006, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_ENUMERATE_HANDLE_TABLES CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2007, METHOD_BUFFERED, FILE_ANY_ACCESS) typedef struct _DRIVER_INITIATION_INFORMATION { diff --git a/driver/queue.c b/driver/queue.c index 512ac2d..0c5dc83 100644 --- a/driver/queue.c +++ b/driver/queue.c @@ -2,7 +2,7 @@ //PQUEUE_HEAD QueueCreate() //{ -// PQUEUE_HEAD head = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( PQUEUE_HEAD ), QUEUE_POOL_TAG ); +// PQUEUE_HEAD head = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( QUEUE_HEAD ), QUEUE_POOL_TAG ); // // if ( !head ) // return NULL; diff --git a/user/client.cpp b/user/client.cpp index 1763c4b..792ead9 100644 --- a/user/client.cpp +++ b/user/client.cpp @@ -22,9 +22,9 @@ global::Client::Client( LPTSTR PipeName ) } /* test the write function */ - global::headers::PIPE_PACKET_HEADER header; - header.message_type = REQUEST_PATTERNS_TO_BE_SCANNED; - this->WriteToPipe( &header, sizeof( global::headers::PIPE_PACKET_HEADER ) ); + //global::headers::PIPE_PACKET_HEADER header; + //header.message_type = REQUEST_PATTERNS_TO_BE_SCANNED; + //this->WriteToPipe( &header, sizeof( global::headers::PIPE_PACKET_HEADER ) ); } void global::Client::WriteToPipe( PVOID Buffer, SIZE_T Size ) diff --git a/user/km/driver.cpp b/user/km/driver.cpp index 74851fe..b845740 100644 --- a/user/km/driver.cpp +++ b/user/km/driver.cpp @@ -259,10 +259,37 @@ void kernelmode::Driver::DetectSystemVirtualization() /* shutdown the application or smth lmao */ } +void kernelmode::Driver::CheckHandleTableEntries() +{ + BOOLEAN status; + DWORD bytes_returned; + + /* + * Only pass the IOCTL code and nothing else since the reports are bundled + * with the handle ObRegisterCallbacks report queue hence the QueryReportQueue + * function will handle these reports. + */ + status = DeviceIoControl( + this->driver_handle, + IOCTL_ENUMERATE_HANDLE_TABLES, + NULL, + NULL, + NULL, + NULL, + &bytes_returned, + NULL + ); + + if ( status == NULL ) + LOG_ERROR( "CheckHandleTableEntries failed with status %x", status ); +} + void kernelmode::Driver::ValidateKPRCBThreads() { + } void kernelmode::Driver::CheckDriverHeartbeat() { + } diff --git a/user/km/driver.h b/user/km/driver.h index 98efa07..1115970 100644 --- a/user/km/driver.h +++ b/user/km/driver.h @@ -12,6 +12,7 @@ #define IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2004, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2005, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_PERFORM_VIRTUALIZATION_CHECK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2006, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_ENUMERATE_HANDLE_TABLES CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2007, METHOD_BUFFERED, FILE_ANY_ACCESS) #define MAX_HANDLE_REPORTS_PER_IRP 10 @@ -36,6 +37,7 @@ namespace kernelmode void DetectSystemVirtualization(); void ValidateKPRCBThreads(); void CheckDriverHeartbeat(); + void CheckHandleTableEntries(); /* todo: driver integrity check */ };