Skip to content

Commit

Permalink
Merge branch 'main' of github.com:memflow/memflow
Browse files Browse the repository at this point in the history
  • Loading branch information
ko1N committed Apr 28, 2024
2 parents a2c5b3c + 591da3d commit b87b6ed
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 38 deletions.
13 changes: 13 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## 0.2.2
- Changed minimum supported rust version to 1.74.0
- Global C/CPP functions are now prefixed with `mf_` to easier distuingish them from other third-party library functions
- Added lossy Macho parsing via https://github.com/m4b/goblin
- Replaced old string read functions with `read_utf8` and `read_utf8_lossy` functions.

## 0.2.1
- Added aarch64 16k page support
- Added a seperate `CachedView` as a new page cache on top of `MemoryView`

## 0.2.0
- Updated ABI version to `1`

## 0.2.0-beta11
- Added dtb1 and dtb2 fields to ProcessInfo structure
- Added a function to the process trait which allows overriding dtb1/dtb2 with a custom value
Expand Down
4 changes: 2 additions & 2 deletions memflow/examples/process_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use log::Level;
use memflow::prelude::v1::*;

fn main() -> Result<()> {
let matches = parse_args();
let chain = extract_args(&matches)?;
//let matches = parse_args();
//let chain = extract_args(&matches)?;

let mut registry = Registry::new();

Expand Down
140 changes: 117 additions & 23 deletions memflow/src/mem/memory_view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ pub trait MemoryView: Send {
/// The string does not have to be null-terminated.
/// If a null terminator is found the string is truncated to the terminator.
/// If no null terminator is found the resulting string is exactly `len` characters long.
#[deprecated = "please use read_utf8 or read_utf8_lossy instead"]
#[skip_func]
fn read_char_array(&mut self, addr: Address, len: usize) -> PartialResult<String> {
let mut buf = vec![0; len];
Expand All @@ -409,44 +410,134 @@ pub trait MemoryView: Send {
/// If no null terminator is found the this function will return an error.
///
/// For reading fixed-size char arrays the [`read_char_array`](Self::read_char_array) should be used.
#[deprecated = "please use read_utf8 or read_utf8_lossy instead"]
#[skip_func]
fn read_char_string_n(&mut self, addr: Address, n: usize) -> PartialResult<String> {
let mut buf = vec![0; std::cmp::min(32, n)];
self.read_utf8_lossy(addr, n)
}

let mut last_n = 0;
/// Reads a variable length string with up to 4kb length from the target.
///
/// # Arguments
///
/// * `addr` - target address to read from
#[deprecated = "please use read_utf8 or read_utf8_lossy instead"]
#[skip_func]
fn read_char_string(&mut self, addr: Address) -> PartialResult<String> {
self.read_utf8_lossy(addr, 4096)
}

loop {
let (_, right) = buf.split_at_mut(last_n);
/// Reads a string at the given position with a length of up to `max_length` characters.
///
/// Not all byte slices are valid `String`s, however: `String`
/// requires that it is valid UTF-8. `from_utf8()` checks to ensure that
/// the bytes are valid UTF-8, and then does the conversion.
///
/// # Remarks
///
/// If this string contains a '\0' terminator the returned string is clamped to the position of the terminator.
///
/// If the string contains no terminator the a string up to `max_length` characters is returned.
///
/// # Examples
///
/// Reading from a `MemoryView`:
/// ```
/// use memflow::types::Address;
/// use memflow::mem::MemoryView;
///
/// fn read(mem: &mut impl MemoryView, read_addr: Address) {
/// let str = mem.read_utf8(read_addr, 32).unwrap();
/// println!("str: {}", str);
/// # assert_eq!(str, "Hello, World!");
/// }
/// # use memflow::dummy::{DummyMemory, DummyOs};
/// # use memflow::os::Process;
/// # use memflow::types::size;
/// # let mut proc = DummyOs::quick_process(size::mb(2), &[72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 0]);
/// # let virt_base = proc.info().address;
/// # read(&mut proc, virt_base);
/// ```
#[skip_func]
fn read_utf8(&mut self, addr: Address, max_length: usize) -> PartialResult<String> {
let mut buf = vec![0; max_length];

self.read_raw_into(addr + last_n, right).data_part()?;
if let Some((n, _)) = right.iter().enumerate().find(|(_, c)| **c == 0_u8) {
buf.truncate(last_n + n);
return Ok(String::from_utf8_lossy(&buf).to_string());
}
if buf.len() >= n {
break;
}
last_n = buf.len();
// we allow partial reads, all bytes we could not read will be 0.
self.read_raw_into(addr, &mut buf).data_part()?;

buf.extend((0..buf.len()).map(|_| 0));
// truncate the buffer to the 0 terminator.
if let Some((n, _)) = buf.iter().enumerate().find(|(_, c)| **c == 0_u8) {
buf.truncate(n);
}

Err(PartialError::Error(Error(
ErrorOrigin::VirtualMemory,
ErrorKind::OutOfBounds,
)))
// convert the bytes into a string
Ok(String::from_utf8(buf).map_err(|err| {
Error(ErrorOrigin::Memory, ErrorKind::Encoding).log_error(format!(
"unable to convert bytes to valid utf8 string: {}",
err
))
})?)
}

/// Reads a variable length string with up to 4kb length from the target.
/// Reads a string at the given position with a length of up to `max_length` characters.
///
/// # Arguments
/// Not all byte slices are valid strings, however: strings
/// are required to be valid UTF-8. During this conversion,
/// `from_utf8_lossy()` will replace any invalid UTF-8 sequences with
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD], which looks like this: �
///
/// * `addr` - target address to read from
/// [byteslice]: prim@slice
/// [U+FFFD]: core::char::REPLACEMENT_CHARACTER
///
/// [`from_utf8_unchecked`]: String::from_utf8_unchecked
///
/// If our byte slice is invalid UTF-8, then we need to insert the replacement characters,
/// which will change the size of the string, and hence, require a `String`. But if
/// it's already valid UTF-8, we don't need a new allocation. This return
/// type allows us to handle both cases.
///
/// # Remarks
///
/// If this string contains a '\0' terminator the returned string is clamped to the position of the terminator.
///
/// If the string contains no terminator the a string up to `max_length` characters is returned.
///
/// # Examples
///
/// Reading from a `MemoryView`:
/// ```
/// use memflow::types::Address;
/// use memflow::mem::MemoryView;
///
/// fn read(mem: &mut impl MemoryView, read_addr: Address) {
/// let str = mem.read_utf8_lossy(read_addr, 32).unwrap();
/// println!("str: {}", str);
/// # assert_eq!(str, "Hello, World!");
/// }
/// # use memflow::dummy::{DummyMemory, DummyOs};
/// # use memflow::os::Process;
/// # use memflow::types::size;
/// # let mut proc = DummyOs::quick_process(size::mb(2), &[72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 0]);
/// # let virt_base = proc.info().address;
/// # read(&mut proc, virt_base);
/// ```
#[skip_func]
fn read_char_string(&mut self, addr: Address) -> PartialResult<String> {
self.read_char_string_n(addr, 4096)
fn read_utf8_lossy(&mut self, addr: Address, max_length: usize) -> PartialResult<String> {
let mut buf = vec![0; max_length];

// we allow partial reads, all bytes we could not read will be 0.
self.read_raw_into(addr, &mut buf).data_part()?;

// truncate the buffer to the 0 terminator.
if let Some((n, _)) = buf.iter().enumerate().find(|(_, c)| **c == 0_u8) {
buf.truncate(n);
}

// convert the bytes into a string
Ok(String::from_utf8_lossy(&buf).to_string())
}

/// Returns a cursor over this memory view. See [`MemoryCursor`] for more details.
#[cfg(feature = "std")]
#[skip_func]
fn cursor(&mut self) -> MemoryCursor<Fwd<&mut Self>>
Expand All @@ -456,6 +547,7 @@ pub trait MemoryView: Send {
MemoryCursor::new(self.forward())
}

/// Converts this memory view into a cursor. See [`MemoryCursor`] for more details.
#[cfg(feature = "std")]
#[skip_func]
fn into_cursor(self) -> MemoryCursor<Self>
Expand All @@ -465,6 +557,7 @@ pub trait MemoryView: Send {
MemoryCursor::new(self)
}

/// Returns a cursor over this memory view at the specified address. See [`MemoryCursor`] for more details.
#[cfg(feature = "std")]
#[skip_func]
fn cursor_at(&mut self, address: Address) -> MemoryCursor<Fwd<&mut Self>>
Expand All @@ -474,6 +567,7 @@ pub trait MemoryView: Send {
MemoryCursor::at(self.forward(), address)
}

/// Converts this memory view into a cursor at the specified address. See [`MemoryCursor`] for more details.
#[cfg(feature = "std")]
#[skip_func]
fn into_cursor_at(self, address: Address) -> MemoryCursor<Self>
Expand Down
19 changes: 10 additions & 9 deletions memflow/src/plugins/registry.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use core::cmp::Reverse;
use std::path::{Path, PathBuf};
use std::time::SystemTime;

use cglue::arc::CArc;
use cglue::trait_group::VerifyLayout;
use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, Utc};
use chrono::{DateTime, Local, NaiveDateTime};
use libloading::Library;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};

use crate::plugins::plugin_analyzer::PluginKind;
use crate::plugins::PluginDescriptor;
use crate::{
error::{Error, ErrorKind, ErrorOrigin, Result},
Expand All @@ -20,10 +18,7 @@ use crate::{
},
};

use super::{
connector, os, LibContext, LibInstance, Loadable, LoadableOs, OsArgs, OsInputArg,
OsInstanceArcBox,
};
use super::{LibContext, Loadable, LoadableOs, OsArgs, OsInputArg, OsInstanceArcBox};

pub struct Registry {
// connectors: Vec<LibInstance<connector::LoadableConnector>>,
Expand Down Expand Up @@ -54,8 +49,8 @@ pub struct PluginMetadata {
pub descriptors: Vec<PluginDescriptorInfo>,
}

impl Registry {
pub fn new() -> Self {
impl Default for Registry {
fn default() -> Self {
let mut registry = Self {
plugins: Vec::new(),
};
Expand All @@ -75,6 +70,12 @@ impl Registry {

registry
}
}

impl Registry {
pub fn new() -> Self {
Self::default()
}

pub fn add_dir<P: AsRef<Path>>(&mut self, path: P) -> Result<&Self> {
let paths = std::fs::read_dir(path.as_ref()).map_err(|err| {
Expand Down
38 changes: 34 additions & 4 deletions memflow/src/types/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Pointer abstraction.

use crate::cglue::ReprCString;
use crate::dataview::Pod;
use crate::error::{PartialResult, PartialResultExt};
use crate::error::PartialResult;
use crate::mem::MemoryView;
use crate::prelude::PartialError;
use crate::types::{imem, umem, Address, ByteSwap, PrimitiveAddress};

use std::convert::TryInto;
Expand Down Expand Up @@ -306,9 +307,38 @@ impl<U: PrimitiveAddress, T: Pod + Sized> Pointer<U, T> {

/// Implement special phys/virt read/write for CReprStr
impl<U: PrimitiveAddress> Pointer<U, ReprCString> {
pub fn read_string<M: MemoryView>(self, mem: &mut M) -> PartialResult<ReprCString> {
mem.read_char_string(self.inner.to_umem().into())
.map_data(|s| s.into())
pub fn read_utf8<M: MemoryView>(
self,
mem: &mut M,
max_length: usize,
) -> PartialResult<ReprCString> {
match mem.read_utf8(self.inner.to_umem().into(), max_length) {
Ok(s) => Ok(s.into()),
Err(PartialError::Error(e)) => Err(PartialError::Error(e)),
Err(PartialError::PartialVirtualRead(s)) => {
Err(PartialError::PartialVirtualRead(s.into()))
}
Err(PartialError::PartialVirtualWrite(s)) => {
Err(PartialError::PartialVirtualWrite(s.into()))
}
}
}

pub fn read_utf8_lossy<M: MemoryView>(
self,
mem: &mut M,
max_length: usize,
) -> PartialResult<ReprCString> {
match mem.read_utf8_lossy(self.inner.to_umem().into(), max_length) {
Ok(s) => Ok(s.into()),
Err(PartialError::Error(e)) => Err(PartialError::Error(e)),
Err(PartialError::PartialVirtualRead(s)) => {
Err(PartialError::PartialVirtualRead(s.into()))
}
Err(PartialError::PartialVirtualWrite(s)) => {
Err(PartialError::PartialVirtualWrite(s.into()))
}
}
}
}

Expand Down

0 comments on commit b87b6ed

Please sign in to comment.