Skip to main content

object/read/pe/
import.rs

1use core::fmt::Debug;
2use core::mem;
3
4use crate::endian::{LittleEndian as LE, U16};
5use crate::pe;
6use crate::pod::Pod;
7use crate::read::{Bytes, ReadError, ReadRef, Result};
8
9use super::{ImageNtHeaders, SectionTable};
10
11/// Information for parsing a PE import table.
12///
13/// Returned by [`DataDirectories::import_table`](super::DataDirectories::import_table).
14#[derive(Debug, Clone)]
15pub struct ImportTable<'data> {
16    import_address: u32,
17    import_section_data: Bytes<'data>,
18    import_section_address: u32,
19    name_section_data: Bytes<'data>,
20    name_section_address: u32,
21}
22
23impl<'data> ImportTable<'data> {
24    /// Create a new import table parser.
25    ///
26    /// The import descriptors start at `import_address`.
27    /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is
28    /// ignored by the Windows loader, and so descriptors will be parsed until a null entry.
29    ///
30    /// `section_data` should be from the section containing `import_address`, and
31    /// `section_address` should be the address of that section. Pointers within the
32    /// descriptors and thunks may point to anywhere within the section data.
33    pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
34        ImportTable {
35            import_address,
36            import_section_data: Bytes(section_data),
37            import_section_address: section_address,
38            name_section_data: Bytes(section_data),
39            name_section_address: section_address,
40        }
41    }
42
43    /// Create a new import table parser.
44    ///
45    /// The import descriptors start at `import_address`.
46    /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is
47    /// ignored by the Windows loader, and so descriptors will be parsed until a null entry.
48    ///
49    /// `data` should be the data for the entire file. The section table is used to
50    /// find the data for the section containing `import_address`.
51    ///
52    /// We also support names in a separate section by parsing the first descriptor
53    /// to get the name address, and finding the section containing that address.
54    pub fn from_sections<R: ReadRef<'data>>(
55        data: R,
56        sections: &SectionTable<'data>,
57        import_address: u32,
58    ) -> Result<Self> {
59        let (section_data, section_address) = sections
60            .pe_data_containing(data, import_address)
61            .read_error("Invalid import data dir virtual address")?;
62        let mut imports = Self::new(section_data, section_address, import_address);
63
64        // Usually imports, names and thunks are all in the same section. However,
65        // this isn't required, and use of different sections has been seen in older
66        // files. We don't know which other section to use without parsing first though,
67        // so do that for the first descriptor and ignore any errors (the user will get
68        // those errors when they parse again later).
69        //
70        // This still won't support all possible layouts (e.g. names split across
71        // multiple sections).
72        if let Ok(mut descriptors) = imports.descriptors() {
73            if let Ok(Some(descriptor)) = descriptors.next() {
74                if let Some((name_section_data, name_section_address)) =
75                    sections.pe_data_containing(data, descriptor.name.get(LE))
76                {
77                    imports.name_section_data = Bytes(name_section_data);
78                    imports.name_section_address = name_section_address;
79                }
80            }
81        }
82        Ok(imports)
83    }
84
85    /// Return an iterator for the import descriptors.
86    pub fn descriptors(&self) -> Result<ImportDescriptorIterator<'data>> {
87        let offset = self
88            .import_address
89            .wrapping_sub(self.import_section_address);
90        let mut data = self.import_section_data;
91        data.skip(offset as usize)
92            .read_error("Invalid PE import descriptor address")?;
93        Ok(ImportDescriptorIterator { data, null: false })
94    }
95
96    /// Return a library name given its address.
97    ///
98    /// This address may be from [`pe::ImageImportDescriptor::name`].
99    pub fn name(&self, address: u32) -> Result<&'data [u8]> {
100        self.name_section_data
101            .read_string_at(address.wrapping_sub(self.name_section_address) as usize)
102            .read_error("Invalid PE import descriptor name")
103    }
104
105    /// Return a list of thunks given its address.
106    ///
107    /// This address may be from [`pe::ImageImportDescriptor::original_first_thunk`]
108    /// or [`pe::ImageImportDescriptor::first_thunk`].
109    pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
110        let offset = address.wrapping_sub(self.import_section_address);
111        let mut data = self.import_section_data;
112        data.skip(offset as usize)
113            .read_error("Invalid PE import thunk table address")?;
114        Ok(ImportThunkList { data })
115    }
116
117    /// Parse a thunk.
118    pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> {
119        if thunk.is_ordinal() {
120            Ok(Import::Ordinal(thunk.ordinal()))
121        } else {
122            let (hint, name) = self.hint_name(thunk.address())?;
123            Ok(Import::Name(hint, name))
124        }
125    }
126
127    /// Return the hint and name at the given address.
128    ///
129    /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`].
130    ///
131    /// The hint is an index into the export name pointer table in the target library.
132    pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
133        let offset = address.wrapping_sub(self.name_section_address);
134        let mut data = self.name_section_data;
135        data.skip(offset as usize)
136            .read_error("Invalid PE import thunk address")?;
137        let hint = data
138            .read::<U16<LE>>()
139            .read_error("Missing PE import thunk hint")?
140            .get(LE);
141        let name = data
142            .read_string()
143            .ok()
144            .filter(|s| !s.is_empty())
145            .read_error("Missing PE import thunk name")?;
146        Ok((hint, name))
147    }
148}
149
150/// A fallible iterator for the descriptors in the import data directory.
151#[derive(Debug, Clone)]
152pub struct ImportDescriptorIterator<'data> {
153    data: Bytes<'data>,
154    null: bool,
155}
156
157impl<'data> ImportDescriptorIterator<'data> {
158    /// Return the next descriptor.
159    ///
160    /// Returns `Ok(None)` when a null descriptor is found.
161    pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> {
162        if self.null {
163            return Ok(None);
164        }
165        let result = self
166            .data
167            .read::<pe::ImageImportDescriptor>()
168            .read_error("Missing PE null import descriptor");
169        match result {
170            Ok(import_desc) => {
171                if import_desc.is_null() {
172                    self.null = true;
173                    Ok(None)
174                } else {
175                    Ok(Some(import_desc))
176                }
177            }
178            Err(e) => {
179                self.null = true;
180                Err(e)
181            }
182        }
183    }
184}
185
186impl<'data> Iterator for ImportDescriptorIterator<'data> {
187    type Item = Result<&'data pe::ImageImportDescriptor>;
188
189    fn next(&mut self) -> Option<Self::Item> {
190        self.next().transpose()
191    }
192}
193
194/// A list of import thunks.
195///
196/// These may be in the import lookup table, or the import address table.
197#[derive(Debug, Clone)]
198pub struct ImportThunkList<'data> {
199    data: Bytes<'data>,
200}
201
202impl<'data> ImportThunkList<'data> {
203    /// Get the thunk at the given index.
204    pub fn get<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> {
205        let thunk = self
206            .data
207            .read_at(index * mem::size_of::<Pe::ImageThunkData>())
208            .read_error("Invalid PE import thunk index")?;
209        Ok(*thunk)
210    }
211
212    /// Return the first thunk in the list, and update `self` to point after it.
213    ///
214    /// Returns `Ok(None)` when a null thunk is found.
215    pub fn next<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> {
216        let thunk = self
217            .data
218            .read::<Pe::ImageThunkData>()
219            .read_error("Missing PE null import thunk")?;
220        if thunk.address() == 0 {
221            Ok(None)
222        } else {
223            Ok(Some(*thunk))
224        }
225    }
226}
227
228/// A parsed import thunk.
229#[derive(Debug, Clone, Copy)]
230pub enum Import<'data> {
231    /// Import by ordinal.
232    Ordinal(u16),
233    /// Import by name.
234    ///
235    /// Includes a hint for the index into the export name pointer table in the target library.
236    Name(u16, &'data [u8]),
237}
238
239/// A trait for generic access to [`pe::ImageThunkData32`] and [`pe::ImageThunkData64`].
240#[allow(missing_docs)]
241pub trait ImageThunkData: Debug + Pod {
242    /// Return the raw thunk value.
243    fn raw(self) -> u64;
244
245    /// Returns true if the ordinal flag is set.
246    fn is_ordinal(self) -> bool;
247
248    /// Return the ordinal portion of the thunk.
249    ///
250    /// Does not check the ordinal flag.
251    fn ordinal(self) -> u16;
252
253    /// Return the RVA portion of the thunk.
254    ///
255    /// Does not check the ordinal flag.
256    fn address(self) -> u32;
257}
258
259impl ImageThunkData for pe::ImageThunkData64 {
260    fn raw(self) -> u64 {
261        self.0.get(LE)
262    }
263
264    fn is_ordinal(self) -> bool {
265        self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG64 != 0
266    }
267
268    fn ordinal(self) -> u16 {
269        self.0.get(LE) as u16
270    }
271
272    fn address(self) -> u32 {
273        self.0.get(LE) as u32 & 0x7fff_ffff
274    }
275}
276
277impl ImageThunkData for pe::ImageThunkData32 {
278    fn raw(self) -> u64 {
279        self.0.get(LE).into()
280    }
281
282    fn is_ordinal(self) -> bool {
283        self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG32 != 0
284    }
285
286    fn ordinal(self) -> u16 {
287        self.0.get(LE) as u16
288    }
289
290    fn address(self) -> u32 {
291        self.0.get(LE) & 0x7fff_ffff
292    }
293}
294
295/// Information for parsing a PE delay-load import table.
296///
297/// Returned by
298/// [`DataDirectories::delay_load_import_table`](super::DataDirectories::delay_load_import_table).
299#[derive(Debug, Clone)]
300pub struct DelayLoadImportTable<'data> {
301    section_data: Bytes<'data>,
302    section_address: u32,
303    import_address: u32,
304}
305
306impl<'data> DelayLoadImportTable<'data> {
307    /// Create a new delay load import table parser.
308    ///
309    /// The import descriptors start at `import_address`.
310    /// This table works in the same way the import table does: descriptors will be
311    /// parsed until a null entry.
312    ///
313    /// `section_data` should be from the section containing `import_address`, and
314    /// `section_address` should be the address of that section. Pointers within the
315    /// descriptors and thunks may point to anywhere within the section data.
316    pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
317        DelayLoadImportTable {
318            section_data: Bytes(section_data),
319            section_address,
320            import_address,
321        }
322    }
323
324    /// Return an iterator for the import descriptors.
325    pub fn descriptors(&self) -> Result<DelayLoadDescriptorIterator<'data>> {
326        let offset = self.import_address.wrapping_sub(self.section_address);
327        let mut data = self.section_data;
328        data.skip(offset as usize)
329            .read_error("Invalid PE delay-load import descriptor address")?;
330        Ok(DelayLoadDescriptorIterator { data, null: false })
331    }
332
333    /// Return a library name given its address.
334    ///
335    /// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`].
336    pub fn name(&self, address: u32) -> Result<&'data [u8]> {
337        self.section_data
338            .read_string_at(address.wrapping_sub(self.section_address) as usize)
339            .read_error("Invalid PE import descriptor name")
340    }
341
342    /// Return a list of thunks given its address.
343    ///
344    /// This address may be from the INT, i.e. from
345    /// [`pe::ImageDelayloadDescriptor::import_name_table_rva`].
346    ///
347    /// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used
348    /// by the delay loader at runtime to store values, and thus do not point inside the same
349    /// section as the INT. Calling this function on those addresses will fail.
350    pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
351        let offset = address.wrapping_sub(self.section_address);
352        let mut data = self.section_data;
353        data.skip(offset as usize)
354            .read_error("Invalid PE delay load import thunk table address")?;
355        Ok(ImportThunkList { data })
356    }
357
358    /// Parse a thunk.
359    pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> {
360        if thunk.is_ordinal() {
361            Ok(Import::Ordinal(thunk.ordinal()))
362        } else {
363            let (hint, name) = self.hint_name(thunk.address())?;
364            Ok(Import::Name(hint, name))
365        }
366    }
367
368    /// Return the hint and name at the given address.
369    ///
370    /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`].
371    ///
372    /// The hint is an index into the export name pointer table in the target library.
373    pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
374        let offset = address.wrapping_sub(self.section_address);
375        let mut data = self.section_data;
376        data.skip(offset as usize)
377            .read_error("Invalid PE delay load import thunk address")?;
378        let hint = data
379            .read::<U16<LE>>()
380            .read_error("Missing PE delay load import thunk hint")?
381            .get(LE);
382        let name = data
383            .read_string()
384            .ok()
385            .filter(|s| !s.is_empty())
386            .read_error("Missing PE delay load import thunk name")?;
387        Ok((hint, name))
388    }
389}
390
391/// A fallible iterator for the descriptors in the delay-load data directory.
392#[derive(Debug, Clone)]
393pub struct DelayLoadDescriptorIterator<'data> {
394    data: Bytes<'data>,
395    null: bool,
396}
397
398impl<'data> DelayLoadDescriptorIterator<'data> {
399    /// Return the next descriptor.
400    ///
401    /// Returns `Ok(None)` when a null descriptor is found.
402    pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> {
403        if self.null {
404            return Ok(None);
405        }
406        let result = self
407            .data
408            .read::<pe::ImageDelayloadDescriptor>()
409            .read_error("Missing PE null delay-load import descriptor");
410        match result {
411            Ok(import_desc) => {
412                if import_desc.is_null() {
413                    self.null = true;
414                    Ok(None)
415                } else {
416                    Ok(Some(import_desc))
417                }
418            }
419            Err(e) => {
420                self.null = true;
421                Err(e)
422            }
423        }
424    }
425}
426
427impl<'data> Iterator for DelayLoadDescriptorIterator<'data> {
428    type Item = Result<&'data pe::ImageDelayloadDescriptor>;
429
430    fn next(&mut self) -> Option<Self::Item> {
431        self.next().transpose()
432    }
433}