use std::mem;
const AR_MAGIC: &[u8] = b"!<arch>\n";
const ELF_MAGIC: &[u8] = &[0x7f, 0x45, 0x4c, 0x46]; const MACHO_MAGIC: &[u8] = &[0xcf, 0xfa, 0xed, 0xfe];
const MACHO_CIGAM: &[u8] = &[0xfe, 0xed, 0xfa, 0xcf];
const MACHO_FAT_MAGIC: &[u8] = &[0xca, 0xfe, 0xba, 0xbe];
const MACHO_FAT_CIGAM: &[u8] = &[0xbe, 0xba, 0xfe, 0xca];
const PE_OFFSET: usize = 0x3c;
type PeOffset = u32;
const PE_OFFSET_SIZE: usize = mem::size_of::<PeOffset>();
const PE_MAGIC: &[u8] = &[0x50, 0x45, 0x00, 0x00];
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum BinaryFormat {
Ar,
Elf,
MachO,
Pe,
}
impl BinaryFormat {
pub(crate) fn name(&self) -> &'static str {
match *self {
BinaryFormat::Ar => "AR",
BinaryFormat::Elf => "ELF",
BinaryFormat::MachO => "Mach-O",
BinaryFormat::Pe => "PE",
}
}
#[allow(clippy::match_like_matches_macro)]
pub(crate) fn is_executable(&self) -> bool {
match *self {
BinaryFormat::Ar => false,
_ => true,
}
}
}
fn detect_pe(file: &[u8]) -> bool {
if file.len() < PE_OFFSET + PE_OFFSET_SIZE {
return false;
}
let mut offset = [0; PE_OFFSET_SIZE];
offset.copy_from_slice(&file[PE_OFFSET..][..PE_OFFSET_SIZE]);
let offset_le = PeOffset::from_le_bytes(offset);
let offset_be = offset_le.swap_bytes();
let offset_le_sz = offset_le as usize;
let offset_be_sz = offset_be as usize;
let magic_at_offset = |offset| offset < file.len() && file[offset..].starts_with(PE_MAGIC);
magic_at_offset(offset_le_sz) || magic_at_offset(offset_be_sz)
}
pub(crate) fn detect_binary_format<C>(content: C) -> Option<BinaryFormat>
where
C: AsRef<[u8]>,
{
detect_binary_format_impl(content.as_ref())
}
fn detect_binary_format_impl(content: &[u8]) -> Option<BinaryFormat> {
if content.starts_with(ELF_MAGIC) {
Some(BinaryFormat::Elf)
} else if content.starts_with(AR_MAGIC) {
Some(BinaryFormat::Ar)
} else if content.starts_with(MACHO_MAGIC)
|| content.starts_with(MACHO_CIGAM)
|| content.starts_with(MACHO_FAT_MAGIC)
|| content.starts_with(MACHO_FAT_CIGAM)
{
Some(BinaryFormat::MachO)
} else if detect_pe(content) {
Some(BinaryFormat::Pe)
} else {
None
}
}
#[cfg(test)]
mod tests {
use crate::binary_format::{detect_binary_format, BinaryFormat};
#[test]
fn test_detect_binary_format_elf() {
assert_eq!(detect_binary_format(b"\x7fELF"), Some(BinaryFormat::Elf));
}
#[test]
fn test_detect_binary_format_ar() {
assert_eq!(detect_binary_format(b"!<arch>\n"), Some(BinaryFormat::Ar));
}
#[test]
fn test_detect_binary_format_macho() {
assert_eq!(
detect_binary_format(b"\xcf\xfa\xed\xfe"),
Some(BinaryFormat::MachO),
);
assert_eq!(
detect_binary_format(b"\xfe\xed\xfa\xcf"),
Some(BinaryFormat::MachO),
);
assert_eq!(
detect_binary_format(b"\xca\xfe\xba\xbe"),
Some(BinaryFormat::MachO),
);
assert_eq!(
detect_binary_format(b"\xbe\xba\xfe\xca"),
Some(BinaryFormat::MachO),
);
}
#[test]
fn test_detect_pe() {
use super::{detect_pe, PE_OFFSET, PE_OFFSET_SIZE};
assert!(!detect_pe(&[]));
assert!(!detect_pe(&[0; PE_OFFSET]));
assert!(!detect_pe(&[0; PE_OFFSET + PE_OFFSET_SIZE - 1]));
assert!(!detect_pe(&[0; PE_OFFSET + PE_OFFSET_SIZE]));
}
#[test]
fn test_detect_binary_format_pe() {
let pe_craft_be =
b"buffer_PE\x00\x000123456789012345678901234567890123456789012345678\x00\x00\x00\x07";
let pe_craft_le =
b"buffer_PE\x00\x000123456789012345678901234567890123456789012345678\x07\x00\x00\x00";
assert_eq!(detect_binary_format(pe_craft_be), Some(BinaryFormat::Pe));
assert_eq!(detect_binary_format(pe_craft_le), Some(BinaryFormat::Pe));
}
}