goblin/mach/
segment.rs

1use scroll::ctx::{self, SizeWith};
2use scroll::{Pread, Pwrite};
3
4use log::{debug, warn};
5
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8use core::fmt;
9use core::ops::{Deref, DerefMut};
10
11use crate::container;
12use crate::error;
13
14use crate::mach::constants::{S_GB_ZEROFILL, S_THREAD_LOCAL_ZEROFILL, S_ZEROFILL, SECTION_TYPE};
15use crate::mach::load_command::{
16    LC_SEGMENT, LC_SEGMENT_64, SIZEOF_SECTION_32, SIZEOF_SECTION_64, SIZEOF_SEGMENT_COMMAND_32,
17    SIZEOF_SEGMENT_COMMAND_64, Section32, Section64, SegmentCommand32, SegmentCommand64,
18};
19use crate::mach::relocation::RelocationInfo;
20
21pub struct RelocationIterator<'a> {
22    data: &'a [u8],
23    nrelocs: usize,
24    offset: usize,
25    count: usize,
26    ctx: scroll::Endian,
27}
28
29impl<'a> Iterator for RelocationIterator<'a> {
30    type Item = error::Result<RelocationInfo>;
31    fn next(&mut self) -> Option<Self::Item> {
32        if self.count >= self.nrelocs {
33            None
34        } else {
35            self.count += 1;
36            match self.data.gread_with(&mut self.offset, self.ctx) {
37                Ok(res) => Some(Ok(res)),
38                Err(e) => Some(Err(e.into())),
39            }
40        }
41    }
42}
43
44/// Generalized 32/64 bit Section
45#[derive(Default)]
46pub struct Section {
47    /// name of this section
48    pub sectname: [u8; 16],
49    /// segment this section goes in
50    pub segname: [u8; 16],
51    /// memory address of this section
52    pub addr: u64,
53    /// size in bytes of this section
54    pub size: u64,
55    /// file offset of this section
56    pub offset: u32,
57    /// section alignment (power of 2)
58    pub align: u32,
59    /// file offset of relocation entries
60    pub reloff: u32,
61    /// number of relocation entries
62    pub nreloc: u32,
63    /// flags (section type and attributes
64    pub flags: u32,
65}
66
67impl Section {
68    /// The name of this section
69    pub fn name(&self) -> error::Result<&str> {
70        Ok(self.sectname.pread::<&str>(0)?)
71    }
72    /// The containing segment's name
73    pub fn segname(&self) -> error::Result<&str> {
74        Ok(self.segname.pread::<&str>(0)?)
75    }
76    /// Iterate this sections relocations given `data`; `data` must be the original binary
77    pub fn iter_relocations<'b>(
78        &self,
79        data: &'b [u8],
80        ctx: container::Ctx,
81    ) -> RelocationIterator<'b> {
82        let offset = self.reloff as usize;
83        debug!(
84            "Relocations for {} starting at offset: {:#x}",
85            self.name().unwrap_or("BAD_SECTION_NAME"),
86            offset
87        );
88        RelocationIterator {
89            offset,
90            nrelocs: self.nreloc as usize,
91            count: 0,
92            data,
93            ctx: ctx.le,
94        }
95    }
96}
97
98impl From<Section> for Section64 {
99    fn from(section: Section) -> Self {
100        Section64 {
101            sectname: section.sectname,
102            segname: section.segname,
103            addr: section.addr as u64,
104            size: section.size as u64,
105            offset: section.offset,
106            align: section.align,
107            reloff: section.reloff,
108            nreloc: section.nreloc,
109            flags: section.flags,
110            reserved1: 0,
111            reserved2: 0,
112            reserved3: 0,
113        }
114    }
115}
116
117impl From<Section> for Section32 {
118    fn from(section: Section) -> Self {
119        Section32 {
120            sectname: section.sectname,
121            segname: section.segname,
122            addr: section.addr as u32,
123            size: section.size as u32,
124            offset: section.offset,
125            align: section.align,
126            reloff: section.reloff,
127            nreloc: section.nreloc,
128            flags: section.flags,
129            reserved1: 0,
130            reserved2: 0,
131        }
132    }
133}
134
135impl fmt::Debug for Section {
136    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
137        fmt.debug_struct("Section")
138            .field("sectname", &self.name().unwrap())
139            .field("segname", &self.segname().unwrap())
140            .field("addr", &self.addr)
141            .field("size", &self.size)
142            .field("offset", &self.offset)
143            .field("align", &self.align)
144            .field("reloff", &self.reloff)
145            .field("nreloc", &self.nreloc)
146            .field("flags", &self.flags)
147            .finish()
148    }
149}
150
151impl From<Section32> for Section {
152    fn from(section: Section32) -> Self {
153        Section {
154            sectname: section.sectname,
155            segname: section.segname,
156            addr: u64::from(section.addr),
157            size: u64::from(section.size),
158            offset: section.offset,
159            align: section.align,
160            reloff: section.reloff,
161            nreloc: section.nreloc,
162            flags: section.flags,
163        }
164    }
165}
166
167impl From<Section64> for Section {
168    fn from(section: Section64) -> Self {
169        Section {
170            sectname: section.sectname,
171            segname: section.segname,
172            addr: section.addr,
173            size: section.size,
174            offset: section.offset,
175            align: section.align,
176            reloff: section.reloff,
177            nreloc: section.nreloc,
178            flags: section.flags,
179        }
180    }
181}
182
183impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Section {
184    type Error = crate::error::Error;
185    fn try_from_ctx(bytes: &'a [u8], ctx: container::Ctx) -> Result<(Self, usize), Self::Error> {
186        match ctx.container {
187            container::Container::Little => {
188                let section = Section::from(bytes.pread_with::<Section32>(0, ctx.le)?);
189                Ok((section, SIZEOF_SECTION_32))
190            }
191            container::Container::Big => {
192                let section = Section::from(bytes.pread_with::<Section64>(0, ctx.le)?);
193                Ok((section, SIZEOF_SECTION_64))
194            }
195        }
196    }
197}
198
199impl ctx::SizeWith<container::Ctx> for Section {
200    fn size_with(ctx: &container::Ctx) -> usize {
201        match ctx.container {
202            container::Container::Little => SIZEOF_SECTION_32,
203            container::Container::Big => SIZEOF_SECTION_64,
204        }
205    }
206}
207
208impl ctx::TryIntoCtx<container::Ctx> for Section {
209    type Error = crate::error::Error;
210    fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::Error> {
211        if ctx.is_big() {
212            bytes.pwrite_with::<Section64>(self.into(), 0, ctx.le)?;
213        } else {
214            bytes.pwrite_with::<Section32>(self.into(), 0, ctx.le)?;
215        }
216        Ok(Self::size_with(&ctx))
217    }
218}
219
220impl ctx::IntoCtx<container::Ctx> for Section {
221    fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
222        bytes.pwrite_with(self, 0, ctx).unwrap();
223    }
224}
225
226pub struct SectionIterator<'a> {
227    data: &'a [u8],
228    count: usize,
229    offset: usize,
230    idx: usize,
231    ctx: container::Ctx,
232}
233
234pub type SectionData<'a> = &'a [u8];
235
236impl<'a> ::core::iter::ExactSizeIterator for SectionIterator<'a> {
237    fn len(&self) -> usize {
238        self.count
239    }
240}
241
242impl<'a> Iterator for SectionIterator<'a> {
243    type Item = error::Result<(Section, SectionData<'a>)>;
244    fn next(&mut self) -> Option<Self::Item> {
245        if self.idx >= self.count {
246            None
247        } else {
248            self.idx += 1;
249            match self.data.gread_with::<Section>(&mut self.offset, self.ctx) {
250                Ok(section) => {
251                    let section_type = section.flags & SECTION_TYPE;
252                    let data = if section_type == S_ZEROFILL
253                        || section_type == S_GB_ZEROFILL
254                        || section_type == S_THREAD_LOCAL_ZEROFILL
255                    {
256                        &[]
257                    } else {
258                        // it's not uncommon to encounter macho files where files are
259                        // truncated but the sections are still remaining in the header.
260                        // Because of this we want to not panic here but instead just
261                        // slice down to a empty data slice.  This way only if code
262                        // actually needs to access those sections it will fall over.
263                        self.data
264                            .get(section.offset as usize..)
265                            .unwrap_or_else(|| {
266                                warn!(
267                                    "section #{} offset {} out of bounds",
268                                    self.idx, section.offset
269                                );
270                                &[]
271                            })
272                            .get(..section.size as usize)
273                            .unwrap_or_else(|| {
274                                warn!("section #{} size {} out of bounds", self.idx, section.size);
275                                &[]
276                            })
277                    };
278                    Some(Ok((section, data)))
279                }
280                Err(e) => Some(Err(e)),
281            }
282        }
283    }
284}
285
286impl<'a, 'b> IntoIterator for &'b Segment<'a> {
287    type Item = error::Result<(Section, SectionData<'a>)>;
288    type IntoIter = SectionIterator<'a>;
289    fn into_iter(self) -> Self::IntoIter {
290        SectionIterator {
291            data: self.raw_data,
292            count: self.nsects as usize,
293            offset: self.offset + Segment::size_with(&self.ctx),
294            idx: 0,
295            ctx: self.ctx,
296        }
297    }
298}
299
300/// Generalized 32/64 bit Segment Command
301pub struct Segment<'a> {
302    pub cmd: u32,
303    pub cmdsize: u32,
304    pub segname: [u8; 16],
305    pub vmaddr: u64,
306    pub vmsize: u64,
307    pub fileoff: u64,
308    pub filesize: u64,
309    pub maxprot: u32,
310    pub initprot: u32,
311    pub nsects: u32,
312    pub flags: u32,
313    pub data: &'a [u8],
314    offset: usize,
315    raw_data: &'a [u8],
316    ctx: container::Ctx,
317}
318
319impl<'a> From<Segment<'a>> for SegmentCommand64 {
320    fn from(segment: Segment<'a>) -> Self {
321        SegmentCommand64 {
322            cmd: segment.cmd,
323            cmdsize: segment.cmdsize,
324            segname: segment.segname,
325            vmaddr: segment.vmaddr as u64,
326            vmsize: segment.vmsize as u64,
327            fileoff: segment.fileoff as u64,
328            filesize: segment.filesize as u64,
329            maxprot: segment.maxprot,
330            initprot: segment.initprot,
331            nsects: segment.nsects,
332            flags: segment.flags,
333        }
334    }
335}
336
337impl<'a> From<Segment<'a>> for SegmentCommand32 {
338    fn from(segment: Segment<'a>) -> Self {
339        SegmentCommand32 {
340            cmd: segment.cmd,
341            cmdsize: segment.cmdsize,
342            segname: segment.segname,
343            vmaddr: segment.vmaddr as u32,
344            vmsize: segment.vmsize as u32,
345            fileoff: segment.fileoff as u32,
346            filesize: segment.filesize as u32,
347            maxprot: segment.maxprot,
348            initprot: segment.initprot,
349            nsects: segment.nsects,
350            flags: segment.flags,
351        }
352    }
353}
354
355impl<'a> fmt::Debug for Segment<'a> {
356    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
357        fmt.debug_struct("Segment")
358            .field("cmd", &self.cmd)
359            .field("cmdsize", &self.cmdsize)
360            .field("segname", &self.segname.pread::<&str>(0).unwrap())
361            .field("vmaddr", &self.vmaddr)
362            .field("vmsize", &self.vmsize)
363            .field("fileoff", &self.fileoff)
364            .field("filesize", &self.filesize)
365            .field("maxprot", &self.maxprot)
366            .field("initprot", &self.initprot)
367            .field("nsects", &self.nsects)
368            .field("flags", &self.flags)
369            .field("data", &self.data.len())
370            .field(
371                "sections()",
372                &self.sections().map(|sections| {
373                    sections
374                        .into_iter()
375                        .map(|(section, _)| section)
376                        .collect::<Vec<_>>()
377                }),
378            )
379            .finish()
380    }
381}
382
383impl<'a> ctx::SizeWith<container::Ctx> for Segment<'a> {
384    fn size_with(ctx: &container::Ctx) -> usize {
385        match ctx.container {
386            container::Container::Little => SIZEOF_SEGMENT_COMMAND_32,
387            container::Container::Big => SIZEOF_SEGMENT_COMMAND_64,
388        }
389    }
390}
391
392impl<'a> ctx::TryIntoCtx<container::Ctx> for Segment<'a> {
393    type Error = crate::error::Error;
394    fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::Error> {
395        let segment_size = Self::size_with(&ctx);
396        // should be able to write the section data inline after this, but not working at the moment
397        //let section_size = bytes.pwrite(data, segment_size)?;
398        //debug!("Segment size: {} raw section data size: {}", segment_size, data.len());
399        if ctx.is_big() {
400            bytes.pwrite_with::<SegmentCommand64>(self.into(), 0, ctx.le)?;
401        } else {
402            bytes.pwrite_with::<SegmentCommand32>(self.into(), 0, ctx.le)?;
403        }
404        //debug!("Section size: {}", section_size);
405        Ok(segment_size)
406    }
407}
408
409impl<'a> ctx::IntoCtx<container::Ctx> for Segment<'a> {
410    fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
411        bytes.pwrite_with(self, 0, ctx).unwrap();
412    }
413}
414
415/// Read data that belongs to a segment if the offset is within the boundaries of bytes.
416fn segment_data(bytes: &[u8], fileoff: u64, filesize: u64) -> Result<&[u8], error::Error> {
417    let data: &[u8] = if filesize != 0 {
418        bytes.pread_with(fileoff as usize, filesize as usize)?
419    } else {
420        &[]
421    };
422    Ok(data)
423}
424
425impl<'a> Segment<'a> {
426    /// Create a new, blank segment, with cmd either `LC_SEGMENT_64`, or `LC_SEGMENT`, depending on `ctx`.
427    /// **NB** You are responsible for providing a correctly marshalled byte array as the sections. You should not use this for anything other than writing.
428    pub fn new(ctx: container::Ctx, sections: &'a [u8]) -> Self {
429        Segment {
430            cmd: if ctx.is_big() {
431                LC_SEGMENT_64
432            } else {
433                LC_SEGMENT
434            },
435            cmdsize: (Self::size_with(&ctx) + sections.len()) as u32,
436            segname: [0; 16],
437            vmaddr: 0,
438            vmsize: 0,
439            fileoff: 0,
440            filesize: 0,
441            maxprot: 0,
442            initprot: 0,
443            nsects: 0,
444            flags: 0,
445            data: sections,
446            offset: 0,
447            raw_data: &[],
448            ctx,
449        }
450    }
451    /// Get the name of this segment
452    pub fn name(&self) -> error::Result<&str> {
453        Ok(self.segname.pread::<&str>(0)?)
454    }
455    /// Get the sections from this segment, erroring if any section couldn't be retrieved
456    pub fn sections(&self) -> error::Result<Vec<(Section, SectionData<'a>)>> {
457        let mut sections = Vec::new();
458        for section in self.into_iter() {
459            sections.push(section?);
460        }
461        Ok(sections)
462    }
463    /// Convert the raw C 32-bit segment command to a generalized version
464    pub fn from_32(
465        bytes: &'a [u8],
466        segment: &SegmentCommand32,
467        offset: usize,
468        ctx: container::Ctx,
469    ) -> Result<Self, error::Error> {
470        Self::from_32_impl(bytes, segment, offset, ctx, false)
471    }
472
473    /// Convert the raw C 32-bit segment command to a generalized version
474    pub fn from_32_lossy(
475        bytes: &'a [u8],
476        segment: &SegmentCommand32,
477        offset: usize,
478        ctx: container::Ctx,
479    ) -> Result<Self, error::Error> {
480        Self::from_32_impl(bytes, segment, offset, ctx, true)
481    }
482
483    pub(crate) fn from_32_impl(
484        bytes: &'a [u8],
485        segment: &SegmentCommand32,
486        offset: usize,
487        ctx: container::Ctx,
488        lossy: bool,
489    ) -> Result<Self, error::Error> {
490        Ok(Segment {
491            cmd: segment.cmd,
492            cmdsize: segment.cmdsize,
493            segname: segment.segname,
494            vmaddr: u64::from(segment.vmaddr),
495            vmsize: u64::from(segment.vmsize),
496            fileoff: u64::from(segment.fileoff),
497            filesize: u64::from(segment.filesize),
498            maxprot: segment.maxprot,
499            initprot: segment.initprot,
500            nsects: segment.nsects,
501            flags: segment.flags,
502            data: match segment_data(
503                bytes,
504                u64::from(segment.fileoff),
505                u64::from(segment.filesize),
506            ) {
507                Ok(v) => v,
508                Err(_) if lossy => &[],
509                Err(e) => return Err(e),
510            },
511            offset,
512            raw_data: bytes,
513            ctx,
514        })
515    }
516
517    /// Convert the raw C 64-bit segment command to a generalized version
518    pub fn from_64(
519        bytes: &'a [u8],
520        segment: &SegmentCommand64,
521        offset: usize,
522        ctx: container::Ctx,
523    ) -> Result<Self, error::Error> {
524        Self::from_64_impl(bytes, segment, offset, ctx, false)
525    }
526
527    /// Convert the raw C 64-bit segment command to a generalized version
528    pub fn from_64_lossy(
529        bytes: &'a [u8],
530        segment: &SegmentCommand64,
531        offset: usize,
532        ctx: container::Ctx,
533    ) -> Result<Self, error::Error> {
534        Self::from_64_impl(bytes, segment, offset, ctx, true)
535    }
536
537    pub(crate) fn from_64_impl(
538        bytes: &'a [u8],
539        segment: &SegmentCommand64,
540        offset: usize,
541        ctx: container::Ctx,
542        lossy: bool,
543    ) -> Result<Self, error::Error> {
544        Ok(Segment {
545            cmd: segment.cmd,
546            cmdsize: segment.cmdsize,
547            segname: segment.segname,
548            vmaddr: segment.vmaddr,
549            vmsize: segment.vmsize,
550            fileoff: segment.fileoff,
551            filesize: segment.filesize,
552            maxprot: segment.maxprot,
553            initprot: segment.initprot,
554            nsects: segment.nsects,
555            flags: segment.flags,
556            data: match segment_data(bytes, segment.fileoff, segment.filesize) {
557                Ok(v) => v,
558                Err(_) if lossy => &[],
559                Err(e) => return Err(e),
560            },
561            offset,
562            raw_data: bytes,
563            ctx,
564        })
565    }
566}
567
568#[derive(Debug, Default)]
569/// An opaque 32/64-bit container for Mach-o segments
570pub struct Segments<'a> {
571    segments: Vec<Segment<'a>>,
572}
573
574impl<'a> Deref for Segments<'a> {
575    type Target = Vec<Segment<'a>>;
576    fn deref(&self) -> &Self::Target {
577        &self.segments
578    }
579}
580
581impl<'a> DerefMut for Segments<'a> {
582    fn deref_mut(&mut self) -> &mut Self::Target {
583        &mut self.segments
584    }
585}
586
587impl<'a, 'b> IntoIterator for &'b Segments<'a> {
588    type Item = &'b Segment<'a>;
589    type IntoIter = ::core::slice::Iter<'b, Segment<'a>>;
590    fn into_iter(self) -> Self::IntoIter {
591        self.segments.iter()
592    }
593}
594
595impl<'a> Segments<'a> {
596    /// Construct a new generalized segment container from this `ctx`
597    pub fn new(_ctx: container::Ctx) -> Self {
598        Segments {
599            segments: Vec::new(),
600        }
601    }
602    /// Get every section from every segment
603    // thanks to SpaceManic for figuring out the 'b lifetimes here :)
604    pub fn sections<'b>(&'b self) -> Box<dyn Iterator<Item = SectionIterator<'a>> + 'b> {
605        Box::new(self.segments.iter().map(|segment| segment.into_iter()))
606    }
607}