我在搞一个AMD平台虚拟化用来练手的东西,我目前做到了进入退出虚拟机,准备做syscall hook,
但是新windows10有kva shadow情况导致不能跟以前一样改个msr_lstar这个地址了
所以搜索了一会在老外的推荐下,我就看了一下AMD的白皮书有一个是msr_efer.sce的字段,字段作用是控制syscall功能的.如果为0则关闭syscall和sysret,系统再用syscall/sysret的时候就会导致UD异常
这样syscall的时候就会走我的vmexit->svm_handle_exception_ud。现在唯一的问题就是模拟syscall
intel还好说,老外有现成源码,AMD就惨了,所以我按照amd的白皮书里面的伪码写syscall模拟流程
伪码在
https://www.amd.com/system/files/TechDocs/24594.pdf 470页
uintptr_t msr_value = _huoji_readmsr(amd64_star);
guest_context->guest_register->Rcx = guest_context->vcpu->svm_stack->guest_vmcb.control.NRip; //RCX.q = next_RIP
guest_context->guest_register->R11 = guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rflags &~ X86_FLAGS_RF; // R11.q = RFLAGS // with rf cleared
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsSelector = (UINT16)((msr_value >> 32) & ~3); //MSR_STAR.SYSCALL_CS
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsBase = 0;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsLimit = 0xFFFFFFFF;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsAttrib = Tools::get_segment_long_mode2type(guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsSelector, guest_context->vcpu->svm_stack->guest_vmcb.state_save.GdtrBase, 1, 0);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsSelector = (UINT16)(((msr_value >> 32) & ~3) + 8); //MSR_STAR.SYSCALL_CS + 8
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsBase = 0;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsLimit = 0xFFFFFFFF;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsAttrib = Tools::get_segment_long_mode2type(guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsSelector, guest_context->vcpu->svm_stack->guest_vmcb.state_save.GdtrBase, 1, 0);
msr_value = _huoji_readmsr(amd64_sfmask);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rflags &= ~(msr_value | X86_FLAGS_RF); //RFLAGS AND ~MSR_SFMASK , RFLAGS.RF = 0
msr_value = _huoji_readmsr(amd64_lstar);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rip = msr_value;
其中,MSR_STAR.SYSCALL_CS在amd白皮书里面说明如下(跟intel是一样的)
SYSCALL如下:
/*
模拟 SYSCALL:
*/
uintptr_t msr_value = _huoji_readmsr(amd64_star);
guest_context->guest_register->Rcx = guest_context->vcpu->svm_stack->guest_vmcb.control.NRip; //RCX.q = next_RIP
guest_context->guest_register->R11 = guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rflags &~ X86_FLAGS_RF; // R11.q = RFLAGS // with rf cleared
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsSelector = (UINT16)((msr_value >> 32) & ~3); //MSR_STAR.SYSCALL_CS
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsBase = 0;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsLimit = 0xFFFFFFFF;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsAttrib = Tools::get_segment_long_mode2type(guest_context->vcpu->svm_stack->guest_vmcb.state_save.CsSelector, guest_context->vcpu->svm_stack->guest_vmcb.state_save.GdtrBase, 1, 0);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsSelector = (UINT16)(((msr_value >> 32) & ~3) + 8); //MSR_STAR.SYSCALL_CS + 8
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsBase = 0;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsLimit = 0xFFFFFFFF;
guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsAttrib = Tools::get_segment_long_mode2type(guest_context->vcpu->svm_stack->guest_vmcb.state_save.SsSelector, guest_context->vcpu->svm_stack->guest_vmcb.state_save.GdtrBase, 1, 0);
msr_value = _huoji_readmsr(amd64_sfmask);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rflags &= ~(msr_value | X86_FLAGS_RF); //RFLAGS AND ~MSR_SFMASK , RFLAGS.RF = 0
msr_value = _huoji_readmsr(amd64_lstar);
guest_context->vcpu->svm_stack->guest_vmcb.state_save.Rip = msr_value;
DebugPrint("SYS CALL ! opcode %d == 1 rax %p r11 %p \n", op_code, guest_context->guest_register->Rcx, guest_context->guest_register->R11);
Tools::get_segment_long_mode2type如下:
unsigned short Tools::get_segment_long_mode2type(unsigned short segment_selector, unsigned __int64 gdt_base, unsigned __int16 long_mode, unsigned __int16 dpl)
{
_segment_descriptor* descriptor = reinterpret_cast<_segment_descriptor*>(gdt_base + (segment_selector & ~3));
_segment_attribute attribute;
attribute.Fields.Type = descriptor->fields.type;
attribute.Fields.System = descriptor->fields.system;
attribute.Fields.Dpl = dpl;
attribute.Fields.Present = descriptor->fields.present;
attribute.Fields.Avl = descriptor->fields.avl;
attribute.Fields.LongMode = long_mode;
attribute.Fields.DefaultBit = descriptor->fields.db;
attribute.Fields.Granularity = descriptor->fields.gran;
attribute.Fields.Reserved1 = 0;
return attribute.AsUInt16;
}
炸的位置我认为是cs和ss的attrib字段设置错误,不过实数不懂错在哪了,还请各位大佬指点一下
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课