AAAAAAAAHHHHHHH

This commit is contained in:
lhodges1 2023-08-22 01:48:34 +10:00
parent 37f204540a
commit 7e20e7480e
14 changed files with 206 additions and 33 deletions

64
driver/asm.asm Normal file
View file

@ -0,0 +1,64 @@
.code
; Tests the emulation of the INVD instruction
;
; source and references:
;
; https://secret.club/2020/04/13/how-anti-cheats-detect-system-emulation.html#invdwbinvd
; https://www.felixcloutier.com/x86/invd
; https://www.felixcloutier.com/x86/wbinvd
;
; Returns int
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
; 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
; back to system memory as opposed to wbinvd, meaning our previous
; instruction which only operated on cached writeback data and not
; system memory has been invalidated.
pop rax ; on a real system as a result of our data update instruction being
; invalidated, the result will be 1. On a system that does not
; properly implement INVD, the result will be 0 as the instruction does
; not properly flush the caches.
xor rax, 1 ; invert result so function returns same way as all verification methods
popfq
ret
TestINVDEmulation ENDP
;
; Note: fild and fistp respectively are used for loading and storing integers in the FPU,
; while fld and fstp are used for floating point numbers. No need to use xmm registers
; as we dont need that level of precision and we need to be as efficient as possible
;
; compiler will take care of saving the SSE state for us and restoring it source:
; https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/using-floating-point-or-mmx-in-a-wdm-driver
;
; arguments: INT64 in RCX
; returns resulting number lol
MySqrt PROC
push rbp
mov rbp, rsp
sub rsp, 16
mov [rsp + 8], rcx ; cannot directly move from a register into a fp register
fild qword ptr[rsp + 8] ; push our number onto the FPU stack
fsqrt ; perform the square root
fistp qword ptr[rsp] ; pop the value from the floating point stack into our general purpose stack
mov rax, qword ptr[rsp] ; store value in rax for return
add rsp, 16
pop rbp
ret
MySqrt ENDP
END

View file

@ -64,6 +64,7 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets"> <ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@ -143,7 +144,11 @@
<ClInclude Include="nmi.h" /> <ClInclude Include="nmi.h" />
<ClInclude Include="queue.h" /> <ClInclude Include="queue.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="asm.asm" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>

View file

@ -72,4 +72,9 @@
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="asm.asm">
<Filter>Source Files</Filter>
</MASM>
</ItemGroup>
</Project> </Project>

View file

@ -9,35 +9,92 @@
#define IA32_APERF_MSR 0x000000E8 #define IA32_APERF_MSR 0x000000E8
/* /*
* 1. Bind thread to a single core * TODO: Perform the test in a loop and average the delta out, then compare it
* 2. Raise the IRQL to HIGH_LEVEL * to an instruction such as FYL2XP1 (source: secret.club) which has an average
* 3. disable interrupts * 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
* virtualized system.
*
* source: https://secret.club/2020/01/12/battleye-hypervisor-detection.html
*/ */
VOID APERFMsrTimingCheck()
INT APERFMsrTimingCheck()
{ {
KAFFINITY new_affinity = { 0 };
KAFFINITY old_affinity = { 0 };
ULONG64 old_irql; ULONG64 old_irql;
INT cpuid_result[ 4 ]; INT cpuid_result[ 4 ];
old_irql = __readcr8(); /*
* First thing we do is we lock the current thread to the logical processor
* its executing on.
*/
new_affinity = ( KAFFINITY )( 1 << KeGetCurrentProcessorNumber() );
old_affinity = KeSetSystemAffinityThreadEx( new_affinity );
/*
* Once we've locked our thread to the current core, we saved 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.
*/
old_irql = __readcr8();
__writecr8( HIGH_LEVEL ); __writecr8( HIGH_LEVEL );
/*
* Then we also disable interrupts, once again making sure our thread
* is not preempted.
*/
_disable(); _disable();
/*
* Once our thread is ready for the test, we read the APERF from the
* MSR register and store it. We then execute a CPUID instruction
* which we don't really care about and immediately after read the APERF
* counter once again and store it in a seperate variable.
*/
UINT64 aperf_before = __readmsr( IA32_APERF_MSR ) << 32; UINT64 aperf_before = __readmsr( IA32_APERF_MSR ) << 32;
__cpuid( cpuid_result, 1 ); __cpuid( cpuid_result, 1 );
UINT64 aperf_after = __readmsr( IA32_APERF_MSR ) << 32; UINT64 aperf_after = __readmsr( IA32_APERF_MSR ) << 32;
/*
* Once we have performed our test, we want to make sure we are not
* hogging the cpu time from other threads, so we reverse the initial
* preparation process. i.e we first enable interrupts, lower our irql
* to the threads previous irql before it was raised and then restore the
* threads affinity back to its original affinity.
*/
_enable(); _enable();
__writecr8( old_irql ); __writecr8( old_irql );
KeRevertToUserAffinityThreadEx( old_affinity );
/*
* Now the only thing left to do is calculate the change. Now, on some VMs
* such as VMWARE the aperf value will be 0, meaning the change will be 0.
* This is a dead giveaway we are executing in a VM.
*/
UINT64 aperf_delta = aperf_after - aperf_before; UINT64 aperf_delta = aperf_after - aperf_before;
_enable(); return aperf_delta == 0 ? TRUE : FALSE;
}
DEBUG_LOG( "delta: %llx", aperf_delta ); NTSTATUS PerformVirtualizationDetection(
_In_ PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
HYPERVISOR_DETECTION_REPORT report;
report.aperf_msr_timing_check = APERFMsrTimingCheck();
report.invd_emulation_check = TestINVDEmulation();
Irp->IoStatus.Information = sizeof( HYPERVISOR_DETECTION_REPORT );
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&report,
sizeof( HYPERVISOR_DETECTION_REPORT )
);
return status;
} }

View file

@ -3,6 +3,17 @@
#include <ntifs.h> #include <ntifs.h>
VOID APERFMsrTimingCheck(); typedef struct _HYPERVISOR_DETECTION_REPORT
{
INT aperf_msr_timing_check;
INT invd_emulation_check;
}HYPERVISOR_DETECTION_REPORT, *PHYPERVISOR_DETECTION_REPORT;
NTSTATUS PerformVirtualizationDetection(
_In_ PIRP Irp
);
extern INT TestINVDEmulation();
#endif #endif

View file

@ -98,8 +98,6 @@ NTSTATUS DeviceControl(
case IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE: case IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE:
APERFMsrTimingCheck();
status = HandlePeriodicCallbackReportQueue(Irp); status = HandlePeriodicCallbackReportQueue(Irp);
if ( !NT_SUCCESS( status ) ) if ( !NT_SUCCESS( status ) )
@ -107,6 +105,15 @@ NTSTATUS DeviceControl(
break; break;
case IOCTL_PERFORM_VIRTUALIZATION_CHECK:
status = PerformVirtualizationDetection( Irp );
if ( !NT_SUCCESS( status ) )
DEBUG_ERROR( "PerformVirtualizationDetection failed with status %x", status );
break;
default: default:
DEBUG_ERROR( "Invalid IOCTL passed to driver" ); DEBUG_ERROR( "Invalid IOCTL passed to driver" );
break; break;

View file

@ -11,6 +11,7 @@
#define IOCTL_VALIDATE_DRIVER_OBJECTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2002, 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) #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_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)
typedef struct _DRIVER_INITIATION_INFORMATION typedef struct _DRIVER_INITIATION_INFORMATION
{ {

View file

@ -5,7 +5,7 @@
#include <intrin.h> #include <intrin.h>
#define REPORT_MODULE_VALIDATION_FAILURE 60 #define REPORT_MODULE_VALIDATION_FAILURE 60
#define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 5 #define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 20
#define MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE 128 #define MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE 128

View file

@ -192,8 +192,6 @@ void kernelmode::Driver::QueryReportQueue()
( UINT64 )buffer + sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT_HEADER ) + ( UINT64 )buffer + sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT_HEADER ) +
i * sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT ) ); i * sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT ) );
std::cout << report->process_id << " " << report->process_name << std::endl;
this->report_interface->ReportViolation( report ); this->report_interface->ReportViolation( report );
} }
@ -232,21 +230,33 @@ void kernelmode::Driver::NotifyDriverOnProcessLaunch()
LOG_ERROR( "DeviceIoControl failed with status code 0x%x", GetLastError() ); LOG_ERROR( "DeviceIoControl failed with status code 0x%x", GetLastError() );
} }
void kernelmode::Driver::CompleteQueuedCallbackReports() void kernelmode::Driver::DetectSystemVirtualization()
{ {
BOOLEAN status;
HYPERVISOR_DETECTION_REPORT report;
DWORD bytes_returned;
} status = DeviceIoControl(
this->driver_handle,
IOCTL_PERFORM_VIRTUALIZATION_CHECK,
NULL,
NULL,
&report,
sizeof( HYPERVISOR_DETECTION_REPORT ),
&bytes_returned,
NULL
);
void kernelmode::Driver::EnableProcessLoadNotifyCallbacks() if ( status == NULL )
{ {
/* LOG_ERROR( "DeviceIoControl failed virtualization detect with status %x", GetLastError() );
* note: no need for these since when the dll is loaded it will simply return;
* notify the driver. }
*/
}
void kernelmode::Driver::DisableProcessLoadNotifyCallbacks() if ( report.aperf_msr_timing_check == TRUE || report.invd_emulation_check == TRUE )
{ LOG_INFO( "HYPERVISOR DETECTED!!!" );
/* shutdown the application or smth lmao */
} }
void kernelmode::Driver::ValidateKPRCBThreads() void kernelmode::Driver::ValidateKPRCBThreads()

View file

@ -11,6 +11,7 @@
#define IOCTL_MONITOR_CALLBACKS_FOR_REPORTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2003, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_MONITOR_CALLBACKS_FOR_REPORTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2003, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2004, METHOD_BUFFERED, FILE_ANY_ACCESS) #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_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 MAX_HANDLE_REPORTS_PER_IRP 10 #define MAX_HANDLE_REPORTS_PER_IRP 10
@ -23,6 +24,7 @@ namespace kernelmode
std::shared_ptr<global::Report> report_interface; std::shared_ptr<global::Report> report_interface;
void QueryReportQueue(); void QueryReportQueue();
public: public:
Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface ); Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface );
@ -31,9 +33,7 @@ namespace kernelmode
void VerifySystemModules(); void VerifySystemModules();
void RunCallbackReportQueue(); void RunCallbackReportQueue();
void NotifyDriverOnProcessLaunch(); void NotifyDriverOnProcessLaunch();
void CompleteQueuedCallbackReports(); void DetectSystemVirtualization();
void EnableProcessLoadNotifyCallbacks();
void DisableProcessLoadNotifyCallbacks();
void ValidateKPRCBThreads(); void ValidateKPRCBThreads();
void CheckDriverHeartbeat(); void CheckDriverHeartbeat();
/* todo: driver integrity check */ /* todo: driver integrity check */
@ -43,6 +43,12 @@ namespace kernelmode
{ {
LONG protected_process_id; LONG protected_process_id;
}; };
struct HYPERVISOR_DETECTION_REPORT
{
INT aperf_msr_timing_check;
INT invd_emulation_check;
};
} }
#endif #endif

View file

@ -20,3 +20,8 @@ void kernelmode::KManager::MonitorCallbackReports()
{ {
this->thread_pool->QueueJob( [ this ]() { this->driver_interface->RunCallbackReportQueue(); } ); this->thread_pool->QueueJob( [ this ]() { this->driver_interface->RunCallbackReportQueue(); } );
} }
void kernelmode::KManager::DetectSystemVirtualization()
{
this->thread_pool->QueueJob( [ this ]() { this->driver_interface->DetectSystemVirtualization(); } );
}

View file

@ -20,6 +20,7 @@ namespace kernelmode
void RunNmiCallbacks(); void RunNmiCallbacks();
void VerifySystemModules(); void VerifySystemModules();
void MonitorCallbackReports(); void MonitorCallbackReports();
void DetectSystemVirtualization();
}; };
} }

View file

@ -28,9 +28,10 @@ DWORD WINAPI Init(HINSTANCE hinstDLL)
usermode::UManager umanager( thread_pool, report_interface ); usermode::UManager umanager( thread_pool, report_interface );
kernelmode::KManager kmanager( driver_name, thread_pool, report_interface); kernelmode::KManager kmanager( driver_name, thread_pool, report_interface);
kmanager.MonitorCallbackReports(); //kmanager.MonitorCallbackReports();
//kmanager.RunNmiCallbacks(); //kmanager.RunNmiCallbacks();
kmanager.VerifySystemModules(); //kmanager.VerifySystemModules();
kmanager.DetectSystemVirtualization();
//umanager.ValidateProcessModules(); //umanager.ValidateProcessModules();
//umanager.ValidateProcessMemory(); //umanager.ValidateProcessMemory();

View file

@ -9,7 +9,7 @@
#define REPORT_BUFFER_SIZE 1024 #define REPORT_BUFFER_SIZE 1024
#define MAX_SIGNATURE_SIZE 256 #define MAX_SIGNATURE_SIZE 256
#define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 5 #define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 20
#define REPORT_CODE_MODULE_VERIFICATION 10 #define REPORT_CODE_MODULE_VERIFICATION 10
#define REPORT_CODE_START_ADDRESS_VERIFICATION 20 #define REPORT_CODE_START_ADDRESS_VERIFICATION 20