简介
unicorn框架qemu之Hyper-v模式比较
跨平台模拟执行unicorn框架和上层qiling框架都是基于qemu的TCG模式(Tiny Code Generator),支持无硬件虚拟化支持方式在源ISA(处理器架构)和目标ISA不同的情况下CPU指令模拟,类似一个jit解释器,一个循环中不断的读入源ISA程序指令,QEMU先转换成源ISA的IR,反汇编并用代码在目标ISA编译后的IR在模拟TranslationBlock指令中执行,当然这些指令也是转换后的汇编模式比起直接调用c函数模拟可以优化效率,qemu对TranslationBlock在分支执行返回后切换到Qemu上下文保存虚拟环境状态继续下个分支执行,转换过程采用内联汇编的方式支持hook断点与内存监视trace等功能。
内存管理分析
HRESULT WhSeMapHostToGuestVirtualMemory(whpx_state *Partition, uintptr_t HostVa,
uintptr_t *GuestVa, size_t Size,
WHSE_MEMORY_ACCESS_FLAGS Flags)
{
auto size = ALIGN_UP(Size);
PWHSE_ALLOCATION_NODE existingNode = nullptr;
auto hresult =
WhSeFindAllocationNodeByGva(Partition, *GuestVa, &existingNode);
uintptr_t suggestedGva = 0;
if (*GuestVa == 0 || existingNode != nullptr) {
auto hresult = WhSiSuggestVirtualAddress(
Partition, size, &suggestedGva, Partition->VirtualProcessor.Mode);
} else
suggestedGva = ALIGN(*GuestVa);
existingNode = nullptr;
hresult = WhSeFindAllocationNodeByGva(Partition, suggestedGva, &existingNode);
auto startingGva = ALIGN(suggestedGva);
auto endingGva = ALIGN_UP(startingGva + size);
uintptr_t suggestedGpa = 0;
hresult = WhSiSuggestPhysicalAddress(Partition, size, &suggestedGpa);
WHSE_ALLOCATION_NODE node{.BlockType =
MEMORY_BLOCK_TYPE::MemoryBlockVirtual,
.HostVirtualAddress = HostVa,
.GuestPhysicalAddress = suggestedGpa,
.GuestVirtualAddress = startingGva,
.Size = size};
hresult = WhSeInsertAllocationTrackingNode(Partition, node);
// Setup matching PTEs
for (auto gva = startingGva, page = suggestedGpa; gva < endingGva;
gva += PAGE_SIZE, page += PAGE_SIZE) {
hresult = WhSiInsertPageTableEntry(Partition, gva, page);
hresult = ::WHvMapGpaRange(
Partition->partition, reinterpret_cast<PVOID>(HostVa),
static_cast<WHV_GUEST_PHYSICAL_ADDRESS>(suggestedGpa), size, Flags);
*GuestVa = startingGva;
return hresult;
}
HRESULT WhSiInsertPageTableEntry(whpx_state *Partition,
uintptr_t VirtualAddress,
uintptr_t PhysicalAddress)
{
// "Explode" the VA into translation indexes
uint16_t pml4Idx;
uint16_t pdpIdx;
uint16_t pdIdx;
uint16_t ptIdx;
uint16_t phyOffset;
auto hresult = WhSiDecomposeVirtualAddress(
VirtualAddress, &pml4Idx, &pdpIdx, &pdIdx, &ptIdx, &phyOffset);
// Search entry in PML4
auto pml4e = reinterpret_cast<PMMPTE_HARDWARE>(
Partition->MemoryLayout.Pml4HostVa)[pml4Idx];
if (pml4e.Valid == FALSE) {
// Shouldn't happen as we initialized all PLM4 entries upfront
return HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR);
}
// Search entry in Page Directory Pointers
uintptr_t pdpHva = 0;
hresult = WhSpLookupHVAFromPFN(Partition, pml4e.PageFrameNumber, &pdpHva);
auto pdp = reinterpret_cast<PMMPTE_HARDWARE>(pdpHva);
auto pdpe = pdp[pdpIdx];
if (pdpe.Valid == FALSE) {
// Allocate a Page Directory page
//
hresult = WhSpInsertPageTableEntry(Partition, pdp, pdpIdx);
pdpe = pdp[pdpIdx];
}
// Search entry in Page Directories
uintptr_t pdHva = 0;
hresult = WhSpLookupHVAFromPFN(Partition, pdpe.PageFrameNumber, &pdHva);
if (FAILED(hresult))
return hresult;
auto pd = reinterpret_cast<PMMPTE_HARDWARE>(pdHva);
auto pde = pd[pdIdx];
if (pde.Valid == FALSE) {
// Allocate a Page Table page
hresult = WhSpInsertPageTableEntry(Partition, pd, pdIdx);
pde = pd[pdIdx];
}
// Add entry in Page Tables
uintptr_t ptHva = 0;
hresult = WhSpLookupHVAFromPFN(Partition, pde.PageFrameNumber, &ptHva);
if (FAILED(hresult))
return hresult;
auto pt = reinterpret_cast<PMMPTE_HARDWARE>(ptHva);
auto ppte = &pt[ptIdx];
if (ppte->Valid == FALSE) {
/*PWHSE_ALLOCATION_NODE found = nullptr;
hresult = WhSeFindAllocationNodeByGpa( Partition, PhysicalAddress,
&found ); if ( hresult != HRESULT_FROM_WIN32( ERROR_NOT_FOUND ) &&
FAILED( hresult ) ) return hresult;
// Create a valid PTE
MMPTE_HARDWARE pte{};
pte.AsUlonglong = 0; // Ensure zeroed
pte.Valid = 1; // Intel's Present bit
pte.Write = 1; // Intel's Read/Write bit
pte.Owner = 1; // Intel's User/Supervisor bit, let's say it is a user
// accessible frame
pte.PageFrameNumber =
(PhysicalAddress / PAGE_SIZE); // Physical address of PDP page
*ppte = pte;
WHSE_ALLOCATION_NODE node{.BlockType =
MEMORY_BLOCK_TYPE::MemoryBlockPte,
.HostVirtualAddress = 0,
.GuestPhysicalAddress = PhysicalAddress,
.GuestVirtualAddress = 0,
.Size = PAGE_SIZE};
hresult = WhSeInsertAllocationTrackingNode(Partition, node);
}
return S_OK;
}
原WinHvShellcodeEmulator项目默认配置不支持xmm寄存器指令,解决方法是需要开启cr4的OSXSAVE位和xcr的XSTATE相关位,开启后就可以正常执行sse指令集了。
先设置cr4的这些位
#define CR4_OSXSAVE_MASK (1U << 18)
#define CR4_OSFXSR_SHIFT 9
#define CR4_OSFXSR_MASK (1U << CR4_OSFXSR_SHIFT)
#define CR4_OSXMMEXCPT_MASK (1U << 10)
RegisterName = WHvX64RegisterCr4;
uint64_t cr4val = 0;
whpx_get_reg(RegisterName, &cr4val);
cr4val = (cr4val | (1ULL << 5)) & ~(1 << 24);
cr4val |= CR4_OSXSAVE_MASK;
cr4val |= CR4_OSFXSR_MASK;
cr4val |= CR4_OSXMMEXCPT_MASK;
whpx_set_reg(RegisterName, cr4val);
//再设置WHvX64RegisterXCr0的这些位
#define XSTATE_FP_BIT 0
#define XSTATE_SSE_BIT 1
#define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT)
#define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT)
WHV_REGISTER_VALUE xcr0;
WHV_REGISTER_NAME xcr0_name = WHvX64RegisterXCr0;
if (!whpx_has_xsave()) {
return;
}
env->xcr0 |= XSTATE_FP_MASK;
env->xcr0 |= XSTATE_SSE_MASK;
/* Only xcr0 is supported by the hypervisor currently */
xcr0.Reg64 = env->xcr0;
hr = WHvSetVirtualProcessorRegisters(whpx->partition, whpx->cpu_index,
&xcr0_name, 1, &xcr0);
调试器功能开发
#define RT_BIT_64(bit) (UINT64_C(1) << (bit))
#define RT_BIT_64_FIND(val, bit) (val & (UINT64_C(1) << (bit)))
#define RT_BIT_64_SLOT(bit) (UINT64_C(1) << (bit << 1))
#define RT_BIT_64_FIND_SLOT(val, bit) (val & (UINT64_C(1) << (bit << 1)))
static void
whpx_apply_hardware_breakpoint(struct whpx_breakpoint_collection *breakpoints,
CPUState *cpu, uintptr_t addrskip)
{
uint8_t hwbpslot = 0;
uint64_t dr7val=0;
uint64_t dr7valrw = 0;
for (int i = 0; i < breakpoints->used; i++) {
struct whpx_breakpoint *breakpoint = &breakpoints->data[i];
WhpxBreakpointState state = breakpoint->state;
if (breakpoint->bptype & 0xff0000) {
if (state == WHPX_BP_SET_PENDING) {
for (uint8_t j = 0; j < 4; j++) {
//如果有使用槽置位详见源码
if (!RT_BIT_64_FIND_SLOT(dr7val, j)) {
breakpoint->original_instruction = j;
hwbpslot |= RT_BIT_64(breakpoint->original_instruction);
whpx_set_reg(WHvX64RegisterDr0+j, breakpoint->address);
}
}
if (breakpoint->bptype == UC_HOOK_HARDWARE_READ) {
dr7valrw |=
RT_BIT_64(breakpoint->original_instruction << 2);
dr7valrw |=
RT_BIT_64((breakpoint->original_instruction << 2) + 1);
}
if (breakpoint->bptype == UC_HOOK_HARDWARE_WRITE) {
dr7valrw |=
RT_BIT_64(breakpoint->original_instruction << 2);
}
breakpoint->state = WHPX_BP_SET;
}
}
}
dr7val = 0;
if (hwbpslot) {
for (uint8_t j = 0; j < 4; j++) {
if (hwbpslot & RT_BIT_64(j)) {
dr7val |= (RT_BIT_64_SLOT(j));
}
}
dr7val |= dr7valrw << 16;
//启用大标志
dr7val |= RT_BIT_64(8);
}
whpx_set_reg(WHvX64RegisterDr7, dr7val);
}
编译方式
uc_err err = uc_open(UC_ARCH_X86_WHPX, UC_MODE_64, &uc);
运行效果
相关引用
参与贡献
看雪ID:王cb
https://bbs.kanxue.com/user-home-609565.htm
# 往期推荐
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):Windows Hypervisor Platform魔改版Unicorn Engine