use anyhow::{Context, Result};
use goblin::pe::{options::ParseOptions, PE};
use std::path::Path;
#[derive(Debug)]
pub struct PeFile {
data: Box<[u8]>,
}
impl PeFile {
pub fn from_path(path: &Path) -> Result<Self> {
let bytes = std::fs::read(path).with_context(|| format!("read {}", path.display()))?;
Ok(Self {
data: bytes.into_boxed_slice(),
})
}
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
Ok(Self {
data: bytes.into_boxed_slice(),
})
}
pub fn image(&self) -> &[u8] {
&self.data
}
fn parse_pe(&self) -> Result<PE<'_>> {
let mut opts = ParseOptions::default();
opts.parse_attribute_certificates = true; PE::parse_with_opts(&self.data, &opts).context("not a valid PE/EFI image")
}
pub fn arch_summary(&self) -> Result<(&'static str, bool)> {
use goblin::pe::header::*;
let pe = self.parse_pe()?;
let arch = match pe.header.coff_header.machine {
COFF_MACHINE_X86_64 => "x86_64",
COFF_MACHINE_ARM64 => "aarch64",
COFF_MACHINE_ARM => "arm",
COFF_MACHINE_X86 => "i386",
_ => "unknown",
};
Ok((arch, pe.is_64))
}
pub fn section_info(&self, name: &str) -> Result<Option<(usize, usize)>> {
let pe = self.parse_pe()?;
let sec = pe.sections.iter().find(|t| t.name().ok() == Some(name));
if let Some(s) = sec {
Ok(Some((
s.pointer_to_raw_data as usize,
s.size_of_raw_data as usize,
)))
} else {
Ok(None)
}
}
pub fn section_bytes(&self, name: &str) -> Result<Option<&[u8]>> {
let pe = self.parse_pe()?;
let sec = pe.sections.iter().find(|t| t.name().ok() == Some(name));
if let Some(s) = sec {
let off = usize::try_from(s.pointer_to_raw_data).ok();
let sz = usize::try_from(s.size_of_raw_data).ok();
if let (Some(off), Some(sz)) = (off, sz) {
let end = off.checked_add(sz);
return Ok(end.and_then(|e| self.data.get(off..e)));
}
}
Ok(None)
}
pub fn read_text(&self, name: &str) -> Result<Option<String>> {
Ok(self.section_bytes(name)?.map(|b| {
let end = b.iter().position(|&c| c == 0).unwrap_or(b.len());
String::from_utf8_lossy(&b[..end]).to_string()
}))
}
pub fn is_signed(&self) -> Result<bool> {
let pe = self.parse_pe()?;
Ok(!pe.certificates.is_empty())
}
pub fn certificate_metadata(&self) -> Result<Vec<(u32, u16, u16)>> {
let pe = self.parse_pe()?;
Ok(pe
.certificates
.iter()
.map(|c| (c.length, c.revision as u16, c.certificate_type as u16))
.collect())
}
pub fn certificate_blobs(&self) -> Result<Vec<&[u8]>> {
let pe = self.parse_pe()?;
Ok(pe.certificates.iter().map(|c| c.certificate).collect())
}
}