|
| 1 | +use crate::error::CrackersError; |
| 2 | +use jingle::sleigh::context::image::{ImageProvider, ImageSection, ImageSectionIterator, Perms}; |
| 3 | +use jingle::sleigh::JingleSleighError::ImageLoadError; |
| 4 | +use jingle::sleigh::VarNode; |
| 5 | +use jingle::JingleError::Sleigh; |
| 6 | +use object::elf::{PF_R, PF_W, PF_X}; |
| 7 | +use object::macho::{VM_PROT_EXECUTE, VM_PROT_READ, VM_PROT_WRITE}; |
| 8 | +use object::pe::{IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE}; |
| 9 | +use object::{File, Object, ObjectSegment, Segment, SegmentFlags}; |
| 10 | +use std::cmp::{max, min}; |
| 11 | + |
| 12 | +#[derive(Debug, PartialEq, Eq)] |
| 13 | +pub struct ImageSegment { |
| 14 | + data: Vec<u8>, |
| 15 | + perms: Perms, |
| 16 | + base_address: usize, |
| 17 | +} |
| 18 | + |
| 19 | +impl<'a> From<&'a ImageSegment> for ImageSection<'a> { |
| 20 | + fn from(value: &'a ImageSegment) -> Self { |
| 21 | + ImageSection { |
| 22 | + data: value.data.as_slice(), |
| 23 | + perms: value.perms.clone(), |
| 24 | + base_address: value.base_address, |
| 25 | + } |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +impl TryFrom<Segment<'_, '_>> for ImageSegment { |
| 30 | + type Error = CrackersError; |
| 31 | + |
| 32 | + fn try_from(value: Segment) -> Result<Self, Self::Error> { |
| 33 | + let data = value |
| 34 | + .data() |
| 35 | + .map_err(|_| CrackersError::Jingle(Sleigh(ImageLoadError)))? |
| 36 | + .to_vec(); |
| 37 | + Ok(ImageSegment { |
| 38 | + data, |
| 39 | + perms: map_seg_flags(&value.flags())?, |
| 40 | + base_address: value.address() as usize, |
| 41 | + }) |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +/// todo: this should go in jingle |
| 46 | +fn map_seg_flags(p0: &SegmentFlags) -> Result<Perms, CrackersError> { |
| 47 | + match p0 { |
| 48 | + SegmentFlags::None => Ok(Perms::RWX), |
| 49 | + SegmentFlags::Elf { p_flags } => Ok(Perms { |
| 50 | + read: p_flags & PF_R != 0, |
| 51 | + write: p_flags & PF_W != 0, |
| 52 | + exec: p_flags & PF_X != 0, |
| 53 | + }), |
| 54 | + SegmentFlags::MachO { maxprot, .. } => Ok(Perms { |
| 55 | + read: maxprot & VM_PROT_READ != 0, |
| 56 | + write: maxprot & VM_PROT_WRITE != 0, |
| 57 | + exec: maxprot & VM_PROT_EXECUTE != 0, |
| 58 | + }), |
| 59 | + SegmentFlags::Coff { characteristics } => Ok(Perms { |
| 60 | + read: characteristics & IMAGE_SCN_MEM_READ != 0, |
| 61 | + write: characteristics & IMAGE_SCN_MEM_WRITE != 0, |
| 62 | + exec: characteristics & IMAGE_SCN_MEM_EXECUTE != 0, |
| 63 | + }), |
| 64 | + _ => Err(CrackersError::Jingle(Sleigh(ImageLoadError))), |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +/// A gross hack because we want to process the entire executable segment, rather |
| 69 | +/// than the portion that is marked executable for the linker |
| 70 | +pub struct SegmentFile { |
| 71 | + segments: Vec<ImageSegment>, |
| 72 | +} |
| 73 | + |
| 74 | +impl SegmentFile { |
| 75 | + pub fn new(file: &File) -> Result<Self, CrackersError> { |
| 76 | + let mut segments = vec![]; |
| 77 | + for x in file |
| 78 | + .segments() |
| 79 | + .filter(|f| map_seg_flags(&f.flags()).map(|f| f.exec).unwrap_or(false)) |
| 80 | + { |
| 81 | + segments.push(x.try_into()?); |
| 82 | + } |
| 83 | + Ok(Self { segments }) |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +impl ImageProvider for SegmentFile { |
| 88 | + fn load(&self, vn: &VarNode, output: &mut [u8]) -> usize { |
| 89 | + let mut written = 0; |
| 90 | + output.fill(0); |
| 91 | + let output_start_addr = vn.offset as usize; |
| 92 | + let output_end_addr = output_start_addr + vn.size; |
| 93 | + if let Some(x) = self.get_section_info().find(|s| { |
| 94 | + output_start_addr >= s.base_address |
| 95 | + && output_start_addr < (s.base_address + s.data.len()) |
| 96 | + }) { |
| 97 | + let input_start_addr = x.base_address; |
| 98 | + let input_end_addr = input_start_addr + x.data.len(); |
| 99 | + let start_addr = max(input_start_addr, output_start_addr); |
| 100 | + let end_addr = max(min(input_end_addr, output_end_addr), start_addr); |
| 101 | + if end_addr > start_addr { |
| 102 | + let i_s = start_addr - x.base_address; |
| 103 | + let i_e = end_addr - x.base_address; |
| 104 | + let o_s = start_addr - vn.offset as usize; |
| 105 | + let o_e = end_addr - vn.offset as usize; |
| 106 | + let out_slice = &mut output[o_s..o_e]; |
| 107 | + let in_slice = &x.data[i_s..i_e]; |
| 108 | + out_slice.copy_from_slice(in_slice); |
| 109 | + written += end_addr - start_addr; |
| 110 | + } |
| 111 | + } |
| 112 | + written |
| 113 | + } |
| 114 | + |
| 115 | + fn has_full_range(&self, vn: &VarNode) -> bool { |
| 116 | + self.get_section_info().any(|s| { |
| 117 | + s.base_address <= vn.offset as usize |
| 118 | + && (s.base_address + s.data.len()) >= (vn.offset as usize + vn.size) |
| 119 | + }) |
| 120 | + } |
| 121 | + |
| 122 | + fn get_section_info(&self) -> ImageSectionIterator { |
| 123 | + ImageSectionIterator::new(self.segments.iter().map(ImageSection::from)) |
| 124 | + } |
| 125 | +} |
0 commit comments