pkbuffer/
lib.rs

1//! [PKBuffer](https://github.com/frank2/pkbuffer) is a library built for arbitrary casting of data structures
2//! onto segments of memory! This includes sections of unowned memory, such as examining the headers of a
3//! currently running executable. It creates an interface for reading and writing data structures to an
4//! arbitrary buffer of bytes.
5//!
6//! For example:
7//! ```rust
8//! use pkbuffer::{Buffer, VecBuffer, Castable};
9//!
10//! #[repr(packed)]
11//! #[derive(Copy, Clone, Castable)]
12//! struct Object {
13//!    byte: u8,
14//!    word: u16,
15//!    dword: u32,
16//! }
17//!
18//! let mut buffer = VecBuffer::with_initial_size(std::mem::size_of::<Object>());
19//! let object = buffer.get_mut_ref::<Object>(0).unwrap();
20//! object.byte = 0x01;
21//! object.word = 0x0302;
22//! object.dword = 0x07060504;
23//!
24//! assert_eq!(buffer, [1,2,3,4,5,6,7]);
25//! ```
26//!
27//! Objects retrieved from [`Buffer`](Buffer) objects must implement the [`Castable`](castable::Castable)
28//! trait. This trait ensures that a series of attributes are applied to the object. For
29//! convenience, a [derive macro](pkbuffer_derive::Castable) is provided.
30//!
31//! Buffer objects are derived from the [`Buffer`](Buffer) trait. This trait
32//! implements much functionality of slice objects as well as data casting
33//! abilities of the derived Buffer objects.
34//!
35//! Buffer objects comes in two forms: *pointer form* ([`PtrBuffer`](PtrBuffer)) and
36//! *allocated form* ([`VecBuffer`](VecBuffer)). Each of these structures come
37//! in handy for different reasons. [`PtrBuffer`](PtrBuffer) is useful on unowned data
38//! such as arbitrary locations in memory, whereas [`VecBuffer`](VecBuffer)'s
39//! utility comes from being able to manipulate the underlying owned data.
40//!
41//! [`VecBuffer`](VecBuffer)s are handy for creating a brand-new buffer of objects.
42//!
43//! ```rust
44//! use pkbuffer::{Buffer, VecBuffer};
45//!
46//! let mut buffer = VecBuffer::new();
47//! buffer.append_ref::<u8>(&0x1);
48//! buffer.append_ref::<u16>(&0x0302);
49//! buffer.append_ref::<u32>(&0x07060504);
50//! assert_eq!(buffer, [1,2,3,4,5,6,7]);
51//! ```
52
53#[cfg(test)]
54mod tests;
55
56mod buffer;
57pub use buffer::*;
58
59mod castable;
60pub use castable::*;
61
62mod ptr;
63pub use ptr::*;
64
65mod vec;
66pub use vec::*;
67
68pub use pkbuffer_derive::*;
69
70/// Errors produced by the library.
71#[derive(Debug)]
72pub enum Error {
73    /// An error produced by [`std::io::Error`](std::io::Error).
74    IoError(std::io::Error),
75    /// The operation went out of bounds.
76    ///
77    /// The first arg represents the current boundary, the second arg
78    /// represents the out-of-bounds argument.
79    OutOfBounds(usize,usize),
80    /// The operation produced an invalid pointer. Argument is the pointer
81    /// in question.
82    InvalidPointer(*const u8),
83    /// The alignment of the given operation is off. The first arg
84    /// represents the expected alignment, the second argument represents
85    /// the alignment of the given object relative to the expected alignment.
86    BadAlignment(usize,usize),
87    /// The type is zero-sized.
88    ZeroSizedType,
89    /// The sizes didn't match. The first arg represents the expected size,
90    /// the second arg represents the received size.
91    SizeMismatch(usize,usize),
92    /// The search term would match everything.
93    SearchMatchesEverything,
94}
95impl std::fmt::Display for Error {
96    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97        match self {
98            Self::IoError(io) => write!(f, "i/o error: {}", io.to_string()),
99            Self::OutOfBounds(expected,got) => write!(f, "out of bounds: boundary is {:#x}, got {:#x} instead", expected, got),
100            Self::InvalidPointer(ptr) => write!(f, "invalid pointer: {:p}", ptr),
101            Self::BadAlignment(expected,got) => write!(f, "bad alignment: expected {}-byte alignment, but alignment is off by {}", expected, got),
102            Self::ZeroSizedType => write!(f, "zero sized type"),
103            Self::SizeMismatch(expected,got) => write!(f, "size mismatch: the two types differed in size, expected {}, got {}", expected, got),
104            Self::SearchMatchesEverything => write!(f, "the search would match everything in the binary"),
105        }
106    }
107}
108impl std::error::Error for Error {
109    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
110        match self {
111            Self::IoError(ref e) => Some(e),
112            _ => None,
113        }
114    }
115}
116impl std::convert::From<std::io::Error> for Error {
117    fn from(io_err: std::io::Error) -> Self {
118        Self::IoError(io_err)
119    }
120}
121unsafe impl Send for Error {}
122unsafe impl Sync for Error {}
123
124/// Convert the given reference of type ```T``` to a [`u8`](u8) [slice](slice).
125pub fn ref_to_bytes<T: Castable>(data: &T) -> Result<&[u8], Error> {
126    if std::mem::size_of::<T>() == 0 { Ok(&[]) }
127    else { slice_ref_to_bytes::<T>(std::slice::from_ref(data)) }
128}
129
130/// Convert the given slice reference of type ```T``` to a [`u8`](u8) [slice](slice).
131pub fn slice_ref_to_bytes<T: Castable>(data: &[T]) -> Result<&[u8], Error> {
132    if std::mem::size_of::<T>() == 0 {
133        Err(Error::ZeroSizedType)
134    }
135    else {
136        Ok(unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data)) })
137    }
138}
139
140/// Convert the given reference of type ```T``` to a mutable [`u8`](u8) [slice](slice).
141pub fn ref_to_mut_bytes<T: Castable>(data: &mut T) -> Result<&mut [u8], Error> {
142    if std::mem::size_of::<T>() == 0 { Ok(&mut []) }
143    else { slice_ref_to_mut_bytes::<T>(std::slice::from_mut(data)) }
144}
145
146/// Convert the given slice reference of type ```T``` to a mutable [`u8`](u8) [slice](slice).
147pub fn slice_ref_to_mut_bytes<T: Castable>(data: &mut [T]) -> Result<&mut [u8], Error> {
148    if std::mem::size_of::<T>() == 0 {
149        Err(Error::ZeroSizedType)
150    }
151    else {
152        Ok(unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, std::mem::size_of_val(data)) })
153    }
154}
155
156/// Cast type `&T` from a [`u8`](u8) slice.
157pub fn bytes_to_ref<T: Castable>(bytes: &[u8]) -> Result<&T, Error> {
158    if bytes.len() != std::mem::size_of::<T>() {
159        Err(Error::SizeMismatch(bytes.len(), std::mem::size_of::<T>()))
160    }
161    else if (bytes.as_ptr() as usize) % std::mem::align_of::<T>() != 0 {
162        Err(Error::BadAlignment(std::mem::align_of::<T>(), (bytes.as_ptr() as usize) % std::mem::align_of::<T>()))
163    }
164    else {
165        Ok(unsafe { &*(bytes.as_ptr() as *const T) })
166    }
167}
168
169/// Cast type `&mut T` from a mutable [`u8`](u8) slice.
170pub fn bytes_to_mut_ref<T: Castable>(bytes: &mut [u8]) -> Result<&mut T, Error> {
171    if bytes.len() != std::mem::size_of::<T>() {
172        Err(Error::SizeMismatch(bytes.len(), std::mem::size_of::<T>()))
173    }
174    else if (bytes.as_ptr() as usize) % std::mem::align_of::<T>() != 0 {
175        Err(Error::BadAlignment(std::mem::align_of::<T>(), (bytes.as_ptr() as usize) % std::mem::align_of::<T>()))
176    }
177    else {
178        Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
179    }
180}