No Errata For U!
If you haven’t already, read Part 1 which outlines three neat tricks used by Patchguard.
KiErrata420Present
The LSTAR MSR can be intercepted using a hypervisor to trap on reads and writes. It is the most common and efficient way to hook syscalls in most modern x86 operating systems. However contrary to what I’ve read online, this unfortunately comes at the cost of many potential detection vectors for the hypervisor if not properly dealt with. Using a few clever tricks in privileged code, we can reliably determine if a hook on the LSTAR MSR is present or not, that is, if proper precautions have not already been implemented in the hypervisor. Starting in Windows 10 1903 build 18362, Microsoft added several LSTAR hook detection techniques.
One of the simpler LSTAR hook detections was not given the meme “errata” name, perhaps it was not good enough 🙁 – so let’s call it KiErrata420Present
(possibly not that far off from what Microsoft calls it internally?).
I have outlined the detection below:
KiErrata420Present: cli ; disable interrupts mov r9d, 0C0000082h ; mov ecx, r9d ; rdmsr ; read LSTAR MSR value shl rdx, 32 ; or rax, rdx ; store LSTAR value in rax lea rdx, [rdi+87Ah] ; store temp LSTAR value in rdx read from pg context mov rbx, rax ; rbx = original LSTAR value mov rax, rdx ; rax = temp LSTAR value shr rdx, 32 ; wrmsr ; write temporary LSTAR MSR value mov r14d, 20000h ; lea rax, [rdi+87Ch] ; rax = stub to execute syscall mov rsi, 0A3A03F5891C8B4E8h ; rsi = constant to obfuscate pg context pointer test [rdi+994h], r14d ; test if should store pg check data? jnz short trigger_syscall ; if nz, skip tracing mov r8, gs:KPCR.CurrentPrcb ; lea rdx, [rdi+rsi] ; mov rcx, [rdi+4C0h] ; mov [rcx], rdx ; mov rcx, [rdi+4C8h] ; store pg check related data mov [rcx], r8 ; mov rcx, [rdi+4D0h] ; mov [rcx], r9 ; mov rcx, [rdi+4D8h] ; mov qword ptr [rcx], 112h ; trigger_syscall: call KeGuardDispatchICall ; dispatch call to syscall instruction stub test [rdi+994h], r14d ; test if pg check should be traced? jnz short restore_lstar ; if nz, skip tracing mov rax, [rdi+4C0h] ; mov [rax], rsi ; mov rax, [rdi+4C8h] ; mov [rax], r13 ; wipe pg check related data mov rax, [rdi+4D0h] ; mov [rax], r13 ; mov rax, [rdi+4D8h] ; mov [rax], r13 ; restore_lstar: mov rdx, rbx ; restore original LSTAR value mov rax, rbx ; shr rdx, 32 ; mov ecx, 0C0000082h ; wrmsr ; write original LSTAR MSR value sti ; reenable interrupts
This check is indeed very simple. It temporarily overwrites the system’s LSTAR MSR value with its own temporary syscall handler, and restores the original LSTAR MSR value afterwards. How do I know this for sure? Let’s dig in further to find out.
First off let’s figure out what temporary value is written to the LSTAR MSR:
lea rdx, [rdi+87Ah] ; store temp LSTAR value in rdx read from pg context mov rbx, rax ; rbx = original LSTAR value mov rax, rdx ; rax = temp LSTAR value shr rdx, 32 ;
As we can see the temporary LSTAR value written is the address at RDI+0x87A
. Knowing a little about the patchguard callback we know that the RDI register holds a temporary addresses of the current “patchguard context”. Using this knowledge, we can easily determine where the context+0x87A
is written to in the patchguard initialization routine:
mov byte ptr [r14+87Ah], 0C3h ; store RET instruction
Great, this is the opcode of the return instruction, which is very interesting!
Next let’s figure out what this call is at KeGuardDispatchICall
. As you may already know, KeGuardDispatchICall
works by branching to an instruction pointer given in RAX
. So let’s check out where RAX
comes from then:
lea rax, [rdi+87Ch] ; rax = stub to execute syscall
Last step, determine where context+0x87C
is written to in the patchguard initialization routine:
mov eax, 050Fh mov [r14+87Ch], ax ; store SYSCALL instruction
Whats the meaning of this 050Fh
we see? Why that is the SYSCALL instruction opcode! I think we already know what is happening now. But let’s simplify this a little bit more using some pseudocode:
_disable(); OriginalSyscall64 = __readmsr(MSR_LSTAR); __writemsr(MSR_LSTAR, &PgContext->DummySyscallHandler); // C3 -> ret KeGuardDispatchICall(&PgContext->Syscall); // 0F 05 -> syscall __writemsr(MSR_LSTAR, OriginalSyscall64); _enable();
Neat! It simply executes the SYSCALL instruction and then immediately returns from the handler.
This is very effective against most hypervisors utilizing LSTAR hooks and is even better at annoying hypervisors that do their best to prevent the guest from tampering with the LSTAR MSR. In many naive LSTAR MSR hook implementations, developers will simply disallow writes to the LSTAR MSR altogether, which will in turn cause a fault in this case because the context is not setup before executing the syscall in this situation. An example of such an implementation is Hyperbone’s LSTAR MSR hook.
This becomes a frustrating issue for the hypervisor developer. They should fret however since there is a rather simple solution to this simple problem for the developer of a hypervisor. The solution is to let the guest overwrite the LSTAR MSR, and effectively shadow the original.
Well then that means the guest can just force us to unhook???
Yes, you’d be right. However, we can restore our hook afterwards in this case and in almost every other case, unless the guest creates their own syscall hook implementation themselves. It is unfortunate for them that in Windows, patchguard has a separate check for asserting the value of the LSTAR MSR is not tampered with. Therefore, realistically no piece of guest software is going to permanently overwrite your precious LSTAR MSR on Windows unless they have disabled patchguard, which is entirely possible, but also very easy to catch. Besides, these circumstances can all be monitored in the VMM and circumvented as needed.
For this case specifically we can circumvent this detection as such:
VMM_EVENT_STATUS HVAPI VmmHandleMsrRead( _In_ PVIRTUAL_CPU Vcpu ) { // ... // // Hide our LSTAR syscall hook handler address. // case MSR_LSTAR: if (Vcpu->OriginalLSTAR) { MsrValue = Vcpu->OriginalLSTAR; } else { MsrValue = __readmsr(MSR_LSTAR); } break; // ... } VMM_EVENT_STATUS HVAPI VmmHandleMsrWrite( _In_ PVIRTUAL_CPU Vcpu ) { // ... // // Let the guest overwrite our hook to avoid possible detection. // // If and only if the guest is writing the original LSTAR, we replace // the MSR value with the hook LSTAR value. // // N.B. We do this to get around one of PatchGuard's syscall hook // detections which works like this: // // _disable(); // OriginalSyscall64 = __readmsr(MSR_LSTAR); // __writemsr(MSR_LSTAR, &PgCtx->PgSyscallDummy); // C3 -> ret // KeGuardDispatchICall(&PgCtx->SyscallOpcode1); // 0F 05 -> syscall // __writemsr(MSR_LSTAR, OriginalSyscall64); // _enable(); // case MSR_LSTAR: if (MsrValue == Vcpu->OriginalLSTAR) { MsrValue = Vcpu->HookLSTAR; } __writemsr(MSR_LSTAR, MsrValue); break; // ... }
KiErrata1337Present
Using a bit of critical thinking, I came up with my own rather deviant LSTAR detection using some tricks I found derived from Patchguard. I call this one KiErrata1337Present
, shamelessly derived from Microsoft’s meme “errata” naming scheme for their other cool patchguard checks.
Those who have looked into modern 64-bit system call handlers in Linux and/or Windows may have noticed they start and (sometimes) end with the SWAPGS instruction. The SWAPGS instruction exchanges the current GS base register (IA32_GS_BASE) value with the kernel GS base register value contained in MSR address C0000102H
(IA32_KERNEL_GS_BASE).
The instructions immediately following the SWAPGS instruction in the syscall handler is a segmented MOV instruction. Here’s a peek of KiSystemCall64
:
KiSystemCall64 proc near swapgs ; swap GS base with IA32_KERNEL_GS_BASE mov gs:KPCR.UserRsp, rsp ; store user mode stack in processor control region mov rsp, gs:KPCR.Prcb.RspBase ; set the kernel stack from processor control region
Cool, knowing these couple details we know we can mess with the GS base to cause a page fault (#PF) inside the syscall handler. Wait, what?
WTF why would you want to purposely page fault???
You’d be sane thinking this. The reason we want to fault inside the syscall handler is so that we can read the REAL RIP of the syscall handler. This is a very important detail!
Alright, let’s try purposely generating a a page fault (#PF) then:
KiErrata1337Present: swapgs ; swapgs to emulate coming from user mode mov ecx 0C0000102h ; xor eax, eax ; set KERNEL_GS_BASE MSR to zero xor edx, edx ; wrmsr ; syscall ; execute the syscall instruction to trigger fault ret
Boom! We page faulted – that’s a good thing by the way!
However, there are a couple problems right off the top: the original page fault handler in Windows is just going to BLOW up and bugcheck, and if we don’t restore the original GS base and kernel GS base values, the operating system is also going to BLOW up in the next context switch. So we need to temporarily hook the interrupt descriptor table (IDT), and back up the GS bases, too easy!
Steps to temporarily hook the interrupt descriptor table IDT are as follows:
- Disable interrupts
- Save the original IDT
- Load our temporary IDT
- Do your thang
- Restore original IDT
- Re-enable interrupts
Here is some pseudo code implementing the above steps with the page fault #PF exception hook we need:
TempIdtr.Limit = sizeof(TempIdt) - 1; TempIdtr.Base = (UINT64)&TempIdt[0]; for (IdtEntry in KPCR->IdtBase) TempIdt[i] = IdtEntry; // Fill in temporary IDT _disable(); // Disable interrupts __sidt(&OriginalIdtr); // Backup original IDT __lidt(&TempIdtr); // Load our temporary hook IDT // Hook page fault handler. TempIdt[PF] = PageFaultHookHandler; // Trigger syscall that will purposely page fault! KiErrata1337Present(); // This must be lean enough not to timeout watchdog! __lidt(&OriginalIdtr); // Restore the original IDT. _enable(); // Re-enable interrupts.
Our page fault handler doesn’t do anything but return from the interrupt for now as we test. We do this using the IRET instruction. Please read about the IRET instruction if you are not already familiar with it as it is very important you understand it for later on when we actually create the detection out of all this!
Here is our boring page fault hook handler:
PageFaultHookHandler: add rsp, 8 ; skip fault code on stack iretq ; return from interrupt
Now that we have a hook setup on the page fault handler, let’s fix our KiErrata1337Present
routine to backup and restore the original GS bases:
KiErrata1337Present: mov ecx, 0C0000101h ; read original GS_BASE MSR rdmsr ; push rdx ; backup original GS_BASE MSR push rax ; mov ecx, 0C0000102h ; read original KERNEL_GS_BASE MSR rdmsr ; push rdx ; backup original KERNEL_GS_BASE MSR push rax ; swapgs ; swapgs to emulate coming from user mode xor eax, eax ; xor edx, edx ; set KERNEL_GS_BASE MSR to zero wrmsr ; syscall ; execute syscall instruction which executes swapgs immediately mov ecx, 0C0000102h ; pop rax ; pop rdx ; restore original KERNEL_GS_BASE MSR wrmsr ; mov ecx, 0C0000101h ; pop rax ; pop rdx ; restore original GS_BASE MSR wrmsr ; ret ; return back to caller
It works! Now for the juicy detection!
Like I mentioned before, the entire reason we want to cause a fault in the syscall handler is so that we can read the RIP from the machine trap frame upon faulting. That part is easy. But how do we jump back to our KiErrata1337Present
routine if we are in the page fault handler? Well, lucky for us, the SYSCALL instruction saves a return address for us which is actually intended for it’s counterpart SYSRET. When the SYSCALL instruction executes, it will store the address of the next instruction in the RCX register.
We can see the SYSCALL instruction operates as such:
RCX ← RIP; (* Will contain address of next instruction *) RIP ← IA32_LSTAR; R11 ← RFLAGS; RFLAGS ← RFLAGS AND NOT(IA32_FMASK); // .... memes
So how do we return? Simple, We override the RIP address on the machine frame. Hopefully you understand how IRET works now if you weren’t already familiar with it, because now is where we use it’s operation to wrap up our detection. Let’s be clever and get two birds stoned at once by using the XCHG instruction:
PageFaultHookHandler: add rsp, 8 ; skip fault code on stack xchg qword [rsp], rcx ; xchg trap frame RIP with syscall return address in RCX iretq ; return from interrupt
We use the XCHG instruction to our advantage to exchange the syscall return address in RCX, with the RIP in the trap frame. This allows us to effectively store the REAL syscall handler address in RCX and still branch back to the instruction immediately after our SYSCALL instruction.
That’s pretty much it. That was a nutty one, wasn’t it? Putting it all together looks something like this:
// detect.c VOID DoTheThing( VOID ) { KIDTENTRY64 TempIdt[19]; X64_DESCRIPTOR TempIdtr; PVOID SyscallHandler; TempIdtr.Limit = sizeof(TempIdt) - 1; TempIdtr.Base = (UINT64)&TempIdt[0]; RtlCopyMemory(TempIdt, KeGetPcr()->IdtBase, TempIdtr.Limit + 1); _disable(); // Disable interrupts __sidt(&OriginalIdtr); // Backup original IDT __lidt(&TempIdtr); // Load our temporary hook IDT // Hook page fault handler. TempIdt[X86_TRAP_PF].OffsetLow = (UINT16)(UINTN)PageFaultHookHandler; TempIdt[X86_TRAP_PF].OffsetMiddle = (UINT16)((UINTN)PageFaultHookHandler >> 16); TempIdt[X86_TRAP_PF].OffsetHigh = (UINT32)((UINTN)PageFaultHookHandler >> 32); // Trigger syscall that will purposely page fault! SyscallHandler = KiErrata1337Present(); __lidt(&OriginalIdtr); // Restore the original IDT. _enable(); // Re-enable interrupts. LOG_INFO("REAL SYSCALL Handler = 0x%p", SyscallHandler); }
; detect.asm PageFaultHookHandler: add rsp, 8 ; skip fault code on stack xchg qword [rsp], rcx ; xchg trap frame RIP with syscall return address in RCX iretq KiErrata1337Present: push rbx ; backup RBX which is to be clobbered mov ecx, 0C0000101h ; read original GS_BASE MSR rdmsr ; push rdx ; backup original GS_BASE MSR push rax ; mov ecx, 0C0000102h ; read original KERNEL_GS_BASE MSR rdmsr ; push rdx ; backup original KERNEL_GS_BASE MSR push rax ; swapgs ; swapgs to emulate coming from user mode xor eax, eax ; xor edx, edx ; set KERNEL_GS_BASE MSR to zero wrmsr ; syscall ; execute syscall instruction which executes swapgs immediately mov rbx, rcx ; store result syscall handler address in RBX for now mov ecx, 0C0000102h ; pop rax ; pop rdx ; restore original KERNEL_GS_BASE MSR wrmsr ; mov ecx, 0C0000101h ; pop rax ; pop rdx ; restore original GS_BASE MSR wrmsr ; mov rax, rbx ; return result in RAX pop rbx ; restore original RBX ret ; return back to caller
PoC||GTFO
This wouldn’t be a complete article without some easy to use paste would it?
You can find the full proof of concept implementation on my github at https://github.com/ajkhoury/Errata1337
4 thoughts on “Patchguard: Detection of Hypervisor Based Introspection [P2]”