1 | use core::fmt::Debug; |
2 | use core::mem; |
3 | |
4 | use crate::endian::{LittleEndian as LE, U16Bytes}; |
5 | use crate::pe; |
6 | use crate::pod::Pod; |
7 | use crate::read::{Bytes, ReadError, Result}; |
8 | |
9 | use 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)] |
15 | pub struct ImportTable<'data> { |
16 | section_data: Bytes<'data>, |
17 | section_address: u32, |
18 | import_address: u32, |
19 | } |
20 | |
21 | impl<'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)] |
102 | pub struct ImportDescriptorIterator<'data> { |
103 | data: Bytes<'data>, |
104 | null: bool, |
105 | } |
106 | |
107 | impl<'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 | |
136 | impl<'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)] |
148 | pub struct ImportThunkList<'data> { |
149 | data: Bytes<'data>, |
150 | } |
151 | |
152 | impl<'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)] |
180 | pub 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)] |
191 | pub 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 | |
209 | impl 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 | |
227 | impl 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)] |
250 | pub struct DelayLoadImportTable<'data> { |
251 | section_data: Bytes<'data>, |
252 | section_address: u32, |
253 | import_address: u32, |
254 | } |
255 | |
256 | impl<'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)] |
341 | pub struct DelayLoadDescriptorIterator<'data> { |
342 | data: Bytes<'data>, |
343 | null: bool, |
344 | } |
345 | |
346 | impl<'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 | |
375 | impl<'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 | |