| use crate::error; |
| use scroll::{ |
| ctx::{self}, |
| Pread, Pwrite, SizeWith, |
| }; |
| |
| #[repr(C)] |
| #[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)] |
| pub struct DataDirectory { |
| pub virtual_address: u32, |
| pub size: u32, |
| } |
| |
| pub const SIZEOF_DATA_DIRECTORY: usize = 8; |
| const NUM_DATA_DIRECTORIES: usize = 16; |
| |
| impl DataDirectory { |
| pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> { |
| Ok(bytes.gread_with(offset, scroll::LE)?) |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Copy, Clone)] |
| pub enum DataDirectoryType { |
| ExportTable, |
| ImportTable, |
| ResourceTable, |
| ExceptionTable, |
| CertificateTable, |
| BaseRelocationTable, |
| DebugTable, |
| Architecture, |
| GlobalPtr, |
| TlsTable, |
| LoadConfigTable, |
| BoundImportTable, |
| ImportAddressTable, |
| DelayImportDescriptor, |
| ClrRuntimeHeader, |
| } |
| |
| impl TryFrom<usize> for DataDirectoryType { |
| type Error = error::Error; |
| fn try_from(value: usize) -> Result<Self, Self::Error> { |
| Ok(match value { |
| 0 => Self::ExportTable, |
| 1 => Self::ImportTable, |
| 2 => Self::ResourceTable, |
| 3 => Self::ExceptionTable, |
| 4 => Self::CertificateTable, |
| 5 => Self::BaseRelocationTable, |
| 6 => Self::DebugTable, |
| 7 => Self::Architecture, |
| 8 => Self::GlobalPtr, |
| 9 => Self::TlsTable, |
| 10 => Self::LoadConfigTable, |
| 11 => Self::BoundImportTable, |
| 12 => Self::ImportAddressTable, |
| 13 => Self::DelayImportDescriptor, |
| 14 => Self::ClrRuntimeHeader, |
| _ => { |
| return Err(error::Error::Malformed( |
| "Wrong data directory index number".into(), |
| )) |
| } |
| }) |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Copy, Clone, Default)] |
| pub struct DataDirectories { |
| pub data_directories: [Option<(usize, DataDirectory)>; NUM_DATA_DIRECTORIES], |
| } |
| |
| impl ctx::TryIntoCtx<scroll::Endian> for DataDirectories { |
| type Error = error::Error; |
| |
| fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> { |
| let offset = &mut 0; |
| for opt_dd in self.data_directories { |
| if let Some((dd_offset, dd)) = opt_dd { |
| bytes.pwrite_with(dd, dd_offset, ctx)?; |
| *offset += dd_offset; |
| } else { |
| bytes.gwrite(&[0; SIZEOF_DATA_DIRECTORY][..], offset)?; |
| } |
| } |
| Ok(NUM_DATA_DIRECTORIES * SIZEOF_DATA_DIRECTORY) |
| } |
| } |
| |
| macro_rules! build_dd_getter { |
| ($dd_name:tt, $index:tt) => { |
| pub fn $dd_name(&self) -> Option<&DataDirectory> { |
| let idx = $index; |
| self.data_directories[idx].as_ref().map(|(_, dd)| dd) |
| } |
| }; |
| } |
| |
| impl DataDirectories { |
| pub fn parse(bytes: &[u8], count: usize, offset: &mut usize) -> error::Result<Self> { |
| let mut data_directories = [None; NUM_DATA_DIRECTORIES]; |
| if count > NUM_DATA_DIRECTORIES { |
| return Err(error::Error::Malformed(format!( |
| "data directory count ({}) is greater than maximum number of data directories ({})", |
| count, NUM_DATA_DIRECTORIES |
| ))); |
| } |
| for dir in data_directories.iter_mut().take(count) { |
| let dd = DataDirectory::parse(bytes, offset)?; |
| let dd = if dd.virtual_address == 0 && dd.size == 0 { |
| None |
| } else { |
| Some((*offset, dd)) |
| }; |
| *dir = dd; |
| } |
| Ok(DataDirectories { data_directories }) |
| } |
| |
| build_dd_getter!(get_export_table, 0); |
| build_dd_getter!(get_import_table, 1); |
| build_dd_getter!(get_resource_table, 2); |
| build_dd_getter!(get_exception_table, 3); |
| build_dd_getter!(get_certificate_table, 4); |
| build_dd_getter!(get_base_relocation_table, 5); |
| build_dd_getter!(get_debug_table, 6); |
| build_dd_getter!(get_architecture, 7); |
| build_dd_getter!(get_global_ptr, 8); |
| build_dd_getter!(get_tls_table, 9); |
| build_dd_getter!(get_load_config_table, 10); |
| build_dd_getter!(get_bound_import_table, 11); |
| build_dd_getter!(get_import_address_table, 12); |
| build_dd_getter!(get_delay_import_descriptor, 13); |
| build_dd_getter!(get_clr_runtime_header, 14); |
| |
| pub fn dirs(&self) -> impl Iterator<Item = (DataDirectoryType, DataDirectory)> { |
| self.data_directories |
| .into_iter() |
| .enumerate() |
| // (Index, Option<DD>) -> Option<(Index, DD)> -> (DDT, DD) |
| .filter_map(|(i, o)| |
| // We should not have invalid indexes. |
| // Indeed: `data_directories: &[_; N]` where N is the number |
| // of data directories. |
| // The `TryFrom` trait for integers to DataDirectoryType |
| // takes into account the N possible data directories. |
| // Therefore, the unwrap can never fail as long as Rust guarantees |
| // on types are honored. |
| o.map(|(_, v)| (i.try_into().unwrap(), v))) |
| } |
| } |