pub(crate) mod status;
use crate::elf::needed_libc::{LibCResolver, NeededLibC};
use crate::errors::Result;
use crate::parser::BinaryParser;
use crate::{archive, cmdline, elf, pe};
use self::status::{
DisplayInColorTerm, ELFFortifySourceStatus, PEControlFlowGuardLevel, YesNoUnknownStatus,
};
pub(crate) trait BinarySecurityOption<'t> {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>>;
}
struct PEDllCharacteristicsBitOption {
name: &'static str,
mask_name: &'static str,
mask: u16,
present: bool,
}
impl BinarySecurityOption<'_> for PEDllCharacteristicsBitOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
if let goblin::Object::PE(pe) = parser.object()
&& let Some(bit_is_set) =
pe::dll_characteristics_bit_is_set(pe, self.mask_name, self.mask)
{
return Ok(Box::new(YesNoUnknownStatus::new(
self.name,
bit_is_set == self.present,
true,
)));
}
Ok(Box::new(YesNoUnknownStatus::unknown(self.name, true)))
}
}
#[derive(Default)]
pub(crate) struct PEHasCheckSumOption;
impl BinarySecurityOption<'_> for PEHasCheckSumOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::PE(pe) = parser.object() {
pe::has_check_sum(pe)
} else {
None
};
Ok(Box::new(r.map_or_else(
|| YesNoUnknownStatus::unknown("CHECKSUM", true),
|r| YesNoUnknownStatus::new("CHECKSUM", r, true),
)))
}
}
#[derive(Default)]
pub(crate) struct DataExecutionPreventionOption;
impl BinarySecurityOption<'_> for DataExecutionPreventionOption {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
if let goblin::Object::PE(_pe) = parser.object() {
PEDllCharacteristicsBitOption {
name: "DATA-EXEC-PREVENT",
mask_name: "IMAGE_DLLCHARACTERISTICS_NX_COMPAT",
mask: pe::IMAGE_DLLCHARACTERISTICS_NX_COMPAT,
present: true,
}
.check(parser, options)
} else {
Ok(Box::new(YesNoUnknownStatus::unknown(
"DATA-EXEC-PREVENT",
true,
)))
}
}
}
#[derive(Default)]
pub(crate) struct PERunsOnlyInAppContainerOption;
impl BinarySecurityOption<'_> for PERunsOnlyInAppContainerOption {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
PEDllCharacteristicsBitOption {
name: "RUNS-IN-APP-CONTAINER",
mask_name: "IMAGE_DLLCHARACTERISTICS_APPCONTAINER",
mask: pe::IMAGE_DLLCHARACTERISTICS_APPCONTAINER,
present: true,
}
.check(parser, options)
}
}
#[derive(Default)]
pub(crate) struct RequiresIntegrityCheckOption;
impl BinarySecurityOption<'_> for RequiresIntegrityCheckOption {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
if let goblin::Object::PE(_pe) = parser.object() {
PEDllCharacteristicsBitOption {
name: "VERIFY-DIGITAL-CERT",
mask_name: "IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY",
mask: pe::IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY,
present: true,
}
.check(parser, options)
} else {
Ok(Box::new(YesNoUnknownStatus::unknown(
"VERIFY-DIGITAL-CERT",
true,
)))
}
}
}
#[derive(Default)]
pub(crate) struct PEEnableManifestHandlingOption;
impl BinarySecurityOption<'_> for PEEnableManifestHandlingOption {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
PEDllCharacteristicsBitOption {
name: "CONSIDER-MANIFEST",
mask_name: "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION",
mask: pe::IMAGE_DLLCHARACTERISTICS_NO_ISOLATION,
present: false,
}
.check(parser, options)
}
}
#[derive(Default)]
pub(crate) struct PEControlFlowGuardOption;
impl BinarySecurityOption<'_> for PEControlFlowGuardOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::PE(pe) = parser.object() {
pe::supports_control_flow_guard(pe)
} else {
PEControlFlowGuardLevel::Unknown
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct PEHandlesAddressesLargerThan2GBOption;
impl BinarySecurityOption<'_> for PEHandlesAddressesLargerThan2GBOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::PE(pe) = parser.object() {
YesNoUnknownStatus::new(
"HANDLES-ADDR-GT-2GB",
pe::handles_addresses_larger_than_2_gigabytes(pe),
true,
)
} else {
YesNoUnknownStatus::unknown("HANDLES-ADDR-GT-2GB", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct AddressSpaceLayoutRandomizationOption;
impl BinarySecurityOption<'_> for AddressSpaceLayoutRandomizationOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
match parser.object() {
goblin::Object::PE(pe) => Ok(Box::new(pe::supports_aslr(pe))),
goblin::Object::Elf(elf_obj) => Ok(Box::new(elf::supports_aslr(elf_obj))),
_ => Ok(Box::new(YesNoUnknownStatus::unknown("ASLR", true))),
}
}
}
#[derive(Default)]
pub(crate) struct PESafeStructuredExceptionHandlingOption;
impl BinarySecurityOption<'_> for PESafeStructuredExceptionHandlingOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::PE(pe) = parser.object() {
YesNoUnknownStatus::new(
"SAFE-SEH",
pe::has_safe_structured_exception_handlers(parser, pe),
true,
)
} else {
YesNoUnknownStatus::unknown("SAFE-SEH", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFStackMustBeExecutableOption;
impl BinarySecurityOption<'_> for ELFStackMustBeExecutableOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
YesNoUnknownStatus::new("EXEC-STACK", elf::stack_must_be_executable(elf), false)
} else {
YesNoUnknownStatus::unknown("EXEC-STACK", false)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFSupportsShadowStackOption;
impl BinarySecurityOption<'_> for ELFSupportsShadowStackOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
let predicate = elf::supports_shadow_stack(elf, parser.bytes())?;
YesNoUnknownStatus::new("SHADOW-STACK", predicate, true)
} else {
YesNoUnknownStatus::unknown("SHADOW-STACK", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFBranchTargetIdentificationOption;
impl BinarySecurityOption<'_> for ELFBranchTargetIdentificationOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
let predicate = elf::branch_target_identification(elf, parser.bytes())?;
YesNoUnknownStatus::new("BRANCH-TARGET-ID", predicate, true)
} else {
YesNoUnknownStatus::unknown("BRANCH-TARGET-ID", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFGuardedControlStackOption;
impl BinarySecurityOption<'_> for ELFGuardedControlStackOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
let predicate = elf::guarded_control_stack(elf, parser.bytes())?;
YesNoUnknownStatus::new("GUARDED-CTRL-STACK", predicate, true)
} else {
YesNoUnknownStatus::unknown("GUARDED-CTRL-STACK", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFStackProtectionOption;
impl BinarySecurityOption<'_> for ELFStackProtectionOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = match parser.object() {
goblin::Object::Elf(elf_obj) => {
YesNoUnknownStatus::new("STACK-PROT", elf::has_stack_protection(elf_obj), true)
}
goblin::Object::Archive(archive) => {
let r = archive::has_stack_protection(parser, archive)?;
YesNoUnknownStatus::new("STACK-PROT", r, true)
}
_ => YesNoUnknownStatus::unknown("STACK-PROT", true),
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFReadOnlyAfterRelocationsOption;
impl BinarySecurityOption<'_> for ELFReadOnlyAfterRelocationsOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
YesNoUnknownStatus::new(
"READ-ONLY-RELOC",
elf::becomes_read_only_after_relocations(elf),
true,
)
} else {
YesNoUnknownStatus::unknown("READ-ONLY-RELOC", true)
};
Ok(Box::new(r))
}
}
#[derive(Default)]
pub(crate) struct ELFImmediateBindingOption;
impl BinarySecurityOption<'_> for ELFImmediateBindingOption {
fn check(
&self,
parser: &BinaryParser,
_options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
let r = if let goblin::Object::Elf(elf) = parser.object() {
YesNoUnknownStatus::new("IMMEDIATE-BIND", elf::requires_immediate_binding(elf), true)
} else {
YesNoUnknownStatus::unknown("IMMEDIATE-BIND", true)
};
Ok(Box::new(r))
}
}
pub(crate) struct ELFFortifySourceOption {
libc_spec: Option<cmdline::LibCSpec>,
}
impl ELFFortifySourceOption {
pub(crate) fn new(libc_spec: Option<cmdline::LibCSpec>) -> Self {
Self { libc_spec }
}
}
impl BinarySecurityOption<'_> for ELFFortifySourceOption {
fn check(
&self,
parser: &BinaryParser,
options: &crate::cmdline::Options,
) -> Result<Box<dyn DisplayInColorTerm>> {
if let goblin::Object::Elf(elf) = parser.object() {
let libc = if let Some(spec) = self.libc_spec {
NeededLibC::from_spec(spec)
} else if let Some(path) = &options.libc {
NeededLibC::open_elf_for_architecture(path, elf)?
} else {
LibCResolver::get(options)?.find_needed_by_executable(elf)?
};
let result = ELFFortifySourceStatus::new(libc, elf)?;
Ok(Box::new(result))
} else {
Ok(Box::new(YesNoUnknownStatus::unknown(
"FORTIFY-SOURCE",
true,
)))
}
}
}