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