object/read/pe/
data_directory.rs

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