From dfed15dedf68dc61962ffeba37d47f63dffaddf6 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Thu, 30 Apr 2020 16:09:34 +1000 Subject: [PATCH] read/pe: add SectionTable Also fix section indices to be 1-based. --- src/read/coff/file.rs | 14 +++------ src/read/coff/section.rs | 64 ++++++++++++++++++++++++++++++++++++++++ src/read/pe/file.rs | 29 +++++++++--------- src/read/pe/mod.rs | 2 ++ src/read/pe/section.rs | 2 +- 5 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/read/coff/file.rs b/src/read/coff/file.rs index af35a37..18d0890 100644 --- a/src/read/coff/file.rs +++ b/src/read/coff/file.rs @@ -12,14 +12,14 @@ use crate::read::{ use super::{ parse_symbol, CoffSection, CoffSectionIterator, CoffSegment, CoffSegmentIterator, - CoffSymbolIterator, SymbolTable, + CoffSymbolIterator, SectionTable, SymbolTable, }; /// A COFF object file. #[derive(Debug)] pub struct CoffFile<'data> { pub(super) header: &'data pe::ImageFileHeader, - pub(super) sections: &'data [pe::ImageSectionHeader], + pub(super) sections: SectionTable<'data>, // TODO: ImageSymbolExBytes pub(super) symbols: SymbolTable<'data>, pub(super) data: Bytes<'data>, @@ -37,10 +37,7 @@ impl<'data> CoffFile<'data> { // Skip over the optional header and get the section headers. tail.skip(header.size_of_optional_header.get(LE) as usize) .read_error("Invalid COFF optional header size")?; - let sections = tail - .read_slice(header.number_of_sections.get(LE) as usize) - .read_error("Invalid COFF section headers")?; - + let sections = SectionTable::parse(header, tail)?; let symbols = SymbolTable::parse(header, data)?; // TODO: maybe validate that the machine is known? @@ -96,10 +93,7 @@ where } fn section_by_index(&'file self, index: SectionIndex) -> Result> { - let section = self - .sections - .get(index.0) - .read_error("Invalid COFF section index")?; + let section = self.sections.section(index.0)?; Ok(CoffSection { file: self, index, diff --git a/src/read/coff/section.rs b/src/read/coff/section.rs index d3f244b..27cf433 100644 --- a/src/read/coff/section.rs +++ b/src/read/coff/section.rs @@ -13,6 +13,70 @@ use crate::read::{ use super::{CoffFile, CoffRelocationIterator}; +/// The table of section headers in a COFF or PE file. +#[derive(Debug, Default, Clone, Copy)] +pub struct SectionTable<'data> { + sections: &'data [pe::ImageSectionHeader], +} + +impl<'data> SectionTable<'data> { + /// Parse the section table. + /// + /// `data` must be the data following the optional header. + pub fn parse(header: &pe::ImageFileHeader, mut data: Bytes<'data>) -> Result { + let sections = data + .read_slice(header.number_of_sections.get(LE) as usize) + .read_error("Invalid COFF/PE section headers")?; + Ok(SectionTable { sections }) + } + + /// Iterate over the section headers. + /// + /// Warning: sections indices start at 1. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> { + self.sections.iter() + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Return the section header at the given index. + /// + /// The index is 1-based. + pub fn section(&self, index: usize) -> read::Result<&'data pe::ImageSectionHeader> { + self.sections + .get(index.wrapping_sub(1)) + .read_error("Invalid COFF/PE section index") + } + + /// Return the section header with the given name. + /// + /// The returned index is 1-based. + /// + /// Ignores sections with invalid names. + pub fn section_by_name( + &self, + strings: StringTable<'data>, + name: &[u8], + ) -> Option<(usize, &'data pe::ImageSectionHeader)> { + self.sections + .iter() + .enumerate() + .find(|(_, section)| section.name(strings) == Ok(name)) + .map(|(index, section)| (index + 1, section)) + } +} + /// An iterator over the loadable sections of a `CoffFile`. #[derive(Debug)] pub struct CoffSegmentIterator<'data, 'file> diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 2f832c2..b8c2285 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -8,11 +8,10 @@ use crate::pe; use crate::pod::{Bytes, Pod}; use crate::read::coff::{parse_symbol, CoffSymbolIterator, SymbolTable}; use crate::read::{ - self, Error, FileFlags, Object, ObjectSection, ReadError, Result, SectionIndex, Symbol, - SymbolIndex, SymbolMap, + self, Error, FileFlags, Object, ReadError, Result, SectionIndex, Symbol, SymbolIndex, SymbolMap, }; -use super::{PeSection, PeSectionIterator, PeSegment, PeSegmentIterator}; +use super::{PeSection, PeSectionIterator, PeSegment, PeSegmentIterator, SectionTable}; /// A PE32 (32-bit) image file. pub type PeFile32<'data> = PeFile<'data, pe::ImageNtHeaders32>; @@ -25,7 +24,7 @@ pub struct PeFile<'data, Pe: ImageNtHeaders> { pub(super) dos_header: &'data pe::ImageDosHeader, pub(super) nt_headers: &'data Pe, pub(super) data_directories: &'data [pe::ImageDataDirectory], - pub(super) sections: &'data [pe::ImageSectionHeader], + pub(super) sections: SectionTable<'data>, pub(super) symbols: SymbolTable<'data>, pub(super) data: Bytes<'data>, } @@ -91,11 +90,9 @@ impl<'data, Pe: ImageNtHeaders> PeFile<'data, Pe> { .read_error("Invalid PE number of RVA and sizes")?; // Section headers are after the optional header. - let sections = nt_tail - .read_slice(nt_headers.file_header().number_of_sections.get(LE) as usize) - .read_error("Invalid PE section headers")?; - - let symbols = SymbolTable::parse(&nt_headers.file_header(), data)?; + let sections = SectionTable::parse(nt_headers.file_header(), nt_tail)?; + // Symbols are at an offset specified in the file header. + let symbols = SymbolTable::parse(nt_headers.file_header(), data)?; Ok(PeFile { dos_header, @@ -153,15 +150,17 @@ where } fn section_by_name(&'file self, section_name: &str) -> Option> { - self.sections() - .find(|section| section.name() == Ok(section_name)) + self.sections + .section_by_name(self.symbols.strings, section_name.as_bytes()) + .map(|(index, section)| PeSection { + file: self, + index: SectionIndex(index), + section, + }) } fn section_by_index(&'file self, index: SectionIndex) -> Result> { - let section = self - .sections - .get(index.0) - .read_error("Invalid PE section index")?; + let section = self.sections.section(index.0)?; Ok(PeSection { file: self, index, diff --git a/src/read/pe/mod.rs b/src/read/pe/mod.rs index 106fc2c..71ff6f4 100644 --- a/src/read/pe/mod.rs +++ b/src/read/pe/mod.rs @@ -12,3 +12,5 @@ pub use file::*; mod section; pub use section::*; + +pub use super::coff::SectionTable; diff --git a/src/read/pe/section.rs b/src/read/pe/section.rs index 4d6681e..6b06d4d 100644 --- a/src/read/pe/section.rs +++ b/src/read/pe/section.rs @@ -134,7 +134,7 @@ impl<'data, 'file, Pe: ImageNtHeaders> Iterator for PeSectionIterator<'data, 'fi fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| PeSection { file: self.file, - index: SectionIndex(index), + index: SectionIndex(index + 1), section, }) }