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&mut DebugStruct<'_, '_>.debug_struct("Export")
56 .field("ordinal", &self.ordinal)
57 .field("name", &self.name.map(ByteString))
58 .field(name:"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: &u32) => write!(f, "Address({:#x})", address),
67 ExportTarget::ForwardByOrdinal(library: &&[u8], ordinal: &u32) => write!(
68 f,
69 "ForwardByOrdinal({:?}.#{})",
70 ByteString(library),
71 ordinal
72 ),
73 ExportTarget::ForwardByName(library: &&[u8], name: &&[u8]) => 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: u8 in digits {
330 let x: u32 = (c as char).to_digit(radix:10)?;
331 result = result.checked_mul(10)?.checked_add(x)?;
332 }
333 Some(result)
334}
335