zhouhe VT(九) 初识无痕Hook & 隐藏页面保护函数

VT在调试与反调试领域有很大的作用,今天我们利用VT 隐藏系统函数
大体思路就是,通过Host接管错误号,让页面保护函数被读写的时候显示为空,在执行的时候正常显示
 

一、获取页面保护的函数地址

0
E号中断对应的函数就是页面保护函数
其物理地址为 541450
0
 

二、查看是否支持不可读不可写但可执行页面

Intel 第三卷手册A.10
A.10
0
 
通过查IA32_VMX_EPT_VPID_CAP(index 48CH)寄存器的第0位 ,看cpu支不支持一个可执行但不可读不可写的页面
0
我的是支持的
 

三、构造假页面让Host去接管

接管的时候根据退出号的位数,进一步判断页面是又什么原因引起的
 
Table 27.7 EPT Violations 记录了产生不同位数错误号的原因
0
 

四、代码如下

ept.c
#include <ntddk.h> #define PAGE_SIZE 0x1000 ULONG64* ept_PML4T; static PVOID *pagesToFree; static int index = 0; void initEptPagesPool() { pagesToFree = ExAllocatePoolWithTag(NonPagedPool, 12*1024*1024, 'ept'); if(!pagesToFree) __asm int 3 RtlZeroMemory(pagesToFree, 12*1024*1024); //12Mb// } static ULONG64* AllocateOnePage() { PVOID page; page = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'ept'); if(!page) __asm int 3 RtlZeroMemory(page, PAGE_SIZE); pagesToFree[index] = page; index++; return (ULONG64 *)page; } //extern PULONG test_data; //PHYSICAL_ADDRESS hook_pa; //ULONG64 *hook_ept_pt; ULONG64 fake_page_pa; ULONG64* hook_ept_PT; void AllocateFakedPage() { PVOID page; page = AllocateOnePage(); fake_page_pa = MmGetPhysicalAddress(page).QuadPart; } ULONG64* MyEptInitialization() { ULONG64 *ept_PDPT, *ept_PDT, *ept_PT; PHYSICAL_ADDRESS FirstPtePA, FirstPdePA, FirstPdptePA; int a, b, c; initEptPagesPool(); AllocateFakedPage();//制造假页面// ept_PML4T = AllocateOnePage(); //PML4E// ept_PDPT = AllocateOnePage(); //PDPTE// FirstPdptePA = MmGetPhysicalAddress(ept_PDPT); *ept_PML4T = (FirstPdptePA.QuadPart) + 7; //加权限//分页的后12位的属性//可读可写可执行// for (a = 0; a < 4; a++) { ept_PDT = AllocateOnePage(); FirstPdePA = MmGetPhysicalAddress(ept_PDT); *ept_PDPT = (FirstPdePA.QuadPart) + 7; ept_PDPT++; for (b = 0; b < 512; b++) { ept_PT = AllocateOnePage(); FirstPtePA = MmGetPhysicalAddress(ept_PT); *ept_PDT = (FirstPtePA.QuadPart) + 7; ept_PDT++; for (c = 0; c < 512; c++) { *ept_PT = ((a << 30) | (b << 21) | (c << 12) | 0x37) & 0xFFFFFFFF; //PTE是加的0x37// 可读可写可执行,有缓存,由于有的地址要求有缓存,方便起见,这里全设置成了0x37,虽然这样会影响CPU的性能// if( (0x541000 == ((a << 30) | (b << 21) | (c << 12)) & 0xffffffff)) { *ept_PT = ((a << 30) | (b << 21) | (c << 12) | 0x34) & 0xFFFFFFFF;//只可执行//当执行它的时候会进入Host// hook_ept_PT = ept_PT; } ept_PT++; } } } return ept_PML4T; } void MyEptFree() //由于直接通过物理地址转虚拟地址不好转,使用全局数组的方式,记录虚拟地址// { int i; for (i = 0; i < index; i++) { ExFreePool(pagesToFree[i]); } ExFreePool(pagesToFree); index = 0; }

vtsystem.c

#include "vtsystem.h" #include "vtasm.h" #include "exithandler.h" VMX_CPU g_VMXCPU; extern ULONG g_ret_esp; extern ULONG g_ret_eip; extern ULONG64* ept_PML4T; static ULONG VmxAdjustControls(ULONG Ctl, ULONG Msr) { LARGE_INTEGER MsrValue; MsrValue.QuadPart = Asm_ReadMsr(Msr); Ctl &= MsrValue.HighPart; /* bit == 0 in high word ==> must be zero */ Ctl |= MsrValue.LowPart; /* bit == 1 in low word ==> must be one */ return Ctl; } void __declspec(naked) GuestEntry() //guest入口// { //加载段选择子与基址// __asm { mov ax, es mov es, ax mov ax, ds mov ds, ax mov ax, fs mov fs, ax mov ax, gs mov gs, ax mov ax, ss mov ss, ax } __asm { mov esp,g_ret_esp jmp g_ret_eip } } void SetupVMCS() { ULONG GdtBase, IdtBase; ULONG64 EPTP; GdtBase = Asm_GetGdtBase(); IdtBase = Asm_GetIdtBase(); //1、Guest state fields Vmx_VmWrite(GUEST_CR0, Asm_GetCr0()); Vmx_VmWrite(GUEST_CR3, Asm_GetCr3()); Vmx_VmWrite(GUEST_CR4, Asm_GetCr4()); Vmx_VmWrite(GUEST_DR7, 0x400);//Dr7调试寄存器默认就是0x400// Vmx_VmWrite(GUEST_RFLAGS, Asm_GetEflags() & ~0x200); //eflag寄存器关中断//cli Vmx_VmWrite(GUEST_ES_SELECTOR, Asm_GetEs() & 0xFFF8); Vmx_VmWrite(GUEST_CS_SELECTOR, Asm_GetCs() & 0xFFF8); Vmx_VmWrite(GUEST_DS_SELECTOR, Asm_GetDs() & 0xFFF8); Vmx_VmWrite(GUEST_FS_SELECTOR, Asm_GetFs() & 0xFFF8); Vmx_VmWrite(GUEST_GS_SELECTOR, Asm_GetGs() & 0xFFF8); Vmx_VmWrite(GUEST_SS_SELECTOR, Asm_GetSs() & 0xFFF8); Vmx_VmWrite(GUEST_TR_SELECTOR, Asm_GetTr() & 0xFFF8); Vmx_VmWrite(GUEST_ES_AR_BYTES, 0x10000); //Intel手册第三卷 Table 24-2 把这些段选择子设为不可用的段选择子需要第16位置1// Vmx_VmWrite(GUEST_FS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性// Vmx_VmWrite(GUEST_DS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性// Vmx_VmWrite(GUEST_SS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性// Vmx_VmWrite(GUEST_GS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性// Vmx_VmWrite(GUEST_LDTR_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性// Vmx_VmWrite(GUEST_CS_AR_BYTES, 0xc09b); //直接读的1号段描述符// Vmx_VmWrite(GUEST_CS_BASE, 0); Vmx_VmWrite(GUEST_CS_LIMIT, 0xffffffff); Vmx_VmWrite(GUEST_TR_AR_BYTES, 0x008b); //5号// Vmx_VmWrite(GUEST_TR_BASE, 0x80042000); Vmx_VmWrite(GUEST_TR_LIMIT, 0x20ab); Vmx_VmWrite(GUEST_GDTR_BASE, GdtBase); Vmx_VmWrite(GUEST_GDTR_LIMIT, Asm_GetGdtLimit()); Vmx_VmWrite(GUEST_IDTR_BASE, IdtBase); Vmx_VmWrite(GUEST_IDTR_LIMIT, Asm_GetIdtLimit()); //Vmx_VmWrite(GUEST_IA32_DEBUGCTL, Asm_ReadMsr(MSR_IA32_DEBUGCTL) & 0xFFFFFFFF); //Vmx_VmWrite(GUEST_IA32_DEBUGCTL_HIGH, Asm_ReadMsr(MSR_IA32_DEBUGCTL) >> 32); Vmx_VmWrite(GUEST_SYSENTER_CS, Asm_ReadMsr(MSR_IA32_SYSENTER_CS) & 0xFFFFFFFF); Vmx_VmWrite(GUEST_SYSENTER_ESP, Asm_ReadMsr(MSR_IA32_SYSENTER_ESP) & 0xFFFFFFFF); Vmx_VmWrite(GUEST_SYSENTER_EIP, Asm_ReadMsr(MSR_IA32_SYSENTER_EIP) & 0xFFFFFFFF); // KiFastCallEntry Vmx_VmWrite(GUEST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x1000); //Guest 临时栈 //上半4kb给guest使用// Vmx_VmWrite(GUEST_RIP, (ULONG)GuestEntry); // 客户机的入口点 Vmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff); //不使用shadow vmcs,都置成f// Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff); //不使用shadow vmcs,都置成f// //2、Host state fields Vmx_VmWrite(HOST_CR0, Asm_GetCr0()); Vmx_VmWrite(HOST_CR3, Asm_GetCr3()); Vmx_VmWrite(HOST_CR4, Asm_GetCr4()); Vmx_VmWrite(HOST_ES_SELECTOR, Asm_GetEs() & 0xFFF8); //Intel规定后三位必须为0// Vmx_VmWrite(HOST_CS_SELECTOR, Asm_GetCs() & 0xFFF8); Vmx_VmWrite(HOST_DS_SELECTOR, Asm_GetDs() & 0xFFF8); Vmx_VmWrite(HOST_FS_SELECTOR, Asm_GetFs() & 0xFFF8); Vmx_VmWrite(HOST_GS_SELECTOR, Asm_GetGs() & 0xFFF8); Vmx_VmWrite(HOST_SS_SELECTOR, Asm_GetSs() & 0xFFF8); Vmx_VmWrite(HOST_TR_SELECTOR, Asm_GetTr() & 0xFFF8); Vmx_VmWrite(HOST_TR_BASE, 0x80042000); //tr寄存器在5号段选择子上,直接查,为0x80042000// 直接填充为tr的基地址// Vmx_VmWrite(HOST_GDTR_BASE, GdtBase); Vmx_VmWrite(HOST_IDTR_BASE, IdtBase); Vmx_VmWrite(HOST_IA32_SYSENTER_CS, Asm_ReadMsr(MSR_IA32_SYSENTER_CS) & 0xFFFFFFFF); Vmx_VmWrite(HOST_IA32_SYSENTER_ESP, Asm_ReadMsr(MSR_IA32_SYSENTER_ESP) & 0xFFFFFFFF); Vmx_VmWrite(HOST_IA32_SYSENTER_EIP, Asm_ReadMsr(MSR_IA32_SYSENTER_EIP) & 0xFFFFFFFF); // KiFastCallEntry Vmx_VmWrite(HOST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x2000); //Host 临时栈 //为后面的中断单独分配一个栈,防止线程切换的时候影响到后续程序的执行// 栈顶 Vmx_VmWrite(HOST_RIP, (ULONG)VMMEntryPoint); //这里定义我们的VMM处理程序入口 //3、vm-control fields // 3.1、vm execution control Vmx_VmWrite(PIN_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS)); //Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0, MSR_IA32_VMX_PROCBASED_CTLS)); //for EPT Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0x80000000, MSR_IA32_VMX_PROCBASED_CTLS)); Vmx_VmWrite(SECONDARY_VM_EXEC_CONTROL, VmxAdjustControls(0x2, MSR_IA32_VMX_PROCBASED_CTLS2)); EPTP = MmGetPhysicalAddress(ept_PML4T).QuadPart; Vmx_VmWrite(EPT_POINTER, (EPTP | 6 | (3 << 3)) & 0xFFFFFFFF); Vmx_VmWrite(EPT_POINTER_HIGH, (EPTP | 6 | (3 << 3)) >> 32); //for EPT with PAE Vmx_VmWrite(GUEST_PDPTR0, MmGetPhysicalAddress((PVOID)0xc0600000).LowPart | 1); Vmx_VmWrite(GUEST_PDPTR0_HIGH, MmGetPhysicalAddress((PVOID)0xc0600000).HighPart); Vmx_VmWrite(GUEST_PDPTR1, MmGetPhysicalAddress((PVOID)0xc0601000).LowPart | 1); Vmx_VmWrite(GUEST_PDPTR1_HIGH, MmGetPhysicalAddress((PVOID)0xc0601000).HighPart); Vmx_VmWrite(GUEST_PDPTR2, MmGetPhysicalAddress((PVOID)0xc0602000).LowPart | 1); Vmx_VmWrite(GUEST_PDPTR2_HIGH, MmGetPhysicalAddress((PVOID)0xc0602000).HighPart); Vmx_VmWrite(GUEST_PDPTR3, MmGetPhysicalAddress((PVOID)0xc0603000).LowPart | 1); Vmx_VmWrite(GUEST_PDPTR3_HIGH, MmGetPhysicalAddress((PVOID)0xc0603000).HighPart); // 3.2、vm exit control Vmx_VmWrite(VM_EXIT_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_EXIT_CTLS)); // 3.3、vm entry control Vmx_VmWrite(VM_ENTRY_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_ENTRY_CTLS)); } NTSTATUS StartVirtualTechnology() { _CR4 uCr4; _EFLAGS uEflags; if(!IsVTEnabled()) { return STATUS_UNSUCCESSFUL; } *((PULONG)&uCr4) = Asm_GetCr4(); uCr4.VMXE = 1; //第一步,开锁完成// Asm_SetCr4(*((PULONG)&uCr4)); g_VMXCPU.pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'tlsn');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k// RtlZeroMemory(g_VMXCPU.pVMXONRegion, 0x1000); *(PULONG)g_VMXCPU.pVMXONRegion = 1;// // g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMXONRegion);//virtual add to phy add// Vmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart,g_VMXCPU.pVMXONRegion_PA.HighPart);//第二步,开启VmxOn模式// *((PULONG)&uEflags) = Asm_GetEflags(); if(uEflags.CF != 0) { Log("Error: VMXON指令调用失败!", 0); ExFreePool(g_VMXCPU.pVMXONRegion); return STATUS_UNSUCCESSFUL; } Log("vmcon success", 0); //为虚拟机分配4kb// g_VMXCPU.pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmcs');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k// RtlZeroMemory(g_VMXCPU.pVMCSRegion, 0x1000); *(PULONG)g_VMXCPU.pVMCSRegion = 1;// g_VMXCPU.pVMCSRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMCSRegion);//virtual add to phy add// Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart); //第三步拔电源// //第四步,选中机器 (我们可能有很多虚拟机,我们要进行选择)// Vmx_VmPtrld(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart); g_VMXCPU.pStack = ExAllocatePoolWithTag(NonPagedPool, 0x2000, 'stck'); //// RtlZeroMemory(g_VMXCPU.pStack, 0x2000); Log("host_stack: ", g_VMXCPU.pStack); //第五步,设置vmcs(vmwrite) ,装机//设置大量寄存器信息// SetupVMCS(); //第六步... Vmx_VmLaunch(); //执行成功,直接跳走,不再向下执行,执行失败,EFLAG的CF或ZF位置1// VM - instruction错误字段将会包含错误代码// Log("ERROR: VmLaunch指令调用失败!!!!!!",Vmx_VmRead(VM_INSTRUCTION_ERROR)); //从错误字段中读取错误号// Intel第三卷 30-4中可以查看错误号// return STATUS_SUCCESS; } extern ULONG g_vmcall_arg; extern ULONG g_stop_esp, g_stop_eip;; NTSTATUS StopVirtualTechnology() {ULONG GdtBase; _CR4 uCr4; g_vmcall_arg = 'EXIT'; __asm { pushad pushfd mov g_stop_esp,esp mov g_stop_eip,offset STOP_EIP } Vmx_VmCall(); //关柜门 这一指令,guest主机是没有权限执行的// __asm { STOP_EIP: popfd popad } *((PULONG)&uCr4) = Asm_GetCr4(); uCr4.VMXE = 0; //关锁// Asm_SetCr4(*((PULONG)&uCr4)); ExFreePool(g_VMXCPU.pVMXONRegion);//在内核分配的内存需要我们自己手动释放// ExFreePool(g_VMXCPU.pVMCSRegion); ExFreePool(g_VMXCPU.pStack); GdtBase = Asm_GetGdtBase(); DbgPrint("GdtBase : %p\n", GdtBase); DbgPrint("VT Stop"); return STATUS_SUCCESS; } BOOLEAN IsVTEnabled() //判断主机能不能开启vt// { ULONG uRet_EAX, uRet_ECX, uRet_EDX, uRet_EBX; _CPUID_ECX uCPUID; _CR0 uCr0; _CR4 uCr4; IA32_FEATURE_CONTROL_MSR msr; //1. CPUID Asm_CPUID(1, &uRet_EAX, &uRet_EBX, &uRet_ECX, &uRet_EDX); *((PULONG)&uCPUID) = uRet_ECX; if (uCPUID.VMX != 1) { Log("ERROR: 这个CPU不支持VT!",0); return FALSE; } // 2. MSR *((PULONG)&msr) = (ULONG)Asm_ReadMsr(MSR_IA32_FEATURE_CONTROL); if (msr.Lock!=1) { Log("ERROR:VT指令未被锁定!",0); return FALSE; } // 3. CR0 CR4 *((PULONG)&uCr0) = Asm_GetCr0(); *((PULONG)&uCr4) = Asm_GetCr4(); if (uCr0.PE != 1 || uCr0.PG!=1 || uCr0.NE!=1) { Log("ERROR:这个CPU没有开启VT!",0); return FALSE; } if (uCr4.VMXE == 1) { Log("ERROR:这个CPU已经开启了VT!",0); Log("可能是别的驱动已经占用了VT,你必须关闭它后才能开启。",0); return FALSE; } Log("SUCCESS:这个CPU支持VT!",0); return TRUE; }

 

五、实验

驱动加载前的xuetr
0
驱动加载后的xuetr
0
0
xuetr误以为这些函数是钩子,并读取不到函数的反汇编代码,甚至Windbg也读取不到。
 
改节代码链接
 
 
 

__EOF__

本文作者_TLSN
本文链接https://www.cnblogs.com/lordtianqiyi/articles/16157872.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   TLSN  阅读(941)  评论(1编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示