pub mod headers;
pub mod imphash;
pub mod pe;
pub mod types;
#[cfg(feature="win32")]
pub mod valloc;
pub use crate::headers::*;
pub use crate::imphash::*;
pub use crate::pe::*;
pub use crate::types::*;
#[cfg(feature="win32")]
pub use crate::valloc::*;
#[cfg(test)]
mod tests;
use md5::{Md5, Digest};
use sha1::Sha1;
use sha2::Sha256;
use num_traits;
pub use pkbuffer::Error as PKError;
use std::collections::HashMap;
use std::io::Error as IoError;
use std::str::Utf8Error;
use widestring::error::Utf16Error;
pub fn align<V: num_traits::Num + num_traits::Unsigned + num_traits::Zero + core::ops::Rem + Copy>(value: V, boundary: V) -> V {
if value % boundary == (num_traits::zero::<V>()) {
value
}
else {
value + (boundary - (value % boundary))
}
}
pub fn find_embedded_images<P: PE>(pe: &P, pe_type: PEType) -> Result<Option<Vec<PtrPE>>, Error> {
let mut results = Vec::<PtrPE>::new();
let mut index = 2usize;
while index < pe.len() {
if index > (u32::MAX as usize) { break; }
let mz = match pe.read_val::<u16>(index) {
Ok(u) => u,
Err(_) => { index += 1; continue; },
};
if mz != DOS_SIGNATURE { index += 1; continue; }
let dos_header = match pe.read_val::<ImageDOSHeader>(index) {
Ok(h) => h,
Err(_) => { index += 1; continue; },
};
let e_lfanew: usize = index + dos_header.e_lfanew.0 as usize;
let nt_signature = match pe.read_val::<u32>(e_lfanew) {
Ok(s) => s,
Err(_) => { index += 1; continue; },
};
if nt_signature != NT_SIGNATURE { index += 1; continue; }
let eof = pe.len() - index;
let pe_ptr = match pe.offset_to_ptr(index) {
Ok(p) => p,
Err(_) => { index += 1; continue; },
};
let temp_pe = PtrPE::new(pe_type, pe_ptr, eof);
let image_size = match pe_type {
PEType::Disk => match temp_pe.calculate_disk_size() {
Ok(s) => s,
Err(_) => { index += 1; continue; },
},
PEType::Memory => match temp_pe.calculate_memory_size() {
Ok(s) => s,
Err(_) => { index += 1; continue; },
},
};
let validate_size = index + image_size;
if validate_size > pe.len() { index += 1; continue; }
let real_pe = PtrPE::new(pe_type, pe_ptr, image_size);
results.push(real_pe);
index += image_size;
}
if results.len() == 0 { Ok(None) }
else { Ok(Some(results)) }
}
#[derive(Debug)]
pub enum Error {
IoError(IoError),
Utf8Error(Utf8Error),
Utf16Error(Utf16Error),
PKBufferError(PKError),
ParseIntError(std::num::ParseIntError),
OutOfBounds(usize,usize),
InvalidDOSSignature(u16),
BadAlignment,
InvalidPESignature(u32),
InvalidNTSignature(u16),
InvalidOffset(Offset),
InvalidRVA(RVA),
InvalidVA(VA),
SectionNotFound,
BadPointer(*const u8),
UnsupportedDirectory(ImageDirectoryEntry),
InvalidRelocation,
BadDirectory(ImageDirectoryEntry),
CorruptDataDirectory,
ArchMismatch(Arch, Arch),
ResourceNotFound,
#[cfg(feature="win32")]
Win32Error(u32),
#[cfg(feature="win32")]
SectionsNotContiguous,
#[cfg(feature="win32")]
BadSectionCharacteristics(SectionCharacteristics),
#[cfg(feature="win32")]
BufferNotAvailable,
#[cfg(feature="win32")]
ImageBaseNotAvailable,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Error::IoError(ref io_error) =>
write!(f, "i/o error: {}", io_error.to_string()),
Error::Utf8Error(ref utf8_error) =>
write!(f, "UTF8 error: {}", utf8_error.to_string()),
Error::Utf16Error(ref utf16_error) =>
write!(f, "UTF16 error: {}", utf16_error.to_string()),
Error::PKBufferError(ref pk_error) =>
write!(f, "PKBuffer error: {}", pk_error.to_string()),
Error::ParseIntError(ref int_error) =>
write!(f, "Int parsing error: {}", int_error.to_string()),
Error::OutOfBounds(expected, got) =>
write!(f, "The PE buffer was too small to complete the operation. Buffer length is {}, got {}.", expected, got),
Error::InvalidDOSSignature(sig) =>
write!(f, "The PE file has an invalid DOS signature: {:#x}", sig),
Error::BadAlignment =>
write!(f, "The header is not aligned correctly."),
Error::InvalidPESignature(sig) =>
write!(f, "The PE file has an invalid PE signature: {:#x}", sig),
Error::InvalidNTSignature(sig) =>
write!(f, "The PE file has an invalid NT signature: {:#x}", sig),
Error::InvalidOffset(offset) =>
write!(f, "The offset provided or generated resulted in an invalid offset value: {:#x}", offset.0),
Error::InvalidRVA(rva) =>
write!(f, "The RVA provided or generated resulted in an invalid RVA value: {:#x}", rva.0),
Error::InvalidVA(va) => {
let va_value = match va {
VA::VA32(va32) => va32.0 as u64,
VA::VA64(va64) => va64.0,
};
write!(f, "The VA provided or generated resulted in an invalid VA value: {:#x}", va_value)
},
Error::SectionNotFound =>
write!(f, "The PE section was not found given the search criteria."),
Error::BadPointer(ptr) =>
write!(f, "The pointer provided or generated did not fit in the range of the buffer: {:p}", ptr),
Error::UnsupportedDirectory(data_dir) =>
write!(f, "The data directory requested is currently unsupported: {:?}", data_dir),
Error::InvalidRelocation =>
write!(f, "The relocation entry is invalid."),
Error::BadDirectory(data_dir) =>
write!(f, "The provided directory is not available in the PE: {:?}", data_dir),
Error::CorruptDataDirectory =>
write!(f, "The data directory is corrupt and cannot be parsed."),
Error::ArchMismatch(expected, got) =>
write!(f, "The architecture of the Rust binary and the given PE file do not match: expected {:?}, got {:?}", expected, got),
Error::ResourceNotFound =>
write!(f, "The resource was not found by the provided parameters."),
#[cfg(feature="win32")]
Error::Win32Error(err) => write!(f, "The function returned a Win32 error: {:#x}", err),
#[cfg(feature="win32")]
Error::SectionsNotContiguous => write!(f, "The sections in the PE file were not contiguous"),
#[cfg(feature="win32")]
Error::BadSectionCharacteristics(chars) => write!(f, "Bad section characteristics: {:#x}", chars.bits()),
#[cfg(feature="win32")]
Error::BufferNotAvailable => write!(f, "The buffer is no longer available"),
#[cfg(feature="win32")]
Error::ImageBaseNotAvailable => write!(f, "The PE could not be loaded at its preferred base address and has no relocation table"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::IoError(ref e) => Some(e),
Self::PKBufferError(ref e) => Some(e),
_ => None,
}
}
}
impl std::convert::From<IoError> for Error {
fn from(io_error: IoError) -> Self {
Self::IoError(io_error)
}
}
impl std::convert::From<Utf8Error> for Error {
fn from(utf8_error: Utf8Error) -> Self {
Self::Utf8Error(utf8_error)
}
}
impl std::convert::From<Utf16Error> for Error {
fn from(utf16_error: Utf16Error) -> Self {
Self::Utf16Error(utf16_error)
}
}
impl std::convert::From<PKError> for Error {
fn from(pk_error: PKError) -> Self {
Self::PKBufferError(pk_error)
}
}
impl std::convert::From<std::num::ParseIntError> for Error {
fn from(int_error: std::num::ParseIntError) -> Self {
Self::ParseIntError(int_error)
}
}
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
pub trait HashData {
fn md5(&self) -> Vec<u8>;
fn sha1(&self) -> Vec<u8>;
fn sha256(&self) -> Vec<u8>;
}
impl HashData for [u8] {
fn md5(&self) -> Vec<u8> {
let mut hash = Md5::new();
hash.update(self);
hash.finalize()
.as_slice()
.iter()
.cloned()
.collect()
}
fn sha1(&self) -> Vec<u8> {
let mut hash = Sha1::new();
hash.update(self);
hash.finalize()
.as_slice()
.iter()
.cloned()
.collect()
}
fn sha256(&self) -> Vec<u8> {
let mut hash = Sha256::new();
hash.update(self);
hash.finalize()
.as_slice()
.iter()
.cloned()
.collect()
}
}
impl<T> HashData for T
where
T: PE
{
fn md5(&self) -> Vec<u8> { self.as_slice().md5() }
fn sha1(&self) -> Vec<u8> { self.as_slice().sha1() }
fn sha256(&self) -> Vec<u8> { self.as_slice().sha256() }
}
pub trait Entropy {
fn entropy(&self) -> f64;
}
impl Entropy for [u8] {
fn entropy(&self) -> f64 {
if self.len() == 0 { return 0.0_f64; }
let mut occurences: HashMap<u8, usize> = (0..=255).map(|x| (x, 0)).collect();
for c in self { occurences.insert(*c, occurences.get(c).unwrap()+1); }
let mut entropy = 0.0_f64;
for (_, weight) in occurences {
let p_x = (weight as f64) / (self.len() as f64);
if p_x == 0.0 { continue; }
entropy -= p_x * p_x.log2();
}
entropy.abs()
}
}
impl<T> Entropy for T
where
T: PE
{
fn entropy(&self) -> f64 { self.as_slice().entropy() }
}