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