1use alloc::borrow::Cow;
2use alloc::vec::Vec;
3
4use zerocopy::little_endian::{U16, U32};
5
6use super::{ImageDataDirectory, SectionTable};
7use crate::address::Rva;
8use crate::bytes::Bytes;
9use crate::error::{Result, malformed};
10
11pe_struct! {
12 pub struct ImageExportDirectory {
14 characteristics: U32 => u32,
15 time_date_stamp: U32 => u32,
16 major_version: U16 => u16,
17 minor_version: U16 => u16,
18 name: U32 => Rva,
20 base: U32 => u32,
22 number_of_functions: U32 => u32,
23 number_of_names: U32 => u32,
24 address_of_functions: U32 => Rva,
25 address_of_names: U32 => Rva,
26 address_of_name_ordinals: U32 => Rva,
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32#[non_exhaustive]
33pub struct ExportFunction<'a> {
34 pub name: Option<Cow<'a, str>>,
35 pub address: Rva,
36 pub ordinal: u16,
37 pub forward_name: Option<Cow<'a, str>>,
39}
40
41impl ExportFunction<'_> {
42 pub fn is_forwarded(&self) -> bool {
43 self.forward_name.as_deref().is_some_and(|n| !n.is_empty())
44 }
45}
46
47pub(crate) fn parse_export_functions<'a>(
50 data: &'a [u8],
51 directory: &ImageExportDirectory,
52 sections: &SectionTable<'a>,
53 export_data_directory: &ImageDataDirectory,
54) -> Result<Vec<ExportFunction<'a>>> {
55 let bytes = Bytes(data);
56 let function_table = sections
57 .rva_to_offset(directory.address_of_functions())?
58 .usize();
59 let addresses: &[U32] =
60 bytes.read_slice(function_table, directory.number_of_functions() as usize)?;
61 let mut functions: Vec<_> = addresses
62 .iter()
63 .zip(0u32..)
64 .map(|(address, i)| ExportFunction {
65 name: None,
66 address: Rva(address.get()),
67 ordinal: i.wrapping_add(directory.base()) as u16,
68 forward_name: None,
69 })
70 .collect();
71 if directory.number_of_names() == 0 {
72 return Ok(functions);
73 }
74
75 let count = directory.number_of_names() as usize;
76 let names: &[U32] = bytes.read_slice(
77 sections
78 .rva_to_offset(directory.address_of_names())?
79 .usize(),
80 count,
81 )?;
82 let ordinals: &[U16] = bytes.read_slice(
83 sections
84 .rva_to_offset(directory.address_of_name_ordinals())?
85 .usize(),
86 count,
87 )?;
88 let start = export_data_directory.virtual_address().get();
89 let end = start.wrapping_add(export_data_directory.size());
90 for (name, ordinal) in names.iter().zip(ordinals) {
91 let function = functions
92 .get_mut(ordinal.get() as usize)
93 .ok_or(malformed("export directory", "name ordinal out of range"))?;
94 if (start..end).contains(&function.address.get()) {
95 let forward = sections.rva_to_offset(function.address)?;
96 function.forward_name = Some(bytes.ascii_string(forward.usize())?);
97 }
98 function.name = Some(bytes.ascii_string(sections.rva_to_offset(Rva(name.get()))?.usize())?);
99 }
100 Ok(functions)
101}