1 | use core::fmt::Debug; |
2 | use core::mem; |
3 | |
4 | use crate::read::{Bytes, ReadError, Result}; |
5 | use crate::{pe, LittleEndian as LE, Pod, U16Bytes}; |
6 | |
7 | use 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)] |
13 | pub struct ImportTable<'data> { |
14 | section_data: Bytes<'data>, |
15 | section_address: u32, |
16 | import_address: u32, |
17 | } |
18 | |
19 | impl<'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)] |
100 | pub struct ImportDescriptorIterator<'data> { |
101 | data: Bytes<'data>, |
102 | } |
103 | |
104 | impl<'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)] |
125 | pub struct ImportThunkList<'data> { |
126 | data: Bytes<'data>, |
127 | } |
128 | |
129 | impl<'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)] |
157 | pub 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)] |
168 | pub 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 | |
186 | impl 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 | |
204 | impl 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)] |
227 | pub struct DelayLoadImportTable<'data> { |
228 | section_data: Bytes<'data>, |
229 | section_address: u32, |
230 | import_address: u32, |
231 | } |
232 | |
233 | impl<'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)] |
318 | pub struct DelayLoadDescriptorIterator<'data> { |
319 | data: Bytes<'data>, |
320 | } |
321 | |
322 | impl<'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 | |