1 use crate::alloc::borrow::Cow;
2 use crate::alloc::vec::Vec;
3 use core::fmt::{LowerHex, Debug};
4 
5 use scroll::{Pread, Pwrite, SizeWith};
6 use scroll::ctx::TryFromCtx;
7 use crate::error;
8 
9 use crate::pe::section_table;
10 use crate::pe::utils;
11 use crate::pe::data_directories;
12 
13 use log::{debug, warn};
14 
15 pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
16 pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
17 pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff;
18 pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff;
19 
20 pub trait Bitfield<'a>: Into<u64> + PartialEq + Eq + LowerHex + Debug + TryFromCtx<'a, scroll::Endian, Error=scroll::Error, Size=usize> {
is_ordinal(&self) -> bool21     fn is_ordinal(&self) -> bool;
to_ordinal(&self) -> u1622     fn to_ordinal(&self) -> u16;
to_rva(&self) -> u3223     fn to_rva(&self) -> u32;
size_of() -> usize24     fn size_of() -> usize;
is_zero(&self) -> bool25     fn is_zero(&self) -> bool;
26 }
27 
28 impl<'a> Bitfield<'a> for u64 {
is_ordinal(&self) -> bool29     fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 }
to_ordinal(&self) -> u1630     fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
to_rva(&self) -> u3231     fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_64) as u32 }
size_of() -> usize32     fn size_of() -> usize { 8 }
is_zero(&self) -> bool33     fn is_zero(&self) -> bool { *self == 0 }
34 }
35 
36 impl<'a> Bitfield<'a> for u32 {
is_ordinal(&self) -> bool37     fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 }
to_ordinal(&self) -> u1638     fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
to_rva(&self) -> u3239     fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_32) as u32 }
size_of() -> usize40     fn size_of() -> usize { 4 }
is_zero(&self) -> bool41     fn is_zero(&self) -> bool { *self == 0 }
42 }
43 
44 #[derive(Debug, Clone)]
45 pub struct HintNameTableEntry<'a> {
46     pub hint: u16,
47     pub name: &'a str,
48 }
49 
50 impl<'a> HintNameTableEntry<'a> {
parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self>51     fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
52         let offset = &mut offset;
53         let hint = bytes.gread_with(offset, scroll::LE)?;
54         let name = bytes.pread::<&'a str>(*offset)?;
55         Ok(HintNameTableEntry { hint, name })
56     }
57 }
58 
59 #[derive(Debug, Clone)]
60 pub enum SyntheticImportLookupTableEntry<'a> {
61     OrdinalNumber(u16),
62     HintNameTableRVA ((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
63 }
64 
65 pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;
66 
67 impl<'a> SyntheticImportLookupTableEntry<'a> {
parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportLookupTable<'a>>68     pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportLookupTable<'a>> {
69         let le = scroll::LE;
70         let offset = &mut offset;
71         let mut table = Vec::new();
72         loop {
73             let bitfield: T = bytes.gread_with(offset, le)?;
74             if bitfield.is_zero() {
75                 debug!("imports done");
76                 break;
77             } else {
78                 let entry = {
79                     debug!("bitfield {:#x}", bitfield);
80                     use self::SyntheticImportLookupTableEntry::*;
81                     if bitfield.is_ordinal() {
82                         let ordinal = bitfield.to_ordinal();
83                         debug!("importing by ordinal {:#x}", ordinal);
84                         OrdinalNumber(ordinal)
85                     } else {
86                         let rva = bitfield.to_rva();
87                         let hentry = {
88                             debug!("searching for RVA {:#x}", rva);
89                             if let Some(offset) = utils::find_offset(rva as usize, sections, file_alignment) {
90                                 debug!("offset {:#x}", offset);
91                                 HintNameTableEntry::parse(bytes, offset)?
92                             } else {
93                                 warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
94                                 continue
95                             }
96                         };
97                         HintNameTableRVA ((rva, hentry))
98                     }
99                 };
100                 table.push(entry);
101             }
102         }
103         Ok(table)
104     }
105 }
106 
107 // get until entry is 0
108 pub type ImportAddressTable = Vec<u64>;
109 
110 #[repr(C)]
111 #[derive(Debug)]
112 #[derive(Pread, Pwrite, SizeWith)]
113 pub struct ImportDirectoryEntry {
114     pub import_lookup_table_rva: u32,
115     pub time_date_stamp: u32,
116     pub forwarder_chain: u32,
117     pub name_rva: u32,
118     pub import_address_table_rva: u32,
119 }
120 
121 pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;
122 
123 impl ImportDirectoryEntry {
is_null(&self) -> bool124     pub fn is_null (&self) -> bool {
125         (self.import_lookup_table_rva == 0) &&
126             (self.time_date_stamp == 0) &&
127             (self.forwarder_chain == 0) &&
128             (self.name_rva == 0) &&
129             (self.import_address_table_rva == 0)
130     }
131 }
132 
133 #[derive(Debug)]
134 pub struct SyntheticImportDirectoryEntry<'a> {
135     pub import_directory_entry: ImportDirectoryEntry,
136     /// Computed
137     pub name: &'a str,
138     /// The import lookup table is a vector of either ordinals, or RVAs + import names
139     pub import_lookup_table: Option<ImportLookupTable<'a>>,
140     /// Computed
141     pub import_address_table: ImportAddressTable,
142 }
143 
144 impl<'a> SyntheticImportDirectoryEntry<'a> {
parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<SyntheticImportDirectoryEntry<'a>>145     pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
146         const LE: scroll::Endian = scroll::LE;
147         let name_rva = import_directory_entry.name_rva;
148         let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment)?;
149         let import_lookup_table = {
150             let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
151             let import_address_table_rva = import_directory_entry.import_address_table_rva;
152             if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections, file_alignment) {
153                 debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
154                 let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections, file_alignment)?;
155                 debug!("Successfully synthesized import lookup table entry from lookup table: {:#?}", import_lookup_table);
156                 Some(import_lookup_table)
157             } else if let Some(import_address_table_offset) = utils::find_offset(import_address_table_rva as usize, sections, file_alignment) {
158                 debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva);
159                 let import_address_table  = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_address_table_offset, sections, file_alignment)?;
160                 debug!("Successfully synthesized import lookup table entry from IAT: {:#?}", import_address_table);
161                 Some(import_address_table)
162             } else {
163                 None
164             }
165         };
166 
167         let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?;
168         let mut import_address_table = Vec::new();
169         loop {
170             let import_address = bytes.gread_with::<T>(import_address_table_offset, LE)?.into();
171             if import_address == 0 { break } else { import_address_table.push(import_address); }
172         }
173         Ok(SyntheticImportDirectoryEntry {
174             import_directory_entry,
175             name,
176             import_lookup_table,
177             import_address_table
178         })
179     }
180 }
181 
182 #[derive(Debug)]
183 /// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
184 pub struct ImportData<'a> {
185     pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
186 }
187 
188 impl<'a> ImportData<'a> {
parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportData<'a>>189     pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportData<'a>> {
190         let import_directory_table_rva = dd.virtual_address as usize;
191         debug!("import_directory_table_rva {:#x}", import_directory_table_rva);
192         let offset = &mut utils::find_offset(import_directory_table_rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;;
193         debug!("import data offset {:#x}", offset);
194         let mut import_data = Vec::new();
195         loop {
196             let import_directory_entry: ImportDirectoryEntry = bytes.gread_with(offset, scroll::LE)?;
197             debug!("{:#?}", import_directory_entry);
198             if import_directory_entry.is_null() {
199                 break;
200             } else {
201                 let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections, file_alignment)?;
202                 debug!("entry {:#?}", entry);
203                 import_data.push(entry);
204             }
205         }
206         debug!("finished ImportData");
207         Ok(ImportData { import_data})
208     }
209 }
210 
211 #[derive(Debug)]
212 /// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
213 pub struct Import<'a> {
214     pub name: Cow<'a, str>,
215     pub dll: &'a str,
216     pub ordinal: u16,
217     pub offset: usize,
218     pub rva: usize,
219     pub size: usize,
220 }
221 
222 impl<'a> Import<'a> {
parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>>223     pub fn parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>> {
224         let mut imports = Vec::new();
225         for data in &import_data.import_data {
226             if let Some(ref import_lookup_table) = data.import_lookup_table {
227                 let dll = data.name;
228                 let import_base = data.import_directory_entry.import_address_table_rva as usize;
229                 debug!("Getting imports from {}", &dll);
230                 for (i, entry) in import_lookup_table.iter().enumerate() {
231                     let offset = import_base + (i * T::size_of());
232                     use self::SyntheticImportLookupTableEntry::*;
233                     let (rva, name, ordinal) =
234                         match *entry {
235                             HintNameTableRVA ((rva, ref hint_entry)) => {
236                                 // if hint_entry.name = "" && hint_entry.hint = 0 {
237                                 //     println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
238                                 // }
239                                 (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint)
240                             },
241                             OrdinalNumber(ordinal) => {
242                                 let name = format!("ORDINAL {}", ordinal);
243                                 (0x0, Cow::Owned(name), ordinal)
244                             },
245                         };
246                     let import =
247                         Import {
248                             name,
249                             ordinal, dll,
250                             size: T::size_of(), offset, rva: rva as usize
251                         };
252                     imports.push(import);
253                 }
254             }
255         }
256         Ok (imports)
257     }
258 }
259