use pelite::pe64::{Pe, PeView};
#[derive(Debug, Clone, Copy)]
pub struct BadRelocsError;
impl std::fmt::Display for BadRelocsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("failed to read image relocations")
}
}
impl std::error::Error for BadRelocsError {}
pub trait ImageView: Clone {
fn base_va(&self) -> u64;
fn sections(&self) -> impl Iterator<Item = (u64, &[u8])>;
fn relocs64(&self) -> Result<impl Iterator<Item = u32>, BadRelocsError>;
fn read(&self, va: u64, min_size: usize) -> Option<&[u8]>;
}
impl<I: ImageView> ImageView for &I {
fn base_va(&self) -> u64 {
(*self).base_va()
}
fn sections(&self) -> impl Iterator<Item = (u64, &[u8])> {
(*self).sections()
}
fn relocs64(&self) -> Result<impl Iterator<Item = u32>, BadRelocsError> {
(*self).relocs64()
}
fn read(&self, va: u64, min_size: usize) -> Option<&[u8]> {
(*self).read(va, min_size)
}
}
impl ImageView for PeView<'_> {
fn base_va(&self) -> u64 {
Pe::optional_header(*self).ImageBase
}
fn sections(&self) -> impl Iterator<Item = (u64, &[u8])> {
use pelite::pe64::Pe;
self.section_headers().iter().filter_map(|s| {
self.get_section_bytes(s)
.ok()
.map(|slice| (self.base_va() + s.VirtualAddress as u64, slice))
})
}
#[allow(clippy::filter_map_bool_then)]
fn relocs64(&self) -> Result<impl Iterator<Item = u32>, BadRelocsError> {
let maybe_relocs = match self.base_relocs() {
Ok(relocs) => Some(relocs),
Err(pelite::Error::Null) => None,
Err(_) => return Err(BadRelocsError),
};
Ok(maybe_relocs
.into_iter()
.flat_map(|relocs| relocs.iter_blocks())
.flat_map(|block| {
block.words().iter().filter_map(move |w| {
(block.type_of(w) == 10).then(|| block.rva_of(w))
})
}))
}
fn read(&self, va: u64, min_size: usize) -> Option<&[u8]> {
pelite::pe64::Pe::read(self, va, min_size, 1).ok()
}
}
#[derive(Debug, Clone, Copy)]
pub struct WithBase<T: AsRef<[u8]> + Clone> {
bytes: T,
base: u64,
}
impl<T: AsRef<[u8]> + Clone> WithBase<T> {
#[allow(dead_code)] pub fn new(bytes: T, base: u64) -> Self {
Self { bytes, base }
}
}
impl<T: AsRef<[u8]> + Clone> ImageView for WithBase<T> {
fn base_va(&self) -> u64 {
self.base
}
fn sections(&self) -> impl Iterator<Item = (u64, &[u8])> {
std::iter::once((self.base, self.bytes.as_ref()))
}
fn relocs64(&self) -> Result<impl Iterator<Item = u32>, BadRelocsError> {
Ok(std::iter::empty())
}
fn read(&self, va: u64, min_size: usize) -> Option<&[u8]> {
va.checked_sub(self.base)
.and_then(|offset| offset.try_into().ok())
.and_then(|offset| self.bytes.as_ref().get(offset..))
.and_then(|bytes| (bytes.len() >= min_size).then_some(bytes))
}
}