use object::{
macho::FatHeader,
Object,
ObjectSection,
read::macho::FatArch,
Section,
};
use crate::Pattern;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SectionResult {
pub raw_offset: usize,
pub section_offset: usize,
pub section_address: u64,
pub archive_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ObjectError {
InvalidObject,
SectionNotFound,
SectionDataNotFound,
}
impl std::fmt::Display for ObjectError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidObject => write!(f, "the content of the data to scan is not a valid object file"),
Self::SectionNotFound => write!(f, "the specified binary section is not found"),
Self::SectionDataNotFound => write!(f, "the data of the specified binary section is not available"),
}
}
}
impl std::error::Error for ObjectError {}
pub trait ObjectScan {
fn scan_object(
&self,
data: &[u8],
section_name: &str,
callback: impl FnMut(SectionResult) -> bool + Send + Sync,
) -> Result<bool, ObjectError>;
}
impl ObjectScan for Pattern {
fn scan_object(
&self,
data: &[u8],
section_name: &str,
mut callback: impl FnMut(SectionResult) -> bool + Send + Sync,
) -> Result<bool, ObjectError> {
if let Ok(file) = object::File::parse(data) {
let section = file.section_by_name(section_name)
.ok_or(ObjectError::SectionNotFound)?;
scan_section(&self, §ion, None, 0, &mut callback)
}
else if let Ok(archive) = FatHeader::parse_arch32(&*data) {
let mut section_found = false;
let mut found = false;
for arch in archive {
if let Ok(data) = arch.data(&*data) {
let file = object::File::parse(data)
.or(Err(ObjectError::InvalidObject))?;
if let Some(section) = file.section_by_name(section_name) {
section_found = true;
if scan_section(
&self,
§ion,
Some(format!("{:#?}", arch.architecture())),
arch.offset() as usize,
&mut callback,
)? {
found = true;
}
}
}
}
if !section_found {
Err(ObjectError::SectionNotFound)
} else {
Ok(found)
}
}
else {
Err(ObjectError::InvalidObject)
}
}
}
fn scan_section(
pattern: &Pattern,
section: &Section,
archive_id: Option<String>,
archive_offset: usize,
callback: &mut (impl FnMut(SectionResult) -> bool + Send + Sync),
) -> Result<bool, ObjectError> {
let section_data = section.data()
.or(Err(ObjectError::SectionDataNotFound))?;
let section_base = archive_offset + section.file_range()
.ok_or(ObjectError::SectionDataNotFound)?.0 as usize;
Ok(pattern.scan(section_data, |offset| {
callback(SectionResult {
raw_offset: section_base + offset,
section_offset: offset,
section_address: section.address(),
archive_id: archive_id.clone(),
})
}))
}