goblin_experimental/mach/
header.rs

1//! A header contains minimal architecture information, the binary kind, the number of load commands, as well as an endianness hint
2
3use core::fmt;
4use plain::Plain;
5use scroll::ctx;
6use scroll::ctx::SizeWith;
7use scroll::{Pread, Pwrite, SizeWith};
8
9use crate::container::{self, Container};
10use crate::error;
11use crate::mach::constants::cputype::{CpuSubType, CpuType, CPU_SUBTYPE_MASK};
12
13// Constants for the flags field of the mach_header
14/// the object file has no undefined references
15pub const MH_NOUNDEFS: u32 = 0x1;
16/// the object file is the output of an incremental link against a base file and can't be
17/// link edited again
18pub const MH_INCRLINK: u32 = 0x2;
19/// the object file is input for the dynamic linker and can't be staticly link edited again
20pub const MH_DYLDLINK: u32 = 0x4;
21/// the object file's undefined references are bound by the dynamic linker when loaded.
22pub const MH_BINDATLOAD: u32 = 0x8;
23/// the file has its dynamic undefined references prebound.
24pub const MH_PREBOUND: u32 = 0x10;
25/// the file has its read-only and read-write segments split
26pub const MH_SPLIT_SEGS: u32 = 0x20;
27/// the shared library init routine is to be run lazily via catching memory faults to its writeable
28/// segments (obsolete)
29pub const MH_LAZY_INIT: u32 = 0x40;
30/// the image is using two-level name space bindings
31pub const MH_TWOLEVEL: u32 = 0x80;
32/// the executable is forcing all images to use flat name space bindings
33pub const MH_FORCE_FLAT: u32 = 0x100;
34/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the
35/// two-level namespace hints can always be used.
36pub const MH_NOMULTIDEFS: u32 = 0x200;
37/// do not have dyld notify the prebinding agent about this executable
38pub const MH_NOFIXPREBINDING: u32 = 0x400;
39/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
40pub const MH_PREBINDABLE: u32 = 0x800;
41/// indicates that this binary binds to all two-level namespace modules of its dependent libraries.
42/// Only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
43pub const MH_ALLMODSBOUND: u32 = 0x1000;
44/// safe to divide up the sections into sub-sections via symbols for dead code stripping
45pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000;
46/// the binary has been canonicalized via the unprebind operation
47pub const MH_CANONICAL: u32 = 0x4000;
48/// the final linked image contains external weak symbols
49pub const MH_WEAK_DEFINES: u32 = 0x8000;
50/// the final linked image uses weak symbols
51pub const MH_BINDS_TO_WEAK: u32 = 0x10000;
52/// When this bit is set, all stacks in the task will be given stack execution privilege.
53/// Only used in MH_EXECUTE filetypes.
54pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000;
55/// When this bit is set, the binary declares it is safe for use in processes with uid zero
56pub const MH_ROOT_SAFE: u32 = 0x40000;
57/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
58pub const MH_SETUID_SAFE: u32 = 0x80000;
59/// When this bit is set on a dylib,  the static linker does not need to examine dependent dylibs to
60/// see if any are re-exported
61pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x0010_0000;
62/// When this bit is set, the OS will load the main executable at a random address.
63/// Only used in MH_EXECUTE filetypes.
64pub const MH_PIE: u32 = 0x0020_0000;
65/// Only for use on dylibs.  When linking against a dylib that has this bit set, the static linker
66/// will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being
67/// referenced from the dylib.
68pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x0040_0000;
69/// Contains a section of type S_THREAD_LOCAL_VARIABLES
70pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x0080_0000;
71/// When this bit is set, the OS will run the main executable with a non-executable heap even on
72/// platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
73pub const MH_NO_HEAP_EXECUTION: u32 = 0x0100_0000;
74
75// TODO: verify this number is correct, it was previously 0x02000000 which could indicate a typo/data entry error
76/// The code was linked for use in an application extension.
77pub const MH_APP_EXTENSION_SAFE: u32 = 0x0200_0000;
78
79#[inline(always)]
80pub fn flag_to_str(flag: u32) -> &'static str {
81    match flag {
82        MH_NOUNDEFS => "MH_NOUNDEFS",
83        MH_INCRLINK => "MH_INCRLINK",
84        MH_DYLDLINK => "MH_DYLDLINK",
85        MH_BINDATLOAD => "MH_BINDATLOAD",
86        MH_PREBOUND => "MH_PREBOUND",
87        MH_SPLIT_SEGS => "MH_SPLIT_SEGS",
88        MH_LAZY_INIT => "MH_LAZY_INIT",
89        MH_TWOLEVEL => "MH_TWOLEVEL",
90        MH_FORCE_FLAT => "MH_FORCE_FLAT",
91        MH_NOMULTIDEFS => "MH_NOMULTIDEFS",
92        MH_NOFIXPREBINDING => "MH_NOFIXPREBINDING",
93        MH_PREBINDABLE => "MH_PREBINDABLE ",
94        MH_ALLMODSBOUND => "MH_ALLMODSBOUND",
95        MH_SUBSECTIONS_VIA_SYMBOLS => "MH_SUBSECTIONS_VIA_SYMBOLS",
96        MH_CANONICAL => "MH_CANONICAL",
97        MH_WEAK_DEFINES => "MH_WEAK_DEFINES",
98        MH_BINDS_TO_WEAK => "MH_BINDS_TO_WEAK",
99        MH_ALLOW_STACK_EXECUTION => "MH_ALLOW_STACK_EXECUTION",
100        MH_ROOT_SAFE => "MH_ROOT_SAFE",
101        MH_SETUID_SAFE => "MH_SETUID_SAFE",
102        MH_NO_REEXPORTED_DYLIBS => "MH_NO_REEXPORTED_DYLIBS",
103        MH_PIE => "MH_PIE",
104        MH_DEAD_STRIPPABLE_DYLIB => "MH_DEAD_STRIPPABLE_DYLIB",
105        MH_HAS_TLV_DESCRIPTORS => "MH_HAS_TLV_DESCRIPTORS",
106        MH_NO_HEAP_EXECUTION => "MH_NO_HEAP_EXECUTION",
107        MH_APP_EXTENSION_SAFE => "MH_APP_EXTENSION_SAFE",
108        _ => "UNKNOWN FLAG",
109    }
110}
111
112/// Mach Header magic constant
113pub const MH_MAGIC: u32 = 0xfeed_face;
114pub const MH_CIGAM: u32 = 0xcefa_edfe;
115/// Mach Header magic constant for 64-bit
116pub const MH_MAGIC_64: u32 = 0xfeed_facf;
117pub const MH_CIGAM_64: u32 = 0xcffa_edfe;
118
119// Constants for the filetype field of the mach_header
120/// relocatable object file
121pub const MH_OBJECT: u32 = 0x1;
122/// demand paged executable file
123pub const MH_EXECUTE: u32 = 0x2;
124/// fixed VM shared library file
125pub const MH_FVMLIB: u32 = 0x3;
126/// core file
127pub const MH_CORE: u32 = 0x4;
128/// preloaded executable file
129pub const MH_PRELOAD: u32 = 0x5;
130/// dynamically bound shared library
131pub const MH_DYLIB: u32 = 0x6;
132/// dynamic link editor
133pub const MH_DYLINKER: u32 = 0x7;
134/// dynamically bound bundle file
135pub const MH_BUNDLE: u32 = 0x8;
136/// shared library stub for static linking only, no section contents
137pub const MH_DYLIB_STUB: u32 = 0x9;
138/// companion file with only debug sections
139pub const MH_DSYM: u32 = 0xa;
140/// x86_64 kexts
141pub const MH_KEXT_BUNDLE: u32 = 0xb;
142/// set of mach-o's
143pub const MH_FILESET: u32 = 0xc;
144
145pub fn filetype_to_str(filetype: u32) -> &'static str {
146    match filetype {
147        MH_OBJECT => "OBJECT",
148        MH_EXECUTE => "EXECUTE",
149        MH_FVMLIB => "FVMLIB",
150        MH_CORE => "CORE",
151        MH_PRELOAD => "PRELOAD",
152        MH_DYLIB => "DYLIB",
153        MH_DYLINKER => "DYLINKER",
154        MH_BUNDLE => "BUNDLE",
155        MH_DYLIB_STUB => "DYLIB_STUB",
156        MH_DSYM => "DSYM",
157        MH_KEXT_BUNDLE => "KEXT_BUNDLE",
158        MH_FILESET => "FILESET",
159        _ => "UNKNOWN FILETYPE",
160    }
161}
162
163#[repr(C)]
164#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)]
165/// A 32-bit Mach-o header
166pub struct Header32 {
167    /// mach magic number identifier
168    pub magic: u32,
169    /// cpu specifier
170    pub cputype: u32,
171    /// machine specifier
172    pub cpusubtype: u32,
173    /// type of file
174    pub filetype: u32,
175    /// number of load commands
176    pub ncmds: u32,
177    /// the size of all the load commands
178    pub sizeofcmds: u32,
179    /// flags
180    pub flags: u32,
181}
182
183pub const SIZEOF_HEADER_32: usize = 0x1c;
184
185unsafe impl Plain for Header32 {}
186
187impl Header32 {
188    /// Transmutes the given byte array into the corresponding 32-bit Mach-o header
189    pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_32]) -> &Self {
190        plain::from_bytes(bytes).unwrap()
191    }
192    pub fn size(&self) -> usize {
193        SIZEOF_HEADER_32
194    }
195}
196
197#[repr(C)]
198#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)]
199/// A 64-bit Mach-o header
200pub struct Header64 {
201    /// mach magic number identifier
202    pub magic: u32,
203    /// cpu specifier
204    pub cputype: u32,
205    /// machine specifier
206    pub cpusubtype: u32,
207    /// type of file
208    pub filetype: u32,
209    /// number of load commands
210    pub ncmds: u32,
211    /// the size of all the load commands
212    pub sizeofcmds: u32,
213    /// flags
214    pub flags: u32,
215    pub reserved: u32,
216}
217
218unsafe impl Plain for Header64 {}
219
220pub const SIZEOF_HEADER_64: usize = 32;
221
222impl Header64 {
223    /// Transmutes the given byte array into the corresponding 64-bit Mach-o header
224    pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_64]) -> &Self {
225        plain::from_bytes(bytes).unwrap()
226    }
227    pub fn size(&self) -> usize {
228        SIZEOF_HEADER_64
229    }
230}
231
232#[repr(C)]
233#[derive(Clone, Copy, Default)]
234/// Generic sized header
235pub struct Header {
236    pub magic: u32,
237    pub cputype: u32,
238    pub cpusubtype: u32,
239    /// type of file
240    pub filetype: u32,
241    /// number of load commands
242    pub ncmds: usize,
243    /// the size of all the load commands
244    pub sizeofcmds: u32,
245    /// flags
246    pub flags: u32,
247    pub reserved: u32,
248}
249
250impl fmt::Debug for Header {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        f.debug_struct("Header")
253            .field("magic", &format_args!("0x{:x}", self.magic))
254            .field("cputype", &self.cputype())
255            .field("cpusubtype", &format_args!("0x{:x}", self.cpusubtype()))
256            .field("filetype", &filetype_to_str(self.filetype))
257            .field("ncmds", &self.ncmds)
258            .field("sizeofcmds", &self.sizeofcmds)
259            .field("flags", &format_args!("0x{:x}", self.flags))
260            .field("reserved", &format_args!("0x{:x}", self.reserved))
261            .finish()
262    }
263}
264
265impl From<Header32> for Header {
266    fn from(header: Header32) -> Self {
267        Header {
268            magic: header.magic,
269            cputype: header.cputype,
270            cpusubtype: header.cpusubtype,
271            filetype: header.filetype,
272            ncmds: header.ncmds as usize,
273            sizeofcmds: header.sizeofcmds,
274            flags: header.flags,
275            reserved: 0,
276        }
277    }
278}
279
280impl From<Header> for Header32 {
281    fn from(header: Header) -> Self {
282        Header32 {
283            magic: header.magic,
284            cputype: header.cputype,
285            cpusubtype: header.cpusubtype,
286            filetype: header.filetype,
287            ncmds: header.ncmds as u32,
288            sizeofcmds: header.sizeofcmds,
289            flags: header.flags,
290        }
291    }
292}
293
294impl From<Header64> for Header {
295    fn from(header: Header64) -> Self {
296        Header {
297            magic: header.magic,
298            cputype: header.cputype,
299            cpusubtype: header.cpusubtype,
300            filetype: header.filetype,
301            ncmds: header.ncmds as usize,
302            sizeofcmds: header.sizeofcmds,
303            flags: header.flags,
304            reserved: header.reserved,
305        }
306    }
307}
308
309impl From<Header> for Header64 {
310    fn from(header: Header) -> Self {
311        Header64 {
312            magic: header.magic,
313            cputype: header.cputype,
314            cpusubtype: header.cpusubtype,
315            filetype: header.filetype,
316            ncmds: header.ncmds as u32,
317            sizeofcmds: header.sizeofcmds,
318            flags: header.flags,
319            reserved: header.reserved,
320        }
321    }
322}
323
324impl Header {
325    pub fn new(ctx: container::Ctx) -> Self {
326        let mut header = Header::default();
327        header.magic = if ctx.is_big() { MH_MAGIC_64 } else { MH_MAGIC };
328        header
329    }
330    /// Returns the cpu type
331    pub fn cputype(&self) -> CpuType {
332        self.cputype
333    }
334    /// Returns the cpu subtype with the capabilities removed
335    pub fn cpusubtype(&self) -> CpuSubType {
336        self.cpusubtype & !CPU_SUBTYPE_MASK
337    }
338    /// Returns the capabilities of the CPU
339    pub fn cpu_caps(&self) -> u32 {
340        (self.cpusubtype & CPU_SUBTYPE_MASK) >> 24
341    }
342}
343
344impl ctx::SizeWith<container::Ctx> for Header {
345    fn size_with(container: &container::Ctx) -> usize {
346        match container.container {
347            Container::Little => SIZEOF_HEADER_32,
348            Container::Big => SIZEOF_HEADER_64,
349        }
350    }
351}
352
353impl ctx::SizeWith<Container> for Header {
354    fn size_with(container: &Container) -> usize {
355        match container {
356            Container::Little => SIZEOF_HEADER_32,
357            Container::Big => SIZEOF_HEADER_64,
358        }
359    }
360}
361
362impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Header {
363    type Error = crate::error::Error;
364    fn try_from_ctx(
365        bytes: &'a [u8],
366        container::Ctx { le, container }: container::Ctx,
367    ) -> error::Result<(Self, usize)> {
368        let size = bytes.len();
369        if size < SIZEOF_HEADER_32 || size < SIZEOF_HEADER_64 {
370            let error =
371                error::Error::Malformed("bytes size is smaller than a Mach-o header".into());
372            Err(error)
373        } else {
374            match container {
375                Container::Little => {
376                    let header = bytes.pread_with::<Header32>(0, le)?;
377                    Ok((Header::from(header), SIZEOF_HEADER_32))
378                }
379                Container::Big => {
380                    let header = bytes.pread_with::<Header64>(0, le)?;
381                    Ok((Header::from(header), SIZEOF_HEADER_64))
382                }
383            }
384        }
385    }
386}
387
388impl ctx::TryIntoCtx<container::Ctx> for Header {
389    type Error = crate::error::Error;
390    fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> error::Result<usize> {
391        match ctx.container {
392            Container::Little => {
393                bytes.pwrite_with(Header32::from(self), 0, ctx.le)?;
394            }
395            Container::Big => {
396                bytes.pwrite_with(Header64::from(self), 0, ctx.le)?;
397            }
398        };
399        Ok(Header::size_with(&ctx))
400    }
401}
402
403impl ctx::IntoCtx<container::Ctx> for Header {
404    fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
405        bytes.pwrite_with(self, 0, ctx).unwrap();
406    }
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412    use std::mem::size_of;
413
414    #[test]
415    fn test_parse_armv7_header() {
416        use crate::mach::constants::cputype::CPU_TYPE_ARM;
417        const CPU_SUBTYPE_ARM_V7: u32 = 9;
418        use super::Header;
419        use crate::container::{Container, Ctx, Endian};
420        use scroll::Pread;
421        let bytes = b"\xce\xfa\xed\xfe\x0c\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x06\x00\x00\x00\x8c\r\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x18\x00\x00\x00\xe0\xf7B\xbb\x1c\xf50w\xa6\xf7u\xa3\xba(";
422        let header: Header = bytes
423            .pread_with(0, Ctx::new(Container::Little, Endian::Little))
424            .unwrap();
425        assert_eq!(header.cputype, CPU_TYPE_ARM);
426        assert_eq!(header.cpusubtype, CPU_SUBTYPE_ARM_V7);
427    }
428
429    #[test]
430    fn sizeof_header32() {
431        assert_eq!(SIZEOF_HEADER_32, size_of::<Header32>());
432    }
433
434    #[test]
435    fn sizeof_header64() {
436        assert_eq!(SIZEOF_HEADER_64, size_of::<Header64>());
437    }
438}