use scroll::ctx::{self, SizeWith}; use scroll::{Pread, Pwrite}; use log::{debug, warn}; use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt; use core::ops::{Deref, DerefMut}; use crate::container; use crate::error; use crate::mach::constants::{SECTION_TYPE, S_GB_ZEROFILL, S_THREAD_LOCAL_ZEROFILL, S_ZEROFILL}; use crate::mach::load_command::{ Section32, Section64, SegmentCommand32, SegmentCommand64, LC_SEGMENT, LC_SEGMENT_64, SIZEOF_SECTION_32, SIZEOF_SECTION_64, SIZEOF_SEGMENT_COMMAND_32, SIZEOF_SEGMENT_COMMAND_64, }; use crate::mach::relocation::RelocationInfo; pub struct RelocationIterator<'a> { data: &'a [u8], nrelocs: usize, offset: usize, count: usize, ctx: scroll::Endian, } impl<'a> Iterator for RelocationIterator<'a> { type Item = error::Result; fn next(&mut self) -> Option { if self.count >= self.nrelocs { None } else { self.count += 1; match self.data.gread_with(&mut self.offset, self.ctx) { Ok(res) => Some(Ok(res)), Err(e) => Some(Err(e.into())), } } } } /// Generalized 32/64 bit Section #[derive(Default)] pub struct Section { /// name of this section pub sectname: [u8; 16], /// segment this section goes in pub segname: [u8; 16], /// memory address of this section pub addr: u64, /// size in bytes of this section pub size: u64, /// file offset of this section pub offset: u32, /// section alignment (power of 2) pub align: u32, /// file offset of relocation entries pub reloff: u32, /// number of relocation entries pub nreloc: u32, /// flags (section type and attributes pub flags: u32, } impl Section { /// The name of this section pub fn name(&self) -> error::Result<&str> { Ok(self.sectname.pread::<&str>(0)?) } /// The containing segment's name pub fn segname(&self) -> error::Result<&str> { Ok(self.segname.pread::<&str>(0)?) } /// Iterate this sections relocations given `data`; `data` must be the original binary pub fn iter_relocations<'b>( &self, data: &'b [u8], ctx: container::Ctx, ) -> RelocationIterator<'b> { let offset = self.reloff as usize; debug!( "Relocations for {} starting at offset: {:#x}", self.name().unwrap_or("BAD_SECTION_NAME"), offset ); RelocationIterator { offset, nrelocs: self.nreloc as usize, count: 0, data, ctx: ctx.le, } } } impl From
for Section64 { fn from(section: Section) -> Self { Section64 { sectname: section.sectname, segname: section.segname, addr: section.addr as u64, size: section.size as u64, offset: section.offset, align: section.align, reloff: section.reloff, nreloc: section.nreloc, flags: section.flags, reserved1: 0, reserved2: 0, reserved3: 0, } } } impl From
for Section32 { fn from(section: Section) -> Self { Section32 { sectname: section.sectname, segname: section.segname, addr: section.addr as u32, size: section.size as u32, offset: section.offset, align: section.align, reloff: section.reloff, nreloc: section.nreloc, flags: section.flags, reserved1: 0, reserved2: 0, } } } impl fmt::Debug for Section { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("Section") .field("sectname", &self.name().unwrap()) .field("segname", &self.segname().unwrap()) .field("addr", &self.addr) .field("size", &self.size) .field("offset", &self.offset) .field("align", &self.align) .field("reloff", &self.reloff) .field("nreloc", &self.nreloc) .field("flags", &self.flags) .finish() } } impl From for Section { fn from(section: Section32) -> Self { Section { sectname: section.sectname, segname: section.segname, addr: u64::from(section.addr), size: u64::from(section.size), offset: section.offset, align: section.align, reloff: section.reloff, nreloc: section.nreloc, flags: section.flags, } } } impl From for Section { fn from(section: Section64) -> Self { Section { sectname: section.sectname, segname: section.segname, addr: section.addr, size: section.size, offset: section.offset, align: section.align, reloff: section.reloff, nreloc: section.nreloc, flags: section.flags, } } } impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Section { type Error = crate::error::Error; fn try_from_ctx(bytes: &'a [u8], ctx: container::Ctx) -> Result<(Self, usize), Self::Error> { match ctx.container { container::Container::Little => { let section = Section::from(bytes.pread_with::(0, ctx.le)?); Ok((section, SIZEOF_SECTION_32)) } container::Container::Big => { let section = Section::from(bytes.pread_with::(0, ctx.le)?); Ok((section, SIZEOF_SECTION_64)) } } } } impl ctx::SizeWith for Section { fn size_with(ctx: &container::Ctx) -> usize { match ctx.container { container::Container::Little => SIZEOF_SECTION_32, container::Container::Big => SIZEOF_SECTION_64, } } } impl ctx::TryIntoCtx for Section { type Error = crate::error::Error; fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result { if ctx.is_big() { bytes.pwrite_with::(self.into(), 0, ctx.le)?; } else { bytes.pwrite_with::(self.into(), 0, ctx.le)?; } Ok(Self::size_with(&ctx)) } } impl ctx::IntoCtx for Section { fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { bytes.pwrite_with(self, 0, ctx).unwrap(); } } pub struct SectionIterator<'a> { data: &'a [u8], count: usize, offset: usize, idx: usize, ctx: container::Ctx, } pub type SectionData<'a> = &'a [u8]; impl<'a> ::core::iter::ExactSizeIterator for SectionIterator<'a> { fn len(&self) -> usize { self.count } } impl<'a> Iterator for SectionIterator<'a> { type Item = error::Result<(Section, SectionData<'a>)>; fn next(&mut self) -> Option { if self.idx >= self.count { None } else { self.idx += 1; match self.data.gread_with::
(&mut self.offset, self.ctx) { Ok(section) => { let section_type = section.flags & SECTION_TYPE; let data = if section_type == S_ZEROFILL || section_type == S_GB_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL { &[] } else { // it's not uncommon to encounter macho files where files are // truncated but the sections are still remaining in the header. // Because of this we want to not panic here but instead just // slice down to a empty data slice. This way only if code // actually needs to access those sections it will fall over. self.data .get(section.offset as usize..) .unwrap_or_else(|| { warn!( "section #{} offset {} out of bounds", self.idx, section.offset ); &[] }) .get(..section.size as usize) .unwrap_or_else(|| { warn!("section #{} size {} out of bounds", self.idx, section.size); &[] }) }; Some(Ok((section, data))) } Err(e) => Some(Err(e)), } } } } impl<'a, 'b> IntoIterator for &'b Segment<'a> { type Item = error::Result<(Section, SectionData<'a>)>; type IntoIter = SectionIterator<'a>; fn into_iter(self) -> Self::IntoIter { SectionIterator { data: self.raw_data, count: self.nsects as usize, offset: self.offset + Segment::size_with(&self.ctx), idx: 0, ctx: self.ctx, } } } /// Generalized 32/64 bit Segment Command pub struct Segment<'a> { pub cmd: u32, pub cmdsize: u32, pub segname: [u8; 16], pub vmaddr: u64, pub vmsize: u64, pub fileoff: u64, pub filesize: u64, pub maxprot: u32, pub initprot: u32, pub nsects: u32, pub flags: u32, pub data: &'a [u8], offset: usize, raw_data: &'a [u8], ctx: container::Ctx, } impl<'a> From> for SegmentCommand64 { fn from(segment: Segment<'a>) -> Self { SegmentCommand64 { cmd: segment.cmd, cmdsize: segment.cmdsize, segname: segment.segname, vmaddr: segment.vmaddr as u64, vmsize: segment.vmsize as u64, fileoff: segment.fileoff as u64, filesize: segment.filesize as u64, maxprot: segment.maxprot, initprot: segment.initprot, nsects: segment.nsects, flags: segment.flags, } } } impl<'a> From> for SegmentCommand32 { fn from(segment: Segment<'a>) -> Self { SegmentCommand32 { cmd: segment.cmd, cmdsize: segment.cmdsize, segname: segment.segname, vmaddr: segment.vmaddr as u32, vmsize: segment.vmsize as u32, fileoff: segment.fileoff as u32, filesize: segment.filesize as u32, maxprot: segment.maxprot, initprot: segment.initprot, nsects: segment.nsects, flags: segment.flags, } } } impl<'a> fmt::Debug for Segment<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("Segment") .field("cmd", &self.cmd) .field("cmdsize", &self.cmdsize) .field("segname", &self.segname.pread::<&str>(0).unwrap()) .field("vmaddr", &self.vmaddr) .field("vmsize", &self.vmsize) .field("fileoff", &self.fileoff) .field("filesize", &self.filesize) .field("maxprot", &self.maxprot) .field("initprot", &self.initprot) .field("nsects", &self.nsects) .field("flags", &self.flags) .field("data", &self.data.len()) .field( "sections()", &self.sections().map(|sections| { sections .into_iter() .map(|(section, _)| section) .collect::>() }), ) .finish() } } impl<'a> ctx::SizeWith for Segment<'a> { fn size_with(ctx: &container::Ctx) -> usize { match ctx.container { container::Container::Little => SIZEOF_SEGMENT_COMMAND_32, container::Container::Big => SIZEOF_SEGMENT_COMMAND_64, } } } impl<'a> ctx::TryIntoCtx for Segment<'a> { type Error = crate::error::Error; fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result { let segment_size = Self::size_with(&ctx); // should be able to write the section data inline after this, but not working at the moment //let section_size = bytes.pwrite(data, segment_size)?; //debug!("Segment size: {} raw section data size: {}", segment_size, data.len()); if ctx.is_big() { bytes.pwrite_with::(self.into(), 0, ctx.le)?; } else { bytes.pwrite_with::(self.into(), 0, ctx.le)?; } //debug!("Section size: {}", section_size); Ok(segment_size) } } impl<'a> ctx::IntoCtx for Segment<'a> { fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { bytes.pwrite_with(self, 0, ctx).unwrap(); } } /// Read data that belongs to a segment if the offset is within the boundaries of bytes. fn segment_data(bytes: &[u8], fileoff: u64, filesize: u64) -> Result<&[u8], error::Error> { let data: &[u8] = if filesize != 0 { bytes.pread_with(fileoff as usize, filesize as usize)? } else { &[] }; Ok(data) } impl<'a> Segment<'a> { /// Create a new, blank segment, with cmd either `LC_SEGMENT_64`, or `LC_SEGMENT`, depending on `ctx`. /// **NB** You are responsible for providing a correctly marshalled byte array as the sections. You should not use this for anything other than writing. pub fn new(ctx: container::Ctx, sections: &'a [u8]) -> Self { Segment { cmd: if ctx.is_big() { LC_SEGMENT_64 } else { LC_SEGMENT }, cmdsize: (Self::size_with(&ctx) + sections.len()) as u32, segname: [0; 16], vmaddr: 0, vmsize: 0, fileoff: 0, filesize: 0, maxprot: 0, initprot: 0, nsects: 0, flags: 0, data: sections, offset: 0, raw_data: &[], ctx, } } /// Get the name of this segment pub fn name(&self) -> error::Result<&str> { Ok(self.segname.pread::<&str>(0)?) } /// Get the sections from this segment, erroring if any section couldn't be retrieved pub fn sections(&self) -> error::Result)>> { let mut sections = Vec::new(); for section in self.into_iter() { sections.push(section?); } Ok(sections) } /// Convert the raw C 32-bit segment command to a generalized version pub fn from_32( bytes: &'a [u8], segment: &SegmentCommand32, offset: usize, ctx: container::Ctx, ) -> Result { Ok(Segment { cmd: segment.cmd, cmdsize: segment.cmdsize, segname: segment.segname, vmaddr: u64::from(segment.vmaddr), vmsize: u64::from(segment.vmsize), fileoff: u64::from(segment.fileoff), filesize: u64::from(segment.filesize), maxprot: segment.maxprot, initprot: segment.initprot, nsects: segment.nsects, flags: segment.flags, data: segment_data( bytes, u64::from(segment.fileoff), u64::from(segment.filesize), )?, offset, raw_data: bytes, ctx, }) } /// Convert the raw C 64-bit segment command to a generalized version pub fn from_64( bytes: &'a [u8], segment: &SegmentCommand64, offset: usize, ctx: container::Ctx, ) -> Result { Ok(Segment { cmd: segment.cmd, cmdsize: segment.cmdsize, segname: segment.segname, vmaddr: segment.vmaddr, vmsize: segment.vmsize, fileoff: segment.fileoff, filesize: segment.filesize, maxprot: segment.maxprot, initprot: segment.initprot, nsects: segment.nsects, flags: segment.flags, data: segment_data(bytes, segment.fileoff, segment.filesize)?, offset, raw_data: bytes, ctx, }) } } #[derive(Debug, Default)] /// An opaque 32/64-bit container for Mach-o segments pub struct Segments<'a> { segments: Vec>, } impl<'a> Deref for Segments<'a> { type Target = Vec>; fn deref(&self) -> &Self::Target { &self.segments } } impl<'a> DerefMut for Segments<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.segments } } impl<'a, 'b> IntoIterator for &'b Segments<'a> { type Item = &'b Segment<'a>; type IntoIter = ::core::slice::Iter<'b, Segment<'a>>; fn into_iter(self) -> Self::IntoIter { self.segments.iter() } } impl<'a> Segments<'a> { /// Construct a new generalized segment container from this `ctx` pub fn new(_ctx: container::Ctx) -> Self { Segments { segments: Vec::new(), } } /// Get every section from every segment // thanks to SpaceManic for figuring out the 'b lifetimes here :) pub fn sections<'b>(&'b self) -> Box> + 'b> { Box::new(self.segments.iter().map(|segment| segment.into_iter())) } }