use self::checked_functions::function_is_checked_version;
use self::needed_libc::NeededLibC;
use crate::security::{
options::{
status::{ASLRCompatibilityLevel, HasSecurityStatus},
AddressSpaceLayoutRandomizationOption, BinarySecurityOption, ELFFortifySourceOption,
ELFImmediateBindingOption, ELFReadOnlyAfterRelocationsOption, ELFStackProtectionOption,
},
parser::BinaryParser,
Result,
};
use std::collections::HashSet;
pub(crate) mod checked_functions;
pub(crate) mod needed_libc;
pub(crate) fn analyze_binary(
parser: &BinaryParser,
options: &crate::BinarySecurityCheckOptions,
) -> Result<Vec<Box<dyn HasSecurityStatus>>> {
let supports_address_space_layout_randomization =
AddressSpaceLayoutRandomizationOption.check(parser, options)?;
let has_stack_protection = ELFStackProtectionOption.check(parser, options)?;
let read_only_after_reloc = ELFReadOnlyAfterRelocationsOption.check(parser, options)?;
let immediate_bind = ELFImmediateBindingOption.check(parser, options)?;
let mut result = vec![
supports_address_space_layout_randomization,
has_stack_protection,
read_only_after_reloc,
immediate_bind,
];
if !options.no_libc {
if let Ok(fortify_source) =
ELFFortifySourceOption::new(options.libc_spec).check(parser, options)
{
result.push(fortify_source);
}
}
Ok(result)
}
pub(crate) fn get_libc_functions_by_protection<'t>(
elf: &goblin::elf::Elf,
libc_ref: &'t NeededLibC,
) -> (HashSet<&'t str>, HashSet<&'t str>) {
let imported_functions = elf
.dynsyms
.iter()
.filter_map(|symbol| dynamic_symbol_is_named_imported_function(elf, &symbol));
let mut protected_functions = HashSet::<&str>::default();
let mut unprotected_functions = HashSet::<&str>::default();
for imported_function in imported_functions {
if function_is_checked_version(imported_function) {
if let Some(unchecked_function) = libc_ref.exports_function(imported_function) {
protected_functions.insert(unchecked_function);
} else {
}
} else if let Some(unchecked_function) =
libc_ref.exports_checked_version_of_function(imported_function)
{
unprotected_functions.insert(unchecked_function);
}
}
(protected_functions, unprotected_functions)
}
pub(crate) fn supports_aslr(elf: &goblin::elf::Elf) -> ASLRCompatibilityLevel {
match elf.header.e_type {
goblin::elf::header::ET_EXEC => {
ASLRCompatibilityLevel::Unsupported
}
goblin::elf::header::ET_DYN => {
ASLRCompatibilityLevel::Supported
}
_ => {
ASLRCompatibilityLevel::Unknown
}
}
}
pub(crate) fn becomes_read_only_after_relocations(elf: &goblin::elf::Elf) -> bool {
let r = elf
.program_headers
.iter()
.any(|ph| ph.p_type == goblin::elf::program_header::PT_GNU_RELRO);
if r {
}
r
}
pub(crate) fn has_stack_protection(elf: &goblin::elf::Elf) -> bool {
let r = elf
.dynsyms
.iter()
.filter_map(|symbol| dynamic_symbol_is_named_function(elf, &symbol))
.any(|name| name == "__stack_chk_fail");
if r {
}
r
}
const STV_DEFAULT: u8 = 0;
pub(crate) fn dynamic_symbol_is_named_exported_function<'elf>(
elf: &'elf goblin::elf::Elf,
symbol: &goblin::elf::sym::Sym,
) -> Option<&'elf str> {
if symbol.st_other == STV_DEFAULT {
let st_type = symbol.st_type();
if st_type == goblin::elf::sym::STT_FUNC || st_type == goblin::elf::sym::STT_GNU_IFUNC {
let st_bind = symbol.st_bind();
if (st_bind == goblin::elf::sym::STB_GLOBAL
|| st_bind == goblin::elf::sym::STB_WEAK
|| st_bind == goblin::elf::sym::STB_GNU_UNIQUE)
&& (symbol.st_value != 0)
{
return elf
.dynstrtab
.get_at(symbol.st_name)
.filter(|name| !name.is_empty()); }
}
}
None
}
pub(crate) const _DF_1_PIE: u64 = 0x08_00_00_00;
pub(crate) fn _symbol_is_named_function_or_unspecified<'elf>(
elf: &'elf goblin::elf::Elf,
symbol: &goblin::elf::sym::Sym,
) -> Option<&'elf str> {
let st_type = symbol.st_type();
if st_type == goblin::elf::sym::STT_FUNC
|| st_type == goblin::elf::sym::STT_GNU_IFUNC
|| st_type == goblin::elf::sym::STT_NOTYPE
{
elf.strtab
.get_at(symbol.st_name)
.filter(|name| !name.is_empty()) } else {
None
}
}
fn dynamic_symbol_is_named_function<'elf>(
elf: &'elf goblin::elf::Elf,
symbol: &goblin::elf::sym::Sym,
) -> Option<&'elf str> {
let st_type = symbol.st_type();
if st_type == goblin::elf::sym::STT_FUNC || st_type == goblin::elf::sym::STT_GNU_IFUNC {
elf.dynstrtab
.get_at(symbol.st_name)
.filter(|name| !name.is_empty()) } else {
None
}
}
fn dynamic_symbol_is_named_imported_function<'elf>(
elf: &'elf goblin::elf::Elf,
symbol: &goblin::elf::sym::Sym,
) -> Option<&'elf str> {
let st_type = symbol.st_type();
if st_type == goblin::elf::sym::STT_FUNC || st_type == goblin::elf::sym::STT_GNU_IFUNC {
let st_bind = symbol.st_bind();
if (st_bind == goblin::elf::sym::STB_GLOBAL
|| st_bind == goblin::elf::sym::STB_WEAK
|| st_bind == goblin::elf::sym::STB_GNU_UNIQUE)
&& (symbol.st_value == 0)
{
return elf
.dynstrtab
.get_at(symbol.st_name)
.filter(|name| !name.is_empty()); }
}
None
}
pub(crate) fn requires_immediate_binding(elf: &goblin::elf::Elf) -> bool {
elf.dynamic
.as_ref()
.and_then(|dli| {
dli.dyns
.iter()
.find(|dyn_entry| dynamic_linking_info_entry_requires_immediate_binding(dyn_entry))
})
.is_some()
}
fn dynamic_linking_info_entry_requires_immediate_binding(
dyn_entry: &goblin::elf::dynamic::Dyn,
) -> bool {
match dyn_entry.d_tag {
goblin::elf::dynamic::DT_BIND_NOW => {
true
}
goblin::elf::dynamic::DT_FLAGS => {
let r = (dyn_entry.d_val & goblin::elf::dynamic::DF_BIND_NOW) != 0;
if r {
}
r
}
goblin::elf::dynamic::DT_FLAGS_1 => {
let r = (dyn_entry.d_val & goblin::elf::dynamic::DF_1_NOW) != 0;
if r {
}
r
}
_ => false,
}
}