1 use scroll::ctx::{self, SizeWith};
2 use scroll::{Pread, Pwrite};
3 
4 use log::{debug, warn};
5 
6 use alloc::boxed::Box;
7 use alloc::vec::Vec;
8 use core::fmt;
9 use core::ops::{Deref, DerefMut};
10 
11 use crate::container;
12 use crate::error;
13 
14 use crate::mach::constants::{SECTION_TYPE, S_GB_ZEROFILL, S_THREAD_LOCAL_ZEROFILL, S_ZEROFILL};
15 use crate::mach::load_command::{
16     Section32, Section64, SegmentCommand32, SegmentCommand64, LC_SEGMENT, LC_SEGMENT_64,
17     SIZEOF_SECTION_32, SIZEOF_SECTION_64, SIZEOF_SEGMENT_COMMAND_32, SIZEOF_SEGMENT_COMMAND_64,
18 };
19 use crate::mach::relocation::RelocationInfo;
20 
21 pub struct RelocationIterator<'a> {
22     data: &'a [u8],
23     nrelocs: usize,
24     offset: usize,
25     count: usize,
26     ctx: scroll::Endian,
27 }
28 
29 impl<'a> Iterator for RelocationIterator<'a> {
30     type Item = error::Result<RelocationInfo>;
next(&mut self) -> Option<Self::Item>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)]
46 pub 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 
67 impl Section {
68     /// The name of this section
name(&self) -> error::Result<&str>69     pub fn name(&self) -> error::Result<&str> {
70         Ok(self.sectname.pread::<&str>(0)?)
71     }
72     /// The containing segment's name
segname(&self) -> error::Result<&str>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
iter_relocations<'b>( &self, data: &'b [u8], ctx: container::Ctx, ) -> RelocationIterator<'b>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 
98 impl From<Section> for Section64 {
from(section: Section) -> Self99     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 
117 impl From<Section> for Section32 {
from(section: Section) -> Self118     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 
135 impl fmt::Debug for Section {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result136     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 
151 impl From<Section32> for Section {
from(section: Section32) -> Self152     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 
167 impl From<Section64> for Section {
from(section: Section64) -> Self168     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 
183 impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Section {
184     type Error = crate::error::Error;
try_from_ctx(bytes: &'a [u8], ctx: container::Ctx) -> Result<(Self, usize), Self::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 
199 impl ctx::SizeWith<container::Ctx> for Section {
size_with(ctx: &container::Ctx) -> usize200     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 
208 impl ctx::TryIntoCtx<container::Ctx> for Section {
209     type Error = crate::error::Error;
try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::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 
220 impl ctx::IntoCtx<container::Ctx> for Section {
into_ctx(self, bytes: &mut [u8], ctx: container::Ctx)221     fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
222         bytes.pwrite_with(self, 0, ctx).unwrap();
223     }
224 }
225 
226 pub struct SectionIterator<'a> {
227     data: &'a [u8],
228     count: usize,
229     offset: usize,
230     idx: usize,
231     ctx: container::Ctx,
232 }
233 
234 pub type SectionData<'a> = &'a [u8];
235 
236 impl<'a> ::core::iter::ExactSizeIterator for SectionIterator<'a> {
len(&self) -> usize237     fn len(&self) -> usize {
238         self.count
239     }
240 }
241 
242 impl<'a> Iterator for SectionIterator<'a> {
243     type Item = error::Result<(Section, SectionData<'a>)>;
next(&mut self) -> Option<Self::Item>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 
286 impl<'a, 'b> IntoIterator for &'b Segment<'a> {
287     type Item = error::Result<(Section, SectionData<'a>)>;
288     type IntoIter = SectionIterator<'a>;
into_iter(self) -> Self::IntoIter289     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
301 pub 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 
319 impl<'a> From<Segment<'a>> for SegmentCommand64 {
from(segment: Segment<'a>) -> Self320     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 
337 impl<'a> From<Segment<'a>> for SegmentCommand32 {
from(segment: Segment<'a>) -> Self338     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 
355 impl<'a> fmt::Debug for Segment<'a> {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result356     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 
383 impl<'a> ctx::SizeWith<container::Ctx> for Segment<'a> {
size_with(ctx: &container::Ctx) -> usize384     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 
392 impl<'a> ctx::TryIntoCtx<container::Ctx> for Segment<'a> {
393     type Error = crate::error::Error;
try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::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 
409 impl<'a> ctx::IntoCtx<container::Ctx> for Segment<'a> {
into_ctx(self, bytes: &mut [u8], ctx: container::Ctx)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.
segment_data(bytes: &[u8], fileoff: u64, filesize: u64) -> Result<&[u8], error::Error>416 fn 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 
425 impl<'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.
new(ctx: container::Ctx, sections: &'a [u8]) -> Self428     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
name(&self) -> error::Result<&str>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
sections(&self) -> error::Result<Vec<(Section, SectionData<'a>)>>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
from_32( bytes: &'a [u8], segment: &SegmentCommand32, offset: usize, ctx: container::Ctx, ) -> Result<Self, error::Error>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         Ok(Segment {
471             cmd: segment.cmd,
472             cmdsize: segment.cmdsize,
473             segname: segment.segname,
474             vmaddr: u64::from(segment.vmaddr),
475             vmsize: u64::from(segment.vmsize),
476             fileoff: u64::from(segment.fileoff),
477             filesize: u64::from(segment.filesize),
478             maxprot: segment.maxprot,
479             initprot: segment.initprot,
480             nsects: segment.nsects,
481             flags: segment.flags,
482             data: segment_data(
483                 bytes,
484                 u64::from(segment.fileoff),
485                 u64::from(segment.filesize),
486             )?,
487             offset,
488             raw_data: bytes,
489             ctx,
490         })
491     }
492     /// Convert the raw C 64-bit segment command to a generalized version
from_64( bytes: &'a [u8], segment: &SegmentCommand64, offset: usize, ctx: container::Ctx, ) -> Result<Self, error::Error>493     pub fn from_64(
494         bytes: &'a [u8],
495         segment: &SegmentCommand64,
496         offset: usize,
497         ctx: container::Ctx,
498     ) -> Result<Self, error::Error> {
499         Ok(Segment {
500             cmd: segment.cmd,
501             cmdsize: segment.cmdsize,
502             segname: segment.segname,
503             vmaddr: segment.vmaddr,
504             vmsize: segment.vmsize,
505             fileoff: segment.fileoff,
506             filesize: segment.filesize,
507             maxprot: segment.maxprot,
508             initprot: segment.initprot,
509             nsects: segment.nsects,
510             flags: segment.flags,
511             data: segment_data(bytes, segment.fileoff, segment.filesize)?,
512             offset,
513             raw_data: bytes,
514             ctx,
515         })
516     }
517 }
518 
519 #[derive(Debug, Default)]
520 /// An opaque 32/64-bit container for Mach-o segments
521 pub struct Segments<'a> {
522     segments: Vec<Segment<'a>>,
523     ctx: container::Ctx,
524 }
525 
526 impl<'a> Deref for Segments<'a> {
527     type Target = Vec<Segment<'a>>;
deref(&self) -> &Self::Target528     fn deref(&self) -> &Self::Target {
529         &self.segments
530     }
531 }
532 
533 impl<'a> DerefMut for Segments<'a> {
deref_mut(&mut self) -> &mut Self::Target534     fn deref_mut(&mut self) -> &mut Self::Target {
535         &mut self.segments
536     }
537 }
538 
539 impl<'a, 'b> IntoIterator for &'b Segments<'a> {
540     type Item = &'b Segment<'a>;
541     type IntoIter = ::core::slice::Iter<'b, Segment<'a>>;
into_iter(self) -> Self::IntoIter542     fn into_iter(self) -> Self::IntoIter {
543         self.segments.iter()
544     }
545 }
546 
547 impl<'a> Segments<'a> {
548     /// Construct a new generalized segment container from this `ctx`
new(ctx: container::Ctx) -> Self549     pub fn new(ctx: container::Ctx) -> Self {
550         Segments {
551             segments: Vec::new(),
552             ctx,
553         }
554     }
555     /// Get every section from every segment
556     // thanks to SpaceManic for figuring out the 'b lifetimes here :)
sections<'b>(&'b self) -> Box<dyn Iterator<Item = SectionIterator<'a>> + 'b>557     pub fn sections<'b>(&'b self) -> Box<dyn Iterator<Item = SectionIterator<'a>> + 'b> {
558         Box::new(self.segments.iter().map(|segment| segment.into_iter()))
559     }
560 }
561