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