diff --git a/Cargo.toml b/Cargo.toml index 8de5549..d543f75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,6 @@ default-members = [ "leechcore-sys", "memflow-pcileech", ] + +[profile.release] +lto = true diff --git a/README.md b/README.md index f9cf0a0..c5fde82 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,23 @@ More information about pcileech can be found under https://github.com/ufrisk/pci ## Compilation -- Make sure the git submodule is checked out -- Make sure gcc, clang, libusb-1.0 are installed (on windows you can use chocolatey) -- Run `cargo build --release` +First make sure that the `leechcore` submodule is checked out: +``` +git submodule init +git submodule sync +git submodule update +``` + +Install the following build tools: +- gcc +- clang +- libusb-1.0 (only required on linux) + +On Windows you additionally need to supply the proprietary FTD3XX.dll. -This project uses libusb to interface with the ftdi chip over usb. Make sure you have the appropiate headers installed. More information about the libusb implementation can be found in the https://github.com/a1ien/rusb project. +On Linux you need to check-out and compile the `leechcore_ft601_driver_linux` project from the [LeechCore-Plugins](https://github.com/ufrisk/LeechCore-plugins) repository. + +More information about these requirements can be found in the [LeechCore-Plugins](https://github.com/ufrisk/LeechCore-plugins) repository. ### Using the install script @@ -40,6 +52,28 @@ To compile a dynamic library as a plugin use the following command: ```cargo build --release --all-features``` +## Arguments + +The following arguments can be used when loading the connector: + +- `device` - the name of the pcileech device to open (e.g. FPGA) (default argument, required) +- `memmap` - a file that contains a custom memory map in TOML format (optional) + +The memory map file must contain a mapping table in the following format: + +```toml +[[range]] +base=0x1000 +length=0x1000 + +[[range]] +base=0x2000 +length=0x1000 +real_base=0x3000 +``` + +The `real_base` parameter is optional. If it is not set there will be no re-mapping. + ## License Licensed under MIT License, see [LICENSE](LICENSE). diff --git a/leechcore-sys/src/lib.rs b/leechcore-sys/src/lib.rs index 780a4ea..ad009f8 100644 --- a/leechcore-sys/src/lib.rs +++ b/leechcore-sys/src/lib.rs @@ -1,8 +1,10 @@ #![allow(non_snake_case)] -#![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] -#![allow(clippy::useless_transmute)] -#![allow(clippy::cognitive_complexity)] +#![allow(non_camel_case_types)] +#![allow(improper_ctypes)] #![allow(clippy::missing_safety_doc)] +//#![allow(clippy::useless_transmute)] +//#![allow(clippy::cognitive_complexity)] + include!(concat!(env!("OUT_DIR"), "/leechcore.rs")); diff --git a/memflow-pcileech/Cargo.toml b/memflow-pcileech/Cargo.toml index 0b960ee..781822d 100644 --- a/memflow-pcileech/Cargo.toml +++ b/memflow-pcileech/Cargo.toml @@ -16,8 +16,8 @@ categories = [ "api-bindings", "memory-management", "os" ] crate-type = ["lib", "cdylib"] [dependencies] -memflow = { version = "0.1", features = ["inventory"] } -memflow-derive = { version = "0.1" } +memflow = { git = "https://github.com/memflow/memflow", branch = "dev", features = ["inventory", "serde_derive"] } +memflow-derive = { git = "https://github.com/memflow/memflow", branch = "dev" } log = { version = "0.4", default-features = false } leechcore-sys = { path = "../leechcore-sys" } @@ -25,12 +25,8 @@ leechcore-sys = { path = "../leechcore-sys" } libc = "0.2" [dev-dependencies] -clap = "2.33" simple_logger = "1.0" -[profile.release] -lto = true - [features] default = [] inventory = [] diff --git a/memflow-pcileech/examples/read_phys.rs b/memflow-pcileech/examples/read_phys.rs index c0c5515..7b67019 100644 --- a/memflow-pcileech/examples/read_phys.rs +++ b/memflow-pcileech/examples/read_phys.rs @@ -1,3 +1,4 @@ +use std::env; use std::time::Instant; use log::{info, Level}; @@ -5,41 +6,27 @@ use log::{info, Level}; use memflow::*; fn main() { - simple_logger::init_with_level(Level::Debug).unwrap(); - - // TODO: parse command-line args - /* - let mut conn = match memflow_pcileech::create_connector( - &ConnectorArgs::parse("name=win10,type=kvm").unwrap(), - ) { - Ok(br) => br, - Err(e) => { - info!("couldn't open memory read context: {:?}", e); - return; - } - }; - */ - let mut conn = memflow_pcileech::PciLeech::new("FPGA").unwrap(); - info!("conn: {:?}", conn); + simple_logger::SimpleLogger::new() + .with_level(Level::Debug.to_level_filter()) + .init() + .unwrap(); - let addr = Address::from(0x1000); - let mut mem = vec![0; 8]; - conn.phys_read_raw_into(addr.into(), &mut mem).unwrap(); - info!("Received memory: {:?}", mem); + let args: Vec = env::args().collect(); + println!("{:?}", args); + let conn_args = if args.len() > 1 { + ConnectorArgs::parse(&args[1]).expect("unable to parse arguments") + } else { + ConnectorArgs::new() + }; - /* - // write - mem[0] = 123; - //mem[5] = 123; - conn.phys_write_raw((addr + 5).into(), &mut mem[..1]) - .unwrap(); + let mut conn = memflow_pcileech::create_connector(&conn_args) + .expect("unable to initialize memflow_pcileech"); - // re-read + let addr = Address::from(0x1000); + let mut mem = vec![0; 16]; conn.phys_read_raw_into(addr.into(), &mut mem).unwrap(); info!("Received memory: {:?}", mem); - */ - /* let start = Instant::now(); let mut counter = 0; loop { @@ -56,5 +43,4 @@ fn main() { } } } - */ } diff --git a/memflow-pcileech/src/lib.rs b/memflow-pcileech/src/lib.rs index aa5aedc..3426d58 100644 --- a/memflow-pcileech/src/lib.rs +++ b/memflow-pcileech/src/lib.rs @@ -2,11 +2,11 @@ use std::ffi::c_void; use std::os::raw::c_char; +use std::path::Path; use std::ptr; -use std::slice; use std::sync::{Arc, Mutex}; -use log::{error, info, warn}; +use log::{error, info}; use memflow::*; use memflow_derive::connector; @@ -14,20 +14,19 @@ use memflow_derive::connector; use leechcore_sys::*; const PAGE_SIZE: usize = 0x1000usize; + const BUF_ALIGN: u64 = 4; const BUF_MIN_LEN: usize = 8; const BUF_LEN_ALIGN: usize = 8; -const fn calc_num_pages(start: u64, size: u64) -> u64 { - ((start & (PAGE_SIZE as u64 - 1)) + size + (PAGE_SIZE as u64 - 1)) >> 12 -} - 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]; adevice[..device.len().min(260)].copy_from_slice(&cdevice[..device.len().min(260)]); - let cfg = LC_CONFIG { + // TODO: copy device + remote + + LC_CONFIG { dwVersion: LC_CONFIG_VERSION, dwPrintfVerbosity: LC_CONFIG_PRINTF_ENABLED | LC_CONFIG_PRINTF_V | LC_CONFIG_PRINTF_VV, szDevice: adevice, @@ -39,11 +38,11 @@ fn build_lc_config(device: &str) -> LC_CONFIG { fRemote: 0, fRemoteDisableCompress: 0, szDeviceName: [0; 260], - }; - - // TODO: copy device + remote + } +} - cfg +const fn calc_num_pages(start: u64, size: u64) -> u64 { + ((start & (PAGE_SIZE as u64 - 1)) + size + (PAGE_SIZE as u64 - 1)) >> 12 } #[derive(Debug)] @@ -59,7 +58,7 @@ impl Clone for PciLeech { fn clone(&self) -> Self { Self { handle: self.handle.clone(), - metadata: self.metadata.clone(), + metadata: self.metadata, mem_map: self.mem_map.clone(), } } @@ -68,11 +67,20 @@ impl Clone for PciLeech { // TODO: proper drop + free impl -> LcMemFree(pLcErrorInfo); impl PciLeech { pub fn new(device: &str) -> Result { - Self::with_map(device, MemoryMap::new()) + Self::with_mapping(device, MemoryMap::new()) + } + + pub fn with_memmap>(device: &str, path: P) -> Result { + info!( + "loading memory mappings from file: {}", + path.as_ref().to_string_lossy() + ); + let memmap = MemoryMap::open(path)?; + info!("{:?}", memmap); + Self::with_mapping(device, memmap) } - // TODO: load a memory map via the arguments - pub fn with_map(device: &str, mem_map: MemoryMap<(Address, usize)>) -> Result { + fn with_mapping(device: &str, mem_map: MemoryMap<(Address, usize)>) -> Result { // open device let mut conf = build_lc_config(device); let err = std::ptr::null_mut::(); @@ -88,7 +96,7 @@ impl PciLeech { handle: Arc::new(Mutex::new(handle)), metadata: PhysicalMemoryMetadata { size: conf.paMax as usize, - readonly: if conf.fVolatile == 0 { true } else { false }, + readonly: conf.fVolatile == 0, // TODO: writable flag }, mem_map, @@ -124,7 +132,7 @@ impl PhysicalMemory for PciLeech { let mut i = 0usize; for read in data.iter_mut() { for (page_addr, out) in read.1.page_chunks(read.0.into(), PAGE_SIZE) { - let mem = unsafe { *mems.offset(i as isize) }; + let mem = unsafe { *mems.add(i) }; let addr_align = page_addr.as_u64() & (BUF_ALIGN - 1); let len_align = out.len() & (BUF_LEN_ALIGN - 1); @@ -162,7 +170,7 @@ impl PhysicalMemory for PciLeech { i = 0usize; for read in data.iter_mut() { for (page_addr, out) in read.1.page_chunks(read.0.into(), PAGE_SIZE) { - let mem = unsafe { *mems.offset(i as isize) }; + let mem = unsafe { *mems.add(i) }; let addr_align = page_addr.as_u64() & (BUF_ALIGN - 1); let len_align = out.len() & (BUF_LEN_ALIGN - 1); @@ -216,7 +224,7 @@ impl PhysicalMemory for PciLeech { let mut i = 0usize; for write in data.iter() { for (page_addr, out) in write.1.page_chunks(write.0.into(), PAGE_SIZE) { - let mem = unsafe { *mems.offset(i as isize) }; + let mem = unsafe { *mems.add(i) }; let addr_align = page_addr.as_u64() & (BUF_ALIGN - 1); let len_align = out.len() & (BUF_LEN_ALIGN - 1); @@ -227,6 +235,8 @@ impl PhysicalMemory for PciLeech { unsafe { (*mem).pb = out.as_ptr() as *mut u8 }; unsafe { (*mem).cb = out.len() as u32 }; } else { + // TODO: dispatch all necessary reads into 1 batch + // 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)); @@ -257,6 +267,8 @@ impl PhysicalMemory for PciLeech { } } + // TODO: unleak memory from Box::into_raw() + // free temporary buffers unsafe { LcMemFree(mems as *mut c_void); @@ -266,7 +278,7 @@ impl PhysicalMemory for PciLeech { } fn metadata(&self) -> PhysicalMemoryMetadata { - self.metadata.clone() + self.metadata } } @@ -277,5 +289,10 @@ pub fn create_connector(args: &ConnectorArgs) -> Result { .get("device") .or_else(|| args.get_default()) .ok_or(Error::Connector("argument 'device' missing"))?; - PciLeech::new(device) + + if let Some(memmap) = args.get("memmap") { + PciLeech::with_memmap(device, memmap) + } else { + PciLeech::new(device) + } }