rbat 1.0.0

A terminal-native binary analysis tool for security researchers and reverse engineers.
Documentation
//! # Binary Header Metadata Extractor
//!
//! This module extracts target platform details, machine architecture codes, and entry point
//! addresses from a parsed [`Object`] representation of ELF, PE, and Mach-O files.

use crate::core::{BinaryMetadata, RbatError, Result};
use goblin::Object;

/// Extracts high-level metadata (architecture type, entry address, name) from a parsed goblin object.
///
/// # Errors
/// Returns `RbatError::UnsupportedBinaryFormat` if the parsed magic does not map to ELF, PE, or Mach-O.
pub fn get_binary_metadata(binary_object: &Object) -> Result<BinaryMetadata> {
    match &binary_object {
        Object::Elf(elf) => Ok(BinaryMetadata {
            binary_type: "Linux ELF".to_string(),
            entry_point: elf.entry,
            architecture: elf.header.e_machine,
        }),
        Object::PE(pe) => Ok(BinaryMetadata {
            binary_type: "Windows PE".to_string(),
            entry_point: pe.entry as u64,
            architecture: pe.header.coff_header.machine,
        }),
        Object::Mach(mach) => match mach {
            goblin::mach::Mach::Binary(macho) => Ok(BinaryMetadata {
                binary_type: "Mach-O".to_string(),
                entry_point: macho.entry,
                architecture: (macho.header.cputype & 0xFFFF) as u16,
            }),
            goblin::mach::Mach::Fat(fat) => {
                for arch in fat {
                    if let Ok(goblin::mach::SingleArch::MachO(macho)) = arch {
                        return Ok(BinaryMetadata {
                            binary_type: "Mach-O (Fat)".to_string(),
                            entry_point: macho.entry,
                            architecture: (macho.header.cputype & 0xFFFF) as u16,
                        });
                    }
                }
                Err(RbatError::UnsupportedBinaryFormat(
                    "Mach-O fat binary did not contain a parseable Mach-O architecture".to_string(),
                ))
            }
        },
        Object::Archive(_) => Err(RbatError::UnsupportedBinaryFormat(
            "Archive metadata parsing is not supported".to_string(),
        )),
        Object::Unknown(magic) => Err(RbatError::UnsupportedBinaryFormat(format!(
            "Unknown file format magic: {magic:#x}"
        ))),
        _ => Err(RbatError::UnsupportedBinaryFormat(
            "Unsupported file type for metadata extraction".to_string(),
        )),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::utils::test_helpers::test_helpers;
    use std::fs;
    use tempfile::tempdir;

    #[test]
    fn test_get_binary_metadata_elf() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("mock_elf");
        test_helpers::generate_elf(&path);

        let buffer = fs::read(&path).unwrap();
        let obj = Object::parse(&buffer).unwrap();

        let meta = get_binary_metadata(&obj).unwrap();
        assert_eq!(meta.binary_type, "Linux ELF");
        assert_eq!(meta.architecture, goblin::elf::header::EM_X86_64);
    }

    #[test]
    fn test_get_binary_metadata_pe() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("mock_pe");
        test_helpers::generate_pe_stub(&path);

        let buffer = fs::read(&path).unwrap();
        let obj = Object::parse(&buffer).unwrap();

        let meta = get_binary_metadata(&obj).unwrap();
        assert_eq!(meta.binary_type, "Windows PE");
        assert_eq!(meta.architecture, 0x8664);
    }

    #[test]
    fn test_get_binary_metadata_macho() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("mock_macho");
        test_helpers::generate_macho(&path);

        let buffer = fs::read(&path).unwrap();
        let obj = Object::parse(&buffer).unwrap();

        let meta = get_binary_metadata(&obj).unwrap();
        assert_eq!(meta.binary_type, "Mach-O");
        assert_eq!(
            meta.architecture,
            (goblin::mach::constants::cputype::CPU_TYPE_X86_64 & 0xFFFF) as u16
        );
    }

    #[test]
    fn test_get_binary_metadata_unknown() {
        let obj = Object::Unknown(0x44434241);
        let result = get_binary_metadata(&obj);
        assert!(result.is_err());
        match result {
            Err(RbatError::UnsupportedBinaryFormat(_)) => {}
            _ => panic!("Expected UnsupportedBinaryFormat error"),
        }
    }
}