| 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 | |