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}