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