From baa28081538333287da7a9661360ae11ffe7420f Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Tue, 29 Jun 2021 13:57:09 +0200 Subject: [PATCH 01/14] cargo: add mflow feature and memflow dependency --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1438e06c..4d233088 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ xen = ["xenctrl", "xenstore-rs", "xenforeignmemory", "xenevtchn", "xenvmevent-sy kvm = ["kvmi"] # VirtualBox driver virtualbox = ["fdp"] +# memflow driver +# feature name is "mflow" to avoid conflict with the dependency +mflow = ["memflow"] [dependencies] @@ -42,6 +45,7 @@ winapi = { version = "0.3", features = ["tlhelp32", "winnt", "handleapi", "secur widestring = { version = "0.4", optional = true } ntapi = { version = "0.3", optional = true } vid-sys = { version = "=0.3.0", features = ["deprecated-apis"], optional = true } +memflow = { version = "0.1.5", optional = true } [dev-dependencies] utilities = { path = "utilities" } From 3b9e853b5ff600acfbc12f5fd002f1cd312f3989 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Tue, 29 Jun 2021 13:57:39 +0200 Subject: [PATCH 02/14] src: add memflow driver skeleton --- src/api/mod.rs | 1 + src/api/params.rs | 41 ++++++++++++++++++++++++++ src/driver/memflow.rs | 17 +++++++++++ src/driver/mod.rs | 2 ++ src/lib.rs | 4 +++ utilities/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 src/driver/memflow.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index 08f3214a..e9c9ded0 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -26,6 +26,7 @@ bitflags! { #[derive(Debug, Copy, Clone, PartialEq, IntoEnumIterator)] pub enum DriverType { KVM, + Memflow, VirtualBox, Xen, } diff --git a/src/api/params.rs b/src/api/params.rs index e5ca0726..34ec5cf1 100644 --- a/src/api/params.rs +++ b/src/api/params.rs @@ -10,6 +10,46 @@ pub enum KVMInitParams { UnixSocket { path: String }, } +/// Memflow connector parameters +/// +/// This enumeration reflects the possibilities to initialize Memflow +/// - [`qemu_procfs`](https://github.com/memflow/memflow-qemu-procfs) +/// - [`kvm`](https://github.com/memflow/memflow-kvm) +/// - [`pcileech`](https://github.com/memflow/memflow-pcileech) +/// - [`coredump`](https://github.com/memflow/memflow-coredump) +/// - unknown: will simply forward the string arguments to the connector +#[derive(Debug, Clone, PartialEq)] +pub enum MemflowConnectorParams { + // optional vm_name, otherwise will search for the first QEMU process + QEMUProcFs { + vm_name: Option, + }, + KVM { + pid: u32, + }, + // default value for device: "FPGA" + PCILeech { + device: Option, + memmap: Option, + }, + Coredump { + filepath: String, + }, + // allow to pass an abritrary list of Strings as parameters + Unknown { + args: Vec, + }, +} + +/// Memflow initialization parameters +#[derive(Debug, Default, Clone, PartialEq)] +pub struct MemflowInitParams { + /// optional connector name + pub connector_name: Option, + /// optional connector initialization parameters + pub connector_args: Option, +} + /// VirtualBox initialization parameters #[derive(Debug, Clone, PartialEq)] pub enum VBoxInitParams {} @@ -30,5 +70,6 @@ pub struct DriverInitParams { pub common: Option, pub xen: Option, pub kvm: Option, + pub memflow: Option, pub virtualbox: Option, } diff --git a/src/driver/memflow.rs b/src/driver/memflow.rs new file mode 100644 index 00000000..6f4c40f1 --- /dev/null +++ b/src/driver/memflow.rs @@ -0,0 +1,17 @@ +use crate::api::params::DriverInitParams; +use crate::api::{DriverType, Introspectable}; +use std::error::Error; + +pub struct Memflow; + +impl Memflow { + pub fn new(_init_params: DriverInitParams) -> Result> { + Ok(Memflow {}) + } +} + +impl Introspectable for Memflow { + fn get_driver_type(&self) -> DriverType { + DriverType::Memflow + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 731645a2..008f1ef4 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,5 +1,7 @@ #[cfg(feature = "kvm")] pub mod kvm; +#[cfg(feature = "mflow")] +pub mod memflow; #[cfg(feature = "virtualbox")] pub mod virtualbox; #[cfg(feature = "xen")] diff --git a/src/lib.rs b/src/lib.rs index a850dcf5..88cec111 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,8 @@ use api::DriverType; use api::Introspectable; #[cfg(feature = "kvm")] use driver::kvm::Kvm; +#[cfg(feature = "mflow")] +use driver::memflow::Memflow; #[cfg(feature = "virtualbox")] use driver::virtualbox::VBox; #[cfg(feature = "xen")] @@ -106,6 +108,8 @@ fn init_driver( match driver_type { #[cfg(feature = "kvm")] DriverType::KVM => Ok(Box::new(Kvm::new(create_kvmi(), _init_params)?)), + #[cfg(feature = "mflow")] + DriverType::Memflow => Ok(Box::new(Memflow::new(_init_params)?)), #[cfg(feature = "virtualbox")] DriverType::VirtualBox => Ok(Box::new(VBox::new(_init_params)?)), #[cfg(feature = "xen")] diff --git a/utilities/src/lib.rs b/utilities/src/lib.rs index 75e88cf9..25f035b3 100644 --- a/utilities/src/lib.rs +++ b/utilities/src/lib.rs @@ -1,6 +1,8 @@ /// This crate implements utilities and common code shared by libmicrovmi examples use clap::{Arg, ArgMatches}; -use microvmi::api::params::{CommonInitParams, DriverInitParams, KVMInitParams}; +use microvmi::api::params::{ + CommonInitParams, DriverInitParams, KVMInitParams, MemflowConnectorParams, MemflowInitParams, +}; /// This trait allows to convert a struct to Clap's command line arguments /// and to parse back the matches into the struct @@ -24,6 +26,15 @@ impl Clappable for DriverInitParams { .long("kvm_unix_socket") .takes_value(true) .help("Driver parameter (required for KVM): KVM unix socket path"), + // memflow + Arg::with_name("memflow_connector_name") + .long("memflow_connector_name") + .takes_value(true) + .help("Driver parameter (optional for Memflow): Memflow connector name"), + Arg::with_name("memflow_connector_args") + .long("memflow_connector_args") + .multiple(true) + .min_values(1), ] } @@ -36,9 +47,20 @@ impl Clappable for DriverInitParams { .map(|s| KVMInitParams::UnixSocket { path: String::from(s), }); + let memflow = Some(MemflowInitParams { + connector_name: matches + .value_of("memflow_connector_name") + .map(|s| s.to_string()), + connector_args: matches.values_of("memflow_connector_args").map(|v| { + MemflowConnectorParams::Unknown { + args: v.map(|s| s.to_string()).collect(), + } + }), + }); DriverInitParams { common, kvm, + memflow, ..Default::default() } } @@ -48,7 +70,7 @@ impl Clappable for DriverInitParams { mod tests { use super::Clappable; use clap::App; - use microvmi::api::params::{DriverInitParams, KVMInitParams}; + use microvmi::api::params::{DriverInitParams, KVMInitParams, MemflowConnectorParams}; #[test] fn test_common_vm_name() { @@ -74,4 +96,45 @@ mod tests { params.kvm.unwrap() ); } + + // tests for memflow + #[test] + fn test_memflow_connector_name() { + let cmdline = vec!["test", "--memflow_connector_name=foobar"]; + let matches = App::new("test") + .args(DriverInitParams::to_clap_args().as_ref()) + .get_matches_from(cmdline); + let params = DriverInitParams::from_matches(&matches); + assert_eq!("foobar", params.memflow.unwrap().connector_name.unwrap()) + } + + #[test] + fn test_memflow_connector_args_one() { + let cmdline = vec!["test", "--memflow_connector_args", "first"]; + let matches = App::new("test") + .args(DriverInitParams::to_clap_args().as_ref()) + .get_matches_from(cmdline); + let params = DriverInitParams::from_matches(&matches); + assert_eq!( + MemflowConnectorParams::Unknown { + args: vec!["first".into()] + }, + params.memflow.unwrap().connector_args.unwrap() + ) + } + + #[test] + fn test_memflow_connector_args_multiple() { + let cmdline = vec!["test", "--memflow_connector_args", "first", "second", "third"]; + let matches = App::new("test") + .args(DriverInitParams::to_clap_args().as_ref()) + .get_matches_from(cmdline); + let params = DriverInitParams::from_matches(&matches); + assert_eq!( + MemflowConnectorParams::Unknown { + args: vec!["first".into(), "second".into(), "third".into()] + }, + params.memflow.unwrap().connector_args.unwrap() + ) + } } From bf9dc08a5aad25c323c5842a839a0d41150651be Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Tue, 29 Jun 2021 15:23:41 +0200 Subject: [PATCH 03/14] memflow: parse connector args --- src/api/params.rs | 50 +++++++++++++++++++++---------------------- src/driver/memflow.rs | 44 +++++++++++++++++++++++++++++++++++-- utilities/src/lib.rs | 42 +++++++++++++++++++++++------------- 3 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/api/params.rs b/src/api/params.rs index 34ec5cf1..14f49820 100644 --- a/src/api/params.rs +++ b/src/api/params.rs @@ -13,39 +13,39 @@ pub enum KVMInitParams { /// Memflow connector parameters /// /// This enumeration reflects the possibilities to initialize Memflow -/// - [`qemu_procfs`](https://github.com/memflow/memflow-qemu-procfs) -/// - [`kvm`](https://github.com/memflow/memflow-kvm) -/// - [`pcileech`](https://github.com/memflow/memflow-pcileech) -/// - [`coredump`](https://github.com/memflow/memflow-coredump) -/// - unknown: will simply forward the string arguments to the connector +/// - default: will simply forward the string arguments to the connector +// TODO +// - [`qemu_procfs`](https://github.com/memflow/memflow-qemu-procfs) +// - [`kvm`](https://github.com/memflow/memflow-kvm) +// - [`pcileech`](https://github.com/memflow/memflow-pcileech) +// - [`coredump`](https://github.com/memflow/memflow-coredump) #[derive(Debug, Clone, PartialEq)] pub enum MemflowConnectorParams { - // optional vm_name, otherwise will search for the first QEMU process - QEMUProcFs { - vm_name: Option, - }, - KVM { - pid: u32, - }, - // default value for device: "FPGA" - PCILeech { - device: Option, - memmap: Option, - }, - Coredump { - filepath: String, - }, // allow to pass an abritrary list of Strings as parameters - Unknown { - args: Vec, - }, + Default { args: Vec }, + // TODO + // // optional vm_name, otherwise will search for the first QEMU process + // QEMUProcFs { + // vm_name: Option, + // }, + // KVM { + // pid: u32, + // }, + // // default value for device: "FPGA" + // PCILeech { + // device: Option, + // memmap: Option, + // }, + // Coredump { + // filepath: String, + // }, } /// Memflow initialization parameters #[derive(Debug, Default, Clone, PartialEq)] pub struct MemflowInitParams { - /// optional connector name - pub connector_name: Option, + /// connector name + pub connector_name: String, /// optional connector initialization parameters pub connector_args: Option, } diff --git a/src/driver/memflow.rs b/src/driver/memflow.rs index 6f4c40f1..72fb08aa 100644 --- a/src/driver/memflow.rs +++ b/src/driver/memflow.rs @@ -1,11 +1,51 @@ -use crate::api::params::DriverInitParams; +use crate::api::params::{DriverInitParams, MemflowConnectorParams}; use crate::api::{DriverType, Introspectable}; use std::error::Error; +use memflow::connector::{ConnectorArgs, ConnectorInventory}; + +#[derive(thiserror::Error, Debug)] +pub enum MemflowDriverError { + #[error("Memfow driver initialization requires a connector parameter")] + MissingConnectorParameter, + #[error("Invalid format for Memflow connector argument (key=value), got {0}")] + InvalidConnectorArgument(String), +} + pub struct Memflow; impl Memflow { - pub fn new(_init_params: DriverInitParams) -> Result> { + pub fn new(init_params: DriverInitParams) -> Result> { + info!("init Memflow"); + // check connector name + let memflow_init_params = init_params + .memflow + .ok_or(MemflowDriverError::MissingConnectorParameter)?; + // create inventory + let inventory = unsafe { ConnectorInventory::scan() }; + // parse connector args + let mut create_connector_args = ConnectorArgs::new(); + if memflow_init_params.connector_args.is_some() { + let MemflowConnectorParams::Default { args } = + memflow_init_params.connector_args.unwrap(); + // for each string, split at '=' to get key, value + for s in args.iter() { + let (key, value) = s + .split_once("=") + .ok_or_else(|| MemflowDriverError::InvalidConnectorArgument(s.clone()))?; + // push it into memflow ConnectorArgs type + create_connector_args = create_connector_args.insert(key, value); + } + } + // create memflow connector + debug!( + "Memflow: create connector - name: {}, args: {:#?}", + &memflow_init_params.connector_name, &create_connector_args + ); + unsafe { + inventory + .create_connector(&memflow_init_params.connector_name, &create_connector_args)? + }; Ok(Memflow {}) } } diff --git a/utilities/src/lib.rs b/utilities/src/lib.rs index 25f035b3..c8c9f533 100644 --- a/utilities/src/lib.rs +++ b/utilities/src/lib.rs @@ -47,16 +47,16 @@ impl Clappable for DriverInitParams { .map(|s| KVMInitParams::UnixSocket { path: String::from(s), }); - let memflow = Some(MemflowInitParams { - connector_name: matches - .value_of("memflow_connector_name") - .map(|s| s.to_string()), - connector_args: matches.values_of("memflow_connector_args").map(|v| { - MemflowConnectorParams::Unknown { - args: v.map(|s| s.to_string()).collect(), - } - }), - }); + let memflow = matches + .value_of("memflow_connector_name") + .map(|name| MemflowInitParams { + connector_name: name.to_string(), + connector_args: matches.values_of("memflow_connector_args").map(|v| { + MemflowConnectorParams::Default { + args: v.map(|s| s.to_string()).collect(), + } + }), + }); DriverInitParams { common, kvm, @@ -105,18 +105,23 @@ mod tests { .args(DriverInitParams::to_clap_args().as_ref()) .get_matches_from(cmdline); let params = DriverInitParams::from_matches(&matches); - assert_eq!("foobar", params.memflow.unwrap().connector_name.unwrap()) + assert_eq!("foobar", params.memflow.unwrap().connector_name) } #[test] fn test_memflow_connector_args_one() { - let cmdline = vec!["test", "--memflow_connector_args", "first"]; + let cmdline = vec![ + "test", + "--memflow_connector_name=foobar", + "--memflow_connector_args", + "first", + ]; let matches = App::new("test") .args(DriverInitParams::to_clap_args().as_ref()) .get_matches_from(cmdline); let params = DriverInitParams::from_matches(&matches); assert_eq!( - MemflowConnectorParams::Unknown { + MemflowConnectorParams::Default { args: vec!["first".into()] }, params.memflow.unwrap().connector_args.unwrap() @@ -125,13 +130,20 @@ mod tests { #[test] fn test_memflow_connector_args_multiple() { - let cmdline = vec!["test", "--memflow_connector_args", "first", "second", "third"]; + let cmdline = vec![ + "test", + "--memflow_connector_name=foobar", + "--memflow_connector_args", + "first", + "second", + "third", + ]; let matches = App::new("test") .args(DriverInitParams::to_clap_args().as_ref()) .get_matches_from(cmdline); let params = DriverInitParams::from_matches(&matches); assert_eq!( - MemflowConnectorParams::Unknown { + MemflowConnectorParams::Default { args: vec!["first".into(), "second".into(), "third".into()] }, params.memflow.unwrap().connector_args.unwrap() From 62cc869bf5926ef8196a475ab8d827312c3e350e Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Tue, 29 Jun 2021 15:28:38 +0200 Subject: [PATCH 04/14] memflow: impl read_physical --- src/driver/memflow.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/driver/memflow.rs b/src/driver/memflow.rs index 72fb08aa..c7992f7d 100644 --- a/src/driver/memflow.rs +++ b/src/driver/memflow.rs @@ -2,7 +2,9 @@ use crate::api::params::{DriverInitParams, MemflowConnectorParams}; use crate::api::{DriverType, Introspectable}; use std::error::Error; -use memflow::connector::{ConnectorArgs, ConnectorInventory}; +use memflow::connector::{ConnectorArgs, ConnectorInstance, ConnectorInventory}; +use memflow::{PhysicalAddress, PhysicalMemory}; +use std::cell::RefCell; #[derive(thiserror::Error, Debug)] pub enum MemflowDriverError { @@ -12,7 +14,11 @@ pub enum MemflowDriverError { InvalidConnectorArgument(String), } -pub struct Memflow; +pub struct Memflow { + // refcell required because read methods are mutable + // contrary to our read_frame signature + connector: RefCell, +} impl Memflow { pub fn new(init_params: DriverInitParams) -> Result> { @@ -42,15 +48,30 @@ impl Memflow { "Memflow: create connector - name: {}, args: {:#?}", &memflow_init_params.connector_name, &create_connector_args ); - unsafe { + let connector = unsafe { inventory .create_connector(&memflow_init_params.connector_name, &create_connector_args)? }; - Ok(Memflow {}) + Ok(Memflow { + connector: RefCell::new(connector), + }) } } impl Introspectable for Memflow { + fn read_physical( + &self, + paddr: u64, + buf: &mut [u8], + bytes_read: &mut u64, + ) -> Result<(), Box> { + self.connector + .borrow_mut() + .phys_read_into(PhysicalAddress::from(paddr), buf)?; + *bytes_read = buf.len() as u64; + Ok(()) + } + fn get_driver_type(&self) -> DriverType { DriverType::Memflow } From 60bdbb4d4a59c6b4ce63f731d925f3261160eee2 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Tue, 29 Jun 2021 15:33:30 +0200 Subject: [PATCH 05/14] ci: add memflow job --- .github/workflows/ci.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90d0d21b..71abfa81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,6 +126,29 @@ jobs: flags: unittests fail_ci_if_error: true + memflow: + runs-on: ubuntu-20.04 + + steps: + - name: install stable toolchain with clippy + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: clippy + - uses: actions/checkout@v1 + - name: build Memflow driver + uses: actions-rs/cargo@v1 + with: + command: build + args: --features mflow + - name: annotate commit with clippy warnings + uses: actions-rs/clippy-check@v1 + with: + name: clippy memflow + token: ${{ secrets.GITHUB_TOKEN }} + args: --features mflow -- -D warnings + virtualbox: runs-on: ubuntu-20.04 From 99eb60d1a8516af03370d5043c32930a067cbeef Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 11:48:40 +0200 Subject: [PATCH 06/14] python/Cargo: expose memflow driver --- python/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/Cargo.toml b/python/Cargo.toml index b4a02f59..0d48276c 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -23,6 +23,8 @@ xen = ["microvmi/xen"] kvm = ["microvmi/kvm"] # VirtualBox driver virtualbox = ["microvmi/virtualbox"] +# memflow driver +mflow = ["microvmi/mflow"] [dependencies] log = "0.4" From a56d4e48b02dc8a66d366e6f1a223f573b0c53ab Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 11:49:10 +0200 Subject: [PATCH 07/14] python: expose memflow init params --- python/microvmi/__init__.py | 2 +- python/src/lib.rs | 9 ++++++++- python/src/params.rs | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/python/microvmi/__init__.py b/python/microvmi/__init__.py index 898f4a46..3f201c8b 100644 --- a/python/microvmi/__init__.py +++ b/python/microvmi/__init__.py @@ -1,3 +1,3 @@ from microvmi.microvmi import DriverType, Microvmi -from .pymicrovmi import CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy +from .pymicrovmi import CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy, MemflowInitParamsPy diff --git a/python/src/lib.rs b/python/src/lib.rs index 9236ba15..da927482 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -11,7 +11,7 @@ use errors::PyMicrovmiError; use microvmi::api as rapi; // rust api use microvmi::api::params as rparams; // rust params use microvmi::init; -use params::{CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy}; +use params::{CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy, MemflowInitParamsPy}; /// microvmi Python module declaration #[pymodule] @@ -24,6 +24,7 @@ fn pymicrovmi(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; Ok(()) } @@ -89,6 +90,12 @@ impl MicrovmiExt { kvm: v.kvm.map(|k| rparams::KVMInitParams::UnixSocket { path: k.unix_socket, }), + memflow: v.memflow.map(|k| rparams::MemflowInitParams { + connector_name: k.connector_name, + connector_args: Some(rparams::MemflowConnectorParams::Default { + args: k.connector_args, + }), + }), ..Default::default() }); diff --git a/python/src/params.rs b/python/src/params.rs index 933c7e63..5c9bae40 100644 --- a/python/src/params.rs +++ b/python/src/params.rs @@ -33,6 +33,27 @@ impl KVMInitParamsPy { } } +/// equivalent of `MemflowInitParams` for Python +#[pyclass] +#[derive(Default, Debug, Clone)] +pub struct MemflowInitParamsPy { + #[pyo3(get, set)] + pub connector_name: String, + #[pyo3(get, set)] + pub connector_args: Vec, +} + +#[pymethods] +impl MemflowInitParamsPy { + #[new] + fn new(name: &str) -> Self { + Self { + connector_name: String::from(name), + connector_args: Vec::new(), + } + } +} + /// equivalent of `DriverInitParams` for Python /// /// # Examples @@ -58,6 +79,8 @@ pub struct DriverInitParamsPy { pub common: Option, #[pyo3(get, set)] pub kvm: Option, + #[pyo3(get, set)] + pub memflow: Option, } #[pymethods] From 99cda4449265f13bc96aba023d189607213cbc3f Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 11:49:21 +0200 Subject: [PATCH 08/14] python: handle memflow init params in volatility handler --- python/microvmi/volatility/vmi_handler.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/python/microvmi/volatility/vmi_handler.py b/python/microvmi/volatility/vmi_handler.py index 37c99f0b..88c0e59a 100644 --- a/python/microvmi/volatility/vmi_handler.py +++ b/python/microvmi/volatility/vmi_handler.py @@ -3,7 +3,7 @@ from urllib.parse import parse_qs, urlparse from urllib.request import BaseHandler, Request -from microvmi import CommonInitParamsPy, DriverInitParamsPy, DriverType, KVMInitParamsPy, Microvmi +from microvmi import CommonInitParamsPy, DriverInitParamsPy, DriverType, KVMInitParamsPy, MemflowInitParamsPy, Microvmi # to be used by volatility, the VMIHandler should inherit from VolatilityHandler # in order to be non cacheable @@ -101,6 +101,7 @@ def _parse_driver_init_params(query: str) -> Optional[DriverInitParamsPy]: return None common = None kvm = None + memflow = None for param, list_value in url_params.items(): if param == "vm_name": common = CommonInitParamsPy() @@ -108,9 +109,16 @@ def _parse_driver_init_params(query: str) -> Optional[DriverInitParamsPy]: elif param == "kvm_unix_socket": kvm = KVMInitParamsPy() kvm.unix_socket = list_value[0] + elif param == "memflow_connector_name": + memflow = MemflowInitParamsPy(list_value[0]) + elif param == "memflow_connector_args": + if memflow is None: + raise MicrovmiHandlerError("memflow connector args received but no connector name specified") + memflow.connector_args = list_value else: raise MicrovmiHandlerError(f"Unknown driver initialization parameter: {param}") init_params = DriverInitParamsPy() init_params.common = common init_params.kvm = kvm + init_params.memflow = memflow return init_params From 850fa319992f075af679276b3cab9cf73a101784 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 11:49:45 +0200 Subject: [PATCH 09/14] nox: add test_volatility_memflow session --- python/noxfile.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/python/noxfile.py b/python/noxfile.py index 2c2b5c96..520b6e44 100644 --- a/python/noxfile.py +++ b/python/noxfile.py @@ -105,6 +105,36 @@ def test_volatility_kvm(session): ) +@nox.session +def test_volatility_memflow(session): + """Run the PsList volatility plugin on the memflow connector specified by the URL""" + # example: + # nox -r -s test_volatility_memflow -- vmi:///?memflow_connector_name=qemu_procfs + args = session.posargs + if not args: + raise RuntimeError("URL required. Example: nox -r -s test_volatility_memflow -- vmi:///...") + # we need to compile and install the extension + session.install("-r", "requirements.txt") + # make sure we have volatility + # Note: we need to use the latest unreleased dev code from Github + session.install("git+https://github.com/volatilityfoundation/volatility3@af090bf29e6bb26a5961e0a6c25b5d1ec6e82498") + # can't use pip install + # see: https://github.com/PyO3/maturin/issues/330 + session.run(f'{CUR_DIR / "setup.py"}', "develop", "--features", "mflow") + vol_path = Path(__file__).parent / ".nox" / "test_volatility_memflow" / "bin" / "vol" + plugins_dir = Path(__file__).parent / "microvmi" / "volatility" + session.run( + "sudo", + "-E", + str(vol_path), + "--plugin-dirs", + str(plugins_dir), + "--single-location", + *args, + "windows.pslist.PsList", + ) + + @nox.session def coverage_html(session): session.install("coverage==5.3") From 041b996627d865b3c957a5c26700f784a1a91352 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 11:53:57 +0200 Subject: [PATCH 10/14] memflow: impl get_max_physical_address --- src/driver/memflow.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/driver/memflow.rs b/src/driver/memflow.rs index c7992f7d..cbb50201 100644 --- a/src/driver/memflow.rs +++ b/src/driver/memflow.rs @@ -72,6 +72,10 @@ impl Introspectable for Memflow { Ok(()) } + fn get_max_physical_addr(&self) -> Result> { + Ok(self.connector.borrow_mut().metadata().size as u64) + } + fn get_driver_type(&self) -> DriverType { DriverType::Memflow } From 1658088ac035be5dfe55f6131bafad174a4df55b Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 14:40:28 +0200 Subject: [PATCH 11/14] src: fix Github doc URL --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 88cec111..230df694 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! libmicrovmi is a cross-platform unified virtual machine introsection interface, following a simple design to keep interoperability at heart. //! -//! Click on this [book 📖](https://libmicrovmi.github.io/) to find our project documentation. +//! Click on this [book 📖](https://wenzel.github.io/libmicrovmi/) to find our project documentation. //! //! The library's entrypoint is the [init](fn.init.html) function. From 5dd80c1e521485c043f2bad54ec0956f77291cfe Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 15:11:40 +0200 Subject: [PATCH 12/14] doc: update drivers reference --- doc/src/reference/drivers.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/src/reference/drivers.md b/doc/src/reference/drivers.md index 8dc4de94..6eeab460 100644 --- a/doc/src/reference/drivers.md +++ b/doc/src/reference/drivers.md @@ -9,12 +9,18 @@ This section documents the drivers available and the requirements to compile the | `xen` | Build the Xen driver | | `kvm` | Build the KVM driver | | `virtualbox` | Build the VirtualBox driver | +| `mflow` | Build the memflow driver | Example ~~~ $ cargo build --features xen,kvm ~~~ +## Rust API initialization parameters + +To initialize each Driver from the Rust API, +please check [`DriverInitParams`](https://docs.rs/microvmi/api/params/struct.DriverInitParams.html). + ## Xen ~~~ @@ -48,3 +54,7 @@ $ g++ -std=c++11 -shared -fPIC FDP.cpp -o libFDP.so $ sudo mv include/* /usr/local/include/ $ sudo mv libFDP.so /usr/local/lib/ ~~~ + +## Memflow + +Please follow the instructions at [memflow](https://github.com/memflow/memflow) From c53c9d48196ca3c7a7af4479cb89a40474f94f34 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 15:47:14 +0200 Subject: [PATCH 13/14] src: update DriverInitParams doc --- src/api/params.rs | 39 ++++++++++++++++++++++++++++++++++++++- src/lib.rs | 2 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/api/params.rs b/src/api/params.rs index 14f49820..6d39cbc5 100644 --- a/src/api/params.rs +++ b/src/api/params.rs @@ -1,4 +1,7 @@ -/// This module describes initialization parameters for all libmicrovmi drivers +//! This module describes initialization parameters for all libmicrovmi drivers +//! +//! The [`DriverInitParams`](struct.DriverInitParams.html) is used to pass additional driver initialization parameters. +//! You might want to check it's documentation for examples on how to initialize your driver. /// Xen initialization parameters #[derive(Debug, Clone, PartialEq)] @@ -65,6 +68,40 @@ pub struct CommonInitParams { } /// This struct is used to specify the initialization parameters for all drivers +/// +/// # Examples +/// +/// ```no_run +/// // Xen +/// // common.vm_name: mandatory +/// use microvmi::api::params::{DriverInitParams, CommonInitParams, KVMInitParams, MemflowInitParams}; +/// let init_params = DriverInitParams { +/// common: Some(CommonInitParams { vm_name: String::from("windows10")}), +/// ..Default::default() +/// }; +/// // KVM +/// // common.vm_name: mandatory +/// // kvm.unix_socket: mandatory +/// let init_params = DriverInitParams { +/// common: Some(CommonInitParams { vm_name: String::from("windows10")}), +/// kvm: Some(KVMInitParams::UnixSocket { path: String::from("/tmp/introspector")}), +/// ..Default::default() +/// }; +/// // VirtualBox +/// // common.vm_name: mandatory +/// let init_params = DriverInitParams { +/// common: Some(CommonInitParams { vm_name: String::from("windows10")}), +/// ..Default::default() +/// }; +/// // Memflow +/// // memflow.connector_name: mandatory +/// // memflow.connector_args: optional +/// let init_params = DriverInitParams { +/// memflow: Some(MemflowInitParams { connector_name: String::from("qemu_procfs"), /// +/// ..Default::default()}), +/// ..Default::default() +/// }; +/// ``` #[derive(Default, Debug, Clone, PartialEq)] pub struct DriverInitParams { pub common: Option, diff --git a/src/lib.rs b/src/lib.rs index 230df694..8ea234b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,8 @@ use kvmi::create_kvmi; /// This function will initialize a libmicrovmi driver and call the hypervisor VMI API. /// It returns a `Box` trait object, which implements the [Introspectable](api/trait.Introspectable.html) trait. /// +/// For complete documentation on driver init params, please check [DriverInitParams](struct.DriverInitParams.html) struct. +/// /// # Arguments /// * `driver_type`: optional driver type to initialize. If None, all compiled drivers will be initialized one by one. The first that succeeds will be returned. /// * `init_params`: optional driver initialization parameters From 7cb60c8b4863bb9f01aa83ebd6e45a95b881daa8 Mon Sep 17 00:00:00 2001 From: Mathieu Tarral Date: Fri, 9 Jul 2021 15:50:13 +0200 Subject: [PATCH 14/14] ci: release with memflow enabled --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71abfa81..7b8676f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -271,7 +271,7 @@ jobs: uses: satackey/action-docker-layer-caching@v0.0.11 - name: Build Wheels with manylinux - run: nox -r -s generate_wheels -- --features xen,kvm,virtualbox --release + run: nox -r -s generate_wheels -- --features xen,kvm,virtualbox,mflow --release working-directory: python # upload all generated wheels *.whl @@ -334,7 +334,7 @@ jobs: run: cargo install cargo-deb - name: build debian package - run: cargo deb -- --features xen,kvm,virtualbox + run: cargo deb -- --features xen,kvm,virtualbox,mflow - name: upload artifact uses: actions/upload-artifact@v2