1 use crate::error;
2 use alloc::string::ToString;
3 use scroll::Pread;
4 
5 use super::options;
6 use super::section_table;
7 
8 use crate::pe::data_directories::DataDirectory;
9 use core::cmp;
10 
11 use log::debug;
12 
is_in_range(rva: usize, r1: usize, r2: usize) -> bool13 pub fn is_in_range(rva: usize, r1: usize, r2: usize) -> bool {
14     r1 <= rva && rva < r2
15 }
16 
17 // reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
18 #[inline]
aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize19 fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
20     const PHYSICAL_ALIGN: usize = 0x1ff;
21     pointer_to_raw_data & !PHYSICAL_ALIGN
22 }
23 
24 #[inline]
section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize25 fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
26     fn round_size(size: usize) -> usize {
27         const PAGE_MASK: usize = 0xfff;
28         (size + PAGE_MASK) & !PAGE_MASK
29     }
30 
31     let file_alignment = file_alignment as usize;
32     let size_of_raw_data = section.size_of_raw_data as usize;
33     let virtual_size = section.virtual_size as usize;
34     let read_size = {
35         let read_size = (section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment
36             - 1)
37             & !(file_alignment - 1);
38         cmp::min(read_size, round_size(size_of_raw_data))
39     };
40 
41     if virtual_size == 0 {
42         read_size
43     } else {
44         cmp::min(read_size, round_size(virtual_size))
45     }
46 }
47 
rva2offset(rva: usize, section: &section_table::SectionTable) -> usize48 fn rva2offset(rva: usize, section: &section_table::SectionTable) -> usize {
49     (rva - section.virtual_address as usize)
50         + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
51 }
52 
is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool53 fn is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
54     let section_rva = section.virtual_address as usize;
55     is_in_range(
56         rva,
57         section_rva,
58         section_rva + section_read_size(section, file_alignment),
59     )
60 }
61 
find_offset( rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, ) -> Option<usize>62 pub fn find_offset(
63     rva: usize,
64     sections: &[section_table::SectionTable],
65     file_alignment: u32,
66     opts: &options::ParseOptions,
67 ) -> Option<usize> {
68     if opts.resolve_rva {
69         for (i, section) in sections.iter().enumerate() {
70             debug!(
71                 "Checking {} for {:#x} ∈ {:#x}..{:#x}",
72                 section.name().unwrap_or(""),
73                 rva,
74                 section.virtual_address,
75                 section.virtual_address + section.virtual_size
76             );
77             if is_in_section(rva, &section, file_alignment) {
78                 let offset = rva2offset(rva, &section);
79                 debug!(
80                     "Found in section {}({}), remapped into offset {:#x}",
81                     section.name().unwrap_or(""),
82                     i,
83                     offset
84                 );
85                 return Some(offset);
86             }
87         }
88         None
89     } else {
90         Some(rva)
91     }
92 }
93 
find_offset_or( rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, msg: &str, ) -> error::Result<usize>94 pub fn find_offset_or(
95     rva: usize,
96     sections: &[section_table::SectionTable],
97     file_alignment: u32,
98     opts: &options::ParseOptions,
99     msg: &str,
100 ) -> error::Result<usize> {
101     find_offset(rva, sections, file_alignment, opts)
102         .ok_or_else(|| error::Error::Malformed(msg.to_string()))
103 }
104 
try_name<'a>( bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, ) -> error::Result<&'a str>105 pub fn try_name<'a>(
106     bytes: &'a [u8],
107     rva: usize,
108     sections: &[section_table::SectionTable],
109     file_alignment: u32,
110     opts: &options::ParseOptions,
111 ) -> error::Result<&'a str> {
112     match find_offset(rva, sections, file_alignment, opts) {
113         Some(offset) => Ok(bytes.pread::<&str>(offset)?),
114         None => Err(error::Error::Malformed(format!(
115             "Cannot find name from rva {:#x} in sections: {:?}",
116             rva, sections
117         ))),
118     }
119 }
120 
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>,121 pub fn get_data<'a, T>(
122     bytes: &'a [u8],
123     sections: &[section_table::SectionTable],
124     directory: DataDirectory,
125     file_alignment: u32,
126 ) -> error::Result<T>
127 where
128     T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
129 {
130     get_data_with_opts(
131         bytes,
132         sections,
133         directory,
134         file_alignment,
135         &options::ParseOptions::default(),
136     )
137 }
138 
get_data_with_opts<'a, T>( bytes: &'a [u8], sections: &[section_table::SectionTable], directory: DataDirectory, file_alignment: u32, opts: &options::ParseOptions, ) -> error::Result<T> where T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,139 pub fn get_data_with_opts<'a, T>(
140     bytes: &'a [u8],
141     sections: &[section_table::SectionTable],
142     directory: DataDirectory,
143     file_alignment: u32,
144     opts: &options::ParseOptions,
145 ) -> error::Result<T>
146 where
147     T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
148 {
149     let rva = directory.virtual_address as usize;
150     let offset = find_offset(rva, sections, file_alignment, opts)
151         .ok_or_else(|| error::Error::Malformed(directory.virtual_address.to_string()))?;
152     let result: T = bytes.pread_with(offset, scroll::LE)?;
153     Ok(result)
154 }
155