Skip to content
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

Added a feature to disable pe rva resolve for already mapped modules #188

Merged
merged 1 commit into from Jan 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 49 additions & 4 deletions src/pe/debug.rs
Expand Up @@ -2,6 +2,7 @@ use crate::error;
use scroll::{Pread, Pwrite, SizeWith};

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

Expand All @@ -17,11 +18,27 @@ impl<'a> DebugData<'a> {
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<Self> {
Self::parse_with_opts(
bytes,
dd,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}

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

Ok(DebugData {
image_debug_directory,
Expand Down Expand Up @@ -59,14 +76,31 @@ pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;

impl ImageDebugDirectory {
#[allow(unused)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i think it would just be better to delete this and the other unused private function, but not important

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was to keep the API compatible here (even if existing code calls parse on something like ImageDebugDirectory manually). Maybe a better idea would be to mark these functions as deprecated and remove them in a next major release.

fn parse(
bytes: &[u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<Self> {
Self::parse_with_opts(
bytes,
dd,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}

fn parse_with_opts(
bytes: &[u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<Self> {
let rva = dd.virtual_address as usize;
let offset = utils::find_offset(rva, sections, file_alignment).ok_or_else(|| {
let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
error::Error::Malformed(format!(
"Cannot map ImageDebugDirectory rva {:#x} into offset",
rva
Expand Down Expand Up @@ -94,14 +128,25 @@ pub struct CodeviewPDB70DebugInfo<'a> {

impl<'a> CodeviewPDB70DebugInfo<'a> {
pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
}

pub fn parse_with_opts(
bytes: &'a [u8],
idd: &ImageDebugDirectory,
opts: &options::ParseOptions,
) -> error::Result<Option<Self>> {
if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW {
// not a codeview debug directory
// that's not an error, but it's not a CodeviewPDB70DebugInfo either
return Ok(None);
}

// ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
let mut offset: usize = idd.pointer_to_raw_data as usize;
let mut offset: usize = match opts.resolve_rva {
true => idd.pointer_to_raw_data as usize,
false => idd.address_of_raw_data as usize,
};

// calculate how long the eventual filename will be, which doubles as a check of the record size
let filename_length = idd.size_of_data as isize - 24;
Expand Down
59 changes: 51 additions & 8 deletions src/pe/exception.rs
Expand Up @@ -49,6 +49,7 @@ use scroll::{self, Pread, Pwrite};
use crate::error;

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

Expand Down Expand Up @@ -666,6 +667,23 @@ impl<'a> ExceptionData<'a> {
directory: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<Self> {
Self::parse_with_opts(
bytes,
directory,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}

/// Parses exception data from the image at the given offset.
pub fn parse_with_opts(
bytes: &'a [u8],
directory: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<Self> {
let size = directory.size as usize;

Expand All @@ -677,7 +695,7 @@ impl<'a> ExceptionData<'a> {
}

let rva = directory.virtual_address as usize;
let offset = utils::find_offset(rva, sections, file_alignment).ok_or_else(|| {
let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
error::Error::Malformed(format!("cannot map exception_rva ({:#x}) into offset", rva))
})?;

Expand Down Expand Up @@ -761,31 +779,56 @@ impl<'a> ExceptionData<'a> {

/// Resolves unwind information for the given function entry.
pub fn get_unwind_info(
&self,
function: RuntimeFunction,
sections: &[section_table::SectionTable],
) -> error::Result<UnwindInfo<'a>> {
self.get_unwind_info_with_opts(function, sections, &options::ParseOptions::default())
}

/// Resolves unwind information for the given function entry.
pub fn get_unwind_info_with_opts(
&self,
mut function: RuntimeFunction,
sections: &[section_table::SectionTable],
opts: &options::ParseOptions,
) -> error::Result<UnwindInfo<'a>> {
while function.unwind_info_address % 2 != 0 {
let rva = (function.unwind_info_address & !1) as usize;
function = self.get_function_by_rva(rva, sections)?;
function = self.get_function_by_rva_with_opts(rva, sections, opts)?;
}

let rva = function.unwind_info_address as usize;
let offset = utils::find_offset(rva, sections, self.file_alignment).ok_or_else(|| {
error::Error::Malformed(format!("cannot map unwind rva ({:#x}) into offset", rva))
})?;
let offset =
utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
error::Error::Malformed(format!("cannot map unwind rva ({:#x}) into offset", rva))
})?;

UnwindInfo::parse(self.bytes, offset)
}

#[allow(dead_code)]
ko1N marked this conversation as resolved.
Show resolved Hide resolved
fn get_function_by_rva(
&self,
rva: usize,
sections: &[section_table::SectionTable],
) -> error::Result<RuntimeFunction> {
let offset = utils::find_offset(rva, sections, self.file_alignment).ok_or_else(|| {
error::Error::Malformed(format!("cannot map exception rva ({:#x}) into offset", rva))
})?;
self.get_function_by_rva_with_opts(rva, sections, &options::ParseOptions::default())
}

fn get_function_by_rva_with_opts(
&self,
rva: usize,
sections: &[section_table::SectionTable],
opts: &options::ParseOptions,
) -> error::Result<RuntimeFunction> {
let offset =
utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
error::Error::Malformed(format!(
"cannot map exception rva ({:#x}) into offset",
rva
))
})?;

self.get_function_by_offset(offset)
}
Expand Down
45 changes: 44 additions & 1 deletion src/pe/export.rs
Expand Up @@ -6,6 +6,7 @@ use log::debug;
use crate::error;

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

Expand Down Expand Up @@ -70,6 +71,22 @@ impl<'a> ExportData<'a> {
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<ExportData<'a>> {
Self::parse_with_opts(
bytes,
dd,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}

pub fn parse_with_opts(
bytes: &'a [u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<ExportData<'a>> {
let export_rva = dd.virtual_address as usize;
let size = dd.size as usize;
Expand All @@ -78,6 +95,7 @@ impl<'a> ExportData<'a> {
export_rva,
sections,
file_alignment,
opts,
&format!("cannot map export_rva ({:#x}) into offset", export_rva),
)?;
let export_directory_table =
Expand All @@ -94,6 +112,7 @@ impl<'a> ExportData<'a> {
export_directory_table.name_pointer_rva as usize,
sections,
file_alignment,
opts,
)
.map_or(vec![], |table_offset| {
let mut offset = table_offset;
Expand All @@ -114,6 +133,7 @@ impl<'a> ExportData<'a> {
export_directory_table.ordinal_table_rva as usize,
sections,
file_alignment,
opts,
)
.map_or(vec![], |table_offset| {
let mut offset = table_offset;
Expand All @@ -134,6 +154,7 @@ impl<'a> ExportData<'a> {
export_directory_table.export_address_table_rva as usize,
sections,
file_alignment,
opts,
)
.map_or(vec![], |table_offset| {
let mut offset = table_offset;
Expand All @@ -159,6 +180,7 @@ impl<'a> ExportData<'a> {
export_directory_table.name_rva as usize,
sections,
file_alignment,
opts,
)
.and_then(|offset| bytes.pread(offset).ok());

Expand Down Expand Up @@ -251,6 +273,7 @@ struct ExportCtx<'a> {
pub file_alignment: u32,
pub addresses: &'a ExportAddressTable,
pub ordinals: &'a ExportOrdinalTable,
pub opts: options::ParseOptions,
}

impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
Expand All @@ -265,11 +288,12 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
file_alignment,
addresses,
ordinals,
opts,
}: ExportCtx<'b>,
) -> Result<(Self, usize), Self::Error> {
use self::ExportAddressTableEntry::*;

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

if let Some(ordinal) = ordinals.get(idx) {
Expand All @@ -281,6 +305,7 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
rva,
sections,
file_alignment,
&opts,
&format!(
"cannot map RVA ({:#x}) of export ordinal {} into offset",
rva, ordinal
Expand All @@ -304,6 +329,7 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> {
rva,
sections,
file_alignment,
&opts,
&format!(
"cannot map RVA ({:#x}) of export ordinal {} into offset",
rva, ordinal
Expand Down Expand Up @@ -343,6 +369,22 @@ impl<'a> Export<'a> {
export_data: &ExportData,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<Vec<Export<'a>>> {
Self::parse_with_opts(
bytes,
export_data,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}

pub fn parse_with_opts(
bytes: &'a [u8],
export_data: &ExportData,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<Vec<Export<'a>>> {
let pointers = &export_data.export_name_pointer_table;
let addresses = &export_data.export_address_table;
Expand All @@ -359,6 +401,7 @@ impl<'a> Export<'a> {
file_alignment,
addresses,
ordinals,
opts: *opts,
},
) {
exports.push(export);
Expand Down