Skip to content

Commit

Permalink
calculate number of bytes for sections
Browse files Browse the repository at this point in the history
  • Loading branch information
tathanhdinh committed Aug 23, 2018
1 parent 37ce0cf commit 4a9f2ca
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 41 deletions.
8 changes: 4 additions & 4 deletions src/pe/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pub struct DebugData<'a> {
}

impl<'a> DebugData<'a> {
pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<Self> {
let image_debug_directory = ImageDebugDirectory::parse(bytes, dd, sections)?;
pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<Self> {
let image_debug_directory = ImageDebugDirectory::parse(bytes, dd, sections, file_alignment)?;
let codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse(bytes, &image_debug_directory)?;

Ok(DebugData{
Expand Down Expand Up @@ -54,9 +54,9 @@ pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;

impl ImageDebugDirectory {
fn parse(bytes: &[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<Self> {
fn parse(bytes: &[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<Self> {
let rva = dd.virtual_address as usize;
let offset = utils::find_offset(rva, sections).ok_or(error::Error::Malformed(format!("Cannot map ImageDebugDirectory rva {:#x} into offset", rva)))?;;
let offset = utils::find_offset(rva, sections, file_alignment).ok_or(error::Error::Malformed(format!("Cannot map ImageDebugDirectory rva {:#x} into offset", rva)))?;;
let idd: Self = bytes.pread_with(offset, scroll::LE)?;
Ok (idd)
}
Expand Down
25 changes: 13 additions & 12 deletions src/pe/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ pub struct ExportData<'a> {
}

impl<'a> ExportData<'a> {
pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<ExportData<'a>> {
pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ExportData<'a>> {
let export_rva = dd.virtual_address as usize;
let size = dd.size as usize;
debug!("export_rva {:#x} size {:#}", export_rva, size);
let export_offset = utils::find_offset_or(export_rva, sections, &format!("cannot map export_rva ({:#x}) into offset", export_rva))?;
let export_offset = utils::find_offset_or(export_rva, sections, file_alignment, &format!("cannot map export_rva ({:#x}) into offset", export_rva))?;
let export_directory_table = ExportDirectoryTable::parse(bytes, export_offset)
.map_err(|_| error::Error::Malformed(format!("cannot parse export_directory_table (offset {:#x})", export_offset)))?;
let number_of_name_pointers = export_directory_table.number_of_name_pointers as usize;
let address_table_entries = export_directory_table.address_table_entries as usize;

let export_name_pointer_table = utils::find_offset(export_directory_table.name_pointer_rva as usize, sections).map_or(vec![], |table_offset| {
let export_name_pointer_table = utils::find_offset(export_directory_table.name_pointer_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| {
let mut offset = table_offset;
let mut table: ExportNamePointerTable = Vec::with_capacity(number_of_name_pointers);

Expand All @@ -89,7 +89,7 @@ impl<'a> ExportData<'a> {
table
});

let export_ordinal_table = utils::find_offset(export_directory_table.ordinal_table_rva as usize, sections).map_or(vec![], |table_offset| {
let export_ordinal_table = utils::find_offset(export_directory_table.ordinal_table_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| {
let mut offset = table_offset;
let mut table: ExportOrdinalTable = Vec::with_capacity(number_of_name_pointers);

Expand All @@ -104,7 +104,7 @@ impl<'a> ExportData<'a> {
table
});

let export_address_table = utils::find_offset(export_directory_table.export_address_table_rva as usize, sections).map_or(vec![], |table_offset| {
let export_address_table = utils::find_offset(export_directory_table.export_address_table_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| {
let mut offset = table_offset;
let mut table: ExportAddressTable = Vec::with_capacity(address_table_entries);
let export_end = export_rva + size;
Expand All @@ -124,7 +124,7 @@ impl<'a> ExportData<'a> {
table
});

let name = utils::find_offset(export_directory_table.name_rva as usize, sections).and_then(|offset| bytes.pread(offset).ok());
let name = utils::find_offset(export_directory_table.name_rva as usize, sections, file_alignment).and_then(|offset| bytes.pread(offset).ok());

Ok(ExportData {
name: name,
Expand Down Expand Up @@ -209,6 +209,7 @@ struct ExportCtx<'a> {
pub ptr: u32,
pub idx: usize,
pub sections: &'a [section_table::SectionTable],
pub file_alignment: u32,
pub addresses: &'a ExportAddressTable,
pub ordinals: &'a ExportOrdinalTable,
}
Expand All @@ -217,23 +218,23 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(bytes: &'a [u8], ExportCtx { ptr, idx, sections, addresses, ordinals }: ExportCtx<'b>) -> Result<(Self, Self::Size), Self::Error> {
fn try_from_ctx(bytes: &'a [u8], ExportCtx { ptr, idx, sections, file_alignment, addresses, ordinals }: ExportCtx<'b>) -> Result<(Self, Self::Size), Self::Error> {
use self::ExportAddressTableEntry::*;

let name = utils::find_offset(ptr as usize, sections).map_or(None, |offset| bytes.pread::<&str>(offset).ok());
let name = utils::find_offset(ptr as usize, sections, file_alignment).map_or(None, |offset| bytes.pread::<&str>(offset).ok());

if let Some(ordinal) = ordinals.get(idx) {
if let Some(rva) = addresses.get(*ordinal as usize) {
match *rva {
ExportRVA(rva) => {
let rva = rva as usize;
let offset = utils::find_offset_or(rva, sections, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?;
let offset = utils::find_offset_or(rva, sections, file_alignment, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?;
Ok((Export { name, offset, rva, reexport: None, size: 0 }, 0))
},

ForwarderRVA(rva) => {
let rva = rva as usize;
let offset = utils::find_offset_or(rva, sections, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?;
let offset = utils::find_offset_or(rva, sections, file_alignment, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?;
let reexport = Reexport::parse(bytes, offset)?;
Ok((Export { name, offset, rva, reexport: Some(reexport), size: 0 }, 0))
}
Expand All @@ -248,14 +249,14 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
}

impl<'a> Export<'a> {
pub fn parse(bytes: &'a [u8], export_data: &ExportData, sections: &[section_table::SectionTable]) -> error::Result<Vec<Export<'a>>> {
pub fn parse(bytes: &'a [u8], export_data: &ExportData, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<Vec<Export<'a>>> {
let pointers = &export_data.export_name_pointer_table;
let addresses = &export_data.export_address_table;
let ordinals = &export_data.export_ordinal_table;

let mut exports = Vec::with_capacity(pointers.len());
for (idx, &ptr) in pointers.iter().enumerate() {
if let Ok(export) = bytes.pread_with(0, ExportCtx { ptr, idx, sections, addresses, ordinals }) {
if let Ok(export) = bytes.pread_with(0, ExportCtx { ptr, idx, sections, file_alignment, addresses, ordinals }) {
exports.push(export);
}
}
Expand Down
21 changes: 10 additions & 11 deletions src/pe/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ pub enum SyntheticImportLookupTableEntry<'a> {
pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;

impl<'a> SyntheticImportLookupTableEntry<'a> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable])
-> error::Result<ImportLookupTable<'a>> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportLookupTable<'a>> {
let le = scroll::LE;
let offset = &mut offset;
let mut table = Vec::new();
Expand All @@ -86,7 +85,7 @@ impl<'a> SyntheticImportLookupTableEntry<'a> {
let rva = bitfield.to_rva();
let hentry = {
debug!("searching for RVA {:#x}", rva);
if let Some(offset) = utils::find_offset(rva as usize, sections) {
if let Some(offset) = utils::find_offset(rva as usize, sections, file_alignment) {
debug!("offset {:#x}", offset);
HintNameTableEntry::parse(bytes, offset)?
} else {
Expand Down Expand Up @@ -142,23 +141,23 @@ pub struct SyntheticImportDirectoryEntry<'a> {
}

impl<'a> SyntheticImportDirectoryEntry<'a> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable]) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
const LE: scroll::Endian = scroll::LE;
let name_rva = import_directory_entry.name_rva;
let name = utils::try_name(bytes, name_rva as usize, sections)?;
let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment)?;
let import_lookup_table = {
let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections) {
let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections)?;
if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections, file_alignment) {
let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections, file_alignment)?;
debug!("Successfully synthesized import lookup table entry: {:#?}", import_lookup_table);
Some(import_lookup_table)
} else {
None
}
};

let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections).ok_or(error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?;
let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections, file_alignment).ok_or(error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?;
let mut import_address_table = Vec::new();
loop {
let import_address = bytes.gread_with::<T>(import_address_table_offset, LE)?.into();
Expand All @@ -180,10 +179,10 @@ pub struct ImportData<'a> {
}

impl<'a> ImportData<'a> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<ImportData<'a>> {
pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportData<'a>> {
let import_directory_table_rva = dd.virtual_address as usize;
debug!("import_directory_table_rva {:#x}", import_directory_table_rva);
let offset = &mut utils::find_offset(import_directory_table_rva, sections).ok_or(error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;;
let offset = &mut utils::find_offset(import_directory_table_rva, sections, file_alignment).ok_or(error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;;
debug!("import data offset {:#x}", offset);
let mut import_data = Vec::new();
loop {
Expand All @@ -192,7 +191,7 @@ impl<'a> ImportData<'a> {
if import_directory_entry.is_null() {
break;
} else {
let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections)?;
let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections, file_alignment)?;
debug!("entry {:#?}", entry);
import_data.push(entry);
}
Expand Down
11 changes: 6 additions & 5 deletions src/pe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,11 @@ impl<'a> PE<'a> {
image_base = optional_header.windows_fields.image_base as usize;
is_64 = optional_header.container()? == container::Container::Big;
debug!("entry {:#x} image_base {:#x} is_64: {}", entry, image_base, is_64);
let file_alignment = optional_header.windows_fields.file_alignment;
if let &Some(export_table) = optional_header.data_directories.get_export_table() {
if let Ok(ed) = export::ExportData::parse(bytes, &export_table, &sections) {
if let Ok(ed) = export::ExportData::parse(bytes, &export_table, &sections, file_alignment) {
debug!("export data {:#?}", ed);
exports = export::Export::parse(bytes, &ed, &sections)?;
exports = export::Export::parse(bytes, &ed, &sections, file_alignment)?;
name = ed.name;
debug!("name: {:#?}", name);
export_data = Some(ed);
Expand All @@ -92,9 +93,9 @@ impl<'a> PE<'a> {
debug!("exports: {:#?}", exports);
if let &Some(import_table) = optional_header.data_directories.get_import_table() {
let id = if is_64 {
import::ImportData::parse::<u64>(bytes, &import_table, &sections)?
import::ImportData::parse::<u64>(bytes, &import_table, &sections, file_alignment)?
} else {
import::ImportData::parse::<u32>(bytes, &import_table, &sections)?
import::ImportData::parse::<u32>(bytes, &import_table, &sections, file_alignment)?
};
debug!("import data {:#?}", id);
if is_64 {
Expand All @@ -109,7 +110,7 @@ impl<'a> PE<'a> {
}
debug!("imports: {:#?}", imports);
if let &Some(debug_table) = optional_header.data_directories.get_debug_table() {
debug_data = Some(debug::DebugData::parse(bytes, &debug_table, &sections)?);
debug_data = Some(debug::DebugData::parse(bytes, &debug_table, &sections, file_alignment)?);
}
}
Ok( PE {
Expand Down
50 changes: 41 additions & 9 deletions src/pe/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,54 @@ use error;

use super::section_table;

use std::cmp;

pub fn is_in_range (rva: usize, r1: usize, r2: usize) -> bool {
r1 <= rva && rva < r2
}

// reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
#[inline]
fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
const PHYSICAL_ALIGN: usize = 0x1ff;
pointer_to_raw_data & !PHYSICAL_ALIGN
}

#[inline]
fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
fn round_size(size: usize) -> usize {
const PAGE_MASK: usize = 0xfff;
(size + PAGE_MASK) & !PAGE_MASK
}

let file_alignment = file_alignment as usize;
let size_of_raw_data = section.size_of_raw_data as usize;
let virtual_size = section.virtual_size as usize;
let read_size = {
let read_size = (section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1) & !(file_alignment - 1);
cmp::min(read_size, round_size(size_of_raw_data))
};

if virtual_size == 0 {
read_size
} else {
cmp::min(read_size, round_size(virtual_size))
}
}

fn rva2offset (rva: usize, section: &section_table::SectionTable) -> usize {
(rva - section.virtual_address as usize) + section.pointer_to_raw_data as usize
(rva - section.virtual_address as usize) + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
}

fn is_in_section (rva: usize, section: &section_table::SectionTable) -> bool {
section.virtual_address as usize <= rva && rva < (section.virtual_address + section.virtual_size) as usize
fn is_in_section (rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
let section_rva = section.virtual_address as usize;
is_in_range(rva, section_rva, section_rva + section_read_size(section, file_alignment))
}

pub fn find_offset (rva: usize, sections: &[section_table::SectionTable]) -> Option<usize> {
pub fn find_offset (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> Option<usize> {
for (i, section) in sections.iter().enumerate() {
debug!("Checking {} for {:#x} ∈ {:#x}..{:#x}", section.name().unwrap_or(""), rva, section.virtual_address, section.virtual_address + section.virtual_size);
if is_in_section(rva, &section) {
if is_in_section(rva, &section, file_alignment) {
let offset = rva2offset(rva, &section);
debug!("Found in section {}({}), remapped into offset {:#x}", section.name().unwrap_or(""), i, offset);
return Some(offset)
Expand All @@ -28,12 +60,12 @@ pub fn find_offset (rva: usize, sections: &[section_table::SectionTable]) -> Opt
None
}

pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], msg: &str) -> error::Result<usize> {
find_offset(rva, sections).ok_or(error::Error::Malformed(msg.to_string()))
pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, msg: &str) -> error::Result<usize> {
find_offset(rva, sections, file_alignment).ok_or(error::Error::Malformed(msg.to_string()))
}

pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable]) -> error::Result<&'a str> {
match find_offset(rva, sections) {
pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<&'a str> {
match find_offset(rva, sections, file_alignment) {
Some(offset) => {
Ok(bytes.pread::<&str>(offset)?)
},
Expand Down

0 comments on commit 4a9f2ca

Please sign in to comment.