use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86BinaryFormatType {
Unknown,
ELF,
PECOFF,
MachO,
}
impl std::fmt::Display for X86BinaryFormatType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => write!(f, "unknown"),
Self::ELF => write!(f, "ELF"),
Self::PECOFF => write!(f, "PE/COFF"),
Self::MachO => write!(f, "Mach-O"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86BinaryArch {
Unknown,
I386, X86_64, }
impl std::fmt::Display for X86BinaryArch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => write!(f, "unknown"),
Self::I386 => write!(f, "i386"),
Self::X86_64 => write!(f, "x86_64"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86BinaryFormat {
pub format: X86BinaryFormatType,
pub arch: X86BinaryArch,
pub data: Vec<u8>,
pub is_64bit: bool,
pub endianness: String,
pub entry_point: u64,
pub base_address: u64,
pub image_size: u64,
pub section_count: u32,
pub os_abi: String,
pub properties: HashMap<String, String>,
}
impl X86BinaryFormat {
pub fn detect(data: Vec<u8>) -> Self {
let mut properties = HashMap::new();
if data.len() < 4 {
return Self {
format: X86BinaryFormatType::Unknown,
arch: X86BinaryArch::Unknown,
data,
is_64bit: false,
endianness: "little".to_string(),
entry_point: 0,
base_address: 0,
image_size: 0,
section_count: 0,
os_abi: "unknown".to_string(),
properties,
};
}
let magic = &data[0..4];
if magic[0] == 0x7F && magic[1] == b'E' && magic[2] == b'L' && magic[3] == b'F' {
let is_64bit = if data.len() > 4 { data[4] == 2 } else { false };
let arch = if data.len() > 18 {
let machine = u16::from_le_bytes([data[18], data[19]]);
match machine {
3 => X86BinaryArch::I386,
62 => X86BinaryArch::X86_64,
_ => X86BinaryArch::Unknown,
}
} else {
X86BinaryArch::Unknown
};
let os_abi = if data.len() > 7 {
match data[7] {
0x00 => "ELFOSABI_NONE / System V",
0x03 => "ELFOSABI_LINUX",
0x09 => "ELFOSABI_FREEBSD",
_ => "unknown",
}
} else {
"unknown"
};
let entry_point = if data.len() >= 24 + if is_64bit { 8 } else { 4 } {
if is_64bit {
u64::from_le_bytes([
data[24], data[25], data[26], data[27], data[28], data[29], data[30],
data[31],
])
} else {
u32::from_le_bytes([data[24], data[25], data[26], data[27]]) as u64
}
} else {
0
};
let ehdr_size = if is_64bit { 64u16 } else { 52u16 };
let shoff = if data.len() >= (if is_64bit { 40 } else { 32 }) + 8 {
if is_64bit {
u64::from_le_bytes([
data[40], data[41], data[42], data[43], data[44], data[45], data[46],
data[47],
])
} else {
u32::from_le_bytes([data[32], data[33], data[34], data[35]]) as u64
}
} else {
0
};
let shnum = if data.len() > (if is_64bit { 60 } else { 48 }) {
u16::from_le_bytes(if is_64bit {
[data[60], data[61]]
} else {
[data[48], data[49]]
})
} else {
0
};
properties.insert("ehdr_size".to_string(), ehdr_size.to_string());
properties.insert("shoff".to_string(), shoff.to_string());
properties.insert("shnum".to_string(), shnum.to_string());
return Self {
format: X86BinaryFormatType::ELF,
arch,
data,
is_64bit,
endianness: "little".to_string(),
entry_point,
base_address: 0,
image_size: 0,
section_count: shnum as u32,
os_abi: os_abi.to_string(),
properties,
};
}
if magic[0] == b'M' && magic[1] == b'Z' {
let pe_offset = if data.len() >= 0x3C + 4 {
u32::from_le_bytes([data[0x3C], data[0x3D], data[0x3E], data[0x3F]]) as usize
} else {
0
};
let mut arch = X86BinaryArch::Unknown;
let mut is_64bit = false;
let mut entry_point: u64 = 0;
let mut base_address: u64 = 0;
let mut image_size: u64 = 0;
let mut section_count: u32 = 0;
if pe_offset > 0 && data.len() > pe_offset + 4 {
let pe_sig = &data[pe_offset..pe_offset + 4];
if pe_sig[0] == b'P' && pe_sig[1] == b'E' && pe_sig[2] == 0x00 && pe_sig[3] == 0x00
{
let coff_start = pe_offset + 4;
if data.len() > coff_start + 20 {
let machine = u16::from_le_bytes([data[coff_start], data[coff_start + 1]]);
arch = match machine {
0x014C => X86BinaryArch::I386,
0x8664 => X86BinaryArch::X86_64,
_ => X86BinaryArch::Unknown,
};
section_count =
u16::from_le_bytes([data[coff_start + 2], data[coff_start + 3]]) as u32;
let size_of_optional =
u16::from_le_bytes([data[coff_start + 16], data[coff_start + 17]]);
let opt_start = coff_start + 20;
if data.len() > opt_start + 2 {
let magic_opt =
u16::from_le_bytes([data[opt_start], data[opt_start + 1]]);
is_64bit = magic_opt == 0x020B; if is_64bit && data.len() > opt_start + 24 {
entry_point = u64::from_le_bytes([
data[opt_start + 16],
data[opt_start + 17],
data[opt_start + 18],
data[opt_start + 19],
data[opt_start + 20],
data[opt_start + 21],
data[opt_start + 22],
data[opt_start + 23],
]);
base_address = u64::from_le_bytes([
data[opt_start + 24],
data[opt_start + 25],
data[opt_start + 26],
data[opt_start + 27],
data[opt_start + 28],
data[opt_start + 29],
data[opt_start + 30],
data[opt_start + 31],
]);
} else if !is_64bit && data.len() > opt_start + 20 {
entry_point = u32::from_le_bytes([
data[opt_start + 16],
data[opt_start + 17],
data[opt_start + 18],
data[opt_start + 19],
]) as u64;
base_address = u32::from_le_bytes([
data[opt_start + 28],
data[opt_start + 29],
data[opt_start + 30],
data[opt_start + 31],
]) as u64;
}
if data.len() > opt_start + 56 {
image_size = u32::from_le_bytes([
data[opt_start + 56],
data[opt_start + 57],
data[opt_start + 58],
data[opt_start + 59],
]) as u64;
}
}
}
}
}
properties.insert("pe_offset".to_string(), pe_offset.to_string());
properties.insert("is_pe32plus".to_string(), is_64bit.to_string());
return Self {
format: X86BinaryFormatType::PECOFF,
arch,
data,
is_64bit,
endianness: "little".to_string(),
entry_point,
base_address,
image_size,
section_count,
os_abi: "windows".to_string(),
properties,
};
}
let mach_magic = u32::from_le_bytes([magic[0], magic[1], magic[2], magic[3]]);
let mach_magic_be = u32::from_be_bytes([magic[0], magic[1], magic[2], magic[3]]);
let (is_macho, is_64bit, is_be) = match mach_magic {
0xFEEDFACE => (true, false, false), 0xFEEDFACF => (true, true, false), 0xCEFAEDFE => (true, false, true), 0xCFFAEDFE => (true, true, true), _ => match mach_magic_be {
0xFEEDFACE | 0xFEEDFACF | 0xCEFAEDFE | 0xCFFAEDFE => (
true,
mach_magic_be == 0xFEEDFACF || mach_magic_be == 0xCFFAEDFE,
true,
),
_ => (false, false, false),
},
};
if is_macho {
let cpu_offset = 4;
let arch = if data.len() > cpu_offset + 4 {
let cputype = if is_be {
u32::from_be_bytes([
data[cpu_offset],
data[cpu_offset + 1],
data[cpu_offset + 2],
data[cpu_offset + 3],
])
} else {
u32::from_le_bytes([
data[cpu_offset],
data[cpu_offset + 1],
data[cpu_offset + 2],
data[cpu_offset + 3],
])
};
match cputype {
7 => X86BinaryArch::I386, 0x01000007 => X86BinaryArch::X86_64, _ => X86BinaryArch::Unknown,
}
} else {
X86BinaryArch::Unknown
};
let ncmds = if data.len() > 20 {
if is_be {
u32::from_be_bytes([data[16], data[17], data[18], data[19]])
} else {
u32::from_le_bytes([data[16], data[17], data[18], data[19]])
}
} else {
0
};
let filetype_val = if data.len() > 16 {
if is_be {
u32::from_be_bytes([data[12], data[13], data[14], data[15]])
} else {
u32::from_le_bytes([data[12], data[13], data[14], data[15]])
}
} else {
0
};
properties.insert("ncmds".to_string(), ncmds.to_string());
properties.insert("filetype".to_string(), filetype_val.to_string());
return Self {
format: X86BinaryFormatType::MachO,
arch,
data,
is_64bit,
endianness: if is_be {
"big".to_string()
} else {
"little".to_string()
},
entry_point: 0,
base_address: 0,
image_size: 0,
section_count: 0,
os_abi: "darwin".to_string(),
properties,
};
}
Self {
format: X86BinaryFormatType::Unknown,
arch: X86BinaryArch::Unknown,
data,
is_64bit: false,
endianness: "little".to_string(),
entry_point: 0,
base_address: 0,
image_size: 0,
section_count: 0,
os_abi: "unknown".to_string(),
properties,
}
}
pub fn as_elf(&self) -> Option<X86ELFBinary> {
if self.format != X86BinaryFormatType::ELF {
return None;
}
Some(X86ELFBinary::read(&self.data, self.is_64bit))
}
pub fn as_pe(&self) -> Option<X86PEBinary> {
if self.format != X86BinaryFormatType::PECOFF {
return None;
}
Some(X86PEBinary::read(&self.data))
}
pub fn as_macho(&self) -> Option<X86MachOBinary> {
if self.format != X86BinaryFormatType::MachO {
return None;
}
Some(X86MachOBinary::read(&self.data))
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str(&format!("Format: {}\n", self.format));
s.push_str(&format!("Arch: {}\n", self.arch));
s.push_str(&format!("64-bit: {}\n", self.is_64bit));
s.push_str(&format!("Entry: 0x{:016X}\n", self.entry_point));
s.push_str(&format!("Base: 0x{:016X}\n", self.base_address));
s.push_str(&format!("Image size: {}\n", self.image_size));
s.push_str(&format!("Sections: {}\n", self.section_count));
s.push_str(&format!("OS/ABI: {}\n", self.os_abi));
s.push_str(&format!("File size: {} bytes\n", self.data.len()));
s
}
}
pub const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
pub const EI_MAG0: usize = 0;
pub const EI_MAG1: usize = 1;
pub const EI_MAG2: usize = 2;
pub const EI_MAG3: usize = 3;
pub const EI_CLASS: usize = 4;
pub const EI_DATA: usize = 5;
pub const EI_VERSION: usize = 6;
pub const EI_OSABI: usize = 7;
pub const EI_ABIVERSION: usize = 8;
pub const EI_PAD: usize = 9;
pub const EI_NIDENT: usize = 16;
pub const ELFCLASSNONE: u8 = 0;
pub const ELFCLASS32: u8 = 1;
pub const ELFCLASS64: u8 = 2;
pub const ELFDATANONE: u8 = 0;
pub const ELFDATA2LSB: u8 = 1;
pub const ELFDATA2MSB: u8 = 2;
pub const ELFOSABI_NONE: u8 = 0;
pub const ELFOSABI_HPUX: u8 = 1;
pub const ELFOSABI_NETBSD: u8 = 2;
pub const ELFOSABI_GNU: u8 = 3;
pub const ELFOSABI_LINUX: u8 = 3;
pub const ELFOSABI_SOLARIS: u8 = 6;
pub const ELFOSABI_AIX: u8 = 7;
pub const ELFOSABI_IRIX: u8 = 8;
pub const ELFOSABI_FREEBSD: u8 = 9;
pub const ELFOSABI_TRU64: u8 = 10;
pub const ELFOSABI_MODESTO: u8 = 11;
pub const ELFOSABI_OPENBSD: u8 = 12;
pub const ELFOSABI_OPENVMS: u8 = 13;
pub const ELFOSABI_NSK: u8 = 14;
pub const ELFOSABI_AROS: u8 = 15;
pub const ELFOSABI_ARM_AEABI: u8 = 64;
pub const ELFOSABI_ARM: u8 = 97;
pub const ELFOSABI_STANDALONE: u8 = 255;
pub const ET_NONE: u16 = 0;
pub const ET_REL: u16 = 1;
pub const ET_EXEC: u16 = 2;
pub const ET_DYN: u16 = 3;
pub const ET_CORE: u16 = 4;
pub const ET_LOOS: u16 = 0xFE00;
pub const ET_HIOS: u16 = 0xFEFF;
pub const ET_LOPROC: u16 = 0xFF00;
pub const ET_HIPROC: u16 = 0xFFFF;
pub const EM_NONE: u16 = 0;
pub const EM_386: u16 = 3;
pub const EM_X86_64: u16 = 62;
pub const EM_SPARC: u16 = 2;
pub const EM_SPARC32PLUS: u16 = 18;
pub const EM_SPARCV9: u16 = 43;
pub const EM_MIPS: u16 = 8;
pub const EM_PPC: u16 = 20;
pub const EM_PPC64: u16 = 21;
pub const EM_ARM: u16 = 40;
pub const EM_AARCH64: u16 = 183;
pub const EM_S390: u16 = 22;
pub const EM_RISCV: u16 = 243;
pub const EV_NONE: u32 = 0;
pub const EV_CURRENT: u32 = 1;
pub const PT_NULL: u32 = 0;
pub const PT_LOAD: u32 = 1;
pub const PT_DYNAMIC: u32 = 2;
pub const PT_INTERP: u32 = 3;
pub const PT_NOTE: u32 = 4;
pub const PT_SHLIB: u32 = 5;
pub const PT_PHDR: u32 = 6;
pub const PT_TLS: u32 = 7;
pub const PT_LOOS: u32 = 0x60000000;
pub const PT_GNU_EH_FRAME: u32 = 0x6474E550;
pub const PT_GNU_STACK: u32 = 0x6474E551;
pub const PT_GNU_RELRO: u32 = 0x6474E552;
pub const PT_GNU_PROPERTY: u32 = 0x6474E553;
pub const PT_GNU_SFRAME: u32 = 0x6474E554;
pub const PT_LOSUNW: u32 = 0x6FFFFFFA;
pub const PT_SUNWBSS: u32 = 0x6FFFFFFA;
pub const PT_SUNWSTACK: u32 = 0x6FFFFFFB;
pub const PT_HISUNW: u32 = 0x6FFFFFFF;
pub const PT_HIOS: u32 = 0x6FFFFFFF;
pub const PT_LOPROC: u32 = 0x70000000;
pub const PT_HIPROC: u32 = 0x7FFFFFFF;
pub const PF_X: u32 = 0x1;
pub const PF_W: u32 = 0x2;
pub const PF_R: u32 = 0x4;
pub const PF_MASKOS: u32 = 0x0FF00000;
pub const PF_MASKPROC: u32 = 0xF0000000;
pub const SHT_NULL: u32 = 0;
pub const SHT_PROGBITS: u32 = 1;
pub const SHT_SYMTAB: u32 = 2;
pub const SHT_STRTAB: u32 = 3;
pub const SHT_RELA: u32 = 4;
pub const SHT_HASH: u32 = 5;
pub const SHT_DYNAMIC: u32 = 6;
pub const SHT_NOTE: u32 = 7;
pub const SHT_NOBITS: u32 = 8;
pub const SHT_REL: u32 = 9;
pub const SHT_SHLIB: u32 = 10;
pub const SHT_DYNSYM: u32 = 11;
pub const SHT_INIT_ARRAY: u32 = 14;
pub const SHT_FINI_ARRAY: u32 = 15;
pub const SHT_PREINIT_ARRAY: u32 = 16;
pub const SHT_GROUP: u32 = 17;
pub const SHT_SYMTAB_SHNDX: u32 = 18;
pub const SHT_LOOS: u32 = 0x60000000;
pub const SHT_GNU_ATTRIBUTES: u32 = 0x6FFFFFF5;
pub const SHT_GNU_HASH: u32 = 0x6FFFFFF6;
pub const SHT_GNU_LIBLIST: u32 = 0x6FFFFFF7;
pub const SHT_CHECKSUM: u32 = 0x6FFFFFF8;
pub const SHT_GNU_VERDEF: u32 = 0x6FFFFFFD;
pub const SHT_GNU_VERNEED: u32 = 0x6FFFFFFE;
pub const SHT_GNU_VERSYM: u32 = 0x6FFFFFFF;
pub const SHT_HIOS: u32 = 0x6FFFFFFF;
pub const SHT_LOPROC: u32 = 0x70000000;
pub const SHT_HIPROC: u32 = 0x7FFFFFFF;
pub const SHT_LOUSER: u32 = 0x80000000;
pub const SHT_HIUSER: u32 = 0xFFFFFFFF;
pub const SHF_WRITE: u64 = 0x1;
pub const SHF_ALLOC: u64 = 0x2;
pub const SHF_EXECINSTR: u64 = 0x4;
pub const SHF_MERGE: u64 = 0x10;
pub const SHF_STRINGS: u64 = 0x20;
pub const SHF_INFO_LINK: u64 = 0x40;
pub const SHF_LINK_ORDER: u64 = 0x80;
pub const SHF_OS_NONCONFORMING: u64 = 0x100;
pub const SHF_GROUP: u64 = 0x200;
pub const SHF_TLS: u64 = 0x400;
pub const SHF_COMPRESSED: u64 = 0x800;
pub const SHF_MASKOS: u64 = 0x0FF00000;
pub const SHF_MASKPROC: u64 = 0xF0000000;
pub const SHF_GNU_RETAIN: u64 = 0x200000;
pub const SHF_GNU_MBIND: u64 = 0x100000;
pub const SHF_ORDERED: u64 = 0x40000000;
pub const SHF_EXCLUDE: u64 = 0x80000000;
pub const SHN_UNDEF: u16 = 0;
pub const SHN_LORESERVE: u16 = 0xFF00;
pub const SHN_LOPROC: u16 = 0xFF00;
pub const SHN_BEFORE: u16 = 0xFF00;
pub const SHN_AFTER: u16 = 0xFF01;
pub const SHN_HIPROC: u16 = 0xFF1F;
pub const SHN_LOOS: u16 = 0xFF20;
pub const SHN_HIOS: u16 = 0xFF3F;
pub const SHN_ABS: u16 = 0xFFF1;
pub const SHN_COMMON: u16 = 0xFFF2;
pub const SHN_XINDEX: u16 = 0xFFFF;
pub const SHN_HIRESERVE: u16 = 0xFFFF;
pub const STB_LOCAL: u8 = 0;
pub const STB_GLOBAL: u8 = 1;
pub const STB_WEAK: u8 = 2;
pub const STB_LOOS: u8 = 10;
pub const STB_GNU_UNIQUE: u8 = 10;
pub const STB_HIOS: u8 = 12;
pub const STB_LOPROC: u8 = 13;
pub const STB_HIPROC: u8 = 15;
pub const STT_NOTYPE: u8 = 0;
pub const STT_OBJECT: u8 = 1;
pub const STT_FUNC: u8 = 2;
pub const STT_SECTION: u8 = 3;
pub const STT_FILE: u8 = 4;
pub const STT_COMMON: u8 = 5;
pub const STT_TLS: u8 = 6;
pub const STT_LOOS: u8 = 10;
pub const STT_GNU_IFUNC: u8 = 10;
pub const STT_HIOS: u8 = 12;
pub const STT_LOPROC: u8 = 13;
pub const STT_HIPROC: u8 = 15;
pub const STV_DEFAULT: u8 = 0;
pub const STV_INTERNAL: u8 = 1;
pub const STV_HIDDEN: u8 = 2;
pub const STV_PROTECTED: u8 = 3;
pub const DT_NULL: i64 = 0;
pub const DT_NEEDED: i64 = 1;
pub const DT_PLTRELSZ: i64 = 2;
pub const DT_PLTGOT: i64 = 3;
pub const DT_HASH: i64 = 4;
pub const DT_STRTAB: i64 = 5;
pub const DT_SYMTAB: i64 = 6;
pub const DT_RELA: i64 = 7;
pub const DT_RELASZ: i64 = 8;
pub const DT_RELAENT: i64 = 9;
pub const DT_STRSZ: i64 = 10;
pub const DT_SYMENT: i64 = 11;
pub const DT_INIT: i64 = 12;
pub const DT_FINI: i64 = 13;
pub const DT_SONAME: i64 = 14;
pub const DT_RPATH: i64 = 15;
pub const DT_SYMBOLIC: i64 = 16;
pub const DT_REL: i64 = 17;
pub const DT_RELSZ: i64 = 18;
pub const DT_RELENT: i64 = 19;
pub const DT_PLTREL: i64 = 20;
pub const DT_DEBUG: i64 = 21;
pub const DT_TEXTREL: i64 = 22;
pub const DT_JMPREL: i64 = 23;
pub const DT_BIND_NOW: i64 = 24;
pub const DT_INIT_ARRAY: i64 = 25;
pub const DT_FINI_ARRAY: i64 = 26;
pub const DT_INIT_ARRAYSZ: i64 = 27;
pub const DT_FINI_ARRAYSZ: i64 = 28;
pub const DT_RUNPATH: i64 = 29;
pub const DT_FLAGS: i64 = 30;
pub const DT_ENCODING: i64 = 32;
pub const DT_PREINIT_ARRAY: i64 = 32;
pub const DT_PREINIT_ARRAYSZ: i64 = 33;
pub const DT_SYMTAB_SHNDX: i64 = 34;
pub const DT_NUM: i64 = 35;
pub const DT_VALRNGLO: i64 = 0x6FFFFD00;
pub const DT_GNU_PRELINKED: i64 = 0x6FFFFDF5;
pub const DT_GNU_CONFLICTSZ: i64 = 0x6FFFFDF6;
pub const DT_GNU_LIBLISTSZ: i64 = 0x6FFFFDF7;
pub const DT_CHECKSUM: i64 = 0x6FFFFDF8;
pub const DT_PLTPADSZ: i64 = 0x6FFFFDF9;
pub const DT_MOVEENT: i64 = 0x6FFFFDFA;
pub const DT_MOVESZ: i64 = 0x6FFFFDFB;
pub const DT_FEATURE_1: i64 = 0x6FFFFDFC;
pub const DT_POSFLAG_1: i64 = 0x6FFFFDFD;
pub const DT_SYMINSZ: i64 = 0x6FFFFDFE;
pub const DT_SYMINENT: i64 = 0x6FFFFDFF;
pub const DT_VALRNGHI: i64 = 0x6FFFFDFF;
pub const DT_ADDRRNGLO: i64 = 0x6FFFFE00;
pub const DT_GNU_HASH: i64 = 0x6FFFFEF5;
pub const DT_TLSDESC_PLT: i64 = 0x6FFFFEF6;
pub const DT_TLSDESC_GOT: i64 = 0x6FFFFEF7;
pub const DT_GNU_CONFLICT: i64 = 0x6FFFFEF8;
pub const DT_GNU_LIBLIST: i64 = 0x6FFFFEF9;
pub const DT_CONFIG: i64 = 0x6FFFFEFA;
pub const DT_DEPAUDIT: i64 = 0x6FFFFEFB;
pub const DT_AUDIT: i64 = 0x6FFFFEFC;
pub const DT_PLTPAD: i64 = 0x6FFFFEFD;
pub const DT_MOVETAB: i64 = 0x6FFFFEFE;
pub const DT_SYMINFO: i64 = 0x6FFFFEFF;
pub const DT_ADDRRNGHI: i64 = 0x6FFFFEFF;
pub const DT_VERSYM: i64 = 0x6FFFFFF0;
pub const DT_RELACOUNT: i64 = 0x6FFFFFF9;
pub const DT_RELCOUNT: i64 = 0x6FFFFFFA;
pub const DT_FLAGS_1: i64 = 0x6FFFFFFB;
pub const DT_VERDEF: i64 = 0x6FFFFFFC;
pub const DT_VERDEFNUM: i64 = 0x6FFFFFFD;
pub const DT_VERNEED: i64 = 0x6FFFFFFE;
pub const DT_VERNEEDNUM: i64 = 0x6FFFFFFF;
pub const DF_ORIGIN: u64 = 0x1;
pub const DF_SYMBOLIC: u64 = 0x2;
pub const DF_TEXTREL: u64 = 0x4;
pub const DF_BIND_NOW: u64 = 0x8;
pub const DF_STATIC_TLS: u64 = 0x10;
pub const DF_1_NOW: u64 = 0x1;
pub const DF_1_GLOBAL: u64 = 0x2;
pub const DF_1_GROUP: u64 = 0x4;
pub const DF_1_NODELETE: u64 = 0x8;
pub const DF_1_LOADFLTR: u64 = 0x10;
pub const DF_1_INITFIRST: u64 = 0x20;
pub const DF_1_NOOPEN: u64 = 0x40;
pub const DF_1_ORIGIN: u64 = 0x80;
pub const DF_1_DIRECT: u64 = 0x100;
pub const DF_1_TRANS: u64 = 0x200;
pub const DF_1_INTERPOSE: u64 = 0x400;
pub const DF_1_NODEFLIB: u64 = 0x800;
pub const DF_1_NODUMP: u64 = 0x1000;
pub const DF_1_CONFALT: u64 = 0x2000;
pub const DF_1_ENDFILTEE: u64 = 0x4000;
pub const DF_1_DISPRELDNE: u64 = 0x8000;
pub const DF_1_DISPRELPND: u64 = 0x10000;
pub const DF_1_NODIRECT: u64 = 0x20000;
pub const DF_1_IGNMULDEF: u64 = 0x40000;
pub const DF_1_NOKSYMS: u64 = 0x80000;
pub const DF_1_NOHDR: u64 = 0x100000;
pub const DF_1_EDITED: u64 = 0x200000;
pub const DF_1_NORELOC: u64 = 0x400000;
pub const DF_1_SYMINTPOSE: u64 = 0x800000;
pub const DF_1_GLOBAUDIT: u64 = 0x1000000;
pub const DF_1_SINGLETON: u64 = 0x2000000;
pub const DF_1_PIE: u64 = 0x8000000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum X86ELFRelocType {
None,
_64,
PC32,
GOT32,
PLT32,
Copy,
GlobDat,
JumpSlot,
Relative,
GotPCRel,
_32,
_32S,
_16,
PC16,
_8,
PC8,
DTPMod64,
DTPOff64,
TPOff64,
TLSDESC,
IRelaRelative,
JUMP_SLOT,
Size32,
Size64,
GotPC32TLSDESC,
TLSDESC_CALL,
TLSDESC_PLT,
IRELATIVE,
RELATIVE64,
PC32_Baseline,
PC32_NB,
PLT32_Baseline,
PLT32_NB,
GOTPCRELX,
REX_GOTPCRELX,
Unknown(u32),
}
impl X86ELFRelocType {
pub fn from_u32(val: u32) -> Self {
match val {
0 => Self::None,
1 => Self::_64,
2 => Self::PC32,
3 => Self::GOT32,
4 => Self::PLT32,
5 => Self::Copy,
6 => Self::GlobDat,
7 => Self::JumpSlot,
8 => Self::Relative,
9 => Self::GotPCRel,
10 => Self::_32,
11 => Self::_32S,
12 => Self::_16,
13 => Self::PC16,
14 => Self::_8,
15 => Self::PC8,
16 => Self::DTPMod64,
17 => Self::DTPOff64,
18 => Self::TPOff64,
19 => Self::TLSDESC,
21 => Self::DTPMod64, 22 => Self::DTPOff64, 23 => Self::TPOff64,
24 => Self::Size32,
25 => Self::Size64,
26 => Self::GotPC32TLSDESC,
27 => Self::TLSDESC,
28 => Self::TLSDESC_CALL,
29 => Self::TLSDESC_PLT,
35 => Self::TLSDESC_CALL,
36 => Self::TLSDESC_CALL,
37 => Self::IRELATIVE,
38 => Self::RELATIVE64,
41 => Self::GOTPCRELX,
42 => Self::REX_GOTPCRELX,
_ => Self::Unknown(val),
}
}
pub fn to_u32(&self) -> u32 {
match self {
Self::None => 0,
Self::_64 => 1,
Self::PC32 => 2,
Self::GOT32 => 3,
Self::PLT32 => 4,
Self::Copy => 5,
Self::GlobDat => 6,
Self::JumpSlot => 7,
Self::Relative => 8,
Self::GotPCRel => 9,
Self::_32 => 10,
Self::_32S => 11,
Self::_16 => 12,
Self::PC16 => 13,
Self::_8 => 14,
Self::PC8 => 15,
Self::DTPMod64 => 16,
Self::DTPOff64 => 17,
Self::TPOff64 => 18,
Self::TLSDESC => 36,
Self::IRelaRelative => 0, Self::JUMP_SLOT => 7,
Self::Size32 => 24,
Self::Size64 => 25,
Self::GotPC32TLSDESC => 26,
Self::TLSDESC_CALL => 28,
Self::TLSDESC_PLT => 29,
Self::IRELATIVE => 37,
Self::RELATIVE64 => 38,
Self::PC32_Baseline => 41,
Self::PC32_NB => 42,
Self::PLT32_Baseline => 2,
Self::PLT32_NB => 2,
Self::GOTPCRELX => 41,
Self::REX_GOTPCRELX => 42,
Self::Unknown(v) => *v,
}
}
pub fn name(&self) -> &str {
match self {
Self::None => "R_X86_64_NONE",
Self::_64 => "R_X86_64_64",
Self::PC32 => "R_X86_64_PC32",
Self::GOT32 => "R_X86_64_GOT32",
Self::PLT32 => "R_X86_64_PLT32",
Self::Copy => "R_X86_64_COPY",
Self::GlobDat => "R_X86_64_GLOB_DAT",
Self::JumpSlot => "R_X86_64_JUMP_SLOT",
Self::Relative => "R_X86_64_RELATIVE",
Self::GotPCRel => "R_X86_64_GOTPCREL",
Self::_32 => "R_X86_64_32",
Self::_32S => "R_X86_64_32S",
Self::_16 => "R_X86_64_16",
Self::PC16 => "R_X86_64_PC16",
Self::_8 => "R_X86_64_8",
Self::PC8 => "R_X86_64_PC8",
Self::DTPMod64 => "R_X86_64_DTPMOD64",
Self::DTPOff64 => "R_X86_64_DTPOFF64",
Self::TPOff64 => "R_X86_64_TPOFF64",
Self::TLSDESC => "R_X86_64_TLSDESC",
Self::Size32 => "R_X86_64_SIZE32",
Self::Size64 => "R_X86_64_SIZE64",
Self::GotPC32TLSDESC => "R_X86_64_GOTPC32_TLSDESC",
Self::TLSDESC_CALL => "R_X86_64_TLSDESC_CALL",
Self::TLSDESC_PLT => "R_X86_64_TLSDESC_PLT",
Self::IRELATIVE => "R_X86_64_IRELATIVE",
Self::RELATIVE64 => "R_X86_64_RELATIVE64",
Self::GOTPCRELX => "R_X86_64_GOTPCRELX",
Self::REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
_ => "R_X86_64_UNKNOWN",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum X86ELFRelocType386 {
None,
_32,
PC32,
GOT32,
PLT32,
Copy,
GlobDat,
JumpSlot,
Relative,
GotOff,
GotPC,
_32PLT,
TLS_TPOFF,
TLS_IE,
TLS_GOTIE,
TLS_LE,
TLS_GD,
TLS_LDM,
_16,
PC16,
_8,
PC8,
TLS_GD_32,
TLS_GD_PUSH,
TLS_GD_CALL,
TLS_GD_POP,
TLS_LDM_32,
TLS_LDM_PUSH,
TLS_LDM_CALL,
TLS_LDM_POP,
TLS_IE_32,
TLS_IE_PUSH,
TLS_IE_CALL,
TLS_IE_POP,
IRelaRelative,
IRELATIVE,
Unknown(u32),
}
impl X86ELFRelocType386 {
pub fn from_u32(val: u32) -> Self {
match val {
0 => Self::None,
1 => Self::_32,
2 => Self::PC32,
3 => Self::GOT32,
4 => Self::PLT32,
5 => Self::Copy,
6 => Self::GlobDat,
7 => Self::JumpSlot,
8 => Self::Relative,
9 => Self::GotOff,
10 => Self::GotPC,
11 => Self::_32PLT,
14 => Self::TLS_TPOFF,
15 => Self::TLS_IE,
16 => Self::TLS_GOTIE,
17 => Self::TLS_LE,
18 => Self::TLS_GD,
19 => Self::TLS_LDM,
20 => Self::_16,
21 => Self::PC16,
22 => Self::_8,
23 => Self::PC8,
24 => Self::TLS_GD_32,
25 => Self::TLS_GD_PUSH,
26 => Self::TLS_GD_CALL,
27 => Self::TLS_GD_POP,
28 => Self::TLS_LDM_32,
29 => Self::TLS_LDM_PUSH,
30 => Self::TLS_LDM_CALL,
31 => Self::TLS_LDM_POP,
32 => Self::TLS_IE_32,
33 => Self::TLS_IE_PUSH,
34 => Self::TLS_IE_CALL,
35 => Self::TLS_IE_POP,
40 => Self::IRelaRelative,
42 => Self::IRELATIVE,
_ => Self::Unknown(val),
}
}
pub fn to_u32(&self) -> u32 {
match self {
Self::None => 0,
Self::_32 => 1,
Self::PC32 => 2,
Self::GOT32 => 3,
Self::PLT32 => 4,
Self::Copy => 5,
Self::GlobDat => 6,
Self::JumpSlot => 7,
Self::Relative => 8,
Self::GotOff => 9,
Self::GotPC => 10,
Self::_32PLT => 11,
Self::TLS_TPOFF => 14,
Self::TLS_IE => 15,
Self::TLS_GOTIE => 16,
Self::TLS_LE => 17,
Self::TLS_GD => 18,
Self::TLS_LDM => 19,
Self::_16 => 20,
Self::PC16 => 21,
Self::_8 => 22,
Self::PC8 => 23,
Self::TLS_GD_32 => 24,
Self::TLS_GD_PUSH => 25,
Self::TLS_GD_CALL => 26,
Self::TLS_GD_POP => 27,
Self::TLS_LDM_32 => 28,
Self::TLS_LDM_PUSH => 29,
Self::TLS_LDM_CALL => 30,
Self::TLS_LDM_POP => 31,
Self::TLS_IE_32 => 32,
Self::TLS_IE_PUSH => 33,
Self::TLS_IE_CALL => 34,
Self::TLS_IE_POP => 35,
Self::IRelaRelative => 40,
Self::IRELATIVE => 42,
Self::Unknown(v) => *v,
}
}
pub fn name(&self) -> &str {
match self {
Self::None => "R_386_NONE",
Self::_32 => "R_386_32",
Self::PC32 => "R_386_PC32",
Self::GOT32 => "R_386_GOT32",
Self::PLT32 => "R_386_PLT32",
Self::Copy => "R_386_COPY",
Self::GlobDat => "R_386_GLOB_DAT",
Self::JumpSlot => "R_386_JUMP_SLOT",
Self::Relative => "R_386_RELATIVE",
Self::GotOff => "R_386_GOTOFF",
Self::GotPC => "R_386_GOTPC",
Self::_32PLT => "R_386_32PLT",
Self::TLS_TPOFF => "R_386_TLS_TPOFF",
Self::TLS_IE => "R_386_TLS_IE",
Self::TLS_GOTIE => "R_386_TLS_GOTIE",
Self::TLS_LE => "R_386_TLS_LE",
Self::TLS_GD => "R_386_TLS_GD",
Self::TLS_LDM => "R_386_TLS_LDM",
Self::_16 => "R_386_16",
Self::PC16 => "R_386_PC16",
Self::_8 => "R_386_8",
Self::PC8 => "R_386_PC8",
Self::TLS_GD_32 => "R_386_TLS_GD_32",
Self::TLS_GD_PUSH => "R_386_TLS_GD_PUSH",
Self::TLS_GD_CALL => "R_386_TLS_GD_CALL",
Self::TLS_GD_POP => "R_386_TLS_GD_POP",
Self::TLS_LDM_32 => "R_386_TLS_LDM_32",
Self::TLS_LDM_PUSH => "R_386_TLS_LDM_PUSH",
Self::TLS_LDM_CALL => "R_386_TLS_LDM_CALL",
Self::TLS_LDM_POP => "R_386_TLS_LDM_POP",
Self::TLS_IE_32 => "R_386_TLS_IE_32",
Self::TLS_IE_PUSH => "R_386_TLS_IE_PUSH",
Self::TLS_IE_CALL => "R_386_TLS_IE_CALL",
Self::TLS_IE_POP => "R_386_TLS_IE_POP",
Self::IRelaRelative => "R_386_IRELARELATIVE",
Self::IRELATIVE => "R_386_IRELATIVE",
_ => "R_386_UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86ELFEIdent {
pub magic: [u8; 4],
pub class: u8,
pub data: u8,
pub version: u8,
pub os_abi: u8,
pub abi_version: u8,
pub pad: [u8; 7],
}
impl X86ELFEIdent {
pub fn new_default() -> Self {
Self {
magic: ELF_MAGIC,
class: ELFCLASS64,
data: ELFDATA2LSB,
version: EV_CURRENT as u8,
os_abi: ELFOSABI_NONE,
abi_version: 0,
pad: [0u8; 7],
}
}
pub fn is_valid(&self) -> bool {
self.magic[EI_MAG0] == 0x7F
&& self.magic[EI_MAG1] == b'E'
&& self.magic[EI_MAG2] == b'L'
&& self.magic[EI_MAG3] == b'F'
}
pub fn is_64bit(&self) -> bool {
self.class == ELFCLASS64
}
pub fn is_little_endian(&self) -> bool {
self.data == ELFDATA2LSB
}
}
#[derive(Debug, Clone)]
pub struct X86ELFHeader {
pub e_ident: X86ELFEIdent,
pub e_type: u16,
pub e_machine: u16,
pub e_version: u32,
pub e_entry: u64,
pub e_phoff: u64,
pub e_shoff: u64,
pub e_flags: u32,
pub e_ehsize: u16,
pub e_phentsize: u16,
pub e_phnum: u16,
pub e_shentsize: u16,
pub e_shnum: u16,
pub e_shstrndx: u16,
}
impl X86ELFHeader {
pub fn new_default() -> Self {
Self {
e_ident: X86ELFEIdent::new_default(),
e_type: ET_REL,
e_machine: EM_X86_64,
e_version: EV_CURRENT,
e_entry: 0,
e_phoff: 0,
e_shoff: 0,
e_flags: 0,
e_ehsize: 64,
e_phentsize: 56,
e_phnum: 0,
e_shentsize: 64,
e_shnum: 0,
e_shstrndx: SHN_UNDEF,
}
}
pub fn read(data: &[u8]) -> Option<Self> {
if data.len() < 64 {
return None;
}
let e_ident = X86ELFEIdent {
magic: [data[0], data[1], data[2], data[3]],
class: data[4],
data: data[5],
version: data[6],
os_abi: data[7],
abi_version: data[8],
pad: [
data[9], data[10], data[11], data[12], data[13], data[14], data[15],
],
};
if !e_ident.is_valid() {
return None;
}
let e_type = u16::from_le_bytes([data[16], data[17]]);
let e_machine = u16::from_le_bytes([data[18], data[19]]);
let e_version = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
let e_entry = u64::from_le_bytes([
data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31],
]);
let e_phoff = u64::from_le_bytes([
data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39],
]);
let e_shoff = u64::from_le_bytes([
data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47],
]);
let e_flags = u32::from_le_bytes([data[48], data[49], data[50], data[51]]);
let e_ehsize = u16::from_le_bytes([data[52], data[53]]);
let e_phentsize = u16::from_le_bytes([data[54], data[55]]);
let e_phnum = u16::from_le_bytes([data[56], data[57]]);
let e_shentsize = u16::from_le_bytes([data[58], data[59]]);
let e_shnum = u16::from_le_bytes([data[60], data[61]]);
let e_shstrndx = u16::from_le_bytes([data[62], data[63]]);
Some(Self {
e_ident,
e_type,
e_machine,
e_version,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize,
e_phentsize,
e_phnum,
e_shentsize,
e_shnum,
e_shstrndx,
})
}
pub fn read32(data: &[u8]) -> Option<Self> {
if data.len() < 52 {
return None;
}
let e_ident = X86ELFEIdent {
magic: [data[0], data[1], data[2], data[3]],
class: data[4],
data: data[5],
version: data[6],
os_abi: data[7],
abi_version: data[8],
pad: [
data[9], data[10], data[11], data[12], data[13], data[14], data[15],
],
};
if !e_ident.is_valid() {
return None;
}
let e_type = u16::from_le_bytes([data[16], data[17]]);
let e_machine = u16::from_le_bytes([data[18], data[19]]);
let e_version = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
let e_entry = u32::from_le_bytes([data[24], data[25], data[26], data[27]]) as u64;
let e_phoff = u32::from_le_bytes([data[28], data[29], data[30], data[31]]) as u64;
let e_shoff = u32::from_le_bytes([data[32], data[33], data[34], data[35]]) as u64;
let e_flags = u32::from_le_bytes([data[36], data[37], data[38], data[39]]);
let e_ehsize = u16::from_le_bytes([data[40], data[41]]);
let e_phentsize = u16::from_le_bytes([data[42], data[43]]);
let e_phnum = u16::from_le_bytes([data[44], data[45]]);
let e_shentsize = u16::from_le_bytes([data[46], data[47]]);
let e_shnum = u16::from_le_bytes([data[48], data[49]]);
let e_shstrndx = u16::from_le_bytes([data[50], data[51]]);
Some(Self {
e_ident,
e_type,
e_machine,
e_version,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize,
e_phentsize,
e_phnum,
e_shentsize,
e_shnum,
e_shstrndx,
})
}
pub fn write(&self, buf: &mut [u8]) {
buf[0] = self.e_ident.magic[0];
buf[1] = self.e_ident.magic[1];
buf[2] = self.e_ident.magic[2];
buf[3] = self.e_ident.magic[3];
buf[4] = self.e_ident.class;
buf[5] = self.e_ident.data;
buf[6] = self.e_ident.version;
buf[7] = self.e_ident.os_abi;
buf[8] = self.e_ident.abi_version;
buf[9..16].copy_from_slice(&self.e_ident.pad);
buf[16..18].copy_from_slice(&self.e_type.to_le_bytes());
buf[18..20].copy_from_slice(&self.e_machine.to_le_bytes());
buf[20..24].copy_from_slice(&self.e_version.to_le_bytes());
buf[24..32].copy_from_slice(&self.e_entry.to_le_bytes());
buf[32..40].copy_from_slice(&self.e_phoff.to_le_bytes());
buf[40..48].copy_from_slice(&self.e_shoff.to_le_bytes());
buf[48..52].copy_from_slice(&self.e_flags.to_le_bytes());
buf[52..54].copy_from_slice(&self.e_ehsize.to_le_bytes());
buf[54..56].copy_from_slice(&self.e_phentsize.to_le_bytes());
buf[56..58].copy_from_slice(&self.e_phnum.to_le_bytes());
buf[58..60].copy_from_slice(&self.e_shentsize.to_le_bytes());
buf[60..62].copy_from_slice(&self.e_shnum.to_le_bytes());
buf[62..64].copy_from_slice(&self.e_shstrndx.to_le_bytes());
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = vec![0u8; self.e_ehsize as usize];
self.write(&mut buf);
buf
}
pub fn type_name(&self) -> &str {
match self.e_type {
ET_NONE => "NONE",
ET_REL => "REL (Relocatable)",
ET_EXEC => "EXEC (Executable)",
ET_DYN => "DYN (Shared object)",
ET_CORE => "CORE",
_ => "UNKNOWN",
}
}
pub fn machine_name(&self) -> &str {
match self.e_machine {
EM_NONE => "NONE",
EM_386 => "Intel 80386",
EM_X86_64 => "AMD x86-64",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86ELFProgramHeader {
pub p_type: u32,
pub p_flags: u32,
pub p_offset: u64,
pub p_vaddr: u64,
pub p_paddr: u64,
pub p_filesz: u64,
pub p_memsz: u64,
pub p_align: u64,
}
impl X86ELFProgramHeader {
pub fn new_default() -> Self {
Self {
p_type: PT_LOAD,
p_flags: PF_R | PF_W,
p_offset: 0,
p_vaddr: 0,
p_paddr: 0,
p_filesz: 0,
p_memsz: 0,
p_align: 0x1000,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 56 > data.len() {
return None;
}
let p_type = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let p_flags = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let p_offset = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let p_vaddr = u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let p_paddr = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let p_filesz = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let p_memsz = u64::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
let p_align = u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
Some(Self {
p_type,
p_flags,
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_align,
})
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 32 > data.len() {
return None;
}
let p_type = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let p_offset = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as u64;
let p_vaddr = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as u64;
let p_paddr = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]) as u64;
let p_filesz = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]) as u64;
let p_memsz = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]) as u64;
let p_flags = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let p_align = u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]) as u64;
Some(Self {
p_type,
p_flags,
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_align,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 4].copy_from_slice(&self.p_type.to_le_bytes());
buf[offset + 4..offset + 8].copy_from_slice(&self.p_flags.to_le_bytes());
buf[offset + 8..offset + 16].copy_from_slice(&self.p_offset.to_le_bytes());
buf[offset + 16..offset + 24].copy_from_slice(&self.p_vaddr.to_le_bytes());
buf[offset + 24..offset + 32].copy_from_slice(&self.p_paddr.to_le_bytes());
buf[offset + 32..offset + 40].copy_from_slice(&self.p_filesz.to_le_bytes());
buf[offset + 40..offset + 48].copy_from_slice(&self.p_memsz.to_le_bytes());
buf[offset + 48..offset + 56].copy_from_slice(&self.p_align.to_le_bytes());
}
pub fn type_name(&self) -> &str {
match self.p_type {
PT_NULL => "NULL",
PT_LOAD => "LOAD",
PT_DYNAMIC => "DYNAMIC",
PT_INTERP => "INTERP",
PT_NOTE => "NOTE",
PT_SHLIB => "SHLIB",
PT_PHDR => "PHDR",
PT_TLS => "TLS",
PT_GNU_EH_FRAME => "GNU_EH_FRAME",
PT_GNU_STACK => "GNU_STACK",
PT_GNU_RELRO => "GNU_RELRO",
PT_GNU_PROPERTY => "GNU_PROPERTY",
_ => "UNKNOWN",
}
}
pub fn flags_string(&self) -> String {
let mut s = String::new();
if self.p_flags & PF_R != 0 {
s.push('R');
}
if self.p_flags & PF_W != 0 {
s.push('W');
}
if self.p_flags & PF_X != 0 {
s.push('X');
}
if s.is_empty() {
s.push_str("---");
}
s
}
}
#[derive(Debug, Clone)]
pub struct X86ELFSectionHeader {
pub sh_name: u32,
pub sh_type: u32,
pub sh_flags: u64,
pub sh_addr: u64,
pub sh_offset: u64,
pub sh_size: u64,
pub sh_link: u32,
pub sh_info: u32,
pub sh_addralign: u64,
pub sh_entsize: u64,
}
impl X86ELFSectionHeader {
pub fn new_default() -> Self {
Self {
sh_name: 0,
sh_type: SHT_NULL,
sh_flags: 0,
sh_addr: 0,
sh_offset: 0,
sh_size: 0,
sh_link: 0,
sh_info: 0,
sh_addralign: 0,
sh_entsize: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 64 > data.len() {
return None;
}
let sh_name = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let sh_type = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let sh_flags = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let sh_addr = u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let sh_offset = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let sh_size = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let sh_link = u32::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
]);
let sh_info = u32::from_le_bytes([
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
let sh_addralign = u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let sh_entsize = u64::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
Some(Self {
sh_name,
sh_type,
sh_flags,
sh_addr,
sh_offset,
sh_size,
sh_link,
sh_info,
sh_addralign,
sh_entsize,
})
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 40 > data.len() {
return None;
}
let sh_name = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let sh_type = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let sh_flags = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as u64;
let sh_addr = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]) as u64;
let sh_offset = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]) as u64;
let sh_size = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]) as u64;
let sh_link = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let sh_info = u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let sh_addralign = u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]) as u64;
let sh_entsize = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]) as u64;
Some(Self {
sh_name,
sh_type,
sh_flags,
sh_addr,
sh_offset,
sh_size,
sh_link,
sh_info,
sh_addralign,
sh_entsize,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 4].copy_from_slice(&self.sh_name.to_le_bytes());
buf[offset + 4..offset + 8].copy_from_slice(&self.sh_type.to_le_bytes());
buf[offset + 8..offset + 16].copy_from_slice(&self.sh_flags.to_le_bytes());
buf[offset + 16..offset + 24].copy_from_slice(&self.sh_addr.to_le_bytes());
buf[offset + 24..offset + 32].copy_from_slice(&self.sh_offset.to_le_bytes());
buf[offset + 32..offset + 40].copy_from_slice(&self.sh_size.to_le_bytes());
buf[offset + 40..offset + 44].copy_from_slice(&self.sh_link.to_le_bytes());
buf[offset + 44..offset + 48].copy_from_slice(&self.sh_info.to_le_bytes());
buf[offset + 48..offset + 56].copy_from_slice(&self.sh_addralign.to_le_bytes());
buf[offset + 56..offset + 64].copy_from_slice(&self.sh_entsize.to_le_bytes());
}
pub fn type_name(&self) -> &str {
match self.sh_type {
SHT_NULL => "NULL",
SHT_PROGBITS => "PROGBITS",
SHT_SYMTAB => "SYMTAB",
SHT_STRTAB => "STRTAB",
SHT_RELA => "RELA",
SHT_HASH => "HASH",
SHT_DYNAMIC => "DYNAMIC",
SHT_NOTE => "NOTE",
SHT_NOBITS => "NOBITS",
SHT_REL => "REL",
SHT_SHLIB => "SHLIB",
SHT_DYNSYM => "DYNSYM",
SHT_INIT_ARRAY => "INIT_ARRAY",
SHT_FINI_ARRAY => "FINI_ARRAY",
SHT_PREINIT_ARRAY => "PREINIT_ARRAY",
SHT_GROUP => "GROUP",
SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX",
SHT_GNU_HASH => "GNU_HASH",
SHT_GNU_VERDEF => "GNU_VERDEF",
SHT_GNU_VERNEED => "GNU_VERNEED",
SHT_GNU_VERSYM => "GNU_VERSYM",
_ => "UNKNOWN",
}
}
pub fn flags_string(&self) -> String {
let mut parts = Vec::new();
if self.sh_flags & SHF_WRITE != 0 {
parts.push("W");
}
if self.sh_flags & SHF_ALLOC != 0 {
parts.push("A");
}
if self.sh_flags & SHF_EXECINSTR != 0 {
parts.push("X");
}
if self.sh_flags & SHF_MERGE != 0 {
parts.push("M");
}
if self.sh_flags & SHF_STRINGS != 0 {
parts.push("S");
}
if self.sh_flags & SHF_TLS != 0 {
parts.push("T");
}
if self.sh_flags & SHF_GROUP != 0 {
parts.push("G");
}
parts.join("")
}
}
#[derive(Debug, Clone)]
pub struct X86ELFSymbol {
pub st_name: u32,
pub st_info: u8,
pub st_other: u8,
pub st_shndx: u16,
pub st_value: u64,
pub st_size: u64,
}
impl X86ELFSymbol {
pub fn new_default() -> Self {
Self {
st_name: 0,
st_info: 0,
st_other: 0,
st_shndx: SHN_UNDEF,
st_value: 0,
st_size: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let st_name = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let st_info = data[offset + 4];
let st_other = data[offset + 5];
let st_shndx = u16::from_le_bytes([data[offset + 6], data[offset + 7]]);
let st_value = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let st_size = u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
Some(Self {
st_name,
st_info,
st_other,
st_shndx,
st_value,
st_size,
})
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let st_name = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let st_value = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as u64;
let st_size = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as u64;
let st_info = data[offset + 12];
let st_other = data[offset + 13];
let st_shndx = u16::from_le_bytes([data[offset + 14], data[offset + 15]]);
Some(Self {
st_name,
st_info,
st_other,
st_shndx,
st_value,
st_size,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 4].copy_from_slice(&self.st_name.to_le_bytes());
buf[offset + 4] = self.st_info;
buf[offset + 5] = self.st_other;
buf[offset + 6..offset + 8].copy_from_slice(&self.st_shndx.to_le_bytes());
buf[offset + 8..offset + 16].copy_from_slice(&self.st_value.to_le_bytes());
buf[offset + 16..offset + 24].copy_from_slice(&self.st_size.to_le_bytes());
}
pub fn binding(&self) -> u8 {
self.st_info >> 4
}
pub fn symbol_type(&self) -> u8 {
self.st_info & 0x0F
}
pub fn visibility(&self) -> u8 {
self.st_other & 0x03
}
pub fn set_binding(&mut self, binding: u8) {
self.st_info = (binding << 4) | (self.st_info & 0x0F);
}
pub fn set_type(&mut self, st_type: u8) {
self.st_info = (self.st_info & 0xF0) | (st_type & 0x0F);
}
pub fn set_visibility(&mut self, vis: u8) {
self.st_other = (self.st_other & 0xFC) | (vis & 0x03);
}
pub fn binding_name(&self) -> &str {
match self.binding() {
STB_LOCAL => "LOCAL",
STB_GLOBAL => "GLOBAL",
STB_WEAK => "WEAK",
STB_GNU_UNIQUE => "GNU_UNIQUE",
_ => "UNKNOWN",
}
}
pub fn type_name(&self) -> &str {
match self.symbol_type() {
STT_NOTYPE => "NOTYPE",
STT_OBJECT => "OBJECT",
STT_FUNC => "FUNC",
STT_SECTION => "SECTION",
STT_FILE => "FILE",
STT_COMMON => "COMMON",
STT_TLS => "TLS",
STT_GNU_IFUNC => "GNU_IFUNC",
_ => "UNKNOWN",
}
}
pub fn visibility_name(&self) -> &str {
match self.visibility() {
STV_DEFAULT => "DEFAULT",
STV_INTERNAL => "INTERNAL",
STV_HIDDEN => "HIDDEN",
STV_PROTECTED => "PROTECTED",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86ELFRelocation {
pub r_offset: u64,
pub r_info: u64,
pub r_addend: i64,
}
impl X86ELFRelocation {
pub fn new_default() -> Self {
Self {
r_offset: 0,
r_info: 0,
r_addend: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let r_offset = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let r_info = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let r_addend = i64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
Some(Self {
r_offset,
r_info,
r_addend,
})
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 12 > data.len() {
return None;
}
let r_offset = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
let r_info = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as u64;
let r_addend = i32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as i64;
Some(Self {
r_offset,
r_info,
r_addend,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 8].copy_from_slice(&self.r_offset.to_le_bytes());
buf[offset + 8..offset + 16].copy_from_slice(&self.r_info.to_le_bytes());
buf[offset + 16..offset + 24].copy_from_slice(&self.r_addend.to_le_bytes());
}
pub fn reloc_type(&self) -> u32 {
(self.r_info & 0xFFFFFFFF) as u32
}
pub fn symbol_index(&self) -> u32 {
(self.r_info >> 32) as u32
}
pub fn set_reloc_info(&mut self, sym: u32, reloc_type: u32) {
self.r_info = ((sym as u64) << 32) | (reloc_type as u64);
}
}
#[derive(Debug, Clone)]
pub struct X86ELFRelocationNoAddend {
pub r_offset: u64,
pub r_info: u64,
}
impl X86ELFRelocationNoAddend {
pub fn new_default() -> Self {
Self {
r_offset: 0,
r_info: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let r_offset = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let r_info = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self { r_offset, r_info })
}
pub fn reloc_type(&self) -> u32 {
(self.r_info & 0xFFFFFFFF) as u32
}
pub fn symbol_index(&self) -> u32 {
(self.r_info >> 32) as u32
}
}
#[derive(Debug, Clone)]
pub struct X86ELFDynamic {
pub d_tag: i64,
pub d_val: u64,
}
impl X86ELFDynamic {
pub fn new_default() -> Self {
Self {
d_tag: DT_NULL,
d_val: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let d_tag = i64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let d_val = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self { d_tag, d_val })
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 8 > data.len() {
return None;
}
let d_tag = i32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as i64;
let d_val = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as u64;
Some(Self { d_tag, d_val })
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 8].copy_from_slice(&self.d_tag.to_le_bytes());
buf[offset + 8..offset + 16].copy_from_slice(&self.d_val.to_le_bytes());
}
pub fn tag_name(&self) -> &str {
match self.d_tag {
DT_NULL => "DT_NULL",
DT_NEEDED => "DT_NEEDED",
DT_PLTRELSZ => "DT_PLTRELSZ",
DT_PLTGOT => "DT_PLTGOT",
DT_HASH => "DT_HASH",
DT_STRTAB => "DT_STRTAB",
DT_SYMTAB => "DT_SYMTAB",
DT_RELA => "DT_RELA",
DT_RELASZ => "DT_RELASZ",
DT_RELAENT => "DT_RELAENT",
DT_STRSZ => "DT_STRSZ",
DT_SYMENT => "DT_SYMENT",
DT_INIT => "DT_INIT",
DT_FINI => "DT_FINI",
DT_SONAME => "DT_SONAME",
DT_RPATH => "DT_RPATH",
DT_SYMBOLIC => "DT_SYMBOLIC",
DT_REL => "DT_REL",
DT_RELSZ => "DT_RELSZ",
DT_RELENT => "DT_RELENT",
DT_PLTREL => "DT_PLTREL",
DT_DEBUG => "DT_DEBUG",
DT_TEXTREL => "DT_TEXTREL",
DT_JMPREL => "DT_JMPREL",
DT_BIND_NOW => "DT_BIND_NOW",
DT_INIT_ARRAY => "DT_INIT_ARRAY",
DT_FINI_ARRAY => "DT_FINI_ARRAY",
DT_INIT_ARRAYSZ => "DT_INIT_ARRAYSZ",
DT_FINI_ARRAYSZ => "DT_FINI_ARRAYSZ",
DT_RUNPATH => "DT_RUNPATH",
DT_FLAGS => "DT_FLAGS",
DT_PREINIT_ARRAY => "DT_PREINIT_ARRAY",
DT_PREINIT_ARRAYSZ => "DT_PREINIT_ARRAYSZ",
DT_GNU_HASH => "DT_GNU_HASH",
DT_VERSYM => "DT_VERSYM",
DT_VERNEED => "DT_VERNEED",
DT_VERNEEDNUM => "DT_VERNEEDNUM",
DT_VERDEF => "DT_VERDEF",
DT_VERDEFNUM => "DT_VERDEFNUM",
DT_RELACOUNT => "DT_RELACOUNT",
DT_RELCOUNT => "DT_RELCOUNT",
DT_FLAGS_1 => "DT_FLAGS_1",
_ => "DT_UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86ELFNote {
pub n_namesz: u32,
pub n_descsz: u32,
pub n_type: u32,
pub name: String,
pub desc: Vec<u8>,
}
impl X86ELFNote {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 12 > data.len() {
return None;
}
let n_namesz = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let n_descsz = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let n_type = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let name_start = offset + 12;
let name_end = name_start + n_namesz as usize;
if name_end > data.len() {
return None;
}
let name_bytes = &data[name_start..name_end];
let name_len = name_bytes
.iter()
.position(|&b| b == 0)
.unwrap_or(name_bytes.len());
let name = String::from_utf8_lossy(&name_bytes[..name_len]).to_string();
let desc_start = (name_end + 3) & !3; let desc_end = desc_start + n_descsz as usize;
if desc_end > data.len() {
return None;
}
let desc = data[desc_start..desc_end].to_vec();
Some(Self {
n_namesz,
n_descsz,
n_type,
name,
desc,
})
}
}
#[derive(Debug, Clone)]
pub struct X86ELFBuildID {
pub name: String,
pub hash: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct X86ELFBinary {
pub data: Vec<u8>,
pub is_64bit: bool,
pub header: X86ELFHeader,
pub program_headers: Vec<X86ELFProgramHeader>,
pub section_headers: Vec<X86ELFSectionHeader>,
pub symbols: Vec<X86ELFSymbol>,
pub dynamics: Vec<X86ELFDynamic>,
pub section_names: Vec<String>,
pub string_tables: HashMap<usize, Vec<u8>>,
pub notes: Vec<X86ELFNote>,
pub build_id: Option<X86ELFBuildID>,
pub dynstr_offset: usize,
}
impl X86ELFBinary {
pub fn read(data: &[u8], is_64bit: bool) -> Self {
let data = data.to_vec();
let header = if is_64bit {
X86ELFHeader::read(&data).unwrap_or_else(X86ELFHeader::new_default)
} else {
X86ELFHeader::read32(&data).unwrap_or_else(X86ELFHeader::new_default)
};
let mut program_headers = Vec::new();
let phoff = header.e_phoff as usize;
let phentsize = header.e_phentsize as usize;
for i in 0..header.e_phnum as usize {
let off = phoff + i * phentsize;
if let Some(ph) = if is_64bit {
X86ELFProgramHeader::read(&data, off)
} else {
X86ELFProgramHeader::read32(&data, off)
} {
program_headers.push(ph);
}
}
let mut section_headers = Vec::new();
let shoff = header.e_shoff as usize;
let shentsize = header.e_shentsize as usize;
for i in 0..header.e_shnum as usize {
let off = shoff + i * shentsize;
if let Some(sh) = if is_64bit {
X86ELFSectionHeader::read(&data, off)
} else {
X86ELFSectionHeader::read32(&data, off)
} {
section_headers.push(sh);
}
}
let mut section_names = Vec::new();
let mut string_tables: HashMap<usize, Vec<u8>> = HashMap::new();
let shstrtab_idx = header.e_shstrndx as usize;
if shstrtab_idx > 0 && shstrtab_idx < section_headers.len() {
let sh = §ion_headers[shstrtab_idx];
let start = sh.sh_offset as usize;
let end = (sh.sh_offset + sh.sh_size) as usize;
if end <= data.len() {
let str_data = data[start..end].to_vec();
let mut pos = 0;
while pos < str_data.len() {
let remaining = &str_data[pos..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
section_names.push(String::from_utf8_lossy(&remaining[..len]).to_string());
pos += len + 1;
}
string_tables.insert(shstrtab_idx, str_data);
}
}
while section_names.len() < section_headers.len() {
section_names.push(String::new());
}
for (i, sh) in section_headers.iter().enumerate() {
if i == shstrtab_idx {
continue;
}
if sh.sh_type == SHT_STRTAB {
let start = sh.sh_offset as usize;
let end = (sh.sh_offset + sh.sh_size) as usize;
if end <= data.len() {
string_tables.insert(i, data[start..end].to_vec());
}
}
}
let mut symbols = Vec::new();
for sh in §ion_headers {
if sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM {
let entsize = sh.sh_entsize as usize;
if entsize == 0 {
continue;
}
let start = sh.sh_offset as usize;
let count = sh.sh_size as usize / entsize;
for j in 0..count {
let off = start + j * entsize;
if let Some(sym) = if is_64bit {
X86ELFSymbol::read(&data, off)
} else {
X86ELFSymbol::read32(&data, off)
} {
symbols.push(sym);
}
}
}
}
let mut dynamics = Vec::new();
let mut dynstr_offset: usize = 0;
for sh in §ion_headers {
if sh.sh_type == SHT_DYNAMIC {
let start = sh.sh_offset as usize;
let entsize = sh.sh_entsize as usize;
let count = if entsize > 0 {
sh.sh_size as usize / entsize
} else {
0
};
for j in 0..count {
let off = start + j * entsize;
if let Some(d) = if is_64bit {
X86ELFDynamic::read(&data, off)
} else {
X86ELFDynamic::read32(&data, off)
} {
if d.d_tag == DT_STRTAB {
dynstr_offset = d.d_val as usize;
}
let is_null = d.d_tag == DT_NULL;
dynamics.push(d);
if is_null {
break;
}
}
}
}
}
let mut notes = Vec::new();
for sh in §ion_headers {
if sh.sh_type == SHT_NOTE {
let start = sh.sh_offset as usize;
let end = (sh.sh_offset + sh.sh_size) as usize;
if end <= data.len() {
let mut pos = start;
while pos + 12 <= end {
if let Some(note) = X86ELFNote::read(&data, pos) {
let namesz = note.n_namesz as usize;
let descsz = note.n_descsz as usize;
pos = ((pos + 12 + namesz + 3) & !3) + ((descsz + 3) & !3);
notes.push(note);
} else {
break;
}
}
}
}
}
let mut build_id: Option<X86ELFBuildID> = None;
for note in ¬es {
if note.n_type == 3 && note.name == "GNU" {
build_id = Some(X86ELFBuildID {
name: note.name.clone(),
hash: note.desc.clone(),
});
break;
}
}
Self {
data,
is_64bit,
header,
program_headers,
section_headers,
symbols,
dynamics,
section_names,
string_tables,
notes,
build_id,
dynstr_offset,
}
}
pub fn get_section(&self, index: usize) -> Option<&X86ELFSectionHeader> {
self.section_headers.get(index)
}
pub fn get_section_name(&self, sh: &X86ELFSectionHeader) -> String {
let idx = sh.sh_name as usize;
if idx < self.section_names.len() && !self.section_names[idx].is_empty() {
return self.section_names[idx].clone();
}
let shstrtab_idx = self.header.e_shstrndx as usize;
if let Some(strtab) = self.string_tables.get(&shstrtab_idx) {
let off = sh.sh_name as usize;
if off < strtab.len() {
let remaining = &strtab[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
return String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
String::new()
}
pub fn find_section_by_name(&self, name: &str) -> Option<&X86ELFSectionHeader> {
for sh in &self.section_headers {
if self.get_section_name(sh) == name {
return Some(sh);
}
}
None
}
pub fn find_sections_by_type(&self, sh_type: u32) -> Vec<&X86ELFSectionHeader> {
self.section_headers
.iter()
.filter(|sh| sh.sh_type == sh_type)
.collect()
}
pub fn read_section_data(&self, sh: &X86ELFSectionHeader) -> Vec<u8> {
let start = sh.sh_offset as usize;
let end = (sh.sh_offset + sh.sh_size) as usize;
if end <= self.data.len() {
self.data[start..end].to_vec()
} else {
Vec::new()
}
}
pub fn read_string(&self, strtab_index: usize, offset: u32) -> String {
if let Some(strtab) = self.string_tables.get(&strtab_index) {
let off = offset as usize;
if off < strtab.len() {
let remaining = &strtab[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
return String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
String::new()
}
pub fn get_symbol_name(&self, sym: &X86ELFSymbol) -> String {
if sym.st_name == 0 {
return String::new();
}
for (_index, strtab) in &self.string_tables {
let off = sym.st_name as usize;
if off < strtab.len() {
let remaining = &strtab[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
if len > 0 {
let s = String::from_utf8_lossy(&remaining[..len]).to_string();
if !s.is_empty() {
return s;
}
}
}
}
if self.dynstr_offset > 0 && self.dynstr_offset < self.data.len() {
let off = self.dynstr_offset + sym.st_name as usize;
if off < self.data.len() {
let remaining = &self.data[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
return String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
String::new()
}
pub fn read_relocations(&self, sh: &X86ELFSectionHeader) -> Vec<X86ELFRelocation> {
let mut relocs = Vec::new();
let data = self.read_section_data(sh);
let entsize = if sh.sh_type == SHT_RELA {
if self.is_64bit {
24
} else {
12
}
} else {
if self.is_64bit {
16
} else {
8
}
};
let mut offset = 0;
while offset + entsize <= data.len() {
if sh.sh_type == SHT_RELA {
if let Some(r) = if self.is_64bit {
X86ELFRelocation::read(&data, offset)
} else {
X86ELFRelocation::read32(&data, offset)
} {
relocs.push(r);
}
}
offset += entsize;
}
relocs
}
pub fn get_program_headers_by_type(&self, p_type: u32) -> Vec<&X86ELFProgramHeader> {
self.program_headers
.iter()
.filter(|ph| ph.p_type == p_type)
.collect()
}
pub fn is_shared_library(&self) -> bool {
self.header.e_type == ET_DYN
}
pub fn is_executable(&self) -> bool {
self.header.e_type == ET_EXEC
}
pub fn is_object(&self) -> bool {
self.header.e_type == ET_REL
}
pub fn get_needed_libraries(&self) -> Vec<String> {
let mut needed = Vec::new();
for d in &self.dynamics {
if d.d_tag == DT_NEEDED {
let name = if self.dynstr_offset > 0 && self.dynstr_offset < self.data.len() {
let off = self.dynstr_offset + d.d_val as usize;
if off < self.data.len() {
let remaining = &self.data[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
String::from_utf8_lossy(&remaining[..len]).to_string()
} else {
String::new()
}
} else {
String::new()
};
if !name.is_empty() {
needed.push(name);
}
}
}
needed
}
pub fn get_entry_point(&self) -> u64 {
self.header.e_entry
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"=== ELF{} Binary Summary ===\n",
if self.is_64bit { "64" } else { "32" }
));
s.push_str(&format!("Type: {}\n", self.header.type_name()));
s.push_str(&format!("Machine: {}\n", self.header.machine_name()));
s.push_str(&format!("Entry: 0x{:016X}\n", self.header.e_entry));
s.push_str(&format!(
"Program Headers: {}\n",
self.program_headers.len()
));
for ph in &self.program_headers {
s.push_str(&format!(
" {:12} flags={} vaddr=0x{:016X} offset=0x{:08X} filesz=0x{:X} memsz=0x{:X}\n",
ph.type_name(),
ph.flags_string(),
ph.p_vaddr,
ph.p_offset,
ph.p_filesz,
ph.p_memsz
));
}
s.push_str(&format!(
"Section Headers: {}\n",
self.section_headers.len()
));
for sh in &self.section_headers {
let name = self.get_section_name(sh);
s.push_str(&format!(
" [{:2}] {:20} {:12} flags={:8} addr=0x{:016X} size=0x{:X}\n",
self.section_headers
.iter()
.position(|h| std::ptr::eq(h, sh))
.unwrap_or(0),
name,
sh.type_name(),
sh.flags_string(),
sh.sh_addr,
sh.sh_size
));
}
s.push_str(&format!("Symbols: {}\n", self.symbols.len()));
s.push_str(&format!("Dynamic entries: {}\n", self.dynamics.len()));
if let Some(ref bid) = self.build_id {
s.push_str(&format!("Build ID: {}\n", hex::encode(&bid.hash)));
}
let needed = self.get_needed_libraries();
if !needed.is_empty() {
s.push_str("Needed libraries:\n");
for lib in &needed {
s.push_str(&format!(" {}\n", lib));
}
}
s
}
pub fn write(&self, buf: &mut Vec<u8>) {
buf.clear();
buf.resize(self.header.e_ehsize as usize, 0);
self.header.write(buf);
let phoff = self.header.e_phoff as usize;
if phoff > 0 {
buf.resize(
phoff + self.header.e_phnum as usize * self.header.e_phentsize as usize,
0,
);
for (i, ph) in self.program_headers.iter().enumerate() {
let off = phoff + i * self.header.e_phentsize as usize;
if off + 56 <= buf.len() {
ph.write(buf, off);
}
}
}
let shoff = self.header.e_shoff as usize;
if shoff > 0 {
buf.resize(
shoff + self.header.e_shnum as usize * self.header.e_shentsize as usize,
0,
);
for (i, sh) in self.section_headers.iter().enumerate() {
let off = shoff + i * self.header.e_shentsize as usize;
if off + 64 <= buf.len() {
sh.write(buf, off);
}
}
}
}
pub fn set_entry_point(&mut self, addr: u64) {
self.header.e_entry = addr;
}
pub fn add_program_header(&mut self, ph: X86ELFProgramHeader) {
self.program_headers.push(ph);
self.header.e_phnum = self.program_headers.len() as u16;
}
pub fn add_section_header(&mut self, sh: X86ELFSectionHeader) {
self.section_headers.push(sh);
self.header.e_shnum = self.section_headers.len() as u16;
}
}
pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; pub const IMAGE_DOSZM_SIGNATURE: u16 = 0x4D5A;
pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550;
pub const IMAGE_FILE_MACHINE_UNKNOWN: u16 = 0x0000;
pub const IMAGE_FILE_MACHINE_I386: u16 = 0x014C;
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
pub const IMAGE_FILE_MACHINE_ARM: u16 = 0x01C0;
pub const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64;
pub const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001;
pub const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002;
pub const IMAGE_FILE_LINE_NUMS_STRIPPED: u16 = 0x0004;
pub const IMAGE_FILE_LOCAL_SYMS_STRIPPED: u16 = 0x0008;
pub const IMAGE_FILE_AGGRESSIVE_WS_TRIM: u16 = 0x0010;
pub const IMAGE_FILE_LARGE_ADDRESS_AWARE: u16 = 0x0020;
pub const IMAGE_FILE_BYTES_REVERSED_LO: u16 = 0x0080;
pub const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100;
pub const IMAGE_FILE_DEBUG_STRIPPED: u16 = 0x0200;
pub const IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP: u16 = 0x0400;
pub const IMAGE_FILE_NET_RUN_FROM_SWAP: u16 = 0x0800;
pub const IMAGE_FILE_SYSTEM: u16 = 0x1000;
pub const IMAGE_FILE_DLL: u16 = 0x2000;
pub const IMAGE_FILE_UP_SYSTEM_ONLY: u16 = 0x4000;
pub const IMAGE_FILE_BYTES_REVERSED_HI: u16 = 0x8000;
pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: u16 = 0x010B; pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x020B; pub const IMAGE_ROM_OPTIONAL_HDR_MAGIC: u16 = 0x0107;
pub const IMAGE_SUBSYSTEM_UNKNOWN: u16 = 0;
pub const IMAGE_SUBSYSTEM_NATIVE: u16 = 1;
pub const IMAGE_SUBSYSTEM_WINDOWS_GUI: u16 = 2;
pub const IMAGE_SUBSYSTEM_WINDOWS_CUI: u16 = 3;
pub const IMAGE_SUBSYSTEM_OS2_CUI: u16 = 5;
pub const IMAGE_SUBSYSTEM_POSIX_CUI: u16 = 7;
pub const IMAGE_SUBSYSTEM_NATIVE_WINDOWS: u16 = 8;
pub const IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: u16 = 9;
pub const IMAGE_SUBSYSTEM_EFI_APPLICATION: u16 = 10;
pub const IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: u16 = 11;
pub const IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: u16 = 12;
pub const IMAGE_SUBSYSTEM_EFI_ROM: u16 = 13;
pub const IMAGE_SUBSYSTEM_XBOX: u16 = 14;
pub const IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: u16 = 16;
pub const IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA: u16 = 0x0020;
pub const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE: u16 = 0x0040;
pub const IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY: u16 = 0x0080;
pub const IMAGE_DLLCHARACTERISTICS_NX_COMPAT: u16 = 0x0100;
pub const IMAGE_DLLCHARACTERISTICS_NO_ISOLATION: u16 = 0x0200;
pub const IMAGE_DLLCHARACTERISTICS_NO_SEH: u16 = 0x0400;
pub const IMAGE_DLLCHARACTERISTICS_NO_BIND: u16 = 0x0800;
pub const IMAGE_DLLCHARACTERISTICS_APPCONTAINER: u16 = 0x1000;
pub const IMAGE_DLLCHARACTERISTICS_WDM_DRIVER: u16 = 0x2000;
pub const IMAGE_DLLCHARACTERISTICS_GUARD_CF: u16 = 0x4000;
pub const IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE: u16 = 0x8000;
pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x00000008;
pub const IMAGE_SCN_CNT_CODE: u32 = 0x00000020;
pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x00000040;
pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x00000080;
pub const IMAGE_SCN_LNK_OTHER: u32 = 0x00000100;
pub const IMAGE_SCN_LNK_INFO: u32 = 0x00000200;
pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x00000800;
pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x00001000;
pub const IMAGE_SCN_GPREL: u32 = 0x00008000;
pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x00020000;
pub const IMAGE_SCN_MEM_16BIT: u32 = 0x00020000;
pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x00040000;
pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x00080000;
pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x00100000;
pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x00200000;
pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x00300000;
pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x00400000;
pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x00500000;
pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x00600000;
pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x00700000;
pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x00800000;
pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x00900000;
pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A00000;
pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B00000;
pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C00000;
pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D00000;
pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E00000;
pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F00000;
pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x01000000;
pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x02000000;
pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x04000000;
pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x08000000;
pub const IMAGE_SCN_MEM_SHARED: u32 = 0x10000000;
pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x20000000;
pub const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
pub const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0;
pub const IMAGE_DIRECTORY_ENTRY_IMPORT: usize = 1;
pub const IMAGE_DIRECTORY_ENTRY_RESOURCE: usize = 2;
pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3;
pub const IMAGE_DIRECTORY_ENTRY_SECURITY: usize = 4;
pub const IMAGE_DIRECTORY_ENTRY_BASERELOC: usize = 5;
pub const IMAGE_DIRECTORY_ENTRY_DEBUG: usize = 6;
pub const IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: usize = 7;
pub const IMAGE_DIRECTORY_ENTRY_GLOBALPTR: usize = 8;
pub const IMAGE_DIRECTORY_ENTRY_TLS: usize = 9;
pub const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: usize = 10;
pub const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: usize = 11;
pub const IMAGE_DIRECTORY_ENTRY_IAT: usize = 12;
pub const IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: usize = 13;
pub const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: usize = 14;
pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES: usize = 16;
pub const DATA_DIRECTORY_NAMES: [&str; 16] = [
"Export",
"Import",
"Resource",
"Exception",
"Security",
"BaseReloc",
"Debug",
"Architecture",
"GlobalPtr",
"TLS",
"LoadConfig",
"BoundImport",
"IAT",
"DelayImport",
"CLR Header",
"Reserved",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86PERelocAMD64 {
Absolute,
Addr64,
Addr32,
Addr32NB,
Rel32,
Rel32_1,
Rel32_2,
Rel32_3,
Rel32_4,
Rel32_5,
Section,
SecRel,
SecRel7,
Token,
SRel32,
Pair,
SsdSpan32,
Unknown(u16),
}
impl X86PERelocAMD64 {
pub fn from_u16(val: u16) -> Self {
match val {
0x0000 => Self::Absolute,
0x0001 => Self::Addr64,
0x0002 => Self::Addr32,
0x0003 => Self::Addr32NB,
0x0004 => Self::Rel32,
0x0005 => Self::Rel32_1,
0x0006 => Self::Rel32_2,
0x0007 => Self::Rel32_3,
0x0008 => Self::Rel32_4,
0x0009 => Self::Rel32_5,
0x000A => Self::Section,
0x000B => Self::SecRel,
0x000C => Self::SecRel7,
0x000D => Self::Token,
0x000E => Self::SRel32,
0x000F => Self::Pair,
0x0010 => Self::SsdSpan32,
_ => Self::Unknown(val),
}
}
pub fn name(&self) -> &str {
match self {
Self::Absolute => "IMAGE_REL_AMD64_ABSOLUTE",
Self::Addr64 => "IMAGE_REL_AMD64_ADDR64",
Self::Addr32 => "IMAGE_REL_AMD64_ADDR32",
Self::Addr32NB => "IMAGE_REL_AMD64_ADDR32NB",
Self::Rel32 => "IMAGE_REL_AMD64_REL32",
Self::Rel32_1 => "IMAGE_REL_AMD64_REL32_1",
Self::Rel32_2 => "IMAGE_REL_AMD64_REL32_2",
Self::Rel32_3 => "IMAGE_REL_AMD64_REL32_3",
Self::Rel32_4 => "IMAGE_REL_AMD64_REL32_4",
Self::Rel32_5 => "IMAGE_REL_AMD64_REL32_5",
Self::Section => "IMAGE_REL_AMD64_SECTION",
Self::SecRel => "IMAGE_REL_AMD64_SECREL",
Self::SecRel7 => "IMAGE_REL_AMD64_SECREL7",
Self::Token => "IMAGE_REL_AMD64_TOKEN",
Self::SRel32 => "IMAGE_REL_AMD64_SREL32",
Self::Pair => "IMAGE_REL_AMD64_PAIR",
Self::SsdSpan32 => "IMAGE_REL_AMD64_SSPAN32",
Self::Unknown(_) => "IMAGE_REL_AMD64_UNKNOWN",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86PERelocI386 {
Absolute,
Dir16,
Rel16,
Dir32,
Dir32NB,
Seg12,
Section,
SecRel,
SecRel7,
Token,
Rel32,
Pair,
SsdSpan32,
Unknown(u16),
}
impl X86PERelocI386 {
pub fn from_u16(val: u16) -> Self {
match val {
0x0000 => Self::Absolute,
0x0001 => Self::Dir16,
0x0002 => Self::Rel16,
0x0006 => Self::Dir32,
0x0007 => Self::Dir32NB,
0x0009 => Self::Seg12,
0x000A => Self::Section,
0x000B => Self::SecRel,
0x000C => Self::SecRel7,
0x000D => Self::Token,
0x0014 => Self::Rel32,
0x000F => Self::Pair,
0x0010 => Self::SsdSpan32,
_ => Self::Unknown(val),
}
}
pub fn name(&self) -> &str {
match self {
Self::Absolute => "IMAGE_REL_I386_ABSOLUTE",
Self::Dir16 => "IMAGE_REL_I386_DIR16",
Self::Rel16 => "IMAGE_REL_I386_REL16",
Self::Dir32 => "IMAGE_REL_I386_DIR32",
Self::Dir32NB => "IMAGE_REL_I386_DIR32NB",
Self::Seg12 => "IMAGE_REL_I386_SEG12",
Self::Section => "IMAGE_REL_I386_SECTION",
Self::SecRel => "IMAGE_REL_I386_SECREL",
Self::SecRel7 => "IMAGE_REL_I386_SECREL7",
Self::Token => "IMAGE_REL_I386_TOKEN",
Self::Rel32 => "IMAGE_REL_I386_REL32",
Self::Pair => "IMAGE_REL_I386_PAIR",
Self::SsdSpan32 => "IMAGE_REL_I386_SSPAN32",
Self::Unknown(_) => "IMAGE_REL_I386_UNKNOWN",
}
}
}
pub const IMAGE_SYM_CLASS_NULL: u8 = 0;
pub const IMAGE_SYM_CLASS_AUTOMATIC: u8 = 1;
pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 2;
pub const IMAGE_SYM_CLASS_STATIC: u8 = 3;
pub const IMAGE_SYM_CLASS_REGISTER: u8 = 4;
pub const IMAGE_SYM_CLASS_EXTERNAL_DEF: u8 = 5;
pub const IMAGE_SYM_CLASS_LABEL: u8 = 6;
pub const IMAGE_SYM_CLASS_UNDEFINED_LABEL: u8 = 7;
pub const IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: u8 = 8;
pub const IMAGE_SYM_CLASS_ARGUMENT: u8 = 9;
pub const IMAGE_SYM_CLASS_STRUCT_TAG: u8 = 10;
pub const IMAGE_SYM_CLASS_MEMBER_OF_UNION: u8 = 11;
pub const IMAGE_SYM_CLASS_UNION_TAG: u8 = 12;
pub const IMAGE_SYM_CLASS_TYPE_DEFINITION: u8 = 13;
pub const IMAGE_SYM_CLASS_UNDEFINED_STATIC: u8 = 14;
pub const IMAGE_SYM_CLASS_ENUM_TAG: u8 = 15;
pub const IMAGE_SYM_CLASS_MEMBER_OF_ENUM: u8 = 16;
pub const IMAGE_SYM_CLASS_REGISTER_PARAM: u8 = 17;
pub const IMAGE_SYM_CLASS_BIT_FIELD: u8 = 18;
pub const IMAGE_SYM_CLASS_FAR_EXTERNAL: u8 = 68;
pub const IMAGE_SYM_CLASS_BLOCK: u8 = 100;
pub const IMAGE_SYM_CLASS_FUNCTION: u8 = 101;
pub const IMAGE_SYM_CLASS_END_OF_STRUCT: u8 = 102;
pub const IMAGE_SYM_CLASS_FILE: u8 = 103;
pub const IMAGE_SYM_CLASS_SECTION: u8 = 104;
pub const IMAGE_SYM_CLASS_WEAK_EXTERNAL: u8 = 105;
pub const IMAGE_SYM_CLASS_CLR_TOKEN: u8 = 107;
pub const IMAGE_SYM_TYPE_NULL: u16 = 0x0000;
pub const IMAGE_SYM_TYPE_VOID: u16 = 0x0001;
pub const IMAGE_SYM_TYPE_CHAR: u16 = 0x0002;
pub const IMAGE_SYM_TYPE_SHORT: u16 = 0x0003;
pub const IMAGE_SYM_TYPE_INT: u16 = 0x0004;
pub const IMAGE_SYM_TYPE_LONG: u16 = 0x0005;
pub const IMAGE_SYM_TYPE_FLOAT: u16 = 0x0006;
pub const IMAGE_SYM_TYPE_DOUBLE: u16 = 0x0007;
pub const IMAGE_SYM_TYPE_STRUCT: u16 = 0x0008;
pub const IMAGE_SYM_TYPE_UNION: u16 = 0x0009;
pub const IMAGE_SYM_TYPE_ENUM: u16 = 0x000A;
pub const IMAGE_SYM_TYPE_MOE: u16 = 0x000B;
pub const IMAGE_SYM_TYPE_BYTE: u16 = 0x000C;
pub const IMAGE_SYM_TYPE_WORD: u16 = 0x000D;
pub const IMAGE_SYM_TYPE_UINT: u16 = 0x000E;
pub const IMAGE_SYM_TYPE_DWORD: u16 = 0x000F;
pub const IMAGE_SYM_TYPE_PCODE: u16 = 0x8000;
#[derive(Debug, Clone)]
pub struct X86PEDosHeader {
pub e_magic: u16, pub e_cblp: u16,
pub e_cp: u16,
pub e_crlc: u16,
pub e_cparhdr: u16,
pub e_minalloc: u16,
pub e_maxalloc: u16,
pub e_ss: u16,
pub e_sp: u16,
pub e_csum: u16,
pub e_ip: u16,
pub e_cs: u16,
pub e_lfarlc: u16,
pub e_ovno: u16,
pub e_res: [u16; 4],
pub e_oemid: u16,
pub e_oeminfo: u16,
pub e_res2: [u16; 10],
pub e_lfanew: u32,
}
impl X86PEDosHeader {
pub fn new_default() -> Self {
Self {
e_magic: IMAGE_DOS_SIGNATURE,
e_cblp: 0,
e_cp: 0,
e_crlc: 0,
e_cparhdr: 4,
e_minalloc: 0,
e_maxalloc: 0xFFFF,
e_ss: 0,
e_sp: 0xB8,
e_csum: 0,
e_ip: 0,
e_cs: 0,
e_lfarlc: 0x40,
e_ovno: 0,
e_res: [0; 4],
e_oemid: 0,
e_oeminfo: 0,
e_res2: [0; 10],
e_lfanew: 0x80,
}
}
pub fn read(data: &[u8]) -> Option<Self> {
if data.len() < 64 {
return None;
}
if data[0] != b'M' || data[1] != b'Z' {
return None;
}
let e_magic = u16::from_le_bytes([data[0], data[1]]);
let e_cblp = u16::from_le_bytes([data[2], data[3]]);
let e_cp = u16::from_le_bytes([data[4], data[5]]);
let e_crlc = u16::from_le_bytes([data[6], data[7]]);
let e_cparhdr = u16::from_le_bytes([data[8], data[9]]);
let e_minalloc = u16::from_le_bytes([data[10], data[11]]);
let e_maxalloc = u16::from_le_bytes([data[12], data[13]]);
let e_ss = u16::from_le_bytes([data[14], data[15]]);
let e_sp = u16::from_le_bytes([data[16], data[17]]);
let e_csum = u16::from_le_bytes([data[18], data[19]]);
let e_ip = u16::from_le_bytes([data[20], data[21]]);
let e_cs = u16::from_le_bytes([data[22], data[23]]);
let e_lfarlc = u16::from_le_bytes([data[24], data[25]]);
let e_ovno = u16::from_le_bytes([data[26], data[27]]);
let e_res = [
u16::from_le_bytes([data[28], data[29]]),
u16::from_le_bytes([data[30], data[31]]),
u16::from_le_bytes([data[32], data[33]]),
u16::from_le_bytes([data[34], data[35]]),
];
let e_oemid = u16::from_le_bytes([data[36], data[37]]);
let e_oeminfo = u16::from_le_bytes([data[38], data[39]]);
let e_res2 = [
u16::from_le_bytes([data[40], data[41]]),
u16::from_le_bytes([data[42], data[43]]),
u16::from_le_bytes([data[44], data[45]]),
u16::from_le_bytes([data[46], data[47]]),
u16::from_le_bytes([data[48], data[49]]),
u16::from_le_bytes([data[50], data[51]]),
u16::from_le_bytes([data[52], data[53]]),
u16::from_le_bytes([data[54], data[55]]),
u16::from_le_bytes([data[56], data[57]]),
u16::from_le_bytes([data[58], data[59]]),
];
let e_lfanew = u32::from_le_bytes([data[60], data[61], data[62], data[63]]);
Some(Self {
e_magic,
e_cblp,
e_cp,
e_crlc,
e_cparhdr,
e_minalloc,
e_maxalloc,
e_ss,
e_sp,
e_csum,
e_ip,
e_cs,
e_lfarlc,
e_ovno,
e_res,
e_oemid,
e_oeminfo,
e_res2,
e_lfanew,
})
}
pub fn write(&self, buf: &mut [u8]) {
buf[0..2].copy_from_slice(&self.e_magic.to_le_bytes());
buf[2..4].copy_from_slice(&self.e_cblp.to_le_bytes());
buf[4..6].copy_from_slice(&self.e_cp.to_le_bytes());
buf[6..8].copy_from_slice(&self.e_crlc.to_le_bytes());
buf[8..10].copy_from_slice(&self.e_cparhdr.to_le_bytes());
buf[10..12].copy_from_slice(&self.e_minalloc.to_le_bytes());
buf[12..14].copy_from_slice(&self.e_maxalloc.to_le_bytes());
buf[14..16].copy_from_slice(&self.e_ss.to_le_bytes());
buf[16..18].copy_from_slice(&self.e_sp.to_le_bytes());
buf[18..20].copy_from_slice(&self.e_csum.to_le_bytes());
buf[20..22].copy_from_slice(&self.e_ip.to_le_bytes());
buf[22..24].copy_from_slice(&self.e_cs.to_le_bytes());
buf[24..26].copy_from_slice(&self.e_lfarlc.to_le_bytes());
buf[26..28].copy_from_slice(&self.e_ovno.to_le_bytes());
for i in 0..4 {
buf[28 + i * 2..30 + i * 2].copy_from_slice(&self.e_res[i].to_le_bytes());
}
buf[36..38].copy_from_slice(&self.e_oemid.to_le_bytes());
buf[38..40].copy_from_slice(&self.e_oeminfo.to_le_bytes());
for i in 0..10 {
buf[40 + i * 2..42 + i * 2].copy_from_slice(&self.e_res2[i].to_le_bytes());
}
buf[60..64].copy_from_slice(&self.e_lfanew.to_le_bytes());
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = vec![0u8; 64];
self.write(&mut buf);
buf
}
}
#[derive(Debug, Clone)]
pub struct X86PECoffHeader {
pub machine: u16,
pub number_of_sections: u16,
pub time_date_stamp: u32,
pub pointer_to_symbol_table: u32,
pub number_of_symbols: u32,
pub size_of_optional_header: u16,
pub characteristics: u16,
}
impl X86PECoffHeader {
pub fn new_default() -> Self {
Self {
machine: IMAGE_FILE_MACHINE_AMD64,
number_of_sections: 0,
time_date_stamp: 0,
pointer_to_symbol_table: 0,
number_of_symbols: 0,
size_of_optional_header: 240, characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 20 > data.len() {
return None;
}
let machine = u16::from_le_bytes([data[offset], data[offset + 1]]);
let number_of_sections = u16::from_le_bytes([data[offset + 2], data[offset + 3]]);
let time_date_stamp = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let pointer_to_symbol_table = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let number_of_symbols = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let size_of_optional_header = u16::from_le_bytes([data[offset + 16], data[offset + 17]]);
let characteristics = u16::from_le_bytes([data[offset + 18], data[offset + 19]]);
Some(Self {
machine,
number_of_sections,
time_date_stamp,
pointer_to_symbol_table,
number_of_symbols,
size_of_optional_header,
characteristics,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 2].copy_from_slice(&self.machine.to_le_bytes());
buf[offset + 2..offset + 4].copy_from_slice(&self.number_of_sections.to_le_bytes());
buf[offset + 4..offset + 8].copy_from_slice(&self.time_date_stamp.to_le_bytes());
buf[offset + 8..offset + 12].copy_from_slice(&self.pointer_to_symbol_table.to_le_bytes());
buf[offset + 12..offset + 16].copy_from_slice(&self.number_of_symbols.to_le_bytes());
buf[offset + 16..offset + 18].copy_from_slice(&self.size_of_optional_header.to_le_bytes());
buf[offset + 18..offset + 20].copy_from_slice(&self.characteristics.to_le_bytes());
}
pub fn machine_name(&self) -> &str {
match self.machine {
IMAGE_FILE_MACHINE_I386 => "i386",
IMAGE_FILE_MACHINE_AMD64 => "AMD64",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86PEDataDirectory {
pub virtual_address: u32,
pub size: u32,
}
impl X86PEDataDirectory {
pub fn new_default() -> Self {
Self {
virtual_address: 0,
size: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 8 > data.len() {
return None;
}
let virtual_address = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let size = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
Some(Self {
virtual_address,
size,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 4].copy_from_slice(&self.virtual_address.to_le_bytes());
buf[offset + 4..offset + 8].copy_from_slice(&self.size.to_le_bytes());
}
}
#[derive(Debug, Clone)]
pub struct X86PEOptionalHeader {
pub magic: u16,
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub size_of_code: u32,
pub size_of_initialized_data: u32,
pub size_of_uninitialized_data: u32,
pub address_of_entry_point: u32,
pub base_of_code: u32,
pub base_of_data: u32,
pub image_base: u64,
pub section_alignment: u32,
pub file_alignment: u32,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub win32_version_value: u32,
pub size_of_image: u32,
pub size_of_headers: u32,
pub check_sum: u32,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u64,
pub size_of_stack_commit: u64,
pub size_of_heap_reserve: u64,
pub size_of_heap_commit: u64,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
pub data_directory: Vec<X86PEDataDirectory>,
}
impl X86PEOptionalHeader {
pub fn new_default(is_64bit: bool) -> Self {
Self {
magic: if is_64bit {
IMAGE_NT_OPTIONAL_HDR64_MAGIC
} else {
IMAGE_NT_OPTIONAL_HDR32_MAGIC
},
major_linker_version: 14,
minor_linker_version: 0,
size_of_code: 0,
size_of_initialized_data: 0,
size_of_uninitialized_data: 0,
address_of_entry_point: 0,
base_of_code: 0,
base_of_data: 0,
image_base: if is_64bit { 0x140000000 } else { 0x00400000 },
section_alignment: 0x1000,
file_alignment: 0x200,
major_operating_system_version: 6,
minor_operating_system_version: 0,
major_image_version: 0,
minor_image_version: 0,
major_subsystem_version: 6,
minor_subsystem_version: 0,
win32_version_value: 0,
size_of_image: 0,
size_of_headers: 0,
check_sum: 0,
subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI,
dll_characteristics: IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
| IMAGE_DLLCHARACTERISTICS_NX_COMPAT,
size_of_stack_reserve: if is_64bit { 0x100000 } else { 0x100000 },
size_of_stack_commit: 0x1000,
size_of_heap_reserve: 0x100000,
size_of_heap_commit: 0x1000,
loader_flags: 0,
number_of_rva_and_sizes: IMAGE_NUMBEROF_DIRECTORY_ENTRIES as u32,
data_directory: vec![
X86PEDataDirectory::new_default();
IMAGE_NUMBEROF_DIRECTORY_ENTRIES
],
}
}
pub fn read_pe32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 96 > data.len() {
return None;
}
let magic = u16::from_le_bytes([data[offset], data[offset + 1]]);
if magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC {
return None;
}
let major_linker_version = data[offset + 2];
let minor_linker_version = data[offset + 3];
let size_of_code = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let size_of_initialized_data = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let size_of_uninitialized_data = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let address_of_entry_point = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let base_of_code = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let base_of_data = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let image_base = u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]) as u64;
let section_alignment = u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]);
let file_alignment = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let major_os = u16::from_le_bytes([data[offset + 40], data[offset + 41]]);
let minor_os = u16::from_le_bytes([data[offset + 42], data[offset + 43]]);
let major_img = u16::from_le_bytes([data[offset + 44], data[offset + 45]]);
let minor_img = u16::from_le_bytes([data[offset + 46], data[offset + 47]]);
let major_subsys = u16::from_le_bytes([data[offset + 48], data[offset + 49]]);
let minor_subsys = u16::from_le_bytes([data[offset + 50], data[offset + 51]]);
let win32_version_value = u32::from_le_bytes([
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let size_of_image = u32::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
]);
let size_of_headers = u32::from_le_bytes([
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
let check_sum = u32::from_le_bytes([
data[offset + 64],
data[offset + 65],
data[offset + 66],
data[offset + 67],
]);
let subsystem = u16::from_le_bytes([data[offset + 68], data[offset + 69]]);
let dll_characteristics = u16::from_le_bytes([data[offset + 70], data[offset + 71]]);
let size_of_stack_reserve = u32::from_le_bytes([
data[offset + 72],
data[offset + 73],
data[offset + 74],
data[offset + 75],
]) as u64;
let size_of_stack_commit = u32::from_le_bytes([
data[offset + 76],
data[offset + 77],
data[offset + 78],
data[offset + 79],
]) as u64;
let size_of_heap_reserve = u32::from_le_bytes([
data[offset + 80],
data[offset + 81],
data[offset + 82],
data[offset + 83],
]) as u64;
let size_of_heap_commit = u32::from_le_bytes([
data[offset + 84],
data[offset + 85],
data[offset + 86],
data[offset + 87],
]) as u64;
let loader_flags = u32::from_le_bytes([
data[offset + 88],
data[offset + 89],
data[offset + 90],
data[offset + 91],
]);
let number_of_rva_and_sizes = u32::from_le_bytes([
data[offset + 92],
data[offset + 93],
data[offset + 94],
data[offset + 95],
]);
let mut data_directory = Vec::new();
let dd_start = offset + 96;
for i in 0..number_of_rva_and_sizes.min(16) as usize {
let dd_off = dd_start + i * 8;
if let Some(dd) = X86PEDataDirectory::read(data, dd_off) {
data_directory.push(dd);
}
}
while data_directory.len() < IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
data_directory.push(X86PEDataDirectory::new_default());
}
Some(Self {
magic,
major_linker_version,
minor_linker_version,
size_of_code,
size_of_initialized_data,
size_of_uninitialized_data,
address_of_entry_point,
base_of_code,
base_of_data,
image_base,
section_alignment,
file_alignment,
major_operating_system_version: major_os,
minor_operating_system_version: minor_os,
major_image_version: major_img,
minor_image_version: minor_img,
major_subsystem_version: major_subsys,
minor_subsystem_version: minor_subsys,
win32_version_value,
size_of_image,
size_of_headers,
check_sum,
subsystem,
dll_characteristics,
size_of_stack_reserve,
size_of_stack_commit,
size_of_heap_reserve,
size_of_heap_commit,
loader_flags,
number_of_rva_and_sizes,
data_directory,
})
}
pub fn read_pe32plus(data: &[u8], offset: usize) -> Option<Self> {
if offset + 112 > data.len() {
return None;
}
let magic = u16::from_le_bytes([data[offset], data[offset + 1]]);
if magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC {
return None;
}
let major_linker_version = data[offset + 2];
let minor_linker_version = data[offset + 3];
let size_of_code = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let size_of_initialized_data = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let size_of_uninitialized_data = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let address_of_entry_point = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let base_of_code = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let image_base = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let section_alignment = u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]);
let file_alignment = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let major_os = u16::from_le_bytes([data[offset + 40], data[offset + 41]]);
let minor_os = u16::from_le_bytes([data[offset + 42], data[offset + 43]]);
let major_img = u16::from_le_bytes([data[offset + 44], data[offset + 45]]);
let minor_img = u16::from_le_bytes([data[offset + 46], data[offset + 47]]);
let major_subsys = u16::from_le_bytes([data[offset + 48], data[offset + 49]]);
let minor_subsys = u16::from_le_bytes([data[offset + 50], data[offset + 51]]);
let win32_version_value = u32::from_le_bytes([
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let size_of_image = u32::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
]);
let size_of_headers = u32::from_le_bytes([
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
let check_sum = u32::from_le_bytes([
data[offset + 64],
data[offset + 65],
data[offset + 66],
data[offset + 67],
]);
let subsystem = u16::from_le_bytes([data[offset + 68], data[offset + 69]]);
let dll_characteristics = u16::from_le_bytes([data[offset + 70], data[offset + 71]]);
let size_of_stack_reserve = u64::from_le_bytes([
data[offset + 72],
data[offset + 73],
data[offset + 74],
data[offset + 75],
data[offset + 76],
data[offset + 77],
data[offset + 78],
data[offset + 79],
]);
let size_of_stack_commit = u64::from_le_bytes([
data[offset + 80],
data[offset + 81],
data[offset + 82],
data[offset + 83],
data[offset + 84],
data[offset + 85],
data[offset + 86],
data[offset + 87],
]);
let size_of_heap_reserve = u64::from_le_bytes([
data[offset + 88],
data[offset + 89],
data[offset + 90],
data[offset + 91],
data[offset + 92],
data[offset + 93],
data[offset + 94],
data[offset + 95],
]);
let size_of_heap_commit = u64::from_le_bytes([
data[offset + 96],
data[offset + 97],
data[offset + 98],
data[offset + 99],
data[offset + 100],
data[offset + 101],
data[offset + 102],
data[offset + 103],
]);
let loader_flags = u32::from_le_bytes([
data[offset + 104],
data[offset + 105],
data[offset + 106],
data[offset + 107],
]);
let number_of_rva_and_sizes = u32::from_le_bytes([
data[offset + 108],
data[offset + 109],
data[offset + 110],
data[offset + 111],
]);
let mut data_directory = Vec::new();
let dd_start = offset + 112;
for i in 0..number_of_rva_and_sizes.min(16) as usize {
let dd_off = dd_start + i * 8;
if let Some(dd) = X86PEDataDirectory::read(data, dd_off) {
data_directory.push(dd);
}
}
while data_directory.len() < IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
data_directory.push(X86PEDataDirectory::new_default());
}
Some(Self {
magic,
major_linker_version,
minor_linker_version,
size_of_code,
size_of_initialized_data,
size_of_uninitialized_data,
address_of_entry_point,
base_of_code,
base_of_data: 0,
image_base,
section_alignment,
file_alignment,
major_operating_system_version: major_os,
minor_operating_system_version: minor_os,
major_image_version: major_img,
minor_image_version: minor_img,
major_subsystem_version: major_subsys,
minor_subsystem_version: minor_subsys,
win32_version_value,
size_of_image,
size_of_headers,
check_sum,
subsystem,
dll_characteristics,
size_of_stack_reserve,
size_of_stack_commit,
size_of_heap_reserve,
size_of_heap_commit,
loader_flags,
number_of_rva_and_sizes,
data_directory,
})
}
pub fn is_pe32plus(&self) -> bool {
self.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
}
}
#[derive(Debug, Clone)]
pub struct X86PESectionHeader {
pub name: [u8; 8],
pub virtual_size: u32,
pub virtual_address: u32,
pub size_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub pointer_to_relocations: u32,
pub pointer_to_linenumbers: u32,
pub number_of_relocations: u16,
pub number_of_linenumbers: u16,
pub characteristics: u32,
}
impl X86PESectionHeader {
pub fn new_default() -> Self {
Self {
name: [0; 8],
virtual_size: 0,
virtual_address: 0,
size_of_raw_data: 0,
pointer_to_raw_data: 0,
pointer_to_relocations: 0,
pointer_to_linenumbers: 0,
number_of_relocations: 0,
number_of_linenumbers: 0,
characteristics: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 40 > data.len() {
return None;
}
let mut name = [0u8; 8];
name.copy_from_slice(&data[offset..offset + 8]);
let virtual_size = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let virtual_address = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let size_of_raw_data = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let pointer_to_raw_data = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let pointer_to_relocations = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let pointer_to_linenumbers = u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let number_of_relocations = u16::from_le_bytes([data[offset + 32], data[offset + 33]]);
let number_of_linenumbers = u16::from_le_bytes([data[offset + 34], data[offset + 35]]);
let characteristics = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
Some(Self {
name,
virtual_size,
virtual_address,
size_of_raw_data,
pointer_to_raw_data,
pointer_to_relocations,
pointer_to_linenumbers,
number_of_relocations,
number_of_linenumbers,
characteristics,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 8].copy_from_slice(&self.name);
buf[offset + 8..offset + 12].copy_from_slice(&self.virtual_size.to_le_bytes());
buf[offset + 12..offset + 16].copy_from_slice(&self.virtual_address.to_le_bytes());
buf[offset + 16..offset + 20].copy_from_slice(&self.size_of_raw_data.to_le_bytes());
buf[offset + 20..offset + 24].copy_from_slice(&self.pointer_to_raw_data.to_le_bytes());
buf[offset + 24..offset + 28].copy_from_slice(&self.pointer_to_relocations.to_le_bytes());
buf[offset + 28..offset + 32].copy_from_slice(&self.pointer_to_linenumbers.to_le_bytes());
buf[offset + 32..offset + 34].copy_from_slice(&self.number_of_relocations.to_le_bytes());
buf[offset + 34..offset + 36].copy_from_slice(&self.number_of_linenumbers.to_le_bytes());
buf[offset + 36..offset + 40].copy_from_slice(&self.characteristics.to_le_bytes());
}
pub fn name_str(&self) -> String {
let len = self.name.iter().position(|&b| b == 0).unwrap_or(8);
String::from_utf8_lossy(&self.name[..len]).to_string()
}
pub fn set_name(&mut self, s: &str) {
let bytes = s.as_bytes();
let len = bytes.len().min(8);
self.name[..len].copy_from_slice(&bytes[..len]);
for i in len..8 {
self.name[i] = 0;
}
}
pub fn is_long_name(&self) -> bool {
self.name[0] == b'/'
}
pub fn long_name_offset(&self) -> Option<usize> {
if self.name[0] != b'/' {
return None;
}
let s = self.name_str();
let num_str = &s[1..];
num_str.parse::<usize>().ok()
}
pub fn characteristics_string(&self) -> String {
let mut parts = Vec::new();
if self.characteristics & IMAGE_SCN_CNT_CODE != 0 {
parts.push("CODE");
}
if self.characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
parts.push("INIT_DATA");
}
if self.characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 {
parts.push("UNINIT_DATA");
}
if self.characteristics & IMAGE_SCN_MEM_EXECUTE != 0 {
parts.push("EXECUTE");
}
if self.characteristics & IMAGE_SCN_MEM_READ != 0 {
parts.push("READ");
}
if self.characteristics & IMAGE_SCN_MEM_WRITE != 0 {
parts.push("WRITE");
}
if self.characteristics & IMAGE_SCN_MEM_DISCARDABLE != 0 {
parts.push("DISCARD");
}
if self.characteristics & IMAGE_SCN_MEM_SHARED != 0 {
parts.push("SHARED");
}
parts.join("|")
}
}
#[derive(Debug, Clone)]
pub struct X86PESymbol {
pub name_short: [u8; 8],
pub name_long: String,
pub value: u32,
pub section_number: i16,
pub sym_type: u16,
pub storage_class: u8,
pub number_of_aux_symbols: u8,
}
impl X86PESymbol {
pub fn new_default() -> Self {
Self {
name_short: [0; 8],
name_long: String::new(),
value: 0,
section_number: 0,
sym_type: 0,
storage_class: IMAGE_SYM_CLASS_NULL,
number_of_aux_symbols: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 18 > data.len() {
return None;
}
let mut name_short = [0u8; 8];
name_short.copy_from_slice(&data[offset..offset + 8]);
let name_long =
if name_short[0] == 0 && name_short[1] == 0 && name_short[2] == 0 && name_short[3] == 0
{
String::new() } else {
let len = name_short.iter().position(|&b| b == 0).unwrap_or(8);
String::from_utf8_lossy(&name_short[..len]).to_string()
};
let value = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let section_number = i16::from_le_bytes([data[offset + 12], data[offset + 13]]);
let sym_type = u16::from_le_bytes([data[offset + 14], data[offset + 15]]);
let storage_class = data[offset + 16];
let number_of_aux_symbols = data[offset + 17];
Some(Self {
name_short,
name_long,
value,
section_number,
sym_type,
storage_class,
number_of_aux_symbols,
})
}
pub fn write(&self, buf: &mut [u8], offset: usize) {
buf[offset..offset + 8].copy_from_slice(&self.name_short);
buf[offset + 8..offset + 12].copy_from_slice(&self.value.to_le_bytes());
buf[offset + 12..offset + 14].copy_from_slice(&self.section_number.to_le_bytes());
buf[offset + 14..offset + 16].copy_from_slice(&self.sym_type.to_le_bytes());
buf[offset + 16] = self.storage_class;
buf[offset + 17] = self.number_of_aux_symbols;
}
pub fn resolve_name(&mut self, string_table: &[u8]) -> String {
if self.name_short[0] == 0
&& self.name_short[1] == 0
&& self.name_short[2] == 0
&& self.name_short[3] == 0
{
let offset = u32::from_be_bytes([
self.name_short[4],
self.name_short[5],
self.name_short[6],
self.name_short[7],
]) as usize;
if offset < string_table.len() {
let remaining = &string_table[offset..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
self.name_long = String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
self.name_long.clone()
}
pub fn storage_class_name(&self) -> &str {
match self.storage_class {
IMAGE_SYM_CLASS_NULL => "NULL",
IMAGE_SYM_CLASS_AUTOMATIC => "AUTOMATIC",
IMAGE_SYM_CLASS_EXTERNAL => "EXTERNAL",
IMAGE_SYM_CLASS_STATIC => "STATIC",
IMAGE_SYM_CLASS_REGISTER => "REGISTER",
IMAGE_SYM_CLASS_EXTERNAL_DEF => "EXTERNAL_DEF",
IMAGE_SYM_CLASS_LABEL => "LABEL",
IMAGE_SYM_CLASS_UNDEFINED_LABEL => "UNDEFINED_LABEL",
IMAGE_SYM_CLASS_MEMBER_OF_STRUCT => "MEMBER_OF_STRUCT",
IMAGE_SYM_CLASS_ARGUMENT => "ARGUMENT",
IMAGE_SYM_CLASS_STRUCT_TAG => "STRUCT_TAG",
IMAGE_SYM_CLASS_MEMBER_OF_UNION => "MEMBER_OF_UNION",
IMAGE_SYM_CLASS_UNION_TAG => "UNION_TAG",
IMAGE_SYM_CLASS_TYPE_DEFINITION => "TYPE_DEFINITION",
IMAGE_SYM_CLASS_UNDEFINED_STATIC => "UNDEFINED_STATIC",
IMAGE_SYM_CLASS_ENUM_TAG => "ENUM_TAG",
IMAGE_SYM_CLASS_MEMBER_OF_ENUM => "MEMBER_OF_ENUM",
IMAGE_SYM_CLASS_REGISTER_PARAM => "REGISTER_PARAM",
IMAGE_SYM_CLASS_BIT_FIELD => "BIT_FIELD",
IMAGE_SYM_CLASS_FAR_EXTERNAL => "FAR_EXTERNAL",
IMAGE_SYM_CLASS_BLOCK => "BLOCK",
IMAGE_SYM_CLASS_FUNCTION => "FUNCTION",
IMAGE_SYM_CLASS_END_OF_STRUCT => "END_OF_STRUCT",
IMAGE_SYM_CLASS_FILE => "FILE",
IMAGE_SYM_CLASS_SECTION => "SECTION",
IMAGE_SYM_CLASS_WEAK_EXTERNAL => "WEAK_EXTERNAL",
IMAGE_SYM_CLASS_CLR_TOKEN => "CLR_TOKEN",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86PEBinary {
pub data: Vec<u8>,
pub is_64bit: bool,
pub dos_header: X86PEDosHeader,
pub pe_signature_offset: usize,
pub coff_header: X86PECoffHeader,
pub optional_header: X86PEOptionalHeader,
pub section_headers: Vec<X86PESectionHeader>,
pub symbols: Vec<X86PESymbol>,
pub string_table: Vec<u8>,
}
impl X86PEBinary {
pub fn read(data: &[u8]) -> Self {
let data = data.to_vec();
let dos_header = X86PEDosHeader::read(&data).unwrap_or_else(X86PEDosHeader::new_default);
let pe_signature_offset = dos_header.e_lfanew as usize;
let coff_header = if pe_signature_offset + 4 + 20 <= data.len() {
let sig = u32::from_le_bytes([
data[pe_signature_offset],
data[pe_signature_offset + 1],
data[pe_signature_offset + 2],
data[pe_signature_offset + 3],
]);
if sig == IMAGE_NT_SIGNATURE {
X86PECoffHeader::read(&data, pe_signature_offset + 4)
.unwrap_or_else(X86PECoffHeader::new_default)
} else {
X86PECoffHeader::new_default()
}
} else {
X86PECoffHeader::new_default()
};
let opt_header_offset = pe_signature_offset + 4 + 20;
let mut is_64bit = false;
let optional_header = if opt_header_offset + 2 <= data.len() {
let magic = u16::from_le_bytes([data[opt_header_offset], data[opt_header_offset + 1]]);
is_64bit = magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
if is_64bit {
X86PEOptionalHeader::read_pe32plus(&data, opt_header_offset)
.unwrap_or_else(|| X86PEOptionalHeader::new_default(is_64bit))
} else {
X86PEOptionalHeader::read_pe32(&data, opt_header_offset)
.unwrap_or_else(|| X86PEOptionalHeader::new_default(is_64bit))
}
} else {
X86PEOptionalHeader::new_default(false)
};
let section_offset = opt_header_offset + coff_header.size_of_optional_header as usize;
let mut section_headers = Vec::new();
for i in 0..coff_header.number_of_sections as usize {
let off = section_offset + i * 40;
if let Some(sh) = X86PESectionHeader::read(&data, off) {
section_headers.push(sh);
}
}
let mut symbols = Vec::new();
let sym_offset = coff_header.pointer_to_symbol_table as usize;
if sym_offset > 0 && sym_offset < data.len() {
let sym_count = coff_header.number_of_symbols as usize;
let sizeof_sym = 18usize;
let mut i = 0;
while i < sym_count {
let off = sym_offset + i * sizeof_sym;
if let Some(sym) = X86PESymbol::read(&data, off) {
let aux_count = sym.number_of_aux_symbols as usize;
symbols.push(sym);
i += 1 + aux_count;
} else {
i += 1;
}
}
}
let string_table_offset = sym_offset + coff_header.number_of_symbols as usize * 18;
let string_table = if string_table_offset + 4 <= data.len() {
let size = u32::from_le_bytes([
data[string_table_offset],
data[string_table_offset + 1],
data[string_table_offset + 2],
data[string_table_offset + 3],
]) as usize;
if string_table_offset + size <= data.len() {
data[string_table_offset..string_table_offset + size].to_vec()
} else {
Vec::new()
}
} else {
Vec::new()
};
if !string_table.is_empty() {
for sym in &mut symbols {
sym.resolve_name(&string_table);
}
}
Self {
data,
is_64bit,
dos_header,
pe_signature_offset,
coff_header,
optional_header,
section_headers,
symbols,
string_table,
}
}
pub fn get_section(&self, index: usize) -> Option<&X86PESectionHeader> {
self.section_headers.get(index)
}
pub fn find_section_by_name(&self, name: &str) -> Option<&X86PESectionHeader> {
self.section_headers.iter().find(|sh| sh.name_str() == name)
}
pub fn read_section_data(&self, sh: &X86PESectionHeader) -> Vec<u8> {
let offset = sh.pointer_to_raw_data as usize;
let size = sh.size_of_raw_data as usize;
if offset + size <= self.data.len() {
self.data[offset..offset + size].to_vec()
} else {
Vec::new()
}
}
pub fn get_entry_point_rva(&self) -> u32 {
self.optional_header.address_of_entry_point
}
pub fn get_image_base(&self) -> u64 {
self.optional_header.image_base
}
pub fn get_entry_point_va(&self) -> u64 {
self.optional_header.image_base + self.optional_header.address_of_entry_point as u64
}
pub fn rva_to_offset(&self, rva: u32) -> Option<usize> {
for sh in &self.section_headers {
if rva >= sh.virtual_address
&& rva < sh.virtual_address + sh.virtual_size.max(sh.size_of_raw_data)
{
let offset_in_section = rva - sh.virtual_address;
if offset_in_section < sh.size_of_raw_data {
return Some(sh.pointer_to_raw_data as usize + offset_in_section as usize);
}
}
}
None
}
pub fn get_data_directory(&self, index: usize) -> Option<&X86PEDataDirectory> {
self.optional_header.data_directory.get(index)
}
pub fn is_dll(&self) -> bool {
self.coff_header.characteristics & IMAGE_FILE_DLL != 0
}
pub fn is_exe(&self) -> bool {
self.coff_header.characteristics & IMAGE_FILE_EXECUTABLE_IMAGE != 0
&& self.coff_header.characteristics & IMAGE_FILE_DLL == 0
}
pub fn subsystem_name(&self) -> &str {
match self.optional_header.subsystem {
IMAGE_SUBSYSTEM_NATIVE => "Native",
IMAGE_SUBSYSTEM_WINDOWS_GUI => "Windows GUI",
IMAGE_SUBSYSTEM_WINDOWS_CUI => "Windows CUI",
IMAGE_SUBSYSTEM_EFI_APPLICATION => "EFI Application",
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER => "EFI Boot Service Driver",
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER => "EFI Runtime Driver",
_ => "Unknown",
}
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str(&format!("=== PE/COFF Binary Summary ===\n"));
s.push_str(&format!(
"Architecture: {}\n",
self.coff_header.machine_name()
));
s.push_str(&format!(
"PE type: {}\n",
if self.is_64bit { "PE32+" } else { "PE32" }
));
s.push_str(&format!(
"Image base: 0x{:016X}\n",
self.optional_header.image_base
));
s.push_str(&format!(
"Entry RVA: 0x{:08X}\n",
self.optional_header.address_of_entry_point
));
s.push_str(&format!(
"Entry VA: 0x{:016X}\n",
self.get_entry_point_va()
));
s.push_str(&format!("Subsystem: {}\n", self.subsystem_name()));
s.push_str(&format!("Is DLL: {}\n", self.is_dll()));
s.push_str(&format!("Sections: {}\n", self.section_headers.len()));
for sh in &self.section_headers {
s.push_str(&format!(
" {:8} vaddr=0x{:08X} vsize=0x{:08X} raw=0x{:08X} rawsize=0x{:08X} flags={}\n",
sh.name_str(),
sh.virtual_address,
sh.virtual_size,
sh.pointer_to_raw_data,
sh.size_of_raw_data,
sh.characteristics_string()
));
}
s.push_str(&format!("Data Directories:\n"));
for (i, dd) in self.optional_header.data_directory.iter().enumerate() {
if dd.virtual_address != 0 || dd.size != 0 {
let name = if i < DATA_DIRECTORY_NAMES.len() {
DATA_DIRECTORY_NAMES[i]
} else {
"Unknown"
};
s.push_str(&format!(
" {:12}: rva=0x{:08X} size=0x{:08X}\n",
name, dd.virtual_address, dd.size
));
}
}
s.push_str(&format!("Symbols: {}\n", self.symbols.len()));
s
}
}
pub const MH_MAGIC: u32 = 0xFEEDFACE;
pub const MH_CIGAM: u32 = 0xCEFAEDFE;
pub const MH_MAGIC_64: u32 = 0xFEEDFACF;
pub const MH_CIGAM_64: u32 = 0xCFFAEDFE;
pub const CPU_ARCH_ABI64: u32 = 0x01000000;
pub const CPU_TYPE_ANY: u32 = !0;
pub const CPU_TYPE_VAX: u32 = 1;
pub const CPU_TYPE_MC680X0: u32 = 6;
pub const CPU_TYPE_I386: u32 = 7;
pub const CPU_TYPE_X86_64: u32 = CPU_TYPE_I386 | CPU_ARCH_ABI64;
pub const CPU_TYPE_MIPS: u32 = 8;
pub const CPU_TYPE_MC98000: u32 = 10;
pub const CPU_TYPE_HPPA: u32 = 11;
pub const CPU_TYPE_ARM: u32 = 12;
pub const CPU_TYPE_ARM64: u32 = CPU_TYPE_ARM | CPU_ARCH_ABI64;
pub const CPU_TYPE_MC88000: u32 = 13;
pub const CPU_TYPE_SPARC: u32 = 14;
pub const CPU_TYPE_I860: u32 = 15;
pub const CPU_TYPE_POWERPC: u32 = 18;
pub const CPU_TYPE_POWERPC64: u32 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64;
pub const MH_OBJECT: u32 = 0x1;
pub const MH_EXECUTE: u32 = 0x2;
pub const MH_FVMLIB: u32 = 0x3;
pub const MH_CORE: u32 = 0x4;
pub const MH_PRELOAD: u32 = 0x5;
pub const MH_DYLIB: u32 = 0x6;
pub const MH_DYLINKER: u32 = 0x7;
pub const MH_BUNDLE: u32 = 0x8;
pub const MH_DYLIB_STUB: u32 = 0x9;
pub const MH_DSYM: u32 = 0xA;
pub const MH_KEXT_BUNDLE: u32 = 0xB;
pub const MH_NOUNDEFS: u32 = 0x1;
pub const MH_INCRLINK: u32 = 0x2;
pub const MH_DYLDLINK: u32 = 0x4;
pub const MH_BINDATLOAD: u32 = 0x8;
pub const MH_PREBOUND: u32 = 0x10;
pub const MH_SPLIT_SEGS: u32 = 0x20;
pub const MH_LAZY_INIT: u32 = 0x40;
pub const MH_TWOLEVEL: u32 = 0x80;
pub const MH_FORCE_FLAT: u32 = 0x100;
pub const MH_NOMULTIDEFS: u32 = 0x200;
pub const MH_NOFIXPREBINDING: u32 = 0x400;
pub const MH_PREBINDABLE: u32 = 0x800;
pub const MH_ALLMODSBOUND: u32 = 0x1000;
pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000;
pub const MH_CANONICAL: u32 = 0x4000;
pub const MH_WEAK_DEFINES: u32 = 0x8000;
pub const MH_BINDS_TO_WEAK: u32 = 0x10000;
pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000;
pub const MH_ROOT_SAFE: u32 = 0x40000;
pub const MH_SETUID_SAFE: u32 = 0x80000;
pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x100000;
pub const MH_PIE: u32 = 0x200000;
pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x400000;
pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x800000;
pub const MH_NO_HEAP_EXECUTION: u32 = 0x1000000;
pub const MH_APP_EXTENSION_SAFE: u32 = 0x02000000;
pub const LC_REQ_DYLD: u32 = 0x80000000;
pub const LC_SEGMENT: u32 = 0x1;
pub const LC_SYMTAB: u32 = 0x2;
pub const LC_SYMSEG: u32 = 0x3;
pub const LC_THREAD: u32 = 0x4;
pub const LC_UNIXTHREAD: u32 = 0x5;
pub const LC_LOADFVMLIB: u32 = 0x6;
pub const LC_IDFVMLIB: u32 = 0x7;
pub const LC_IDENT: u32 = 0x8;
pub const LC_FVMFILE: u32 = 0x9;
pub const LC_PREPAGE: u32 = 0xA;
pub const LC_DYSYMTAB: u32 = 0xB;
pub const LC_LOAD_DYLIB: u32 = 0xC;
pub const LC_ID_DYLIB: u32 = 0xD;
pub const LC_LOAD_DYLINKER: u32 = 0xE;
pub const LC_ID_DYLINKER: u32 = 0xF;
pub const LC_PREBOUND_DYLIB: u32 = 0x10;
pub const LC_ROUTINES: u32 = 0x11;
pub const LC_SUB_FRAMEWORK: u32 = 0x12;
pub const LC_SUB_UMBRELLA: u32 = 0x13;
pub const LC_SUB_CLIENT: u32 = 0x14;
pub const LC_SUB_LIBRARY: u32 = 0x15;
pub const LC_TWOLEVEL_HINTS: u32 = 0x16;
pub const LC_PREBIND_CKSUM: u32 = 0x17;
pub const LC_LOAD_WEAK_DYLIB: u32 = 0x18 | LC_REQ_DYLD;
pub const LC_SEGMENT_64: u32 = 0x19;
pub const LC_ROUTINES_64: u32 = 0x1A;
pub const LC_UUID: u32 = 0x1B;
pub const LC_RPATH: u32 = 0x1C | LC_REQ_DYLD;
pub const LC_CODE_SIGNATURE: u32 = 0x1D;
pub const LC_SEGMENT_SPLIT_INFO: u32 = 0x1E;
pub const LC_REEXPORT_DYLIB: u32 = 0x1F | LC_REQ_DYLD;
pub const LC_LAZY_LOAD_DYLIB: u32 = 0x20;
pub const LC_ENCRYPTION_INFO: u32 = 0x21;
pub const LC_DYLD_INFO: u32 = 0x22;
pub const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD;
pub const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD;
pub const LC_VERSION_MIN_MACOSX: u32 = 0x24;
pub const LC_VERSION_MIN_IPHONEOS: u32 = 0x25;
pub const LC_FUNCTION_STARTS: u32 = 0x26;
pub const LC_DYLD_ENVIRONMENT: u32 = 0x27;
pub const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD;
pub const LC_DATA_IN_CODE: u32 = 0x29;
pub const LC_SOURCE_VERSION: u32 = 0x2A;
pub const LC_DYLIB_CODE_SIGN_DRS: u32 = 0x2B;
pub const LC_ENCRYPTION_INFO_64: u32 = 0x2C;
pub const LC_LINKER_OPTION: u32 = 0x2D;
pub const LC_LINKER_OPTIMIZATION_HINT: u32 = 0x2E;
pub const LC_VERSION_MIN_TVOS: u32 = 0x2F;
pub const LC_VERSION_MIN_WATCHOS: u32 = 0x30;
pub const LC_NOTE: u32 = 0x31;
pub const LC_BUILD_VERSION: u32 = 0x32;
pub const LC_DYLD_EXPORTS_TRIE: u32 = 0x33 | LC_REQ_DYLD;
pub const LC_DYLD_CHAINED_FIXUPS: u32 = 0x34 | LC_REQ_DYLD;
pub const LC_FILESET_ENTRY: u32 = 0x35 | LC_REQ_DYLD;
pub const VM_PROT_NONE: u32 = 0x0;
pub const VM_PROT_READ: u32 = 0x1;
pub const VM_PROT_WRITE: u32 = 0x2;
pub const VM_PROT_EXECUTE: u32 = 0x4;
pub const VM_PROT_DEFAULT: u32 = VM_PROT_READ | VM_PROT_WRITE;
pub const VM_PROT_ALL: u32 = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
pub const S_REGULAR: u32 = 0x0;
pub const S_ZEROFILL: u32 = 0x1;
pub const S_CSTRING_LITERALS: u32 = 0x2;
pub const S_4BYTE_LITERALS: u32 = 0x3;
pub const S_8BYTE_LITERALS: u32 = 0x4;
pub const S_LITERAL_POINTERS: u32 = 0x5;
pub const S_NON_LAZY_SYMBOL_POINTERS: u32 = 0x6;
pub const S_LAZY_SYMBOL_POINTERS: u32 = 0x7;
pub const S_SYMBOL_STUBS: u32 = 0x8;
pub const S_MOD_INIT_FUNC_POINTERS: u32 = 0x9;
pub const S_MOD_TERM_FUNC_POINTERS: u32 = 0xA;
pub const S_COALESCED: u32 = 0xB;
pub const S_GB_ZEROFILL: u32 = 0xC;
pub const S_INTERPOSING: u32 = 0xD;
pub const S_16BYTE_LITERALS: u32 = 0xE;
pub const S_DTRACE_DOF: u32 = 0xF;
pub const S_LAZY_DYLIB_SYMBOL_POINTERS: u32 = 0x10;
pub const S_THREAD_LOCAL_REGULAR: u32 = 0x11;
pub const S_THREAD_LOCAL_ZEROFILL: u32 = 0x12;
pub const S_THREAD_LOCAL_VARIABLES: u32 = 0x13;
pub const S_THREAD_LOCAL_VARIABLE_POINTERS: u32 = 0x14;
pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: u32 = 0x15;
pub const S_ATTR_PURE_INSTRUCTIONS: u32 = 0x80000000;
pub const S_ATTR_NO_TOC: u32 = 0x40000000;
pub const S_ATTR_STRIP_STATIC_SYMS: u32 = 0x20000000;
pub const S_ATTR_NO_DEAD_STRIP: u32 = 0x10000000;
pub const S_ATTR_LIVE_SUPPORT: u32 = 0x08000000;
pub const S_ATTR_SELF_MODIFYING_CODE: u32 = 0x04000000;
pub const S_ATTR_DEBUG: u32 = 0x02000000;
pub const S_ATTR_SOME_INSTRUCTIONS: u32 = 0x00000400;
pub const S_ATTR_EXT_RELOC: u32 = 0x00000200;
pub const S_ATTR_LOC_RELOC: u32 = 0x00000100;
pub const N_STAB: u8 = 0xE0;
pub const N_PEXT: u8 = 0x10;
pub const N_TYPE: u8 = 0x0E;
pub const N_EXT: u8 = 0x01;
pub const N_UNDF: u8 = 0x0;
pub const N_ABS: u8 = 0x2;
pub const N_SECT: u8 = 0xE;
pub const N_PBUD: u8 = 0xC;
pub const N_INDR: u8 = 0xA;
pub const REFERENCE_TYPE: u16 = 0x7;
pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 0x3;
pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 0x4;
pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 0x5;
pub const REFERENCED_DYNAMICALLY: u16 = 0x0010;
pub const N_DESC_DISCARDED: u16 = 0x0020;
pub const N_NO_DEAD_STRIP: u16 = 0x0020;
pub const N_WEAK_REF: u16 = 0x0040;
pub const N_WEAK_DEF: u16 = 0x0080;
pub const N_REF_TO_WEAK: u16 = 0x0080;
pub const N_ARM_THUMB_DEF: u16 = 0x0008;
pub const N_SYMBOL_RESOLVER: u16 = 0x0100;
pub const N_ALT_ENTRY: u16 = 0x0200;
pub const N_COLD_FUNC: u16 = 0x0400;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86MachORelocType {
Unsigned,
Branch,
GOT,
GOTLoad,
Subtractor,
Signed,
Signed1,
Signed2,
Signed4,
TLVP,
Unknown(u32),
}
impl X86MachORelocType {
pub fn from_u32(val: u32) -> Self {
match val {
0 => Self::Unsigned,
1 => Self::Signed,
2 => Self::Branch,
3 => Self::GOTLoad,
4 => Self::GOT,
5 => Self::Subtractor,
6 => Self::Signed1,
7 => Self::Signed2,
8 => Self::Signed4,
9 => Self::TLVP,
_ => Self::Unknown(val),
}
}
pub fn to_u32(&self) -> u32 {
match self {
Self::Unsigned => 0,
Self::Signed => 1,
Self::Branch => 2,
Self::GOTLoad => 3,
Self::GOT => 4,
Self::Subtractor => 5,
Self::Signed1 => 6,
Self::Signed2 => 7,
Self::Signed4 => 8,
Self::TLVP => 9,
Self::Unknown(v) => *v,
}
}
pub fn name(&self) -> &str {
match self {
Self::Unsigned => "X86_64_RELOC_UNSIGNED",
Self::Branch => "X86_64_RELOC_BRANCH",
Self::GOT => "X86_64_RELOC_GOT",
Self::GOTLoad => "X86_64_RELOC_GOT_LOAD",
Self::Subtractor => "X86_64_RELOC_SUBTRACTOR",
Self::Signed => "X86_64_RELOC_SIGNED",
Self::Signed1 => "X86_64_RELOC_SIGNED_1",
Self::Signed2 => "X86_64_RELOC_SIGNED_2",
Self::Signed4 => "X86_64_RELOC_SIGNED_4",
Self::TLVP => "X86_64_RELOC_TLV",
Self::Unknown(_) => "X86_64_RELOC_UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachOHeader {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
pub reserved: u32,
}
impl X86MachOHeader {
pub fn new_default() -> Self {
Self {
magic: MH_MAGIC_64,
cputype: CPU_TYPE_X86_64,
cpusubtype: 3, filetype: MH_EXECUTE,
ncmds: 0,
sizeofcmds: 0,
flags: MH_TWOLEVEL | MH_PIE | MH_NO_HEAP_EXECUTION,
reserved: 0,
}
}
pub fn read(data: &[u8]) -> Option<Self> {
if data.len() < 32 {
return None;
}
let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
let (cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags, reserved) =
if magic == MH_MAGIC_64 || magic == MH_CIGAM_64 {
let is_be = magic == MH_CIGAM_64;
if is_be {
(
u32::from_be_bytes([data[4], data[5], data[6], data[7]]),
u32::from_be_bytes([data[8], data[9], data[10], data[11]]),
u32::from_be_bytes([data[12], data[13], data[14], data[15]]),
u32::from_be_bytes([data[16], data[17], data[18], data[19]]),
u32::from_be_bytes([data[20], data[21], data[22], data[23]]),
u32::from_be_bytes([data[24], data[25], data[26], data[27]]),
u32::from_be_bytes([data[28], data[29], data[30], data[31]]),
)
} else {
(
u32::from_le_bytes([data[4], data[5], data[6], data[7]]),
u32::from_le_bytes([data[8], data[9], data[10], data[11]]),
u32::from_le_bytes([data[12], data[13], data[14], data[15]]),
u32::from_le_bytes([data[16], data[17], data[18], data[19]]),
u32::from_le_bytes([data[20], data[21], data[22], data[23]]),
u32::from_le_bytes([data[24], data[25], data[26], data[27]]),
u32::from_le_bytes([data[28], data[29], data[30], data[31]]),
)
}
} else if magic == MH_MAGIC || magic == MH_CIGAM {
let is_be = magic == MH_CIGAM;
if data.len() < 28 {
return None;
}
if is_be {
(
u32::from_be_bytes([data[4], data[5], data[6], data[7]]),
u32::from_be_bytes([data[8], data[9], data[10], data[11]]),
u32::from_be_bytes([data[12], data[13], data[14], data[15]]),
u32::from_be_bytes([data[16], data[17], data[18], data[19]]),
u32::from_be_bytes([data[20], data[21], data[22], data[23]]),
u32::from_be_bytes([data[24], data[25], data[26], data[27]]),
0u32,
)
} else {
(
u32::from_le_bytes([data[4], data[5], data[6], data[7]]),
u32::from_le_bytes([data[8], data[9], data[10], data[11]]),
u32::from_le_bytes([data[12], data[13], data[14], data[15]]),
u32::from_le_bytes([data[16], data[17], data[18], data[19]]),
u32::from_le_bytes([data[20], data[21], data[22], data[23]]),
u32::from_le_bytes([data[24], data[25], data[26], data[27]]),
0u32,
)
}
} else {
return None;
};
Some(Self {
magic,
cputype,
cpusubtype,
filetype,
ncmds,
sizeofcmds,
flags,
reserved,
})
}
pub fn is_64bit(&self) -> bool {
self.magic == MH_MAGIC_64 || self.magic == MH_CIGAM_64
}
pub fn is_big_endian(&self) -> bool {
self.magic == MH_CIGAM || self.magic == MH_CIGAM_64
}
pub fn cputype_name(&self) -> &str {
match self.cputype {
CPU_TYPE_I386 => "i386",
CPU_TYPE_X86_64 => "x86_64",
_ => "unknown",
}
}
pub fn filetype_name(&self) -> &str {
match self.filetype {
MH_OBJECT => "OBJECT",
MH_EXECUTE => "EXECUTE",
MH_FVMLIB => "FVMLIB",
MH_CORE => "CORE",
MH_PRELOAD => "PRELOAD",
MH_DYLIB => "DYLIB",
MH_DYLINKER => "DYLINKER",
MH_BUNDLE => "BUNDLE",
MH_DYLIB_STUB => "DYLIB_STUB",
MH_DSYM => "DSYM",
MH_KEXT_BUNDLE => "KEXT_BUNDLE",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachOSegmentCommand {
pub cmd: u32,
pub cmdsize: u32,
pub segname: [u8; 16],
pub vmaddr: u64,
pub vmsize: u64,
pub fileoff: u64,
pub filesize: u64,
pub maxprot: u32,
pub initprot: u32,
pub nsects: u32,
pub flags: u32,
}
impl X86MachOSegmentCommand {
pub fn new_default() -> Self {
Self {
cmd: LC_SEGMENT_64,
cmdsize: 72,
segname: [0; 16],
vmaddr: 0,
vmsize: 0,
fileoff: 0,
filesize: 0,
maxprot: VM_PROT_ALL,
initprot: VM_PROT_DEFAULT,
nsects: 0,
flags: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 72 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let mut segname = [0u8; 16];
segname.copy_from_slice(&data[offset + 8..offset + 24]);
let vmaddr = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let vmsize = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let fileoff = u64::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
let filesize = u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let maxprot = u32::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
]);
let initprot = u32::from_le_bytes([
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
let nsects = u32::from_le_bytes([
data[offset + 64],
data[offset + 65],
data[offset + 66],
data[offset + 67],
]);
let flags = u32::from_le_bytes([
data[offset + 68],
data[offset + 69],
data[offset + 70],
data[offset + 71],
]);
Some(Self {
cmd,
cmdsize,
segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
nsects,
flags,
})
}
pub fn segname_str(&self) -> String {
let len = self.segname.iter().position(|&b| b == 0).unwrap_or(16);
String::from_utf8_lossy(&self.segname[..len]).to_string()
}
pub fn set_segname(&mut self, s: &str) {
let bytes = s.as_bytes();
let len = bytes.len().min(16);
self.segname[..len].copy_from_slice(&bytes[..len]);
for i in len..16 {
self.segname[i] = 0;
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachOSection {
pub sectname: [u8; 16],
pub segname: [u8; 16],
pub addr: u64,
pub size: u64,
pub offset: u32,
pub align: u32,
pub reloff: u32,
pub nreloc: u32,
pub flags: u32,
pub reserved1: u32,
pub reserved2: u32,
pub reserved3: u32,
}
impl X86MachOSection {
pub fn new_default() -> Self {
Self {
sectname: [0; 16],
segname: [0; 16],
addr: 0,
size: 0,
offset: 0,
align: 0,
reloff: 0,
nreloc: 0,
flags: 0,
reserved1: 0,
reserved2: 0,
reserved3: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 80 > data.len() {
return None;
}
let mut sectname = [0u8; 16];
sectname.copy_from_slice(&data[offset..offset + 16]);
let mut segname = [0u8; 16];
segname.copy_from_slice(&data[offset + 16..offset + 32]);
let addr = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let size = u64::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
let file_offset = u32::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
]);
let align = u32::from_le_bytes([
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let reloff = u32::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
]);
let nreloc = u32::from_le_bytes([
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
let flags = u32::from_le_bytes([
data[offset + 64],
data[offset + 65],
data[offset + 66],
data[offset + 67],
]);
let reserved1 = u32::from_le_bytes([
data[offset + 68],
data[offset + 69],
data[offset + 70],
data[offset + 71],
]);
let reserved2 = u32::from_le_bytes([
data[offset + 72],
data[offset + 73],
data[offset + 74],
data[offset + 75],
]);
let reserved3 = u32::from_le_bytes([
data[offset + 76],
data[offset + 77],
data[offset + 78],
data[offset + 79],
]);
Some(Self {
sectname,
segname,
addr,
size,
offset: file_offset,
align,
reloff,
nreloc,
flags,
reserved1,
reserved2,
reserved3,
})
}
pub fn sectname_str(&self) -> String {
let len = self.sectname.iter().position(|&b| b == 0).unwrap_or(16);
String::from_utf8_lossy(&self.sectname[..len]).to_string()
}
pub fn segname_str(&self) -> String {
let len = self.segname.iter().position(|&b| b == 0).unwrap_or(16);
String::from_utf8_lossy(&self.segname[..len]).to_string()
}
pub fn section_type(&self) -> &str {
match self.flags & 0xFF {
S_REGULAR => "REGULAR",
S_ZEROFILL => "ZEROFILL",
S_CSTRING_LITERALS => "CSTRING_LITERALS",
S_4BYTE_LITERALS => "4BYTE_LITERALS",
S_8BYTE_LITERALS => "8BYTE_LITERALS",
S_LITERAL_POINTERS => "LITERAL_POINTERS",
S_NON_LAZY_SYMBOL_POINTERS => "NON_LAZY_SYMBOL_POINTERS",
S_LAZY_SYMBOL_POINTERS => "LAZY_SYMBOL_POINTERS",
S_SYMBOL_STUBS => "SYMBOL_STUBS",
S_MOD_INIT_FUNC_POINTERS => "MOD_INIT_FUNC_POINTERS",
S_MOD_TERM_FUNC_POINTERS => "MOD_TERM_FUNC_POINTERS",
S_COALESCED => "COALESCED",
S_GB_ZEROFILL => "GB_ZEROFILL",
S_INTERPOSING => "INTERPOSING",
S_16BYTE_LITERALS => "16BYTE_LITERALS",
S_DTRACE_DOF => "DTRACE_DOF",
S_LAZY_DYLIB_SYMBOL_POINTERS => "LAZY_DYLIB_SYMBOL_POINTERS",
S_THREAD_LOCAL_REGULAR => "THREAD_LOCAL_REGULAR",
S_THREAD_LOCAL_ZEROFILL => "THREAD_LOCAL_ZEROFILL",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachONlist64 {
pub n_strx: u32,
pub n_type: u8,
pub n_sect: u8,
pub n_desc: u16,
pub n_value: u64,
}
impl X86MachONlist64 {
pub fn new_default() -> Self {
Self {
n_strx: 0,
n_type: 0,
n_sect: 0,
n_desc: 0,
n_value: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let n_strx = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let n_type = data[offset + 4];
let n_sect = data[offset + 5];
let n_desc = u16::from_le_bytes([data[offset + 6], data[offset + 7]]);
let n_value = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
n_strx,
n_type,
n_sect,
n_desc,
n_value,
})
}
pub fn nlist_type(&self) -> u8 {
self.n_type & N_TYPE
}
pub fn is_stab(&self) -> bool {
self.n_type & N_STAB != 0
}
pub fn is_pext(&self) -> bool {
self.n_type & N_PEXT != 0
}
pub fn is_ext(&self) -> bool {
self.n_type & N_EXT != 0
}
pub fn type_name(&self) -> &str {
if self.is_stab() {
return "STAB";
}
match self.nlist_type() {
N_UNDF => "UNDF",
N_ABS => "ABS",
N_SECT => "SECT",
N_PBUD => "PBUD",
N_INDR => "INDR",
_ => "UNKNOWN",
}
}
pub fn binding(&self) -> &str {
if self.is_stab() {
return "STAB";
}
if self.n_type & N_EXT != 0 {
if self.n_desc & N_WEAK_DEF != 0 {
"WEAK"
} else if self.n_desc & N_WEAK_REF != 0 {
"WEAK_REF"
} else {
"GLOBAL"
}
} else {
"LOCAL"
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachORelocation {
pub r_address: i32,
pub r_symbolnum: u32,
pub r_pcrel: bool,
pub r_length: u8,
pub r_extern: bool,
pub r_type: u8,
}
impl X86MachORelocation {
pub fn new_default() -> Self {
Self {
r_address: 0,
r_symbolnum: 0,
r_pcrel: false,
r_length: 2,
r_extern: false,
r_type: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 8 > data.len() {
return None;
}
let r_address = i32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let bits = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let r_symbolnum = bits & 0x00FFFFFF;
let r_pcrel = (bits & 0x01000000) != 0;
let r_length = ((bits >> 25) & 0x3) as u8;
let r_extern = (bits & 0x10000000) != 0;
let r_type = ((bits >> 28) & 0xF) as u8;
Some(Self {
r_address,
r_symbolnum,
r_pcrel,
r_length,
r_extern,
r_type,
})
}
pub fn length_bytes(&self) -> u8 {
1u8 << self.r_length
}
pub fn reloc_type_name(&self) -> String {
match self.r_type {
0 => "X86_64_RELOC_UNSIGNED".to_string(),
1 => "X86_64_RELOC_SIGNED".to_string(),
2 => "X86_64_RELOC_BRANCH".to_string(),
3 => "X86_64_RELOC_GOT_LOAD".to_string(),
4 => "X86_64_RELOC_GOT".to_string(),
5 => "X86_64_RELOC_SUBTRACTOR".to_string(),
6 => "X86_64_RELOC_SIGNED_1".to_string(),
7 => "X86_64_RELOC_SIGNED_2".to_string(),
8 => "X86_64_RELOC_SIGNED_4".to_string(),
9 => "X86_64_RELOC_TLV".to_string(),
_ => format!("X86_64_RELOC_UNKNOWN({})", self.r_type),
}
}
}
#[derive(Debug, Clone)]
pub struct X86MachOUUID {
pub cmd: u32,
pub cmdsize: u32,
pub uuid: [u8; 16],
}
impl X86MachOUUID {
pub fn new_default() -> Self {
Self {
cmd: LC_UUID,
cmdsize: 24,
uuid: [0; 16],
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let mut uuid = [0u8; 16];
uuid.copy_from_slice(&data[offset + 8..offset + 24]);
Some(Self { cmd, cmdsize, uuid })
}
pub fn to_string(&self) -> String {
format!(
"{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
self.uuid[0], self.uuid[1], self.uuid[2], self.uuid[3],
self.uuid[4], self.uuid[5], self.uuid[6], self.uuid[7],
self.uuid[8], self.uuid[9], self.uuid[10], self.uuid[11],
self.uuid[12], self.uuid[13], self.uuid[14], self.uuid[15],
)
}
}
#[derive(Debug, Clone)]
pub struct X86MachOBuildVersion {
pub cmd: u32,
pub cmdsize: u32,
pub platform: u32,
pub minos: u32,
pub sdk: u32,
pub ntools: u32,
}
pub const PLATFORM_MACOS: u32 = 1;
pub const PLATFORM_IOS: u32 = 2;
pub const PLATFORM_TVOS: u32 = 3;
pub const PLATFORM_WATCHOS: u32 = 4;
pub const PLATFORM_BRIDGEOS: u32 = 5;
pub const PLATFORM_MACCATALYST: u32 = 6;
pub const PLATFORM_IOSSIMULATOR: u32 = 7;
pub const PLATFORM_TVOSSIMULATOR: u32 = 8;
pub const PLATFORM_WATCHOSSIMULATOR: u32 = 9;
pub const PLATFORM_DRIVERKIT: u32 = 10;
pub const PLATFORM_XROS: u32 = 11;
pub const PLATFORM_XROSSIMULATOR: u32 = 12;
impl X86MachOBuildVersion {
pub fn new_default() -> Self {
Self {
cmd: LC_BUILD_VERSION,
cmdsize: 24,
platform: PLATFORM_MACOS,
minos: 0x000C0000, sdk: 0x000C0000,
ntools: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let platform = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let minos = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let sdk = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let ntools = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
Some(Self {
cmd,
cmdsize,
platform,
minos,
sdk,
ntools,
})
}
pub fn platform_name(&self) -> &str {
match self.platform {
PLATFORM_MACOS => "macOS",
PLATFORM_IOS => "iOS",
PLATFORM_TVOS => "tvOS",
PLATFORM_WATCHOS => "watchOS",
PLATFORM_BRIDGEOS => "bridgeOS",
PLATFORM_MACCATALYST => "Mac Catalyst",
PLATFORM_IOSSIMULATOR => "iOS Simulator",
PLATFORM_TVOSSIMULATOR => "tvOS Simulator",
PLATFORM_WATCHOSSIMULATOR => "watchOS Simulator",
PLATFORM_DRIVERKIT => "DriverKit",
PLATFORM_XROS => "xrOS",
PLATFORM_XROSSIMULATOR => "xrOS Simulator",
_ => "unknown",
}
}
pub fn format_version(ver: u32) -> String {
let major = ver >> 16;
let minor = (ver >> 8) & 0xFF;
let patch = ver & 0xFF;
format!("{}.{}.{}", major, minor, patch)
}
}
#[derive(Debug, Clone)]
pub struct X86MachODylibCommand {
pub cmd: u32,
pub cmdsize: u32,
pub dylib_name_offset: u32,
pub dylib_timestamp: u32,
pub dylib_current_version: u32,
pub dylib_compatibility_version: u32,
pub name: String,
}
impl X86MachODylibCommand {
pub fn new_default() -> Self {
Self {
cmd: LC_LOAD_DYLIB,
cmdsize: 0,
dylib_name_offset: 24,
dylib_timestamp: 0,
dylib_current_version: 0,
dylib_compatibility_version: 0,
name: String::new(),
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let dylib_name_offset = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let dylib_timestamp = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let dylib_current_version = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let dylib_compatibility_version = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let name_start = offset + dylib_name_offset as usize;
let name_end = offset + cmdsize as usize;
let name = if name_start < name_end && name_end <= data.len() {
let bytes = &data[name_start..name_end];
let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
String::from_utf8_lossy(&bytes[..len]).to_string()
} else {
String::new()
};
Some(Self {
cmd,
cmdsize,
dylib_name_offset,
dylib_timestamp,
dylib_current_version,
dylib_compatibility_version,
name,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOSymtabCommand {
pub cmd: u32,
pub cmdsize: u32,
pub symoff: u32,
pub nsyms: u32,
pub stroff: u32,
pub strsize: u32,
}
impl X86MachOSymtabCommand {
pub fn new_default() -> Self {
Self {
cmd: LC_SYMTAB,
cmdsize: 24,
symoff: 0,
nsyms: 0,
stroff: 0,
strsize: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let symoff = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let nsyms = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let stroff = if offset + 20 <= data.len() {
u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
])
} else {
0
};
let strsize = if offset + 24 <= data.len() {
u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
])
} else {
0
};
Some(Self {
cmd,
cmdsize,
symoff,
nsyms,
stroff,
strsize,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachODysymtabCommand {
pub cmd: u32,
pub cmdsize: u32,
pub ilocalsym: u32,
pub nlocalsym: u32,
pub iextdefsym: u32,
pub nextdefsym: u32,
pub iundefsym: u32,
pub nundefsym: u32,
pub tocoff: u32,
pub ntoc: u32,
pub modtaboff: u32,
pub nmodtab: u32,
pub extrefsymoff: u32,
pub nextrefsyms: u32,
pub indirectsymoff: u32,
pub nindirectsyms: u32,
pub extreloff: u32,
pub nextrel: u32,
pub locreloff: u32,
pub nlocrel: u32,
}
impl X86MachODysymtabCommand {
pub fn new_default() -> Self {
Self {
cmd: LC_DYSYMTAB,
cmdsize: 80,
ilocalsym: 0,
nlocalsym: 0,
iextdefsym: 0,
nextdefsym: 0,
iundefsym: 0,
nundefsym: 0,
tocoff: 0,
ntoc: 0,
modtaboff: 0,
nmodtab: 0,
extrefsymoff: 0,
nextrefsyms: 0,
indirectsymoff: 0,
nindirectsyms: 0,
extreloff: 0,
nextrel: 0,
locreloff: 0,
nlocrel: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 80 > data.len() {
return None;
}
fn u32_at(d: &[u8], o: usize) -> u32 {
u32::from_le_bytes([d[o], d[o + 1], d[o + 2], d[o + 3]])
}
let cmd = u32_at(data, offset);
let cmdsize = u32_at(data, offset + 4);
let ilocalsym = u32_at(data, offset + 8);
let nlocalsym = u32_at(data, offset + 12);
let iextdefsym = u32_at(data, offset + 16);
let nextdefsym = u32_at(data, offset + 20);
let iundefsym = u32_at(data, offset + 24);
let nundefsym = u32_at(data, offset + 28);
let tocoff = u32_at(data, offset + 32);
let ntoc = u32_at(data, offset + 36);
let modtaboff = u32_at(data, offset + 40);
let nmodtab = u32_at(data, offset + 44);
let extrefsymoff = u32_at(data, offset + 48);
let nextrefsyms = u32_at(data, offset + 52);
let indirectsymoff = u32_at(data, offset + 56);
let nindirectsyms = u32_at(data, offset + 60);
let extreloff = u32_at(data, offset + 64);
let nextrel = u32_at(data, offset + 68);
let locreloff = u32_at(data, offset + 72);
let nlocrel = u32_at(data, offset + 76);
Some(Self {
cmd,
cmdsize,
ilocalsym,
nlocalsym,
iextdefsym,
nextdefsym,
iundefsym,
nundefsym,
tocoff,
ntoc,
modtaboff,
nmodtab,
extrefsymoff,
nextrefsyms,
indirectsymoff,
nindirectsyms,
extreloff,
nextrel,
locreloff,
nlocrel,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOMainCommand {
pub cmd: u32,
pub cmdsize: u32,
pub entryoff: u64,
pub stacksize: u64,
}
impl X86MachOMainCommand {
pub fn new_default() -> Self {
Self {
cmd: LC_MAIN,
cmdsize: 24,
entryoff: 0,
stacksize: 0,
}
}
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let entryoff = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let stacksize = u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
Some(Self {
cmd,
cmdsize,
entryoff,
stacksize,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOBinary {
pub data: Vec<u8>,
pub is_big_endian: bool,
pub is_64bit: bool,
pub header: X86MachOHeader,
pub segments: Vec<X86MachOSegmentCommand>,
pub sections: Vec<X86MachOSection>,
pub symbols: Vec<X86MachONlist64>,
pub string_table: Vec<u8>,
pub symtab: Option<X86MachOSymtabCommand>,
pub dysymtab: Option<X86MachODysymtabCommand>,
pub uuid: Option<X86MachOUUID>,
pub build_version: Option<X86MachOBuildVersion>,
pub main_command: Option<X86MachOMainCommand>,
pub dylibs: Vec<X86MachODylibCommand>,
pub id_dylib: Option<X86MachODylibCommand>,
pub entry_point: u64,
}
impl X86MachOBinary {
pub fn read(data: &[u8]) -> Self {
let data = data.to_vec();
let header = X86MachOHeader::read(&data).unwrap_or_else(X86MachOHeader::new_default);
let is_64bit = header.is_64bit();
let is_big_endian = header.is_big_endian();
let mut segments = Vec::new();
let mut sections = Vec::new();
let mut symbols = Vec::new();
let mut string_table = Vec::new();
let mut symtab: Option<X86MachOSymtabCommand> = None;
let mut dysymtab: Option<X86MachODysymtabCommand> = None;
let mut uuid: Option<X86MachOUUID> = None;
let mut build_version: Option<X86MachOBuildVersion> = None;
let mut main_command: Option<X86MachOMainCommand> = None;
let mut dylibs = Vec::new();
let mut id_dylib: Option<X86MachODylibCommand> = None;
let mut entry_point: u64 = 0;
let header_size = if is_64bit { 32 } else { 28 };
let mut cmd_offset = header_size;
for _ in 0..header.ncmds {
if cmd_offset + 8 > data.len() {
break;
}
let cmd = u32::from_le_bytes([
data[cmd_offset],
data[cmd_offset + 1],
data[cmd_offset + 2],
data[cmd_offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[cmd_offset + 4],
data[cmd_offset + 5],
data[cmd_offset + 6],
data[cmd_offset + 7],
]) as usize;
match cmd {
LC_SEGMENT_64 => {
if let Some(seg) = X86MachOSegmentCommand::read(&data, cmd_offset) {
for i in 0..seg.nsects {
let sec_off = cmd_offset + 72 + i as usize * 80;
if let Some(sec) = X86MachOSection::read(&data, sec_off) {
sections.push(sec);
}
}
segments.push(seg);
}
}
LC_SEGMENT => {
if cmd_offset + 56 <= data.len() {
let nsects = u32::from_le_bytes([
data[cmd_offset + 48],
data[cmd_offset + 49],
data[cmd_offset + 50],
data[cmd_offset + 51],
]);
}
}
LC_SYMTAB => {
if let Some(st) = X86MachOSymtabCommand::read(&data, cmd_offset) {
symtab = Some(st.clone());
let symoff = st.symoff as usize;
let nsyms = st.nsyms as usize;
let sym_size = if is_64bit { 16 } else { 12 };
for i in 0..nsyms {
let off = symoff + i * sym_size;
if let Some(nlist) = X86MachONlist64::read(&data, off) {
symbols.push(nlist);
}
}
let stroff = st.stroff as usize;
let strsize = st.strsize as usize;
if stroff + strsize <= data.len() {
string_table = data[stroff..stroff + strsize].to_vec();
}
}
}
LC_DYSYMTAB => {
if let Some(ds) = X86MachODysymtabCommand::read(&data, cmd_offset) {
dysymtab = Some(ds);
}
}
LC_UUID => {
if let Some(u) = X86MachOUUID::read(&data, cmd_offset) {
uuid = Some(u);
}
}
LC_BUILD_VERSION => {
if let Some(bv) = X86MachOBuildVersion::read(&data, cmd_offset) {
build_version = Some(bv);
}
}
LC_VERSION_MIN_MACOSX => {
if build_version.is_none() {
let minos = u32::from_le_bytes([
data[cmd_offset + 8],
data[cmd_offset + 9],
data[cmd_offset + 10],
data[cmd_offset + 11],
]);
let sdk = u32::from_le_bytes([
data[cmd_offset + 12],
data[cmd_offset + 13],
data[cmd_offset + 14],
data[cmd_offset + 15],
]);
build_version = Some(X86MachOBuildVersion {
cmd: LC_BUILD_VERSION,
cmdsize: 24,
platform: PLATFORM_MACOS,
minos,
sdk,
ntools: 0,
});
}
}
LC_MAIN => {
if let Some(mc) = X86MachOMainCommand::read(&data, cmd_offset) {
entry_point = mc.entryoff;
main_command = Some(mc);
}
}
LC_LOAD_DYLIB | LC_LOAD_WEAK_DYLIB | LC_REEXPORT_DYLIB | LC_LAZY_LOAD_DYLIB
| LC_LOAD_UPWARD_DYLIB => {
if let Some(dc) = X86MachODylibCommand::read(&data, cmd_offset) {
dylibs.push(dc);
}
}
LC_ID_DYLIB => {
if let Some(dc) = X86MachODylibCommand::read(&data, cmd_offset) {
id_dylib = Some(dc);
}
}
_ => {}
}
cmd_offset += cmdsize;
if cmdsize == 0 {
break;
} }
Self {
data,
is_big_endian,
is_64bit,
header,
segments,
sections,
symbols,
string_table,
symtab,
dysymtab,
uuid,
build_version,
main_command,
dylibs,
id_dylib,
entry_point,
}
}
pub fn filetype_name(&self) -> &str {
self.header.filetype_name()
}
pub fn cputype_name(&self) -> &str {
self.header.cputype_name()
}
pub fn find_segment(&self, name: &str) -> Option<&X86MachOSegmentCommand> {
self.segments.iter().find(|seg| seg.segname_str() == name)
}
pub fn find_sections_in_segment(&self, segname: &str) -> Vec<&X86MachOSection> {
self.sections
.iter()
.filter(|sec| sec.segname_str() == segname)
.collect()
}
pub fn find_section(&self, segname: &str, sectname: &str) -> Option<&X86MachOSection> {
self.sections
.iter()
.find(|sec| sec.segname_str() == segname && sec.sectname_str() == sectname)
}
pub fn read_section_data(&self, sec: &X86MachOSection) -> Vec<u8> {
let offset = sec.offset as usize;
let size = sec.size as usize;
if offset + size <= self.data.len() {
self.data[offset..offset + size].to_vec()
} else {
Vec::new()
}
}
pub fn get_string(&self, offset: u32) -> String {
let off = offset as usize;
if off < self.string_table.len() {
let remaining = &self.string_table[off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
String::from_utf8_lossy(&remaining[..len]).to_string()
} else {
String::new()
}
}
pub fn get_symbol_name(&self, sym: &X86MachONlist64) -> String {
self.get_string(sym.n_strx)
}
pub fn uuid_string(&self) -> Option<String> {
self.uuid.as_ref().map(|u| u.to_string())
}
pub fn minos_version_string(&self) -> Option<String> {
self.build_version
.as_ref()
.map(|bv| X86MachOBuildVersion::format_version(bv.minos))
}
pub fn sdk_version_string(&self) -> Option<String> {
self.build_version
.as_ref()
.map(|bv| X86MachOBuildVersion::format_version(bv.sdk))
}
pub fn get_dylib_names(&self) -> Vec<String> {
self.dylibs.iter().map(|d| d.name.clone()).collect()
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str("=== Mach-O Binary Summary ===\n");
s.push_str(&format!("64-bit: {}\n", self.is_64bit));
s.push_str(&format!("CPU: {}\n", self.cputype_name()));
s.push_str(&format!("File type: {}\n", self.filetype_name()));
s.push_str(&format!("Load cmds: {}\n", self.header.ncmds));
s.push_str(&format!("Entry point: 0x{:016X}\n", self.entry_point));
if let Some(ref bv) = self.build_version {
s.push_str(&format!("Platform: {}\n", bv.platform_name()));
s.push_str(&format!(
"Min OS: {}\n",
X86MachOBuildVersion::format_version(bv.minos)
));
s.push_str(&format!(
"SDK: {}\n",
X86MachOBuildVersion::format_version(bv.sdk)
));
}
if let Some(ref u) = self.uuid {
s.push_str(&format!("UUID: {}\n", u.to_string()));
}
if let Some(ref id) = self.id_dylib {
s.push_str(&format!("Dylib ID: {}\n", id.name));
}
s.push_str(&format!("Segments: {}\n", self.segments.len()));
for seg in &self.segments {
s.push_str(&format!(
" {:16} vmaddr=0x{:016X} vmsize=0x{:016X} fileoff=0x{:08X} filesize=0x{:08X}\n",
seg.segname_str(),
seg.vmaddr,
seg.vmsize,
seg.fileoff,
seg.filesize
));
}
s.push_str(&format!("Sections: {}\n", self.sections.len()));
for sec in &self.sections {
s.push_str(&format!(
" {:16},{:16} addr=0x{:016X} size=0x{:08X} offset=0x{:08X} align=2^{}\n",
sec.segname_str(),
sec.sectname_str(),
sec.addr,
sec.size,
sec.offset,
sec.align
));
}
s.push_str(&format!("Symbols: {}\n", self.symbols.len()));
if !self.dylibs.is_empty() {
s.push_str("Dylibs:\n");
for d in &self.dylibs {
s.push_str(&format!(" {}\n", d.name));
}
}
s
}
pub fn is_executable(&self) -> bool {
self.header.filetype == MH_EXECUTE
}
pub fn is_dylib(&self) -> bool {
self.header.filetype == MH_DYLIB
}
pub fn is_object(&self) -> bool {
self.header.filetype == MH_OBJECT
}
pub fn load_command_name(cmd: u32) -> &'static str {
match cmd {
LC_SEGMENT => "LC_SEGMENT",
LC_SYMTAB => "LC_SYMTAB",
LC_SYMSEG => "LC_SYMSEG",
LC_THREAD => "LC_THREAD",
LC_UNIXTHREAD => "LC_UNIXTHREAD",
LC_LOADFVMLIB => "LC_LOADFVMLIB",
LC_IDFVMLIB => "LC_IDFVMLIB",
LC_IDENT => "LC_IDENT",
LC_FVMFILE => "LC_FVMFILE",
LC_PREPAGE => "LC_PREPAGE",
LC_DYSYMTAB => "LC_DYSYMTAB",
LC_LOAD_DYLIB => "LC_LOAD_DYLIB",
LC_ID_DYLIB => "LC_ID_DYLIB",
LC_LOAD_DYLINKER => "LC_LOAD_DYLINKER",
LC_ID_DYLINKER => "LC_ID_DYLINKER",
LC_PREBOUND_DYLIB => "LC_PREBOUND_DYLIB",
LC_ROUTINES => "LC_ROUTINES",
LC_SUB_FRAMEWORK => "LC_SUB_FRAMEWORK",
LC_SUB_UMBRELLA => "LC_SUB_UMBRELLA",
LC_SUB_CLIENT => "LC_SUB_CLIENT",
LC_SUB_LIBRARY => "LC_SUB_LIBRARY",
LC_TWOLEVEL_HINTS => "LC_TWOLEVEL_HINTS",
LC_PREBIND_CKSUM => "LC_PREBIND_CKSUM",
LC_LOAD_WEAK_DYLIB => "LC_LOAD_WEAK_DYLIB",
LC_SEGMENT_64 => "LC_SEGMENT_64",
LC_ROUTINES_64 => "LC_ROUTINES_64",
LC_UUID => "LC_UUID",
LC_RPATH => "LC_RPATH",
LC_CODE_SIGNATURE => "LC_CODE_SIGNATURE",
LC_SEGMENT_SPLIT_INFO => "LC_SEGMENT_SPLIT_INFO",
LC_REEXPORT_DYLIB => "LC_REEXPORT_DYLIB",
LC_LAZY_LOAD_DYLIB => "LC_LAZY_LOAD_DYLIB",
LC_ENCRYPTION_INFO => "LC_ENCRYPTION_INFO",
LC_DYLD_INFO => "LC_DYLD_INFO",
LC_DYLD_INFO_ONLY => "LC_DYLD_INFO_ONLY",
LC_LOAD_UPWARD_DYLIB => "LC_LOAD_UPWARD_DYLIB",
LC_VERSION_MIN_MACOSX => "LC_VERSION_MIN_MACOSX",
LC_VERSION_MIN_IPHONEOS => "LC_VERSION_MIN_IPHONEOS",
LC_FUNCTION_STARTS => "LC_FUNCTION_STARTS",
LC_DYLD_ENVIRONMENT => "LC_DYLD_ENVIRONMENT",
LC_MAIN => "LC_MAIN",
LC_DATA_IN_CODE => "LC_DATA_IN_CODE",
LC_SOURCE_VERSION => "LC_SOURCE_VERSION",
LC_DYLIB_CODE_SIGN_DRS => "LC_DYLIB_CODE_SIGN_DRS",
LC_ENCRYPTION_INFO_64 => "LC_ENCRYPTION_INFO_64",
LC_LINKER_OPTION => "LC_LINKER_OPTION",
LC_LINKER_OPTIMIZATION_HINT => "LC_LINKER_OPTIMIZATION_HINT",
LC_VERSION_MIN_TVOS => "LC_VERSION_MIN_TVOS",
LC_VERSION_MIN_WATCHOS => "LC_VERSION_MIN_WATCHOS",
LC_NOTE => "LC_NOTE",
LC_BUILD_VERSION => "LC_BUILD_VERSION",
LC_DYLD_EXPORTS_TRIE => "LC_DYLD_EXPORTS_TRIE",
LC_DYLD_CHAINED_FIXUPS => "LC_DYLD_CHAINED_FIXUPS",
LC_FILESET_ENTRY => "LC_FILESET_ENTRY",
_ => "LC_UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86PEImportDescriptor {
pub original_first_thunk: u32,
pub time_date_stamp: u32,
pub forwarder_chain: u32,
pub name_rva: u32,
pub first_thunk: u32,
pub name: String,
}
impl X86PEImportDescriptor {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 20 > data.len() {
return None;
}
let original_first_thunk = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let time_date_stamp = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let forwarder_chain = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let name_rva = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let first_thunk = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
Some(Self {
original_first_thunk,
time_date_stamp,
forwarder_chain,
name_rva,
first_thunk,
name: String::new(),
})
}
pub fn resolve_name(&mut self, pe: &X86PEBinary) {
if self.name_rva != 0 {
if let Some(file_off) = pe.rva_to_offset(self.name_rva) {
if file_off < pe.data.len() {
let remaining = &pe.data[file_off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
self.name = String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct X86PEThunkData {
pub address: u64,
pub is_ordinal: bool,
pub ordinal: u16,
pub hint: u16,
pub name: String,
}
impl X86PEThunkData {
pub fn read64(data: &[u8], offset: usize) -> Option<Self> {
if offset + 8 > data.len() {
return None;
}
let address = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let is_ordinal = (address & 0x8000000000000000) != 0;
let ordinal = if is_ordinal {
(address & 0xFFFF) as u16
} else {
0
};
Some(Self {
address,
is_ordinal,
ordinal,
hint: 0,
name: String::new(),
})
}
pub fn read32(data: &[u8], offset: usize) -> Option<Self> {
if offset + 4 > data.len() {
return None;
}
let address = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
let is_ordinal = (address & 0x80000000) != 0;
let ordinal = if is_ordinal {
(address & 0xFFFF) as u16
} else {
0
};
Some(Self {
address,
is_ordinal,
ordinal,
hint: 0,
name: String::new(),
})
}
pub fn resolve_hint_name(&mut self, pe: &X86PEBinary, name_rva: u32) {
if self.is_ordinal {
return;
}
let rva = (self.address & 0x7FFFFFFF) as u32;
let hint_name_offset = rva + 2; if let Some(file_off) = pe.rva_to_offset(hint_name_offset) {
if file_off < pe.data.len() {
let remaining = &pe.data[file_off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
self.name = String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
let hint_off = (self.address & 0x7FFFFFFF) as u32;
if let Some(file_off) = pe.rva_to_offset(hint_off) {
if file_off + 2 <= pe.data.len() {
self.hint = u16::from_le_bytes([pe.data[file_off], pe.data[file_off + 1]]);
}
}
}
}
#[derive(Debug, Clone)]
pub struct X86PEImportDirectory {
pub descriptors: Vec<X86PEImportDescriptor>,
pub imported_symbols: Vec<(String, String)>,
}
impl X86PEImportDirectory {
pub fn parse(pe: &X86PEBinary) -> Option<Self> {
let import_dd = pe.get_data_directory(IMAGE_DIRECTORY_ENTRY_IMPORT)?;
if import_dd.virtual_address == 0 {
return None;
}
let offset = pe.rva_to_offset(import_dd.virtual_address)?;
let mut descriptors = Vec::new();
let mut imported_symbols = Vec::new();
let mut pos = offset;
loop {
if let Some(desc) = X86PEImportDescriptor::read(&pe.data, pos) {
if desc.original_first_thunk == 0 && desc.first_thunk == 0 && desc.name_rva == 0 {
break;
}
descriptors.push(desc);
pos += 20;
} else {
break;
}
}
for desc in &mut descriptors {
desc.resolve_name(pe);
let thunk_rva = if desc.original_first_thunk != 0 {
desc.original_first_thunk
} else {
desc.first_thunk
};
if thunk_rva != 0 {
if let Some(thunk_off) = pe.rva_to_offset(thunk_rva) {
let mut tp = thunk_off;
loop {
let thunk = if pe.is_64bit {
X86PEThunkData::read64(&pe.data, tp)
} else {
X86PEThunkData::read32(&pe.data, tp)
};
match thunk {
Some(ref t) if t.address == 0 => break,
Some(mut t) => {
t.resolve_hint_name(pe, thunk_rva);
if !t.name.is_empty() {
imported_symbols.push((desc.name.clone(), t.name.clone()));
} else if t.is_ordinal {
imported_symbols
.push((desc.name.clone(), format!("#{}", t.ordinal)));
}
tp += if pe.is_64bit { 8 } else { 4 };
}
None => break,
}
}
}
}
}
Some(Self {
descriptors,
imported_symbols,
})
}
}
#[derive(Debug, Clone)]
pub struct X86PEExportDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub name_rva: u32,
pub base: u32,
pub number_of_functions: u32,
pub number_of_names: u32,
pub address_of_functions: u32,
pub address_of_names: u32,
pub address_of_name_ordinals: u32,
pub dll_name: String,
pub exports: Vec<(String, u32)>,
}
impl X86PEExportDirectory {
pub fn parse(pe: &X86PEBinary) -> Option<Self> {
let export_dd = pe.get_data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT)?;
if export_dd.virtual_address == 0 {
return None;
}
let offset = pe.rva_to_offset(export_dd.virtual_address)?;
if offset + 40 > pe.data.len() {
return None;
}
let characteristics = u32::from_le_bytes([
pe.data[offset],
pe.data[offset + 1],
pe.data[offset + 2],
pe.data[offset + 3],
]);
let time_date_stamp = u32::from_le_bytes([
pe.data[offset + 4],
pe.data[offset + 5],
pe.data[offset + 6],
pe.data[offset + 7],
]);
let major_version = u16::from_le_bytes([pe.data[offset + 8], pe.data[offset + 9]]);
let minor_version = u16::from_le_bytes([pe.data[offset + 10], pe.data[offset + 11]]);
let name_rva = u32::from_le_bytes([
pe.data[offset + 12],
pe.data[offset + 13],
pe.data[offset + 14],
pe.data[offset + 15],
]);
let base = u32::from_le_bytes([
pe.data[offset + 16],
pe.data[offset + 17],
pe.data[offset + 18],
pe.data[offset + 19],
]);
let number_of_functions = u32::from_le_bytes([
pe.data[offset + 20],
pe.data[offset + 21],
pe.data[offset + 22],
pe.data[offset + 23],
]);
let number_of_names = u32::from_le_bytes([
pe.data[offset + 24],
pe.data[offset + 25],
pe.data[offset + 26],
pe.data[offset + 27],
]);
let address_of_functions = u32::from_le_bytes([
pe.data[offset + 28],
pe.data[offset + 29],
pe.data[offset + 30],
pe.data[offset + 31],
]);
let address_of_names = u32::from_le_bytes([
pe.data[offset + 32],
pe.data[offset + 33],
pe.data[offset + 34],
pe.data[offset + 35],
]);
let address_of_name_ordinals = u32::from_le_bytes([
pe.data[offset + 36],
pe.data[offset + 37],
pe.data[offset + 38],
pe.data[offset + 39],
]);
let mut dll_name = String::new();
if let Some(name_off) = pe.rva_to_offset(name_rva) {
if name_off < pe.data.len() {
let remaining = &pe.data[name_off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
dll_name = String::from_utf8_lossy(&remaining[..len]).to_string();
}
}
let mut exports = Vec::new();
if let Some(func_off) = pe.rva_to_offset(address_of_functions) {
if let Some(name_off) = pe.rva_to_offset(address_of_names) {
if let Some(ord_off) = pe.rva_to_offset(address_of_name_ordinals) {
for i in 0..number_of_names as usize {
let name_entry = name_off + i * 4;
let ord_entry = ord_off + i * 2;
if name_entry + 4 <= pe.data.len() && ord_entry + 2 <= pe.data.len() {
let name_rva_val = u32::from_le_bytes([
pe.data[name_entry],
pe.data[name_entry + 1],
pe.data[name_entry + 2],
pe.data[name_entry + 3],
]);
let ordinal_idx =
u16::from_le_bytes([pe.data[ord_entry], pe.data[ord_entry + 1]]);
let export_rva_off = func_off + (ordinal_idx as usize) * 4;
let export_rva = if export_rva_off + 4 <= pe.data.len() {
u32::from_le_bytes([
pe.data[export_rva_off],
pe.data[export_rva_off + 1],
pe.data[export_rva_off + 2],
pe.data[export_rva_off + 3],
])
} else {
0
};
if let Some(n_off) = pe.rva_to_offset(name_rva_val) {
if n_off < pe.data.len() {
let remaining = &pe.data[n_off..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
let name =
String::from_utf8_lossy(&remaining[..len]).to_string();
exports.push((name, base + ordinal_idx as u32));
}
}
}
}
}
}
}
Some(Self {
characteristics,
time_date_stamp,
major_version,
minor_version,
name_rva,
base,
number_of_functions,
number_of_names,
address_of_functions,
address_of_names,
address_of_name_ordinals,
dll_name,
exports,
})
}
}
#[derive(Debug, Clone)]
pub struct X86PEResourceDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub number_of_named_entries: u16,
pub number_of_id_entries: u16,
pub entries: Vec<X86PEResourceDirEntry>,
}
#[derive(Debug, Clone)]
pub struct X86PEResourceDirEntry {
pub name_id: u32,
pub offset_to_data: u32,
pub is_directory: bool,
pub is_string_name: bool,
pub name_string: String,
pub child: Option<Box<X86PEResourceDirectory>>,
pub data_entry: Option<X86PEResourceDataEntry>,
}
#[derive(Debug, Clone)]
pub struct X86PEResourceDataEntry {
pub offset_to_data: u32,
pub size: u32,
pub code_page: u32,
pub reserved: u32,
}
impl X86PEResourceDirectory {
pub fn parse(pe: &X86PEBinary) -> Option<Self> {
let res_dd = pe.get_data_directory(IMAGE_DIRECTORY_ENTRY_RESOURCE)?;
if res_dd.virtual_address == 0 {
return None;
}
let res_base = pe.rva_to_offset(res_dd.virtual_address)?;
Self::parse_tree(pe, res_base, res_base)
}
fn parse_tree(pe: &X86PEBinary, offset: usize, res_base: usize) -> Option<Self> {
if offset + 16 > pe.data.len() {
return None;
}
let characteristics = u32::from_le_bytes([
pe.data[offset],
pe.data[offset + 1],
pe.data[offset + 2],
pe.data[offset + 3],
]);
let time_date_stamp = u32::from_le_bytes([
pe.data[offset + 4],
pe.data[offset + 5],
pe.data[offset + 6],
pe.data[offset + 7],
]);
let major_version = u16::from_le_bytes([pe.data[offset + 8], pe.data[offset + 9]]);
let minor_version = u16::from_le_bytes([pe.data[offset + 10], pe.data[offset + 11]]);
let number_of_named_entries =
u16::from_le_bytes([pe.data[offset + 12], pe.data[offset + 13]]);
let number_of_id_entries = u16::from_le_bytes([pe.data[offset + 14], pe.data[offset + 15]]);
let total_entries = number_of_named_entries as usize + number_of_id_entries as usize;
let mut entries = Vec::new();
for i in 0..total_entries {
let entry_off = offset + 16 + i * 8;
if entry_off + 8 > pe.data.len() {
break;
}
let name_id = u32::from_le_bytes([
pe.data[entry_off],
pe.data[entry_off + 1],
pe.data[entry_off + 2],
pe.data[entry_off + 3],
]);
let offset_to_data = u32::from_le_bytes([
pe.data[entry_off + 4],
pe.data[entry_off + 5],
pe.data[entry_off + 6],
pe.data[entry_off + 7],
]);
let is_directory = (offset_to_data & 0x80000000) != 0;
let is_string_name = (name_id & 0x80000000) != 0;
let data_offset = (offset_to_data & 0x7FFFFFFF) as usize;
let mut name_string = String::new();
if is_string_name {
let str_off = res_base + (name_id & 0x7FFFFFFF) as usize;
if str_off + 2 <= pe.data.len() {
let len = u16::from_le_bytes([pe.data[str_off], pe.data[str_off + 1]]) as usize;
let str_start = str_off + 2;
if str_start + len * 2 <= pe.data.len() {
let mut s = String::new();
for j in 0..len {
let ch = u16::from_le_bytes([
pe.data[str_start + j * 2],
pe.data[str_start + j * 2 + 1],
]);
if ch != 0 {
s.push(char::from_u32(ch as u32).unwrap_or('?'));
}
}
name_string = s;
}
}
}
let child = if is_directory {
Box::new(Self::parse_tree(pe, res_base + data_offset, res_base)?).into()
} else {
None
};
let data_entry = if !is_directory {
let de_off = res_base + data_offset;
if de_off + 16 <= pe.data.len() {
Some(X86PEResourceDataEntry {
offset_to_data: u32::from_le_bytes([
pe.data[de_off],
pe.data[de_off + 1],
pe.data[de_off + 2],
pe.data[de_off + 3],
]),
size: u32::from_le_bytes([
pe.data[de_off + 4],
pe.data[de_off + 5],
pe.data[de_off + 6],
pe.data[de_off + 7],
]),
code_page: u32::from_le_bytes([
pe.data[de_off + 8],
pe.data[de_off + 9],
pe.data[de_off + 10],
pe.data[de_off + 11],
]),
reserved: u32::from_le_bytes([
pe.data[de_off + 12],
pe.data[de_off + 13],
pe.data[de_off + 14],
pe.data[de_off + 15],
]),
})
} else {
None
}
} else {
None
};
entries.push(X86PEResourceDirEntry {
name_id,
offset_to_data,
is_directory,
is_string_name,
name_string,
child,
data_entry,
});
}
Some(Self {
characteristics,
time_date_stamp,
major_version,
minor_version,
number_of_named_entries,
number_of_id_entries,
entries,
})
}
}
#[derive(Debug, Clone)]
pub struct X86PEDebugDirectoryEntry {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub debug_type: u32,
pub size_of_data: u32,
pub address_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub type_name: String,
}
pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0;
pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1;
pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2;
pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3;
pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7;
pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8;
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10;
pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11;
pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12;
pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13;
pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14;
pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15;
pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16;
impl X86PEDebugDirectoryEntry {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 28 > data.len() {
return None;
}
let characteristics = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let time_date_stamp = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let major_version = u16::from_le_bytes([data[offset + 8], data[offset + 9]]);
let minor_version = u16::from_le_bytes([data[offset + 10], data[offset + 11]]);
let debug_type = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let size_of_data = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let address_of_raw_data = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let pointer_to_raw_data = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let type_name = match debug_type {
IMAGE_DEBUG_TYPE_CODEVIEW => "CODEVIEW".to_string(),
IMAGE_DEBUG_TYPE_COFF => "COFF".to_string(),
IMAGE_DEBUG_TYPE_FPO => "FPO".to_string(),
IMAGE_DEBUG_TYPE_MISC => "MISC".to_string(),
IMAGE_DEBUG_TYPE_POGO => "POGO".to_string(),
IMAGE_DEBUG_TYPE_REPRO => "REPRO".to_string(),
_ => format!("TYPE_{}", debug_type),
};
Some(Self {
characteristics,
time_date_stamp,
major_version,
minor_version,
debug_type,
size_of_data,
address_of_raw_data,
pointer_to_raw_data,
type_name,
})
}
}
pub fn parse_pe_debug_directory(pe: &X86PEBinary) -> Vec<X86PEDebugDirectoryEntry> {
let mut entries = Vec::new();
if let Some(dd) = pe.get_data_directory(IMAGE_DIRECTORY_ENTRY_DEBUG) {
if dd.virtual_address != 0 {
if let Some(off) = pe.rva_to_offset(dd.virtual_address) {
let count = dd.size as usize / 28;
for i in 0..count {
if let Some(entry) = X86PEDebugDirectoryEntry::read(&pe.data, off + i * 28) {
if entry.debug_type == 0 && entry.size_of_data == 0 {
break;
}
entries.push(entry);
}
}
}
}
}
entries
}
#[derive(Debug, Clone)]
pub struct X86PECodeViewInfo {
pub signature: u32,
pub guid: [u8; 16],
pub age: u32,
pub pdb_name: String,
}
pub const CODEVIEW_SIGNATURE_NB10: u32 = 0x3031424E;
pub const CODEVIEW_SIGNATURE_RSDS: u32 = 0x53445352;
impl X86PECodeViewInfo {
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < 4 {
return None;
}
let signature = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
match signature {
CODEVIEW_SIGNATURE_RSDS => {
if data.len() < 24 {
return None;
}
let mut guid = [0u8; 16];
guid.copy_from_slice(&data[4..20]);
let age = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
let pdb_name = if data.len() > 24 {
let remaining = &data[24..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
String::from_utf8_lossy(&remaining[..len]).to_string()
} else {
String::new()
};
Some(Self {
signature,
guid,
age,
pdb_name,
})
}
CODEVIEW_SIGNATURE_NB10 => {
if data.len() < 16 {
return None;
}
let _offset = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
let _timestamp = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
let age = u32::from_le_bytes([data[12], data[13], data[14], data[15]]);
let pdb_name = if data.len() > 16 {
let remaining = &data[16..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
String::from_utf8_lossy(&remaining[..len]).to_string()
} else {
String::new()
};
Some(Self {
signature,
guid: [0; 16],
age,
pdb_name,
})
}
_ => None,
}
}
pub fn guid_string(&self) -> String {
if self.signature == CODEVIEW_SIGNATURE_RSDS {
format!(
"{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
u32::from_le_bytes([self.guid[0], self.guid[1], self.guid[2], self.guid[3]]),
u16::from_le_bytes([self.guid[4], self.guid[5]]),
u16::from_le_bytes([self.guid[6], self.guid[7]]),
self.guid[8],
self.guid[9],
self.guid[10],
self.guid[11],
self.guid[12],
self.guid[13],
self.guid[14],
self.guid[15],
)
} else {
String::new()
}
}
}
#[derive(Debug, Clone)]
pub struct X86PETLSDirectory {
pub start_address_of_raw_data: u64,
pub end_address_of_raw_data: u64,
pub address_of_index: u64,
pub address_of_callbacks: u64,
pub size_of_zero_fill: u32,
pub characteristics: u32,
}
impl X86PETLSDirectory {
pub fn read64(data: &[u8], offset: usize) -> Option<Self> {
if offset + 40 > data.len() {
return None;
}
let start = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let end = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let idx = u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let callbacks = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let size_of_zero_fill = u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]);
let characteristics = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
Some(Self {
start_address_of_raw_data: start,
end_address_of_raw_data: end,
address_of_index: idx,
address_of_callbacks: callbacks,
size_of_zero_fill,
characteristics,
})
}
}
#[derive(Debug, Clone)]
pub struct X86PELoadConfigDirectory {
pub size: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub global_flags_clear: u32,
pub global_flags_set: u32,
pub critical_section_default_timeout: u32,
pub de_commit_free_block_threshold: u64,
pub de_commit_total_free_threshold: u64,
pub lock_prefix_table: u64,
pub maximum_allocation_size: u64,
pub virtual_memory_threshold: u64,
pub process_affinity_mask: u64,
pub process_heap_flags: u32,
pub csd_version: u16,
pub dependent_load_flags: u16,
pub edit_list: u64,
pub security_cookie: u64,
pub se_handler_table: u64,
pub se_handler_count: u64,
pub guard_cf_check_function_pointer: u64,
pub guard_cf_dispatch_function_pointer: u64,
pub guard_cf_function_table: u64,
pub guard_cf_function_count: u64,
pub guard_flags: u32,
}
impl X86PELoadConfigDirectory {
pub fn read64(data: &[u8], offset: usize) -> Option<Self> {
if offset + 4 > data.len() {
return None;
}
let size = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
if size < 64 || offset + size as usize > data.len() {
return None;
}
let time_date_stamp = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let major_version = u16::from_le_bytes([data[offset + 8], data[offset + 9]]);
let minor_version = u16::from_le_bytes([data[offset + 10], data[offset + 11]]);
let global_flags_clear = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let global_flags_set = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let critical_section_default_timeout = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let de_commit_free_block_threshold = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let de_commit_total_free_threshold = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let lock_prefix_table = u64::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
let maximum_allocation_size = u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]);
let virtual_memory_threshold = u64::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]);
let process_affinity_mask = u64::from_le_bytes([
data[offset + 64],
data[offset + 65],
data[offset + 66],
data[offset + 67],
data[offset + 68],
data[offset + 69],
data[offset + 70],
data[offset + 71],
]);
let process_heap_flags = u32::from_le_bytes([
data[offset + 72],
data[offset + 73],
data[offset + 74],
data[offset + 75],
]);
let csd_version = u16::from_le_bytes([data[offset + 76], data[offset + 77]]);
let dependent_load_flags = u16::from_le_bytes([data[offset + 78], data[offset + 79]]);
let edit_list = u64::from_le_bytes([
data[offset + 80],
data[offset + 81],
data[offset + 82],
data[offset + 83],
data[offset + 84],
data[offset + 85],
data[offset + 86],
data[offset + 87],
]);
let security_cookie = u64::from_le_bytes([
data[offset + 88],
data[offset + 89],
data[offset + 90],
data[offset + 91],
data[offset + 92],
data[offset + 93],
data[offset + 94],
data[offset + 95],
]);
let se_handler_table = u64::from_le_bytes([
data[offset + 96],
data[offset + 97],
data[offset + 98],
data[offset + 99],
data[offset + 100],
data[offset + 101],
data[offset + 102],
data[offset + 103],
]);
let se_handler_count = u64::from_le_bytes([
data[offset + 104],
data[offset + 105],
data[offset + 106],
data[offset + 107],
data[offset + 108],
data[offset + 109],
data[offset + 110],
data[offset + 111],
]);
let guard_cf_check_function_pointer = u64::from_le_bytes([
data[offset + 112],
data[offset + 113],
data[offset + 114],
data[offset + 115],
data[offset + 116],
data[offset + 117],
data[offset + 118],
data[offset + 119],
]);
let guard_cf_dispatch_function_pointer = u64::from_le_bytes([
data[offset + 120],
data[offset + 121],
data[offset + 122],
data[offset + 123],
data[offset + 124],
data[offset + 125],
data[offset + 126],
data[offset + 127],
]);
let guard_cf_function_table = u64::from_le_bytes([
data[offset + 128],
data[offset + 129],
data[offset + 130],
data[offset + 131],
data[offset + 132],
data[offset + 133],
data[offset + 134],
data[offset + 135],
]);
let guard_cf_function_count = u64::from_le_bytes([
data[offset + 136],
data[offset + 137],
data[offset + 138],
data[offset + 139],
data[offset + 140],
data[offset + 141],
data[offset + 142],
data[offset + 143],
]);
let guard_flags = if size >= 152 {
u32::from_le_bytes([
data[offset + 144],
data[offset + 145],
data[offset + 146],
data[offset + 147],
])
} else {
0
};
Some(Self {
size,
time_date_stamp,
major_version,
minor_version,
global_flags_clear,
global_flags_set,
critical_section_default_timeout,
de_commit_free_block_threshold,
de_commit_total_free_threshold,
lock_prefix_table,
maximum_allocation_size,
virtual_memory_threshold,
process_affinity_mask,
process_heap_flags,
csd_version,
dependent_load_flags,
edit_list,
security_cookie,
se_handler_table,
se_handler_count,
guard_cf_check_function_pointer,
guard_cf_dispatch_function_pointer,
guard_cf_function_table,
guard_cf_function_count,
guard_flags,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachODyldInfo {
pub cmd: u32,
pub cmdsize: u32,
pub rebase_off: u32,
pub rebase_size: u32,
pub bind_off: u32,
pub bind_size: u32,
pub weak_bind_off: u32,
pub weak_bind_size: u32,
pub lazy_bind_off: u32,
pub lazy_bind_size: u32,
pub export_off: u32,
pub export_size: u32,
}
impl X86MachODyldInfo {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 48 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let rebase_off = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let rebase_size = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let bind_off = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let bind_size = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
let weak_bind_off = u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]);
let weak_bind_size = u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let lazy_bind_off = u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]);
let lazy_bind_size = u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
let export_off = u32::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
]);
let export_size = u32::from_le_bytes([
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]);
Some(Self {
cmd,
cmdsize,
rebase_off,
rebase_size,
bind_off,
bind_size,
weak_bind_off,
weak_bind_size,
lazy_bind_off,
lazy_bind_size,
export_off,
export_size,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOEncryptionInfo {
pub cmd: u32,
pub cmdsize: u32,
pub cryptoff: u32,
pub cryptsize: u32,
pub cryptid: u32,
pub pad: u32,
}
impl X86MachOEncryptionInfo {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 24 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let cryptoff = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let cryptsize = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
let cryptid = u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]);
let pad = u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]);
Some(Self {
cmd,
cmdsize,
cryptoff,
cryptsize,
cryptid,
pad,
})
}
pub fn is_encrypted(&self) -> bool {
self.cryptid != 0
}
}
#[derive(Debug, Clone)]
pub struct X86MachORPath {
pub cmd: u32,
pub cmdsize: u32,
pub path_offset: u32,
pub path: String,
}
impl X86MachORPath {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 8 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
if offset + 12 > data.len() {
return None;
}
let path_offset = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let path_start = offset + path_offset as usize;
let path_end = offset + cmdsize as usize;
let path = if path_start < path_end && path_end <= data.len() {
let bytes = &data[path_start..path_end];
let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
String::from_utf8_lossy(&bytes[..len]).to_string()
} else {
String::new()
};
Some(Self {
cmd,
cmdsize,
path_offset,
path,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOSourceVersion {
pub cmd: u32,
pub cmdsize: u32,
pub version: u64,
}
impl X86MachOSourceVersion {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let version = u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
cmd,
cmdsize,
version,
})
}
pub fn version_string(&self) -> String {
let a = (self.version >> 40) & 0xFFFFFF;
let b = (self.version >> 30) & 0x3FF;
let c = (self.version >> 20) & 0x3FF;
let d = (self.version >> 10) & 0x3FF;
let e = self.version & 0x3FF;
format!("{}.{}.{}.{}.{}", a, b, c, d, e)
}
}
#[derive(Debug, Clone)]
pub struct X86MachOLinkerOption {
pub cmd: u32,
pub cmdsize: u32,
pub count: u32,
pub options: Vec<String>,
}
impl X86MachOLinkerOption {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 12 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let count = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let mut options = Vec::new();
let mut pos = offset + 12;
for _ in 0..count {
if pos >= offset + cmdsize as usize {
break;
}
let remaining = &data[pos..];
let len = remaining
.iter()
.position(|&b| b == 0)
.unwrap_or(remaining.len());
if len > 0 {
options.push(String::from_utf8_lossy(&remaining[..len]).to_string());
}
pos += len + 1;
}
Some(Self {
cmd,
cmdsize,
count,
options,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOFunctionStarts {
pub cmd: u32,
pub cmdsize: u32,
pub data_offset: u32,
pub data_size: u32,
}
impl X86MachOFunctionStarts {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let data_offset = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let data_size = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
cmd,
cmdsize,
data_offset,
data_size,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachODataInCode {
pub cmd: u32,
pub cmdsize: u32,
pub data_offset: u32,
pub data_size: u32,
}
impl X86MachODataInCode {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let data_offset = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let data_size = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
cmd,
cmdsize,
data_offset,
data_size,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachONote {
pub cmd: u32,
pub cmdsize: u32,
pub data_owner: [u8; 16],
pub offset: u64,
pub size: u64,
}
impl X86MachONote {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 40 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let mut data_owner = [0u8; 16];
data_owner.copy_from_slice(&data[offset + 8..offset + 24]);
let note_offset = u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]);
let size = u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]);
Some(Self {
cmd,
cmdsize,
data_owner,
offset: note_offset,
size,
})
}
pub fn owner_string(&self) -> String {
let len = self.data_owner.iter().position(|&b| b == 0).unwrap_or(16);
String::from_utf8_lossy(&self.data_owner[..len]).to_string()
}
}
#[derive(Debug, Clone)]
pub struct X86MachOCodeSignature {
pub cmd: u32,
pub cmdsize: u32,
pub dataoff: u32,
pub datasize: u32,
}
impl X86MachOCodeSignature {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let dataoff = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let datasize = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
cmd,
cmdsize,
dataoff,
datasize,
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOSegmentSplitInfo {
pub cmd: u32,
pub cmdsize: u32,
pub dataoff: u32,
pub datasize: u32,
}
impl X86MachOSegmentSplitInfo {
pub fn read(data: &[u8], offset: usize) -> Option<Self> {
if offset + 16 > data.len() {
return None;
}
let cmd = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
let cmdsize = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let dataoff = u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]);
let datasize = u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]);
Some(Self {
cmd,
cmdsize,
dataoff,
datasize,
})
}
}
#[inline]
pub fn u8_not(v: u8) -> u8 {
!v
}
mod hex {
pub fn encode(bytes: &[u8]) -> String {
bytes
.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<_>>()
.join("")
}
}
pub fn hex_dump(data: &[u8], max_bytes: usize) -> String {
let len = data.len().min(max_bytes);
let mut s = String::new();
for i in 0..len {
if i > 0 && i % 16 == 0 {
s.push('\n');
} else if i > 0 {
s.push(' ');
}
s.push_str(&format!("{:02X}", data[i]));
}
if data.len() > max_bytes {
s.push_str(&format!("\n... ({} more bytes)", data.len() - max_bytes));
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_empty() {
let bf = X86BinaryFormat::detect(vec![]);
assert_eq!(bf.format, X86BinaryFormatType::Unknown);
}
#[test]
fn test_detect_elf64() {
let mut data = vec![0u8; 64];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2; data[5] = 1; data[7] = 3; data[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[24..32].copy_from_slice(&0x400000u64.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::ELF);
assert_eq!(bf.arch, X86BinaryArch::X86_64);
assert!(bf.is_64bit);
assert_eq!(bf.entry_point, 0x400000);
assert!(bf.os_abi.contains("LINUX"));
}
#[test]
fn test_detect_elf32() {
let mut data = vec![0u8; 52];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 1; data[5] = 1; data[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
data[18..20].copy_from_slice(&EM_386.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::ELF);
assert_eq!(bf.arch, X86BinaryArch::I386);
assert!(!bf.is_64bit);
}
#[test]
fn test_detect_pe() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
let pe_offset: u32 = 0x80;
data[0x3C..0x40].copy_from_slice(&pe_offset.to_le_bytes());
let off = pe_offset as usize;
data[off] = b'P';
data[off + 1] = b'E';
data[off + 2] = 0;
data[off + 3] = 0;
let coff_start = off + 4;
data[coff_start..coff_start + 2].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[coff_start + 2..coff_start + 4].copy_from_slice(&3u16.to_le_bytes()); data[coff_start + 16..coff_start + 18].copy_from_slice(&0xF0u16.to_le_bytes()); let opt_start = coff_start + 20;
data[opt_start..opt_start + 2]
.copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::PECOFF);
assert_eq!(bf.arch, X86BinaryArch::X86_64);
assert!(bf.is_64bit);
}
#[test]
fn test_detect_pe32() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
let pe_offset: u32 = 0x80;
data[0x3C..0x40].copy_from_slice(&pe_offset.to_le_bytes());
let off = pe_offset as usize;
data[off] = b'P';
data[off + 1] = b'E';
data[off + 2] = 0;
data[off + 3] = 0;
let coff_start = off + 4;
data[coff_start..coff_start + 2].copy_from_slice(&IMAGE_FILE_MACHINE_I386.to_le_bytes());
data[coff_start + 16..coff_start + 18].copy_from_slice(&0xE0u16.to_le_bytes());
let opt_start = coff_start + 20;
data[opt_start..opt_start + 2]
.copy_from_slice(&IMAGE_NT_OPTIONAL_HDR32_MAGIC.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::PECOFF);
assert_eq!(bf.arch, X86BinaryArch::I386);
assert!(!bf.is_64bit);
}
#[test]
fn test_detect_macho64() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&5u32.to_le_bytes()); let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::MachO);
assert_eq!(bf.arch, X86BinaryArch::X86_64);
assert!(bf.is_64bit);
}
#[test]
fn test_detect_macho32() {
let mut data = vec![0u8; 28];
data[0..4].copy_from_slice(&MH_MAGIC.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_I386.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::MachO);
assert_eq!(bf.arch, X86BinaryArch::I386);
}
#[test]
fn test_elf_header_read64() {
let mut data = vec![0u8; 64];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[6] = 1;
data[16..18].copy_from_slice(&ET_DYN.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[24..32].copy_from_slice(&0x1000u64.to_le_bytes());
data[60..62].copy_from_slice(&5u16.to_le_bytes());
data[62..64].copy_from_slice(&2u16.to_le_bytes());
let hdr = X86ELFHeader::read(&data).unwrap();
assert!(hdr.e_ident.is_valid());
assert!(hdr.e_ident.is_64bit());
assert_eq!(hdr.e_type, ET_DYN);
assert_eq!(hdr.e_machine, EM_X86_64);
assert_eq!(hdr.e_entry, 0x1000);
assert_eq!(hdr.e_shnum, 5);
assert_eq!(hdr.e_shstrndx, 2);
assert_eq!(hdr.type_name(), "DYN (Shared object)");
assert_eq!(hdr.machine_name(), "AMD x86-64");
}
#[test]
fn test_elf_header_read32() {
let mut data = vec![0u8; 52];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 1;
data[5] = 1;
data[6] = 1;
data[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
data[18..20].copy_from_slice(&EM_386.to_le_bytes());
data[24..28].copy_from_slice(&0x08048000u32.to_le_bytes());
let hdr = X86ELFHeader::read32(&data).unwrap();
assert!(!hdr.e_ident.is_64bit());
assert_eq!(hdr.e_machine, EM_386);
assert_eq!(hdr.e_entry, 0x08048000);
assert_eq!(hdr.machine_name(), "Intel 80386");
}
#[test]
fn test_elf_header_write() {
let hdr = X86ELFHeader::new_default();
let mut buf = vec![0u8; 64];
hdr.write(&mut buf);
assert_eq!(buf[0], 0x7F);
assert_eq!(buf[1], b'E');
assert_eq!(buf[2], b'L');
assert_eq!(buf[3], b'F');
assert_eq!(buf[4], 2); let reread = X86ELFHeader::read(&buf).unwrap();
assert_eq!(reread.e_type, ET_REL);
assert_eq!(reread.e_machine, EM_X86_64);
}
#[test]
fn test_elf_header_defaults() {
let hdr = X86ELFHeader::new_default();
assert_eq!(hdr.e_type, ET_REL);
assert_eq!(hdr.e_machine, EM_X86_64);
assert_eq!(hdr.e_version, EV_CURRENT);
assert_eq!(hdr.e_ehsize, 64);
assert_eq!(hdr.e_phentsize, 56);
assert_eq!(hdr.e_shentsize, 64);
assert_eq!(hdr.e_shstrndx, SHN_UNDEF);
}
#[test]
fn test_elf_program_header_read64() {
let mut data = vec![0u8; 56];
data[0..4].copy_from_slice(&PT_LOAD.to_le_bytes());
data[4..8].copy_from_slice(&(PF_R | PF_X).to_le_bytes());
data[8..16].copy_from_slice(&0x1000u64.to_le_bytes()); data[16..24].copy_from_slice(&0x400000u64.to_le_bytes()); data[24..32].copy_from_slice(&0x400000u64.to_le_bytes()); data[32..40].copy_from_slice(&0x10000u64.to_le_bytes()); data[40..48].copy_from_slice(&0x20000u64.to_le_bytes()); data[48..56].copy_from_slice(&0x1000u64.to_le_bytes());
let ph = X86ELFProgramHeader::read(&data, 0).unwrap();
assert_eq!(ph.p_type, PT_LOAD);
assert_eq!(ph.p_flags, PF_R | PF_X);
assert_eq!(ph.p_offset, 0x1000);
assert_eq!(ph.p_vaddr, 0x400000);
assert_eq!(ph.p_filesz, 0x10000);
assert_eq!(ph.p_memsz, 0x20000);
assert_eq!(ph.p_align, 0x1000);
assert_eq!(ph.type_name(), "LOAD");
assert_eq!(ph.flags_string(), "RX");
}
#[test]
fn test_elf_program_header_flags_string() {
let mut ph = X86ELFProgramHeader::new_default();
ph.p_flags = PF_R;
assert_eq!(ph.flags_string(), "R");
ph.p_flags = PF_R | PF_W;
assert_eq!(ph.flags_string(), "RW");
ph.p_flags = PF_R | PF_W | PF_X;
assert_eq!(ph.flags_string(), "RWX");
ph.p_flags = 0;
assert_eq!(ph.flags_string(), "---");
}
#[test]
fn test_elf_program_header_types() {
assert_eq!(X86ELFProgramHeader::new_default().type_name(), "LOAD");
let mut ph = X86ELFProgramHeader::new_default();
ph.p_type = PT_GNU_STACK;
assert_eq!(ph.type_name(), "GNU_STACK");
ph.p_type = PT_GNU_RELRO;
assert_eq!(ph.type_name(), "GNU_RELRO");
ph.p_type = PT_TLS;
assert_eq!(ph.type_name(), "TLS");
}
#[test]
fn test_elf_section_header_read64() {
let mut data = vec![0u8; 64];
data[0..4].copy_from_slice(&11u32.to_le_bytes()); data[4..8].copy_from_slice(&SHT_PROGBITS.to_le_bytes());
data[8..16].copy_from_slice(&(SHF_ALLOC | SHF_EXECINSTR).to_le_bytes());
data[16..24].copy_from_slice(&0x401000u64.to_le_bytes());
data[24..32].copy_from_slice(&0x1000u64.to_le_bytes());
data[32..40].copy_from_slice(&0x5000u64.to_le_bytes());
let sh = X86ELFSectionHeader::read(&data, 0).unwrap();
assert_eq!(sh.sh_name, 11);
assert_eq!(sh.sh_type, SHT_PROGBITS);
assert_eq!(sh.sh_flags, SHF_ALLOC | SHF_EXECINSTR);
assert_eq!(sh.sh_addr, 0x401000);
assert_eq!(sh.sh_offset, 0x1000);
assert_eq!(sh.sh_size, 0x5000);
assert_eq!(sh.type_name(), "PROGBITS");
assert!(sh.flags_string().contains("A"));
assert!(sh.flags_string().contains("X"));
}
#[test]
fn test_elf_section_header_types() {
let mut sh = X86ELFSectionHeader::new_default();
sh.sh_type = SHT_SYMTAB;
assert_eq!(sh.type_name(), "SYMTAB");
sh.sh_type = SHT_RELA;
assert_eq!(sh.type_name(), "RELA");
sh.sh_type = SHT_DYNAMIC;
assert_eq!(sh.type_name(), "DYNAMIC");
sh.sh_type = SHT_GNU_HASH;
assert_eq!(sh.type_name(), "GNU_HASH");
}
#[test]
fn test_elf_symbol_binding() {
let mut sym = X86ELFSymbol::new_default();
sym.st_info = (STB_GLOBAL << 4) | STT_FUNC;
assert_eq!(sym.binding(), STB_GLOBAL);
assert_eq!(sym.binding_name(), "GLOBAL");
assert_eq!(sym.symbol_type(), STT_FUNC);
assert_eq!(sym.type_name(), "FUNC");
sym.set_binding(STB_WEAK);
assert_eq!(sym.binding(), STB_WEAK);
assert_eq!(sym.binding_name(), "WEAK");
assert_eq!(sym.symbol_type(), STT_FUNC);
sym.set_type(STT_OBJECT);
assert_eq!(sym.symbol_type(), STT_OBJECT);
assert_eq!(sym.type_name(), "OBJECT");
}
#[test]
fn test_elf_symbol_visibility() {
let mut sym = X86ELFSymbol::new_default();
sym.set_visibility(STV_HIDDEN);
assert_eq!(sym.visibility(), STV_HIDDEN);
assert_eq!(sym.visibility_name(), "HIDDEN");
sym.set_visibility(STV_PROTECTED);
assert_eq!(sym.visibility(), STV_PROTECTED);
assert_eq!(sym.visibility_name(), "PROTECTED");
sym.set_visibility(STV_INTERNAL);
assert_eq!(sym.visibility(), STV_INTERNAL);
assert_eq!(sym.visibility_name(), "INTERNAL");
sym.set_visibility(STV_DEFAULT);
assert_eq!(sym.visibility(), STV_DEFAULT);
assert_eq!(sym.visibility_name(), "DEFAULT");
}
#[test]
fn test_elf_symbol_read64() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&5u32.to_le_bytes()); data[4] = (STB_GLOBAL << 4) | STT_FUNC; data[5] = STV_HIDDEN; data[6..8].copy_from_slice(&1u16.to_le_bytes()); data[8..16].copy_from_slice(&0x401000u64.to_le_bytes());
data[16..24].copy_from_slice(&0x100u64.to_le_bytes());
let sym = X86ELFSymbol::read(&data, 0).unwrap();
assert_eq!(sym.st_name, 5);
assert_eq!(sym.binding(), STB_GLOBAL);
assert_eq!(sym.symbol_type(), STT_FUNC);
assert_eq!(sym.visibility(), STV_HIDDEN);
assert_eq!(sym.st_shndx, 1);
assert_eq!(sym.st_value, 0x401000);
assert_eq!(sym.st_size, 0x100);
}
#[test]
fn test_elf_symbol_read32() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&7u32.to_le_bytes());
data[4..8].copy_from_slice(&0x8048000u32.to_le_bytes());
data[8..12].copy_from_slice(&0x50u32.to_le_bytes());
data[12] = (STB_LOCAL << 4) | STT_OBJECT;
data[13] = STV_DEFAULT;
let sym = X86ELFSymbol::read32(&data, 0).unwrap();
assert_eq!(sym.st_name, 7);
assert_eq!(sym.binding(), STB_LOCAL);
assert_eq!(sym.symbol_type(), STT_OBJECT);
assert_eq!(sym.st_value, 0x8048000);
assert_eq!(sym.st_size, 0x50);
}
#[test]
fn test_elf_relocation_read64() {
let mut data = vec![0u8; 24];
data[0..8].copy_from_slice(&0x1000u64.to_le_bytes()); data[8..16].copy_from_slice(&((3u64 << 32) | R_X86_64_PC32 as u64).to_le_bytes());
data[16..24].copy_from_slice(&(-4i64).to_le_bytes());
let rel = X86ELFRelocation::read(&data, 0).unwrap();
assert_eq!(rel.r_offset, 0x1000);
assert_eq!(rel.symbol_index(), 3);
assert_eq!(rel.reloc_type(), 2); assert_eq!(rel.r_addend, -4);
}
#[test]
fn test_elf_relocation_set_info() {
let mut rel = X86ELFRelocation::new_default();
rel.set_reloc_info(7, 8); assert_eq!(rel.symbol_index(), 7);
assert_eq!(rel.reloc_type(), 8);
}
#[test]
fn test_x86_64_reloc_types() {
assert_eq!(X86ELFRelocType::from_u32(0).name(), "R_X86_64_NONE");
assert_eq!(X86ELFRelocType::from_u32(1).name(), "R_X86_64_64");
assert_eq!(X86ELFRelocType::from_u32(2).name(), "R_X86_64_PC32");
assert_eq!(X86ELFRelocType::from_u32(5).name(), "R_X86_64_COPY");
assert_eq!(X86ELFRelocType::from_u32(6).name(), "R_X86_64_GLOB_DAT");
assert_eq!(X86ELFRelocType::from_u32(7).name(), "R_X86_64_JUMP_SLOT");
assert_eq!(X86ELFRelocType::from_u32(8).name(), "R_X86_64_RELATIVE");
assert_eq!(X86ELFRelocType::from_u32(9).name(), "R_X86_64_GOTPCREL");
assert_eq!(X86ELFRelocType::from_u32(10).name(), "R_X86_64_32");
assert_eq!(X86ELFRelocType::from_u32(11).name(), "R_X86_64_32S");
assert_eq!(X86ELFRelocType::from_u32(99).name(), "R_X86_64_UNKNOWN");
}
#[test]
fn test_i386_reloc_types() {
assert_eq!(X86ELFRelocType386::from_u32(1).name(), "R_386_32");
assert_eq!(X86ELFRelocType386::from_u32(2).name(), "R_386_PC32");
assert_eq!(X86ELFRelocType386::from_u32(3).name(), "R_386_GOT32");
assert_eq!(X86ELFRelocType386::from_u32(4).name(), "R_386_PLT32");
assert_eq!(X86ELFRelocType386::from_u32(5).name(), "R_386_COPY");
assert_eq!(X86ELFRelocType386::from_u32(6).name(), "R_386_GLOB_DAT");
assert_eq!(X86ELFRelocType386::from_u32(7).name(), "R_386_JUMP_SLOT");
assert_eq!(X86ELFRelocType386::from_u32(8).name(), "R_386_RELATIVE");
}
#[test]
fn test_elf_dynamic_read64() {
let mut data = vec![0u8; 16];
data[0..8].copy_from_slice(&DT_NEEDED.to_le_bytes());
data[8..16].copy_from_slice(&1u64.to_le_bytes());
let d = X86ELFDynamic::read(&data, 0).unwrap();
assert_eq!(d.d_tag, DT_NEEDED);
assert_eq!(d.d_val, 1);
assert_eq!(d.tag_name(), "DT_NEEDED");
data[0..8].copy_from_slice(&DT_SONAME.to_le_bytes());
let d2 = X86ELFDynamic::read(&data, 0).unwrap();
assert_eq!(d2.tag_name(), "DT_SONAME");
}
#[test]
fn test_elf_dynamic_tags() {
let tags = [
(DT_NULL, "DT_NULL"),
(DT_INIT, "DT_INIT"),
(DT_FINI, "DT_FINI"),
(DT_RPATH, "DT_RPATH"),
(DT_RUNPATH, "DT_RUNPATH"),
(DT_FLAGS_1, "DT_FLAGS_1"),
(DT_GNU_HASH, "DT_GNU_HASH"),
];
for (tag, name) in &tags {
let d = X86ELFDynamic {
d_tag: *tag,
d_val: 0,
};
assert_eq!(d.tag_name(), *name);
}
}
#[test]
fn test_elf_note_read() {
let name = b"GNU\0";
let desc = b"\x01\x02\x03\x04";
let mut data = vec![0u8; 12 + 4 + 4];
data[0..4].copy_from_slice(&(name.len() as u32).to_le_bytes());
data[4..8].copy_from_slice(&(desc.len() as u32).to_le_bytes());
data[8..12].copy_from_slice(&3u32.to_le_bytes()); data[12..16].copy_from_slice(name);
let desc_start = 12 + ((name.len() + 3) & !3);
data[desc_start..desc_start + 4].copy_from_slice(desc);
let note = X86ELFNote::read(&data, 0).unwrap();
assert_eq!(note.n_namesz, 4);
assert_eq!(note.n_descsz, 4);
assert_eq!(note.n_type, 3);
assert_eq!(note.name, "GNU");
assert_eq!(note.desc, vec![1, 2, 3, 4]);
}
#[test]
fn test_elf_binary_read_empty() {
let elf = X86ELFBinary::read(&[], true);
assert_eq!(elf.symbols.len(), 0);
assert_eq!(elf.program_headers.len(), 0);
}
#[test]
fn test_elf_binary_minimal() {
let mut data = vec![0u8; 256];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[6] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[40..48].copy_from_slice(&128u64.to_le_bytes());
data[58..60].copy_from_slice(&64u16.to_le_bytes());
data[60..62].copy_from_slice(&2u16.to_le_bytes());
data[62..64].copy_from_slice(&1u16.to_le_bytes());
let sh_offset = 128;
data[sh_offset..sh_offset + 4].copy_from_slice(&1u32.to_le_bytes()); data[sh_offset + 4..sh_offset + 8].copy_from_slice(&SHT_STRTAB.to_le_bytes());
data[sh_offset + 24..sh_offset + 32].copy_from_slice(&200u64.to_le_bytes()); data[sh_offset + 32..sh_offset + 40].copy_from_slice(&50u64.to_le_bytes());
data[200] = 0; data[201] = b'.';
data[202] = b's';
data[203] = b'h';
data[204] = b's';
data[205] = b't';
data[206] = b'r';
data[207] = b't';
data[208] = b'a';
data[209] = b'b';
data[210] = 0; data[211] = b'.';
data[212] = b't';
data[213] = b'e';
data[214] = b'x';
data[215] = b't';
data[216] = 0;
let elf = X86ELFBinary::read(&data, true);
assert_eq!(elf.section_headers.len(), 2);
assert_eq!(elf.is_64bit, true);
assert!(elf.is_object());
assert!(!elf.is_executable());
assert!(!elf.is_shared_library());
}
#[test]
fn test_elf_get_section_name() {
let mut data = vec![0u8; 300];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[6] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[40..48].copy_from_slice(&128u64.to_le_bytes());
data[58..60].copy_from_slice(&64u16.to_le_bytes());
data[60..62].copy_from_slice(&3u16.to_le_bytes()); data[62..64].copy_from_slice(&1u16.to_le_bytes());
let sh_base = 128;
data[sh_base + 64..sh_base + 68].copy_from_slice(&1u32.to_le_bytes()); data[sh_base + 64 + 4..sh_base + 64 + 8].copy_from_slice(&SHT_STRTAB.to_le_bytes());
data[sh_base + 64 + 24..sh_base + 64 + 32].copy_from_slice(&200u64.to_le_bytes());
data[sh_base + 64 + 32..sh_base + 64 + 40].copy_from_slice(&50u64.to_le_bytes());
data[sh_base + 128..sh_base + 132].copy_from_slice(&10u32.to_le_bytes());
data[sh_base + 128 + 4..sh_base + 128 + 8].copy_from_slice(&SHT_PROGBITS.to_le_bytes());
data[200] = 0;
data[201..211].copy_from_slice(b".shstrtab\0");
data[211..217].copy_from_slice(b".text\0");
let elf = X86ELFBinary::read(&data, true);
assert_eq!(elf.section_headers.len(), 3);
let sh1 = &elf.section_headers[1];
let name1 = elf.get_section_name(sh1);
assert_eq!(name1, ".shstrtab");
let sh2 = &elf.section_headers[2];
let name2 = elf.get_section_name(sh2);
assert_eq!(name2, ".text");
}
#[test]
fn test_elf_find_section_by_name() {
let mut data = vec![0u8; 400];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[6] = 1;
data[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[40..48].copy_from_slice(&128u64.to_le_bytes());
data[58..60].copy_from_slice(&64u16.to_le_bytes());
data[60..62].copy_from_slice(&3u16.to_le_bytes());
data[62..64].copy_from_slice(&1u16.to_le_bytes());
let sh_base = 128;
data[sh_base + 64..sh_base + 68].copy_from_slice(&1u32.to_le_bytes());
data[sh_base + 64 + 4..sh_base + 64 + 8].copy_from_slice(&SHT_STRTAB.to_le_bytes());
data[sh_base + 64 + 24..sh_base + 64 + 32].copy_from_slice(&300u64.to_le_bytes());
data[sh_base + 64 + 32..sh_base + 64 + 40].copy_from_slice(&50u64.to_le_bytes());
data[sh_base + 128..sh_base + 132].copy_from_slice(&10u32.to_le_bytes());
data[sh_base + 128 + 4..sh_base + 128 + 8].copy_from_slice(&SHT_PROGBITS.to_le_bytes());
data[300] = 0;
data[301..311].copy_from_slice(b".shstrtab\0");
data[311..317].copy_from_slice(b".data\0");
let elf = X86ELFBinary::read(&data, true);
let found = elf.find_section_by_name(".data");
assert!(found.is_some());
assert_eq!(found.unwrap().sh_type, SHT_PROGBITS);
let not_found = elf.find_section_by_name(".bss");
assert!(not_found.is_none());
}
#[test]
fn test_elf_needed_libraries() {
let mut elf = X86ELFBinary::read(&[], true);
elf.dynstr_offset = 500;
elf.data.resize(600, 0);
elf.dynamics.push(X86ELFDynamic {
d_tag: DT_NEEDED,
d_val: 0,
}); elf.dynamics.push(X86ELFDynamic {
d_tag: DT_NEEDED,
d_val: 10,
}); elf.dynamics.push(X86ELFDynamic {
d_tag: DT_NULL,
d_val: 0,
});
elf.data[500] = b'l';
elf.data[501] = b'i';
elf.data[502] = b'b';
elf.data[503] = b'c';
elf.data[504] = b'.';
elf.data[505] = b's';
elf.data[506] = b'o';
elf.data[507] = b'.';
elf.data[508] = b'6';
elf.data[509] = 0;
elf.data[510] = b'l';
elf.data[511] = b'i';
elf.data[512] = b'b';
elf.data[513] = b'm';
elf.data[514] = b'.';
elf.data[515] = b's';
elf.data[516] = b'o';
elf.data[517] = b'.';
elf.data[518] = b'6';
elf.data[519] = 0;
let needed = elf.get_needed_libraries();
assert_eq!(needed.len(), 2);
assert_eq!(needed[0], "libc.so.6");
assert_eq!(needed[1], "libm.so.6");
}
#[test]
fn test_elf_entry_point() {
let mut hdr = X86ELFHeader::new_default();
hdr.e_entry = 0x401000;
let mut elf = X86ELFBinary::read(&[], true);
elf.header = hdr;
assert_eq!(elf.get_entry_point(), 0x401000);
}
#[test]
fn test_pe_dos_header_read() {
let mut data = vec![0u8; 64];
data[0] = b'M';
data[1] = b'Z';
data[60..64].copy_from_slice(&0x80u32.to_le_bytes());
let dos = X86PEDosHeader::read(&data).unwrap();
assert_eq!(dos.e_magic, IMAGE_DOS_SIGNATURE);
assert_eq!(dos.e_lfanew, 0x80);
}
#[test]
fn test_pe_dos_header_write() {
let dos = X86PEDosHeader::new_default();
let mut buf = vec![0u8; 64];
dos.write(&mut buf);
assert_eq!(buf[0], b'M');
assert_eq!(buf[1], b'Z');
let e_lfanew = u32::from_le_bytes([buf[60], buf[61], buf[62], buf[63]]);
assert_eq!(e_lfanew, 0x80);
}
#[test]
fn test_pe_coff_header_read() {
let mut data = vec![0u8; 20];
data[0..2].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[2..4].copy_from_slice(&5u16.to_le_bytes()); data[16..18].copy_from_slice(&0xF0u16.to_le_bytes()); data[18..20].copy_from_slice(&(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL).to_le_bytes());
let coff = X86PECoffHeader::read(&data, 0).unwrap();
assert_eq!(coff.machine, IMAGE_FILE_MACHINE_AMD64);
assert_eq!(coff.number_of_sections, 5);
assert_eq!(coff.size_of_optional_header, 0xF0);
assert_eq!(
coff.characteristics,
IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL
);
assert_eq!(coff.machine_name(), "AMD64");
}
#[test]
fn test_pe_optional_header_pe32plus() {
let mut data = vec![0u8; 112 + 128]; data[0..2].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
data[16..20].copy_from_slice(&0x1000u32.to_le_bytes()); data[24..32].copy_from_slice(&0x140000000u64.to_le_bytes()); data[32..36].copy_from_slice(&0x1000u32.to_le_bytes()); data[36..40].copy_from_slice(&0x200u32.to_le_bytes()); data[68..70].copy_from_slice(&IMAGE_SUBSYSTEM_WINDOWS_CUI.to_le_bytes());
data[108..112].copy_from_slice(&16u32.to_le_bytes());
let opt = X86PEOptionalHeader::read_pe32plus(&data, 0).unwrap();
assert!(opt.is_pe32plus());
assert_eq!(opt.address_of_entry_point, 0x1000);
assert_eq!(opt.image_base, 0x140000000);
assert_eq!(opt.section_alignment, 0x1000);
assert_eq!(opt.file_alignment, 0x200);
assert_eq!(opt.subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI);
}
#[test]
fn test_pe_optional_header_pe32() {
let mut data = vec![0u8; 96 + 128];
data[0..2].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR32_MAGIC.to_le_bytes());
data[16..20].copy_from_slice(&0x1000u32.to_le_bytes()); data[28..32].copy_from_slice(&0x00400000u32.to_le_bytes()); data[92..96].copy_from_slice(&16u32.to_le_bytes());
let opt = X86PEOptionalHeader::read_pe32(&data, 0).unwrap();
assert!(!opt.is_pe32plus());
assert_eq!(opt.address_of_entry_point, 0x1000);
assert_eq!(opt.image_base, 0x00400000);
}
#[test]
fn test_pe_section_header_read() {
let mut data = vec![0u8; 40];
data[0..8].copy_from_slice(b".text\0\0\0");
data[8..12].copy_from_slice(&0x5000u32.to_le_bytes()); data[12..16].copy_from_slice(&0x1000u32.to_le_bytes()); data[16..20].copy_from_slice(&0x5000u32.to_le_bytes()); data[20..24].copy_from_slice(&0x400u32.to_le_bytes()); data[36..40].copy_from_slice(
&(IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ).to_le_bytes(),
);
let sh = X86PESectionHeader::read(&data, 0).unwrap();
assert_eq!(sh.name_str(), ".text");
assert_eq!(sh.virtual_size, 0x5000);
assert_eq!(sh.virtual_address, 0x1000);
assert_eq!(sh.size_of_raw_data, 0x5000);
assert_eq!(sh.pointer_to_raw_data, 0x400);
assert!(sh.characteristics_string().contains("CODE"));
assert!(sh.characteristics_string().contains("EXECUTE"));
assert!(sh.characteristics_string().contains("READ"));
let mut sh2 = sh;
sh2.set_name(".data");
assert_eq!(sh2.name_str(), ".data");
}
#[test]
fn test_pe_section_long_name() {
let mut sh = X86PESectionHeader::new_default();
sh.set_name("/12345");
assert!(sh.is_long_name());
assert_eq!(sh.long_name_offset(), Some(12345));
sh.set_name(".text");
assert!(!sh.is_long_name());
assert_eq!(sh.long_name_offset(), None);
}
#[test]
fn test_pe_symbol_read() {
let mut data = vec![0u8; 18];
data[0..8].copy_from_slice(b"main\0\0\0\0");
data[8..12].copy_from_slice(&0x1000u32.to_le_bytes()); data[12..14].copy_from_slice(&1i16.to_le_bytes()); data[14..16].copy_from_slice(&0u16.to_le_bytes()); data[16] = IMAGE_SYM_CLASS_EXTERNAL;
data[17] = 0;
let sym = X86PESymbol::read(&data, 0).unwrap();
assert_eq!(sym.name_long, "main");
assert_eq!(sym.value, 0x1000);
assert_eq!(sym.section_number, 1);
assert_eq!(sym.storage_class, IMAGE_SYM_CLASS_EXTERNAL);
assert_eq!(sym.storage_class_name(), "EXTERNAL");
}
#[test]
fn test_pe_symbol_long_name() {
let mut data = vec![0u8; 18];
data[0..4].copy_from_slice(&[0u8; 4]);
data[4..8].copy_from_slice(&0x10u32.to_be_bytes());
let mut sym = X86PESymbol::read(&data, 0).unwrap();
assert!(sym.name_long.is_empty());
let string_table = b"\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0longname\0";
let resolved = sym.resolve_name(string_table);
assert_eq!(resolved, "longname");
}
#[test]
fn test_pe_symbol_storage_classes() {
let classes = [
(IMAGE_SYM_CLASS_NULL, "NULL"),
(IMAGE_SYM_CLASS_EXTERNAL, "EXTERNAL"),
(IMAGE_SYM_CLASS_STATIC, "STATIC"),
(IMAGE_SYM_CLASS_FUNCTION, "FUNCTION"),
(IMAGE_SYM_CLASS_FILE, "FILE"),
(IMAGE_SYM_CLASS_SECTION, "SECTION"),
(IMAGE_SYM_CLASS_WEAK_EXTERNAL, "WEAK_EXTERNAL"),
];
for (class, name) in &classes {
let mut sym = X86PESymbol::new_default();
sym.storage_class = *class;
assert_eq!(sym.storage_class_name(), *name);
}
}
#[test]
fn test_pe_reloc_amd64_types() {
assert_eq!(
X86PERelocAMD64::from_u16(0x0000).name(),
"IMAGE_REL_AMD64_ABSOLUTE"
);
assert_eq!(
X86PERelocAMD64::from_u16(0x0001).name(),
"IMAGE_REL_AMD64_ADDR64"
);
assert_eq!(
X86PERelocAMD64::from_u16(0x0002).name(),
"IMAGE_REL_AMD64_ADDR32"
);
assert_eq!(
X86PERelocAMD64::from_u16(0x0004).name(),
"IMAGE_REL_AMD64_REL32"
);
assert_eq!(
X86PERelocAMD64::from_u16(0xFFFF).name(),
"IMAGE_REL_AMD64_UNKNOWN"
);
}
#[test]
fn test_pe_reloc_i386_types() {
assert_eq!(
X86PERelocI386::from_u16(0x0006).name(),
"IMAGE_REL_I386_DIR32"
);
assert_eq!(
X86PERelocI386::from_u16(0x0014).name(),
"IMAGE_REL_I386_REL32"
);
assert_eq!(
X86PERelocI386::from_u16(0x000A).name(),
"IMAGE_REL_I386_SECTION"
);
assert_eq!(
X86PERelocI386::from_u16(0xFFFF).name(),
"IMAGE_REL_I386_UNKNOWN"
);
}
#[test]
fn test_pe_binary_basic() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80..0x84].copy_from_slice(b"PE\0\0");
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x86..0x88].copy_from_slice(&2u16.to_le_bytes()); data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes()); data[0x96..0x98].copy_from_slice(&IMAGE_FILE_EXECUTABLE_IMAGE.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
data[0xA8..0xAC].copy_from_slice(&0x1000u32.to_le_bytes()); data[0xB0..0xB8].copy_from_slice(&0x140000000u64.to_le_bytes());
let pe = X86PEBinary::read(&data);
assert_eq!(pe.coff_header.machine, IMAGE_FILE_MACHINE_AMD64);
assert!(pe.is_64bit);
assert_eq!(pe.get_entry_point_va(), 0x140001000);
assert_eq!(pe.get_image_base(), 0x140000000);
assert!(pe.is_exe());
assert!(!pe.is_dll());
}
#[test]
fn test_pe_rva_to_offset() {
let mut pe = X86PEBinary::read(&[]);
let mut sh1 = X86PESectionHeader::new_default();
sh1.set_name(".text");
sh1.virtual_address = 0x1000;
sh1.virtual_size = 0x5000;
sh1.size_of_raw_data = 0x5000;
sh1.pointer_to_raw_data = 0x400;
pe.section_headers.push(sh1);
assert_eq!(pe.rva_to_offset(0x1000), Some(0x400));
assert_eq!(pe.rva_to_offset(0x1500), Some(0x900));
assert_eq!(pe.rva_to_offset(0x0FFF), None);
assert_eq!(pe.rva_to_offset(0x7000), None);
}
#[test]
fn test_pe_data_directory() {
let dd = X86PEDataDirectory {
virtual_address: 0x5000,
size: 0x100,
};
assert_eq!(dd.virtual_address, 0x5000);
assert_eq!(dd.size, 0x100);
}
#[test]
fn test_macho_header_read() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[8..12].copy_from_slice(&3u32.to_le_bytes()); data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&3u32.to_le_bytes()); data[20..24].copy_from_slice(&0x100u32.to_le_bytes()); data[24..28].copy_from_slice(&(MH_TWOLEVEL | MH_PIE).to_le_bytes());
let hdr = X86MachOHeader::read(&data).unwrap();
assert!(hdr.is_64bit());
assert!(!hdr.is_big_endian());
assert_eq!(hdr.cputype, CPU_TYPE_X86_64);
assert_eq!(hdr.filetype, MH_EXECUTE);
assert_eq!(hdr.ncmds, 3);
assert_eq!(hdr.cputype_name(), "x86_64");
assert_eq!(hdr.filetype_name(), "EXECUTE");
}
#[test]
fn test_macho_header_i386() {
let mut data = vec![0u8; 28];
data[0..4].copy_from_slice(&MH_MAGIC.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_I386.to_le_bytes());
data[12..16].copy_from_slice(&MH_OBJECT.to_le_bytes());
let hdr = X86MachOHeader::read(&data).unwrap();
assert!(!hdr.is_64bit());
assert_eq!(hdr.cputype, CPU_TYPE_I386);
assert_eq!(hdr.cputype_name(), "i386");
assert_eq!(hdr.filetype_name(), "OBJECT");
}
#[test]
fn test_macho_header_big_endian() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&MH_CIGAM_64.to_be_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_be_bytes());
let hdr = X86MachOHeader::read(&data).unwrap();
assert!(hdr.is_big_endian());
}
#[test]
fn test_macho_segment_read() {
let mut data = vec![0u8; 72];
data[0..4].copy_from_slice(&LC_SEGMENT_64.to_le_bytes());
data[4..8].copy_from_slice(&72u32.to_le_bytes()); data[8..24].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
data[24..32].copy_from_slice(&0x100000000u64.to_le_bytes()); data[32..40].copy_from_slice(&0x5000u64.to_le_bytes()); data[40..48].copy_from_slice(&0u64.to_le_bytes()); data[48..56].copy_from_slice(&0x5000u64.to_le_bytes()); data[56..60].copy_from_slice(&VM_PROT_ALL.to_le_bytes()); data[60..64].copy_from_slice(&(VM_PROT_READ | VM_PROT_EXECUTE).to_le_bytes()); data[64..68].copy_from_slice(&3u32.to_le_bytes()); data[68..72].copy_from_slice(&0u32.to_le_bytes());
let seg = X86MachOSegmentCommand::read(&data, 0).unwrap();
assert_eq!(seg.segname_str(), "__TEXT");
assert_eq!(seg.vmaddr, 0x100000000);
assert_eq!(seg.vmsize, 0x5000);
assert_eq!(seg.maxprot, VM_PROT_ALL);
assert_eq!(seg.initprot, VM_PROT_READ | VM_PROT_EXECUTE);
assert_eq!(seg.nsects, 3);
}
#[test]
fn test_macho_segment_set_name() {
let mut seg = X86MachOSegmentCommand::new_default();
seg.set_segname("__DATA");
assert_eq!(seg.segname_str(), "__DATA");
seg.set_segname("__LINKEDIT");
assert_eq!(seg.segname_str(), "__LINKEDIT");
}
#[test]
fn test_macho_section_read() {
let mut data = vec![0u8; 80];
data[0..16].copy_from_slice(b"__text\0\0\0\0\0\0\0\0\0\0");
data[16..32].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
data[32..40].copy_from_slice(&0x100003F5u64.to_le_bytes()); data[40..48].copy_from_slice(&0x100u64.to_le_bytes()); data[48..52].copy_from_slice(&0x3F5u32.to_le_bytes()); data[52..56].copy_from_slice(&4u32.to_le_bytes()); data[64..68].copy_from_slice(
&(S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS).to_le_bytes(),
);
let sec = X86MachOSection::read(&data, 0).unwrap();
assert_eq!(sec.sectname_str(), "__text");
assert_eq!(sec.segname_str(), "__TEXT");
assert_eq!(sec.addr, 0x100003F5);
assert_eq!(sec.size, 0x100);
assert_eq!(sec.offset, 0x3F5);
assert_eq!(sec.align, 4);
assert_eq!(sec.section_type(), "REGULAR");
}
#[test]
fn test_macho_nlist64_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&0x50u32.to_le_bytes()); data[4] = N_SECT | N_EXT; data[5] = 1; data[6..8].copy_from_slice(&0u16.to_le_bytes()); data[8..16].copy_from_slice(&0x100004000u64.to_le_bytes());
let nlist = X86MachONlist64::read(&data, 0).unwrap();
assert_eq!(nlist.n_strx, 0x50);
assert_eq!(nlist.n_type, N_SECT | N_EXT);
assert_eq!(nlist.n_sect, 1);
assert_eq!(nlist.n_value, 0x100004000);
assert!(!nlist.is_stab());
assert!(!nlist.is_pext());
assert!(nlist.is_ext());
assert_eq!(nlist.nlist_type(), N_SECT);
assert_eq!(nlist.type_name(), "SECT");
assert_eq!(nlist.binding(), "GLOBAL");
}
#[test]
fn test_macho_nlist64_local() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_SECT; assert_eq!(nlist.binding(), "LOCAL");
}
#[test]
fn test_macho_nlist64_weak() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_SECT | N_EXT;
nlist.n_desc = N_WEAK_DEF;
assert_eq!(nlist.binding(), "WEAK");
}
#[test]
fn test_macho_relocation_read() {
let mut data = vec![0u8; 8];
data[0..4].copy_from_slice(&0x10i32.to_le_bytes()); let bits: u32 = 5 | (1 << 24) | (2 << 25) | (1 << 27) | (2 << 28);
data[4..8].copy_from_slice(&bits.to_le_bytes());
let rel = X86MachORelocation::read(&data, 0).unwrap();
assert_eq!(rel.r_address, 0x10);
assert_eq!(rel.r_symbolnum, 5);
assert!(rel.r_pcrel);
assert_eq!(rel.r_length, 2);
assert_eq!(rel.length_bytes(), 4);
assert!(rel.r_extern);
assert_eq!(rel.r_type, 2);
assert_eq!(rel.reloc_type_name(), "X86_64_RELOC_BRANCH");
}
#[test]
fn test_macho_reloc_types() {
assert_eq!(
X86MachORelocType::from_u32(0).name(),
"X86_64_RELOC_UNSIGNED"
);
assert_eq!(X86MachORelocType::from_u32(1).name(), "X86_64_RELOC_SIGNED");
assert_eq!(X86MachORelocType::from_u32(2).name(), "X86_64_RELOC_BRANCH");
assert_eq!(
X86MachORelocType::from_u32(3).name(),
"X86_64_RELOC_GOT_LOAD"
);
assert_eq!(X86MachORelocType::from_u32(4).name(), "X86_64_RELOC_GOT");
assert_eq!(
X86MachORelocType::from_u32(5).name(),
"X86_64_RELOC_SUBTRACTOR"
);
assert_eq!(
X86MachORelocType::from_u32(6).name(),
"X86_64_RELOC_SIGNED_1"
);
assert_eq!(
X86MachORelocType::from_u32(7).name(),
"X86_64_RELOC_SIGNED_2"
);
assert_eq!(
X86MachORelocType::from_u32(8).name(),
"X86_64_RELOC_SIGNED_4"
);
assert_eq!(X86MachORelocType::from_u32(9).name(), "X86_64_RELOC_TLV");
}
#[test]
fn test_macho_uuid_read() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&LC_UUID.to_le_bytes());
data[4..8].copy_from_slice(&24u32.to_le_bytes());
for i in 0..16 {
data[8 + i] = (i as u8) * 17;
}
let uuid = X86MachOUUID::read(&data, 0).unwrap();
assert_eq!(uuid.cmd, LC_UUID);
let uuid_str = uuid.to_string();
assert!(uuid_str.contains('-'));
assert_eq!(uuid_str.len(), 36);
}
#[test]
fn test_macho_build_version_read() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&LC_BUILD_VERSION.to_le_bytes());
data[4..8].copy_from_slice(&24u32.to_le_bytes());
data[8..12].copy_from_slice(&PLATFORM_MACOS.to_le_bytes());
data[12..16].copy_from_slice(&0x000E0000u32.to_le_bytes()); data[16..20].copy_from_slice(&0x000E0300u32.to_le_bytes());
let bv = X86MachOBuildVersion::read(&data, 0).unwrap();
assert_eq!(bv.platform, PLATFORM_MACOS);
assert_eq!(bv.platform_name(), "macOS");
assert_eq!(X86MachOBuildVersion::format_version(bv.minos), "14.0.0");
assert_eq!(X86MachOBuildVersion::format_version(bv.sdk), "14.3.0");
}
#[test]
fn test_macho_build_version_platforms() {
let platforms = [
(PLATFORM_MACOS, "macOS"),
(PLATFORM_IOS, "iOS"),
(PLATFORM_TVOS, "tvOS"),
(PLATFORM_WATCHOS, "watchOS"),
(PLATFORM_DRIVERKIT, "DriverKit"),
];
for (plat, name) in &platforms {
let mut bv = X86MachOBuildVersion::new_default();
bv.platform = *plat;
assert_eq!(bv.platform_name(), *name);
}
}
#[test]
fn test_macho_version_format() {
assert_eq!(X86MachOBuildVersion::format_version(0x000C0000), "12.0.0");
assert_eq!(X86MachOBuildVersion::format_version(0x000D0102), "13.1.2");
assert_eq!(X86MachOBuildVersion::format_version(0x000A0F00), "10.15.0");
assert_eq!(X86MachOBuildVersion::format_version(0), "0.0.0");
}
#[test]
fn test_macho_dylib_command_read() {
let name = b"/usr/lib/libSystem.B.dylib\0";
let cmdsize = 24 + name.len() as u32;
let mut data = vec![0u8; cmdsize as usize];
data[0..4].copy_from_slice(&LC_LOAD_DYLIB.to_le_bytes());
data[4..8].copy_from_slice(&cmdsize.to_le_bytes());
data[8..12].copy_from_slice(&24u32.to_le_bytes()); data[12..16].copy_from_slice(&0xDEADBEEFu32.to_le_bytes()); data[16..20].copy_from_slice(&0x00010000u32.to_le_bytes()); data[20..24].copy_from_slice(&0x00010000u32.to_le_bytes()); data[24..24 + name.len()].copy_from_slice(name);
let dc = X86MachODylibCommand::read(&data, 0).unwrap();
assert_eq!(dc.cmd, LC_LOAD_DYLIB);
assert_eq!(dc.dylib_name_offset, 24);
assert_eq!(dc.dylib_timestamp, 0xDEADBEEF);
assert_eq!(dc.name, "/usr/lib/libSystem.B.dylib");
}
#[test]
fn test_macho_symtab_command_read() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&LC_SYMTAB.to_le_bytes());
data[4..8].copy_from_slice(&24u32.to_le_bytes());
data[8..12].copy_from_slice(&0x1000u32.to_le_bytes());
data[12..16].copy_from_slice(&50u32.to_le_bytes());
data[16..20].copy_from_slice(&0x2000u32.to_le_bytes());
data[20..24].copy_from_slice(&0x500u32.to_le_bytes());
let st = X86MachOSymtabCommand::read(&data, 0).unwrap();
assert_eq!(st.symoff, 0x1000);
assert_eq!(st.nsyms, 50);
assert_eq!(st.stroff, 0x2000);
assert_eq!(st.strsize, 0x500);
}
#[test]
fn test_macho_dysymtab_command_read() {
let mut data = vec![0u8; 80];
data[0..4].copy_from_slice(&LC_DYSYMTAB.to_le_bytes());
data[4..8].copy_from_slice(&80u32.to_le_bytes());
data[8..12].copy_from_slice(&0u32.to_le_bytes()); data[12..16].copy_from_slice(&10u32.to_le_bytes()); data[16..20].copy_from_slice(&10u32.to_le_bytes()); data[20..24].copy_from_slice(&5u32.to_le_bytes()); data[24..28].copy_from_slice(&15u32.to_le_bytes()); data[28..32].copy_from_slice(&3u32.to_le_bytes());
let ds = X86MachODysymtabCommand::read(&data, 0).unwrap();
assert_eq!(ds.nlocalsym, 10);
assert_eq!(ds.nextdefsym, 5);
assert_eq!(ds.nundefsym, 3);
}
#[test]
fn test_macho_main_command_read() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&LC_MAIN.to_le_bytes());
data[4..8].copy_from_slice(&24u32.to_le_bytes());
data[8..16].copy_from_slice(&0x100003F50u64.to_le_bytes());
data[16..24].copy_from_slice(&0x800000u64.to_le_bytes());
let mc = X86MachOMainCommand::read(&data, 0).unwrap();
assert_eq!(mc.entryoff, 0x100003F50);
assert_eq!(mc.stacksize, 0x800000);
}
#[test]
fn test_macho_binary_basic() {
let header_size = 32;
let lc_main_size = 24;
let total_size = header_size + lc_main_size;
let mut data = vec![0u8; total_size];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[8..12].copy_from_slice(&3u32.to_le_bytes()); data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes()); data[20..24].copy_from_slice(&(lc_main_size as u32).to_le_bytes());
data[24..28].copy_from_slice(&(MH_TWOLEVEL | MH_PIE).to_le_bytes());
let lc_off = header_size;
data[lc_off..lc_off + 4].copy_from_slice(&LC_MAIN.to_le_bytes());
data[lc_off + 4..lc_off + 8].copy_from_slice(&(lc_main_size as u32).to_le_bytes());
data[lc_off + 8..lc_off + 16].copy_from_slice(&0x100000FA0u64.to_le_bytes()); data[lc_off + 16..lc_off + 24].copy_from_slice(&0x0u64.to_le_bytes());
let macho = X86MachOBinary::read(&data);
assert!(macho.is_64bit);
assert!(macho.is_executable());
assert!(!macho.is_dylib());
assert!(!macho.is_object());
assert_eq!(macho.cputype_name(), "x86_64");
assert_eq!(macho.filetype_name(), "EXECUTE");
assert_eq!(macho.entry_point, 0x100000FA0);
assert!(macho.main_command.is_some());
}
#[test]
fn test_macho_binary_with_segments_and_sections() {
let header_size = 32;
let seg_cmd_size = 72 + 2 * 80; let total = header_size + seg_cmd_size;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_OBJECT.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes());
data[20..24].copy_from_slice(&(seg_cmd_size as u32).to_le_bytes());
let seg_off = header_size;
data[seg_off..seg_off + 4].copy_from_slice(&LC_SEGMENT_64.to_le_bytes());
data[seg_off + 4..seg_off + 8].copy_from_slice(&(seg_cmd_size as u32).to_le_bytes());
data[seg_off + 8..seg_off + 24].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
data[seg_off + 64..seg_off + 68].copy_from_slice(&2u32.to_le_bytes());
let sec0 = seg_off + 72;
data[sec0..sec0 + 16].copy_from_slice(b"__text\0\0\0\0\0\0\0\0\0\0");
data[sec0 + 16..sec0 + 32].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
let sec1 = seg_off + 72 + 80;
data[sec1..sec1 + 16].copy_from_slice(b"__cstring\0\0\0\0\0\0\0");
data[sec1 + 16..sec1 + 32].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
let macho = X86MachOBinary::read(&data);
assert_eq!(macho.segments.len(), 1);
assert_eq!(macho.sections.len(), 2);
assert_eq!(macho.segments[0].segname_str(), "__TEXT");
assert_eq!(macho.sections[0].sectname_str(), "__text");
assert_eq!(macho.sections[1].sectname_str(), "__cstring");
assert!(macho.find_segment("__TEXT").is_some());
assert!(macho.find_segment("__DATA").is_none());
let text_sections = macho.find_sections_in_segment("__TEXT");
assert_eq!(text_sections.len(), 2);
let found = macho.find_section("__TEXT", "__text");
assert!(found.is_some());
}
#[test]
fn test_macho_binary_with_symtab() {
let header_size = 32;
let symtab_size = 24;
let nlist_size = 16;
let strtable: Vec<u8> = b"\0_main\0_foo\0_bar\0".to_vec();
let strtable_size = strtable.len();
let nlist_count = 3;
let symtab_data_offset = header_size + symtab_size;
let strtab_offset = symtab_data_offset + nlist_count * nlist_size;
let total = strtab_offset + strtable_size;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_OBJECT.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes()); data[20..24].copy_from_slice(&(symtab_size as u32).to_le_bytes());
data[header_size..header_size + 4].copy_from_slice(&LC_SYMTAB.to_le_bytes());
data[header_size + 4..header_size + 8].copy_from_slice(&(symtab_size as u32).to_le_bytes());
data[header_size + 8..header_size + 12]
.copy_from_slice(&(symtab_data_offset as u32).to_le_bytes());
data[header_size + 12..header_size + 16]
.copy_from_slice(&(nlist_count as u32).to_le_bytes());
data[header_size + 16..header_size + 20]
.copy_from_slice(&(strtab_offset as u32).to_le_bytes());
data[header_size + 20..header_size + 24]
.copy_from_slice(&(strtable_size as u32).to_le_bytes());
data[symtab_data_offset..symtab_data_offset + 4].copy_from_slice(&1u32.to_le_bytes());
data[symtab_data_offset + 4] = N_SECT | N_EXT;
data[symtab_data_offset + 16..symtab_data_offset + 20].copy_from_slice(&6u32.to_le_bytes());
data[symtab_data_offset + 20] = N_SECT;
data[symtab_data_offset + 32..symtab_data_offset + 36]
.copy_from_slice(&10u32.to_le_bytes());
data[symtab_data_offset + 36] = N_SECT | N_EXT;
data[strtab_offset..strtab_offset + strtable_size].copy_from_slice(&strtable);
let macho = X86MachOBinary::read(&data);
assert_eq!(macho.symbols.len(), 3);
assert_eq!(macho.string_table.len(), strtable_size);
assert_eq!(macho.get_symbol_name(&macho.symbols[0]), "_main");
assert_eq!(macho.get_symbol_name(&macho.symbols[1]), "_foo");
assert_eq!(macho.get_symbol_name(&macho.symbols[2]), "_bar");
assert!(macho.symbols[0].is_ext());
assert!(!macho.symbols[1].is_ext());
assert!(macho.symbols[2].is_ext());
}
#[test]
fn test_macho_binary_with_uuid() {
let header_size = 32;
let uuid_size = 24;
let total = header_size + uuid_size;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_DYLIB.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes());
data[20..24].copy_from_slice(&(uuid_size as u32).to_le_bytes());
data[header_size..header_size + 4].copy_from_slice(&LC_UUID.to_le_bytes());
data[header_size + 4..header_size + 8].copy_from_slice(&(uuid_size as u32).to_le_bytes());
for i in 0..16u8 {
data[header_size + 8 + i as usize] = 0xAA + i;
}
let macho = X86MachOBinary::read(&data);
assert!(macho.uuid.is_some());
assert!(macho.uuid_string().is_some());
assert!(macho.uuid_string().unwrap().contains('-'));
assert!(macho.is_dylib());
}
#[test]
fn test_macho_binary_with_build_version() {
let header_size = 32;
let bv_size = 24;
let total = header_size + bv_size;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes());
data[20..24].copy_from_slice(&(bv_size as u32).to_le_bytes());
data[header_size..header_size + 4].copy_from_slice(&LC_BUILD_VERSION.to_le_bytes());
data[header_size + 4..header_size + 8].copy_from_slice(&(bv_size as u32).to_le_bytes());
data[header_size + 8..header_size + 12].copy_from_slice(&PLATFORM_MACOS.to_le_bytes());
data[header_size + 12..header_size + 16].copy_from_slice(&0x000D0000u32.to_le_bytes());
data[header_size + 16..header_size + 20].copy_from_slice(&0x000D0000u32.to_le_bytes());
let macho = X86MachOBinary::read(&data);
assert!(macho.build_version.is_some());
assert_eq!(macho.minos_version_string(), Some("13.0.0".to_string()));
assert_eq!(macho.sdk_version_string(), Some("13.0.0".to_string()));
}
#[test]
fn test_macho_binary_with_dylibs() {
let header_size = 32;
let name1 = b"/usr/lib/libc++.1.dylib\0";
let name2 = b"/usr/lib/libSystem.B.dylib\0";
let cmd1_size = (24 + name1.len()) as u32;
let cmd2_size = (24 + name2.len()) as u32;
let total = header_size + cmd1_size as usize + cmd2_size as usize;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&2u32.to_le_bytes());
let total_cmd_size = (cmd1_size + cmd2_size) as u32;
data[20..24].copy_from_slice(&total_cmd_size.to_le_bytes());
let off1 = header_size;
data[off1..off1 + 4].copy_from_slice(&LC_LOAD_DYLIB.to_le_bytes());
data[off1 + 4..off1 + 8].copy_from_slice(&cmd1_size.to_le_bytes());
data[off1 + 8..off1 + 12].copy_from_slice(&24u32.to_le_bytes());
data[off1 + 24..off1 + 24 + name1.len()].copy_from_slice(name1);
let off2 = header_size + cmd1_size as usize;
data[off2..off2 + 4].copy_from_slice(&LC_LOAD_WEAK_DYLIB.to_le_bytes());
data[off2 + 4..off2 + 8].copy_from_slice(&cmd2_size.to_le_bytes());
data[off2 + 8..off2 + 12].copy_from_slice(&24u32.to_le_bytes());
data[off2 + 24..off2 + 24 + name2.len()].copy_from_slice(name2);
let macho = X86MachOBinary::read(&data);
let names = macho.get_dylib_names();
assert_eq!(names.len(), 2);
assert_eq!(names[0], "/usr/lib/libc++.1.dylib");
assert_eq!(names[1], "/usr/lib/libSystem.B.dylib");
assert_eq!(macho.dylibs[0].cmd, LC_LOAD_DYLIB);
assert_eq!(macho.dylibs[1].cmd, LC_LOAD_WEAK_DYLIB);
}
#[test]
fn test_macho_load_command_names() {
assert_eq!(
X86MachOBinary::load_command_name(LC_SEGMENT_64),
"LC_SEGMENT_64"
);
assert_eq!(X86MachOBinary::load_command_name(LC_MAIN), "LC_MAIN");
assert_eq!(X86MachOBinary::load_command_name(LC_UUID), "LC_UUID");
assert_eq!(
X86MachOBinary::load_command_name(LC_BUILD_VERSION),
"LC_BUILD_VERSION"
);
assert_eq!(
X86MachOBinary::load_command_name(LC_CODE_SIGNATURE),
"LC_CODE_SIGNATURE"
);
assert_eq!(X86MachOBinary::load_command_name(99999), "LC_UNKNOWN");
}
#[test]
fn test_elf_constants() {
assert_eq!(ELF_MAGIC, [0x7F, b'E', b'L', b'F']);
assert_eq!(EI_NIDENT, 16);
assert_eq!(ELFCLASS64, 2);
assert_eq!(ELFDATA2LSB, 1);
assert_eq!(ET_REL, 1);
assert_eq!(EM_386, 3);
assert_eq!(EM_X86_64, 62);
assert_eq!(PT_LOAD, 1);
assert_eq!(PF_X, 1);
assert_eq!(PF_W, 2);
assert_eq!(PF_R, 4);
assert_eq!(SHT_SYMTAB, 2);
assert_eq!(SHF_ALLOC, 2);
assert_eq!(STB_GLOBAL, 1);
assert_eq!(STT_FUNC, 2);
assert_eq!(STV_HIDDEN, 2);
}
#[test]
fn test_pe_constants() {
assert_eq!(IMAGE_DOS_SIGNATURE, 0x5A4D);
assert_eq!(IMAGE_NT_SIGNATURE, 0x00004550);
assert_eq!(IMAGE_FILE_MACHINE_I386, 0x014C);
assert_eq!(IMAGE_FILE_MACHINE_AMD64, 0x8664);
assert_eq!(IMAGE_FILE_EXECUTABLE_IMAGE, 0x0002);
assert_eq!(IMAGE_FILE_DLL, 0x2000);
assert_eq!(IMAGE_NT_OPTIONAL_HDR64_MAGIC, 0x020B);
assert_eq!(IMAGE_NT_OPTIONAL_HDR32_MAGIC, 0x010B);
assert_eq!(IMAGE_SUBSYSTEM_WINDOWS_GUI, 2);
assert_eq!(IMAGE_SCN_CNT_CODE, 0x00000020);
assert_eq!(IMAGE_SCN_MEM_EXECUTE, 0x20000000);
assert_eq!(IMAGE_SCN_MEM_READ, 0x40000000);
assert_eq!(IMAGE_SCN_MEM_WRITE, 0x80000000);
assert_eq!(IMAGE_DIRECTORY_ENTRY_EXPORT, 0);
assert_eq!(IMAGE_DIRECTORY_ENTRY_IMPORT, 1);
assert_eq!(IMAGE_NUMBEROF_DIRECTORY_ENTRIES, 16);
}
#[test]
fn test_macho_constants() {
assert_eq!(MH_MAGIC_64, 0xFEEDFACF);
assert_eq!(MH_CIGAM_64, 0xCFFAEDFE);
assert_eq!(CPU_TYPE_I386, 7);
assert_eq!(CPU_TYPE_X86_64, 0x01000007);
assert_eq!(MH_OBJECT, 1);
assert_eq!(MH_EXECUTE, 2);
assert_eq!(MH_DYLIB, 6);
assert_eq!(LC_SEGMENT_64, 0x19);
assert_eq!(LC_MAIN, 0x80000028);
assert_eq!(LC_UUID, 0x1B);
assert_eq!(LC_BUILD_VERSION, 0x32);
}
#[test]
fn test_hex_dump() {
let data = vec![
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF,
];
let dump = hex_dump(&data, 20);
assert!(dump.contains("00 11 22 33"));
assert!(dump.contains("88 99 AA BB"));
assert!(dump.contains("CC DD EE FF"));
let trunc = hex_dump(&data, 4);
assert!(trunc.contains("more bytes"));
}
#[test]
fn test_hex_encode() {
let bytes = [0xDE, 0xAD, 0xBE, 0xEF];
let encoded = hex::encode(&bytes);
assert_eq!(encoded, "deadbeef");
}
#[test]
fn test_u8_not() {
assert_eq!(u8_not(0), 0xFF);
assert_eq!(u8_not(0xFF), 0);
assert_eq!(u8_not(0x0F), 0xF0);
}
#[test]
fn test_x86_binary_format_display() {
assert_eq!(format!("{}", X86BinaryFormatType::ELF), "ELF");
assert_eq!(format!("{}", X86BinaryFormatType::PECOFF), "PE/COFF");
assert_eq!(format!("{}", X86BinaryFormatType::MachO), "Mach-O");
assert_eq!(format!("{}", X86BinaryArch::I386), "i386");
assert_eq!(format!("{}", X86BinaryArch::X86_64), "x86_64");
}
#[test]
fn test_x86_binary_format_as_elf() {
let mut data = vec![0u8; 64];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert!(bf.as_elf().is_some());
assert!(bf.as_pe().is_none());
assert!(bf.as_macho().is_none());
}
#[test]
fn test_x86_binary_format_as_pe() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes()); data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert!(bf.as_pe().is_some());
assert!(bf.as_elf().is_none());
assert!(bf.as_macho().is_none());
}
#[test]
fn test_x86_binary_format_as_macho() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&0u32.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert!(bf.as_macho().is_some());
assert!(bf.as_elf().is_none());
assert!(bf.as_pe().is_none());
}
#[test]
fn test_x86_binary_format_summary() {
let mut data = vec![0u8; 64];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[7] = 3; data[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[24..32].copy_from_slice(&0x401000u64.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
let summary = bf.summary();
assert!(summary.contains("ELF"));
assert!(summary.contains("x86_64"));
assert!(summary.contains("0x0000000000401000"));
assert!(summary.contains("LINUX"));
}
#[test]
fn test_x86_binary_format_section_names_from_elf() {
let mut data = vec![0u8; 400];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[40..48].copy_from_slice(&128u64.to_le_bytes()); data[58..60].copy_from_slice(&64u16.to_le_bytes());
data[60..62].copy_from_slice(&3u16.to_le_bytes()); data[62..64].copy_from_slice(&1u16.to_le_bytes());
let sh_base = 128;
data[sh_base + 64..sh_base + 68].copy_from_slice(&1u32.to_le_bytes());
data[sh_base + 64 + 4..sh_base + 64 + 8].copy_from_slice(&SHT_STRTAB.to_le_bytes());
data[sh_base + 64 + 24..sh_base + 64 + 32].copy_from_slice(&300u64.to_le_bytes());
data[sh_base + 64 + 32..sh_base + 64 + 40].copy_from_slice(&100u64.to_le_bytes());
data[sh_base + 128..sh_base + 132].copy_from_slice(&11u32.to_le_bytes());
data[sh_base + 128 + 4..sh_base + 128 + 8].copy_from_slice(&SHT_NOTE.to_le_bytes());
data[300] = 0;
data[301..311].copy_from_slice(b".shstrtab\0");
data[311..330].copy_from_slice(b".note.gnu.build-id\0");
let bf = X86BinaryFormat::detect(data.clone());
assert_eq!(bf.format, X86BinaryFormatType::ELF);
let elf = bf.as_elf().unwrap();
let found = elf.find_section_by_name(".note.gnu.build-id");
assert!(found.is_some());
}
#[test]
fn test_elf_read_section_data() {
let mut data = vec![0u8; 300];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
let section_data = b"Hello, ELF section data!\0";
let sec_offset = 200;
data[sec_offset..sec_offset + section_data.len()].copy_from_slice(section_data);
let mut elf = X86ELFBinary::read(&data, true);
let mut sh = X86ELFSectionHeader::new_default();
sh.sh_offset = sec_offset as u64;
sh.sh_size = section_data.len() as u64;
elf.section_headers.push(sh);
let raw = elf.read_section_data(&elf.section_headers[0]);
assert_eq!(raw.len(), section_data.len());
assert_eq!(&raw[..6], b"Hello,");
}
#[test]
fn test_pe_read_section_data() {
let mut pe = X86PEBinary::read(&[]);
let mut sh = X86PESectionHeader::new_default();
sh.set_name(".data");
sh.pointer_to_raw_data = 0x200;
sh.size_of_raw_data = 16;
pe.section_headers.push(sh);
pe.data.resize(0x210, 0);
pe.data[0x200..0x210].copy_from_slice(&[0xAAu8; 16]);
let data = pe.read_section_data(&pe.section_headers[0]);
assert_eq!(data.len(), 16);
assert_eq!(data[0], 0xAA);
}
#[test]
fn test_macho_read_section_data() {
let mut sec = X86MachOSection::new_default();
sec.offset = 0x100;
sec.size = 32;
let mut data = vec![0u8; 0x120];
data[0x100..0x120].copy_from_slice(&[0xBBu8; 32]);
let mut macho = X86MachOBinary::read(&data);
macho.sections.push(sec);
let raw = macho.read_section_data(&macho.sections[0]);
assert_eq!(raw.len(), 32);
assert_eq!(raw[0], 0xBB);
}
#[test]
fn test_pe_is_exe_vs_dll() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x96..0x98]
.copy_from_slice(&(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL).to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let pe = X86PEBinary::read(&data);
assert!(!pe.is_exe());
assert!(pe.is_dll());
}
#[test]
fn test_pe_subsystem_name() {
let mut pe = X86PEBinary::read(&[]);
pe.optional_header.subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
assert_eq!(pe.subsystem_name(), "Windows GUI");
pe.optional_header.subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
assert_eq!(pe.subsystem_name(), "Windows CUI");
pe.optional_header.subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
assert_eq!(pe.subsystem_name(), "EFI Application");
}
#[test]
fn test_macho_is_executable_dylib_object() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
let m = X86MachOBinary::read(&data);
assert!(m.is_executable());
assert!(!m.is_dylib());
assert!(!m.is_object());
data[12..16].copy_from_slice(&MH_DYLIB.to_le_bytes());
let m2 = X86MachOBinary::read(&data);
assert!(m2.is_dylib());
assert!(!m2.is_executable());
assert!(!m2.is_object());
data[12..16].copy_from_slice(&MH_OBJECT.to_le_bytes());
let m3 = X86MachOBinary::read(&data);
assert!(m3.is_object());
assert!(!m3.is_executable());
assert!(!m3.is_dylib());
}
#[test]
fn test_elf_summary_output() {
let mut data = vec![0u8; 300];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[5] = 1;
data[16..18].copy_from_slice(&ET_DYN.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[24..32].copy_from_slice(&0x1000u64.to_le_bytes()); data[32..40].copy_from_slice(&64u64.to_le_bytes()); data[54..56].copy_from_slice(&56u16.to_le_bytes()); data[56..58].copy_from_slice(&1u16.to_le_bytes()); data[64..68].copy_from_slice(&PT_LOAD.to_le_bytes());
data[68..72].copy_from_slice(&(PF_R | PF_X).to_le_bytes());
data[72..80].copy_from_slice(&0u64.to_le_bytes()); data[80..88].copy_from_slice(&0x400000u64.to_le_bytes()); data[96..104].copy_from_slice(&0x1000u64.to_le_bytes()); data[104..112].copy_from_slice(&0x1000u64.to_le_bytes()); data[40..48].copy_from_slice(&120u64.to_le_bytes()); data[58..60].copy_from_slice(&64u16.to_le_bytes()); data[60..62].copy_from_slice(&1u16.to_le_bytes());
let bf = X86BinaryFormat::detect(data.clone());
let elf = bf.as_elf().unwrap();
let summary = elf.summary();
assert!(summary.contains("ELF64"));
assert!(summary.contains("DYN"));
assert!(summary.contains("0x0000000000001000"));
assert!(summary.contains("LOAD"));
assert!(summary.contains("0x0000000000400000"));
}
#[test]
fn test_pe_summary_output() {
let mut data = vec![0u8; 512];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x86..0x88].copy_from_slice(&1u16.to_le_bytes()); data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x96..0x98].copy_from_slice(&IMAGE_FILE_EXECUTABLE_IMAGE.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
data[0xA8..0xAC].copy_from_slice(&0x1000u32.to_le_bytes());
data[0xB0..0xB8].copy_from_slice(&0x140000000u64.to_le_bytes());
let sec_off = 0x98 + 0xF0;
data[sec_off..sec_off + 8].copy_from_slice(b".text\0\0\0");
data[sec_off + 12..sec_off + 16].copy_from_slice(&0x1000u32.to_le_bytes()); data[sec_off + 20..sec_off + 24].copy_from_slice(&0x400u32.to_le_bytes());
let bf = X86BinaryFormat::detect(data.clone());
let pe = bf.as_pe().unwrap();
let summary = pe.summary();
assert!(summary.contains("PE/COFF"));
assert!(summary.contains("AMD64"));
assert!(summary.contains("PE32+"));
assert!(summary.contains("0x0000000140000000"));
assert!(summary.contains(".text"));
}
#[test]
fn test_macho_summary_output() {
let header_size = 32;
let bv_size = 24;
let total = header_size + bv_size;
let mut data = vec![0u8; total];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
data[4..8].copy_from_slice(&CPU_TYPE_X86_64.to_le_bytes());
data[12..16].copy_from_slice(&MH_EXECUTE.to_le_bytes());
data[16..20].copy_from_slice(&1u32.to_le_bytes());
data[20..24].copy_from_slice(&(bv_size as u32).to_le_bytes());
data[header_size..header_size + 4].copy_from_slice(&LC_BUILD_VERSION.to_le_bytes());
data[header_size + 4..header_size + 8].copy_from_slice(&(bv_size as u32).to_le_bytes());
data[header_size + 8..header_size + 12].copy_from_slice(&PLATFORM_MACOS.to_le_bytes());
data[header_size + 12..header_size + 16].copy_from_slice(&0x000E0000u32.to_le_bytes());
data[header_size + 16..header_size + 20].copy_from_slice(&0x000E0000u32.to_le_bytes());
let bf = X86BinaryFormat::detect(data.clone());
let macho = bf.as_macho().unwrap();
let summary = macho.summary();
assert!(summary.contains("Mach-O"));
assert!(summary.contains("EXECUTE"));
assert!(summary.contains("x86_64"));
assert!(summary.contains("macOS"));
assert!(summary.contains("14.0.0"));
}
#[test]
fn test_elf_set_entry_point() {
let mut elf = X86ELFBinary::read(&[], true);
elf.set_entry_point(0x401000);
assert_eq!(elf.get_entry_point(), 0x401000);
assert_eq!(elf.header.e_entry, 0x401000);
}
#[test]
fn test_elf_add_program_header() {
let mut elf = X86ELFBinary::read(&[], true);
assert_eq!(elf.program_headers.len(), 0);
let ph = X86ELFProgramHeader::new_default();
elf.add_program_header(ph);
assert_eq!(elf.program_headers.len(), 1);
assert_eq!(elf.header.e_phnum, 1);
}
#[test]
fn test_elf_add_section_header() {
let mut elf = X86ELFBinary::read(&[], true);
assert_eq!(elf.section_headers.len(), 0);
let sh = X86ELFSectionHeader::new_default();
elf.add_section_header(sh);
assert_eq!(elf.section_headers.len(), 1);
assert_eq!(elf.header.e_shnum, 1);
}
#[test]
fn test_elf_find_sections_by_type() {
let mut elf = X86ELFBinary::read(&[], true);
let mut sh1 = X86ELFSectionHeader::new_default();
sh1.sh_type = SHT_PROGBITS;
let mut sh2 = X86ELFSectionHeader::new_default();
sh2.sh_type = SHT_SYMTAB;
let mut sh3 = X86ELFSectionHeader::new_default();
sh3.sh_type = SHT_PROGBITS;
elf.section_headers = vec![sh1, sh2, sh3];
let progbits = elf.find_sections_by_type(SHT_PROGBITS);
assert_eq!(progbits.len(), 2);
let symtabs = elf.find_sections_by_type(SHT_SYMTAB);
assert_eq!(symtabs.len(), 1);
let dyns = elf.find_sections_by_type(SHT_DYNAMIC);
assert_eq!(dyns.len(), 0);
}
#[test]
fn test_elf_get_program_headers_by_type() {
let mut elf = X86ELFBinary::read(&[], true);
let mut ph1 = X86ELFProgramHeader::new_default();
ph1.p_type = PT_LOAD;
let mut ph2 = X86ELFProgramHeader::new_default();
ph2.p_type = PT_GNU_STACK;
let mut ph3 = X86ELFProgramHeader::new_default();
ph3.p_type = PT_LOAD;
elf.program_headers = vec![ph1, ph2, ph3];
let loads = elf.get_program_headers_by_type(PT_LOAD);
assert_eq!(loads.len(), 2);
let stacks = elf.get_program_headers_by_type(PT_GNU_STACK);
assert_eq!(stacks.len(), 1);
}
#[test]
fn test_elf_get_section_out_of_bounds() {
let elf = X86ELFBinary::read(&[], true);
assert!(elf.get_section(999).is_none());
}
#[test]
fn test_pe_get_data_directory() {
let mut pe = X86PEBinary::read(&[]);
pe.optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_EXPORT] = X86PEDataDirectory {
virtual_address: 0x5000,
size: 0x100,
};
let dd = pe.get_data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);
assert!(dd.is_some());
assert_eq!(dd.unwrap().virtual_address, 0x5000);
assert!(pe.get_data_directory(99).is_none());
}
#[test]
fn test_elf_header_default_to_bytes() {
let hdr = X86ELFHeader::new_default();
let bytes = hdr.to_bytes();
assert_eq!(bytes.len(), 64);
assert_eq!(bytes[0], 0x7F);
assert_eq!(bytes[1], b'E');
assert_eq!(bytes[4], 2); }
#[test]
fn test_pe_dos_header_to_bytes() {
let dos = X86PEDosHeader::new_default();
assert_eq!(dos.e_magic, IMAGE_DOS_SIGNATURE);
let bytes = dos.to_bytes();
assert_eq!(bytes.len(), 64);
assert_eq!(bytes[0], b'M');
assert_eq!(bytes[1], b'Z');
}
#[test]
fn test_elveident_default() {
let ident = X86ELFEIdent::new_default();
assert!(ident.is_valid());
assert!(ident.is_64bit());
assert!(ident.is_little_endian());
assert_eq!(ident.magic, ELF_MAGIC);
}
#[test]
fn test_elveident_invalid() {
let mut ident = X86ELFEIdent::new_default();
ident.magic[0] = 0;
assert!(!ident.is_valid());
}
#[test]
fn test_dt_flags_constants() {
assert_eq!(DF_ORIGIN, 1);
assert_eq!(DF_SYMBOLIC, 2);
assert_eq!(DF_TEXTREL, 4);
assert_eq!(DF_BIND_NOW, 8);
assert_eq!(DF_STATIC_TLS, 16);
}
#[test]
fn test_dt_flags_1_constants() {
assert_eq!(DF_1_NOW, 1);
assert_eq!(DF_1_GLOBAL, 2);
assert_eq!(DF_1_NODELETE, 8);
assert_eq!(DF_1_PIE, 0x8000000);
}
#[test]
fn test_shn_special_values() {
assert_eq!(SHN_UNDEF, 0);
assert_eq!(SHN_ABS, 0xFFF1);
assert_eq!(SHN_COMMON, 0xFFF2);
assert_eq!(SHN_XINDEX, 0xFFFF);
}
#[test]
fn test_elf_writer_empty() {
let elf = X86ELFBinary::read(&[], true);
let mut buf = Vec::new();
elf.write(&mut buf);
assert!(!buf.is_empty());
}
#[test]
fn test_pe_get_entry_point_rva() {
let mut pe = X86PEBinary::read(&[]);
pe.optional_header.address_of_entry_point = 0x1234;
assert_eq!(pe.get_entry_point_rva(), 0x1234);
}
#[test]
fn test_pe_data_directory_names() {
assert_eq!(DATA_DIRECTORY_NAMES[0], "Export");
assert_eq!(DATA_DIRECTORY_NAMES[1], "Import");
assert_eq!(DATA_DIRECTORY_NAMES[2], "Resource");
assert_eq!(DATA_DIRECTORY_NAMES[5], "BaseReloc");
assert_eq!(DATA_DIRECTORY_NAMES[9], "TLS");
assert_eq!(DATA_DIRECTORY_NAMES[12], "IAT");
assert_eq!(DATA_DIRECTORY_NAMES[13], "DelayImport");
assert_eq!(DATA_DIRECTORY_NAMES[14], "CLR Header");
}
#[test]
fn test_macho_section_types() {
let mut sec = X86MachOSection::new_default();
sec.flags = S_REGULAR;
assert_eq!(sec.section_type(), "REGULAR");
sec.flags = S_ZEROFILL;
assert_eq!(sec.section_type(), "ZEROFILL");
sec.flags = S_CSTRING_LITERALS;
assert_eq!(sec.section_type(), "CSTRING_LITERALS");
sec.flags = S_NON_LAZY_SYMBOL_POINTERS;
assert_eq!(sec.section_type(), "NON_LAZY_SYMBOL_POINTERS");
sec.flags = S_SYMBOL_STUBS;
assert_eq!(sec.section_type(), "SYMBOL_STUBS");
}
#[test]
fn test_macho_header_flags() {
assert_eq!(MH_NOUNDEFS, 0x1);
assert_eq!(MH_TWOLEVEL, 0x80);
assert_eq!(MH_PIE, 0x200000);
assert_eq!(MH_NO_HEAP_EXECUTION, 0x1000000);
assert_eq!(MH_APP_EXTENSION_SAFE, 0x02000000);
}
#[test]
fn test_x86_elf_reloc_type_roundtrip() {
let types = [
X86ELFRelocType::None,
X86ELFRelocType::_64,
X86ELFRelocType::PC32,
X86ELFRelocType::Copy,
X86ELFRelocType::GlobDat,
X86ELFRelocType::JumpSlot,
X86ELFRelocType::Relative,
X86ELFRelocType::GotPCRel,
X86ELFRelocType::_32,
X86ELFRelocType::_32S,
];
for t in &types {
let val = t.to_u32();
let back = X86ELFRelocType::from_u32(val);
assert_eq!(back, *t, "Roundtrip failed for {:?}", t);
}
}
#[test]
fn test_x86_elf_reloc_386_type_roundtrip() {
let types = [
X86ELFRelocType386::None,
X86ELFRelocType386::_32,
X86ELFRelocType386::PC32,
X86ELFRelocType386::Copy,
X86ELFRelocType386::GlobDat,
X86ELFRelocType386::Relative,
];
for t in &types {
let val = t.to_u32();
let back = X86ELFRelocType386::from_u32(val);
assert_eq!(back, *t, "Roundtrip failed for {:?}", t);
}
}
#[test]
fn test_macho_reloc_length_bytes() {
let mut rel = X86MachORelocation::new_default();
rel.r_length = 0;
assert_eq!(rel.length_bytes(), 1);
rel.r_length = 1;
assert_eq!(rel.length_bytes(), 2);
rel.r_length = 2;
assert_eq!(rel.length_bytes(), 4);
rel.r_length = 3;
assert_eq!(rel.length_bytes(), 8);
}
#[test]
fn test_macho_reloc_type_to_u32() {
assert_eq!(X86MachORelocType::Unsigned.to_u32(), 0);
assert_eq!(X86MachORelocType::Signed.to_u32(), 1);
assert_eq!(X86MachORelocType::Branch.to_u32(), 2);
assert_eq!(X86MachORelocType::GOTLoad.to_u32(), 3);
assert_eq!(X86MachORelocType::GOT.to_u32(), 4);
}
#[test]
fn test_x86_binary_format_unsupported_arch() {
let bf = X86BinaryFormat::detect(vec![0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(bf.format, X86BinaryFormatType::Unknown);
assert_eq!(bf.arch, X86BinaryArch::Unknown);
assert!(!bf.is_64bit);
}
#[test]
fn test_x86_binary_arch_display() {
assert_eq!(format!("{}", X86BinaryArch::I386), "i386");
assert_eq!(format!("{}", X86BinaryArch::X86_64), "x86_64");
assert_eq!(format!("{}", X86BinaryArch::Unknown), "unknown");
}
#[test]
fn test_x86_binary_format_type_display() {
assert_eq!(format!("{}", X86BinaryFormatType::Unknown), "unknown");
assert_eq!(format!("{}", X86BinaryFormatType::ELF), "ELF");
assert_eq!(format!("{}", X86BinaryFormatType::PECOFF), "PE/COFF");
assert_eq!(format!("{}", X86BinaryFormatType::MachO), "Mach-O");
}
#[test]
fn test_pe_machine_types() {
assert_eq!(IMAGE_FILE_MACHINE_UNKNOWN, 0);
assert_eq!(IMAGE_FILE_MACHINE_I386, 0x014C);
assert_eq!(IMAGE_FILE_MACHINE_AMD64, 0x8664);
}
#[test]
fn test_pe_characteristics_flags() {
assert_eq!(IMAGE_FILE_RELOCS_STRIPPED, 0x0001);
assert_eq!(IMAGE_FILE_EXECUTABLE_IMAGE, 0x0002);
assert_eq!(IMAGE_FILE_32BIT_MACHINE, 0x0100);
assert_eq!(IMAGE_FILE_DLL, 0x2000);
}
#[test]
fn test_pe_dll_characteristics() {
assert_eq!(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, 0x0040);
assert_eq!(IMAGE_DLLCHARACTERISTICS_NX_COMPAT, 0x0100);
assert_eq!(IMAGE_DLLCHARACTERISTICS_GUARD_CF, 0x4000);
}
#[test]
fn test_pe_subsystem_values() {
assert_eq!(IMAGE_SUBSYSTEM_UNKNOWN, 0);
assert_eq!(IMAGE_SUBSYSTEM_NATIVE, 1);
assert_eq!(IMAGE_SUBSYSTEM_WINDOWS_GUI, 2);
assert_eq!(IMAGE_SUBSYSTEM_WINDOWS_CUI, 3);
assert_eq!(IMAGE_SUBSYSTEM_EFI_APPLICATION, 10);
}
#[test]
fn test_macho_vm_prot_constants() {
assert_eq!(VM_PROT_NONE, 0);
assert_eq!(VM_PROT_READ, 1);
assert_eq!(VM_PROT_WRITE, 2);
assert_eq!(VM_PROT_EXECUTE, 4);
assert_eq!(VM_PROT_DEFAULT, 3);
assert_eq!(VM_PROT_ALL, 7);
}
#[test]
fn test_macho_nlist_reference_flags() {
assert_eq!(REFERENCED_DYNAMICALLY, 0x0010);
assert_eq!(N_WEAK_DEF, 0x0080);
assert_eq!(N_WEAK_REF, 0x0040);
assert_eq!(N_ALT_ENTRY, 0x0200);
assert_eq!(N_COLD_FUNC, 0x0400);
}
#[test]
fn test_elf_osabi_values() {
assert_eq!(ELFOSABI_NONE, 0);
assert_eq!(ELFOSABI_LINUX, 3);
assert_eq!(ELFOSABI_FREEBSD, 9);
assert_eq!(ELFOSABI_STANDALONE, 255);
}
#[test]
fn test_elf_relocation_read32() {
let mut data = vec![0u8; 12];
data[0..4].copy_from_slice(&0x100u32.to_le_bytes()); data[4..8].copy_from_slice(&(0x07u32).to_le_bytes()); data[8..12].copy_from_slice(&0xFFFCu32.to_le_bytes());
let rel = X86ELFRelocation::read32(&data, 0).unwrap();
assert_eq!(rel.r_offset, 0x100);
assert_eq!(rel.reloc_type(), 7); assert_eq!(rel.r_addend, -4);
}
#[test]
fn test_elf_relocation_no_addend_read() {
let mut data = vec![0u8; 16];
data[0..8].copy_from_slice(&0x200u64.to_le_bytes());
data[8..16].copy_from_slice(&((2u64 << 32) | (R_X86_64_RELATIVE as u64)).to_le_bytes());
let rel = X86ELFRelocationNoAddend::read(&data, 0).unwrap();
assert_eq!(rel.r_offset, 0x200);
assert_eq!(rel.symbol_index(), 2);
assert_eq!(rel.reloc_type(), 8);
}
#[test]
fn test_elf_releident_defaults() {
let ident = X86ELFEIdent::new_default();
assert_eq!(ident.class, ELFCLASS64);
assert_eq!(ident.data, ELFDATA2LSB);
assert_eq!(ident.version, 1);
assert_eq!(ident.os_abi, ELFOSABI_NONE);
}
#[test]
fn test_elf_relocation_write() {
let mut rel = X86ELFRelocation::new_default();
rel.r_offset = 0x1000;
rel.set_reloc_info(1, R_X86_64_64 as u32);
rel.r_addend = 42;
let mut buf = vec![0u8; 24];
rel.write(&mut buf, 0);
let read_back = X86ELFRelocation::read(&buf, 0).unwrap();
assert_eq!(read_back.r_offset, 0x1000);
assert_eq!(read_back.symbol_index(), 1);
assert_eq!(read_back.reloc_type(), 1);
assert_eq!(read_back.r_addend, 42);
}
#[test]
fn test_macho_nlist64_write() {
let nlist = X86MachONlist64 {
n_strx: 0x42,
n_type: N_SECT | N_EXT,
n_sect: 3,
n_desc: N_WEAK_DEF,
n_value: 0x100005000,
};
let mut buf = vec![0u8; 16];
buf[0..4].copy_from_slice(&nlist.n_strx.to_le_bytes());
buf[4] = nlist.n_type;
buf[5] = nlist.n_sect;
buf[6..8].copy_from_slice(&nlist.n_desc.to_le_bytes());
buf[8..16].copy_from_slice(&nlist.n_value.to_le_bytes());
let read_back = X86MachONlist64::read(&buf, 0).unwrap();
assert_eq!(read_back.n_strx, 0x42);
assert_eq!(read_back.n_type, N_SECT | N_EXT);
assert_eq!(read_back.n_sect, 3);
assert_eq!(read_back.n_desc, N_WEAK_DEF);
assert_eq!(read_back.n_value, 0x100005000);
}
#[test]
fn test_macho_header_default() {
let hdr = X86MachOHeader::new_default();
assert_eq!(hdr.magic, MH_MAGIC_64);
assert_eq!(hdr.cputype, CPU_TYPE_X86_64);
assert_eq!(hdr.filetype, MH_EXECUTE);
assert!(hdr.is_64bit());
assert!(!hdr.is_big_endian());
}
#[test]
fn test_elfheader_eident_is_valid() {
let hdr = X86ELFHeader::new_default();
assert!(hdr.e_ident.is_valid());
let mut bad = X86ELFHeader::new_default();
bad.e_ident.magic[0] = 0;
assert!(!bad.e_ident.is_valid());
}
#[test]
fn test_data_directory_constants() {
assert_eq!(DATA_DIRECTORY_NAMES.len(), 16);
assert_eq!(DATA_DIRECTORY_NAMES[IMAGE_DIRECTORY_ENTRY_EXPORT], "Export");
assert_eq!(DATA_DIRECTORY_NAMES[IMAGE_DIRECTORY_ENTRY_IAT], "IAT");
assert_eq!(
DATA_DIRECTORY_NAMES[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT],
"DelayImport"
);
}
#[test]
fn test_pe_symbol_read_and_write() {
let mut sym = X86PESymbol::new_default();
sym.name_short.copy_from_slice(b"printf\0\0");
sym.value = 0x5000;
sym.section_number = 2;
sym.storage_class = IMAGE_SYM_CLASS_EXTERNAL;
sym.number_of_aux_symbols = 0;
let mut buf = vec![0u8; 18];
sym.write(&mut buf, 0);
let read_back = X86PESymbol::read(&buf, 0).unwrap();
assert_eq!(read_back.name_long, "printf");
assert_eq!(read_back.value, 0x5000);
assert_eq!(read_back.section_number, 2);
assert_eq!(read_back.storage_class, IMAGE_SYM_CLASS_EXTERNAL);
}
#[test]
fn test_pe_section_header_write() {
let mut sh = X86PESectionHeader::new_default();
sh.set_name(".data");
sh.virtual_address = 0x2000;
sh.size_of_raw_data = 0x1000;
sh.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
let mut buf = vec![0u8; 40];
sh.write(&mut buf, 0);
let read_back = X86PESectionHeader::read(&buf, 0).unwrap();
assert_eq!(read_back.name_str(), ".data");
assert_eq!(read_back.virtual_address, 0x2000);
assert_eq!(read_back.size_of_raw_data, 0x1000);
assert_eq!(
read_back.characteristics,
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
);
}
#[test]
fn test_pe_coff_header_write() {
let coff = X86PECoffHeader {
machine: IMAGE_FILE_MACHINE_I386,
number_of_sections: 4,
time_date_stamp: 0x12345678,
pointer_to_symbol_table: 0x1000,
number_of_symbols: 50,
size_of_optional_header: 0xE0,
characteristics: IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE,
};
let mut buf = vec![0u8; 20];
coff.write(&mut buf, 0);
let read_back = X86PECoffHeader::read(&buf, 0).unwrap();
assert_eq!(read_back.machine, IMAGE_FILE_MACHINE_I386);
assert_eq!(read_back.number_of_sections, 4);
assert_eq!(read_back.time_date_stamp, 0x12345678);
assert_eq!(read_back.pointer_to_symbol_table, 0x1000);
assert_eq!(read_back.number_of_symbols, 50);
}
#[test]
fn test_pe_data_directory_read_write() {
let dd = X86PEDataDirectory {
virtual_address: 0x6000,
size: 0x200,
};
let mut buf = vec![0u8; 8];
dd.write(&mut buf, 0);
let read_back = X86PEDataDirectory::read(&buf, 0).unwrap();
assert_eq!(read_back.virtual_address, 0x6000);
assert_eq!(read_back.size, 0x200);
}
#[test]
fn test_element_symbol_write() {
let sym = X86ELFSymbol {
st_name: 15,
st_info: (STB_GLOBAL << 4) | STT_OBJECT,
st_other: STV_PROTECTED,
st_shndx: 3,
st_value: 0x600000,
st_size: 0x80,
};
let mut buf = vec![0u8; 24];
sym.write(&mut buf, 0);
let read_back = X86ELFSymbol::read(&buf, 0).unwrap();
assert_eq!(read_back.st_name, 15);
assert_eq!(read_back.binding(), STB_GLOBAL);
assert_eq!(read_back.symbol_type(), STT_OBJECT);
assert_eq!(read_back.visibility(), STV_PROTECTED);
assert_eq!(read_back.st_shndx, 3);
assert_eq!(read_back.st_value, 0x600000);
assert_eq!(read_back.st_size, 0x80);
}
#[test]
fn test_elf_dynamic_write() {
let d = X86ELFDynamic {
d_tag: DT_FLAGS,
d_val: DF_BIND_NOW as u64,
};
let mut buf = vec![0u8; 16];
d.write(&mut buf, 0);
let read_back = X86ELFDynamic::read(&buf, 0).unwrap();
assert_eq!(read_back.d_tag, DT_FLAGS);
assert_eq!(read_back.d_val, DF_BIND_NOW as u64);
}
#[test]
fn test_elf_get_string_from_dynstr() {
let mut elf = X86ELFBinary::read(&[], true);
elf.dynstr_offset = 0x100;
elf.data.resize(0x200, 0);
elf.data[0x100..0x110].copy_from_slice(b"libpthread.so.0\0");
let sym = X86ELFSymbol {
st_name: 0,
..X86ELFSymbol::new_default()
};
assert_eq!(elf.get_symbol_name(&sym), "libpthread.so.0");
}
#[test]
fn test_macho_build_version_format_edge_cases() {
assert_eq!(X86MachOBuildVersion::format_version(0), "0.0.0");
assert_eq!(
X86MachOBuildVersion::format_version(0xFFFFFFFF),
"255.255.255"
);
}
#[test]
fn test_elf_program_header_read32() {
let mut data = vec![0u8; 32];
data[0..4].copy_from_slice(&PT_LOAD.to_le_bytes());
data[4..8].copy_from_slice(&0x100u32.to_le_bytes()); data[8..12].copy_from_slice(&0x8048000u32.to_le_bytes()); data[24..28].copy_from_slice(&(PF_R | PF_X).to_le_bytes());
let ph = X86ELFProgramHeader::read32(&data, 0).unwrap();
assert_eq!(ph.p_type, PT_LOAD);
assert_eq!(ph.p_offset, 0x100);
assert_eq!(ph.p_vaddr, 0x8048000);
assert_eq!(ph.p_flags, PF_R | PF_X);
}
#[test]
fn test_elf_section_header_read32() {
let mut data = vec![0u8; 40];
data[0..4].copy_from_slice(&1u32.to_le_bytes()); data[4..8].copy_from_slice(&SHT_NOBITS.to_le_bytes());
data[16..20].copy_from_slice(&0x2000u32.to_le_bytes()); data[20..24].copy_from_slice(&0x1000u32.to_le_bytes());
let sh = X86ELFSectionHeader::read32(&data, 0).unwrap();
assert_eq!(sh.sh_type, SHT_NOBITS);
assert_eq!(sh.sh_offset, 0x2000);
assert_eq!(sh.sh_size, 0x1000);
}
#[test]
fn test_pe_optional_header_default_64() {
let opt = X86PEOptionalHeader::new_default(true);
assert!(opt.is_pe32plus());
assert_eq!(opt.image_base, 0x140000000);
assert_eq!(opt.section_alignment, 0x1000);
assert_eq!(opt.file_alignment, 0x200);
}
#[test]
fn test_pe_optional_header_default_32() {
let opt = X86PEOptionalHeader::new_default(false);
assert!(!opt.is_pe32plus());
assert_eq!(opt.image_base, 0x00400000);
}
#[test]
fn test_elf_constants_shf() {
assert_eq!(SHF_WRITE, 1);
assert_eq!(SHF_ALLOC, 2);
assert_eq!(SHF_EXECINSTR, 4);
assert_eq!(SHF_TLS, 0x400);
assert_eq!(SHF_EXCLUDE, 0x80000000);
}
#[test]
fn test_elf_constants_dt() {
assert_eq!(DT_NEEDED, 1);
assert_eq!(DT_STRTAB, 5);
assert_eq!(DT_SYMTAB, 6);
assert_eq!(DT_RELA, 7);
assert_eq!(DT_GNU_HASH, 0x6FFFFEF5);
}
#[test]
fn test_elf_constants_pt() {
assert_eq!(PT_NULL, 0);
assert_eq!(PT_LOAD, 1);
assert_eq!(PT_DYNAMIC, 2);
assert_eq!(PT_INTERP, 3);
assert_eq!(PT_GNU_EH_FRAME, 0x6474E550);
assert_eq!(PT_GNU_STACK, 0x6474E551);
assert_eq!(PT_GNU_RELRO, 0x6474E552);
}
#[test]
fn test_elf_constants_sht() {
assert_eq!(SHT_NULL, 0);
assert_eq!(SHT_PROGBITS, 1);
assert_eq!(SHT_SYMTAB, 2);
assert_eq!(SHT_STRTAB, 3);
assert_eq!(SHT_RELA, 4);
assert_eq!(SHT_DYNAMIC, 6);
assert_eq!(SHT_NOBITS, 8);
assert_eq!(SHT_GNU_HASH, 0x6FFFFFF6);
}
#[test]
fn test_macho_platform_xros() {
assert_eq!(PLATFORM_XROS, 11);
assert_eq!(PLATFORM_XROSSIMULATOR, 12);
}
#[test]
fn test_pe_section_align_values() {
assert_eq!(IMAGE_SCN_ALIGN_1BYTES, 0x00100000);
assert_eq!(IMAGE_SCN_ALIGN_16BYTES, 0x00500000);
assert_eq!(IMAGE_SCN_ALIGN_4096BYTES, 0x00D00000);
assert_eq!(IMAGE_SCN_ALIGN_MASK, 0x00F00000);
}
#[test]
fn test_pe_sym_class_values() {
assert_eq!(IMAGE_SYM_CLASS_EXTERNAL, 2);
assert_eq!(IMAGE_SYM_CLASS_STATIC, 3);
assert_eq!(IMAGE_SYM_CLASS_FUNCTION, 101);
assert_eq!(IMAGE_SYM_CLASS_FILE, 103);
}
#[test]
fn test_pe_import_descriptor_read() {
let mut data = vec![0u8; 20];
data[0..4].copy_from_slice(&0x2000u32.to_le_bytes()); data[4..8].copy_from_slice(&0x12345678u32.to_le_bytes()); data[12..16].copy_from_slice(&0x3000u32.to_le_bytes()); data[16..20].copy_from_slice(&0x2000u32.to_le_bytes());
let desc = X86PEImportDescriptor::read(&data, 0).unwrap();
assert_eq!(desc.original_first_thunk, 0x2000);
assert_eq!(desc.time_date_stamp, 0x12345678);
assert_eq!(desc.forwarder_chain, 0);
assert_eq!(desc.name_rva, 0x3000);
assert_eq!(desc.first_thunk, 0x2000);
}
#[test]
fn test_pe_import_descriptor_resolve_name() {
let mut data = vec![0u8; 0x500];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let sec_off = 0x98 + 0xF0;
data[sec_off..sec_off + 8].copy_from_slice(b".rdata\0\0");
data[sec_off + 12..sec_off + 16].copy_from_slice(&0x1000u32.to_le_bytes()); data[sec_off + 8..sec_off + 12].copy_from_slice(&0x5000u32.to_le_bytes()); data[sec_off + 16..sec_off + 20].copy_from_slice(&0x5000u32.to_le_bytes()); data[sec_off + 20..sec_off + 24].copy_from_slice(&0x200u32.to_le_bytes()); data[0x86..0x88].copy_from_slice(&1u16.to_le_bytes());
data[0x300..0x318].copy_from_slice(b"KERNEL32.dll\0");
let pe = X86PEBinary::read(&data);
let mut desc = X86PEImportDescriptor {
original_first_thunk: 0,
time_date_stamp: 0,
forwarder_chain: 0,
name_rva: 0x1100,
first_thunk: 0,
name: String::new(),
};
desc.resolve_name(&pe);
assert_eq!(desc.name, "KERNEL32.dll");
}
#[test]
fn test_pe_thunk_data_read64() {
let mut data = vec![0u8; 8];
let addr: u64 = 0x8000000000001234;
data[0..8].copy_from_slice(&addr.to_le_bytes());
let thunk = X86PEThunkData::read64(&data, 0).unwrap();
assert!(thunk.is_ordinal);
assert_eq!(thunk.ordinal, 0x1234);
let addr2: u64 = 0x4000;
data[0..8].copy_from_slice(&addr2.to_le_bytes());
let thunk2 = X86PEThunkData::read64(&data, 0).unwrap();
assert!(!thunk2.is_ordinal);
assert_eq!(thunk2.address, 0x4000);
}
#[test]
fn test_pe_thunk_data_read32() {
let mut data = vec![0u8; 4];
let addr: u32 = 0x80001234;
data[0..4].copy_from_slice(&addr.to_le_bytes());
let thunk = X86PEThunkData::read32(&data, 0).unwrap();
assert!(thunk.is_ordinal);
assert_eq!(thunk.ordinal, 0x1234);
}
#[test]
fn test_pe_thunk_resolve_hint_name() {
let mut data = vec![0u8; 0x600];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let sec_off = 0x98 + 0xF0;
data[sec_off..sec_off + 8].copy_from_slice(b".rdata\0\0");
data[sec_off + 12..sec_off + 16].copy_from_slice(&0x1000u32.to_le_bytes());
data[sec_off + 8..sec_off + 12].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 16..sec_off + 20].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 20..sec_off + 24].copy_from_slice(&0x200u32.to_le_bytes());
data[0x86..0x88].copy_from_slice(&1u16.to_le_bytes());
data[0x400..0x404].copy_from_slice(&0x01E0u16.to_le_bytes()); data[0x404..0x414].copy_from_slice(b"CreateFileA\0");
let pe = X86PEBinary::read(&data);
let mut thunk = X86PEThunkData {
address: 0x1200,
is_ordinal: false,
ordinal: 0,
hint: 0,
name: String::new(),
};
thunk.resolve_hint_name(&pe, 0);
assert_eq!(thunk.hint, 0x1E0);
assert_eq!(thunk.name, "CreateFileA");
}
#[test]
fn test_pe_export_directory_parse() {
let mut data = vec![0u8; 0x800];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let sec_off = 0x98 + 0xF0;
data[sec_off..sec_off + 8].copy_from_slice(b".edata\0\0");
data[sec_off + 12..sec_off + 16].copy_from_slice(&0x1000u32.to_le_bytes());
data[sec_off + 8..sec_off + 12].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 16..sec_off + 20].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 20..sec_off + 24].copy_from_slice(&0x200u32.to_le_bytes());
data[0x86..0x88].copy_from_slice(&1u16.to_le_bytes());
let dd_off = 0x98 + 112;
data[dd_off..dd_off + 4].copy_from_slice(&0x1000u32.to_le_bytes());
data[dd_off + 4..dd_off + 8].copy_from_slice(&0x200u32.to_le_bytes());
data[0x200 + 12..0x200 + 16].copy_from_slice(&0x1200u32.to_le_bytes());
data[0x200 + 16..0x200 + 20].copy_from_slice(&1u32.to_le_bytes()); data[0x200 + 20..0x200 + 24].copy_from_slice(&1u32.to_le_bytes()); data[0x200 + 24..0x200 + 28].copy_from_slice(&1u32.to_le_bytes()); data[0x200 + 28..0x200 + 32].copy_from_slice(&0x1300u32.to_le_bytes()); data[0x200 + 32..0x200 + 36].copy_from_slice(&0x1400u32.to_le_bytes()); data[0x200 + 36..0x200 + 40].copy_from_slice(&0x1500u32.to_le_bytes());
data[0x400..0x420].copy_from_slice(b"TEST.DLL\0");
data[0x600..0x604].copy_from_slice(&0x1600u32.to_le_bytes()); data[0x700..0x702].copy_from_slice(&0u16.to_le_bytes()); data[0x500..0x504].copy_from_slice(&0x2000u32.to_le_bytes());
data[0x800..0x810].copy_from_slice(b"MyExport\0");
let pe = X86PEBinary::read(&data);
let export = X86PEExportDirectory::parse(&pe);
assert!(export.is_some());
let exp = export.unwrap();
assert_eq!(exp.dll_name, "TEST.DLL");
assert_eq!(exp.base, 1);
assert_eq!(exp.number_of_names, 1);
assert_eq!(exp.exports.len(), 1);
assert_eq!(exp.exports[0].0, "MyExport");
assert_eq!(exp.exports[0].1, 1);
}
#[test]
fn test_pe_export_directory_empty() {
let mut data = vec![0u8; 500];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let pe = X86PEBinary::read(&data);
assert!(X86PEExportDirectory::parse(&pe).is_none());
}
#[test]
fn test_pe_debug_entry_read() {
let mut data = vec![0u8; 28];
data[12..16].copy_from_slice(&IMAGE_DEBUG_TYPE_CODEVIEW.to_le_bytes());
data[16..20].copy_from_slice(&0x100u32.to_le_bytes()); data[20..24].copy_from_slice(&0x5000u32.to_le_bytes()); data[24..28].copy_from_slice(&0x400u32.to_le_bytes());
let entry = X86PEDebugDirectoryEntry::read(&data, 0).unwrap();
assert_eq!(entry.debug_type, IMAGE_DEBUG_TYPE_CODEVIEW);
assert_eq!(entry.type_name, "CODEVIEW");
assert_eq!(entry.size_of_data, 0x100);
}
#[test]
fn test_pe_debug_types() {
assert_eq!(IMAGE_DEBUG_TYPE_CODEVIEW, 2);
assert_eq!(IMAGE_DEBUG_TYPE_COFF, 1);
assert_eq!(IMAGE_DEBUG_TYPE_FPO, 3);
assert_eq!(IMAGE_DEBUG_TYPE_REPRO, 16);
}
#[test]
fn test_pe_codeview_rsds_parse() {
let mut data = vec![0u8; 50];
data[0..4].copy_from_slice(&CODEVIEW_SIGNATURE_RSDS.to_le_bytes());
for i in 0..16 {
data[4 + i] = (i + 1) as u8;
}
data[20..24].copy_from_slice(&1u32.to_le_bytes()); data[24..44].copy_from_slice(b"test.pdb\0");
let cv = X86PECodeViewInfo::parse(&data).unwrap();
assert_eq!(cv.signature, CODEVIEW_SIGNATURE_RSDS);
assert_eq!(cv.age, 1);
assert_eq!(cv.pdb_name, "test.pdb");
let guid = cv.guid_string();
assert!(guid.contains('-'));
}
#[test]
fn test_pe_codeview_nb10_parse() {
let mut data = vec![0u8; 30];
data[0..4].copy_from_slice(&CODEVIEW_SIGNATURE_NB10.to_le_bytes());
data[12..16].copy_from_slice(&5u32.to_le_bytes()); data[16..28].copy_from_slice(b"nb10.pdb\0");
let cv = X86PECodeViewInfo::parse(&data).unwrap();
assert_eq!(cv.signature, CODEVIEW_SIGNATURE_NB10);
assert_eq!(cv.age, 5);
assert_eq!(cv.pdb_name, "nb10.pdb");
assert_eq!(cv.guid_string(), "");
}
#[test]
fn test_pe_codeview_invalid() {
let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
assert!(X86PECodeViewInfo::parse(&data).is_none());
}
#[test]
fn test_pe_tls_directory_read64() {
let mut data = vec![0u8; 40];
data[0..8].copy_from_slice(&0x1000u64.to_le_bytes()); data[8..16].copy_from_slice(&0x2000u64.to_le_bytes()); data[16..24].copy_from_slice(&0x3000u64.to_le_bytes()); data[24..32].copy_from_slice(&0x4000u64.to_le_bytes()); data[32..36].copy_from_slice(&0x100u32.to_le_bytes()); data[36..40].copy_from_slice(&0x20u32.to_le_bytes());
let tls = X86PETLSDirectory::read64(&data, 0).unwrap();
assert_eq!(tls.start_address_of_raw_data, 0x1000);
assert_eq!(tls.end_address_of_raw_data, 0x2000);
assert_eq!(tls.address_of_index, 0x3000);
assert_eq!(tls.address_of_callbacks, 0x4000);
assert_eq!(tls.size_of_zero_fill, 0x100);
assert_eq!(tls.characteristics, 0x20);
}
#[test]
fn test_pe_load_config_read64() {
let mut data = vec![0u8; 200];
data[0..4].copy_from_slice(&140u32.to_le_bytes()); data[4..8].copy_from_slice(&0xDEADBEEFu32.to_le_bytes()); data[12..16].copy_from_slice(&0x100u32.to_le_bytes()); data[80..88].copy_from_slice(&0x12345678_9ABCDEF0u64.to_le_bytes()); data[88..96].copy_from_slice(&0xCAFEu64.to_le_bytes()); data[144..148].copy_from_slice(&0x500u32.to_le_bytes());
let lc = X86PELoadConfigDirectory::read64(&data, 0).unwrap();
assert_eq!(lc.size, 140);
assert_eq!(lc.security_cookie, 0xCAFE);
assert_eq!(lc.guard_flags, 0x500);
}
#[test]
fn test_macho_dyld_info_read() {
let mut data = vec![0u8; 48];
data[0..4].copy_from_slice(&LC_DYLD_INFO_ONLY.to_le_bytes());
data[4..8].copy_from_slice(&48u32.to_le_bytes());
data[8..12].copy_from_slice(&0x1000u32.to_le_bytes()); data[12..16].copy_from_slice(&0x100u32.to_le_bytes());
data[16..20].copy_from_slice(&0x2000u32.to_le_bytes()); data[20..24].copy_from_slice(&0x200u32.to_le_bytes());
data[24..28].copy_from_slice(&0x3000u32.to_le_bytes()); data[28..32].copy_from_slice(&0x50u32.to_le_bytes());
data[40..44].copy_from_slice(&0x5000u32.to_le_bytes()); data[44..48].copy_from_slice(&0x400u32.to_le_bytes());
let info = X86MachODyldInfo::read(&data, 0).unwrap();
assert_eq!(info.rebase_off, 0x1000);
assert_eq!(info.rebase_size, 0x100);
assert_eq!(info.bind_off, 0x2000);
assert_eq!(info.weak_bind_off, 0x3000);
assert_eq!(info.weak_bind_size, 0x50);
assert_eq!(info.export_off, 0x5000);
assert_eq!(info.export_size, 0x400);
}
#[test]
fn test_macho_encryption_info_read() {
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&LC_ENCRYPTION_INFO_64.to_le_bytes());
data[4..8].copy_from_slice(&24u32.to_le_bytes());
data[8..12].copy_from_slice(&0x4000u32.to_le_bytes()); data[12..16].copy_from_slice(&0x10000u32.to_le_bytes()); data[16..20].copy_from_slice(&0u32.to_le_bytes());
let info = X86MachOEncryptionInfo::read(&data, 0).unwrap();
assert!(!info.is_encrypted());
assert_eq!(info.cryptoff, 0x4000);
assert_eq!(info.cryptsize, 0x10000);
data[16..20].copy_from_slice(&1u32.to_le_bytes());
let info2 = X86MachOEncryptionInfo::read(&data, 0).unwrap();
assert!(info2.is_encrypted());
}
#[test]
fn test_macho_rpath_read() {
let name = b"@executable_path/../Frameworks\0";
let cmdsize = 12 + name.len() as u32;
let mut data = vec![0u8; cmdsize as usize];
data[0..4].copy_from_slice(&LC_RPATH.to_le_bytes());
data[4..8].copy_from_slice(&cmdsize.to_le_bytes());
data[8..12].copy_from_slice(&12u32.to_le_bytes()); data[12..12 + name.len()].copy_from_slice(name);
let rpath = X86MachORPath::read(&data, 0).unwrap();
assert_eq!(rpath.cmd, LC_RPATH);
assert_eq!(rpath.path, "@executable_path/../Frameworks");
}
#[test]
fn test_macho_source_version_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&LC_SOURCE_VERSION.to_le_bytes());
data[4..8].copy_from_slice(&16u32.to_le_bytes());
let ver: u64 = (1 << 40) | (2 << 30) | (3 << 20) | (4 << 10) | 5;
data[8..16].copy_from_slice(&ver.to_le_bytes());
let sv = X86MachOSourceVersion::read(&data, 0).unwrap();
assert_eq!(sv.version_string(), "1.2.3.4.5");
}
#[test]
fn test_macho_linker_option_read() {
let opt1 = b"-framework\0";
let opt2 = b"CoreFoundation\0";
let cmdsize = 12 + opt1.len() + opt2.len();
let mut data = vec![0u8; cmdsize];
data[0..4].copy_from_slice(&LC_LINKER_OPTION.to_le_bytes());
data[4..8].copy_from_slice(&(cmdsize as u32).to_le_bytes());
data[8..12].copy_from_slice(&2u32.to_le_bytes());
data[12..12 + opt1.len()].copy_from_slice(opt1);
data[12 + opt1.len()..12 + opt1.len() + opt2.len()].copy_from_slice(opt2);
let lo = X86MachOLinkerOption::read(&data, 0).unwrap();
assert_eq!(lo.count, 2);
assert_eq!(lo.options.len(), 2);
assert_eq!(lo.options[0], "-framework");
assert_eq!(lo.options[1], "CoreFoundation");
}
#[test]
fn test_macho_function_starts_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&LC_FUNCTION_STARTS.to_le_bytes());
data[4..8].copy_from_slice(&16u32.to_le_bytes());
data[8..12].copy_from_slice(&0x5000u32.to_le_bytes());
data[12..16].copy_from_slice(&0x200u32.to_le_bytes());
let fs = X86MachOFunctionStarts::read(&data, 0).unwrap();
assert_eq!(fs.data_offset, 0x5000);
assert_eq!(fs.data_size, 0x200);
}
#[test]
fn test_macho_data_in_code_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&LC_DATA_IN_CODE.to_le_bytes());
data[4..8].copy_from_slice(&16u32.to_le_bytes());
data[8..12].copy_from_slice(&0x7000u32.to_le_bytes());
data[12..16].copy_from_slice(&0x80u32.to_le_bytes());
let dic = X86MachODataInCode::read(&data, 0).unwrap();
assert_eq!(dic.data_offset, 0x7000);
assert_eq!(dic.data_size, 0x80);
}
#[test]
fn test_macho_note_read() {
let mut data = vec![0u8; 40];
data[0..4].copy_from_slice(&LC_NOTE.to_le_bytes());
data[4..8].copy_from_slice(&40u32.to_le_bytes());
data[8..24].copy_from_slice(b"GNU\0\0\0\0\0\0\0\0\0\0\0\0\0");
data[24..32].copy_from_slice(&0x5000u64.to_le_bytes());
data[32..40].copy_from_slice(&0x100u64.to_le_bytes());
let note = X86MachONote::read(&data, 0).unwrap();
assert_eq!(note.owner_string(), "GNU");
assert_eq!(note.offset, 0x5000);
assert_eq!(note.size, 0x100);
}
#[test]
fn test_macho_code_signature_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&LC_CODE_SIGNATURE.to_le_bytes());
data[4..8].copy_from_slice(&16u32.to_le_bytes());
data[8..12].copy_from_slice(&0x8000u32.to_le_bytes());
data[12..16].copy_from_slice(&0x4000u32.to_le_bytes());
let cs = X86MachOCodeSignature::read(&data, 0).unwrap();
assert_eq!(cs.dataoff, 0x8000);
assert_eq!(cs.datasize, 0x4000);
}
#[test]
fn test_macho_segment_split_info_read() {
let mut data = vec![0u8; 16];
data[0..4].copy_from_slice(&LC_SEGMENT_SPLIT_INFO.to_le_bytes());
data[4..8].copy_from_slice(&16u32.to_le_bytes());
data[8..12].copy_from_slice(&0x9000u32.to_le_bytes());
data[12..16].copy_from_slice(&0x200u32.to_le_bytes());
let ssi = X86MachOSegmentSplitInfo::read(&data, 0).unwrap();
assert_eq!(ssi.dataoff, 0x9000);
assert_eq!(ssi.datasize, 0x200);
}
#[test]
fn test_pe_resource_directory_no_resource() {
let mut data = vec![0u8; 500];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let pe = X86PEBinary::read(&data);
assert!(X86PEResourceDirectory::parse(&pe).is_none());
}
#[test]
fn test_all_x86_64_reloc_names() {
let relocs = [
(0, "R_X86_64_NONE"),
(1, "R_X86_64_64"),
(2, "R_X86_64_PC32"),
(3, "R_X86_64_GOT32"),
(4, "R_X86_64_PLT32"),
(5, "R_X86_64_COPY"),
(6, "R_X86_64_GLOB_DAT"),
(7, "R_X86_64_JUMP_SLOT"),
(8, "R_X86_64_RELATIVE"),
(9, "R_X86_64_GOTPCREL"),
(10, "R_X86_64_32"),
(11, "R_X86_64_32S"),
];
for (val, name) in &relocs {
assert_eq!(X86ELFRelocType::from_u32(*val).name(), *name);
}
}
#[test]
fn test_all_i386_reloc_names() {
let relocs = [
(0, "R_386_NONE"),
(1, "R_386_32"),
(2, "R_386_PC32"),
(3, "R_386_GOT32"),
(4, "R_386_PLT32"),
(5, "R_386_COPY"),
(8, "R_386_RELATIVE"),
];
for (val, name) in &relocs {
assert_eq!(X86ELFRelocType386::from_u32(*val).name(), *name);
}
}
#[test]
fn test_detect_partial_elf() {
let data = vec![0x7F, b'E', b'L', b'F'];
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::ELF);
}
#[test]
fn test_detect_partial_pe() {
let data = vec![b'M', b'Z'];
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::PECOFF);
}
#[test]
fn test_detect_partial_macho() {
let mut data = vec![0u8; 4];
data[0..4].copy_from_slice(&MH_MAGIC_64.to_le_bytes());
let bf = X86BinaryFormat::detect(data);
assert_eq!(bf.format, X86BinaryFormatType::MachO);
}
#[test]
fn test_elf_osabi_detection() {
let mut data = vec![0u8; 64];
data[0] = 0x7F;
data[1] = b'E';
data[2] = b'L';
data[3] = b'F';
data[4] = 2;
data[7] = ELFOSABI_FREEBSD;
let bf = X86BinaryFormat::detect(data);
assert!(bf.os_abi.contains("FREEBSD"));
}
#[test]
fn test_pe_import_directory_parse() {
let mut data = vec![0u8; 0x1000];
data[0] = b'M';
data[1] = b'Z';
data[0x3C..0x40].copy_from_slice(&0x80u32.to_le_bytes());
data[0x80] = b'P';
data[0x81] = b'E';
data[0x82] = 0;
data[0x83] = 0;
data[0x84..0x86].copy_from_slice(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes());
data[0x94..0x96].copy_from_slice(&0xF0u16.to_le_bytes());
data[0x98..0x9A].copy_from_slice(&IMAGE_NT_OPTIONAL_HDR64_MAGIC.to_le_bytes());
let sec_off = 0x98 + 0xF0;
data[sec_off..sec_off + 8].copy_from_slice(b".rdata\0\0");
data[sec_off + 12..sec_off + 16].copy_from_slice(&0x2000u32.to_le_bytes());
data[sec_off + 8..sec_off + 12].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 16..sec_off + 20].copy_from_slice(&0x5000u32.to_le_bytes());
data[sec_off + 20..sec_off + 24].copy_from_slice(&0x200u32.to_le_bytes());
data[0x86..0x88].copy_from_slice(&1u16.to_le_bytes());
let dd_base = 0x98 + 112; data[dd_base + 8..dd_base + 12].copy_from_slice(&0x2000u32.to_le_bytes()); data[dd_base + 12..dd_base + 16].copy_from_slice(&0x200u32.to_le_bytes());
let pe = X86PEBinary::read(&data);
let import = X86PEImportDirectory::parse(&pe);
data[0x200 + 12..0x200 + 16].copy_from_slice(&0x2100u32.to_le_bytes()); data[0x200..0x204].copy_from_slice(&0x2200u32.to_le_bytes()); data[0x2F0..0x300].copy_from_slice(b"USER32.dll\0"); data[0x300..0x310].copy_from_slice(b"USER32.dll\0");
let pe2 = X86PEBinary::read(&data);
let import2 = X86PEImportDirectory::parse(&pe2);
assert!(import2.is_some());
let imp = import2.unwrap();
assert_eq!(imp.descriptors.len(), 1);
}
#[test]
fn test_elveident_not_little_endian() {
let mut ident = X86ELFEIdent::new_default();
ident.data = ELFDATA2MSB;
assert!(!ident.is_little_endian());
assert!(ident.is_64bit());
assert!(ident.is_valid());
}
#[test]
fn test_elf_section_tls_flags() {
let mut sh = X86ELFSectionHeader::new_default();
sh.sh_flags = SHF_TLS | SHF_ALLOC;
assert!(sh.flags_string().contains("T"));
assert!(sh.flags_string().contains("A"));
}
#[test]
fn test_elf_section_group_flags() {
let mut sh = X86ELFSectionHeader::new_default();
sh.sh_flags = SHF_GROUP;
assert!(sh.flags_string().contains("G"));
}
#[test]
fn test_elf_section_merge_flags() {
let mut sh = X86ELFSectionHeader::new_default();
sh.sh_flags = SHF_MERGE | SHF_STRINGS;
assert!(sh.flags_string().contains("M"));
assert!(sh.flags_string().contains("S"));
}
#[test]
fn test_elf_symbol_binding_generic() {
let mut sym = X86ELFSymbol::new_default();
for b in 0..16u8 {
sym.st_info = (b << 4) | 0;
let name = sym.binding_name();
match b {
STB_LOCAL => assert_eq!(name, "LOCAL"),
STB_GLOBAL => assert_eq!(name, "GLOBAL"),
STB_WEAK => assert_eq!(name, "WEAK"),
STB_GNU_UNIQUE => assert_eq!(name, "GNU_UNIQUE"),
_ => assert_eq!(name, "UNKNOWN"),
}
}
}
#[test]
fn test_elf_symbol_type_generic() {
let mut sym = X86ELFSymbol::new_default();
for t in 0..16u8 {
sym.st_info = 0 | t;
let name = sym.type_name();
match t {
STT_NOTYPE => assert_eq!(name, "NOTYPE"),
STT_OBJECT => assert_eq!(name, "OBJECT"),
STT_FUNC => assert_eq!(name, "FUNC"),
STT_SECTION => assert_eq!(name, "SECTION"),
STT_FILE => assert_eq!(name, "FILE"),
STT_COMMON => assert_eq!(name, "COMMON"),
STT_TLS => assert_eq!(name, "TLS"),
STT_GNU_IFUNC => assert_eq!(name, "GNU_IFUNC"),
_ => assert_eq!(name, "UNKNOWN"),
}
}
}
#[test]
fn test_macho_nlist_stab() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_STAB;
assert!(nlist.is_stab());
assert_eq!(nlist.type_name(), "STAB");
assert_eq!(nlist.binding(), "STAB");
}
#[test]
fn test_macho_nlist_abs() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_ABS;
assert_eq!(nlist.nlist_type(), N_ABS);
assert_eq!(nlist.type_name(), "ABS");
}
#[test]
fn test_macho_nlist_weak_ref() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_SECT | N_EXT;
nlist.n_desc = N_WEAK_REF;
assert_eq!(nlist.binding(), "WEAK_REF");
}
#[test]
fn test_macho_nlist_pext() {
let mut nlist = X86MachONlist64::new_default();
nlist.n_type = N_PEXT | N_SECT | N_EXT;
assert!(nlist.is_pext());
assert!(nlist.is_ext());
}
}