Open jonomango opened 1 year ago
Related solution by Daax: https://www.unknowncheats.me/forum/3247434-post5.html.
The only downside to his solution is that it requires a vm-exit on every rdtsc
instruction, which results in a heavy performance hit. The general idea is what I'll be aiming for, however.
One idea is to enable/disable rdtsc
vm-exiting only when it is required: i.e. only when cpuid
or a similar instruction has been executed.
Wow this is annoying... one issue I'm encountering is the Windows scheduler kicking us off the CPU right after we execute cpuid
, but before we execute rdtsc
. This is much more common than I initially thought due to how much time we spend handling the cpuid
vm-exit. This isn't a huge issue by itself because we'll eventually get execution back if we wait long enough, but the problem is that we might get swapped to another CPU which won't have rdtsc
-exiting enabled (since cpuid
wasn't executed on that CPU). I guess the solution is to send an IPI out to every CPU to enable rdtsc
-exiting whenever cpuid
is executed on any CPU.
Optimization: we can ignore cpu->hide_vm_exit_overhead
if we can confirm that the vm-exit was triggered from a "safe" module (i.e. a module that is guaranteed to not be timing us). Essentially, vm-exits from ntoskrnl.exe and hal can be safely ignored.
You may also run into multi-threaded timing checks which are not so easily defeated. I'm not sure there is a way to actually hide time in this scenario aside suspending all other cores during an exit - especially if they use different timers such as a pure software one running without interrupts
Daax mentioned this: "This doesn't defeat MP timing attacks, such as the one EAC utilizes where one thread counts and the other executes the unconditionally exiting instruction."
Agreed. I don't think it is ever possible to fully hide from timing attacks in a generic manner. Vm-exits will always cause overhead that can be observed. However, the scenario that Daax illustrates isn't too hard to evade as long as we don't share a timer between threads (although the detection can be easily amplified by counting in BOTH threads, but that is a different matter entirely).
The latest commit evades attacks 1 and 3 but will still fail for method 2.
List of timing detections to hide from:
rdtsc
+cpuid
+rdtsc
rdtsc
+cpuid
+rdtsc
+sleep()
rdtsc
+cpuid
+rdtsc
on VCPU1 while VCPU2 spamscpuid