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
*.swp
.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]
members = [
"leechcore-sys",
@ -6,7 +12,4 @@ members = [
default-members = [
"leechcore-sys",
"memflow-pcileech",
]
[profile.release]
lto = true
]

@ -1,6 +1,6 @@
# 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.
@ -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
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:
```
@ -79,7 +78,7 @@ let conn_args = if args.len() > 1 {
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");
```
@ -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
```
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
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
sudo mkdir /usr/lib/memflow
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
# install connector in user dir
@ -16,4 +16,4 @@ echo "installing connector for user in ~/.local/lib/memflow"
if [[ ! -d ~/.local/lib/memflow ]]; then
mkdir -p ~/.local/lib/memflow
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]
name = "leechcore-sys"
version = "0.1.5"
version = "0.2.0-beta1"
authors = ["ko1N <ko1N1337@gmail.com>"]
edition = "2018"
readme = "../README.md"
license-file = "../LICENSE"
license = "GPL-3.0"
links = "leechcore"

@ -32,6 +32,7 @@ fn build_leechcore(target: &str) {
"device_pmem.c",
"device_tmd.c",
"device_usb3380.c",
"device_vmware.c",
"leechrpcclient.c",
];
if target.contains("windows") {
@ -47,7 +48,8 @@ fn build_leechcore(target: &str) {
.map(|o| "src/leechcore/leechcore/".to_string() + o)
.collect::<Vec<_>>(),
)
.flag(&format!("-D{}", os_define()));
.flag(&format!("-D{}", os_define()))
.flag("-D_GNU_SOURCE");
// EXPORTED_FUNCTION= to not export any symbols
if !target.contains("windows") {
@ -105,61 +107,13 @@ fn main() {
// generate bindings
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");
// workaround for windows.h
// see https://github.com/rust-lang/rust-bindgen/issues/1556
if target.contains("windows") {
builder = builder
.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");
builder = builder.blacklist_type("_?P?IMAGE_TLS_DIRECTORY.*")
}
let bindings = builder

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

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

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

@ -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 log::{info, Level};
use memflow::*;
use memflow::prelude::v1::*;
fn main() {
simple_logger::SimpleLogger::new()
.with_level(Level::Debug.to_level_filter())
.init()
.unwrap();
let args: Vec<String> = env::args().collect();
let conn_args = if args.len() > 1 {
ConnectorArgs::parse(&args[1]).expect("unable to parse arguments")
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 {
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)
.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_view()
.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_view()
.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);

@ -1,5 +1,3 @@
// https://github.com/ufrisk/pcileech/blob/master/pcileech/device.c
use std::ffi::c_void;
use std::os::raw::c_char;
use std::path::Path;
@ -9,17 +7,23 @@ use std::sync::{Arc, Mutex};
use log::{error, info};
use memflow::*;
use memflow_derive::connector;
use memflow::cglue;
use memflow::mem::phys_mem::*;
use memflow::prelude::v1::*;
use leechcore_sys::*;
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_LEN_ALIGN: usize = 8;
cglue_impl_group!(PciLeech, ConnectorInstance<'a>, {});
fn build_lc_config(device: &str) -> LC_CONFIG {
let cdevice = unsafe { &*(device.as_bytes() as *const [u8] as *const [c_char]) };
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)]
#[derive(Debug)]
#[derive(Clone)]
pub struct PciLeech {
handle: Arc<Mutex<HANDLE>>,
metadata: PhysicalMemoryMetadata,
mem_map: MemoryMap<(Address, usize)>,
conf: LC_CONFIG,
mem_map: Option<MemoryMap<(Address, umem)>>,
}
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);
#[allow(clippy::mutex_atomic)]
impl PciLeech {
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!(
"loading memory mappings from file: {}",
path.as_ref().to_string_lossy()
);
let memmap = MemoryMap::open(path)?;
info!("{:?}", memmap);
Self::with_mapping(device, memmap)
let mem_map = MemoryMap::open(path)?;
info!("{:?}", mem_map);
Self::new_internal(device, Some(mem_map))
}
#[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
let mut conf = build_lc_config(device);
let err = std::ptr::null_mut::<PLC_CONFIG_ERRORINFO>();
@ -91,17 +86,13 @@ impl PciLeech {
if handle.is_null() {
// 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(&format!("unable to create leechcore context: {:?}", err)));
}
Ok(Self {
handle: Arc::new(Mutex::new(handle)),
metadata: PhysicalMemoryMetadata {
size: conf.paMax as usize,
readonly: conf.fVolatile == 0,
// TODO: writable flag
},
conf,
mem_map,
})
}
@ -124,14 +115,25 @@ struct WriteGap {
in_end: usize,
}
// TODO: handle mem_map
impl PhysicalMemory for PciLeech {
fn phys_read_raw_list(&mut self, data: &mut [PhysicalReadData]) -> Result<()> {
//let mem_map = &self.mem_map;
fn phys_read_raw_iter<'a>(
&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
let num_pages = data.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.as_u64(), read.1.len() as u64)
let num_pages = vec.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.to_umem(), read.1.len() as u64)
});
// allocate scatter buffer
@ -145,28 +147,45 @@ 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
let mut gaps = Vec::new();
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) {
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);
if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN {
// properly aligned read
unsafe { (*mem).qwA = page_addr.as_u64() };
unsafe { (*mem).pb = out.as_mut_ptr() };
unsafe { (*mem).qwA = page_addr.to_umem() };
unsafe { (*mem).__bindgen_anon_1.pb = out.as_mut_ptr() };
unsafe { (*mem).cb = out.len() as u32 };
} else {
// non-aligned or small read
let mut buffer_len = (out.len() + addr_align as usize).max(BUF_MIN_LEN);
buffer_len += BUF_LEN_ALIGN - (buffer_len & (BUF_LEN_ALIGN - 1));
let page_addr_align = page_addr.to_umem() - addr_align;
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_ptr = Box::into_raw(buffer) as *mut u8;
@ -179,8 +198,8 @@ impl PhysicalMemory for PciLeech {
out_end: out.len() + addr_align as usize,
});
unsafe { (*mem).qwA = page_addr.as_u64() - addr_align };
unsafe { (*mem).pb = buffer_ptr };
unsafe { (*mem).qwA = page_addr_align };
unsafe { (*mem).__bindgen_anon_1.pb = buffer_ptr };
unsafe { (*mem).cb = buffer_len as u32 };
}
@ -223,12 +242,24 @@ impl PhysicalMemory for PciLeech {
Ok(())
}
fn phys_write_raw_list(&mut self, data: &[PhysicalWriteData]) -> Result<()> {
//let mem_map = &self.mem_map;
fn phys_write_raw_iter<'a>(
&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
let num_pages = data.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.as_u64(), read.1.len() as u64)
let num_pages = vec.iter().fold(0u64, |acc, read| {
acc + calc_num_pages(read.0.to_umem(), read.1.len() as u64)
});
// allocate scatter buffer
@ -242,37 +273,53 @@ 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
let mut gaps = Vec::new();
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) {
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);
if addr_align == 0 && len_align == 0 && out.len() >= BUF_MIN_LEN {
// properly aligned read
unsafe { (*mem).qwA = page_addr.as_u64() };
unsafe { (*mem).pb = out.as_ptr() as *mut u8 };
// properly aligned write
unsafe { (*mem).qwA = page_addr.to_umem() };
unsafe { (*mem).__bindgen_anon_1.pb = out.as_ptr() as *mut u8 };
unsafe { (*mem).cb = out.len() as u32 };
} else {
// non-aligned or small read
let mut buffer_len = (out.len() + addr_align as usize).max(BUF_MIN_LEN);
buffer_len += BUF_LEN_ALIGN - (buffer_len & (BUF_LEN_ALIGN - 1));
// prepare gap buffer for reading
let write_addr = (page_addr.as_u64() - addr_align).into();
// non-aligned or small write
let page_addr_align = page_addr.to_umem() - addr_align;
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 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_ptr = Box::into_raw(buffer) as *mut u8;
// send over to our gaps list
gaps.push(WriteGap {
gap_addr: write_addr,
gap_addr: page_addr_align.into(),
gap_buffer: buffer_ptr,
gap_buffer_len: buffer_len,
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)
unsafe { (*mem).qwA = write_addr.as_u64() };
unsafe { (*mem).pb = buffer_ptr };
unsafe { (*mem).qwA = page_addr_align };
unsafe { (*mem).__bindgen_anon_1.pb = buffer_ptr };
unsafe { (*mem).cb = buffer_len as u32 };
}
@ -292,18 +339,25 @@ impl PhysicalMemory for PciLeech {
// dispatch necessary reads to fill the gaps
if !gaps.is_empty() {
let mut datas = gaps
let mut vec = gaps
.iter()
.map(|g| {
PhysicalReadData(g.gap_addr, unsafe {
slice::from_raw_parts_mut(g.gap_buffer, g.gap_buffer_len)
})
MemData(
g.gap_addr,
unsafe { slice::from_raw_parts_mut(g.gap_buffer, g.gap_buffer_len) }.into(),
)
})
.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()));
for (gap, read) in gaps.iter().zip(datas) {
let out_fail = &mut |_| true;
self.phys_read_raw_iter((&mut iter).into(), &mut out_fail.into())?;
for (gap, mut read) in gaps.iter().zip(vec) {
let in_buffer =
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);
@ -334,21 +388,89 @@ impl PhysicalMemory for PciLeech {
}
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.
#[connector(name = "pcileech")]
#[connector(name = "pcileech", help_fn = "help", target_list_fn = "target_list")]
pub fn create_connector(args: &ConnectorArgs) -> Result<PciLeech> {
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 = validator();
let args = &args.extra_args;
match validator.validate(args) {
Ok(_) => {
let device = args
.get("device")
.or_else(|| args.get_default())
.ok_or_else(|| {
Error(ErrorOrigin::Connector, ErrorKind::ArgValidation)
.log_error("'device' argument is missing")
})?;
if let Some(memmap) = args.get("memmap") {
PciLeech::with_mem_map_file(device, memmap)
} else {
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