Introduction
This story describes how to use Hardware Virtualization (HVM) to keep some of your hook code away from the kernel, making it less susceptible to interference from other kernel hooks and harder to detect. The idea for this article comes from a dynamic Linux kernel update project at a certain school, with much of the code borrowed from BluePill.
Chapter 1: The Dawn of Avalon
As the number of skilled drivers increases, the competition for system control intensifies, leaving almost no untouched area within the kernel. Inline hooks, SSDT hooks, and various other hooks clutter our fragile kernel. Some of these hooks are complex, some are interrelated, and some are monitored for detection. To regain control over those control points, one must invest effort in studying these hooks, making what was once a simple hook become a complex web of dependencies. Now, with hardware virtualization technology, we can approach these problems from a different angle…
1. Composition of Avalon
The essence of Avalon consists of the following parts: 1. Avlboot.sys (used to save the system kernel that has not yet been polluted by hooks for later use) 2. Avalon.sys (used to read pseudo-kernel information and enable hardware virtualization to load the pseudo-kernel) 3. XXX.sys (a program that utilizes the information provided by Avlboot.sys to perform specific hook operations)
2. The Truth of Avalon
The Avalon framework introduced in this article is a kernel loading framework based on virtualization. It utilizes Hardware Virtualization (HVM) and a kernel memory that has not been polluted by hooks, allowing two kernels to run simultaneously on a PC. Avalon gains control by manipulating the sysenter_eip and the corresponding addresses in the IDT pointing to the pseudo-kernel. In short, Avalon uses Hardware Virtualization (HVM) to load its own kernel, bypassing the original kernel, allowing it to gain kernel control in an undetectable environment. The implementation principle is illustrated in the following diagram:
After obtaining the base address of the pseudo-kernel, your hook program can handle SSDT and shadow SSDT through hookport (strongod needs to back up its hooked SSDT, which is a bit more complicated, while AGP can be used by simply offsetting the address).
3. Applications of Avalon
We can actually view Avalon as a form of sysenter hook + idt hook. Since Avalon is based on Hardware Virtualization (HVM), it allows us to bypass the entire kernel, enabling real-time replacement of the kernel during runtime. Because the memory range of the pseudo-kernel is not subject to memory monitoring, other programs cannot directly access it, making our pseudo-kernel a safe haven for hooking without worrying about other interference issues.
This method of hooking has the following advantages compared to traditional hooks:
1. It operates outside the traditional memory monitoring range, making it harder to detect. 2. It does not interfere with the existing hook code in the system, providing higher compatibility.
However, it also has some disadvantages:
1. It cannot directly interfere with object hooks, IRP hooks, and other hook codes that do not rely on kernel memory. 2. The entire system depends on Hardware Virtualization (HVM), which has certain hardware requirements.
Chapter 2: Manifestation of Fantasies
1. Pure Initialization
First, let’s implement the initialization of Avalon, which consists of four parts: kernel information acquisition, pseudo-kernel construction, IDT information preservation, and original SYSENTER_EIP acquisition. The implementation code is as follows:
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
...
KrnlCopy();
IDTCopy();
ReadMsrSysenter();
...
}
/*
Kernel Copy
*/
VOID KrnlCopy()
{
PVOID Buffer;
ULONG Size;
ULONG RetSize;
PSYSTEM_MODULE_INFORMATION InfoBuffer;
NTSTATUS Status;
PVOID ModuleBase;
ULONG ModuleSize;
Size=0x1000;
do {
Buffer=ExAllocatePool(NonPagedPool,Size);
Status=ZwQuerySystemInformation(SystemModuleInformation,Buffer,Size,&RetSize);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
ExFreePool(Buffer);
Size = RetSize;
}
}while(Status == STATUS_INFO_LENGTH_MISMATCH);
InfoBuffer = (PSYSTEM_MODULE_INFORMATION)Buffer;
ModuleBase=(PVOID)(InfoBuffer->ModuleInfo[0].Base);
Orig_krnl=(ULONG)ModuleBase;
DbgPrint("AvlBoot:Orig_krnl Base=%x\n",ModuleBase);
ModuleSize=InfoBuffer->ModuleInfo[0].Size;
makeKernelCopy((ULONG)ModuleBase,ModuleSize);
DbgPrint("AvlBoot:Avl_krnl Base=%x\n",Avl_krnl);
ExFreePool(Buffer);
}
After initialization, other components can access Avlboot.sys to obtain information about the pseudo-kernel and the original kernel.
2. Borrowed Virtualization
Here, I will briefly introduce the steps of Intel VT hardware virtualization:
1. Check the VT environment 2. Enable VT functionality 3. Fill VT, storing the virtual machine state 4. Set the items to intercept 5. Set the return address when exiting the virtual machine 6. Start the virtual machine and wait for the interception items to trigger 7. When the interception item is triggered, enter the relevant handling routine 8. Restore the virtual machine to continue running Next are some troublesome codes:
/*
Vmx Initialization
*/
NTSTATUS NTAPI VmxInitialize (
PCPU Cpu,
PVOID GuestEip,
PVOID GuestEsp
)
{
PHYSICAL_ADDRESS AlignedVmcsPA;
ULONG VaDelta;
NTSTATUS Status;
// Allocate memory space for VMXON region
Cpu->Vmx.OriginaVmxonR = MmAllocateContiguousPages(
VMX_VMXONR_SIZE_IN_PAGES,
&Cpu->Vmx.OriginalVmxonRPA);
if (!Cpu->Vmx.OriginaVmxonR)
{
DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
DbgPrint("VmxInitialize(): OriginaVmxonR VA: 0x%x\n", Cpu->Vmx.OriginaVmxonR);
DbgPrint("VmxInitialize(): OriginaVmxonR PA: 0x%llx\n", Cpu->Vmx.OriginalVmxonRPA.QuadPart);
// Allocate memory space for VMCS
Cpu->Vmx.OriginalVmcs = MmAllocateContiguousPages(
VMX_VMCS_SIZE_IN_PAGES,
&Cpu->Vmx.OriginalVmcsPA);
if (!Cpu->Vmx.OriginalVmcs)
{
DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
DbgPrint("VmxInitialize(): Vmcs VA: 0x%x\n", Cpu->Vmx.OriginalVmcs);
DbgPrint("VmxInitialize(): Vmcs PA: 0x%llx\n", Cpu->Vmx.OriginalVmcsPA.QuadPart);
// Enable VMX
if (!NT_SUCCESS (VmxEnable (Cpu->Vmx.OriginaVmxonR)))
{
DbgPrint("VmxInitialize(): Failed to enable Vmx\n");
return STATUS_UNSUCCESSFUL;
}
*((ULONG64 *)(Cpu->Vmx.OriginalVmcs)) =
(MsrRead (MSR_IA32_VMX_BASIC) & 0xffffffff); //set up vmcs_revision_id
// Fill VMCS structure
Status = VmxSetupVMCS (Cpu, GuestEip, GuestEsp);
if (!NT_SUCCESS (Status))
{
DbgPrint("VmxSetupVMCS() failed with status 0x%08hX\n", Status);
VmxDisable();
return Status;
}
DbgPrint("VmxInitialize(): Vmx enabled\n");
// Save EFER
Cpu->Vmx.GuestEFER = MsrRead (MSR_EFER);
DbgPrint("Guest MSR_EFER Read 0x%llx \n", Cpu->Vmx.GuestEFER);
// Save control registers
Cpu->Vmx.GuestCR0 = RegGetCr0 ();
Cpu->Vmx.GuestCR3 = RegGetCr3 ();
Cpu->Vmx.GuestCR4 = RegGetCr4 ();
CmCli ();
return STATUS_SUCCESS;
}
…
3. Painful Interception Handling
Handling sysenter: Since Hardware Virtualization (HVM) cannot directly intercept the sysenter instruction, we can only use other methods to gain control. There are three methods:
1. Write cpuid, int3, etc., at the head of kifastcallentery to enter the VM using interrupts or privileged instructions. 2. Use debug registers to trigger hardware interrupts under kifastcallentery, entering the VM using interrupts. 3. After entering the VMM, directly modify the guest’s sysenter_eip address, deceiving other programs accessing MSR by controlling MSR read/write. To avoid memory detection and fully utilize debug registers, Avalon uses the third option to control the execution flow after the process executes sysenter. Partial code:
static BOOLEAN NTAPI VmxDispatchMsrRead (
PCPU Cpu,
PGUEST_REGS GuestRegs,
PNBP_TRAP Trap,
BOOLEAN WillBeAlsoHandledByGuestHv
)
{
...
switch (ecx) {
case MSR_IA32_SYSENTER_CS:
MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_CS);
break;
case MSR_IA32_SYSENTER_ESP:
MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_ESP);
break;
case MSR_IA32_SYSENTER_EIP:
MsrValue.QuadPart = Avlkrnlinfo->SysenterAddr;
break;
...
}
IDT Redirection Handling Methods:
1. IDT address deception 2. IDT simulated delivery. The first method involves intercepting the sidt and lidt instructions to fill in a forged IDT address to mislead the accessors (which is relatively stable for system delivery). The second method involves simulating the IDT processing by writing a program to deliver the IDT. Since the first method requires using a disassembly engine to analyze the specific saved address, which is too large, this version of Avalon uses the second method, i.e., IDT simulated delivery. Partial code:
static BOOLEAN NTAPI VmxDispatchException (
PCPU Cpu,
PGUEST_REGS GuestRegs,
PNBP_TRAP Trap,
BOOLEAN WillBeAlsoHandledByGuestHv
)
{
...
// SETP 7. SET EIP
if((uIntrInfo & 0xff) == 1){
ComPrint("VmxDispatchException():#BD hit /n");
VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[0]);
}
else if ((uIntrInfo & 0xff) == 3){
ComPrint("VmxDispatchException():#BP hit /n");
VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[1]);
}
...
}
Chapter 3: The Twilight of the Ideal Land
1. Detection of Avalon
For programs based on Hardware Virtualization (HVM), the first thought is to directly detect and counteract hardware virtualization. Detection methods for hardware virtualization mainly include: EFER detection, VME detection. For VMMs that handle interrupts, they can also determine whether they are in a virtual machine by calculating the time difference before and after the interrupt. Of course, there are other detection methods specific to Avalon (omitted here).
2. Future Updates
Avalon has just begun, and its functionality is not yet complete; there are many features I want to add:
1. Move the kernel into EPT (NPT) so that you cannot see it at all. 2. Interact with ring 3 programs… 3. Other hidden features.
Conclusion
Avalon is just the tip of the iceberg in the application of hardware virtualization, and there are more applications waiting for us to explore. My skills are limited, and I will continue to seek advice from experts and strive to learn more.
The testing environment for this bin is as follows:
-
bochs 2.4.5
-
windows xp sp3
Note: This bin is just a simple sample; running it on real machines will cause a blue screen, and it only targets ring 0 interrupts, with three bugs in ring 3 that have not been fixed.
Attachment Contents (bin + source + bin = cake + red bean paste + cake, click at the end of the articleRead the original text to obtain the attachment):
-
avlboot.sys
-
avalonreader (source code for reading avlboot information)
-
avalon_vt.sys
– End –
Kanxue ID: Three-Inch Mage
https://bbs.pediy.com/user-135566.htm
This article is original by Three-Inch Mage from Kanxue Forum
Please indicate the source from Kanxue Community when reprinting
Recommended Books:
ClickBuy Now!
Popular Technical Articles Recommended:
-
Analysis of Wanna Cry by Newbies
-
Overview of Key Windows Kernel Data Structures (Part 1)
-
Overview of Key Windows Kernel Data Structures (Part 2)
-
Understanding Smart Homes, Revealing Vulnerabilities in Simple IoT Products
Official WeChat ID: ikanxue Official Weibo: Kanxue Security
Business Cooperation: [email protected]