goblin_experimental/mach/
header.rs1use 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
13pub const MH_NOUNDEFS: u32 = 0x1;
16pub const MH_INCRLINK: u32 = 0x2;
19pub const MH_DYLDLINK: u32 = 0x4;
21pub const MH_BINDATLOAD: u32 = 0x8;
23pub const MH_PREBOUND: u32 = 0x10;
25pub const MH_SPLIT_SEGS: u32 = 0x20;
27pub const MH_LAZY_INIT: u32 = 0x40;
30pub const MH_TWOLEVEL: u32 = 0x80;
32pub const MH_FORCE_FLAT: u32 = 0x100;
34pub const MH_NOMULTIDEFS: u32 = 0x200;
37pub const MH_NOFIXPREBINDING: u32 = 0x400;
39pub const MH_PREBINDABLE: u32 = 0x800;
41pub const MH_ALLMODSBOUND: u32 = 0x1000;
44pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000;
46pub const MH_CANONICAL: u32 = 0x4000;
48pub const MH_WEAK_DEFINES: u32 = 0x8000;
50pub const MH_BINDS_TO_WEAK: u32 = 0x10000;
52pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000;
55pub const MH_ROOT_SAFE: u32 = 0x40000;
57pub const MH_SETUID_SAFE: u32 = 0x80000;
59pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x0010_0000;
62pub const MH_PIE: u32 = 0x0020_0000;
65pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x0040_0000;
69pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x0080_0000;
71pub const MH_NO_HEAP_EXECUTION: u32 = 0x0100_0000;
74
75pub 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
112pub const MH_MAGIC: u32 = 0xfeed_face;
114pub const MH_CIGAM: u32 = 0xcefa_edfe;
115pub const MH_MAGIC_64: u32 = 0xfeed_facf;
117pub const MH_CIGAM_64: u32 = 0xcffa_edfe;
118
119pub const MH_OBJECT: u32 = 0x1;
122pub const MH_EXECUTE: u32 = 0x2;
124pub const MH_FVMLIB: u32 = 0x3;
126pub const MH_CORE: u32 = 0x4;
128pub const MH_PRELOAD: u32 = 0x5;
130pub const MH_DYLIB: u32 = 0x6;
132pub const MH_DYLINKER: u32 = 0x7;
134pub const MH_BUNDLE: u32 = 0x8;
136pub const MH_DYLIB_STUB: u32 = 0x9;
138pub const MH_DSYM: u32 = 0xa;
140pub const MH_KEXT_BUNDLE: u32 = 0xb;
142pub 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)]
165pub struct Header32 {
167 pub magic: u32,
169 pub cputype: u32,
171 pub cpusubtype: u32,
173 pub filetype: u32,
175 pub ncmds: u32,
177 pub sizeofcmds: u32,
179 pub flags: u32,
181}
182
183pub const SIZEOF_HEADER_32: usize = 0x1c;
184
185unsafe impl Plain for Header32 {}
186
187impl Header32 {
188 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)]
199pub struct Header64 {
201 pub magic: u32,
203 pub cputype: u32,
205 pub cpusubtype: u32,
207 pub filetype: u32,
209 pub ncmds: u32,
211 pub sizeofcmds: u32,
213 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 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)]
234pub struct Header {
236 pub magic: u32,
237 pub cputype: u32,
238 pub cpusubtype: u32,
239 pub filetype: u32,
241 pub ncmds: usize,
243 pub sizeofcmds: u32,
245 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 pub fn cputype(&self) -> CpuType {
332 self.cputype
333 }
334 pub fn cpusubtype(&self) -> CpuSubType {
336 self.cpusubtype & !CPU_SUBTYPE_MASK
337 }
338 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}