#![allow(clippy::match_bool)]
use crate::check::kernel::KernelChecker;
use crate::check::{Checker, FeatureCheck};
use crate::errors::{BinError, BinResult, ErrorKind};
use crate::format::BinFormat;
use crate::rule_engine::YaraExecutor;
use goblin::mach::Mach::{Binary, Fat};
use goblin::Object;
use serde::{Deserialize, Serialize};
use std::boxed::Box;
use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;
#[derive(Serialize, Deserialize)]
pub struct Detector {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bin_info: Option<Box<dyn FeatureCheck>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub harden_features: Option<Box<dyn FeatureCheck>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel_features: Option<Box<dyn FeatureCheck>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rule_features: Option<Box<dyn FeatureCheck>>,
}
impl Detector {
pub fn detect(
path: PathBuf,
basic_info: bool,
harden: bool,
kernel: bool,
rules: bool,
) -> BinResult<Self> {
let buffer = fs::read(path.as_path())?;
let mut bin_info: Option<Box<dyn FeatureCheck>> = None;
let harden_features: Option<Box<dyn FeatureCheck>> = match harden {
true => match Object::parse(&buffer)? {
Object::Elf(elf) => {
bin_info = match basic_info {
true => Some(elf.bin_info()),
false => None,
};
Some(elf.harden_check())
}
Object::PE(pe) => {
bin_info = match basic_info {
true => Some(pe.bin_info()),
false => None,
};
Some(pe.harden_check())
}
Object::Mach(_mach) => match _mach {
Binary(mach) => {
bin_info = match basic_info {
true => Some(mach.bin_info()),
false => None,
};
Some(mach.harden_check())
}
Fat(_) => {
return Err(BinError {
kind: ErrorKind::BinaryError,
msg: "does not support multiarch FAT binary containers yet".to_string(),
});
}
},
_ => {
return Err(BinError {
kind: ErrorKind::BinaryError,
msg: "unsupported filetype for analysis".to_string(),
});
}
},
false => None,
};
let kernel_features: Option<Box<dyn FeatureCheck>> = match kernel {
true => Some(KernelChecker::detect()?),
false => None,
};
let rule_features: Option<Box<dyn FeatureCheck>> = match rules {
true => {
let mut rule_exec: YaraExecutor = YaraExecutor::new(path);
let paths = fs::read_dir("rules")?;
for _path in paths {
let path: PathBuf = _path?.path();
let path_ext: Option<&str> = path.as_path().extension().and_then(OsStr::to_str);
if path_ext != Some("yara") {
continue;
}
rule_exec.add_rule(path)?;
}
rule_exec.execute()?;
Some(Box::new(rule_exec.matches))
}
false => None,
};
Ok(Self {
bin_info,
kernel_features,
rule_features,
harden_features,
})
}
pub fn output(self, format: &BinFormat) -> BinResult<String> {
format.dump(self)
}
}