1pub mod headers;
31pub mod imphash;
32pub mod pe;
33pub mod types;
34
35#[cfg(feature="win32")]
36pub mod valloc;
37
38pub use crate::headers::*;
39pub use crate::imphash::*;
40pub use crate::pe::*;
41pub use crate::types::*;
42
43#[cfg(feature="win32")]
44pub use crate::valloc::*;
45
46#[cfg(test)]
47mod tests;
48
49use md5::{Md5, Digest};
50use sha1::Sha1;
51use sha2::Sha256;
52
53use num_traits;
54
55use pkbuffer::Error as PKError;
56
57use std::collections::HashMap;
58use std::io::Error as IoError;
59use std::str::Utf8Error;
60use widestring::error::Utf16Error;
61
62pub fn align<V: num_traits::Num + num_traits::Unsigned + num_traits::Zero + core::ops::Rem + Copy>(value: V, boundary: V) -> V {
77 if value % boundary == (num_traits::zero::<V>()) {
78 value
79 }
80 else {
81 value + (boundary - (value % boundary))
82 }
83}
84
85pub fn find_embedded_images<P: PE>(pe: &P, pe_type: PEType) -> Result<Option<Vec<PtrPE>>, Error> {
87 let mut results = Vec::<PtrPE>::new();
88 let mut index = 2usize; while index < pe.len() {
91 if index > (u32::MAX as usize) { break; }
92
93 let mz = match pe.get_ref::<u16>(index) {
94 Ok(u) => u,
95 Err(_) => { index += 1; continue; },
96 };
97 if *mz != DOS_SIGNATURE { index += 1; continue; }
98
99 let dos_header = match pe.get_ref::<ImageDOSHeader>(index) {
100 Ok(h) => h,
101 Err(_) => { index += 1; continue; },
102 };
103
104 let e_lfanew: usize = index + dos_header.e_lfanew.0 as usize;
105
106 let nt_signature = match pe.get_ref::<u32>(e_lfanew) {
107 Ok(s) => s,
108 Err(_) => { index += 1; continue; },
109 };
110
111 if *nt_signature != NT_SIGNATURE { index += 1; continue; }
112
113 let eof = pe.len() - index;
117 let pe_ptr = match pe.offset_to_ptr(index) {
118 Ok(p) => p,
119 Err(_) => { index += 1; continue; },
120 };
121 let temp_pe = PtrPE::new(pe_type, pe_ptr, eof);
122
123 let image_size = match pe_type {
124 PEType::Disk => match temp_pe.calculate_disk_size() {
125 Ok(s) => s,
126 Err(_) => { index += 1; continue; },
127 },
128 PEType::Memory => match temp_pe.calculate_memory_size() {
129 Ok(s) => s,
130 Err(_) => { index += 1; continue; },
131 },
132 };
133
134 let validate_size = index + image_size;
135 if validate_size > pe.len() { index += 1; continue; }
136
137 let real_pe = PtrPE::new(pe_type, pe_ptr, image_size);
138
139 results.push(real_pe);
140 index += image_size;
141 }
142
143 if results.len() == 0 { Ok(None) }
144 else { Ok(Some(results)) }
145}
146
147#[derive(Debug)]
149pub enum Error {
150 IoError(IoError),
152 Utf8Error(Utf8Error),
154 Utf16Error(Utf16Error),
156 PKBufferError(PKError),
158 ParseIntError(std::num::ParseIntError),
160 OutOfBounds(usize,usize),
164 InvalidDOSSignature(u16),
168 BadAlignment,
170 InvalidPESignature(u32),
174 InvalidNTSignature(u16),
178 InvalidOffset(Offset),
182 InvalidRVA(RVA),
186 InvalidVA(VA),
190 SectionNotFound,
192 BadPointer(*const u8),
196 UnsupportedDirectory(ImageDirectoryEntry),
200 InvalidRelocation,
202 BadDirectory(ImageDirectoryEntry),
206 CorruptDataDirectory,
208 ArchMismatch(Arch, Arch),
212 ResourceNotFound,
214 #[cfg(feature="win32")]
218 Win32Error(u32),
219 #[cfg(feature="win32")]
221 SectionsNotContiguous,
222 #[cfg(feature="win32")]
225 BadSectionCharacteristics(SectionCharacteristics),
226 #[cfg(feature="win32")]
228 BufferNotAvailable,
229}
230impl std::fmt::Display for Error {
231 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
232 match *self {
233 Error::IoError(ref io_error) =>
234 write!(f, "i/o error: {}", io_error.to_string()),
235 Error::Utf8Error(ref utf8_error) =>
236 write!(f, "UTF8 error: {}", utf8_error.to_string()),
237 Error::Utf16Error(ref utf16_error) =>
238 write!(f, "UTF16 error: {}", utf16_error.to_string()),
239 Error::PKBufferError(ref pk_error) =>
240 write!(f, "PKBuffer error: {}", pk_error.to_string()),
241 Error::ParseIntError(ref int_error) =>
242 write!(f, "Int parsing error: {}", int_error.to_string()),
243 Error::OutOfBounds(expected, got) =>
244 write!(f, "The PE buffer was too small to complete the operation. Buffer length is {}, got {}.", expected, got),
245 Error::InvalidDOSSignature(sig) =>
246 write!(f, "The PE file has an invalid DOS signature: {:#x}", sig),
247 Error::BadAlignment =>
248 write!(f, "The header is not aligned correctly."),
249 Error::InvalidPESignature(sig) =>
250 write!(f, "The PE file has an invalid PE signature: {:#x}", sig),
251 Error::InvalidNTSignature(sig) =>
252 write!(f, "The PE file has an invalid NT signature: {:#x}", sig),
253 Error::InvalidOffset(offset) =>
254 write!(f, "The offset provided or generated resulted in an invalid offset value: {:#x}", offset.0),
255 Error::InvalidRVA(rva) =>
256 write!(f, "The RVA provided or generated resulted in an invalid RVA value: {:#x}", rva.0),
257 Error::InvalidVA(va) => {
258 let va_value = match va {
259 VA::VA32(va32) => va32.0 as u64,
260 VA::VA64(va64) => va64.0,
261 };
262
263 write!(f, "The VA provided or generated resulted in an invalid VA value: {:#x}", va_value)
264 },
265 Error::SectionNotFound =>
266 write!(f, "The PE section was not found given the search criteria."),
267 Error::BadPointer(ptr) =>
268 write!(f, "The pointer provided or generated did not fit in the range of the buffer: {:p}", ptr),
269 Error::UnsupportedDirectory(data_dir) =>
270 write!(f, "The data directory requested is currently unsupported: {:?}", data_dir),
271 Error::InvalidRelocation =>
272 write!(f, "The relocation entry is invalid."),
273 Error::BadDirectory(data_dir) =>
274 write!(f, "The provided directory is not available in the PE: {:?}", data_dir),
275 Error::CorruptDataDirectory =>
276 write!(f, "The data directory is corrupt and cannot be parsed."),
277 Error::ArchMismatch(expected, got) =>
278 write!(f, "The architecture of the Rust binary and the given PE file do not match: expected {:?}, got {:?}", expected, got),
279 Error::ResourceNotFound =>
280 write!(f, "The resource was not found by the provided parameters."),
281 #[cfg(feature="win32")]
282 Error::Win32Error(err) => write!(f, "The function returned a Win32 error: {:#x}", err),
283 #[cfg(feature="win32")]
284 Error::SectionsNotContiguous => write!(f, "The sections in the PE file were not contiguous"),
285 #[cfg(feature="win32")]
286 Error::BadSectionCharacteristics(chars) => write!(f, "Bad section characteristics: {:#x}", chars.bits()),
287 #[cfg(feature="win32")]
288 Error::BufferNotAvailable => write!(f, "The buffer is no longer available"),
289 }
290 }
291}
292impl std::error::Error for Error {
293 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
294 match self {
295 Self::IoError(ref e) => Some(e),
296 Self::PKBufferError(ref e) => Some(e),
297 _ => None,
298 }
299 }
300}
301impl std::convert::From<IoError> for Error {
302 fn from(io_error: IoError) -> Self {
303 Self::IoError(io_error)
304 }
305}
306impl std::convert::From<Utf8Error> for Error {
307 fn from(utf8_error: Utf8Error) -> Self {
308 Self::Utf8Error(utf8_error)
309 }
310}
311impl std::convert::From<Utf16Error> for Error {
312 fn from(utf16_error: Utf16Error) -> Self {
313 Self::Utf16Error(utf16_error)
314 }
315}
316impl std::convert::From<PKError> for Error {
317 fn from(pk_error: PKError) -> Self {
318 Self::PKBufferError(pk_error)
319 }
320}
321impl std::convert::From<std::num::ParseIntError> for Error {
322 fn from(int_error: std::num::ParseIntError) -> Self {
323 Self::ParseIntError(int_error)
324 }
325}
326unsafe impl Send for Error {}
327unsafe impl Sync for Error {}
328
329pub trait HashData {
354 fn md5(&self) -> Vec<u8>;
356 fn sha1(&self) -> Vec<u8>;
358 fn sha256(&self) -> Vec<u8>;
360}
361impl HashData for [u8] {
362 fn md5(&self) -> Vec<u8> {
363 let mut hash = Md5::new();
364 hash.update(self);
365 hash.finalize()
366 .as_slice()
367 .iter()
368 .cloned()
369 .collect()
370 }
371 fn sha1(&self) -> Vec<u8> {
372 let mut hash = Sha1::new();
373 hash.update(self);
374 hash.finalize()
375 .as_slice()
376 .iter()
377 .cloned()
378 .collect()
379 }
380 fn sha256(&self) -> Vec<u8> {
381 let mut hash = Sha256::new();
382 hash.update(self);
383 hash.finalize()
384 .as_slice()
385 .iter()
386 .cloned()
387 .collect()
388 }
389}
390impl<T> HashData for T
391where
392 T: PE
393{
394 fn md5(&self) -> Vec<u8> { self.as_slice().md5() }
395 fn sha1(&self) -> Vec<u8> { self.as_slice().sha1() }
396 fn sha256(&self) -> Vec<u8> { self.as_slice().sha256() }
397}
398
399pub trait Entropy {
401 fn entropy(&self) -> f64;
403}
404impl Entropy for [u8] {
405 fn entropy(&self) -> f64 {
407 if self.len() == 0 { return 0.0_f64; }
408
409 let mut occurences: HashMap<u8, usize> = (0..=255).map(|x| (x, 0)).collect();
410 for c in self { occurences.insert(*c, occurences.get(c).unwrap()+1); }
411
412 let mut entropy = 0.0_f64;
413
414 for (_, weight) in occurences {
415 let p_x = (weight as f64) / (self.len() as f64);
416
417 if p_x == 0.0 { continue; }
418
419 entropy -= p_x * p_x.log2();
420 }
421
422 entropy.abs()
423 }
424}
425impl<T> Entropy for T
426where
427 T: PE
428{
429 fn entropy(&self) -> f64 { self.as_slice().entropy() }
430}
431