memflow/types/
pointer.rs

1/*!
2Pointer abstraction.
3*/
4
5use crate::cglue::ReprCString;
6use crate::dataview::Pod;
7use crate::error::PartialResult;
8use crate::mem::MemoryView;
9use crate::prelude::PartialError;
10use crate::types::{imem, umem, Address, ByteSwap, PrimitiveAddress};
11
12use std::convert::TryInto;
13use std::marker::PhantomData;
14use std::mem::size_of;
15use std::{cmp, fmt, hash, ops};
16
17pub type Pointer32<T> = Pointer<u32, T>;
18pub type Pointer64<T> = Pointer<u64, T>;
19
20const _: [(); std::mem::size_of::<Pointer32<()>>()] = [(); std::mem::size_of::<u32>()];
21const _: [(); std::mem::size_of::<Pointer64<()>>()] = [(); std::mem::size_of::<u64>()];
22
23/// This type can be used in structs that are being read from the target memory.
24/// It holds a phantom type that can be used to describe the proper type of the pointer
25/// and to read it in a more convenient way.
26///
27/// This module is a direct adaption of [CasualX's great IntPtr crate](https://github.com/CasualX/intptr).
28///
29/// Generally the generic Type should implement the Pod trait to be read into easily.
30/// See [here](https://docs.rs/dataview/0.1.1/dataview/) for more information on the Pod trait.
31///
32/// # Examples
33///
34/// ```
35/// use memflow::types::Pointer64;
36/// use memflow::mem::MemoryView;
37/// use memflow::dataview::Pod;
38///
39/// #[repr(C)]
40/// #[derive(Clone, Debug, Pod)]
41/// struct Foo {
42///     pub some_value: i64,
43/// }
44///
45/// #[repr(C)]
46/// #[derive(Clone, Debug, Pod)]
47/// struct Bar {
48///     pub foo_ptr: Pointer64<Foo>,
49/// }
50///
51/// fn read_foo_bar(mem: &mut impl MemoryView) {
52///     let bar: Bar = mem.read(0x1234.into()).unwrap();
53///     let foo = bar.foo_ptr.read(mem).unwrap();
54///     println!("value: {}", foo.some_value);
55/// }
56///
57/// # use memflow::types::size;
58/// # use memflow::dummy::DummyOs;
59/// # use memflow::os::Process;
60/// # read_foo_bar(&mut DummyOs::quick_process(size::mb(2), &[]));
61/// ```
62///
63/// ```
64/// use memflow::types::Pointer64;
65/// use memflow::mem::MemoryView;
66/// use memflow::dataview::Pod;
67///
68/// #[repr(C)]
69/// #[derive(Clone, Debug, Pod)]
70/// struct Foo {
71///     pub some_value: i64,
72/// }
73///
74/// #[repr(C)]
75/// #[derive(Clone, Debug, Pod)]
76/// struct Bar {
77///     pub foo_ptr: Pointer64<Foo>,
78/// }
79///
80/// fn read_foo_bar(mem: &mut impl MemoryView) {
81///     let bar: Bar = mem.read(0x1234.into()).unwrap();
82///     let foo = mem.read_ptr(bar.foo_ptr).unwrap();
83///     println!("value: {}", foo.some_value);
84/// }
85///
86/// # use memflow::dummy::DummyOs;
87/// # use memflow::os::Process;
88/// # use memflow::types::size;
89/// # read_foo_bar(&mut DummyOs::quick_process(size::mb(2), &[]));
90/// ```
91#[repr(transparent)]
92#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
93pub struct Pointer<U: Sized, T: ?Sized = ()> {
94    pub inner: U,
95    phantom_data: PhantomData<fn() -> T>,
96}
97unsafe impl<U: Pod, T: ?Sized + 'static> Pod for Pointer<U, T> {}
98
99impl<U: PrimitiveAddress, T: ?Sized> Pointer<U, T> {
100    const PHANTOM_DATA: PhantomData<fn() -> T> = PhantomData;
101
102    /// Returns a pointer64 with a value of zero.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// use memflow::types::Pointer64;
108    ///
109    /// println!("pointer: {}", Pointer64::<()>::null());
110    /// ```
111    #[inline]
112    pub fn null() -> Self {
113        Pointer {
114            inner: U::null(),
115            phantom_data: PhantomData,
116        }
117    }
118
119    /// Returns `true` if the pointer64 is null.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use memflow::types::Pointer32;
125    ///
126    /// let ptr = Pointer32::<()>::from(0x1000u32);
127    /// assert!(!ptr.is_null());
128    /// ```
129    #[inline]
130    pub fn is_null(self) -> bool {
131        self.inner.is_null()
132    }
133
134    /// Converts the pointer64 to an Option that is None when it is null
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use memflow::types::Pointer64;
140    ///
141    /// assert_eq!(Pointer64::<()>::null().non_null(), None);
142    /// assert_eq!(Pointer64::<()>::from(0x1000u64).non_null(), Some(Pointer64::from(0x1000u64)));
143    /// ```
144    #[inline]
145    pub fn non_null(self) -> Option<Pointer<U, T>> {
146        if self.is_null() {
147            None
148        } else {
149            Some(self)
150        }
151    }
152
153    /// Converts the pointer into a raw `umem` value.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use memflow::types::{Pointer64, umem};
159    ///
160    /// let ptr = Pointer64::<()>::from(0x1000u64);
161    /// let ptr_umem: umem = ptr.to_umem();
162    /// assert_eq!(ptr_umem, 0x1000);
163    /// ```
164    #[inline]
165    pub fn to_umem(self) -> umem {
166        self.inner.to_umem()
167    }
168
169    // Returns the address this pointer holds.
170    #[inline]
171    pub fn address(&self) -> Address {
172        Address::from(self.inner)
173    }
174}
175
176impl<U: PrimitiveAddress, T: Sized> Pointer<U, T> {
177    /// Calculates the offset from a pointer64
178    ///
179    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer offset of `3 * size_of::<T>()` bytes.
180    ///
181    /// # Panics
182    ///
183    /// This function panics if `T` is a Zero-Sized Type ("ZST").
184    /// This function also panics when `offset * size_of::<T>()`
185    /// causes overflow of a signed 64-bit integer.
186    ///
187    /// # Examples:
188    ///
189    /// ```
190    /// use memflow::types::Pointer64;
191    ///
192    /// let ptr = Pointer64::<u16>::from(0x1000u64);
193    ///
194    /// println!("{:?}", ptr.offset(3));
195    /// ```
196    pub fn offset(self, count: imem) -> Self {
197        let pointee_size = U::from_umem(size_of::<T>() as umem);
198        assert!(U::null() < pointee_size && pointee_size <= PrimitiveAddress::max());
199
200        if count >= 0 {
201            self.inner
202                .wrapping_add(U::from_umem(pointee_size.to_umem() * count as umem))
203                .into()
204        } else {
205            self.inner
206                .wrapping_sub(U::from_umem(pointee_size.to_umem() * (-count) as umem))
207                .into()
208        }
209    }
210
211    /// Calculates the distance between two pointers. The returned value is in
212    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
213    ///
214    /// This function is the inverse of [`offset`].
215    ///
216    /// [`offset`]: #method.offset
217    ///
218    /// # Panics
219    ///
220    /// This function panics if `T` is a Zero-Sized Type ("ZST").
221    ///
222    /// # Examples:
223    ///
224    /// ```
225    /// use memflow::types::Pointer64;
226    ///
227    /// let ptr1 = Pointer64::<u16>::from(0x1000u64);
228    /// let ptr2 = Pointer64::<u16>::from(0x1008u64);
229    ///
230    /// assert_eq!(ptr2.offset_from(ptr1), 4);
231    /// assert_eq!(ptr1.offset_from(ptr2), -4);
232    /// ```
233    pub fn offset_from(self, origin: Self) -> imem {
234        let pointee_size: imem = size_of::<T>().try_into().unwrap();
235        let offset = self.inner.to_imem().wrapping_sub(origin.inner.to_imem());
236        offset / pointee_size as imem
237    }
238
239    /// Calculates the offset from a pointer (convenience for `.offset(count as i64)`).
240    ///
241    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
242    /// offset of `3 * size_of::<T>()` bytes.
243    ///
244    /// # Panics
245    ///
246    /// This function panics if `T` is a Zero-Sized Type ("ZST").
247    ///
248    /// # Examples
249    ///
250    /// Basic usage:
251    ///
252    /// ```
253    /// use memflow::types::Pointer64;
254    ///
255    /// let ptr = Pointer64::<u16>::from(0x1000u64);
256    ///
257    /// println!("{:?}", ptr.add(3));
258    /// ```
259    #[allow(clippy::should_implement_trait)]
260    pub fn add(self, count: umem) -> Self {
261        self.offset(count as imem)
262    }
263
264    /// Calculates the offset from a pointer (convenience for
265    /// `.offset((count as isize).wrapping_neg())`).
266    ///
267    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
268    /// offset of `3 * size_of::<T>()` bytes.
269    ///
270    /// # Panics
271    ///
272    /// This function panics if `T` is a Zero-Sized Type ("ZST").
273    ///
274    /// # Examples
275    ///
276    /// Basic usage:
277    ///
278    /// ```
279    /// use memflow::types::Pointer64;
280    ///
281    /// let ptr = Pointer64::<u16>::from(0x1000u64);
282    ///
283    /// println!("{:?}", ptr.sub(3));
284    /// ```
285    #[allow(clippy::should_implement_trait)]
286    pub fn sub(self, count: umem) -> Self {
287        self.offset((count as imem).wrapping_neg())
288    }
289}
290
291/// Implement special phys/virt read/write for Pod types
292impl<U: PrimitiveAddress, T: Pod + ?Sized> Pointer<U, T> {
293    pub fn read_into<M: MemoryView>(self, mem: &mut M, out: &mut T) -> PartialResult<()> {
294        mem.read_ptr_into(self, out)
295    }
296}
297
298impl<U: PrimitiveAddress, T: Pod + Sized> Pointer<U, T> {
299    pub fn read<M: MemoryView>(self, mem: &mut M) -> PartialResult<T> {
300        mem.read_ptr(self)
301    }
302
303    pub fn write<M: MemoryView>(self, mem: &mut M, data: &T) -> PartialResult<()> {
304        mem.write_ptr(self, data)
305    }
306}
307
308/// Implement special phys/virt read/write for CReprStr
309impl<U: PrimitiveAddress> Pointer<U, ReprCString> {
310    pub fn read_utf8<M: MemoryView>(
311        self,
312        mem: &mut M,
313        max_length: usize,
314    ) -> PartialResult<ReprCString> {
315        match mem.read_utf8(self.inner.to_umem().into(), max_length) {
316            Ok(s) => Ok(s.into()),
317            Err(PartialError::Error(e)) => Err(PartialError::Error(e)),
318            Err(PartialError::PartialVirtualRead(s)) => {
319                Err(PartialError::PartialVirtualRead(s.into()))
320            }
321            Err(PartialError::PartialVirtualWrite(s)) => {
322                Err(PartialError::PartialVirtualWrite(s.into()))
323            }
324        }
325    }
326
327    pub fn read_utf8_lossy<M: MemoryView>(
328        self,
329        mem: &mut M,
330        max_length: usize,
331    ) -> PartialResult<ReprCString> {
332        match mem.read_utf8_lossy(self.inner.to_umem().into(), max_length) {
333            Ok(s) => Ok(s.into()),
334            Err(PartialError::Error(e)) => Err(PartialError::Error(e)),
335            Err(PartialError::PartialVirtualRead(s)) => {
336                Err(PartialError::PartialVirtualRead(s.into()))
337            }
338            Err(PartialError::PartialVirtualWrite(s)) => {
339                Err(PartialError::PartialVirtualWrite(s.into()))
340            }
341        }
342    }
343}
344
345impl<U: PrimitiveAddress, T> Pointer<U, [T]> {
346    pub fn decay(self) -> Pointer<U, T> {
347        Pointer {
348            inner: self.inner,
349            phantom_data: Pointer::<U, T>::PHANTOM_DATA,
350        }
351    }
352
353    pub fn at(self, i: umem) -> Pointer<U, T> {
354        let inner = self
355            .inner
356            .wrapping_add(U::from_umem(size_of::<T>() as umem * i));
357        Pointer {
358            inner,
359            phantom_data: Pointer::<U, T>::PHANTOM_DATA,
360        }
361    }
362}
363
364impl<U: PrimitiveAddress, T: ?Sized> Copy for Pointer<U, T> {}
365impl<U: PrimitiveAddress, T: ?Sized> Clone for Pointer<U, T> {
366    #[inline(always)]
367    fn clone(&self) -> Pointer<U, T> {
368        *self
369    }
370}
371impl<U: PrimitiveAddress, T: ?Sized> Default for Pointer<U, T> {
372    #[inline(always)]
373    fn default() -> Pointer<U, T> {
374        Pointer::null()
375    }
376}
377impl<U: PrimitiveAddress, T: ?Sized> Eq for Pointer<U, T> {}
378impl<U: PrimitiveAddress, T: ?Sized> PartialEq for Pointer<U, T> {
379    #[inline(always)]
380    fn eq(&self, rhs: &Pointer<U, T>) -> bool {
381        self.inner == rhs.inner
382    }
383}
384impl<U: PrimitiveAddress, T: ?Sized> PartialOrd for Pointer<U, T> {
385    #[inline(always)]
386    fn partial_cmp(&self, rhs: &Pointer<U, T>) -> Option<cmp::Ordering> {
387        Some(self.cmp(rhs))
388    }
389}
390impl<U: PrimitiveAddress, T: ?Sized> Ord for Pointer<U, T> {
391    #[inline(always)]
392    fn cmp(&self, rhs: &Pointer<U, T>) -> cmp::Ordering {
393        self.inner.cmp(&rhs.inner)
394    }
395}
396impl<U: PrimitiveAddress, T: ?Sized> hash::Hash for Pointer<U, T> {
397    #[inline(always)]
398    fn hash<H: hash::Hasher>(&self, state: &mut H) {
399        self.inner.hash(state)
400    }
401}
402impl<U: PrimitiveAddress, T: ?Sized> AsRef<U> for Pointer<U, T> {
403    #[inline(always)]
404    fn as_ref(&self) -> &U {
405        &self.inner
406    }
407}
408impl<U: PrimitiveAddress, T: ?Sized> AsMut<U> for Pointer<U, T> {
409    #[inline(always)]
410    fn as_mut(&mut self) -> &mut U {
411        &mut self.inner
412    }
413}
414
415// From implementations
416impl<U: PrimitiveAddress, T: ?Sized> From<U> for Pointer<U, T> {
417    #[inline(always)]
418    fn from(address: U) -> Pointer<U, T> {
419        Pointer {
420            inner: address,
421            phantom_data: PhantomData,
422        }
423    }
424}
425
426impl<T: ?Sized> From<Address> for Pointer64<T> {
427    #[inline(always)]
428    fn from(address: Address) -> Pointer64<T> {
429        Pointer {
430            inner: address.to_umem() as u64,
431            phantom_data: PhantomData,
432        }
433    }
434}
435
436// Into implementations
437impl<U: Into<Address>, T: ?Sized> From<Pointer<U, T>> for umem {
438    #[inline(always)]
439    fn from(ptr: Pointer<U, T>) -> umem {
440        let address: Address = ptr.inner.into();
441        address.to_umem()
442    }
443}
444
445// Arithmetic operations
446impl<U: PrimitiveAddress, T> ops::Add<umem> for Pointer<U, T> {
447    type Output = Pointer<U, T>;
448    #[inline(always)]
449    fn add(self, other: umem) -> Pointer<U, T> {
450        let address = self.inner + U::from_umem(size_of::<T>() as umem * other);
451        Pointer {
452            inner: address,
453            phantom_data: self.phantom_data,
454        }
455    }
456}
457impl<U: PrimitiveAddress, T> ops::Sub<umem> for Pointer<U, T> {
458    type Output = Pointer<U, T>;
459    #[inline(always)]
460    fn sub(self, other: umem) -> Pointer<U, T> {
461        let address = self.inner - U::from_umem(size_of::<T>() as umem * other);
462        Pointer {
463            inner: address,
464            phantom_data: self.phantom_data,
465        }
466    }
467}
468
469#[cfg(feature = "64_bit_mem")]
470impl<U: PrimitiveAddress, T> ops::Add<usize> for Pointer<U, T> {
471    type Output = Pointer<U, T>;
472    #[inline(always)]
473    fn add(self, other: usize) -> Pointer<U, T> {
474        self + other as umem
475    }
476}
477#[cfg(feature = "64_bit_mem")]
478impl<U: PrimitiveAddress, T> ops::Sub<usize> for Pointer<U, T> {
479    type Output = Pointer<U, T>;
480    #[inline(always)]
481    fn sub(self, other: usize) -> Pointer<U, T> {
482        self - other as umem
483    }
484}
485
486impl<U: PrimitiveAddress, T: ?Sized> fmt::Debug for Pointer<U, T> {
487    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488        write!(f, "{:x}", self.inner)
489    }
490}
491impl<U: PrimitiveAddress, T: ?Sized> fmt::UpperHex for Pointer<U, T> {
492    #[inline(always)]
493    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
494        write!(f, "{:X}", self.inner)
495    }
496}
497impl<U: PrimitiveAddress, T: ?Sized> fmt::LowerHex for Pointer<U, T> {
498    #[inline(always)]
499    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
500        write!(f, "{:x}", self.inner)
501    }
502}
503impl<U: PrimitiveAddress, T: ?Sized> fmt::Display for Pointer<U, T> {
504    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
505        write!(f, "{:x}", self.inner)
506    }
507}
508
509impl<U: PrimitiveAddress, T: ?Sized + 'static> ByteSwap for Pointer<U, T> {
510    fn byte_swap(&mut self) {
511        self.inner.byte_swap();
512    }
513}
514
515#[cfg(test)]
516mod tests {
517    use super::*;
518
519    #[test]
520    fn offset32() {
521        let ptr8 = Pointer32::<u8>::from(0x1000u32);
522        assert_eq!(ptr8.offset(3).to_umem(), 0x1003);
523        assert_eq!(ptr8.offset(-5).to_umem(), 0xFFB);
524
525        let ptr16 = Pointer32::<u16>::from(0x1000u32);
526        assert_eq!(ptr16.offset(3).to_umem(), 0x1006);
527        assert_eq!(ptr16.offset(-5).to_umem(), 0xFF6);
528
529        let ptr32 = Pointer32::<u32>::from(0x1000u32);
530        assert_eq!(ptr32.offset(3).to_umem(), 0x100C);
531        assert_eq!(ptr32.offset(-5).to_umem(), 0xFEC);
532    }
533
534    #[test]
535    fn offset64() {
536        let ptr8 = Pointer64::<u8>::from(0x1000u64);
537        assert_eq!(ptr8.offset(3).to_umem(), 0x1003);
538        assert_eq!(ptr8.offset(-5).to_umem(), 0xFFB);
539
540        let ptr16 = Pointer64::<u16>::from(0x1000u64);
541        assert_eq!(ptr16.offset(3).to_umem(), 0x1006);
542        assert_eq!(ptr16.offset(-5).to_umem(), 0xFF6);
543
544        let ptr32 = Pointer64::<u32>::from(0x1000u64);
545        assert_eq!(ptr32.offset(3).to_umem(), 0x100C);
546        assert_eq!(ptr32.offset(-5).to_umem(), 0xFEC);
547
548        let ptr64 = Pointer64::<u64>::from(0x1000u64);
549        assert_eq!(ptr64.offset(3).to_umem(), 0x1018);
550        assert_eq!(ptr64.offset(-5).to_umem(), 0xFD8);
551    }
552
553    #[test]
554    fn offset_from() {
555        let ptr1 = Pointer64::<u16>::from(0x1000u64);
556        let ptr2 = Pointer64::<u16>::from(0x1008u64);
557
558        assert_eq!(ptr2.offset_from(ptr1), 4);
559        assert_eq!(ptr1.offset_from(ptr2), -4);
560    }
561}