#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
pub mod doc;
pub mod exec;
pub mod utils;
use crate::exec::ExecutableFile;
use std::fmt::{Display, Formatter};
use crate::doc::DocumentFile;
use anyhow::Result;
use chrono::{DateTime, Utc};
use tracing::instrument;
pub const MDB_VERSION: &str = env!("CARGO_PKG_VERSION");
pub trait SpecimenFile {
const MAGIC: &'static [&'static [u8]];
fn type_name(&self) -> &'static str;
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
pub enum KnownType<'a> {
#[cfg(feature = "elf")]
ELF(exec::elf::Elf<'a>),
#[cfg(feature = "pe32")]
EXE(exec::pe32::EXE<'a>),
#[cfg(feature = "macho")]
MachO(exec::macho::Macho<'a>),
#[cfg(feature = "macho")]
FatMachO(exec::macho::fat::FatMacho<'a>),
#[cfg(feature = "pef")]
PEF(exec::pef::Pef<'a>),
#[cfg(feature = "office95")]
Office95(doc::office95::Office95<'a>),
#[cfg(feature = "pdf")]
PDF(doc::pdf::PDF<'a>),
#[cfg(feature = "rtf")]
RTF(doc::rtf::Rtf<'a>),
Unknown(&'a [u8]),
}
impl<'a> KnownType<'a> {
#[instrument]
pub fn new(data: &'a [u8]) -> Result<Self> {
#[cfg(feature = "elf")]
if data.starts_with(exec::elf::Elf::MAGIC[0]) {
return Ok(Self::ELF(exec::elf::Elf::from(data)?));
}
#[cfg(feature = "pe32")]
if data.starts_with(exec::pe32::EXE::MAGIC[0])
|| data.starts_with(exec::pe32::EXE::MAGIC[1])
{
return Ok(Self::EXE(exec::pe32::EXE::from(data)?));
}
#[cfg(feature = "macho")]
for mach_magic in exec::macho::Macho::MAGIC {
if data.starts_with(mach_magic) {
return Ok(Self::MachO(exec::macho::Macho::from(data)?));
}
}
#[cfg(feature = "macho")]
for mach_magic in exec::macho::fat::FatMacho::MAGIC {
if data.starts_with(mach_magic) {
return Ok(Self::FatMachO(exec::macho::fat::FatMacho::from(data)?));
}
}
#[cfg(feature = "office95")]
if data.starts_with(doc::office95::Office95::MAGIC[0]) {
return Ok(Self::Office95(doc::office95::Office95::from(data)?));
}
#[cfg(feature = "pdf")]
if data.starts_with(doc::pdf::PDF::MAGIC[0]) {
return Ok(Self::PDF(doc::pdf::PDF::from(data)?));
}
#[cfg(feature = "rtf")]
if data.starts_with(doc::rtf::Rtf::MAGIC[0]) {
return Ok(Self::RTF(doc::rtf::Rtf::from(data)?));
}
#[cfg(feature = "pef")]
if data.starts_with(exec::pef::Pef::MAGIC[0]) {
return Ok(Self::PEF(exec::pef::Pef::from(data)?));
}
Ok(Self::Unknown(data))
}
pub fn is_exec(&self) -> bool {
match self {
#[cfg(feature = "elf")]
KnownType::ELF(_) => true,
#[cfg(feature = "pe32")]
KnownType::EXE(_) => true,
#[cfg(feature = "macho")]
KnownType::MachO(_) => true,
#[cfg(feature = "macho")]
KnownType::FatMachO(_) => true,
#[cfg(feature = "pef")]
KnownType::PEF(_) => true,
_ => false,
}
}
pub fn is_doc(&self) -> bool {
match self {
#[cfg(feature = "pdf")]
KnownType::PDF(_) => true,
#[cfg(feature = "rtf")]
KnownType::RTF(_) => true,
#[cfg(feature = "office95")]
KnownType::Office95(_) => true,
_ => false,
}
}
pub fn created(&self) -> Option<DateTime<Utc>> {
match self {
#[cfg(feature = "pe32")]
KnownType::EXE(e) => e.compiled_timestamp(),
#[cfg(feature = "pef")]
KnownType::PEF(p) => p.compiled_timestamp(),
#[cfg(feature = "pdf")]
KnownType::PDF(p) => p.creation_date,
_ => None,
}
}
pub fn exec(self) -> Option<Box<dyn ExecutableFile + Send + 'a>> {
match self {
#[cfg(feature = "elf")]
KnownType::ELF(e) => Some(Box::new(e)),
#[cfg(feature = "pe32")]
KnownType::EXE(e) => Some(Box::new(e)),
#[cfg(feature = "macho")]
KnownType::MachO(m) => Some(Box::new(m)),
#[cfg(feature = "macho")]
KnownType::FatMachO(m) => Some(Box::new(m)),
#[cfg(feature = "pef")]
KnownType::PEF(p) => Some(Box::new(p)),
_ => None,
}
}
pub fn children(&self) -> Option<Vec<KnownType>> {
match self {
#[cfg(feature = "macho")]
KnownType::FatMachO(m) => Some(
m.binaries
.iter()
.map(|b| KnownType::MachO(b.clone()))
.collect(),
),
_ => None,
}
}
pub fn contents(&self) -> &'a [u8] {
match self {
#[cfg(feature = "elf")]
KnownType::ELF(e) => e.contents,
#[cfg(feature = "pe32")]
KnownType::EXE(e) => e.contents,
#[cfg(feature = "macho")]
KnownType::MachO(m) => m.contents,
#[cfg(feature = "macho")]
KnownType::FatMachO(m) => m.contents,
#[cfg(feature = "pef")]
KnownType::PEF(p) => p.contents,
#[cfg(feature = "office95")]
KnownType::Office95(p) => p.contents,
#[cfg(feature = "pdf")]
KnownType::PDF(p) => p.contents,
#[cfg(feature = "rtf")]
KnownType::RTF(r) => r.contents,
KnownType::Unknown(u) => u,
}
}
pub fn doc(self) -> Option<Box<dyn DocumentFile + Send + 'a>> {
match self {
#[cfg(feature = "office95")]
KnownType::Office95(o) => Some(Box::new(o)),
#[cfg(feature = "pdf")]
KnownType::PDF(p) => Some(Box::new(p)),
#[cfg(feature = "rtf")]
KnownType::RTF(r) => Some(Box::new(r)),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Ordering {
BigEndian,
LittleEndian,
BiEndian,
}
impl Display for Ordering {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Ordering::BigEndian => write!(f, "Big Endian"),
Ordering::LittleEndian => write!(f, "Little Endian"),
Ordering::BiEndian => write!(f, "Bi-Endian"),
}
}
}