#[cfg(feature = "authenticode")]
mod authenticode;
mod data_directories;
#[cfg(feature = "dotnet")]
mod dot_net;
#[cfg(feature = "resources")]
mod resources;
use md5::Md5;
use sha1::Sha1;
use sha2::{Digest, Sha256};
use crate::error::{Error, Result};
use crate::pe::{FileCharacteristics, ImageDosHeader, MagicType, NtHeaders, SectionTable};
#[derive(Debug)]
pub struct PeFile<'a> {
data: &'a [u8],
dos_header: &'a ImageDosHeader,
nt_headers: NtHeaders<'a>,
section_headers: Result<SectionTable<'a>>,
#[cfg_attr(
not(any(feature = "dotnet", feature = "authenticode")),
allow(dead_code)
)]
cache: Cache<'a>,
}
#[derive(Debug, Default)]
struct Cache<'a> {
#[cfg(feature = "dotnet")]
dot_net: dot_net::DotNetCache<'a>,
#[cfg(feature = "authenticode")]
authenticode: crate::lazy::Lazy<Result<Option<crate::authenticode::AuthenticodeInfo<'a>>>>,
_data: core::marker::PhantomData<&'a [u8]>,
}
impl<'a> PeFile<'a> {
pub fn parse(data: &'a [u8]) -> Result<Self> {
let dos_header = ImageDosHeader::parse(data, 0)
.map_err(|_| Error::NotAPeFile("too small for a DOS header"))?;
let nt_headers = NtHeaders::parse(data, dos_header.e_lfanew() as usize)
.map_err(|_| Error::NotAPeFile("NT headers outside of the file"))?;
if nt_headers.signature != NtHeaders::SIGNATURE {
return Err(Error::NotAPeFile("missing PE signature"));
}
let section_headers = SectionTable::parse(
data,
nt_headers.section_table_offset(),
nt_headers.file_header.number_of_sections(),
nt_headers.optional_header.image_base(),
);
Ok(Self {
data,
dos_header,
nt_headers,
section_headers,
cache: Cache::default(),
})
}
pub fn is_pe(data: &[u8]) -> bool {
data.starts_with(b"MZ")
}
#[cfg(feature = "std")]
pub fn is_pe_reader(reader: &mut impl std::io::Read) -> Result<bool> {
let mut magic = [0u8; 2];
reader.read_exact(&mut magic)?;
Ok(Self::is_pe(&magic))
}
pub fn data(&self) -> &'a [u8] {
self.data
}
pub fn dos_header(&self) -> &'a ImageDosHeader {
self.dos_header
}
pub fn nt_headers(&self) -> &NtHeaders<'a> {
&self.nt_headers
}
pub fn section_headers(&self) -> Result<&SectionTable<'a>> {
self.section_headers.as_ref().map_err(Clone::clone)
}
pub fn is_64_bit(&self) -> bool {
self.nt_headers.optional_header.magic() == MagicType::Bit64
}
pub fn is_32_bit(&self) -> bool {
self.nt_headers.optional_header.magic() == MagicType::Bit32
}
pub fn is_dll(&self) -> bool {
self.characteristics().contains(FileCharacteristics::DLL)
}
pub fn is_exe(&self) -> bool {
self.characteristics()
.contains(FileCharacteristics::EXECUTABLE_IMAGE)
}
fn characteristics(&self) -> FileCharacteristics {
self.nt_headers.file_header.characteristics()
}
pub fn md5(&self) -> [u8; 16] {
Md5::digest(self.data).into()
}
pub fn sha1(&self) -> [u8; 20] {
Sha1::digest(self.data).into()
}
pub fn sha256(&self) -> [u8; 32] {
Sha256::digest(self.data).into()
}
}
impl<'a> TryFrom<&'a [u8]> for PeFile<'a> {
type Error = Error;
fn try_from(data: &'a [u8]) -> Result<Self> {
Self::parse(data)
}
}