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>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets">
<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="queue.h" />
</ItemGroup>
<ItemGroup>
<MASM Include="asm.asm" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>

View file

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

View file

@ -9,35 +9,92 @@
#define IA32_APERF_MSR 0x000000E8
/*
* 1. Bind thread to a single core
* 2. Raise the IRQL to HIGH_LEVEL
* 3. disable interrupts
* TODO: Perform the test in a loop and average the delta out, then compare it
* 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
* 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;
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 );
/*
* Then we also disable interrupts, once again making sure our thread
* is not preempted.
*/
_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;
__cpuid( cpuid_result, 1 );
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();
__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;
_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>
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

View file

@ -98,8 +98,6 @@ NTSTATUS DeviceControl(
case IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE:
APERFMsrTimingCheck();
status = HandlePeriodicCallbackReportQueue(Irp);
if ( !NT_SUCCESS( status ) )
@ -107,6 +105,15 @@ NTSTATUS DeviceControl(
break;
case IOCTL_PERFORM_VIRTUALIZATION_CHECK:
status = PerformVirtualizationDetection( Irp );
if ( !NT_SUCCESS( status ) )
DEBUG_ERROR( "PerformVirtualizationDetection failed with status %x", status );
break;
default:
DEBUG_ERROR( "Invalid IOCTL passed to driver" );
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_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)
typedef struct _DRIVER_INITIATION_INFORMATION
{

View file

@ -5,7 +5,7 @@
#include <intrin.h>
#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

View file

@ -192,8 +192,6 @@ void kernelmode::Driver::QueryReportQueue()
( UINT64 )buffer + sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT_HEADER ) +
i * sizeof( global::report_structures::OPEN_HANDLE_FAILURE_REPORT ) );
std::cout << report->process_id << " " << report->process_name << std::endl;
this->report_interface->ReportViolation( report );
}
@ -232,21 +230,33 @@ void kernelmode::Driver::NotifyDriverOnProcessLaunch()
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()
{
/*
* note: no need for these since when the dll is loaded it will simply
* notify the driver.
*/
}
if ( status == NULL )
{
LOG_ERROR( "DeviceIoControl failed virtualization detect with status %x", GetLastError() );
return;
}
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()

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

View file

@ -20,3 +20,8 @@ void kernelmode::KManager::MonitorCallbackReports()
{
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 VerifySystemModules();
void MonitorCallbackReports();
void DetectSystemVirtualization();
};
}

View file

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

View file

@ -9,7 +9,7 @@
#define REPORT_BUFFER_SIZE 1024
#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_START_ADDRESS_VERIFICATION 20