[2022] hypervisor: 检测与预防 (一) huoji hypervisor,SVM,intel 2022-05-12 1479 次浏览 0 次点赞 ### rdtsc 大部分虚拟机包括沙箱用的都存在一个问题 cpuid等一些会造成vmexit的东西, 被硬件加速了 不开虚拟机的时候 只花很少的时间,大于200个CPU周期左右 一旦进了虚拟机 会到大约20000个周期不等 木马 外挂 可以通过判断CPU周期确定自己是不是在虚拟机里 导致沙箱不存活之类的 如 https://github.com/huoji120/DuckSandboxDetect/blob/main/ConsoleApplication1/ConsoleApplication1.cpp#L25 因此我们要在进入虚拟化后,算一下退出所消耗的周期,然后在tsc_offset减去对应的值即可 ### 不可信参数 host不能接受不可信参数,比如xsetbv如果给0会造成PG. 可以通过try catch预防 但是很多manual map的虚拟机是没办法用try catch的,因此要么做idt shadow 要么老老实实按照xen的来检查惨 ### idt/gdt shadow host机不能跟guest机用同一个idt/gdt 需要自己自定义异常处理 异常处理可以支持嵌套异常处理 这是简单的异常处理代码: ```cpp uintptr_t idt::search_seh_jmp_rip(uintptr_t rip) { const auto exception_directory = &nt_headers->_optional_header._data_directory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; .... } void idt::seh_handler(_idt_stack_register_error_code* stack_reg) { if (global::cpu_type == _cpu_type::amd) { svm::svm_context->vcpu[function::get_current_cpu_num()]->last_error_code = stack_reg->error_code; } else { //intel.. } uintptr_t search_rip = stack_reg->rip; uintptr_t search_rsp = stack_reg->rsp; uintptr_t search_rbp = stack_reg->rbp; size_t frame = 0; DebugPrint("[hyperduck] search exception rip %p \n", search_rip); for (;;frame++) { if (search_rip < reinterpret_cast(MmSystemRangeStart) || search_rsp < reinterpret_cast(MmSystemRangeStart)) break; if (!search_rip || !MmIsAddressValid((PVOID)search_rip) || !MmIsAddressValid((PVOID)search_rip)) { break; } search_rip = search_seh_jmp_rip(search_rip); /* fix me : Because the add value of RSP is not calculated through assembly, generally speaking, one or two layers of simple functions can be used, and complex functions are not very good. If you want to be accurate, you need to traverse the assembly like Microsoft, and encounter the increase of add RSP Refer to rtlvirtualunwind for details In addition, the recovery of registers and stacks is not completely restored */ if (search_rip == 0) { search_rip = (uintptr_t)(*(uintptr_t*)search_rsp); search_rsp += 8; } else { break; } } if (search_rip == 0) { DebugPrint("[hyperduck] unhandle exception! %p \n", search_rip); function::bug_check(); } DebugPrint("[hyperduck] handle exception jmp to %p frame: %d \n", search_rip, frame); stack_reg->rip = search_rip; stack_reg->rsp = search_rsp; stack_reg->rbp = search_rbp; } ``` 这样你就可以把之前所说的不可信参数加一个检查 ```cpp __try { asm_xsetbv(xcr, xcr_value.QuadPart); //_xsetbv(xcr, xcr_value.QuadPart); } __except (EXCEPTION_EXECUTE_HANDLER) { inject_exception_gp_by_code(guest_context, guest_context->vcpu->last_error_code); return; } ``` 另外也能预防类似于NMI异常导致的vmexit暴露的问题 ### msr 对于AMD来说(intel也一样) 有些MSR是不能改的 比如amd64_efer 如果在虚拟机运行的时候改了会造成DF异常 因此需要shadow这些MSR ```cpp void svm_exit::svm_shadow_msr(ULARGE_INTEGER* fake_msr_value, bool is_read, uintptr_t origin_msr, _svm_guest_status* guest_context) { if (is_read) { if (fake_msr_value->QuadPart == NULL) { fake_msr_value->QuadPart = origin_msr; } guest_context->guest_register->Rax = fake_msr_value->LowPart; guest_context->guest_register->Rdx = fake_msr_value->HighPart; } else { fake_msr_value->LowPart = guest_context->guest_register->Rax & MAXUINT32; fake_msr_value->HighPart = guest_context->guest_register->Rdx & MAXUINT32; } } ``` 这些MSR在svm中需要注意: amd64_vmcr、amd64_tsx、amd64_efer ### tsx TSX指令会造成一个vmexit 但是这个vmexit是没有具体的vmexit code的,导致虚拟机崩溃 最好的办法是在CPUID部分给guest返回不支持RTM ```cpp switch (guest_context->guest_register->Rax) { case cpuid_get_extended_feature: _bittestandreset((long*)&cpuid_reg.rcx, amd64_cpuid_tsx_support); break; ``` 并且在TSX MSR上注入PG异常 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
大佬有没有推荐的bug少一点的虚拟机框架
KVM/XEN