diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..14004e0e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "type_traits": "c" + } +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5f1cc3c5..298aaf07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,16 +28,20 @@ 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/Wenzel/xenctrl", 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 = { git = "https://github.com/Wenzel/kvmi", rev = "dd10135a27bb3658d399dfb5477299ca0f4baeac", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} +xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys"} +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 } cty = "0.2.1" +nix = "0.18.0" +bitflags = "1.2.1" [dev-dependencies] ctrlc = "3.1.3" diff --git a/c_examples/Makefile b/c_examples/Makefile index 728dde05..9554b95c 100644 --- a/c_examples/Makefile +++ b/c_examples/Makefile @@ -5,7 +5,7 @@ CWD := $(shell pwd) .PHONY: all clean -all: mem-dump pause regs-dump +all: mem-dump pause regs-dump cr-events msr-events interrupt-events singlestep-events libmicrovmi.h: ../target/debug/libmicrovmi.so cd ..; \ @@ -20,5 +20,17 @@ pause: libmicrovmi.h pause.c regs-dump: libmicrovmi.h regs-dump.c $(CC) $(CFLAGS) regs-dump.c -o $@ $(LDFLAGS) +cr-events: libmicrovmi.h cr-events.c + $(CC) $(CFLAGS) cr-events.c -o $@ $(LDFLAGS) + +msr-events: libmicrovmi.h msr-events.c + $(CC) $(CFLAGS) msr-events.c -o $@ $(LDFLAGS) + +interrupt-events: libmicrovmi.h interrupt-events.c + $(CC) $(CFLAGS) interrupt-events.c -o $@ $(LDFLAGS) + +singlestep-events: libmicrovmi.h singlestep-events.c + $(CC) $(CFLAGS) singlestep-events.c -o $@ $(LDFLAGS) + clean: - rm -f libmicrovmi.h mem-dump pause regs-dump + rm -f libmicrovmi.h mem-dump pause regs-dump cr-events msr-events interrupt-events singlestep-events diff --git a/c_examples/cr-events.c b/c_examples/cr-events.c new file mode 100644 index 00000000..a2474e19 --- /dev/null +++ b/c_examples/cr-events.c @@ -0,0 +1,69 @@ +#include +#include +#include + +#include "libmicrovmi.h" + +bool display_cr(int index) +{ + switch (index) + { + case 0: + printf("Cr0 "); + return true; + case 1: + printf("Cr3 "); + return true; + case 2: + printf("Cr4 "); + return true; + default: + break; + } + return false; +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("No domain name given.\n"); + return 1; + } + microvmi_envlogger_init(); + void* driver = microvmi_init(argv[1], NULL, NULL); + InterceptType intercept = { .tag = Cr, .cr = {._0 = Cr3} }; + for(uint16_t vcpu =0; vcpu<2;vcpu++) + microvmi_toggle_intercept(driver, vcpu, intercept, true); + while(true) + { + Event ev; + if(microvmi_listen(driver, 1000, &ev)==true) + { + switch(ev.kind.tag) + { + case CrEvents: + + if(display_cr(ev.kind.cr_events.cr_type)==true) + { + printf("vcpu: %d ", ev.vcpu); + printf("old value: 0x%" PRIx64 " ", ev.kind.cr_events.old); + printf("new value: 0x%" PRIx64 "\n", ev.kind.cr_events.new_); + } + else + { + printf("No Events..\n"); + } + + break; + default: + printf("No Events..\n"); + } + } + else + { + printf("No events..\n"); + } + + } + microvmi_destroy(driver); + return 0; +} diff --git a/c_examples/interrupt-events.c b/c_examples/interrupt-events.c new file mode 100644 index 00000000..62d4423a --- /dev/null +++ b/c_examples/interrupt-events.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "libmicrovmi.h" + + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("No domain name given.\n"); + return 1; + } + microvmi_envlogger_init(); + void* driver = microvmi_init(argv[1], NULL, NULL); + InterceptType intercept = { .tag = Breakpoint}; + for(uint16_t vcpu =0; vcpu<2;vcpu++) + microvmi_toggle_intercept(driver, vcpu, intercept, true); + while(true) + { + Event ev; + if(microvmi_listen(driver, 1000, &ev)==true) + { + switch(ev.kind.tag) + { + case BreakpointEvents: + printf("vcpu: %d ", ev.vcpu); + printf("Breakpoint detected!! "); + printf("gpa: 0x%" PRIx64 ": ", ev.kind.breakpoint_events.gpa); + printf("insn_len: 0x%" PRIx16 "\n", ev.kind.breakpoint_events.insn_len); + break; + default: + printf("No Events..\n"); + } + } + else + { + printf("No events..\n"); + } + + } + microvmi_destroy(driver); + return 0; +} diff --git a/c_examples/msr-events.c b/c_examples/msr-events.c new file mode 100644 index 00000000..e6b7c4e4 --- /dev/null +++ b/c_examples/msr-events.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "libmicrovmi.h" + + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("No domain name given.\n"); + return 1; + } + microvmi_envlogger_init(); + void* driver = microvmi_init(argv[1], NULL, NULL); + InterceptType intercept = { .tag = Msr, .msr = {._0 = (uint32_t)0xc0000080} }; + for(uint16_t vcpu =0; vcpu<2;vcpu++) + microvmi_toggle_intercept(driver, vcpu, intercept, true); + while(true) + { + Event ev; + if(microvmi_listen(driver, 1000, &ev)==true) + { + switch(ev.kind.tag) + { + case MsrEvents: + printf("vcpu: %d ", ev.vcpu); + printf("msr index: 0x%" PRIx32 ": ", ev.kind.msr_events.msr_type); + printf("old value: 0x%" PRIx64 "\n", ev.kind.msr_events.value); + break; + default: + printf("No Events..\n"); + } + } + else + { + printf("No events..\n"); + } + + } + microvmi_destroy(driver); + return 0; +} diff --git a/c_examples/regs-dump.c b/c_examples/regs-dump.c index 602c1265..63ff7916 100644 --- a/c_examples/regs-dump.c +++ b/c_examples/regs-dump.c @@ -4,6 +4,13 @@ #include "libmicrovmi.h" + +void display_segment_register(SegmentReg segment) +{ + printf("base: 0x%" PRIx64 "\n", segment.base); + printf("base: 0x%" PRIx32 "\n", segment.limit); + printf("base: 0x%" PRIx16 "\n", segment.selector); +} void read_registers(void* driver, const char* vm_name) { if (microvmi_pause(driver)) { printf("Paused.\n"); @@ -24,7 +31,47 @@ void read_registers(void* driver, const char* vm_name) { printf("rbp: 0x%" PRIx64 "\n", regs.x86._0.rbp); printf("rip: 0x%" PRIx64 "\n", regs.x86._0.rip); printf("rflags: 0x%" PRIx64 "\n", regs.x86._0.rflags); + printf("r8: 0x%" PRIx64 "\n", regs.x86._0.r8); + printf("r9: 0x%" PRIx64 "\n", regs.x86._0.r9); + printf("r10: 0x%" PRIx64 "\n", regs.x86._0.r10); + printf("r11: 0x%" PRIx64 "\n", regs.x86._0.r11); + printf("r12: 0x%" PRIx64 "\n", regs.x86._0.r12); + printf("r13: 0x%" PRIx64 "\n", regs.x86._0.r13); + printf("r14: 0x%" PRIx64 "\n", regs.x86._0.r14); + printf("r15: 0x%" PRIx64 "\n", regs.x86._0.r15); + printf("cr0: 0x%" PRIx64 "\n", regs.x86._0.cr0); + printf("cr2: 0x%" PRIx64 "\n", regs.x86._0.cr2); printf("cr3: 0x%" PRIx64 "\n", regs.x86._0.cr3); + printf("sysenter_cs: 0x%" PRIx64 "\n", regs.x86._0.sysenter_cs); + printf("sysenter_esp: 0x%" PRIx64 "\n", regs.x86._0.sysenter_esp); + printf("sysenter_eip: 0x%" PRIx64 "\n", regs.x86._0.sysenter_eip); + printf("msr_star: 0x%" PRIx64 "\n", regs.x86._0.msr_star); + printf("msr_lstar: 0x%" PRIx64 "\n", regs.x86._0.msr_lstar); + printf("msr_efer: 0x%" PRIx64 "\n", regs.x86._0.msr_efer); + printf("cs {\n"); + display_segment_register(regs.x86._0.cs); + printf("}\n"); + printf("ds {\n"); + display_segment_register(regs.x86._0.ds); + printf("}\n"); + printf("es {\n"); + display_segment_register(regs.x86._0.es); + printf("}\n"); + printf("fs {\n"); + display_segment_register(regs.x86._0.fs); + printf("}\n"); + printf("gs {\n"); + display_segment_register(regs.x86._0.gs); + printf("}\n"); + printf("ss {\n"); + display_segment_register(regs.x86._0.ss); + printf("}\n"); + printf("tr {\n"); + display_segment_register(regs.x86._0.tr); + printf("}\n"); + printf("ldt {\n"); + display_segment_register(regs.x86._0.ldt); + printf("}\n"); } else { printf("Unable to read registers.\n"); } diff --git a/c_examples/singlestep-events.c b/c_examples/singlestep-events.c new file mode 100644 index 00000000..63a36221 --- /dev/null +++ b/c_examples/singlestep-events.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "libmicrovmi.h" + + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("No domain name given.\n"); + return 1; + } + microvmi_envlogger_init(); + void* driver = microvmi_init(argv[1], NULL, NULL); + InterceptType intercept = { .tag = Breakpoint}; + for(uint16_t vcpu =0; vcpu<2;vcpu++) + microvmi_toggle_intercept(driver, vcpu, intercept, true); + while(true) + { + Event ev; + if(microvmi_listen(driver, 1000, &ev)==true) + { + switch(ev.kind.tag) + { + case SinglestepEvents: + printf("vcpu: %d ", ev.vcpu); + printf("Breakpoint detected!! "); + printf("gpa: 0x%" PRIx64 ": ", ev.kind.singlestep_events.gpa); + break; + default: + printf("No Events..\n"); + } + } + else + { + printf("No events..\n"); + } + + } + microvmi_destroy(driver); + return 0; +} diff --git a/examples/cr-events.rs b/examples/cr-events.rs index 8bd89c2e..e5ac592d 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,11 +102,11 @@ 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 { - EventType::Cr { cr_type, new, old } => (cr_type, new, old), + EventType::CrEvents { cr_type, new, old } => (cr_type, new, old), _ => panic!("not cr event"), }; let cr_color = match cr_type { @@ -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/interrupt-events.rs b/examples/interrupt-events.rs index 4d8d150b..eb38d8e8 100644 --- a/examples/interrupt-events.rs +++ b/examples/interrupt-events.rs @@ -72,7 +72,7 @@ fn main() { match event { Some(ev) => { let (gpa, insn_len) = match ev.kind { - EventType::Breakpoint { gpa, insn_len } => (gpa, insn_len), + EventType::BreakpointEvents { gpa, insn_len } => (gpa, insn_len), _ => panic!("Not interrupt event"), }; let ev_nb_output = format!("{}", i).cyan(); diff --git a/examples/mem-events.rs b/examples/mem-events.rs new file mode 100644 index 00000000..f530a63d --- /dev/null +++ b/examples/mem-events.rs @@ -0,0 +1,108 @@ +use clap::{App, Arg, ArgMatches}; +use colored::*; +use env_logger; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use microvmi::api::{ + Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SIZE, +}; + +fn parse_args() -> ArgMatches<'static> { + App::new(file!()) + .version("0.1") + .about("Watches memory VMI events") + .arg(Arg::with_name("vm_name").index(1).required(true)) + .get_matches() +} + +fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { + drv.pause().expect("Failed to pause VM"); + + let intercept = InterceptType::Pagefault; + let status_str = if enabled { "Enabling" } else { "Disabling" }; + println!("{} memory events", status_str); + for vcpu in 0..1 { + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable page faults")); + } + + 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); + println!("Listen for memory events..."); + // record elapsed time + let start = Instant::now(); + toggle_pf_intercept(&mut drv, true); + let mut i: u64 = 0; + + //Code snippet to get page fault + let max_addr = drv.get_max_physical_addr().unwrap(); + //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::X) + .expect("failed to set page access"); + } + + while running.load(Ordering::SeqCst) { + let event = drv.listen(1000).expect("Failed to listen for events"); + match event { + Some(ev) => { + let (gva, gpa, pf_access) = match ev.kind { + EventType::PagefaultEvents { gva, gpa, access } => (gva, gpa, access), + _ => panic!("Not pf event"), + }; + let ev_nb_output = format!("{}", i).cyan(); + let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); + let pagefault_output = format!("pagefault occurred!").color("blue"); + println!( + "[{}] {} - {}: gva = 0x{:x} gpa = 0x{:x} access = {:?} ", + ev_nb_output, vcpu_output, pagefault_output, gva, gpa, pf_access + ); + 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, Access::RWX) + .expect("Failed to set page access"); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); + i = i + 1; + } + None => println!("No events yet..."), + } + } + let duration = start.elapsed(); + toggle_pf_intercept(&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/examples/msr-events.rs b/examples/msr-events.rs index d496cf6e..12ea2426 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::MsrEvents { 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/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/examples/singlestep-events.rs b/examples/singlestep-events.rs new file mode 100644 index 00000000..d1496efb --- /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::SinglestepEvents { 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 c7de2b13..88f98e72 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,6 +4,20 @@ use std::ffi::{CStr, IntoStringError}; use crate::capi::DriverInitParamFFI; +bitflags! { + #[repr(C)] + pub struct Access: u32 { + const R=0b00000001; + const W=0b00000010; + const X=0b00000100; + const NIL=0b00000000; + const RW=Self::R.bits | Self::W.bits; + const WX=Self::W.bits | Self::X.bits; + const RX=Self::R.bits | Self::X.bits; + const RWX=Self::R.bits | Self::W.bits | Self::X.bits; + } +} + ///Represents the available hypervisor VMI drivers supported by libmicrovmi #[repr(C)] #[derive(Debug)] @@ -206,6 +220,16 @@ pub trait Introspectable { unimplemented!(); } + //get page access + fn get_page_access(&self, _paddr: u64) -> Result> { + unimplemented!(); + } + + //set page access + fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box> { + unimplemented!(); + } + /// Used to pause the VM /// fn pause(&mut self) -> Result<(), Box> { @@ -268,6 +292,8 @@ pub enum InterceptType { Msr(u32), /// 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 @@ -275,7 +301,7 @@ pub enum InterceptType { #[derive(Debug)] pub enum EventType { ///Cr register interception - Cr { + CrEvents { ///Type of control register cr_type: CrType, /// new value after cr register has been intercepted by the guest. @@ -284,21 +310,33 @@ pub enum EventType { old: u64, }, ///Msr register interception - Msr { + MsrEvents { ///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 { + BreakpointEvents { /// Physical memory address of the guest gpa: u64, /// instruction length. Generally it should be one. Anything other than one implies malicious guest. insn_len: u8, }, + ///Pagefault interception + PagefaultEvents { + /// Virtual memory address of the guest + gva: u64, + /// Physical memory address of the guest + gpa: u64, + //Acsess responsible for thr pagefault + //access: Access, + }, + ///Singlestep event + SinglestepEvents { + ///Physical memory address of the guest + gpa: u64, + }, } ///Types of x86 control registers are listed here diff --git a/src/capi.rs b/src/capi.rs index 4caac4ac..d527f123 100644 --- a/src/capi.rs +++ b/src/capi.rs @@ -1,6 +1,9 @@ -use crate::api::{DriverInitParam, DriverType, Introspectable, Registers}; +use crate::api::{ + Access, DriverInitParam, DriverType, Event, EventReplyType, InterceptType, Introspectable, + Registers, +}; use crate::init; -use cty::{c_char, size_t, uint16_t, uint64_t, uint8_t}; +use cty::{c_char, size_t, uint16_t, uint32_t, uint64_t, uint8_t}; use std::convert::TryInto; use std::ffi::{c_void, CStr}; use std::slice; @@ -82,6 +85,20 @@ pub unsafe extern "C" fn microvmi_read_physical( .is_ok() } +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_write_physical( + context: *mut c_void, + physical_address: uint64_t, + buffer: *mut uint8_t, + size: size_t, +) -> bool { + let driver = get_driver_mut_ptr(context); + (*driver) + .write_physical(physical_address, slice::from_raw_parts_mut(buffer, size)) + .is_ok() +} + #[allow(clippy::missing_safety_doc)] #[no_mangle] pub unsafe extern "C" fn microvmi_get_max_physical_addr( @@ -115,6 +132,104 @@ pub unsafe extern "C" fn microvmi_read_registers( } } +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_write_registers( + context: *mut c_void, + vcpu: uint16_t, + registers: Registers, +) -> bool { + let driver = get_driver_mut_ptr(context); + (*driver).write_registers(vcpu, registers).is_ok() +} + +/*#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_get_page_access( + context: *mut c_void, + paddr: uint64_t, + access: *mut Access, +) -> bool { + let driver = get_driver_mut_ptr(context); + match (*driver).get_page_access(paddr) { + Ok(flags) => { + access.write(flags); + true + } + Err(_) => false, + } +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_set_page_access( + context: *mut c_void, + paddr: uint64_t, + access: Access, +) -> bool { + let driver = get_driver_mut_ptr(context); + (*driver).set_page_access(paddr, access).is_ok() +}*/ + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_toggle_intercept( + context: *mut c_void, + vcpu: uint16_t, + intercept_type: InterceptType, + enabled: bool, +) -> bool { + let driver = get_driver_mut_ptr(context); + (*driver) + .toggle_intercept(vcpu, intercept_type, enabled) + .is_ok() +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_listen( + context: *mut c_void, + timeout: uint32_t, + event_ptr: *mut Event, +) -> bool { + let driver = get_driver_mut_ptr(context); + match (*driver).listen(timeout) { + Ok(Some(event)) => { + event_ptr.write(event); + true + } + Ok(None) => true, + Err(_) => false, + } +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_reply_event( + context: *mut c_void, + event: Event, + reply_type: EventReplyType, +) -> bool { + let driver = get_driver_mut_ptr(context); + (*driver).reply_event(event, reply_type).is_ok() +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn microvmi_get_vcpu_count( + context: *mut c_void, + vcpu_ptr: *mut uint16_t, +) -> bool { + let driver = get_driver_mut_ptr(context); + match (*driver).get_vcpu_count() { + Ok(vcpu_count) => { + vcpu_ptr.write(vcpu_count); + true + } + Err(_) => false, + } +} + unsafe fn get_driver_mut_ptr(context: *mut c_void) -> *mut dyn Introspectable { let driver: *mut *mut dyn Introspectable = context as *mut _; driver.read() diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 1229db33..40eabf6a 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -239,7 +239,7 @@ impl Introspectable for Kvm { None => Ok(None), Some(kvmi_event) => { let microvmi_event_kind = match kvmi_event.ev_type { - KVMiEventType::Cr { cr_type, new, old } => EventType::Cr { + KVMiEventType::Cr { cr_type, new, old } => EventType::CrEvents { cr_type: match cr_type { KVMiCr::Cr0 => CrType::Cr0, KVMiCr::Cr3 => CrType::Cr3, @@ -248,12 +248,11 @@ impl Introspectable for Kvm { new, old, }, - KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { + KVMiEventType::Msr { msr_type, new, old } => EventType::MsrEvents { msr_type, - new, - old, + value: new, }, - KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { + KVMiEventType::Breakpoint {gpa, insn_len } => EventType::BreakpointEvents { gpa, insn_len, }, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 388c90d0..f5df2bea 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,17 +1,65 @@ -use crate::api::{DriverInitParam, Introspectable, Registers, SegmentReg, X86Registers}; -use libc::PROT_READ; +use crate::api::{ + Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, + SegmentReg, X86Registers, +}; +use std::convert::{From, TryFrom}; use std::error::Error; +use std::mem; + +use libc::PROT_READ; +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, XenPageAccess}; +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, +}; + +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, + } + } +} -// 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 { @@ -33,21 +81,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 { @@ -164,6 +216,115 @@ 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::CrEvents { + cr_type: match cr_type { + XenCr::Cr0 => CrType::Cr0, + XenCr::Cr3 => CrType::Cr3, + XenCr::Cr4 => CrType::Cr4, + }, + new, + old, + }, + XenEventType::Msr { msr_type, value } => EventType::MsrEvents { msr_type, value }, + XenEventType::Breakpoint { gpa, insn_len } => { + EventType::BreakpointEvents { gpa, insn_len } + } + XenEventType::Pagefault { gva, gpa, access } => EventType::PagefaultEvents { + gva, + gpa, + access: access.into(), + }, + XenEventType::Singlestep { gpa } => EventType::SinglestepEvents { gpa }, + }; + 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 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, + 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)?) + } + InterceptType::Msr(micro_msr_type) => { + Ok(self + .xc + .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) + } + InterceptType::Breakpoint => { + Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) + } + InterceptType::Pagefault => Ok(()), + InterceptType::Singlestep => Ok(self.xc.monitor_singlestep(self.domid, enabled)?), + } + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); Ok(self.xc.domain_pause(self.domid)?) @@ -177,6 +338,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"); } } diff --git a/src/lib.rs b/src/lib.rs index a3fe0978..1a93c4ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,8 @@ mod driver; #[macro_use] extern crate log; +#[macro_use] +extern crate bitflags; use api::Introspectable; use api::{DriverInitParam, DriverType};