mod elf;
#[cfg(test)]
pub mod fixture;
mod macho;
mod pe;
use crate::analysis::entropy::entropy;
use crate::model::{Binary, Format, Section};
use anyhow::{bail, Result};
use goblin::Object;
#[allow(dead_code)]
pub fn detect(bytes: &[u8]) -> Format {
match Object::parse(bytes) {
Ok(Object::PE(_)) => Format::Pe,
Ok(Object::Elf(_)) => Format::Elf,
Ok(Object::Mach(_)) => Format::MachO,
Ok(Object::Archive(_)) => Format::Archive,
_ => Format::Unknown,
}
}
pub fn analyze(path: &str, bytes: &[u8]) -> Result<Binary> {
let mut bin = match Object::parse(bytes) {
Ok(Object::PE(pe)) => pe::build(path, bytes, pe),
Ok(Object::Elf(elf)) => elf::build(path, bytes, elf),
Ok(Object::Mach(mach)) => macho::build(path, bytes, mach)?,
Ok(Object::Archive(_)) => bail!("archive files are listed, not analyzed (try `knife ls`)"),
_ => bail!("unrecognized format: not PE, ELF, or Mach-O"),
};
bin.overall_entropy = entropy(bytes);
detect_overlay(&mut bin, bytes.len() as u64, bytes);
Ok(bin)
}
fn detect_overlay(bin: &mut Binary, file_len: u64, bytes: &[u8]) {
let end = bin
.sections
.iter()
.filter(|s| s.file_size > 0)
.map(|s| s.file_off + s.file_size)
.max()
.unwrap_or(0);
let mut effective_end = file_len;
if let Some((coff, csize)) = bin.sig_region {
if coff >= end && coff + csize >= file_len.saturating_sub(8) {
effective_end = coff;
}
}
if end > 0 && end < effective_end {
let off = end as usize;
let stop = effective_end as usize;
bin.overlay_off = Some(end);
bin.overlay_size = effective_end - end;
bin.overlay_entropy = entropy(&bytes[off..stop]);
}
}
pub(crate) fn section_entropy(bytes: &[u8], off: u64, size: u64) -> f64 {
let off = off as usize;
if off >= bytes.len() || size == 0 {
return 0.0;
}
let end = (off + size as usize).min(bytes.len());
entropy(&bytes[off..end])
}
pub fn list_archive(bytes: &[u8]) -> Result<Vec<(String, u64)>> {
match Object::parse(bytes) {
Ok(Object::Archive(ar)) => Ok(ar
.members()
.into_iter()
.map(|m| {
let size = ar.get(m).map(|e| e.size() as u64).unwrap_or(0);
(m.to_string(), size)
})
.collect()),
_ => bail!("not an archive"),
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn mk_section(
name: impl Into<String>,
vaddr: u64,
vsize: u64,
file_off: u64,
file_size: u64,
read: bool,
write: bool,
exec: bool,
bytes: &[u8],
) -> Section {
Section {
name: name.into(),
vaddr,
vsize,
file_off,
file_size,
entropy: section_entropy(bytes, file_off, file_size),
read,
write,
exec,
}
}