necessities:
running hypervisor (AMD, Intel)
hypervisor must detect CR3 updates from inside of SwapContext (exported from ntoskrnl.exe, ntkrnlmp.exe) - also possible from hypervisor (idea / code how to detect SwapContext is not for release, you must develop your own way)

principle of antidebug:

every process has its own unique base of virtual memory translation tables in CR3, so each process is separated and its memory is protected from other processes
hypervisor detects CR3 updates in SwapContext procedure, encrypts protected memory block of the protected process when its CR3 is unloaded in SwapContext, decrypts protected memory block of the protected process when its CR3 is loaded in SwapContext
there is absolutelly necessary to detect writes of CR3 in SwapContext !
updates of CR3 occure also in procedures like KeAttachProcess, KeStackAttachProcess, KeDetachProcess, KeUnstackDetachProcess and its clones, at this point hypervisor does nothing with encrypted/decrypted protected memory block of the protected process (calling of these process attach / process detach procedures is a result of e.g. calling ring3 procedures like kernel32.ReadProcessMemory, kernel32.WriteProcessMemory, the protected memory is usually in encrypted state when calling these procedures and then attacker reads only encrypted data)
when the CR3 of an alien process is loaded in SwapContext, hypervisor encrypts the protected memory block of the protected process
when the CR3 of the protected process with the protected memory block is loaded in SwapContext (it means that any thread of the process is going to be loaded into context), hypervisor decrypts the protected memory block of the protected process
so then any attacked may dump only encrypted memory

here an sample of debugger which is an alien process when it tries to read the code and data of the protected process, the encryption are simple ROR by 1 bit instructions with every qword, decryption is then inverse instruction ROL by 1 bit, the encryption should be stronger, the instruction which protects the memory is the CPUID instruction with 3 parameters: eax=magic_value, ecx=size, rdx=pointer_to_the_begin_of_the_protected_memory_block (note that the hypervisor grabs the CR3 of the process at the time of intercepting the CPUID instruction)

before the process protects its memory


after the process protects its memory


possible attacks are by e.g. creating remote thread in the process with the protected memory block and leeching the memory through that thread... but there is also possibility to protect against this type of attack and encrypt / decrypt the memory when only one allowed thread of the process is going into context (only one "trusted" thread of the process with the CR3 instead of all threads of the process with the CR3)

another type of attack is possible thanks to SMP, the protected memory block may be in decrypted state at CPU0 because a thread of the protected process is in context at CPU0 (CR3 loaded through SwapContext)
attacker may try to dump the protected memory at the same time but running at CPU1 (e.g. a coindicence at the same moment)
to protect against this type of attack, hypervisor must be running on all CPUs in SMP and wait some time at CPU1 until thread leaves context at CPU0 if there is an attempt to load CR3 of the protected process from inside of KeAttachProcess and similar clones at CPU1 (hypervisor must perform some delay at CPU1 and resume the guest trying to load CR3 of the protected process through the MOV CR3 instruction in KeAttachProcess at CPU1 only after the protected memory block is in encrypted state = after thread of the protected process leaves context through CR3 update in SwapContext at CPU0)

by Feryno, 2009 November 05