1use alloc::borrow::Cow;
2use alloc::vec::Vec;
3use core::fmt::{Debug, LowerHex};
4
5use crate::error;
6use scroll::ctx::TryFromCtx;
7use scroll::{Pread, Pwrite, SizeWith};
8
9use crate::pe::data_directories;
10use crate::pe::options;
11use crate::pe::section_table;
12use crate::pe::utils;
13
14use log::{debug, warn};
15
16pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
17pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
18pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff;
19pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff;
20
21pub trait Bitfield<'a>:
22 Into<u64>
23 + PartialEq
24 + Eq
25 + LowerHex
26 + Debug
27 + TryFromCtx<'a, scroll::Endian, Error = scroll::Error>
28{
29 fn is_ordinal(&self) -> bool;
30 fn to_ordinal(&self) -> u16;
31 fn to_rva(&self) -> u32;
32 fn size_of() -> usize;
33 fn is_zero(&self) -> bool;
34}
35
36impl<'a> Bitfield<'a> for u64 {
37 fn is_ordinal(&self) -> bool {
38 self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64
39 }
40 fn to_ordinal(&self) -> u16 {
41 (0xffff & self) as u16
42 }
43 fn to_rva(&self) -> u32 {
44 (self & IMPORT_RVA_MASK_64) as u32
45 }
46 fn size_of() -> usize {
47 8
48 }
49 fn is_zero(&self) -> bool {
50 *self == 0
51 }
52}
53
54impl<'a> Bitfield<'a> for u32 {
55 fn is_ordinal(&self) -> bool {
56 self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32
57 }
58 fn to_ordinal(&self) -> u16 {
59 (0xffff & self) as u16
60 }
61 fn to_rva(&self) -> u32 {
62 (self & IMPORT_RVA_MASK_32) as u32
63 }
64 fn size_of() -> usize {
65 4
66 }
67 fn is_zero(&self) -> bool {
68 *self == 0
69 }
70}
71
72#[derive(Debug, Clone)]
73pub struct HintNameTableEntry<'a> {
74 pub hint: u16,
75 pub name: &'a str,
76}
77
78impl<'a> HintNameTableEntry<'a> {
79 fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
80 let offset = &mut offset;
81 let hint = bytes.gread_with(offset, scroll::LE)?;
82 let name = bytes.pread::<&'a str>(*offset)?;
83 Ok(HintNameTableEntry { hint, name })
84 }
85}
86
87#[derive(Debug, Clone)]
88pub enum SyntheticImportLookupTableEntry<'a> {
89 OrdinalNumber(u16),
90 HintNameTableRVA((u32, HintNameTableEntry<'a>)), }
92
93pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;
94
95impl<'a> SyntheticImportLookupTableEntry<'a> {
96 pub fn parse<T: Bitfield<'a>>(
97 bytes: &'a [u8],
98 offset: usize,
99 sections: &[section_table::SectionTable],
100 file_alignment: u32,
101 ) -> error::Result<ImportLookupTable<'a>> {
102 Self::parse_with_opts::<T>(
103 bytes,
104 offset,
105 sections,
106 file_alignment,
107 &options::ParseOptions::default(),
108 )
109 }
110
111 pub fn parse_with_opts<T: Bitfield<'a>>(
112 bytes: &'a [u8],
113 mut offset: usize,
114 sections: &[section_table::SectionTable],
115 file_alignment: u32,
116 opts: &options::ParseOptions,
117 ) -> error::Result<ImportLookupTable<'a>> {
118 let le = scroll::LE;
119 let offset = &mut offset;
120 let mut table = Vec::new();
121 loop {
122 let bitfield: T = bytes.gread_with(offset, le)?;
123 if bitfield.is_zero() {
124 debug!("imports done");
125 break;
126 } else {
127 let entry = {
128 debug!("bitfield {:#x}", bitfield);
129 use self::SyntheticImportLookupTableEntry::*;
130 if bitfield.is_ordinal() {
131 let ordinal = bitfield.to_ordinal();
132 debug!("importing by ordinal {:#x} ({})", ordinal, ordinal);
133 OrdinalNumber(ordinal)
134 } else {
135 let rva = bitfield.to_rva();
136 let hentry = {
137 debug!("searching for RVA {:#x}", rva);
138 if let Some(offset) =
139 utils::find_offset(rva as usize, sections, file_alignment, opts)
140 {
141 debug!("offset {:#x}", offset);
142 HintNameTableEntry::parse(bytes, offset)?
143 } else {
144 warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
145 continue;
146 }
147 };
148 HintNameTableRVA((rva, hentry))
149 }
150 };
151 table.push(entry);
152 }
153 }
154 Ok(table)
155 }
156}
157
158pub type ImportAddressTable = Vec<u64>;
160
161#[repr(C)]
162#[derive(Debug, Pread, Pwrite, SizeWith)]
163pub struct ImportDirectoryEntry {
164 pub import_lookup_table_rva: u32,
165 pub time_date_stamp: u32,
166 pub forwarder_chain: u32,
167 pub name_rva: u32,
168 pub import_address_table_rva: u32,
169}
170
171pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;
172
173impl ImportDirectoryEntry {
174 pub fn is_null(&self) -> bool {
176 (self.import_lookup_table_rva == 0)
177 && (self.time_date_stamp == 0)
178 && (self.forwarder_chain == 0)
179 && (self.name_rva == 0)
180 && (self.import_address_table_rva == 0)
181 }
182
183 pub fn is_possibly_valid(&self) -> bool {
187 self.name_rva != 0 && self.import_address_table_rva != 0
188 }
189}
190
191#[derive(Debug)]
192pub struct SyntheticImportDirectoryEntry<'a> {
193 pub import_directory_entry: ImportDirectoryEntry,
194 pub name: &'a str,
196 pub import_lookup_table: Option<ImportLookupTable<'a>>,
198 pub import_address_table: ImportAddressTable,
200}
201
202impl<'a> SyntheticImportDirectoryEntry<'a> {
203 pub fn parse<T: Bitfield<'a>>(
204 bytes: &'a [u8],
205 import_directory_entry: ImportDirectoryEntry,
206 sections: &[section_table::SectionTable],
207 file_alignment: u32,
208 ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
209 Self::parse_with_opts::<T>(
210 bytes,
211 import_directory_entry,
212 sections,
213 file_alignment,
214 &options::ParseOptions::default(),
215 )
216 }
217
218 pub fn parse_with_opts<T: Bitfield<'a>>(
219 bytes: &'a [u8],
220 import_directory_entry: ImportDirectoryEntry,
221 sections: &[section_table::SectionTable],
222 file_alignment: u32,
223 opts: &options::ParseOptions,
224 ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
225 const LE: scroll::Endian = scroll::LE;
226 let name_rva = import_directory_entry.name_rva;
227 let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment, opts)?;
228 let import_lookup_table = {
229 let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
230 let import_address_table_rva = import_directory_entry.import_address_table_rva;
231 if let Some(import_lookup_table_offset) = utils::find_offset(
232 import_lookup_table_rva as usize,
233 sections,
234 file_alignment,
235 opts,
236 ) {
237 debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
238 let import_lookup_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
239 bytes,
240 import_lookup_table_offset,
241 sections,
242 file_alignment,
243 opts,
244 )?;
245 debug!(
246 "Successfully synthesized import lookup table entry from lookup table: {:#?}",
247 import_lookup_table
248 );
249 Some(import_lookup_table)
250 } else if let Some(import_address_table_offset) = utils::find_offset(
251 import_address_table_rva as usize,
252 sections,
253 file_alignment,
254 opts,
255 ) {
256 debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva);
257 let import_address_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
258 bytes,
259 import_address_table_offset,
260 sections,
261 file_alignment,
262 opts,
263 )?;
264 debug!(
265 "Successfully synthesized import lookup table entry from IAT: {:#?}",
266 import_address_table
267 );
268 Some(import_address_table)
269 } else {
270 None
271 }
272 };
273
274 let rva = match import_directory_entry.import_address_table_rva.is_zero() {
275 true => import_directory_entry.import_lookup_table_rva,
276 false => import_directory_entry.import_address_table_rva,
277 };
278
279 let import_address_table_offset =
280 &mut utils::find_offset(rva as usize, sections, file_alignment, opts).ok_or_else(
281 || {
282 let target = if import_directory_entry.import_address_table_rva.is_zero() {
283 "import_lookup_table_rva"
284 } else {
285 "import_address_table_rva"
286 };
287 error::Error::Malformed(format!(
288 "Cannot map {} {:#x} into offset for {}",
289 target, rva, name
290 ))
291 },
292 )?;
293 let mut import_address_table = Vec::new();
294 loop {
295 let import_address = bytes
296 .gread_with::<T>(import_address_table_offset, LE)?
297 .into();
298 if import_address == 0 {
299 break;
300 } else {
301 import_address_table.push(import_address);
302 }
303 }
304 Ok(SyntheticImportDirectoryEntry {
305 import_directory_entry,
306 name,
307 import_lookup_table,
308 import_address_table,
309 })
310 }
311}
312
313#[derive(Debug)]
314pub struct ImportData<'a> {
316 pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
317}
318
319impl<'a> ImportData<'a> {
320 pub fn parse<T: Bitfield<'a>>(
321 bytes: &'a [u8],
322 dd: data_directories::DataDirectory,
323 sections: &[section_table::SectionTable],
324 file_alignment: u32,
325 ) -> error::Result<ImportData<'a>> {
326 Self::parse_with_opts::<T>(
327 bytes,
328 dd,
329 sections,
330 file_alignment,
331 &options::ParseOptions::default(),
332 )
333 }
334
335 pub fn parse_with_opts<T: Bitfield<'a>>(
336 bytes: &'a [u8],
337 dd: data_directories::DataDirectory,
338 sections: &[section_table::SectionTable],
339 file_alignment: u32,
340 opts: &options::ParseOptions,
341 ) -> error::Result<ImportData<'a>> {
342 let import_directory_table_rva = dd.virtual_address as usize;
343 debug!(
344 "import_directory_table_rva {:#x}",
345 import_directory_table_rva
346 );
347 let offset =
348 &mut utils::find_offset(import_directory_table_rva, sections, file_alignment, opts)
349 .ok_or_else(|| {
350 error::Error::Malformed(format!(
351 "Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset",
352 import_directory_table_rva
353 ))
354 })?;
355 debug!("import data offset {:#x}", offset);
356 let mut import_data = Vec::new();
357 loop {
358 let import_directory_entry: ImportDirectoryEntry =
359 bytes.gread_with(offset, scroll::LE)?;
360 debug!("{:#?} at {:#x}", import_directory_entry, offset);
361 if import_directory_entry.is_null() || !import_directory_entry.is_possibly_valid() {
362 break;
363 } else {
364 let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>(
365 bytes,
366 import_directory_entry,
367 sections,
368 file_alignment,
369 opts,
370 )?;
371 debug!("entry {:#?} at {:#x}", entry, offset);
372 import_data.push(entry);
373 }
374 }
375 debug!("finished ImportData");
376 Ok(ImportData { import_data })
377 }
378}
379
380#[derive(Debug)]
381pub struct Import<'a> {
383 pub name: Cow<'a, str>,
384 pub dll: &'a str,
385 pub ordinal: u16,
386 pub offset: usize,
387 pub rva: usize,
388 pub size: usize,
389}
390
391impl<'a> Import<'a> {
392 pub fn parse<T: Bitfield<'a>>(
393 _bytes: &'a [u8],
394 import_data: &ImportData<'a>,
395 _sections: &[section_table::SectionTable],
396 ) -> error::Result<Vec<Import<'a>>> {
397 let mut imports = Vec::new();
398 for data in &import_data.import_data {
399 if let Some(ref import_lookup_table) = data.import_lookup_table {
400 let dll = data.name;
401 let import_base = data.import_directory_entry.import_address_table_rva as usize;
402 debug!("Getting imports from {}", &dll);
403 for (i, entry) in import_lookup_table.iter().enumerate() {
404 let offset = import_base + (i * T::size_of());
405 use self::SyntheticImportLookupTableEntry::*;
406 let (rva, name, ordinal) = match *entry {
407 HintNameTableRVA((rva, ref hint_entry)) => {
408 (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint)
412 }
413 OrdinalNumber(ordinal) => {
414 let name = format!("ORDINAL {}", ordinal);
415 (0x0, Cow::Owned(name), ordinal)
416 }
417 };
418 let import = Import {
419 name,
420 ordinal,
421 dll,
422 size: T::size_of(),
423 offset,
424 rva: rva as usize,
425 };
426 imports.push(import);
427 }
428 }
429 }
430 Ok(imports)
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 const NOT_WELL_FORMED_IMPORT: &[u8] =
437 include_bytes!("../../tests/bins/pe/not_well_formed_import.exe.bin");
438 const WELL_FORMED_IMPORT: &[u8] =
439 include_bytes!("../../tests/bins/pe/well_formed_import.exe.bin");
440
441 #[test]
442 fn parse_non_well_formed_import_table() {
443 let binary = crate::pe::PE::parse(NOT_WELL_FORMED_IMPORT).expect("Unable to parse binary");
444 assert_eq!(binary.import_data.is_some(), true);
445 assert_eq!(binary.imports.len(), 1);
446 assert_eq!(binary.imports[0].name, "ORDINAL 51398");
447 assert_eq!(binary.imports[0].dll, "abcd.dll");
448 assert_eq!(binary.imports[0].ordinal, 51398);
449 assert_eq!(binary.imports[0].offset, 0x7014);
450 assert_eq!(binary.imports[0].rva, 0);
451 assert_eq!(binary.imports[0].size, 8);
452 }
453
454 #[test]
455 fn parse_well_formed_import_table() {
456 let binary = crate::pe::PE::parse(WELL_FORMED_IMPORT).expect("Unable to parse binary");
457 assert_eq!(binary.import_data.is_some(), true);
458 assert_eq!(binary.imports.len(), 1);
459 assert_eq!(binary.imports[0].name, "GetLastError");
460 assert_eq!(binary.imports[0].dll, "KERNEL32.dll");
461 assert_eq!(binary.imports[0].ordinal, 647);
462 assert_eq!(binary.imports[0].offset, 0x2000);
463 assert_eq!(binary.imports[0].rva, 0x21B8);
464 assert_eq!(binary.imports[0].size, 8);
465 }
466}