1 use scroll::Pread;
2 use crate::alloc::string::ToString;
3 use crate::error;
4 
5 use super::section_table;
6 
7 use core::cmp;
8 use crate::pe::data_directories::DataDirectory;
9 
10 use log::debug;
11 
is_in_range(rva: usize, r1: usize, r2: usize) -> bool12 pub fn is_in_range (rva: usize, r1: usize, r2: usize) -> bool {
13     r1 <= rva && rva < r2
14 }
15 
16 // reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
17 #[inline]
aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize18 fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
19     const PHYSICAL_ALIGN: usize = 0x1ff;
20     pointer_to_raw_data & !PHYSICAL_ALIGN
21 }
22 
23 #[inline]
section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize24 fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
25     fn round_size(size: usize) -> usize {
26         const PAGE_MASK: usize = 0xfff;
27         (size + PAGE_MASK) & !PAGE_MASK
28     }
29 
30     let file_alignment = file_alignment as usize;
31     let size_of_raw_data = section.size_of_raw_data as usize;
32     let virtual_size = section.virtual_size as usize;
33     let read_size = {
34         let read_size = (section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1) & !(file_alignment - 1);
35         cmp::min(read_size, round_size(size_of_raw_data))
36     };
37 
38     if virtual_size == 0 {
39         read_size
40     } else {
41         cmp::min(read_size, round_size(virtual_size))
42     }
43 }
44 
rva2offset(rva: usize, section: &section_table::SectionTable) -> usize45 fn rva2offset (rva: usize, section: &section_table::SectionTable) -> usize {
46     (rva - section.virtual_address as usize) + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
47 }
48 
is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool49 fn is_in_section (rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
50     let section_rva = section.virtual_address as usize;
51     is_in_range(rva, section_rva, section_rva + section_read_size(section, file_alignment))
52 }
53 
find_offset(rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> Option<usize>54 pub fn find_offset (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> Option<usize> {
55     for (i, section) in sections.iter().enumerate() {
56         debug!("Checking {} for {:#x} ∈ {:#x}..{:#x}", section.name().unwrap_or(""), rva, section.virtual_address, section.virtual_address + section.virtual_size);
57         if is_in_section(rva, &section, file_alignment) {
58             let offset = rva2offset(rva, &section);
59             debug!("Found in section {}({}), remapped into offset {:#x}", section.name().unwrap_or(""), i, offset);
60             return Some(offset)
61         }
62     }
63     None
64 }
65 
find_offset_or(rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, msg: &str) -> error::Result<usize>66 pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, msg: &str) -> error::Result<usize> {
67     find_offset(rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(msg.to_string()))
68 }
69 
try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<&'a str>70 pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<&'a str> {
71     match find_offset(rva, sections, file_alignment) {
72         Some(offset) => {
73             Ok(bytes.pread::<&str>(offset)?)
74         },
75         None => {
76             Err(error::Error::Malformed(format!("Cannot find name from rva {:#x} in sections: {:?}", rva, sections)))
77         }
78     }
79 }
80 
get_data<'a, T>(bytes: &'a [u8], sections: &[section_table::SectionTable], directory: DataDirectory, file_alignment: u32) -> error::Result<T> where T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>81 pub fn get_data<'a, T>(bytes: &'a [u8], sections: &[section_table::SectionTable], directory: DataDirectory, file_alignment: u32) -> error::Result<T>
82     where T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>  {
83     let rva = directory.virtual_address as usize;
84     let offset = find_offset(rva, sections, file_alignment)
85         .ok_or_else(||error::Error::Malformed(directory.virtual_address.to_string()))?;
86     let result: T = bytes.pread_with(offset, scroll::LE)?;
87     Ok(result)
88 }
89