diff --git a/src/avmext/ntinternal.h b/src/avmext/ntinternal.h index 89267bd..ad311b5 100644 --- a/src/avmext/ntinternal.h +++ b/src/avmext/ntinternal.h @@ -1,227 +1,222 @@ #pragma once #include #pragma warning (disable : 4201) #pragma warning (disable : 4204) #pragma warning (disable : 4214) // // Inspired by WRK. // #define CR4_TSD 0x00000004 // Time stamp disable +#define CR0_WP 0x10000 // Write Protection enable + #define ReadTSC() __rdtsc() #define ReadTSCP(data) __rdtscp(data) +#define ReadCR0() __readcr0() +#define WriteCR0(data) __writecr0(data) + #define ReadCR4() __readcr4() #define WriteCR4(data) __writecr4(data) #define ReadIDT(data) __sidt(data) #define WriteIDT(data) __lidt(data) #define EnableInterrupts() _enable() // sti instruction #define DisableInterrupts() _disable() // cli instruction // // IDT descriptor. // struct _AVM_KDESCRIPTOR32 { USHORT Pad; USHORT Limit; ULONG Base; }; -struct _AVM_KDESCRIPTOR64 -{ - union - { - struct - { - USHORT Pad[3]; - USHORT Limit; - }; - - ULONG_PTR LowPart; - }; - union - { - ULONG_PTR Base; - ULONG_PTR HighPart; - }; -}; - // // Entry of Interrupt Descriptor Table (IDTENTRY) // struct _AVM_KIDTENTRY32 { USHORT Offset; USHORT Selector; USHORT Access; USHORT ExtendedOffset; }; +#pragma pack(1) +struct _AVM_KDESCRIPTOR64 +{ + USHORT limit; + ULONG64 Base; +}; + struct _AVM_KIDTENTRY64 { union { struct { USHORT OffsetLow; USHORT Selector; USHORT IstIndex : 3; USHORT Reserved0 : 5; USHORT Type : 5; USHORT Dpl : 2; USHORT Present : 1; USHORT OffsetMiddle; ULONG OffsetHigh; ULONG Reserved1; }; ULONG64 Alignment; }; }; +#pragma pack() + // // Trap frame // struct _AVM_KTRAP_FRAME32 { // // Segment registers // ULONG SegGs; ULONG SegEs; ULONG SegDs; // // Volatile registers // ULONG Edx; ULONG Ecx; ULONG Eax; // // FS is TIB/PCR pointer, is here to make save sequence easy // ULONG SegFs; // // Non-volatile registers // ULONG Edi; ULONG Esi; ULONG Ebx; ULONG Ebp; // // Control registers // ULONG ErrCode; ULONG Eip; ULONG SegCs; ULONG EFlags; ULONG HardwareEsp; // WARNING - segSS:esp are only here for stacks ULONG HardwareSegSs; // that involve a ring transition. }; struct _AVM_KTRAP_FRAME64 { // // Volatile registers. // // N.B. These registers are only saved on exceptions and interrupts. They // are not saved for system calls. // ULONG64 Rax; ULONG64 Rcx; ULONG64 Rdx; ULONG64 R8; ULONG64 R9; ULONG64 R10; ULONG64 R11; // // Saved nonvolatile registers RBX, RDI and RSI. These registers are only // saved in system service trap frames. // ULONG64 Rbx; ULONG64 Rdi; ULONG64 Rsi; // // Saved nonvolatile register RBP. This register is used as a frame // pointer during trap processing and is saved in all trap frames. // ULONG64 Rbp; // // Information pushed by hardware. // // N.B. The error code is not always pushed by hardware. For those cases // where it is not pushed by hardware a dummy error code is allocated // on the stack. // union { ULONG64 ErrorCode; ULONG64 ExceptionFrame; }; ULONG64 Rip; USHORT SegCs; USHORT Fill1[3]; ULONG EFlags; ULONG Fill2; ULONG64 Rsp; USHORT SegSs; USHORT Fill3[1]; }; #if defined(_X86_) typedef struct _AVM_KDESCRIPTOR32 AVM_KDESCRIPTOR, *PAVM_KDESCRIPTOR; typedef struct _AVM_KIDTENTRY32 AVM_KIDTENTRY, *PAVM_KIDTENTRY; typedef struct _AVM_KTRAP_FRAME32 AVM_KTRAP_FRAME, *PAVM_KTRAP_FRAME; #elif defined(_AMD64_) typedef struct _AVM_KDESCRIPTOR64 AVM_KDESCRIPTOR, *PAVM_KDESCRIPTOR; typedef struct _AVM_KIDTENTRY64 AVM_KIDTENTRY, *PAVM_KIDTENTRY; typedef struct _AVM_KTRAP_FRAME64 AVM_KTRAP_FRAME, *PAVM_KTRAP_FRAME; #else # error "Unknown architecture" #endif // // Initialize & destroy routines. // NTSTATUS NTAPI AvmNtInternalInitialize( IN PDRIVER_OBJECT DriverObject ); VOID NTAPI AvmNtInternalDestroy( IN PDRIVER_OBJECT DriverObject ); diff --git a/src/avmext/rdtscemup.c b/src/avmext/rdtscemup.c index 0e59eb5..0c52e9a 100644 --- a/src/avmext/rdtscemup.c +++ b/src/avmext/rdtscemup.c @@ -1,125 +1,164 @@ #include "rdtscemu.h" #include "ntinternal.h" #include #include // // It is very unlikely that each CPU will have different Trap0D handlers. // ULONG_PTR AvmpRdtscEmulationTrap0DOriginalHandler; // // Set CR4_TSD flag into the CR4 register. // VOID NTAPI AvmpRdtscEmulationSetTimeStampDisableFlag( VOID ) { WriteCR4(ReadCR4() | CR4_TSD); } // // Unset CR4_TSD flag from the CR4 register. // VOID NTAPI AvmpRdtscEmulationUnsetTimeStampDisableFlag( VOID ) { WriteCR4(ReadCR4() & ~CR4_TSD); } // // Force immediate context switch immediate context switch if the current processor // does not fall in the newly set affinity mask and does not return to the caller // until the thread is rescheduled on a processor conforming to the new affinity mask. // // ref: http://www.drdobbs.com/monitoring-nt-debug-services/184416239 // VOID NTAPI AvmpRdtscEmulationSwitchToProcessor( IN UCHAR ProcessorIndex ) { // // If KeSetSystemAffinityThread is called at IRQL <= APC_LEVEL and the call is successful, // the new affinity mask takes effect immediately. // // ref: https://msdn.microsoft.com/en-us/library/windows/hardware/ff553267(v=vs.85).aspx (see Remarks section) // if (KeGetCurrentIrql() > APC_LEVEL) { KeLowerIrql(APC_LEVEL); } KeSetSystemAffinityThread(AFFINITY_MASK(ProcessorIndex)); } // // Replace IDT entry. // #if defined(_X86_) VOID NTAPI AvmpRdtscEmulationHookInterruptEntry( IN UCHAR Index, IN ULONG_PTR NewRoutineAddress, OUT ULONG_PTR* OldRoutineAddress ) { AVM_KDESCRIPTOR Idtr; ReadIDT(&Idtr); PAVM_KIDTENTRY Idt = (PAVM_KIDTENTRY)(Idtr.Limit | Idtr.Base << 16); DisableInterrupts(); { ULONG_PTR OriginalHandler = (ULONG)(Idt[Index].ExtendedOffset) << 16 | Idt[Index].Offset; Idt[Index].Offset = (USHORT)NewRoutineAddress; Idt[Index].ExtendedOffset = (USHORT)((ULONG_PTR)NewRoutineAddress >> 16); *OldRoutineAddress = OriginalHandler; } EnableInterrupts(); } #else +// +// WP bit needs to be disabled on newer Windows 64-bit versions, +// Presumably due to Kernel Data Protection. +// Code referenced from https://github.com/LLLZed/IDTHOOK +// + +KIRQL WPOFFx64() +{ + KIRQL irql = KeRaiseIrqlToDpcLevel(); + UINT64 cr0 = ReadCR0(); + cr0 &= ~CR0_WP; + WriteCR0(cr0); + DisableInterrupts(); + return irql; +} + +void WPONx64(KIRQL irql) +{ + UINT64 cr0 = ReadCR0(); + cr0 |= CR0_WP; + EnableInterrupts(); + WriteCR0(cr0); + KeLowerIrql(irql); +} + +ULONG64 SetIdtAddr(ULONG64 IdtBaseAddr, UCHAR Index, ULONG64 NewAddr) +{ + PAVM_KIDTENTRY Pidt = (PAVM_KIDTENTRY)(IdtBaseAddr); + Pidt = Pidt + Index; + ULONG64 OffsetHigh, OffsetMiddle, OffsetLow, ret; + + + OffsetHigh = Pidt->OffsetHigh; + OffsetHigh = OffsetHigh << 32; + OffsetMiddle = Pidt->OffsetMiddle; + OffsetMiddle = OffsetMiddle << 16; + + OffsetLow = Pidt->OffsetLow; + ret = OffsetHigh + OffsetMiddle + OffsetLow; + Pidt->OffsetHigh = NewAddr >> 32; + Pidt->OffsetMiddle = NewAddr << 32 >> 48; + Pidt->OffsetLow = NewAddr & 0xFFFF; + return ret; +} + +// +// System might triple fault using this method if KVA Shadowing is enabled, +// Disable through registry before continuing. +// + VOID NTAPI AvmpRdtscEmulationHookInterruptEntry( IN UCHAR Index, IN ULONG_PTR NewRoutineAddress, OUT ULONG_PTR* OldRoutineAddress ) { AVM_KDESCRIPTOR Idtr; ReadIDT(&Idtr); - PAVM_KIDTENTRY Idt = (PAVM_KIDTENTRY)(Idtr.LowPart >> 16 | Idtr.HighPart << 48); - PAVM_KIDTENTRY IdtAt = &Idt[Index]; - - DisableInterrupts(); - { - ULONG_PTR OriginalHandler = (ULONG_PTR)Idt[Index].OffsetLow | - (ULONG_PTR)Idt[Index].OffsetMiddle << 16 | - (ULONG_PTR)Idt[Index].OffsetHigh << 32; + KIRQL IRQL = WPOFFx64(); - IdtAt->OffsetLow = (USHORT)(NewRoutineAddress); - IdtAt->OffsetMiddle = (USHORT)(NewRoutineAddress >> 16); - IdtAt->OffsetHigh = (ULONG) (NewRoutineAddress >> 32); + *OldRoutineAddress = SetIdtAddr(Idtr.Base, Index, NewRoutineAddress); - *OldRoutineAddress = OriginalHandler; - } - EnableInterrupts(); + WPONx64(IRQL); } #endif