| 1 | use core::slice; | 
| 2 |  | 
| 3 | use crate::read::{Error, ReadError, ReadRef, Result}; | 
| 4 | use crate::{pe, LittleEndian as LE}; | 
| 5 |  | 
| 6 | use super::{ | 
| 7 |     DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, | 
| 8 |     SectionTable, | 
| 9 | }; | 
| 10 |  | 
| 11 | /// The table of data directories in a PE file. | 
| 12 | /// | 
| 13 | /// Returned by [`ImageNtHeaders::parse`](super::ImageNtHeaders::parse). | 
| 14 | #[derive (Debug, Clone, Copy)] | 
| 15 | pub struct DataDirectories<'data> { | 
| 16 |     entries: &'data [pe::ImageDataDirectory], | 
| 17 | } | 
| 18 |  | 
| 19 | impl<'data> DataDirectories<'data> { | 
| 20 |     /// Parse the data directory table. | 
| 21 |     /// | 
| 22 |     /// `data` must be the remaining optional data following the | 
| 23 |     /// [optional header](pe::ImageOptionalHeader64).  `number` must be from the | 
| 24 |     /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes) | 
| 25 |     /// field of the optional header. | 
| 26 |     pub fn parse(data: &'data [u8], number: u32) -> Result<Self> { | 
| 27 |         let entries = data | 
| 28 |             .read_slice_at(0, number as usize) | 
| 29 |             .read_error("Invalid PE number of RVA and sizes" )?; | 
| 30 |         Ok(DataDirectories { entries }) | 
| 31 |     } | 
| 32 |  | 
| 33 |     /// The number of data directories. | 
| 34 |     #[allow (clippy::len_without_is_empty)] | 
| 35 |     pub fn len(&self) -> usize { | 
| 36 |         self.entries.len() | 
| 37 |     } | 
| 38 |  | 
| 39 |     /// Iterator over the data directories. | 
| 40 |     pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> { | 
| 41 |         self.entries.iter() | 
| 42 |     } | 
| 43 |  | 
| 44 |     /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants). | 
| 45 |     pub fn enumerate(&self) -> core::iter::Enumerate<slice::Iter<'data, pe::ImageDataDirectory>> { | 
| 46 |         self.entries.iter().enumerate() | 
| 47 |     } | 
| 48 |  | 
| 49 |     /// Returns the data directory at the given index. | 
| 50 |     /// | 
| 51 |     /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants. | 
| 52 |     /// | 
| 53 |     /// Returns `None` if the index is larger than the table size, | 
| 54 |     /// or if the entry at the index has a zero virtual address. | 
| 55 |     pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> { | 
| 56 |         self.entries | 
| 57 |             .get(index) | 
| 58 |             .filter(|d| d.virtual_address.get(LE) != 0) | 
| 59 |     } | 
| 60 |  | 
| 61 |     /// Returns the unparsed export directory. | 
| 62 |     /// | 
| 63 |     /// `data` must be the entire file data. | 
| 64 |     pub fn export_directory<R: ReadRef<'data>>( | 
| 65 |         &self, | 
| 66 |         data: R, | 
| 67 |         sections: &SectionTable<'data>, | 
| 68 |     ) -> Result<Option<&'data pe::ImageExportDirectory>> { | 
| 69 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { | 
| 70 |             Some(data_dir) => data_dir, | 
| 71 |             None => return Ok(None), | 
| 72 |         }; | 
| 73 |         let export_data = data_dir.data(data, sections)?; | 
| 74 |         ExportTable::parse_directory(export_data).map(Some) | 
| 75 |     } | 
| 76 |  | 
| 77 |     /// Returns the partially parsed export directory. | 
| 78 |     /// | 
| 79 |     /// `data` must be the entire file data. | 
| 80 |     pub fn export_table<R: ReadRef<'data>>( | 
| 81 |         &self, | 
| 82 |         data: R, | 
| 83 |         sections: &SectionTable<'data>, | 
| 84 |     ) -> Result<Option<ExportTable<'data>>> { | 
| 85 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { | 
| 86 |             Some(data_dir) => data_dir, | 
| 87 |             None => return Ok(None), | 
| 88 |         }; | 
| 89 |         let export_va = data_dir.virtual_address.get(LE); | 
| 90 |         let export_data = data_dir.data(data, sections)?; | 
| 91 |         ExportTable::parse(export_data, export_va).map(Some) | 
| 92 |     } | 
| 93 |  | 
| 94 |     /// Returns the partially parsed import directory. | 
| 95 |     /// | 
| 96 |     /// `data` must be the entire file data. | 
| 97 |     pub fn import_table<R: ReadRef<'data>>( | 
| 98 |         &self, | 
| 99 |         data: R, | 
| 100 |         sections: &SectionTable<'data>, | 
| 101 |     ) -> Result<Option<ImportTable<'data>>> { | 
| 102 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) { | 
| 103 |             Some(data_dir) => data_dir, | 
| 104 |             None => return Ok(None), | 
| 105 |         }; | 
| 106 |         let import_va = data_dir.virtual_address.get(LE); | 
| 107 |         let (section_data, section_va) = sections | 
| 108 |             .pe_data_containing(data, import_va) | 
| 109 |             .read_error("Invalid import data dir virtual address" )?; | 
| 110 |         Ok(Some(ImportTable::new(section_data, section_va, import_va))) | 
| 111 |     } | 
| 112 |  | 
| 113 |     /// Returns the partially parsed delay-load import directory. | 
| 114 |     /// | 
| 115 |     /// `data` must be the entire file data. | 
| 116 |     pub fn delay_load_import_table<R: ReadRef<'data>>( | 
| 117 |         &self, | 
| 118 |         data: R, | 
| 119 |         sections: &SectionTable<'data>, | 
| 120 |     ) -> Result<Option<DelayLoadImportTable<'data>>> { | 
| 121 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) { | 
| 122 |             Some(data_dir) => data_dir, | 
| 123 |             None => return Ok(None), | 
| 124 |         }; | 
| 125 |         let import_va = data_dir.virtual_address.get(LE); | 
| 126 |         let (section_data, section_va) = sections | 
| 127 |             .pe_data_containing(data, import_va) | 
| 128 |             .read_error("Invalid import data dir virtual address" )?; | 
| 129 |         Ok(Some(DelayLoadImportTable::new( | 
| 130 |             section_data, | 
| 131 |             section_va, | 
| 132 |             import_va, | 
| 133 |         ))) | 
| 134 |     } | 
| 135 |  | 
| 136 |     /// Returns the blocks in the base relocation directory. | 
| 137 |     /// | 
| 138 |     /// `data` must be the entire file data. | 
| 139 |     pub fn relocation_blocks<R: ReadRef<'data>>( | 
| 140 |         &self, | 
| 141 |         data: R, | 
| 142 |         sections: &SectionTable<'data>, | 
| 143 |     ) -> Result<Option<RelocationBlockIterator<'data>>> { | 
| 144 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) { | 
| 145 |             Some(data_dir) => data_dir, | 
| 146 |             None => return Ok(None), | 
| 147 |         }; | 
| 148 |         let reloc_data = data_dir.data(data, sections)?; | 
| 149 |         Ok(Some(RelocationBlockIterator::new(reloc_data))) | 
| 150 |     } | 
| 151 |  | 
| 152 |     /// Returns the resource directory. | 
| 153 |     /// | 
| 154 |     /// `data` must be the entire file data. | 
| 155 |     pub fn resource_directory<R: ReadRef<'data>>( | 
| 156 |         &self, | 
| 157 |         data: R, | 
| 158 |         sections: &SectionTable<'data>, | 
| 159 |     ) -> Result<Option<ResourceDirectory<'data>>> { | 
| 160 |         let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) { | 
| 161 |             Some(data_dir) => data_dir, | 
| 162 |             None => return Ok(None), | 
| 163 |         }; | 
| 164 |         let rsrc_data = data_dir.data(data, sections)?; | 
| 165 |         Ok(Some(ResourceDirectory::new(rsrc_data))) | 
| 166 |     } | 
| 167 | } | 
| 168 |  | 
| 169 | impl pe::ImageDataDirectory { | 
| 170 |     /// Return the virtual address range of this directory entry. | 
| 171 |     pub fn address_range(&self) -> (u32, u32) { | 
| 172 |         (self.virtual_address.get(LE), self.size.get(LE)) | 
| 173 |     } | 
| 174 |  | 
| 175 |     /// Return the file offset and size of this directory entry. | 
| 176 |     /// | 
| 177 |     /// This function has some limitations: | 
| 178 |     /// - It requires that the data is contained in a single section. | 
| 179 |     /// - It uses the size field of the directory entry, which is | 
| 180 |     /// not desirable for all data directories. | 
| 181 |     /// - It uses the `virtual_address` of the directory entry as an address, | 
| 182 |     /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. | 
| 183 |     pub fn file_range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> { | 
| 184 |         let (offset, section_size) = sections | 
| 185 |             .pe_file_range_at(self.virtual_address.get(LE)) | 
| 186 |             .read_error("Invalid data dir virtual address" )?; | 
| 187 |         let size = self.size.get(LE); | 
| 188 |         if size > section_size { | 
| 189 |             return Err(Error("Invalid data dir size" )); | 
| 190 |         } | 
| 191 |         Ok((offset, size)) | 
| 192 |     } | 
| 193 |  | 
| 194 |     /// Get the data referenced by this directory entry. | 
| 195 |     /// | 
| 196 |     /// This function has some limitations: | 
| 197 |     /// - It requires that the data is contained in a single section. | 
| 198 |     /// - It uses the size field of the directory entry, which is | 
| 199 |     /// not desirable for all data directories. | 
| 200 |     /// - It uses the `virtual_address` of the directory entry as an address, | 
| 201 |     /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. | 
| 202 |     pub fn data<'data, R: ReadRef<'data>>( | 
| 203 |         &self, | 
| 204 |         data: R, | 
| 205 |         sections: &SectionTable<'data>, | 
| 206 |     ) -> Result<&'data [u8]> { | 
| 207 |         sections | 
| 208 |             .pe_data_at(data, self.virtual_address.get(LE)) | 
| 209 |             .read_error("Invalid data dir virtual address" )? | 
| 210 |             .get(..self.size.get(LE) as usize) | 
| 211 |             .read_error("Invalid data dir size" ) | 
| 212 |     } | 
| 213 | } | 
| 214 |  |