basic macho parsing
This commit is contained in:
parent
4f09f3b8aa
commit
65278fbb33
2
osx/src/lib.rs
Normal file
2
osx/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod macho;
|
||||||
|
pub use macho::Macho;
|
331
osx/src/macho.rs
Normal file
331
osx/src/macho.rs
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
use std::io::{Read, Write, Seek, SeekFrom};
|
||||||
|
use std::fmt;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, LittleEndian, BigEndian, ReadBytesExt};
|
||||||
|
|
||||||
|
trait ReadString: Read {
|
||||||
|
fn read_utf8(self: &mut Self, len: usize) -> Result<String, Box<dyn Error>> {
|
||||||
|
let mut buf = vec![0u8; len];
|
||||||
|
self.read_exact(&mut buf)?;
|
||||||
|
Ok(String::from_utf8(buf.split(|&b| b == 0).next().unwrap().to_vec())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> ReadString for R {}
|
||||||
|
|
||||||
|
const MH_MAGIC: u32 = 0xfeedface;
|
||||||
|
const MH_CIGAM: u32 = MH_MAGIC.swap_bytes();
|
||||||
|
const MH_MAGIC_64: u32 = 0xfeedfacf;
|
||||||
|
const MH_CIGAM_64: u32 = MH_MAGIC_64.swap_bytes();
|
||||||
|
|
||||||
|
const LC_SEGMENT: u32 = 0x1;
|
||||||
|
const LC_SEGMENT_64: u32 = 0x19;
|
||||||
|
const LC_CODE_SIGNATURE: u32 = 0x1d;
|
||||||
|
|
||||||
|
pub struct Header {
|
||||||
|
magic: u32,
|
||||||
|
cputype: u32,
|
||||||
|
cpusubtype: u32,
|
||||||
|
filetype: u32,
|
||||||
|
ncmds: u32,
|
||||||
|
sizeofcmds: u32,
|
||||||
|
flags: u32,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
fn parse<O: ByteOrder, R: Read>(magic: u32, cursor: &mut R) -> Option<Header> {
|
||||||
|
let cputype: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let cpusubtype: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let filetype: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let ncmds: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let sizeofcmds: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let flags: u32 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved: u32 = {
|
||||||
|
if magic == MH_MAGIC_64 || magic == MH_CIGAM_64 {
|
||||||
|
cursor.read_u32::<O>().ok()?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Header {
|
||||||
|
magic,
|
||||||
|
cputype,
|
||||||
|
cpusubtype,
|
||||||
|
filetype,
|
||||||
|
ncmds,
|
||||||
|
sizeofcmds,
|
||||||
|
flags,
|
||||||
|
reserved,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Section {
|
||||||
|
sectname: String,
|
||||||
|
segname: String,
|
||||||
|
addr: u64,
|
||||||
|
size: u64,
|
||||||
|
offset: u32,
|
||||||
|
align: u32,
|
||||||
|
reloff: u32,
|
||||||
|
nreloc: u32,
|
||||||
|
flags: u32,
|
||||||
|
reserved1: u32,
|
||||||
|
reserved2: u32,
|
||||||
|
reserved3: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Section {
|
||||||
|
fn parse_32<O: ByteOrder, R: Read>(cursor: &mut R) -> Option<Section> {
|
||||||
|
let sectname = cursor.read_utf8(16).ok()?;
|
||||||
|
let segname = cursor.read_utf8(16).ok()?;
|
||||||
|
let addr = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let size = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let offset = cursor.read_u32::<O>().ok()?;
|
||||||
|
let align = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reloff = cursor.read_u32::<O>().ok()?;
|
||||||
|
let nreloc = cursor.read_u32::<O>().ok()?;
|
||||||
|
let flags = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved1 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved2 = cursor.read_u32::<O>().ok()?;
|
||||||
|
Some(Section {
|
||||||
|
sectname,
|
||||||
|
segname,
|
||||||
|
addr,
|
||||||
|
size,
|
||||||
|
offset,
|
||||||
|
align,
|
||||||
|
reloff,
|
||||||
|
nreloc,
|
||||||
|
flags,
|
||||||
|
reserved1,
|
||||||
|
reserved2,
|
||||||
|
reserved3: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_64<O: ByteOrder, R: Read>(cursor: &mut R) -> Option<Section> {
|
||||||
|
let sectname = cursor.read_utf8(16).ok()?;
|
||||||
|
let segname = cursor.read_utf8(16).ok()?;
|
||||||
|
let addr = cursor.read_u64::<O>().ok()? as u64;
|
||||||
|
let size = cursor.read_u64::<O>().ok()? as u64;
|
||||||
|
let offset = cursor.read_u32::<O>().ok()?;
|
||||||
|
let align = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reloff = cursor.read_u32::<O>().ok()?;
|
||||||
|
let nreloc = cursor.read_u32::<O>().ok()?;
|
||||||
|
let flags = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved1 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved2 = cursor.read_u32::<O>().ok()?;
|
||||||
|
let reserved3 = cursor.read_u32::<O>().ok()?;
|
||||||
|
Some(Section {
|
||||||
|
sectname,
|
||||||
|
segname,
|
||||||
|
addr,
|
||||||
|
size,
|
||||||
|
offset,
|
||||||
|
align,
|
||||||
|
reloff,
|
||||||
|
nreloc,
|
||||||
|
flags,
|
||||||
|
reserved1,
|
||||||
|
reserved2,
|
||||||
|
reserved3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Segment {
|
||||||
|
segname: String,
|
||||||
|
vmaddr: u64,
|
||||||
|
vmsize: u64,
|
||||||
|
fileoff: u64,
|
||||||
|
filesize: u64,
|
||||||
|
maxprot: u32,
|
||||||
|
initprot: u32,
|
||||||
|
flags: u32,
|
||||||
|
sections: Vec<Section>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Linkedit {
|
||||||
|
pub dataoff: u32,
|
||||||
|
pub datasize: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LoadCommand {
|
||||||
|
Segment(Segment),
|
||||||
|
Codesignature(Linkedit),
|
||||||
|
Cmd(u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadCommand {
|
||||||
|
fn parse<O: ByteOrder, R: Read>(cursor: &mut R) -> Option<LoadCommand> {
|
||||||
|
let cmd = cursor.read_u32::<O>().ok()?;
|
||||||
|
let cmdsize = cursor.read_u32::<O>().ok()?;
|
||||||
|
|
||||||
|
if cmdsize <= 8 {
|
||||||
|
// impossible
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
match cmd {
|
||||||
|
LC_SEGMENT => {
|
||||||
|
let segname = cursor.read_utf8(16).ok()?;
|
||||||
|
let vmaddr = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let vmsize = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let fileoff = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let filesize = cursor.read_u32::<O>().ok()? as u64;
|
||||||
|
let maxprot = cursor.read_u32::<O>().ok()?;
|
||||||
|
let initprot = cursor.read_u32::<O>().ok()?;
|
||||||
|
let nsects = cursor.read_u32::<O>().ok()?;
|
||||||
|
let flags = cursor.read_u32::<O>().ok()?;
|
||||||
|
let sections = std::iter::repeat_with(|| Section::parse_32::<O, R>(cursor))
|
||||||
|
.take_while(|x| x.is_some())
|
||||||
|
.take(nsects as usize)
|
||||||
|
.filter_map(|x| x)
|
||||||
|
.collect::<Vec<Section>>();
|
||||||
|
|
||||||
|
Some(LoadCommand::Segment(Segment {
|
||||||
|
segname,
|
||||||
|
vmaddr,
|
||||||
|
vmsize,
|
||||||
|
fileoff,
|
||||||
|
filesize,
|
||||||
|
maxprot,
|
||||||
|
initprot,
|
||||||
|
flags,
|
||||||
|
sections,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
LC_SEGMENT_64 => {
|
||||||
|
let segname = cursor.read_utf8(16).ok()?;
|
||||||
|
let vmaddr = cursor.read_u64::<O>().ok()?;
|
||||||
|
let vmsize = cursor.read_u64::<O>().ok()?;
|
||||||
|
let fileoff = cursor.read_u64::<O>().ok()?;
|
||||||
|
let filesize = cursor.read_u64::<O>().ok()?;
|
||||||
|
let maxprot = cursor.read_u32::<O>().ok()?;
|
||||||
|
let initprot = cursor.read_u32::<O>().ok()?;
|
||||||
|
let nsects = cursor.read_u32::<O>().ok()?;
|
||||||
|
let flags = cursor.read_u32::<O>().ok()?;
|
||||||
|
let sections = std::iter::repeat_with(|| Section::parse_64::<O, R>(cursor))
|
||||||
|
.take_while(|x| x.is_some())
|
||||||
|
.take(nsects as usize)
|
||||||
|
.filter_map(|x| x)
|
||||||
|
.collect::<Vec<Section>>();
|
||||||
|
|
||||||
|
Some(LoadCommand::Segment(Segment {
|
||||||
|
segname,
|
||||||
|
vmaddr,
|
||||||
|
vmsize,
|
||||||
|
fileoff,
|
||||||
|
filesize,
|
||||||
|
maxprot,
|
||||||
|
initprot,
|
||||||
|
flags,
|
||||||
|
sections,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
LC_CODE_SIGNATURE => {
|
||||||
|
let dataoff = cursor.read_u32::<O>().ok()?;
|
||||||
|
let datasize = cursor.read_u32::<O>().ok()?;
|
||||||
|
Some(LoadCommand::Codesignature(Linkedit {
|
||||||
|
dataoff, datasize
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let mut buf = vec![0u8; cmdsize as usize - 4*2];
|
||||||
|
cursor.read_exact(&mut buf).ok()?;
|
||||||
|
Some(LoadCommand::Cmd(cmd, cmdsize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LoadCommand {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
LoadCommand::Segment(segment) => {
|
||||||
|
writeln!(f, "{}", segment.segname)?;
|
||||||
|
segment.sections.iter().for_each(|section| {
|
||||||
|
write!(f, " {}.{}\n", section.segname, section.sectname).ok();
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
LoadCommand::Codesignature(linkedit) => {
|
||||||
|
write!(f, "Codesignature(dataoff=0x{:x}, datasize={})", linkedit.dataoff, linkedit.datasize)
|
||||||
|
},
|
||||||
|
LoadCommand::Cmd(cmd, cmdsize) => {
|
||||||
|
write!(f, "cmd=0x{:x} cmdsize={}", cmd, cmdsize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Macho {
|
||||||
|
pub header: Header,
|
||||||
|
pub commands: Vec<LoadCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Macho {
|
||||||
|
pub fn codesignature(self: &Self) -> Option<&Linkedit> {
|
||||||
|
self.commands
|
||||||
|
.iter()
|
||||||
|
.find_map(|cmd| match cmd {
|
||||||
|
LoadCommand::Codesignature(linkedit) => Some(linkedit),
|
||||||
|
_ => None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MachoParseError {
|
||||||
|
message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MachoParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "MachoParseError(message={})", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MachoParseError {}
|
||||||
|
|
||||||
|
impl MachoParseError {
|
||||||
|
fn new(msg: &str) -> Box<MachoParseError> {
|
||||||
|
Box::new(MachoParseError {
|
||||||
|
message: String::from(msg),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Macho {
|
||||||
|
pub fn from<R: Read>(cursor: &mut R) -> Result<Macho, Box<MachoParseError>> {
|
||||||
|
let magic = cursor.read_u32::<LittleEndian>()
|
||||||
|
.or(Err(MachoParseError::new("Cannot read magic")))?;
|
||||||
|
if magic == MH_MAGIC || magic == MH_MAGIC_64 {
|
||||||
|
Self::parse::<LittleEndian, R>(magic, cursor)
|
||||||
|
} else {
|
||||||
|
Self::parse::<BigEndian, R>(magic, cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<O: ByteOrder, R: Read>(magic: u32, cursor: &mut R) -> Result<Macho, Box<MachoParseError>> {
|
||||||
|
let header = Header::parse::<O, R>(magic, cursor)
|
||||||
|
.ok_or(MachoParseError::new("Cannot parse macho header"))?;
|
||||||
|
let commands = std::iter::repeat_with(|| LoadCommand::parse::<O, R>(cursor))
|
||||||
|
.take_while(|x| x.is_some())
|
||||||
|
.take(header.ncmds as usize)
|
||||||
|
.filter_map(|x| x)
|
||||||
|
.collect::<Vec<LoadCommand>>();
|
||||||
|
|
||||||
|
Ok(Macho {
|
||||||
|
header,
|
||||||
|
commands
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user