This post firstly gives an introduction about IDT shadow in that paper, then talks about guest IDT hooking with this technique, and finally explains how to detect such a hooking with processor LBR feature.
See picture below (captured from that presentation), it presents an idea of Exitess Interrupt Delivery.
The basic ideas are as follows:
- Setup a guest shadow IDT table. With hardware virtualization's help, we can easily to cheat guest OS software by monitoring and trapping guest execution of LIDT and SIDT instruction.
We can maintain a shadow IDT table (and keep it sync'ed with the original one), then let the real guest logical processor IDTR.base point to our shadow IDT table.
- And in shadow IDT table, it clears Present bit or reduces IDTR.limit to trap the wanted guest interrupts/exceptions.
The former idea is exactly the same with my idea in previous post by generating #NP fault. The later idea (triggering #GP) is not recommended in my opinion because decreasing DITR.limit will cause many false positives (please correct me if I am wrong).
In that paper (see References at the end of this post for the link of this full paper), ELI utilizes the shadow IDT to implement a high-performance guest/host interrupt delivery solution without changes to guest OS kernel.
And one of other benefits is that the guest OS kernel integrity check (like Windows x64 OS PatchGuard) cannot even detect it.
Guest IDT vector entry hooking
Another usage of shadow IDT is to hook any one of guest IDT ISRs by changing the corresponding ISR entry in the shadow IDT table.
For example, to implement an interrupt filter for a particular interrupt, we can change the IDT entry in the shadow IDT table and let it point to our own hooking ISR entry. After that, whenever that particular interrupt is triggered, the processor will firstly pass on the control to our hooking ISR routine, and we can do something (e.g. filtering), then jump to original OS kernel ISR for further handling. And under some circumstance, we can even let the control be back to our routine after original ISR handling (see the patent in the References).
However, what if this kind guest IDT hooking is done by a malicious hypervisor? Then how to detect it in guest OS?
Detect IDT hooking with LBR (Last Branch Record)
Nowadays, all the x86/Intel processors have a feature: Last Branch Record.
When it is enabled, the processor records a running trace of the most recent branches, interrupts, and/or exceptions taken by the processor in the last branch record (LBR) stack (could be in MSRs and/or Branch Trace Store (BTS) Buffer in DS save memory area).
To be specific, when this feature is configured by guest OS kernel, the processor will capture the trace (e.g. LastBranchToIP address) in LBR stack whenever an interrupt is generated by processor. So when the OS original ISR gets executed, it can check the content of LastBranchToIP in LBR stack, to see if it matches with the OS original ISR entry. If there is a mismatch, then it indicates that the corresponding ISR entry is hooked by others, e.g. by a malicious hypervisor.
But in a hypervisor environment, there are some solutions to prevent guest OS kernel from detecting IDT hooking with LBR feature, e.g.,
- Hide LBR and disabling this feature by trapping corresponding CPUID instruction (LBR capability check) and MSR read/write access to LBR control MSRs.
- Because LBR stack could be stored with some LBR MSRs, hypervisor must trap those MSRs, and return faked values.
- And hypervisor must also trap read access to Branch Trace Store (BTS) Buffer of DS-save memory area if guest OS configures the LBR stack to be also stored in BTS buffer.
Ravi, et al.,Patent: Secure handling of interrupted events utilizing a virtual interrupt definition table (VIDT):
The full paper for ELI presentation from IBM israel research lab: Abel Gordon, et al. ELI: Bare-Metal Performance for I/O Virtualization