-
Notifications
You must be signed in to change notification settings - Fork 195
PE: preparations for a writer outside of Goblin #389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3d1d725
a690d07
1e34fbe
9372220
e6d2fdc
677f9ac
34f7f7f
335f261
7d7d1c8
dbdcff1
51ba0ed
d814cbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,25 @@ impl DataDirectory { | |
| pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> { | ||
| Ok(bytes.gread_with(offset, scroll::LE)?) | ||
| } | ||
|
|
||
| /// Given a view of the PE binary, represented by `bytes` and the on-disk offset `disk_offset` | ||
| /// this will return a view of the data directory's contents. | ||
| /// If the range are out of bands, this will fail with a [`error::Error::Malformed`] error. | ||
| pub fn data<'a>(&self, bytes: &'a [u8], disk_offset: usize) -> error::Result<&'a [u8]> { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should have just returned an |
||
| let disk_end = disk_offset.saturating_add(self.size.try_into().map_err(|_| { | ||
| error::Error::Malformed(format!("Data directory size cannot fit in platform `usize")) | ||
| })?); | ||
|
|
||
| bytes | ||
|
RaitoBezarius marked this conversation as resolved.
|
||
| .get(disk_offset..disk_end) | ||
| .ok_or(error::Error::Malformed(format!( | ||
| "Requesting bytes from data directory at {} (end: {}) of size {}, buffer is {}", | ||
| disk_offset, | ||
| disk_end, | ||
| self.size, | ||
| bytes.len() | ||
| ))) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, PartialEq, Copy, Clone)] | ||
|
|
@@ -37,6 +56,7 @@ pub enum DataDirectoryType { | |
| ImportAddressTable, | ||
| DelayImportDescriptor, | ||
| ClrRuntimeHeader, | ||
| Reserved, | ||
| } | ||
|
|
||
| impl TryFrom<usize> for DataDirectoryType { | ||
|
|
@@ -58,6 +78,7 @@ impl TryFrom<usize> for DataDirectoryType { | |
| 12 => Self::ImportAddressTable, | ||
| 13 => Self::DelayImportDescriptor, | ||
| 14 => Self::ClrRuntimeHeader, | ||
| 15 => Self::Reserved, | ||
| _ => { | ||
| return Err(error::Error::Malformed( | ||
| "Wrong data directory index number".into(), | ||
|
|
@@ -78,9 +99,8 @@ impl ctx::TryIntoCtx<scroll::Endian> for DataDirectories { | |
| 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; | ||
| if let Some((_, dd)) = opt_dd { | ||
| bytes.gwrite_with(dd, offset, ctx)?; | ||
| } else { | ||
| bytes.gwrite(&[0; SIZEOF_DATA_DIRECTORY][..], offset)?; | ||
| } | ||
|
|
@@ -136,6 +156,14 @@ impl DataDirectories { | |
| build_dd_getter!(get_clr_runtime_header, 14); | ||
|
|
||
| pub fn dirs(&self) -> impl Iterator<Item = (DataDirectoryType, DataDirectory)> { | ||
| self.dirs_with_offset().map(|(a, _b, c)| (a, c)) | ||
| } | ||
|
|
||
| /// Returns all data directories | ||
| /// with their types, offsets and contents. | ||
| pub fn dirs_with_offset( | ||
| &self, | ||
| ) -> impl Iterator<Item = (DataDirectoryType, usize, DataDirectory)> { | ||
| self.data_directories | ||
| .into_iter() | ||
| .enumerate() | ||
|
|
@@ -148,6 +176,6 @@ impl DataDirectories { | |
| // 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))) | ||
| o.map(|(offset, v)| (i.try_into().unwrap(), offset, v))) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,9 @@ use alloc::string::{String, ToString}; | |
| use alloc::vec::Vec; | ||
| use scroll::{ctx, Pread, Pwrite}; | ||
|
|
||
| use super::utils::align_to; | ||
| use super::PE; | ||
|
|
||
| #[repr(C)] | ||
| #[derive(Debug, PartialEq, Clone, Default)] | ||
| pub struct SectionTable { | ||
|
|
@@ -55,6 +58,60 @@ fn base64_decode_string_entry(s: &str) -> Result<usize, ()> { | |
| } | ||
|
|
||
| impl SectionTable { | ||
| /// Given a view of the PE binary and minimal section information, | ||
| /// this will return a [`SectionTable`] filled with a virtual address | ||
| /// automatically based on the last section offset present in the PE. | ||
| /// | ||
| /// Most of the fields will be zeroed when they require a full vision of the PE | ||
| /// to be derived. | ||
| /// | ||
| /// Caller is responsible to fill on-disk file offsets and various pointers. | ||
| pub fn new( | ||
|
RaitoBezarius marked this conversation as resolved.
|
||
| pe: &PE, | ||
| name: &[u8; 8], | ||
| contents: &[u8], | ||
| characteristics: u32, | ||
| section_alignment: u32, | ||
| ) -> error::Result<Self> { | ||
| let mut table = SectionTable::default(); | ||
| // VA is needed only if characteristics is | ||
| // execute | read | write. | ||
| let need_virtual_address = true; | ||
|
|
||
| table.name = *name; | ||
| table.size_of_raw_data = contents.len().try_into()?; | ||
| table.characteristics = characteristics; | ||
|
|
||
| // Filling this data requires a complete overview | ||
| // of the final PE which may involve rewriting | ||
| // the complete PE. | ||
| table.pointer_to_raw_data = 0; | ||
| table.pointer_to_relocations = 0; | ||
|
|
||
| table.pointer_to_linenumbers = 0; | ||
| table.number_of_linenumbers = 0; | ||
| table.pointer_to_relocations = 0; | ||
|
|
||
| if need_virtual_address { | ||
| table.virtual_size = contents.len().try_into()?; | ||
| let mut sections = pe.sections.clone(); | ||
| sections.sort_by_key(|sect| sect.virtual_address); | ||
| // Base VA = 0 ? | ||
| let last_section_offset = sections | ||
| .last() | ||
| .map(|last_section| last_section.virtual_address + last_section.virtual_size) | ||
| .ok_or(0u32) | ||
| .unwrap(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this unwrap infallible? |
||
|
|
||
| table.virtual_address = align_to(last_section_offset, section_alignment); | ||
| } else { | ||
| table.virtual_size = 0; | ||
| table.virtual_address = 0; | ||
| } | ||
|
|
||
| Ok(table) | ||
| } | ||
|
|
||
| pub fn parse( | ||
| bytes: &[u8], | ||
| offset: &mut usize, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.