use std::{error::Error, fs, path::Path};
use goblin::{
Object,
mach::{Mach, MultiArch, SingleArch},
};
use regex::Regex;
pub const ELF: &str = "ELF";
pub const PE: &str = "PE";
pub const MACH_O: &str = "Mach-O";
pub const MACH_O_FAT: &str = "Mach-O (Fat)";
#[derive(Default)]
pub struct DynLibMatches {
pub matched: Vec<String>,
pub unmatched: Vec<String>,
}
impl DynLibMatches {
fn add_matched(&mut self, lib: &str) {
self.matched.push(lib.to_string());
}
fn add_unmatched(&mut self, lib: &str) {
self.unmatched.push(lib.to_string());
}
pub fn all_matched(&self) -> bool {
self.unmatched.is_empty()
}
}
pub struct DynLibs(Vec<String>);
impl DynLibs {
pub fn build_matches(&self, dyn_lib_regexes: &[Regex]) -> DynLibMatches {
let mut matches = DynLibMatches::default();
let mut dyn_lib_regexes = dyn_lib_regexes.to_vec();
for lib in self.0.iter() {
let regexes = dyn_lib_regexes.clone();
let mut matched = false;
for (idx, regex) in regexes.iter().enumerate() {
if regex.is_match(lib) {
matches.add_matched(lib);
dyn_lib_regexes.remove(idx);
matched = true;
break;
}
}
if !matched {
matches.add_unmatched(lib);
}
}
matches
}
pub fn iter(&self) -> impl Iterator<Item = &String> {
self.0.iter()
}
}
pub struct DynLibEntry {
pub index: usize,
pub dyn_libs: DynLibs,
}
pub enum DynLibEntries {
SingleArch(DynLibs),
MultiArch(Vec<DynLibEntry>),
}
pub struct Executable {
pub binary_type: &'static str,
pub dyn_libs: DynLibEntries,
}
impl Executable {
fn parse_libs(libs: &[&str]) -> DynLibs {
DynLibs(
libs.iter()
.filter(|&&lib| lib != "self")
.map(|lib| lib.to_string())
.collect(),
)
}
fn parse_single_entry(libs: &[&str]) -> DynLibEntries {
DynLibEntries::SingleArch(Self::parse_libs(libs))
}
fn parse_mach_o_fat(fat: &MultiArch) -> Result<(&'static str, DynLibEntries), Box<dyn Error>> {
let mut dyn_lib_entries = Vec::with_capacity(fat.narches);
for idx in 0..fat.narches {
match fat.get(idx)? {
SingleArch::MachO(macho) => {
dyn_lib_entries.push(DynLibEntry {
index: idx,
dyn_libs: Self::parse_libs(&macho.libs),
});
}
SingleArch::Archive(_archive) => {
continue;
}
}
}
Ok((MACH_O_FAT, DynLibEntries::MultiArch(dyn_lib_entries)))
}
pub fn from_path(binary_path: &Path) -> Result<Self, Box<dyn Error>> {
let buffer = fs::read(binary_path)?;
let (binary_type, dyn_libs) = match Object::parse(&buffer)? {
Object::Elf(elf) => (ELF, Self::parse_single_entry(&elf.libraries)),
Object::PE(pe) => (PE, Self::parse_single_entry(&pe.libraries)),
Object::Mach(mach) => match mach {
Mach::Binary(macho) => (MACH_O, Self::parse_single_entry(&macho.libs)),
Mach::Fat(fat) => Self::parse_mach_o_fat(&fat)?,
},
_ => {
return Err(format!("Unsupported binary format: {}", binary_path.display()).into());
}
};
Ok(Self {
binary_type,
dyn_libs,
})
}
}