object/read/pe/
export.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes};
5use crate::pe;
6use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result};
7
8/// Where an export is pointing to.
9#[derive(Clone, Copy)]
10pub enum ExportTarget<'data> {
11    /// The address of the export, relative to the image base.
12    Address(u32),
13    /// Forwarded to an export ordinal in another DLL.
14    ///
15    /// This gives the name of the DLL, and the ordinal.
16    ForwardByOrdinal(&'data [u8], u32),
17    /// Forwarded to an export name in another DLL.
18    ///
19    /// This gives the name of the DLL, and the export name.
20    ForwardByName(&'data [u8], &'data [u8]),
21}
22
23impl<'data> ExportTarget<'data> {
24    /// Returns true if the target is an address.
25    pub fn is_address(&self) -> bool {
26        match self {
27            ExportTarget::Address(_) => true,
28            _ => false,
29        }
30    }
31
32    /// Returns true if the export is forwarded to another DLL.
33    pub fn is_forward(&self) -> bool {
34        !self.is_address()
35    }
36}
37
38/// An export from a PE file.
39///
40/// There are multiple kinds of PE exports (with or without a name, and local or forwarded).
41#[derive(Clone, Copy)]
42pub struct Export<'data> {
43    /// The ordinal of the export.
44    ///
45    /// These are sequential, starting at a base specified in the DLL.
46    pub ordinal: u32,
47    /// The name of the export, if known.
48    pub name: Option<&'data [u8]>,
49    /// The target of this export.
50    pub target: ExportTarget<'data>,
51}
52
53impl<'a> Debug for Export<'a> {
54    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
55        f.debug_struct("Export")
56            .field("ordinal", &self.ordinal)
57            .field("name", &self.name.map(ByteString))
58            .field("target", &self.target)
59            .finish()
60    }
61}
62
63impl<'a> Debug for ExportTarget<'a> {
64    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
65        match self {
66            ExportTarget::Address(address) => write!(f, "Address({:#x})", address),
67            ExportTarget::ForwardByOrdinal(library, ordinal) => write!(
68                f,
69                "ForwardByOrdinal({:?}.#{})",
70                ByteString(library),
71                ordinal
72            ),
73            ExportTarget::ForwardByName(library, name) => write!(
74                f,
75                "ForwardByName({:?}.{:?})",
76                ByteString(library),
77                ByteString(name)
78            ),
79        }
80    }
81}
82
83/// A partially parsed PE export table.
84///
85/// Returned by [`DataDirectories::export_table`](super::DataDirectories::export_table).
86#[derive(Debug, Clone)]
87pub struct ExportTable<'data> {
88    data: Bytes<'data>,
89    virtual_address: u32,
90    directory: &'data pe::ImageExportDirectory,
91    addresses: &'data [U32Bytes<LE>],
92    names: &'data [U32Bytes<LE>],
93    name_ordinals: &'data [U16Bytes<LE>],
94}
95
96impl<'data> ExportTable<'data> {
97    /// Parse the export table given its section data and address.
98    pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> {
99        let directory = Self::parse_directory(data)?;
100        let data = Bytes(data);
101
102        let mut addresses = &[][..];
103        let address_of_functions = directory.address_of_functions.get(LE);
104        if address_of_functions != 0 {
105            addresses = data
106                .read_slice_at::<U32Bytes<_>>(
107                    address_of_functions.wrapping_sub(virtual_address) as usize,
108                    directory.number_of_functions.get(LE) as usize,
109                )
110                .read_error("Invalid PE export address table")?;
111        }
112
113        let mut names = &[][..];
114        let mut name_ordinals = &[][..];
115        let address_of_names = directory.address_of_names.get(LE);
116        let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE);
117        if address_of_names != 0 {
118            if address_of_name_ordinals == 0 {
119                return Err(Error("Missing PE export ordinal table"));
120            }
121
122            let number = directory.number_of_names.get(LE) as usize;
123            names = data
124                .read_slice_at::<U32Bytes<_>>(
125                    address_of_names.wrapping_sub(virtual_address) as usize,
126                    number,
127                )
128                .read_error("Invalid PE export name pointer table")?;
129            name_ordinals = data
130                .read_slice_at::<U16Bytes<_>>(
131                    address_of_name_ordinals.wrapping_sub(virtual_address) as usize,
132                    number,
133                )
134                .read_error("Invalid PE export ordinal table")?;
135        }
136
137        Ok(ExportTable {
138            data,
139            virtual_address,
140            directory,
141            addresses,
142            names,
143            name_ordinals,
144        })
145    }
146
147    /// Parse the export directory given its section data.
148    pub fn parse_directory(data: &'data [u8]) -> Result<&'data pe::ImageExportDirectory> {
149        data.read_at::<pe::ImageExportDirectory>(0)
150            .read_error("Invalid PE export dir size")
151    }
152
153    /// Returns the header of the export table.
154    pub fn directory(&self) -> &'data pe::ImageExportDirectory {
155        self.directory
156    }
157
158    /// Returns the base value of ordinals.
159    ///
160    /// Adding this to an address index will give an ordinal.
161    pub fn ordinal_base(&self) -> u32 {
162        self.directory.base.get(LE)
163    }
164
165    /// Returns the unparsed address table.
166    ///
167    /// An address table entry may be a local address, or the address of a forwarded export entry.
168    /// See [`Self::is_forward`] and [`Self::target_from_address`].
169    pub fn addresses(&self) -> &'data [U32Bytes<LE>] {
170        self.addresses
171    }
172
173    /// Returns the unparsed name pointer table.
174    ///
175    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
176    pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] {
177        self.names
178    }
179
180    /// Returns the unparsed ordinal table.
181    ///
182    /// An ordinal table entry is a 0-based index into the address table.
183    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
184    pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] {
185        self.name_ordinals
186    }
187
188    /// Returns an iterator for the entries in the name pointer table and ordinal table.
189    ///
190    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
191    ///
192    /// An ordinal table entry is a 0-based index into the address table.
193    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
194    pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data {
195        self.names
196            .iter()
197            .map(|x| x.get(LE))
198            .zip(self.name_ordinals.iter().map(|x| x.get(LE)))
199    }
200
201    /// Returns the export address table entry at the given address index.
202    ///
203    /// This may be a local address, or the address of a forwarded export entry.
204    /// See [`Self::is_forward`] and [`Self::target_from_address`].
205    ///
206    /// `index` is a 0-based index into the export address table.
207    pub fn address_by_index(&self, index: u32) -> Result<u32> {
208        Ok(self
209            .addresses
210            .get(index as usize)
211            .read_error("Invalid PE export address index")?
212            .get(LE))
213    }
214
215    /// Returns the export address table entry at the given ordinal.
216    ///
217    /// This may be a local address, or the address of a forwarded export entry.
218    /// See [`Self::is_forward`] and [`Self::target_from_address`].
219    pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> {
220        self.address_by_index(ordinal.wrapping_sub(self.ordinal_base()))
221    }
222
223    /// Returns the target of the export at the given address index.
224    ///
225    /// `index` is a 0-based index into the export address table.
226    pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> {
227        self.target_from_address(self.address_by_index(index)?)
228    }
229
230    /// Returns the target of the export at the given ordinal.
231    pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> {
232        self.target_from_address(self.address_by_ordinal(ordinal)?)
233    }
234
235    /// Convert an export address table entry into a target.
236    pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> {
237        Ok(if let Some(forward) = self.forward_string(address)? {
238            let i = forward
239                .iter()
240                .position(|x| *x == b'.')
241                .read_error("Missing PE forwarded export separator")?;
242            let library = &forward[..i];
243            match &forward[i + 1..] {
244                [b'#', digits @ ..] => {
245                    let ordinal =
246                        parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?;
247                    ExportTarget::ForwardByOrdinal(library, ordinal)
248                }
249                [] => {
250                    return Err(Error("Missing PE forwarded export name"));
251                }
252                name => ExportTarget::ForwardByName(library, name),
253            }
254        } else {
255            ExportTarget::Address(address)
256        })
257    }
258
259    fn forward_offset(&self, address: u32) -> Option<usize> {
260        let offset = address.wrapping_sub(self.virtual_address) as usize;
261        if offset < self.data.len() {
262            Some(offset)
263        } else {
264            None
265        }
266    }
267
268    /// Return true if the export address table entry is a forward.
269    pub fn is_forward(&self, address: u32) -> bool {
270        self.forward_offset(address).is_some()
271    }
272
273    /// Return the forward string if the export address table entry is a forward.
274    pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> {
275        if let Some(offset) = self.forward_offset(address) {
276            self.data
277                .read_string_at(offset)
278                .read_error("Invalid PE forwarded export address")
279                .map(Some)
280        } else {
281            Ok(None)
282        }
283    }
284
285    /// Convert an export name pointer table entry into a name.
286    pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> {
287        let offset = name_pointer.wrapping_sub(self.virtual_address);
288        self.data
289            .read_string_at(offset as usize)
290            .read_error("Invalid PE export name pointer")
291    }
292
293    /// Returns the parsed exports in this table.
294    pub fn exports(&self) -> Result<Vec<Export<'data>>> {
295        // First, let's list all exports.
296        let mut exports = Vec::new();
297        let ordinal_base = self.ordinal_base();
298        for (i, address) in self.addresses.iter().enumerate() {
299            // Convert from an array index to an ordinal.
300            let ordinal = ordinal_base.wrapping_add(i as u32);
301            let target = self.target_from_address(address.get(LE))?;
302            exports.push(Export {
303                ordinal,
304                target,
305                // Might be populated later.
306                name: None,
307            });
308        }
309
310        // Now, check whether some (or all) of them have an associated name.
311        // `ordinal_index` is a 0-based index into `addresses`.
312        for (name_pointer, ordinal_index) in self.name_iter() {
313            let name = self.name_from_pointer(name_pointer)?;
314            exports
315                .get_mut(ordinal_index as usize)
316                .read_error("Invalid PE export ordinal")?
317                .name = Some(name);
318        }
319
320        Ok(exports)
321    }
322}
323
324fn parse_ordinal(digits: &[u8]) -> Option<u32> {
325    if digits.is_empty() {
326        return None;
327    }
328    let mut result: u32 = 0;
329    for &c in digits {
330        let x = (c as char).to_digit(10)?;
331        result = result.checked_mul(10)?.checked_add(x)?;
332    }
333    Some(result)
334}