1 pub mod app_memory;
2 pub mod exception_stream;
3 pub mod mappings;
4 pub mod memory_list_stream;
5 pub mod systeminfo_stream;
6 pub mod thread_list_stream;
7 pub mod thread_names_stream;
8 
9 use crate::errors::MemoryWriterError;
10 use crate::minidump_format::*;
11 use std::convert::TryInto;
12 use std::io::{Cursor, Write};
13 
14 type Result<T> = std::result::Result<T, MemoryWriterError>;
15 
16 #[derive(Debug, PartialEq)]
17 pub struct MemoryWriter<T: Default + Sized> {
18     pub position: MDRVA,
19     pub size: usize,
20     phantom: std::marker::PhantomData<T>,
21 }
22 
23 impl<T> MemoryWriter<T>
24 where
25     T: Default + Sized,
26 {
27     /// Create a slot for a type T in the buffer, we can fill right now with real values.
alloc_with_val(buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<Self>28     pub fn alloc_with_val(buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<Self> {
29         // Get position of this value (e.g. before we add ourselves there)
30         let position = buffer.position();
31         let size = std::mem::size_of::<T>();
32         let bytes = unsafe { std::slice::from_raw_parts(&val as *const T as *const u8, size) };
33         buffer.write_all(bytes)?;
34 
35         Ok(MemoryWriter {
36             position: position as u32,
37             size,
38             phantom: std::marker::PhantomData::<T> {},
39         })
40     }
41 
42     /// Create a slot for a type T in the buffer, we can fill later with real values.
43     /// This function fills it with `Default::default()`, which is less performant than
44     /// using uninitialized memory, but safe.
alloc(buffer: &mut Cursor<Vec<u8>>) -> Result<Self>45     pub fn alloc(buffer: &mut Cursor<Vec<u8>>) -> Result<Self> {
46         // Filling out the buffer with default-values
47         let val: T = Default::default();
48         Self::alloc_with_val(buffer, val)
49     }
50 
51     /// Write actual values in the buffer-slot we got during `alloc()`
set_value(&mut self, buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<()>52     pub fn set_value(&mut self, buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<()> {
53         // Save whereever the current cursor stands in the buffer
54         let curr_pos = buffer.position();
55 
56         // Write the actual value we want at our position that
57         // was determined by `alloc()` into the buffer
58         buffer.set_position(self.position as u64);
59         let bytes = unsafe {
60             std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>())
61         };
62         let res = buffer.write_all(bytes);
63 
64         // Resetting whereever we were before updating this
65         // regardless of the write-result
66         buffer.set_position(curr_pos);
67 
68         res?;
69         Ok(())
70     }
71 
location(&self) -> MDLocationDescriptor72     pub fn location(&self) -> MDLocationDescriptor {
73         MDLocationDescriptor {
74             data_size: std::mem::size_of::<T>() as u32,
75             rva: self.position,
76         }
77     }
78 }
79 
80 #[derive(Debug, PartialEq)]
81 pub struct MemoryArrayWriter<T: Default + Sized> {
82     pub position: MDRVA,
83     array_size: usize,
84     phantom: std::marker::PhantomData<T>,
85 }
86 
87 impl<T> MemoryArrayWriter<T>
88 where
89     T: Default + Sized,
90 {
91     /// Create a slot for a type T in the buffer, we can fill in the values in one go.
alloc_from_array(buffer: &mut Cursor<Vec<u8>>, array: &[T]) -> Result<Self>92     pub fn alloc_from_array(buffer: &mut Cursor<Vec<u8>>, array: &[T]) -> Result<Self> {
93         // Get position of this value (e.g. before we add ourselves there)
94         let position = buffer.position();
95         for val in array {
96             let bytes = unsafe {
97                 std::slice::from_raw_parts(val as *const T as *const u8, std::mem::size_of::<T>())
98             };
99             buffer.write_all(bytes)?;
100         }
101 
102         Ok(MemoryArrayWriter {
103             position: position as u32,
104             array_size: array.len(),
105             phantom: std::marker::PhantomData::<T> {},
106         })
107     }
108 
109     /// Create a slot for a type T in the buffer, we can fill later with real values.
110     /// This function fills it with `Default::default()`, which is less performant than
111     /// using uninitialized memory, but safe.
alloc_array(buffer: &mut Cursor<Vec<u8>>, array_size: usize) -> Result<Self>112     pub fn alloc_array(buffer: &mut Cursor<Vec<u8>>, array_size: usize) -> Result<Self> {
113         // Get position of this value (e.g. before we add ourselves there)
114         let position = buffer.position();
115         for _ in 0..array_size {
116             // Filling out the buffer with default-values
117             let val: T = Default::default();
118             let bytes = unsafe {
119                 std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>())
120             };
121             buffer.write_all(bytes)?;
122         }
123 
124         Ok(MemoryArrayWriter {
125             position: position as u32,
126             array_size,
127             phantom: std::marker::PhantomData::<T> {},
128         })
129     }
130 
131     /// Write actual values in the buffer-slot we got during `alloc()`
set_value_at( &mut self, buffer: &mut Cursor<Vec<u8>>, val: T, index: usize, ) -> Result<()>132     pub fn set_value_at(
133         &mut self,
134         buffer: &mut Cursor<Vec<u8>>,
135         val: T,
136         index: usize,
137     ) -> Result<()> {
138         // Save whereever the current cursor stands in the buffer
139         let curr_pos = buffer.position();
140 
141         // Write the actual value we want at our position that
142         // was determined by `alloc()` into the buffer
143         buffer.set_position(self.position as u64 + (std::mem::size_of::<T>() * index) as u64);
144         let bytes = unsafe {
145             std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>())
146         };
147         let res = buffer.write_all(bytes);
148 
149         // Resetting whereever we were before updating this
150         // regardless of the write-result
151         buffer.set_position(curr_pos);
152 
153         res?;
154         Ok(())
155     }
156 
location(&self) -> MDLocationDescriptor157     pub fn location(&self) -> MDLocationDescriptor {
158         MDLocationDescriptor {
159             data_size: (self.array_size * std::mem::size_of::<T>()) as u32,
160             rva: self.position,
161         }
162     }
163 
location_of_index(&self, idx: usize) -> MDLocationDescriptor164     pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor {
165         MDLocationDescriptor {
166             data_size: std::mem::size_of::<T>() as u32,
167             rva: self.position + (std::mem::size_of::<T>() * idx) as u32,
168         }
169     }
170 }
171 
write_string_to_location( buffer: &mut Cursor<Vec<u8>>, text: &str, ) -> Result<MDLocationDescriptor>172 pub fn write_string_to_location(
173     buffer: &mut Cursor<Vec<u8>>,
174     text: &str,
175 ) -> Result<MDLocationDescriptor> {
176     let letters: Vec<u16> = text.encode_utf16().collect();
177 
178     // First write size of the string (x letters in u16, times the size of u16)
179     let text_header = MemoryWriter::<u32>::alloc_with_val(
180         buffer,
181         (letters.len() * std::mem::size_of::<u16>()).try_into()?,
182     )?;
183 
184     // Then write utf-16 letters after that
185     let mut text_section = MemoryArrayWriter::<u16>::alloc_array(buffer, letters.len())?;
186     for (index, letter) in letters.iter().enumerate() {
187         text_section.set_value_at(buffer, *letter, index)?;
188     }
189 
190     let mut location = text_header.location();
191     location.data_size += text_section.location().data_size;
192 
193     Ok(location)
194 }
195