One of the tactics widely used by cheaters in multiplayer games is modifying the software loaded into memory by overwriting the original code of the process, or by injecting their own code. Anti-cheat software is trying to detect such modifications and decide whether they are a cheat or just expected variations. But what happens when the operating system itself starts to modify memory in an unexpected way?
Puzzled by the observation of runtime modification to Windows kernel components, our developers investigated the origins of these alterations. What we discovered are artifacts of Windows mitigations for well-known CPU side-channel vulnerabilities. Understanding these memory patches is crucial for an anti-cheat solution. This article explores the technical background, so be prepared for a dense read.
Every CPU instruction is executed by a sequence of elementary operations in a pipeline. The instruction is fetched from memory, decoded and then the logical operation is executed. The result is written and the instruction counter is increased. Processors reach for the next instruction when the first step of the previous instruction is performed. That way several instructions can be executed in parallel, each one at a different step in the pipeline at a given time. Modern CPUs can sequence up to 100 instructions at a time in the pipeline and use several tricks to improve performance:
Meltdown and Spectre are two types of side-channel attacks. They are security exploits that target the system hardware rather than flaws in a program’s code directly.
Meltdown allows a user-mode process to reach kernel-mode without any permissions to do so. Since mode separation is assured at the hardware level, an instruction reaching kernel-mode raises an exception that immediately prevents a data leak. This is perfectly safe for a processor running instructions sequentially, but due to pipelining and speculative execution, instructions that are eventually rolled back are executed and may alter and leak information to the CPU’s architectural state.
The processor will later raise an exception, prompting the snapshot to be restored from before the privileged instruction execution. The attacker can employ a side-channel attack on the cache to retrieve information from kernel space that should not be reachable.
Spectre v1 specifically trains the branch predictor in a way that it allows for speculative execution, that would otherwise not be reached in the regular flow.
Spectre v2 on the other hand relies on exploiting the branch target predictor. The attacker may use the branch target predictor to choose the indirect jump to the part of the memory otherwise unreachable. The code then accesses the sensitive data and buffers it. The rest of the attack pattern is the same as Meltdown – the speculative execution is rejected and through cache probing, the attacker can obtain sensitive data.
There are several ways to mitigate the risks of Spectre and Meltdown attacks, many of which are provided by the CPU manufacturers through microcode updates. But depending on the type of attack, a different approach may be used. Meltdown has a few viable solutions, one of which is Kernel Page Table Isolation (KPTI). Spectre v1 can be solved with a LFENCE instruction which stops speculative execution, but for Spectre v2, the solution is more complex as there is a greater performance impact. A more promising solution however is to implement a retpoline mechanism on the operating system level.
Retpoline is a mechanism proposed by Google engineers that replaces an indirect call or jump with a sequence that has safe speculation behavior. A potentially unsafe indirect call is overwritten by a jump to that safe isolated space, followed by an interruption that breaks the speculative injection. Retpoline changes are done ad-hoc when loading a kernel module into memory. How does Windows accomplish these patches?
Dynamic Value Relocation Table (DVRT) refers to metadata that builds into the Load Config directory of the binary file during the compilation phase. DVRT was introduced in the past but has been extended to contain information for mitigating Spectre v2. The compiler keeps all the metadata about indirect jumps and stores them under the .reloc section, repurposed for DVRT. During the runtime, the kernel parses the DVRT metadata and applies the retpoline patches for each reference.
This can be enabled on OS level, using registry key System\CurrentControlSet\Control\Session Manager\Memory Management\FeatureSettings.
The code of the retpoline sequence is stored in the RETPOL section of the kernel image where it is mapped to the page right at the end of each kernel module. All the replacements of the indirect calls and jumps then refer to the same page with the retpoline code, which in turn executes the control flow in a safe manner, free from speculative execution attacks.
Analyzing differences between executable images causes the developers of anti-cheat software great headaches. Windows kernel patches more places to redirect calls and jumps to the retpoline section to mitigate Spectre v2. DVRT is the structure stored in the .reloc section that describes these patch locations. We have dissected and documented this data structure.
Feel free to check out our blog for more details and learn how anti-cheat helps keep gaming experiences fun and fair!