From ade936b3e0d44be5ae33f9eb71c0c302c3b0cc7c Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sat, 25 Jul 2020 11:57:37 +0530 Subject: [PATCH 1/5] event support for xen --- Cargo.toml | 11 +--- examples/cr-events.rs | 15 ++--- examples/regs-dump.rs | 2 +- src/driver/xen.rs | 124 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 122 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71d9a73d..bd98eefb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,6 @@ name = "microvmi" version = "0.1.0" authors = ["Mathieu Tarral "] edition = "2018" -description = "A cross-platform, unified, low-level VM introspection API supporting multiple hypervisors" -readme = "README.md" -homepage = "https://github.com/Wenzel/libmicrovmi" -repository = "https://github.com/Wenzel/libmicrovmi" -license = "GPL-3.0-only" -keywords = ["introspection", "VMI"] [lib] crate-type = ["cdylib", "lib"] @@ -26,21 +20,20 @@ hyper-v = ["winapi", "widestring", "ntapi", "vid-sys"] [dependencies] log = "0.4.8" -env_logger = "0.7.1" libc = { version = "0.2.58", optional = true } xenctrl = { git = "https://github.com/Wenzel/xenctrl", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } -kvmi = { version = "0.2.1", optional = true } +kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } fdp = { git = "https://github.com/Wenzel/fdp", optional = true } winapi = { version = "0.3.8", features = ["tlhelp32", "winnt", "handleapi", "securitybaseapi"], optional = true } widestring = { version = "0.4.0", optional = true } ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } -bitflags = "1.2.1" cty = "0.2.1" [dev-dependencies] +env_logger = "0.7.1" ctrlc = "3.1.3" clap = "2.33.0" colored = "1.9.3" diff --git a/examples/cr-events.rs b/examples/cr-events.rs index 8bd89c2e..4810748f 100644 --- a/examples/cr-events.rs +++ b/examples/cr-events.rs @@ -42,10 +42,11 @@ fn toggle_cr_intercepts(drv: &mut Box, vec_cr: &Vec, let intercept = InterceptType::Cr(*cr); let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} intercept on {:?}", status_str, cr); - for vcpu in 0..drv.get_vcpu_count().unwrap() { - drv.toggle_intercept(vcpu, intercept, enabled) - .expect(&format!("Failed to enable {:?}", cr)); - } + //for vcpu in 0..drv.get_vcpu_count().unwrap() { + let vcpu = 0; + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable {:?}", cr)); + //} } drv.resume().expect("Failed to resume VM"); @@ -101,7 +102,7 @@ fn main() { // listen let mut i: u64 = 0; while running.load(Ordering::SeqCst) { - let event = drv.listen(1000).expect("Failed to listen for events"); + let event = drv.listen(10).expect("Failed to listen for events"); match event { Some(ev) => { let (cr_type, new, old) = match ev.kind { @@ -120,8 +121,8 @@ fn main() { "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", ev_nb_output, vcpu_output, cr_output, old, new ); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + // drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/examples/regs-dump.rs b/examples/regs-dump.rs index 9495f9d3..68295913 100644 --- a/examples/regs-dump.rs +++ b/examples/regs-dump.rs @@ -33,7 +33,7 @@ fn main() { println!("pausing the VM"); drv.pause().expect("Failed to pause VM"); - let total_vcpu_count: u16 = drv.get_vcpu_count().expect("Failed to get vcpu count"); + let total_vcpu_count: u16 = 1; for vcpu in 0..total_vcpu_count { println!("dumping registers on VCPU {}", vcpu); let regs = drv.read_registers(vcpu).expect("Failed to read registers"); diff --git a/src/driver/xen.rs b/src/driver/xen.rs index f2bee070..d28b1d0a 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,19 +1,30 @@ -use crate::api::{ - DriverInitParam, Introspectable, Registers, SegmentReg, SystemTableReg, X86Registers, -}; use libc::{PROT_READ, PROT_WRITE}; use std::error::Error; +use std::mem; +use crate::api::{ + CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, SystemTableReg, +}; +use nix::poll::PollFlags; +use nix::poll::{poll, PollFd}; +use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; -use xenctrl::XenControl; +use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; +use xenctrl::{XenControl, XenCr, XenEventType}; +use xenevtchn::XenEventChannel; +use xenforeignmemory::XenForeignMem; use xenstore::{XBTransaction, Xs, XsOpenFlags}; +use xenvmevent_sys::{ + vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION, +}; -// unit struct #[derive(Debug)] pub struct Xen { xc: XenControl, - xen_fgn: xenforeignmemory::XenForeignMem, + xev: XenEventChannel, + xen_fgn: XenForeignMem, dom_name: String, domid: u32, + back_ring: vm_event_back_ring, } impl Xen { @@ -35,21 +46,25 @@ impl Xen { if !found { panic!("Cannot find domain {}", domain_name); } - let xc = XenControl::new(None, None, 0).unwrap(); - let xen_fgn = xenforeignmemory::XenForeignMem::new().unwrap(); + + let mut xc = XenControl::new(None, None, 0).unwrap(); + let (_ring_page, back_ring, remote_port) = xc + .monitor_enable(cand_domid) + .expect("Failed to map event ring page"); + let xev = XenEventChannel::new(cand_domid, remote_port).unwrap(); + + let xen_fgn = XenForeignMem::new().unwrap(); let xen = Xen { xc, + xev, xen_fgn, dom_name: domain_name.to_string(), domid: cand_domid, + back_ring, }; debug!("Initialized {:#?}", xen); xen } - - fn close(&mut self) { - debug!("close"); - } } impl Introspectable for Xen { @@ -205,6 +220,86 @@ impl Introspectable for Xen { })) } + fn listen(&mut self, timeout: u32) -> Result, Box> { + let fd = self.xev.xenevtchn_fd()?; + let fd_struct = PollFd::new(fd, PollFlags::POLLIN | PollFlags::POLLERR); + let mut fds = [fd_struct]; + let mut vcpu: u16 = 0; + let mut event_type = unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + let poll_result = poll(&mut fds, timeout.try_into().unwrap()).unwrap(); + let mut pending_event_port = -1; + if poll_result == 1 { + pending_event_port = self.xev.xenevtchn_pending()?; + if pending_event_port != -1 { + self.xev + .xenevtchn_unmask(pending_event_port.try_into().unwrap())?; + } + } + let back_ring_ptr = &mut self.back_ring; + let mut flag = false; + if poll_result > 0 + && self.xev.get_bind_port() == pending_event_port + && RING_HAS_UNCONSUMED_REQUESTS!(back_ring_ptr) != 0 + { + flag = true; + let req = self.xc.get_request(back_ring_ptr)?; + if req.version != VM_EVENT_INTERFACE_VERSION { + panic!("version mismatch"); + } + let xen_event_type = (self.xc.get_event_type(req)).unwrap(); + event_type = match xen_event_type { + XenEventType::Cr { cr_type, new, old } => EventType::Cr { + cr_type: match cr_type { + XenCr::Cr0 => CrType::Cr0, + XenCr::Cr3 => CrType::Cr3, + XenCr::Cr4 => CrType::Cr4, + }, + new, + old, + }, + _ => unimplemented!(), + }; + vcpu = req.vcpu_id.try_into().unwrap(); + let mut rsp = + unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + rsp.reason = req.reason; + rsp.version = VM_EVENT_INTERFACE_VERSION; + rsp.vcpu_id = req.vcpu_id; + rsp.flags = req.flags & VM_EVENT_FLAG_VCPU_PAUSED; + self.xc.put_response(&mut rsp, &mut self.back_ring)?; + } + self.xev.xenevtchn_notify()?; + if flag { + Ok(Some(Event { + vcpu, + kind: event_type, + })) + } else { + Ok(None) + } + } + + fn toggle_intercept( + &mut self, + _vcpu: u16, + intercept_type: InterceptType, + enabled: bool, + ) -> Result<(), Box> { + match intercept_type { + InterceptType::Cr(micro_cr_type) => { + let xen_cr = match micro_cr_type { + CrType::Cr0 => XenCr::Cr0, + CrType::Cr3 => XenCr::Cr3, + CrType::Cr4 => XenCr::Cr4, + }; + Ok(self + .xc + .monitor_write_ctrlreg(self.domid, xen_cr, enabled, true, true)?) + } + _ => unimplemented!(), + } + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); Ok(self.xc.domain_pause(self.domid)?) @@ -218,6 +313,9 @@ impl Introspectable for Xen { impl Drop for Xen { fn drop(&mut self) { - self.close(); + debug!("Closing Xen driver"); + self.xc + .monitor_disable(self.domid) + .expect("Failed to unmap event ring page"); } } From 6c4ad12b5ff204393f7bb1c1a78b1913199eaf8c Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Fri, 14 Aug 2020 22:55:00 +0530 Subject: [PATCH 2/5] msr event support added --- src/api.rs | 5 ++--- src/driver/kvm.rs | 3 +-- src/driver/xen.rs | 6 ++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/api.rs b/src/api.rs index bc598423..735d825c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -320,6 +320,7 @@ pub enum InterceptType { #[repr(C)] #[derive(Debug)] pub enum EventType { +<<<<<<< HEAD ///Cr register interception Cr { ///Type of control register @@ -334,9 +335,7 @@ pub enum EventType { ///Type of model specific register msr_type: u32, /// new value after msr register has been intercepted by the guest. - new: u64, - /// old value before cr register has been intercepted by the guest. - old: u64, + value: u64, }, ///int3 interception Breakpoint { diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 17262c67..30225f1c 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -311,8 +311,7 @@ impl Introspectable for Kvm { }, KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { msr_type, - new, - old, + value, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index d28b1d0a..c5d420ba 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -257,6 +257,7 @@ impl Introspectable for Xen { new, old, }, + XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, _ => unimplemented!(), }; vcpu = req.vcpu_id.try_into().unwrap(); @@ -296,6 +297,11 @@ impl Introspectable for Xen { .xc .monitor_write_ctrlreg(self.domid, xen_cr, enabled, true, true)?) } + InterceptType::Msr(micro_msr_type) => { + Ok(self + .xc + .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) + } _ => unimplemented!(), } } From 7e972d8c8c69ff4717fca13a0c1cab965a473d15 Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sat, 15 Aug 2020 00:11:09 +0530 Subject: [PATCH 3/5] breakpoint support added --- Cargo.toml | 18 ++++++++++++++---- src/api.rs | 1 - src/driver/xen.rs | 6 ++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd98eefb..a9ba9f47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,19 @@ name = "microvmi" version = "0.1.0" authors = ["Mathieu Tarral "] edition = "2018" +description = "A cross-platform, unified, low-level VM introspection API supporting multiple hypervisors" +readme = "README.md" +homepage = "https://github.com/Wenzel/libmicrovmi" +repository = "https://github.com/Wenzel/libmicrovmi" +license = "GPL-3.0-only" +keywords = ["introspection", "VMI"] [lib] crate-type = ["cdylib", "lib"] [features] # Xen driver -xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc"] +xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc", "xenevtchn", "xenvmevent-sys"] # KVM driver kvm = ["kvmi"] # VirtualBox driver @@ -20,20 +26,24 @@ hyper-v = ["winapi", "widestring", "ntapi", "vid-sys"] [dependencies] log = "0.4.8" +env_logger = "0.7.1" libc = { version = "0.2.58", optional = true } -xenctrl = { git = "https://github.com/Wenzel/xenctrl", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "write_register", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } -kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } +kvmi = { version = "0.2.1", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification", optional = true} +xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys", optional = true} fdp = { git = "https://github.com/Wenzel/fdp", optional = true } winapi = { version = "0.3.8", features = ["tlhelp32", "winnt", "handleapi", "securitybaseapi"], optional = true } widestring = { version = "0.4.0", optional = true } ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } +bitflags = "1.2.1" cty = "0.2.1" +nix = "0.18.0" [dev-dependencies] -env_logger = "0.7.1" ctrlc = "3.1.3" clap = "2.33.0" colored = "1.9.3" diff --git a/src/api.rs b/src/api.rs index 735d825c..ae76f031 100644 --- a/src/api.rs +++ b/src/api.rs @@ -320,7 +320,6 @@ pub enum InterceptType { #[repr(C)] #[derive(Debug)] pub enum EventType { -<<<<<<< HEAD ///Cr register interception Cr { ///Type of control register diff --git a/src/driver/xen.rs b/src/driver/xen.rs index c5d420ba..34b6ef49 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -258,6 +258,9 @@ impl Introspectable for Xen { old, }, XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, + XenEventType::Breakpoint { gpa, insn_len } => { + EventType::Breakpoint { gpa, insn_len } + } _ => unimplemented!(), }; vcpu = req.vcpu_id.try_into().unwrap(); @@ -302,6 +305,9 @@ impl Introspectable for Xen { .xc .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) } + InterceptType::Breakpoint => { + Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) + } _ => unimplemented!(), } } From b2c7e234317702873ffcfdfb0d60ab6e492039d2 Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sun, 16 Aug 2020 20:00:57 +0530 Subject: [PATCH 4/5] pagefault event support added --- examples/mem-events.rs | 19 ++++++-------- src/api.rs | 1 - src/driver/kvm.rs | 4 +-- src/driver/xen.rs | 58 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/examples/mem-events.rs b/examples/mem-events.rs index 30cd926a..647f89d9 100644 --- a/examples/mem-events.rs +++ b/examples/mem-events.rs @@ -6,11 +6,9 @@ use std::sync::Arc; use std::time::Instant; use microvmi::api::{ - Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, + Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SIZE, }; -const PAGE_SIZE: usize = 4096; - fn parse_args() -> ArgMatches<'static> { App::new(file!()) .version("0.1") @@ -25,7 +23,7 @@ fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { let intercept = InterceptType::Pagefault; let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} memory events", status_str); - for vcpu in 0..drv.get_vcpu_count().unwrap() { + for vcpu in 0..1 { drv.toggle_intercept(vcpu, intercept, enabled) .expect(&format!("Failed to enable page faults")); } @@ -39,7 +37,6 @@ fn main() { let matches = parse_args(); let domain_name = matches.value_of("vm_name").unwrap(); - // set CTRL-C handler let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -61,11 +58,11 @@ fn main() { //Code snippet to get page fault let max_addr = drv.get_max_physical_addr().unwrap(); - - for cur_addr in (0..max_addr).step_by(PAGE_SIZE) { + //println!("max_gpfn: {}", max_addr>>PAGE_SHIFT); + for cur_addr in (0..max_addr).step_by(PAGE_SIZE as usize) { let mut access: Access = drv.get_page_access(cur_addr).unwrap(); access &= !Access::X; - drv.set_page_access(cur_addr, access) + drv.set_page_access(cur_addr, !Access::X) .expect("failed to set page access"); } @@ -87,10 +84,10 @@ fn main() { let mut page_access = drv.get_page_access(gpa).expect("Failed to get page access"); //setting the access bits in the page due to which page fault occurred page_access |= pf_access; - drv.set_page_access(gpa, page_access) + drv.set_page_access(gpa, Access::RWX) .expect("Failed to set page access"); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/src/api.rs b/src/api.rs index ae76f031..aba5a6ab 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::ffi::{CStr, IntoStringError}; use crate::capi::DriverInitParamFFI; - bitflags! { pub struct Access: u32 { const R=0b00000001; diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 30225f1c..d99c4c12 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -309,9 +309,9 @@ impl Introspectable for Kvm { new, old, }, - KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { + KVMiEventType::Msr { msr_type, new, old: _ } => EventType::Msr { msr_type, - value, + value: new, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 34b6ef49..b14df140 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -3,13 +3,18 @@ use std::error::Error; use std::mem; use crate::api::{ CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, SystemTableReg, + Access, CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, + X86Registers, }; +use std::convert::{From, TryFrom}; +use std::error::Error; +use std::mem; use nix::poll::PollFlags; use nix::poll::{poll, PollFd}; use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; -use xenctrl::{XenControl, XenCr, XenEventType}; +use xenctrl::{XenControl, XenCr, XenEventType, XenPageAccess}; use xenevtchn::XenEventChannel; use xenforeignmemory::XenForeignMem; use xenstore::{XBTransaction, Xs, XsOpenFlags}; @@ -17,6 +22,38 @@ use xenvmevent_sys::{ vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION, }; +impl TryFrom for XenPageAccess { + type Error = &'static str; + fn try_from(access: Access) -> Result { + match access { + Access::NIL => Ok(XenPageAccess::NIL), + Access::R => Ok(XenPageAccess::R), + Access::W => Ok(XenPageAccess::W), + Access::RW => Ok(XenPageAccess::RW), + Access::X => Ok(XenPageAccess::X), + Access::RX => Ok(XenPageAccess::RX), + Access::WX => Ok(XenPageAccess::WX), + Access::RWX => Ok(XenPageAccess::RWX), + _ => Err("invalid access value"), + } + } +} + +impl From for Access { + fn from(access: XenPageAccess) -> Self { + match access { + XenPageAccess::NIL => Access::NIL, + XenPageAccess::R => Access::R, + XenPageAccess::W => Access::W, + XenPageAccess::RW => Access::RW, + XenPageAccess::X => Access::X, + XenPageAccess::RX => Access::RX, + XenPageAccess::WX => Access::WX, + XenPageAccess::RWX => Access::RWX, + } + } +} + #[derive(Debug)] pub struct Xen { xc: XenControl, @@ -261,7 +298,11 @@ impl Introspectable for Xen { XenEventType::Breakpoint { gpa, insn_len } => { EventType::Breakpoint { gpa, insn_len } } - _ => unimplemented!(), + XenEventType::Pagefault { gva, gpa, access } => EventType::Pagefault { + gva, + gpa, + access: access.into(), + }, }; vcpu = req.vcpu_id.try_into().unwrap(); let mut rsp = @@ -283,6 +324,17 @@ impl Introspectable for Xen { } } + fn get_page_access(&self, paddr: u64) -> Result> { + let access = self.xc.get_mem_access(self.domid, paddr >> PAGE_SHIFT)?; + Ok(access.into()) + } + + fn set_page_access(&self, paddr: u64, access: Access) -> Result<(), Box> { + Ok(self + .xc + .set_mem_access(self.domid, access.try_into().unwrap(), paddr >> PAGE_SHIFT)?) + } + fn toggle_intercept( &mut self, _vcpu: u16, @@ -308,7 +360,7 @@ impl Introspectable for Xen { InterceptType::Breakpoint => { Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) } - _ => unimplemented!(), + InterceptType::Pagefault => Ok(()), } } From 14d96bab0a264490b93f34da796288c88f204de1 Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Tue, 18 Aug 2020 13:36:18 +0000 Subject: [PATCH 5/5] singlestep support added --- Cargo.toml | 2 +- examples/msr-events.rs | 8 +-- examples/singlestep-events.rs | 96 +++++++++++++++++++++++++++++++++++ src/api.rs | 7 +++ src/driver/kvm.rs | 1 + src/driver/xen.rs | 17 +++---- 6 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 examples/singlestep-events.rs diff --git a/Cargo.toml b/Cargo.toml index a9ba9f47..ebec515e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ hyper-v = ["winapi", "widestring", "ntapi", "vid-sys"] log = "0.4.8" env_logger = "0.7.1" libc = { version = "0.2.58", optional = true } -xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "write_register", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "singlestep", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } kvmi = { version = "0.2.1", optional = true } diff --git a/examples/msr-events.rs b/examples/msr-events.rs index d496cf6e..43beb667 100644 --- a/examples/msr-events.rs +++ b/examples/msr-events.rs @@ -109,8 +109,8 @@ fn main() { let event = drv.listen(1000).expect("Failed to listen for events"); match event { Some(ev) => { - let (msr_type, new, old) = match ev.kind { - EventType::Msr { msr_type, new, old } => (msr_type, new, old), + let (msr_type, value) = match ev.kind { + EventType::Msr { msr_type, value } => (msr_type, value), _ => panic!("not msr event"), }; let msr_color = "blue"; @@ -118,8 +118,8 @@ fn main() { let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); let msr_output = format!("0x{:x}", msr_type).color(msr_color); println!( - "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", - ev_nb_output, vcpu_output, msr_output, old, new + "[{}] {} - {}: new value: 0x{:x}", + ev_nb_output, vcpu_output, msr_output, value, ); drv.reply_event(ev, EventReplyType::Continue) .expect("Failed to send event reply"); diff --git a/examples/singlestep-events.rs b/examples/singlestep-events.rs new file mode 100644 index 00000000..2d21fc36 --- /dev/null +++ b/examples/singlestep-events.rs @@ -0,0 +1,96 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use clap::{App, Arg, ArgMatches}; +use colored::*; +use env_logger; + +use microvmi::api::*; + +fn parse_args() -> ArgMatches<'static> { + App::new(file!()) + .version("0.1") + .about("Watches singlestep VMI events") + .arg(Arg::with_name("vm_name").index(1).required(true)) + .get_matches() +} + +fn toggle_singlestep_interception(drv: &mut Box, enabled: bool) { + drv.pause().expect("Failed to pause VM"); + + let intercept = InterceptType::Singlestep; + let status_str = if enabled { "Enabling" } else { "Disabling" }; + println!("{} singlestep events", status_str); + for vcpu in 0..1 { + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable singlestep")); + } + + drv.resume().expect("Failed to resume VM"); +} + +fn main() { + env_logger::init(); + + let matches = parse_args(); + + let domain_name = matches.value_of("vm_name").unwrap(); + + let init_option = matches + .value_of("kvmi_socket") + .map(|socket| DriverInitParam::KVMiSocket(socket.into())); + // set CTRL-C handler + let running = Arc::new(AtomicBool::new(true)); + let r = running.clone(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); + + println!("Initialize Libmicrovmi"); + let mut drv: Box = microvmi::init(domain_name, None, init_option); + + //Enable singlestep interception + toggle_singlestep_interception(&mut drv, true); + + println!("Listen for singlestep events..."); + // record elapsed time + let start = Instant::now(); + // listen + let mut i: u64 = 0; + while running.load(Ordering::SeqCst) { + let event = drv.listen(1000).expect("Failed to listen for events"); + match event { + Some(ev) => { + let gpa = match ev.kind { + EventType::Singlestep { gpa } => (gpa), + _ => panic!("Not singlestep event"), + }; + let ev_nb_output = format!("{}", i).cyan(); + let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); + let singlestep_output = format!("singlestep occurred!").color("blue"); + println!( + "[{}] {} - {}: gpa = 0x{:x} ", + ev_nb_output, vcpu_output, singlestep_output, gpa + ); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); + i = i + 1; + } + None => println!("No events yet..."), + } + } + let duration = start.elapsed(); + + //disable singlestep interception + toggle_singlestep_interception(&mut drv, false); + + let ev_per_sec = i as f64 / duration.as_secs_f64(); + println!( + "Caught {} events in {:.2} seconds ({:.2} events/sec)", + i, + duration.as_secs_f64(), + ev_per_sec + ); +} diff --git a/src/api.rs b/src/api.rs index aba5a6ab..334f08cb 100644 --- a/src/api.rs +++ b/src/api.rs @@ -313,6 +313,7 @@ pub enum InterceptType { /// Intercept when guest requests an access to a page for which the requested type of access is not granted. For example , guest tries to write on a read only page. Breakpoint, Pagefault, + Singlestep, } /// Various types of events along with their relevant attributes being handled by this driver @@ -342,6 +343,7 @@ pub enum EventType { /// instruction length. Generally it should be one. Anything other than one implies malicious guest. insn_len: u8, }, + ///Pagefault interception Pagefault { /// Virtual memory address of the guest gva: u64, @@ -350,6 +352,11 @@ pub enum EventType { /// Acsess responsible for thr pagefault access: Access, }, + ///Singlestep event + Singlestep { + ///Physical memory address of the guest + gpa: u64, + }, } ///Types of x86 control registers are listed here diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index d99c4c12..2ff26be4 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -289,6 +289,7 @@ impl Introspectable for Kvm { .kvmi .control_events(vcpu, KVMiInterceptType::Pagefault, enabled)?) } + _ => unimplemented!(), } } diff --git a/src/driver/xen.rs b/src/driver/xen.rs index b14df140..32aa0335 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,17 +1,14 @@ -use libc::{PROT_READ, PROT_WRITE}; -use std::error::Error; -use std::mem; use crate::api::{ - CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, SystemTableReg, - Access, CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, - X86Registers, + Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, + SegmentReg, SystemTableReg, X86Registers, }; -use std::convert::{From, TryFrom}; -use std::error::Error; -use std::mem; +use libc::{PROT_READ, PROT_WRITE}; use nix::poll::PollFlags; use nix::poll::{poll, PollFd}; use std::convert::TryInto; +use std::convert::{From, TryFrom}; +use std::error::Error; +use std::mem; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; use xenctrl::{XenControl, XenCr, XenEventType, XenPageAccess}; @@ -303,6 +300,7 @@ impl Introspectable for Xen { gpa, access: access.into(), }, + XenEventType::Singlestep { gpa } => EventType::Singlestep { gpa }, }; vcpu = req.vcpu_id.try_into().unwrap(); let mut rsp = @@ -361,6 +359,7 @@ impl Introspectable for Xen { Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) } InterceptType::Pagefault => Ok(()), + InterceptType::Singlestep => Ok(self.xc.monitor_singlestep(self.domid, enabled)?), } }