From 2eb3ca73e6f3290036f1676bd62f21d0b0600c6a Mon Sep 17 00:00:00 2001 From: ko1N Date: Fri, 16 Apr 2021 17:59:05 +0200 Subject: [PATCH] Updated to latest memflow, added set_mem_map support --- memflow-pcileech/Cargo.toml | 14 +++++- memflow-pcileech/examples/ps_inventory.rs | 57 +++++++++++++++++++++ memflow-pcileech/examples/ps_win32.rs | 56 +++++++++++++++++++++ memflow-pcileech/examples/read_phys.rs | 30 ++++++------ memflow-pcileech/src/lib.rs | 60 +++++++++++++++++------ 5 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 memflow-pcileech/examples/ps_inventory.rs create mode 100644 memflow-pcileech/examples/ps_win32.rs diff --git a/memflow-pcileech/Cargo.toml b/memflow-pcileech/Cargo.toml index 82e34e2..35fde2c 100644 --- a/memflow-pcileech/Cargo.toml +++ b/memflow-pcileech/Cargo.toml @@ -16,11 +16,15 @@ categories = [ "api-bindings", "memory-management", "os" ] crate-type = ["lib", "cdylib"] [dependencies] -memflow = { git = "https://github.com/memflow/memflow", branch = "next", features = ["inventory", "memmapfiles"] } +memflow = { git = "https://github.com/memflow/memflow", branch = "next", features = ["plugins", "memmapfiles"] } log = { version = "0.4", default-features = false } simple_logger = "1.0" leechcore-sys = { path = "../leechcore-sys" } +[dev-dependencies] +clap = "2.33" +memflow-win32 = { git = "https://github.com/memflow/memflow", branch = "next" } + [features] default = [] inventory = [] @@ -28,3 +32,11 @@ inventory = [] [[example]] name = "read_phys" path = "examples/read_phys.rs" + +[[example]] +name = "ps_win32" +path = "examples/ps_win32.rs" + +[[example]] +name = "ps_inventory" +path = "examples/ps_inventory.rs" diff --git a/memflow-pcileech/examples/ps_inventory.rs b/memflow-pcileech/examples/ps_inventory.rs new file mode 100644 index 0000000..b1caab4 --- /dev/null +++ b/memflow-pcileech/examples/ps_inventory.rs @@ -0,0 +1,57 @@ +/*! +This example shows how to use the pcileech connector in conjunction +with a specific OS layer. This example uses the `Inventory` feature of memflow +to create the connector itself and the os instance. + +The example is an adaption of the memflow core process list example: +https://github.com/memflow/memflow/blob/next/memflow/examples/process_list.rs + +# Remarks: +To run this example you must have the `pcileech` connector and `win32` plugin installed on your system. +Make sure they can be found in one of the following locations: + +~/.local/lib/memflow/ +/usr/lib/memflow/ + +or in any other path found in the official memflow documentation. +*/ +use std::env::args; + +use log::{info, Level}; + +use memflow::prelude::v1::*; + +fn main() { + simple_logger::SimpleLogger::new() + .with_level(Level::Debug.to_level_filter()) + .init() + .unwrap(); + + let connector_args = if let Some(arg) = args().nth(1) { + Args::parse(arg.as_ref()).expect("unable to parse command line arguments") + } else { + Args::default() + }; + + let inventory = Inventory::scan(); + let connector = inventory + .create_connector("pcileech", None, &connector_args) + .expect("unable to create pcileech connector"); + let mut os = inventory + .create_os("win32", Some(connector), &Args::default()) + .expect("unable to create win32 instance with pcileech connector"); + + let process_list = os.process_info_list().expect("unable to read process list"); + + info!( + "{:>5} {:>10} {:>10} {:<}", + "PID", "SYS ARCH", "PROC ARCH", "NAME" + ); + + for p in process_list { + info!( + "{:>5} {:^10} {:^10} {}", + p.pid, p.sys_arch, p.proc_arch, p.name + ); + } +} diff --git a/memflow-pcileech/examples/ps_win32.rs b/memflow-pcileech/examples/ps_win32.rs new file mode 100644 index 0000000..96f78e7 --- /dev/null +++ b/memflow-pcileech/examples/ps_win32.rs @@ -0,0 +1,56 @@ +/*! +This example shows how to use the pcileech connector in conjunction +with a specific OS layer. This example does not use the `Inventory` feature of memflow +but hard-wires the connector instance with the OS layer directly. + +The example is an adaption of the memflow core process list example: +https://github.com/memflow/memflow/blob/next/memflow/examples/process_list.rs + +# Remarks: +The most flexible and recommended way to use memflow is to go through the inventory. +The inventory allows the user to swap out connectors and os layers at runtime. +For more information about the Inventory see the ps_inventory.rs example in this repository +or check out the documentation at: +https://docs.rs/memflow/0.1.5/memflow/connector/inventory/index.html +*/ +use std::env::args; + +use log::{info, Level}; + +use memflow::prelude::v1::*; +use memflow_win32::prelude::v1::*; + +fn main() { + simple_logger::SimpleLogger::new() + .with_level(Level::Debug.to_level_filter()) + .init() + .unwrap(); + + let connector_args = if let Some(arg) = args().nth(1) { + Args::parse(arg.as_ref()).expect("unable to parse command line arguments") + } else { + Args::default() + }; + + let connector = memflow_pcileech::create_connector(&connector_args, Level::Debug) + .expect("unable to create pcileech connector"); + + let mut os = Win32Kernel::builder(connector) + .build_default_caches() + .build() + .expect("unable to create win32 instance with pcileech connector"); + + let process_list = os.process_info_list().expect("unable to read process list"); + + info!( + "{:>5} {:>10} {:>10} {:<}", + "PID", "SYS ARCH", "PROC ARCH", "NAME" + ); + + for p in process_list { + info!( + "{:>5} {:^10} {:^10} {}", + p.pid, p.sys_arch, p.proc_arch, p.name + ); + } +} diff --git a/memflow-pcileech/examples/read_phys.rs b/memflow-pcileech/examples/read_phys.rs index 179b2d2..1881051 100644 --- a/memflow-pcileech/examples/read_phys.rs +++ b/memflow-pcileech/examples/read_phys.rs @@ -1,4 +1,8 @@ -use std::env; +/*! +This example shows how to use the pcileech connector to read physical_memory +from a target machine. It also evaluates the number of read cycles per second +and prints them to stdout. +*/ use std::time::Instant; use log::{info, Level}; @@ -11,30 +15,28 @@ fn main() { .init() .unwrap(); - let args: Vec = env::args().collect(); - let conn_args = if args.len() > 1 { - ConnectorArgs::parse(&args[1]).expect("unable to parse arguments") - } else { - ConnectorArgs::new() - }; + let mut connector = memflow_pcileech::create_connector(&Args::default(), Level::Debug) + .expect("unable to create pcileech connector"); - let mut conn = memflow_pcileech::create_connector(Level::Debug, &conn_args) - .expect("unable to initialize memflow_pcileech"); + let metadata = connector.metadata(); + info!("Received metadata: {:?}", metadata); let mut mem = vec![0; 8]; - conn.phys_read_raw_into(Address::from(0x1000).into(), &mut mem) - .unwrap(); + connector + .phys_read_raw_into(Address::from(0x1000).into(), &mut mem) + .expect("unable to read physical memory"); info!("Received memory: {:?}", mem); let start = Instant::now(); let mut counter = 0; loop { let mut buf = vec![0; 0x1000]; - conn.phys_read_raw_into(Address::from(0x1000).into(), &mut buf) - .unwrap(); + connector + .phys_read_raw_into(Address::from(0x1000).into(), &mut buf) + .expect("unable to read physical memory"); counter += 1; - if (counter % 10000) == 0 { + if (counter % 10000000) == 0 { let elapsed = start.elapsed().as_millis() as f64; if elapsed > 0.0 { info!("{} reads/sec", (f64::from(counter)) / elapsed * 1000.0); diff --git a/memflow-pcileech/src/lib.rs b/memflow-pcileech/src/lib.rs index 30f9e3f..3f35008 100644 --- a/memflow-pcileech/src/lib.rs +++ b/memflow-pcileech/src/lib.rs @@ -93,7 +93,8 @@ impl PciLeech { // TODO: handle version error // TODO: handle special case of fUserInputRequest error!("leechcore error: {:?}", err); - return Err(Error::Connector("unable to create leechcore context")); + return Err(Error(ErrorOrigin::Connector, ErrorKind::Configuration) + .log_error("unable to create leechcore context")); } Ok(Self { @@ -146,7 +147,8 @@ impl PhysicalMemory for PciLeech { ) }; if result != 1 { - return Err(Error::Connector("unable to allocate scatter buffer")); + return Err(Error(ErrorOrigin::Connector, ErrorKind::InvalidMemorySize) + .log_error("unable to allocate scatter buffer")); } // prepare mems @@ -243,7 +245,8 @@ impl PhysicalMemory for PciLeech { ) }; if result != 1 { - return Err(Error::Connector("unable to allocate scatter buffer")); + return Err(Error(ErrorOrigin::Connector, ErrorKind::InvalidMemorySize) + .log_error("unable to allocate scatter buffer")); } // prepare mems @@ -337,24 +340,53 @@ impl PhysicalMemory for PciLeech { fn metadata(&self) -> PhysicalMemoryMetadata { self.metadata } + + fn set_mem_map(&mut self, mem_map: MemoryMap<(Address, usize)>) { + // TODO: check if current mem_map is empty + // TODO: update metadata.size + self.mem_map = mem_map; + } } /// Creates a new PciLeech Connector instance. -#[connector(name = "pcileech", ty = "PciLeech")] -pub fn create_connector(log_level: Level, args: &ConnectorArgs) -> Result { +pub fn create_connector(args: &Args, log_level: Level) -> Result { simple_logger::SimpleLogger::new() .with_level(log_level.to_level_filter()) .init() .ok(); - let device = args - .get("device") - .or_else(|| args.get_default()) - .ok_or(Error::Connector("argument 'device' missing"))?; - - if let Some(memmap) = args.get("memmap") { - PciLeech::with_memmap(device, memmap) - } else { - PciLeech::new(device) + let validator = ArgsValidator::new() + .arg(ArgDescriptor::new("default").description("the target device to be used by LeechCore")) + .arg(ArgDescriptor::new("device").description("the target device to be used by LeechCore")) + .arg(ArgDescriptor::new("memmap").description("the memory map file of the target machine")); + + match validator.validate(&args) { + Ok(_) => { + let device = args.get("device").or_else(|| args.get_default()).ok_or( + Error(ErrorOrigin::Connector, ErrorKind::ArgValidation) + .log_error("'device' argument is missing"), + )?; + + if let Some(memmap) = args.get("memmap") { + PciLeech::with_memmap(device, memmap) + } else { + PciLeech::new(device) + } + } + Err(err) => { + error!( + "unable to validate provided arguments, valid arguments are:\n{}", + validator + ); + Err(err) + } } } + +/// Creates a new PciLeech Connector instance. +#[connector(name = "pcileech")] +pub fn create_connector_instance(args: &Args, log_level: Level) -> Result { + let connector = create_connector(args, log_level)?; + let instance = ConnectorInstance::builder(connector).build(); + Ok(instance) +}