Merge pull request #7 from memflow/next

Update to memflow 0.2.0-beta
next 0.2.0-beta1
ko1N 3 years ago committed by GitHub
commit 4e51d1572b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -2,4 +2,3 @@
**/*.rs.bk **/*.rs.bk
*.swp *.swp
.vscode .vscode
Cargo.lock

1554
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,3 +1,9 @@
[profile.bench]
debug = true
[profile.release]
lto = "fat"
[workspace] [workspace]
members = [ members = [
"leechcore-sys", "leechcore-sys",
@ -7,6 +13,3 @@ default-members = [
"leechcore-sys", "leechcore-sys",
"memflow-pcileech", "memflow-pcileech",
] ]
[profile.release]
lto = true

@ -1,6 +1,6 @@
# memflow-pcileech # memflow-pcileech
This connector implements a rust-native implementation of the pcileech interface. This connector implements the [LeechCore](https://github.com/ufrisk/LeechCore) interface of pcileech for memflow.
More information about pcileech can be found under https://github.com/ufrisk/pcileech. More information about pcileech can be found under https://github.com/ufrisk/pcileech.
@ -48,7 +48,6 @@ Remarks: The `install.sh` script does currently not place the `leechcore_ft601_d
### Building the stand-alone connector for dynamic loading ### Building the stand-alone connector for dynamic loading
The stand-alone connector of this library is feature-gated behind the `inventory` feature.
To compile a dynamic library for use with the connector inventory use the following command: To compile a dynamic library for use with the connector inventory use the following command:
``` ```
@ -79,7 +78,7 @@ let conn_args = if args.len() > 1 {
ConnectorArgs::new() ConnectorArgs::new()
}; };
let mut conn = memflow_pcileech::create_connector(&conn_args) let mut conn = memflow_pcileech::create_connector(&conn_args, log::Level::Debug)
.expect("unable to initialize memflow_pcileech"); .expect("unable to initialize memflow_pcileech");
``` ```
@ -110,6 +109,14 @@ On Windows systems the memory map can be obtained from the Registry under the fo
HKEY_LOCAL_MACHINE\\HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory\\.Translated HKEY_LOCAL_MACHINE\\HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory\\.Translated
``` ```
In case no memory mappings are provided by the user the connector will use the memory mappings found by the os integration (e.g. win32).
## Troubleshooting
Q: The plugin is not detected/found by memflow
A: Make sure to compile the plugin with the correct flags. See the [usage section](#using-the-library-in-a-rust-project) for more information.
## License ## License
Licensed under GPL-3.0 License, see [LICENSE](LICENSE). Licensed under GPL-3.0 License, see [LICENSE](LICENSE).

@ -0,0 +1,27 @@
// builds the connector from a given path (never called by the engine directly)
fn build_from_path(ctx, repo_path) {
info("Installing connector");
cargo("build --release --all-features", repo_path);
ctx.copy_cargo_plugin_artifact(repo_path, name_to_lib(ctx.crate_name()));
// TODO: download leechcore_ft601_driver_linux
}
// builds the connector from local path
fn build_local(ctx) {
build_from_path(ctx, ctx.build_path())
}
fn get_source(ctx) {
ctx.clone_repository()
}
// builds the connector from source
fn build_from_source(ctx) {
build_from_path(ctx, get_source(ctx))
}
// downloads a binary release of the plugin, still needs dkms
fn install(ctx) {
}

@ -8,7 +8,7 @@ if [ ! -z "$1" ] && [ $1 = "--system" ]; then
if [[ ! -d /usr/lib/memflow ]]; then if [[ ! -d /usr/lib/memflow ]]; then
sudo mkdir /usr/lib/memflow sudo mkdir /usr/lib/memflow
fi fi
sudo cp target/release/libmemflow_pcileech.so /usr/lib/memflow sudo cp target/release/libmemflow_pcileech.so /usr/lib/memflow/libmemflow_pcileech.7.so
fi fi
# install connector in user dir # install connector in user dir
@ -16,4 +16,4 @@ echo "installing connector for user in ~/.local/lib/memflow"
if [[ ! -d ~/.local/lib/memflow ]]; then if [[ ! -d ~/.local/lib/memflow ]]; then
mkdir -p ~/.local/lib/memflow mkdir -p ~/.local/lib/memflow
fi fi
cp target/release/libmemflow_pcileech.so ~/.local/lib/memflow cp target/release/libmemflow_pcileech.so ~/.local/lib/memflow/libmemflow_pcileech.7.so

@ -1,10 +1,11 @@
[package] [package]
name = "leechcore-sys" name = "leechcore-sys"
version = "0.1.5" version = "0.2.0-beta1"
authors = ["ko1N <ko1N1337@gmail.com>"] authors = ["ko1N <ko1N1337@gmail.com>"]
edition = "2018" edition = "2018"
readme = "../README.md" readme = "../README.md"
license-file = "../LICENSE" license-file = "../LICENSE"
license = "GPL-3.0"
links = "leechcore" links = "leechcore"

@ -32,6 +32,7 @@ fn build_leechcore(target: &str) {
"device_pmem.c", "device_pmem.c",
"device_tmd.c", "device_tmd.c",
"device_usb3380.c", "device_usb3380.c",
"device_vmware.c",
"leechrpcclient.c", "leechrpcclient.c",
]; ];
if target.contains("windows") { if target.contains("windows") {
@ -47,7 +48,8 @@ fn build_leechcore(target: &str) {
.map(|o| "src/leechcore/leechcore/".to_string() + o) .map(|o| "src/leechcore/leechcore/".to_string() + o)
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) )
.flag(&format!("-D{}", os_define())); .flag(&format!("-D{}", os_define()))
.flag("-D_GNU_SOURCE");
// EXPORTED_FUNCTION= to not export any symbols // EXPORTED_FUNCTION= to not export any symbols
if !target.contains("windows") { if !target.contains("windows") {
@ -105,61 +107,13 @@ fn main() {
// generate bindings // generate bindings
let mut builder = bindgen::builder() let mut builder = bindgen::builder()
.clang_arg(format!("-D{}", os_define())) .clang_arg(format!("-D{} -D_GNU_SOURCE", os_define()))
.header("./src/leechcore/leechcore/leechcore.h"); .header("./src/leechcore/leechcore/leechcore.h");
// workaround for windows.h // workaround for windows.h
// see https://github.com/rust-lang/rust-bindgen/issues/1556 // see https://github.com/rust-lang/rust-bindgen/issues/1556
if target.contains("windows") { if target.contains("windows") {
builder = builder builder = builder.blacklist_type("_?P?IMAGE_TLS_DIRECTORY.*")
.blacklist_type("LPMONITORINFOEXA?W?")
.blacklist_type("LPTOP_LEVEL_EXCEPTION_FILTER")
.blacklist_type("MONITORINFOEXA?W?")
.blacklist_type("PEXCEPTION_FILTER")
.blacklist_type("PEXCEPTION_ROUTINE")
.blacklist_type("PSLIST_HEADER")
.blacklist_type("PTOP_LEVEL_EXCEPTION_FILTER")
.blacklist_type("PVECTORED_EXCEPTION_HANDLER")
.blacklist_type("_?L?P?CONTEXT")
.blacklist_type("_?L?P?EXCEPTION_POINTERS")
.blacklist_type("_?P?DISPATCHER_CONTEXT")
.blacklist_type("_?P?EXCEPTION_REGISTRATION_RECORD")
.blacklist_type("_?P?IMAGE_TLS_DIRECTORY.*")
.blacklist_type("_?P?NT_TIB")
.blacklist_type("tagMONITORINFOEXA")
.blacklist_type("tagMONITORINFOEXW")
.blacklist_function("AddVectoredContinueHandler")
.blacklist_function("AddVectoredExceptionHandler")
.blacklist_function("CopyContext")
.blacklist_function("GetThreadContext")
.blacklist_function("GetXStateFeaturesMask")
.blacklist_function("InitializeContext")
.blacklist_function("InitializeContext2")
.blacklist_function("InitializeSListHead")
.blacklist_function("InterlockedFlushSList")
.blacklist_function("InterlockedPopEntrySList")
.blacklist_function("InterlockedPushEntrySList")
.blacklist_function("InterlockedPushListSListEx")
.blacklist_function("LocateXStateFeature")
.blacklist_function("QueryDepthSList")
.blacklist_function("RaiseFailFastException")
.blacklist_function("RtlCaptureContext")
.blacklist_function("RtlCaptureContext2")
.blacklist_function("RtlFirstEntrySList")
.blacklist_function("RtlInitializeSListHead")
.blacklist_function("RtlInterlockedFlushSList")
.blacklist_function("RtlInterlockedPopEntrySList")
.blacklist_function("RtlInterlockedPushEntrySList")
.blacklist_function("RtlInterlockedPushListSListEx")
.blacklist_function("RtlQueryDepthSList")
.blacklist_function("RtlRestoreContext")
.blacklist_function("RtlUnwindEx")
.blacklist_function("RtlVirtualUnwind")
.blacklist_function("SetThreadContext")
.blacklist_function("SetUnhandledExceptionFilter")
.blacklist_function("SetXStateFeaturesMask")
.blacklist_function("UnhandledExceptionFilter")
.blacklist_function("__C_specific_handler");
} }
let bindings = builder let bindings = builder

@ -1 +1 @@
Subproject commit e31a5084e264b4ab3c21238a95f703e994ec3384 Subproject commit 99b3bba5ff04f1420ccea25e1f6907c7f4f3782c

@ -3,6 +3,7 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(improper_ctypes)] #![allow(improper_ctypes)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#![allow(clippy::redundant_static_lifetimes)]
include!(concat!(env!("OUT_DIR"), "/leechcore.rs")); include!(concat!(env!("OUT_DIR"), "/leechcore.rs"));

@ -1,6 +1,6 @@
[package] [package]
name = "memflow-pcileech" name = "memflow-pcileech"
version = "0.1.5" version = "0.2.0-beta1"
authors = ["ko1N <ko1N1337@gmail.com>"] authors = ["ko1N <ko1N1337@gmail.com>"]
edition = "2018" edition = "2018"
description = "qemu procfs connector for leechcore/pcileech" description = "qemu procfs connector for leechcore/pcileech"
@ -9,6 +9,7 @@ readme = "../README.md"
homepage = "https://memflow.github.io" homepage = "https://memflow.github.io"
repository = "https://github.com/memflow/memflow-pcileech" repository = "https://github.com/memflow/memflow-pcileech"
license-file = "../LICENSE" license-file = "../LICENSE"
license = "GPL-3.0"
keywords = [ "memflow", "introspection", "memory" ] keywords = [ "memflow", "introspection", "memory" ]
categories = [ "api-bindings", "memory-management", "os" ] categories = [ "api-bindings", "memory-management", "os" ]
@ -16,16 +17,23 @@ categories = [ "api-bindings", "memory-management", "os" ]
crate-type = ["lib", "cdylib"] crate-type = ["lib", "cdylib"]
[dependencies] [dependencies]
memflow = { version = "0.1", features = ["inventory", "serde_derive"] } memflow = { version = "^0.2.0-beta", features = ["plugins", "memmapfiles"] }
memflow-derive = { version = "0.1" } leechcore-sys = { version = "0.2.0-beta1", path = "../leechcore-sys" }
log = { version = "0.4", default-features = false } log = "^0.4.14"
simple_logger = "1.0"
leechcore-sys = { path = "../leechcore-sys" }
[features] [dev-dependencies]
default = [] clap = { version = "^3.0.5", features = ["cargo"] }
inventory = [] simplelog = "^0.11.1"
memflow-win32 = { version = "^0.2.0-beta" }
[[example]] [[example]]
name = "read_phys" name = "read_phys"
path = "examples/read_phys.rs" path = "examples/read_phys.rs"
[[example]]
name = "ps_win32"
path = "examples/ps_win32.rs"
[[example]]
name = "ps_inventory"
path = "examples/ps_inventory.rs"

@ -0,0 +1,61 @@
/*!
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() {
simplelog::TermLogger::init(
Level::Debug.to_level_filter(),
simplelog::Config::default(),
simplelog::TerminalMode::Stdout,
simplelog::ColorChoice::Auto,
)
.unwrap();
let connector_args = if let Some(arg) = args().nth(1) {
arg.parse()
} else {
"device=FPGA".parse()
}
.expect("unable to parse command line arguments");
let inventory = Inventory::scan();
let connector = inventory
.create_connector("pcileech", None, Some(&connector_args))
.expect("unable to create pcileech connector");
let mut os = inventory
.create_os("win32", Some(connector), None)
.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
);
}
}

@ -0,0 +1,60 @@
/*!
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() {
simplelog::TermLogger::init(
Level::Debug.to_level_filter(),
simplelog::Config::default(),
simplelog::TerminalMode::Stdout,
simplelog::ColorChoice::Auto,
)
.unwrap();
let connector_args = if let Some(arg) = args().nth(1) {
arg.parse()
} else {
"device=FPGA".parse()
}
.expect("unable to parse command line arguments");
let connector = memflow_pcileech::create_connector(&connector_args)
.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
);
}
}

@ -1,40 +1,55 @@
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::env::args;
use std::time::Instant; use std::time::Instant;
use log::{info, Level}; use log::{info, Level};
use memflow::*; use memflow::prelude::v1::*;
fn main() { fn main() {
simple_logger::SimpleLogger::new() simplelog::TermLogger::init(
.with_level(Level::Debug.to_level_filter()) Level::Debug.to_level_filter(),
.init() simplelog::Config::default(),
simplelog::TerminalMode::Stdout,
simplelog::ColorChoice::Auto,
)
.unwrap(); .unwrap();
let args: Vec<String> = env::args().collect(); let connector_args = if let Some(arg) = args().nth(1) {
let conn_args = if args.len() > 1 { arg.parse()
ConnectorArgs::parse(&args[1]).expect("unable to parse arguments")
} else { } else {
ConnectorArgs::new() "device=FPGA".parse()
}; }
.expect("unable to parse command line arguments");
let mut connector = memflow_pcileech::create_connector(&connector_args)
.expect("unable to create pcileech connector");
let mut conn = memflow_pcileech::create_connector(&conn_args) let metadata = connector.metadata();
.expect("unable to initialize memflow_pcileech"); info!("Received metadata: {:?}", metadata);
let mut mem = vec![0; 8]; let mut mem = vec![0; 8];
conn.phys_read_raw_into(Address::from(0x1000).into(), &mut mem) connector
.unwrap(); .phys_view()
.read_raw_into(Address::from(0x1000).into(), &mut mem)
.expect("unable to read physical memory");
info!("Received memory: {:?}", mem); info!("Received memory: {:?}", mem);
let start = Instant::now(); let start = Instant::now();
let mut counter = 0; let mut counter = 0;
loop { loop {
let mut buf = vec![0; 0x1000]; let mut buf = vec![0; 0x1000];
conn.phys_read_raw_into(Address::from(0x1000).into(), &mut buf) connector
.unwrap(); .phys_view()
.read_raw_into(Address::from(0x1000).into(), &mut buf)
.expect("unable to read physical memory");
counter += 1; counter += 1;
if (counter % 10000) == 0 { if (counter % 10000000) == 0 {
let elapsed = start.elapsed().as_millis() as f64; let elapsed = start.elapsed().as_millis() as f64;
if elapsed > 0.0 { if elapsed > 0.0 {
info!("{} reads/sec", (f64::from(counter)) / elapsed * 1000.0); info!("{} reads/sec", (f64::from(counter)) / elapsed * 1000.0);

@ -1,5 +1,3 @@
// https://github.com/ufrisk/pcileech/blob/master/pcileech/device.c
use std::ffi::c_void; use std::ffi::c_void;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::path::Path; use std::path::Path;
@ -9,17 +7,23 @@ use std::sync::{Arc, Mutex};
use log::{error, info}; use log::{error, info};
use memflow::*; use memflow::cglue;
use memflow_derive::connector; use memflow::mem::phys_mem::*;
use memflow::prelude::v1::*;
use leechcore_sys::*; use leechcore_sys::*;
const PAGE_SIZE: usize = 0x1000usize; const PAGE_SIZE: usize = 0x1000usize;
const BUF_ALIGN: u64 = 4; // the absolute minimum BUF_ALIGN is 4.
// using 8 bytes as BUF_ALIGN here simplifies things a lot
// and makes our gap detection code work in cases where page boundaries would be crossed.
const BUF_ALIGN: u64 = 8;
const BUF_MIN_LEN: usize = 8; const BUF_MIN_LEN: usize = 8;
const BUF_LEN_ALIGN: usize = 8; const BUF_LEN_ALIGN: usize = 8;
cglue_impl_group!(PciLeech, ConnectorInstance<'a>, {});
fn build_lc_config(device: &str) -> LC_CONFIG { fn build_lc_config(device: &str) -> LC_CONFIG {
let cdevice = unsafe { &*(device.as_bytes() as *const [u8] as *const [c_char]) }; let cdevice = unsafe { &*(device.as_bytes() as *const [u8] as *const [c_char]) };
let mut adevice: [c_char; 260] = [0; 260]; let mut adevice: [c_char; 260] = [0; 260];
@ -47,43 +51,34 @@ const fn calc_num_pages(start: u64, size: u64) -> u64 {
} }
#[allow(clippy::mutex_atomic)] #[allow(clippy::mutex_atomic)]
#[derive(Debug)] #[derive(Clone)]
pub struct PciLeech { pub struct PciLeech {
handle: Arc<Mutex<HANDLE>>, handle: Arc<Mutex<HANDLE>>,
metadata: PhysicalMemoryMetadata, conf: LC_CONFIG,
mem_map: MemoryMap<(Address, usize)>, mem_map: Option<MemoryMap<(Address, umem)>>,
} }
unsafe impl Send for PciLeech {} unsafe impl Send for PciLeech {}
impl Clone for PciLeech {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
metadata: self.metadata,
mem_map: self.mem_map.clone(),
}
}
}
// TODO: proper drop + free impl -> LcMemFree(pLcErrorInfo); // TODO: proper drop + free impl -> LcMemFree(pLcErrorInfo);
#[allow(clippy::mutex_atomic)]
impl PciLeech { impl PciLeech {
pub fn new(device: &str) -> Result<Self> { pub fn new(device: &str) -> Result<Self> {
Self::with_mapping(device, MemoryMap::new()) Self::new_internal(device, None)
} }
pub fn with_memmap<P: AsRef<Path>>(device: &str, path: P) -> Result<Self> { pub fn with_mem_map_file<P: AsRef<Path>>(device: &str, path: P) -> Result<Self> {
info!( info!(
"loading memory mappings from file: {}", "loading memory mappings from file: {}",
path.as_ref().to_string_lossy() path.as_ref().to_string_lossy()
); );
let memmap = MemoryMap::open(path)?; let mem_map = MemoryMap::open(path)?;
info!("{:?}", memmap); info!("{:?}", mem_map);
Self::with_mapping(device, memmap) Self::new_internal(device, Some(mem_map))
} }
#[allow(clippy::mutex_atomic)] #[allow(clippy::mutex_atomic)]
fn with_mapping(device: &str, mem_map: MemoryMap<(Address, usize)>) -> Result<Self> { fn new_internal(device: &str, mem_map: Option<MemoryMap<(Address, umem)>>) -> Result<Self> {
// open device // open device
let mut conf = build_lc_config(device); let mut conf = build_lc_config(device);
let err = std::ptr::null_mut::<PLC_CONFIG_ERRORINFO>(); let err = std::ptr::null_mut::<PLC_CONFIG_ERRORINFO>();
@ -91,17 +86,13 @@ impl PciLeech {
if handle.is_null() { if handle.is_null() {
// TODO: handle version error // TODO: handle version error
// TODO: handle special case of fUserInputRequest // TODO: handle special case of fUserInputRequest
error!("leechcore error: {:?}", err); return Err(Error(ErrorOrigin::Connector, ErrorKind::Configuration)
return Err(Error::Connector("unable to create leechcore context")); .log_error(&format!("unable to create leechcore context: {:?}", err)));
} }
Ok(Self { Ok(Self {
handle: Arc::new(Mutex::new(handle)), handle: Arc::new(Mutex::new(handle)),
metadata: PhysicalMemoryMetadata { conf,
size: conf.paMax as usize,
readonly: conf.fVolatile == 0,
// TODO: writable flag
},
mem_map, mem_map,
}) })
} }
@ -124,14 +115,25 @@ struct WriteGap {
in_end: usize, in_end: usize,
} }
// TODO: handle mem_map
impl PhysicalMemory for PciLeech { impl PhysicalMemory for PciLeech {
fn phys_read_raw_list(&mut self, data: &mut [PhysicalReadData]) -> Result<()> { fn phys_read_raw_iter<'a>(
//let mem_map = &self.mem_map; &mut self,
data: CIterator<PhysicalReadData<'a>>,
out_fail: &mut ReadFailCallback<'_, 'a>,
) -> Result<()> {
let vec = if let Some(mem_map) = &self.mem_map {
mem_map
.map_iter(data, out_fail)
.map(|d| (d.0 .0.into(), d.1))
.collect::<Vec<_>>()
} else {
data.map(|MemData(addr, buf)| (addr, buf))
.collect::<Vec<_>>()
};
// get total number of pages // get total number of pages
let num_pages = data.iter().fold(0u64, |acc, read| { let num_pages = vec.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.as_u64(), read.1.len() as u64) acc + calc_num_pages(read.0.to_umem(), read.1.len() as u64)
}); });
// allocate scatter buffer // allocate scatter buffer
@ -145,28 +147,45 @@ impl PhysicalMemory for PciLeech {
) )
}; };
if result != 1 { 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 // prepare mems
let mut gaps = Vec::new(); let mut gaps = Vec::new();
let mut i = 0usize; let mut i = 0usize;
for read in data.iter_mut() { for read in vec.into_iter() {
for (page_addr, out) in read.1.page_chunks(read.0.into(), PAGE_SIZE) { for (page_addr, out) in read.1.page_chunks(read.0.into(), PAGE_SIZE) {
let mem = unsafe { *mems.add(i) }; let mem = unsafe { *mems.add(i) };
let addr_align = page_addr.as_u64() & (BUF_ALIGN - 1); let addr_align = page_addr.to_umem() & (BUF_ALIGN - 1);
let len_align = out.len() & (BUF_LEN_ALIGN - 1); let len_align = out.len() & (BUF_LEN_ALIGN - 1);
if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN { if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN {
// properly aligned read // properly aligned read
unsafe { (*mem).qwA = page_addr.as_u64() }; unsafe { (*mem).qwA = page_addr.to_umem() };
unsafe { (*mem).pb = out.as_mut_ptr() }; unsafe { (*mem).__bindgen_anon_1.pb = out.as_mut_ptr() };
unsafe { (*mem).cb = out.len() as u32 }; unsafe { (*mem).cb = out.len() as u32 };
} else { } else {
// non-aligned or small read // non-aligned or small read
let mut buffer_len = (out.len() + addr_align as usize).max(BUF_MIN_LEN); let page_addr_align = page_addr.to_umem() - addr_align;
buffer_len += BUF_LEN_ALIGN - (buffer_len & (BUF_LEN_ALIGN - 1)); let mut buffer_len = out.len() + addr_align as usize;
let buf_align = buffer_len & (BUF_LEN_ALIGN - 1);
if buf_align > 0 {
buffer_len += BUF_LEN_ALIGN - buf_align;
}
buffer_len = buffer_len.max(BUF_MIN_LEN);
// note that this always holds true because addr alignment is equal to buf length alignment
assert!(buffer_len >= out.len());
// we never want to cross page boundaries, otherwise the read will just not work
assert_eq!(
page_addr.to_umem() - (page_addr.to_umem() & (PAGE_SIZE as umem - 1)),
(page_addr_align + buffer_len as umem - 1)
- ((page_addr_align + buffer_len as umem - 1)
& (PAGE_SIZE as umem - 1))
);
let buffer = vec![0u8; buffer_len].into_boxed_slice(); let buffer = vec![0u8; buffer_len].into_boxed_slice();
let buffer_ptr = Box::into_raw(buffer) as *mut u8; let buffer_ptr = Box::into_raw(buffer) as *mut u8;
@ -179,8 +198,8 @@ impl PhysicalMemory for PciLeech {
out_end: out.len() + addr_align as usize, out_end: out.len() + addr_align as usize,
}); });
unsafe { (*mem).qwA = page_addr.as_u64() - addr_align }; unsafe { (*mem).qwA = page_addr_align };
unsafe { (*mem).pb = buffer_ptr }; unsafe { (*mem).__bindgen_anon_1.pb = buffer_ptr };
unsafe { (*mem).cb = buffer_len as u32 }; unsafe { (*mem).cb = buffer_len as u32 };
} }
@ -223,12 +242,24 @@ impl PhysicalMemory for PciLeech {
Ok(()) Ok(())
} }
fn phys_write_raw_list(&mut self, data: &[PhysicalWriteData]) -> Result<()> { fn phys_write_raw_iter<'a>(
//let mem_map = &self.mem_map; &mut self,
data: CIterator<PhysicalWriteData<'a>>,
out_fail: &mut WriteFailCallback<'_, 'a>,
) -> Result<()> {
let vec = if let Some(mem_map) = &self.mem_map {
mem_map
.map_iter(data, out_fail)
.map(|d| (d.0 .0.into(), d.1))
.collect::<Vec<_>>()
} else {
data.map(|MemData(addr, buf)| (addr, buf))
.collect::<Vec<_>>()
};
// get total number of pages // get total number of pages
let num_pages = data.iter().fold(0u64, |acc, read| { let num_pages = vec.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.as_u64(), read.1.len() as u64) acc + calc_num_pages(read.0.to_umem(), read.1.len() as u64)
}); });
// allocate scatter buffer // allocate scatter buffer
@ -242,37 +273,53 @@ impl PhysicalMemory for PciLeech {
) )
}; };
if result != 1 { 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 // prepare mems
let mut gaps = Vec::new(); let mut gaps = Vec::new();
let mut i = 0usize; let mut i = 0usize;
for write in data.iter() { for write in vec.iter() {
for (page_addr, out) in write.1.page_chunks(write.0.into(), PAGE_SIZE) { for (page_addr, out) in write.1.page_chunks(write.0.into(), PAGE_SIZE) {
let mem = unsafe { *mems.add(i) }; let mem = unsafe { *mems.add(i) };
let addr_align = page_addr.as_u64() & (BUF_ALIGN - 1); let addr_align = page_addr.to_umem() & (BUF_ALIGN - 1);
let len_align = out.len() & (BUF_LEN_ALIGN - 1); let len_align = out.len() & (BUF_LEN_ALIGN - 1);
if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN { if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN {
// properly aligned read // properly aligned write
unsafe { (*mem).qwA = page_addr.as_u64() }; unsafe { (*mem).qwA = page_addr.to_umem() };
unsafe { (*mem).pb = out.as_ptr() as *mut u8 }; unsafe { (*mem).__bindgen_anon_1.pb = out.as_ptr() as *mut u8 };
unsafe { (*mem).cb = out.len() as u32 }; unsafe { (*mem).cb = out.len() as u32 };
} else { } else {
// non-aligned or small read // non-aligned or small write
let mut buffer_len = (out.len() + addr_align as usize).max(BUF_MIN_LEN); let page_addr_align = page_addr.to_umem() - addr_align;
buffer_len += BUF_LEN_ALIGN - (buffer_len & (BUF_LEN_ALIGN - 1)); let mut buffer_len = out.len() + addr_align as usize;
let buf_align = buffer_len & (BUF_LEN_ALIGN - 1);
if buf_align > 0 {
buffer_len += BUF_LEN_ALIGN - buf_align;
}
buffer_len = buffer_len.max(BUF_MIN_LEN);
// prepare gap buffer for reading // note that this always holds true because addr alignment is equal to buf length alignment
let write_addr = (page_addr.as_u64() - addr_align).into(); assert!(buffer_len >= out.len());
// we never want to cross page boundaries, otherwise the write will just not work
assert_eq!(
page_addr.to_umem() - (page_addr.to_umem() & (PAGE_SIZE as umem - 1)),
(page_addr_align + buffer_len as umem - 1)
- ((page_addr_align + buffer_len as umem - 1)
& (PAGE_SIZE as umem - 1))
);
// prepare gap buffer for writing
let buffer = vec![0u8; buffer_len].into_boxed_slice(); let buffer = vec![0u8; buffer_len].into_boxed_slice();
let buffer_ptr = Box::into_raw(buffer) as *mut u8; let buffer_ptr = Box::into_raw(buffer) as *mut u8;
// send over to our gaps list // send over to our gaps list
gaps.push(WriteGap { gaps.push(WriteGap {
gap_addr: write_addr, gap_addr: page_addr_align.into(),
gap_buffer: buffer_ptr, gap_buffer: buffer_ptr,
gap_buffer_len: buffer_len, gap_buffer_len: buffer_len,
in_buffer: out.as_ptr(), in_buffer: out.as_ptr(),
@ -281,8 +328,8 @@ impl PhysicalMemory for PciLeech {
}); });
// store pointers into pcileech struct for writing (after we dispatched a read) // store pointers into pcileech struct for writing (after we dispatched a read)
unsafe { (*mem).qwA = write_addr.as_u64() }; unsafe { (*mem).qwA = page_addr_align };
unsafe { (*mem).pb = buffer_ptr }; unsafe { (*mem).__bindgen_anon_1.pb = buffer_ptr };
unsafe { (*mem).cb = buffer_len as u32 }; unsafe { (*mem).cb = buffer_len as u32 };
} }
@ -292,18 +339,25 @@ impl PhysicalMemory for PciLeech {
// dispatch necessary reads to fill the gaps // dispatch necessary reads to fill the gaps
if !gaps.is_empty() { if !gaps.is_empty() {
let mut datas = gaps let mut vec = gaps
.iter() .iter()
.map(|g| { .map(|g| {
PhysicalReadData(g.gap_addr, unsafe { MemData(
slice::from_raw_parts_mut(g.gap_buffer, g.gap_buffer_len) g.gap_addr,
}) unsafe { slice::from_raw_parts_mut(g.gap_buffer, g.gap_buffer_len) }.into(),
)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.phys_read_raw_list(datas.as_mut_slice())?; let mut iter = vec
.iter_mut()
.map(|MemData(a, d): &mut PhysicalReadData| MemData(*a, d.into()));
let out_fail = &mut |_| true;
for (gap, read) in gaps.iter().zip(datas) { self.phys_read_raw_iter((&mut iter).into(), &mut out_fail.into())?;
for (gap, mut read) in gaps.iter().zip(vec) {
let in_buffer = let in_buffer =
unsafe { slice::from_raw_parts(gap.in_buffer, gap.in_end - gap.in_start) }; unsafe { slice::from_raw_parts(gap.in_buffer, gap.in_end - gap.in_start) };
read.1[gap.in_start..gap.in_end].copy_from_slice(in_buffer); read.1[gap.in_start..gap.in_end].copy_from_slice(in_buffer);
@ -334,21 +388,89 @@ impl PhysicalMemory for PciLeech {
} }
fn metadata(&self) -> PhysicalMemoryMetadata { fn metadata(&self) -> PhysicalMemoryMetadata {
self.metadata let (max_address, real_size) = if let Some(mem_map) = &self.mem_map {
(mem_map.max_address(), mem_map.real_size())
} else {
(
(self.conf.paMax as usize - 1_usize).into(),
self.conf.paMax as umem,
)
};
PhysicalMemoryMetadata {
max_address,
real_size,
readonly: self.conf.fVolatile == 0,
ideal_batch_size: 128,
}
}
// Sets the memory map only in cases where no previous memory map was being set by the end-user.
fn set_mem_map(&mut self, mem_map: &[PhysicalMemoryMapping]) {
if self.mem_map.is_none() {
self.mem_map = Some(MemoryMap::<(Address, umem)>::from_vec(mem_map.to_vec()));
}
} }
} }
fn validator() -> ArgsValidator {
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"))
}
/// Creates a new PciLeech Connector instance. /// Creates a new PciLeech Connector instance.
#[connector(name = "pcileech")] #[connector(name = "pcileech", help_fn = "help", target_list_fn = "target_list")]
pub fn create_connector(args: &ConnectorArgs) -> Result<PciLeech> { pub fn create_connector(args: &ConnectorArgs) -> Result<PciLeech> {
let validator = validator();
let args = &args.extra_args;
match validator.validate(args) {
Ok(_) => {
let device = args let device = args
.get("device") .get("device")
.or_else(|| args.get_default()) .or_else(|| args.get_default())
.ok_or(Error::Connector("argument 'device' missing"))?; .ok_or_else(|| {
Error(ErrorOrigin::Connector, ErrorKind::ArgValidation)
.log_error("'device' argument is missing")
})?;
if let Some(memmap) = args.get("memmap") { if let Some(memmap) = args.get("memmap") {
PciLeech::with_memmap(device, memmap) PciLeech::with_mem_map_file(device, memmap)
} else { } else {
PciLeech::new(device) PciLeech::new(device)
} }
}
Err(err) => {
error!(
"unable to validate provided arguments, valid arguments are:\n{}",
validator
);
Err(err)
}
}
}
/// Retrieve the help text for the Qemu Procfs Connector.
pub fn help() -> String {
let validator = validator();
format!(
"\
The `pcileech` connector implements the LeechCore interface of pcileech for memflow.
More information about pcileech can be found under https://github.com/ufrisk/pcileech.
This connector requires access to the usb ports to access the pcileech hardware.
Available arguments are:
{}",
validator.to_string()
)
}
/// Retrieve a list of all currently available PciLeech targets.
pub fn target_list() -> Result<Vec<TargetInfo>> {
// TODO: check if usb is connected, then list 1 target
Ok(vec![])
} }

Loading…
Cancel
Save