object/read/pe/
import.rs

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