saurvs / hypervisor-rs

Rust API to the OS X Hypervisor framework for hardware-accelerated virtualization
MIT License
62 stars 9 forks source link

Need a simple example #1

Open orklann opened 5 years ago

orklann commented 5 years ago

I used this crate, with the code below, and it seems there must be something wrong in using this crate, can you help?

extern crate hypervisor;

use hypervisor::vCPU;
use hypervisor::VMXCap;
use hypervisor::read_vmx_cap;
use hypervisor::consts::vmcs;
use hypervisor::consts::vmx_exit;
use hypervisor::map_mem;
use hypervisor::MemPerm;
use hypervisor::x86Reg;
use hypervisor::Error;

fn cap2ctrl(cap: u64, ctrl: u64) -> u64 {
    return ((ctrl) | ((cap) & 0xffffffff)) & ((cap) >> 32);
}

fn req(e: Error) {
    match e {
        Error::Success => println!("Exited OK"),
        _ => println!("Exited Error")
    }
}

const VMCS_PRI_PROC_BASED_CTLS_HLT: u64 = (1 << 7);
const VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD: u64 = (1 << 19);
const VMCS_PRI_PROC_BASED_CTLS_CR8_STORE: u64 = (1 << 20);

fn main() {
    hypervisor::create_vm();
    let cpu = vCPU::new().expect("Failed create vCPU");
    let cap_pinbased_type = VMXCap::PINBASED;
    let cap_procbased_type = VMXCap::PROCBASED;
    let cap_procbased2_type = VMXCap::PROCBASED2;
    let cap_entry_type = VMXCap::ENTRY;
    let cap_pinbased: u64;
    let cap_procbased: u64;
    let cap_procbased2: u64;
    let cap_entry: u64;
    cap_pinbased = read_vmx_cap(&cap_pinbased_type).expect("Reading cap error");
    cap_procbased = read_vmx_cap(&cap_procbased_type).expect("Reading cap error");
    cap_procbased2 = read_vmx_cap(&cap_procbased2_type).expect("Reading cap error");
    cap_entry = read_vmx_cap(&cap_entry_type).expect("Reading cap error");

    req(cpu.write_vmcs(vmcs::VMCS_CTRL_PIN_BASED, cap2ctrl(cap_pinbased, 0)));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CPU_BASED, cap2ctrl(cap_procbased,
        VMCS_PRI_PROC_BASED_CTLS_HLT |
        VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD |
        VMCS_PRI_PROC_BASED_CTLS_CR8_STORE)));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CPU_BASED2, cap2ctrl(cap_procbased2, 0) | (1 << 7)));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_VMENTRY_CONTROLS, cap2ctrl(cap_entry, 0)));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_EXC_BITMAP, 0xffffffff));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CR0_MASK, 0xffffffff));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CR0_SHADOW, 0xffffffff));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CR4_MASK, 0xffffffff));
    req(cpu.write_vmcs(vmcs::VMCS_CTRL_CR4_SHADOW, 0xffffffff));

    req(cpu.write_vmcs(vmcs::VMCS_GUEST_CS, 1 << 3));
    req(cpu.write_vmcs(vmcs::VMCS_GUEST_CS_AR, 0xc093));
    req(cpu.write_vmcs(vmcs::VMCS_GUEST_CS_LIMIT, 0xffffffff));
    cpu.write_vmcs(vmcs::VMCS_GUEST_CS_BASE, 0x0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_DS, 2 << 3);
    cpu.write_vmcs(vmcs::VMCS_GUEST_DS_AR, 0xc093);
    cpu.write_vmcs(vmcs::VMCS_GUEST_DS_LIMIT, 0xffffffff);
    cpu.write_vmcs(vmcs::VMCS_GUEST_DS_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_ES, 2 << 3);
    cpu.write_vmcs(vmcs::VMCS_GUEST_ES_AR, 0xc093);
    cpu.write_vmcs(vmcs::VMCS_GUEST_ES_LIMIT, 0xffffffff);
    cpu.write_vmcs(vmcs::VMCS_GUEST_ES_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_FS, 2 << 3);
    cpu.write_vmcs(vmcs::VMCS_GUEST_FS_AR, 0xc093);
    cpu.write_vmcs(vmcs::VMCS_GUEST_FS_LIMIT, 0xffffffff);
    cpu.write_vmcs(vmcs::VMCS_GUEST_FS_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_GS, 2 << 3);
    cpu.write_vmcs(vmcs::VMCS_GUEST_GS_AR, 0xc093);
    cpu.write_vmcs(vmcs::VMCS_GUEST_GS_LIMIT, 0xffffffff);
    cpu.write_vmcs(vmcs::VMCS_GUEST_GS_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_SS, 2 << 3);
    cpu.write_vmcs(vmcs::VMCS_GUEST_SS_AR, 0xc093);
    cpu.write_vmcs(vmcs::VMCS_GUEST_SS_LIMIT, 0xffffffff);
    cpu.write_vmcs(vmcs::VMCS_GUEST_SS_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_LDTR, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_LDTR_LIMIT, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_LDTR_AR, 0x10000);
    cpu.write_vmcs(vmcs::VMCS_GUEST_LDTR_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_TR, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_TR_LIMIT, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_TR_AR, 0x83);
    cpu.write_vmcs(vmcs::VMCS_GUEST_LDTR_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_GDTR_LIMIT, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_GDTR_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_IDTR_LIMIT, 0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_IDTR_BASE, 0);

    cpu.write_vmcs(vmcs::VMCS_GUEST_CR0, 0x20);
    cpu.write_vmcs(vmcs::VMCS_GUEST_CR3, 0x0);
    cpu.write_vmcs(vmcs::VMCS_GUEST_CR4, 0x2000);

    //     mov ax, 0x1234
    //     nop
    //     nop
    //     hlt

    let vm_mem: Vec<u8> = vec![0xB8, 0x34, 0x12, 0x90, 0x90, 0xF4]; 
    // ### I think i am doing wrong here?
    match map_mem(&vm_mem as &[u8], 0, &MemPerm::ExecAndWrite) {
        Error::Success => println!("map_mem() OK"),
        _ => println!("map_mem() error")
    }

    req(cpu.write_register(&x86Reg::RIP, 0));
    req(cpu.write_register(&x86Reg::RFLAGS, 0x2));
    req(cpu.write_register(&x86Reg::RAX, 0));

    loop {
        match cpu.run() {
            Error::Success => println!("Cpu running..."),
            _ => println!("Running cpu error")
        }
        let exit_reason = cpu.read_vmcs(vmcs::VMCS_RO_EXIT_REASON).expect("read vmcs error");
        println!("Exit reason: {:x}, {}", exit_reason, exit_reason);
        if exit_reason == vmx_exit::VMX_REASON_EPT_VIOLATION ||
             exit_reason == vmx_exit::VMX_REASON_IRQ {
                continue;
        }
        break;
    }

    let v = cpu.read_register(&x86Reg::RAX).expect("Read register error");
    println!("RAX = {:x}", v);
    let ip = cpu.read_register(&x86Reg::RIP).expect("Read register error");
    println!("RIP = {:x}", ip);
    println!("Done.");
}

RAX should be 0x90901234 RIP should be 0x5

orklann commented 5 years ago

The above code is ported from this C program, and this C program works as expected.

And here is the command line to build this program (Let's call it protect_mode.c):

clang++ -std=c++11 -framework Hypervisor -o protect_mode protect_mode.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Hypervisor/hv.h>
#include <Hypervisor/hv_arch_vmx.h>
#include <Hypervisor/hv_vmx.h>

#define req(x) { \
  hv_return_t ret = (x); \
  if (ret != HV_SUCCESS) { \
    printf("%s exited with code %d\n", #x, (int)ret); \
    exit(1); \
  } \
}

#define cap2ctrl(cap, ctrl) ((ctrl) | ((cap) & 0xffffffff)) & ((cap) >> 32)
#define VMCS_PRI_PROC_BASED_CTLS_HLT           (1 << 7)
#define VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD      (1 << 19)
#define VMCS_PRI_PROC_BASED_CTLS_CR8_STORE     (1 << 20)

int main() {
  req(hv_vm_create(HV_VM_DEFAULT));

  hv_vcpuid_t vcpu;
  req(hv_vcpu_create(&vcpu, HV_VCPU_DEFAULT));

  uint64_t vmx_cap_pinbased, vmx_cap_procbased, vmx_cap_procbased2, vmx_cap_entry;
  req(hv_vmx_read_capability(HV_VMX_CAP_PINBASED, &vmx_cap_pinbased));
  req(hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &vmx_cap_procbased));
  req(hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &vmx_cap_procbased2));
  req(hv_vmx_read_capability(HV_VMX_CAP_ENTRY, &vmx_cap_entry));

  printf("pinbased: %llu procbased: %llu procbased2: %llu, entry: %llu\n", vmx_cap_pinbased, 
      vmx_cap_procbased, vmx_cap_procbased2, vmx_cap_entry);

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_PIN_BASED, cap2ctrl(vmx_cap_pinbased, 0)));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CPU_BASED, cap2ctrl(
    vmx_cap_procbased,
    VMCS_PRI_PROC_BASED_CTLS_HLT |
    VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD |
    VMCS_PRI_PROC_BASED_CTLS_CR8_STORE)));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CPU_BASED2, cap2ctrl(vmx_cap_procbased2, 0) | (1 << 7)));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_VMENTRY_CONTROLS, cap2ctrl(vmx_cap_entry, 0)));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_EXC_BITMAP, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CR0_MASK, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CR0_SHADOW, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CR4_MASK, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_CTRL_CR4_SHADOW, 0xffffffff));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CS, 1 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CS_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CS_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CS_BASE, 0x0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_DS, 2 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_DS_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_DS_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_DS_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_ES, 2 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_ES_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_ES_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_ES_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_FS, 2 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_FS_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_FS_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_FS_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GS, 2 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GS_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GS_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GS_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_SS, 2 << 3));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_SS_AR, 0xc093));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_SS_LIMIT, 0xffffffff));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_SS_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_LDTR, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_LDTR_LIMIT, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_LDTR_AR, 0x10000));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_LDTR_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_TR, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_TR_LIMIT, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_TR_AR, 0x83));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_TR_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GDTR_LIMIT, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_GDTR_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_IDTR_LIMIT, 0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_IDTR_BASE, 0));

  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CR0, 0x20));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CR3, 0x0));
  req(hv_vmx_vcpu_write_vmcs(vcpu, VMCS_GUEST_CR4, 0x2000));

  // Online assembly: https://defuse.ca/online-x86-assembler.htm#disassembly
  // loads 0x1234 to ax if in 16-bit mode
  // loads 0x90901234 to eax if in 32-bit mode
  //     mov ax, 0x1234
  //     nop
  //     nop
  //     hlt
  unsigned char code[] = { 0xB8, 0x34, 0x12, 0x90, 0x90, 0xF4};
  //void *vm_mem = valloc(1 << 30);
  void *vm_mem = valloc(6);
  memcpy(vm_mem, code, sizeof code);
  req(hv_vm_map(vm_mem, 0, 6, HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC));

  req(hv_vcpu_write_register(vcpu, HV_X86_RIP, 0));
  req(hv_vcpu_write_register(vcpu, HV_X86_RFLAGS, 0x2));
  req(hv_vcpu_write_register(vcpu, HV_X86_RAX, 0));

  for (;;) {
    req(hv_vcpu_run(vcpu));
    uint64_t rip;
    req(hv_vcpu_read_register(vcpu, HV_X86_RIP, &rip));
    printf("rip = 0x%llx\n", rip);
    uint64_t exit_reason;
    req(hv_vmx_vcpu_read_vmcs(vcpu, VMCS_RO_EXIT_REASON, &exit_reason));
    printf("exit reason: %llu\n", exit_reason);
    if (exit_reason == VMX_REASON_EPT_VIOLATION || exit_reason == VMX_REASON_IRQ)
      continue;
    break;
  }

  uint64_t x;
  req(hv_vcpu_read_register(vcpu, HV_X86_RAX, &x));
  printf("rax = 0x%llx\n", x);
  uint64_t rip;
  req(hv_vcpu_read_register(vcpu, HV_X86_RIP, &rip));
  printf("rip = 0x%llx\n", rip);
  printf("protect mode\n");
  return 0;
}
stlankes commented 4 years ago

Hello, I create a fork and add a small example. Does it help?

@saurvs Do you want to integrate the example in your repository? If yes, I will create a pull requesst.