Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xen: Singlestep events #113

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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
Expand All @@ -28,17 +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 = { 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]
ctrlc = "3.1.3"
Expand Down
15 changes: 8 additions & 7 deletions examples/cr-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ fn toggle_cr_intercepts(drv: &mut Box<dyn Introspectable>, vec_cr: &Vec<CrType>,
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");
Expand Down Expand Up @@ -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 {
Expand All @@ -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..."),
Expand Down
19 changes: 8 additions & 11 deletions examples/mem-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -25,7 +23,7 @@ fn toggle_pf_intercept(drv: &mut Box<dyn Introspectable>, 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"));
}
Expand All @@ -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();
Expand All @@ -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");
}

Expand All @@ -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..."),
Expand Down
8 changes: 4 additions & 4 deletions examples/msr-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,17 @@ 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";
let ev_nb_output = format!("{}", i).cyan();
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");
Expand Down
2 changes: 1 addition & 1 deletion examples/regs-dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
96 changes: 96 additions & 0 deletions examples/singlestep-events.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Introspectable>, 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<dyn Introspectable> = 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
);
}
12 changes: 8 additions & 4 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -314,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
Expand All @@ -334,9 +334,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 {
Expand All @@ -345,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,
Expand All @@ -353,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
Expand Down
6 changes: 3 additions & 3 deletions src/driver/kvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ impl<T: KVMIntrospectable> Introspectable for Kvm<T> {
.kvmi
.control_events(vcpu, KVMiInterceptType::Pagefault, enabled)?)
}
_ => unimplemented!(),
}
}

Expand All @@ -309,10 +310,9 @@ impl<T: KVMIntrospectable> Introspectable for Kvm<T> {
new,
old,
},
KVMiEventType::Msr { msr_type, new, old } => EventType::Msr {
KVMiEventType::Msr { msr_type, new, old: _ } => EventType::Msr {
msr_type,
new,
old,
value: new,
},
KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint {
gpa,
Expand Down
Loading