I can't figure out how I can do a trace on an amd hypervisor. Is there something like eflags.tf in svm? so that I can trace the instructions
I initially thought that amd has something like intel, where I vmwrite in CPU_BASED_VM_EXEC_CONTROL(0x00004002) value CPU_BASED_MONITOR_TRAP_FLAG(0x08000000), but amd-v does not have this, and the tf flag does not work. I also found information that the rf flag can also affect this somehow, but I didn't understand how, since when I switch the rf flag to 1, nothing is intercepted in SvmExitHandler either.
Thus the main question: Can someone tell me how can I trace instructions in the case of amd-v? i.e., for example, initially I virtualize the entire system, after that, for example, I compiled a large application through the clang compiler with my optimization phases, then I need to trace it like:
In short, you need to intercept DB exception, and then process it in SvmExitHandler. Only I haven't figured out how to get out of it yet, because after turning on the tf flag, I'm staying there forever.
How can you do that:
https://github.com/HoShiMin/Kernel-Bridge
You need to Enable this interception
Private->Guest.ControlArea.InterceptExceptions.Bitmap.InterceptionVectorDB = TRUE;
and you can catch DB exception:
extern "C" VMM_STATUS SvmVmexitHandler(PRIVATE_VM_DATA* Private, GUEST_CONTEXT* Context, GUEST_SSE_CONTEXT* SSeContext, GUEST_AVX_CONTEXT* AvxContext)
{
// Load the host state:
__svm_vmload(reinterpret_cast<size_t>(Private->VmmStack.Layout.InitialStack.HostVmcbPa));
// Restore the guest's RAX that was overwritten by host's RAX on #VMEXIT:
Context->Rax = Private->Guest.StateSaveArea.Rax;
VMM_STATUS Status = VMM_STATUS::VMM_CONTINUE;
switch (Private->Guest.ControlArea.ExitCode)
{
case VMEXIT_CPUID:
{
CPUID_REGS Regs = {};
int Function = static_cast<int>(Context->Rax);
int SubLeaf = static_cast<int>(Context->Rcx);
__cpuidex(Regs.Raw, Function, SubLeaf);
switch (Function) {
case CPUID_VMM_SHUTDOWN:
{
// Shutdown was triggered:
Status = VMM_STATUS::VMM_SHUTDOWN;
break;
}
case CPUID::Generic::CPUID_MAXIMUM_FUNCTION_NUMBER_AND_VENDOR_ID:
{
Context->Rax = Regs.Regs.Eax;
GetHvCpuName(Context->Rbx, Context->Rcx, Context->Rdx);
break;
}
default:
{
Context->Rax = Regs.Regs.Eax;
Context->Rbx = Regs.Regs.Ebx;
Context->Rcx = Regs.Regs.Ecx;
Context->Rdx = Regs.Regs.Edx;
break;
}
}
break;
}
case VMEXIT_MSR:
{
if ((Context->Rcx & MAXUINT32) == static_cast<unsigned int>(AMD_MSR::MSR_EFER) && Private->Guest.ControlArea.ExitInfo1)
{
EFER Efer = {};
Efer.Value = ((Context->Rdx & MAXUINT32) << 32) | (Context->Rax & MAXUINT32);
if (!Efer.Bitmap.SecureVirtualMachineEnable)
{
InjectEvent(&Private->Guest, INTERRUPT_VECTOR::GeneralProtection, EXCEPTION_VECTOR::FaultTrapException, 0); // #GP (Vector = 13, Type = Exception)
break;
}
Private->Guest.StateSaveArea.Efer = Efer.Value;
}
break;
}
case VMEXIT_VMRUN:
{
InjectEvent(&Private->Guest, INTERRUPT_VECTOR::GeneralProtection, EXCEPTION_VECTOR::FaultTrapException, 0); // #GP (Vector = 13, Type = Exception)
break;
}
case VMEXIT_EXCP_DB://Trace, you can call this by TF flag switch to 1
{
KdPrint(("RIP %p\n", Private->Guest.StateSaveArea.Rip));
Private->Guest.StateSaveArea.Rax = Context->Rax;
return Status;
}
}
if (Status == VMM_STATUS::VMM_SHUTDOWN)
{
// We should to devirtualize this processor:
Context->Rax = reinterpret_cast<UINT64>(Private) & MAXUINT32; // Low part
Context->Rbx = Private->Guest.ControlArea.NextRip;
Context->Rcx = Private->Guest.StateSaveArea.Rsp;
Context->Rdx = reinterpret_cast<UINT64>(Private) >> 32; // High part
// Load the guest's state:
__svm_vmload(reinterpret_cast<size_t>(Private->VmmStack.Layout.InitialStack.GuestVmcbPa));
// Store the GIF - Global Interrupt Flag:
_disable();
__svm_stgi();
// Disable the SVM by resetting the EFER.SVME bit:
EFER Efer = {};
Efer.Value = __readmsr(static_cast<unsigned long>(AMD_MSR::MSR_EFER));
Efer.Bitmap.SecureVirtualMachineEnable = FALSE;
__writemsr(static_cast<unsigned long>(AMD_MSR::MSR_EFER), Efer.Value);
// Restoring the EFlags:
__writeeflags(Private->Guest.StateSaveArea.Rflags);
}
Private->Guest.StateSaveArea.Rax = Context->Rax;
// Go to the next instruction:
Private->Guest.StateSaveArea.Rip = Private->Guest.ControlArea.NextRip;
return Status;
}