use core::cmp::max;
use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;
use log::warn;
use resource::ResourceData;
pub mod authenticode;
pub mod certificate_table;
pub mod characteristic;
pub mod clr;
pub mod data_directories;
pub mod debug;
pub mod dll_characteristic;
pub mod exception;
pub mod export;
pub mod header;
pub mod import;
pub mod load_config;
pub mod optional_header;
pub mod options;
pub mod relocation;
pub mod resource;
pub mod section_table;
pub mod subsystem;
pub mod symbol;
pub mod tls;
pub mod utils;
use crate::container;
use crate::error;
use crate::options::Permissive;
use crate::pe::utils::pad;
use crate::strtab;
use options::ParseMode;
use scroll::{Pwrite, ctx};
use log::debug;
#[derive(Debug)]
pub struct PE<'a> {
bytes: &'a [u8],
authenticode_excluded_sections: Option<authenticode::ExcludedSections>,
pub header: header::Header<'a>,
pub sections: Vec<section_table::SectionTable>,
pub size: usize,
pub name: Option<&'a str>,
pub is_lib: bool,
pub is_64: bool,
pub entry: u32,
pub image_base: u64,
pub export_data: Option<export::ExportData<'a>>,
pub import_data: Option<import::ImportData<'a>>,
pub exports: Vec<export::Export<'a>>,
pub imports: Vec<import::Import<'a>>,
pub libraries: Vec<&'a str>,
pub debug_data: Option<debug::DebugData<'a>>,
pub tls_data: Option<tls::TlsData<'a>>,
pub exception_data: Option<exception::ExceptionData<'a>>,
pub relocation_data: Option<relocation::RelocationData<'a>>,
pub load_config_data: Option<load_config::LoadConfigData>,
pub certificates: certificate_table::CertificateDirectoryTable<'a>,
pub resource_data: Option<ResourceData<'a>>,
pub clr_data: Option<clr::ClrData<'a>>,
}
impl<'a> PE<'a> {
pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
Self::parse_with_opts(bytes, &options::ParseOptions::default())
}
pub fn parse_with_opts(bytes: &'a [u8], opts: &options::ParseOptions) -> error::Result<Self> {
let header = header::Header::parse_with_opts(bytes, opts)?;
let mut authenticode_excluded_sections = None;
debug!("{:#?}", header);
let optional_header_offset = header.dos_header.pe_pointer as usize
+ header::SIZEOF_PE_MAGIC
+ header::SIZEOF_COFF_HEADER;
let offset =
&mut (optional_header_offset + header.coff_header.size_of_optional_header as usize);
let sections = header.coff_header.sections_with_opts(bytes, offset, opts)?;
let is_lib = characteristic::is_dll(header.coff_header.characteristics);
let mut entry = 0;
let mut image_base = 0;
let mut exports = vec![];
let mut export_data = None;
let mut name = None;
let mut imports = vec![];
let mut import_data = None;
let mut libraries = vec![];
let mut debug_data = None;
let mut tls_data = None;
let mut exception_data = None;
let mut relocation_data = None;
let mut load_config_data = None;
let mut certificates = Default::default();
let mut resource_data = Default::default();
let mut clr_data = Default::default();
let mut is_64 = false;
if let Some(optional_header) = header.optional_header {
let (checksum, datadir_entry_certtable) = match optional_header.standard_fields.magic {
optional_header::MAGIC_32 => {
let standard_field_offset =
optional_header_offset + optional_header::SIZEOF_STANDARD_FIELDS_32;
let checksum_field_offset =
standard_field_offset + optional_header::OFFSET_WINDOWS_FIELDS_32_CHECKSUM;
(
checksum_field_offset..checksum_field_offset + 4,
optional_header_offset + 128..optional_header_offset + 136,
)
}
optional_header::MAGIC_64 => {
let standard_field_offset =
optional_header_offset + optional_header::SIZEOF_STANDARD_FIELDS_64;
let checksum_field_offset =
standard_field_offset + optional_header::OFFSET_WINDOWS_FIELDS_64_CHECKSUM;
(
checksum_field_offset..checksum_field_offset + 4,
optional_header_offset + 144..optional_header_offset + 152,
)
}
magic => {
return Err(error::Error::Malformed(format!(
"Unsupported header magic ({:#x})",
magic
)));
}
};
entry = optional_header.standard_fields.address_of_entry_point;
image_base = optional_header.windows_fields.image_base;
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_with_opts(
bytes,
export_table,
§ions,
file_alignment,
opts,
) {
debug!("export data {:#?}", ed);
exports = export::Export::parse_with_opts(
bytes,
&ed,
§ions,
file_alignment,
opts,
)?;
name = ed.name;
debug!("name: {:#?}", name);
export_data = Some(ed);
}
}
debug!("exports: {:#?}", exports);
if let Some(&import_table) = optional_header.data_directories.get_import_table() {
let id = if is_64 {
import::ImportData::parse_with_opts::<u64>(
bytes,
import_table,
§ions,
file_alignment,
opts,
)?
} else {
import::ImportData::parse_with_opts::<u32>(
bytes,
import_table,
§ions,
file_alignment,
opts,
)?
};
debug!("import data {:#?}", id);
if is_64 {
imports = import::Import::parse::<u64>(bytes, &id, §ions)?
} else {
imports = import::Import::parse::<u32>(bytes, &id, §ions)?
}
libraries = id
.import_data
.iter()
.map(|data| data.name)
.collect::<Vec<&'a str>>();
libraries.sort();
libraries.dedup();
import_data = Some(id);
}
debug!("imports: {:#?}", imports);
if let Some(&debug_table) = optional_header.data_directories.get_debug_table() {
debug_data = debug::DebugData::parse_with_opts(
bytes,
debug_table,
§ions,
file_alignment,
opts,
)
.map(Some)
.or_permissive_and_default(
opts.parse_mode.is_permissive(),
"Failed to parse resource data",
)?;
}
if let Some(tls_table) = optional_header.data_directories.get_tls_table() {
tls_data = tls::TlsData::parse_with_opts(
bytes,
image_base,
tls_table,
§ions,
file_alignment,
opts,
is_64,
) .or_else(|e| {
opts.parse_mode
.is_permissive()
.then_some(None) .ok_or(e) })?;
debug!("tls data: {:#?}", tls_data);
}
if header.coff_header.machine == header::COFF_MACHINE_X86_64 {
debug!("exception data: {:#?}", exception_data);
if let Some(&exception_table) =
optional_header.data_directories.get_exception_table()
{
exception_data = exception::ExceptionData::parse_with_opts(
bytes,
exception_table,
§ions,
file_alignment,
opts,
)
.map(Some)
.or_permissive_and_default(
opts.parse_mode.is_permissive(),
"Failed to parse security data",
)?;
}
}
if let Some(&baserelocs_dir) =
optional_header.data_directories.get_base_relocation_table()
{
relocation_data = relocation::RelocationData::parse_with_opts(
bytes,
baserelocs_dir,
§ions,
file_alignment,
opts,
)
.map(Some)
.or_permissive_and_default(
opts.parse_mode.is_permissive(),
"Failed to parse base relocation data",
)?;
}
if let Some(&load_config_dir) = optional_header.data_directories.get_load_config_table()
{
debug!(
"LoadConfig directory found: virtual_address={:#x}, size={:#x}",
load_config_dir.virtual_address, load_config_dir.size
);
load_config_data = load_config::LoadConfigData::parse_with_opts(
bytes,
load_config_dir,
§ions,
file_alignment,
opts,
is_64,
)
.map(Some)
.or_permissive_and_default(
opts.parse_mode.is_permissive(),
"Failed to parse load config data",
)?;
}
if let Some(com_descriptor) = optional_header.data_directories.get_clr_runtime_header()
{
clr_data = clr::ClrData::parse_with_opts(
bytes,
&com_descriptor,
§ions,
file_alignment,
opts,
)
.map(Some)
.or_permissive_and_default(
opts.parse_mode.is_permissive(),
"Failed to parse CLR runtime data",
)?;
}
let certificate_table_size = if opts.parse_attribute_certificates {
if let Some(&certificate_table) =
optional_header.data_directories.get_certificate_table()
{
let certificates_result = certificate_table::enumerate_certificates(
bytes,
certificate_table.virtual_address,
certificate_table.size,
);
certificates = match opts.parse_mode {
ParseMode::Strict => certificates_result?,
ParseMode::Permissive => certificates_result.unwrap_or_else(|err| {
warn!("Cannot parse CertificateTable: {:?}", err);
Default::default()
}),
};
certificate_table.size as usize
} else {
0
}
} else {
0
};
if let Some(&resource_table) = optional_header.data_directories.get_resource_table() {
let data = resource::ResourceData::parse_with_opts(
bytes,
resource_table,
§ions,
file_alignment,
opts,
)?;
resource_data = Some(data);
debug!("resource_data data: {:#?}", data.version_info);
}
authenticode_excluded_sections = Some(authenticode::ExcludedSections::new(
checksum,
datadir_entry_certtable,
certificate_table_size,
optional_header.windows_fields.size_of_headers as usize,
));
}
Ok(PE {
bytes,
authenticode_excluded_sections,
header,
sections,
size: 0,
name,
is_lib,
is_64,
entry,
image_base,
export_data,
import_data,
exports,
imports,
libraries,
debug_data,
tls_data,
exception_data,
relocation_data,
load_config_data,
certificates,
resource_data,
clr_data,
})
}
pub fn write_sections(
&self,
bytes: &mut [u8],
offset: &mut usize,
file_alignment: Option<usize>,
ctx: scroll::Endian,
) -> Result<usize, error::Error> {
debug_assert!(
self.sections
.iter()
.flat_map(|section_a| {
self.sections
.iter()
.map(move |section_b| (section_a, section_b))
})
.all(|(section_i, section_j)| section_i == section_j
|| !section_i.overlaps_with(section_j)),
"Overlapping sections were found, this is not supported."
);
for section in &self.sections {
let section_data = section.data(&self.bytes)?.ok_or_else(|| {
error::Error::Malformed(format!(
"Section data `{}` is malformed",
section.name().unwrap_or("unknown name")
))
})?;
let file_section_offset =
usize::try_from(section.pointer_to_raw_data).map_err(|_| {
error::Error::Malformed(format!(
"Section `{}`'s pointer to raw data does not fit in platform `usize`",
section.name().unwrap_or("unknown name")
))
})?;
let vsize: usize = section.virtual_size.try_into()?;
let ondisk_size: usize = section.size_of_raw_data.try_into()?;
let section_name = String::from(section.name().unwrap_or("unknown name"));
let mut file_offset = file_section_offset;
match section_data {
Cow::Borrowed(borrowed) => bytes.gwrite(borrowed, &mut file_offset)?,
Cow::Owned(owned) => bytes.gwrite(owned.as_slice(), &mut file_offset)?,
};
bytes.gwrite_with(section, offset, ctx)?;
if file_offset < vsize {
bytes.gwrite(vec![0u8; vsize - file_offset].as_slice(), &mut file_offset)?;
}
if let Some(pad) = pad(file_offset - file_section_offset, file_alignment) {
debug!(
"aligning `{}` {:#x} -> {:#x} bytes'",
section_name,
file_offset - file_section_offset,
file_offset - file_section_offset + pad.len()
);
bytes.gwrite(pad.as_slice(), &mut file_offset)?;
}
let written_data_size = file_offset - file_section_offset;
if ondisk_size != written_data_size {
warn!(
"Original PE is inefficient or bug (on-disk data size in PE: {:#x}), we wrote {:#x} bytes",
ondisk_size, written_data_size
);
}
}
Ok(*offset)
}
pub fn write_certificates(
&self,
bytes: &mut [u8],
ctx: scroll::Endian,
) -> Result<usize, error::Error> {
let opt_header = self
.header
.optional_header
.ok_or(error::Error::Malformed(format!(
"This PE binary has no optional header; it is required to write certificates"
)))?;
let mut max_offset = 0;
if let Some(certificate_directory) = opt_header.data_directories.get_certificate_table() {
let mut certificate_start = certificate_directory.virtual_address.try_into()?;
for certificate in &self.certificates {
bytes.gwrite_with(certificate, &mut certificate_start, ctx)?;
max_offset = max(certificate_start, max_offset);
}
}
Ok(max_offset)
}
}
impl<'a> ctx::TryIntoCtx<scroll::Endian> for PE<'a> {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
let mut max_offset = 0;
let file_alignment: Option<usize> = match self.header.optional_header {
Some(opt_header) => {
debug_assert!(
opt_header.windows_fields.file_alignment.count_ones() == 1,
"file alignment should be a power of 2"
);
Some(opt_header.windows_fields.file_alignment.try_into()?)
}
_ => None,
};
bytes.gwrite_with(self.header, &mut offset, ctx)?;
max_offset = max(offset, max_offset);
self.write_sections(bytes, &mut offset, file_alignment, ctx)?;
max_offset = max(
self.sections
.iter()
.max_by_key(|section| section.pointer_to_raw_data as usize)
.map(|section| (section.pointer_to_raw_data + section.size_of_raw_data) as usize)
.unwrap_or(offset),
max_offset,
);
assert!(
self.header.coff_header.pointer_to_symbol_table == 0,
"Symbol tables in PE are deprecated and not supported to write"
);
max_offset = max(max_offset, self.write_certificates(bytes, ctx)?);
Ok(max_offset)
}
}
#[cfg(feature = "te")]
#[derive(Debug)]
pub struct TE<'a> {
pub header: header::TeHeader,
pub sections: Vec<section_table::SectionTable>,
pub debug_data: debug::DebugData<'a>,
pub rva_offset: usize,
}
#[cfg(feature = "te")]
impl<'a> TE<'a> {
pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
let opts = &options::ParseOptions::te();
let mut offset = 0;
let header = header::TeHeader::parse(bytes, &mut offset)?;
let rva_offset = header.stripped_size as usize - core::mem::size_of::<header::TeHeader>();
let sections = header.sections(bytes, &mut offset)?;
let debug_data = debug::DebugData::parse_with_opts_and_fixup(
bytes,
header.debug_dir,
§ions,
0,
opts,
rva_offset as u32,
)?;
Ok(TE {
header,
sections,
debug_data,
rva_offset,
})
}
}
#[derive(Debug)]
pub struct Coff<'a> {
pub header: header::CoffHeader,
pub sections: Vec<section_table::SectionTable>,
pub symbols: Option<symbol::SymbolTable<'a>>,
pub strings: Option<strtab::Strtab<'a>>,
}
impl<'a> Coff<'a> {
pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
let offset = &mut 0;
let header = header::CoffHeader::parse(bytes, offset)?;
debug!("{:#?}", header);
*offset += header.size_of_optional_header as usize;
let sections = header.sections(bytes, offset)?;
let symbols = header.symbols(bytes)?;
let strings = header.strings(bytes)?;
Ok(Coff {
header,
sections,
symbols,
strings,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pe::options::{ParseMode, ParseOptions};
static INVALID_DOS_SIGNATURE: [u8; 512] = [
0x3D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x31, 0xE2, 0xB1, 0x67, 0x50, 0x8C,
0xE2, 0x67, 0x50, 0x8C, 0xE2, 0x67, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x88, 0xE3, 0x6D, 0x50,
0x8C, 0xE2, 0x3C, 0x38, 0x8F, 0xE3, 0x62, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x89, 0xE3, 0xE0,
0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x89, 0xE3, 0x42, 0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x88, 0xE3,
0x77, 0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x8F, 0xE3, 0x6E, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x8D,
0xE3, 0x64, 0x50, 0x8C, 0xE2, 0x67, 0x50, 0x8D, 0xE2, 0x3F, 0x50, 0x8C, 0xE2, 0xE1, 0x20,
0x85, 0xE3, 0x66, 0x50, 0x8C, 0xE2, 0xE1, 0x20, 0x73, 0xE2, 0x66, 0x50, 0x8C, 0xE2, 0xE1,
0x20, 0x8E, 0xE3, 0x66, 0x50, 0x8C, 0xE2, 0x52, 0x69, 0x63, 0x68, 0x67, 0x50, 0x8C, 0xE2,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x07, 0x00, 0x5F, 0x41, 0xFC, 0x5E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x1A, 0x00,
0xFC, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x14, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE0,
0x68, 0x02, 0x00, 0x03, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xA3, 0x01, 0x00, 0x28,
0x00, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x00,
0x60, 0x0F, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x00, 0xF8, 0x46, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x54, 0x06, 0x00, 0x00, 0xF0, 0x91, 0x01, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x92, 0x01, 0x00, 0x30, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x48, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
static INVALID_PE_SIGNATURE: [u8; 512] = [
0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x31, 0xE2, 0xB1, 0x67, 0x50, 0x8C,
0xE2, 0x67, 0x50, 0x8C, 0xE2, 0x67, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x88, 0xE3, 0x6D, 0x50,
0x8C, 0xE2, 0x3C, 0x38, 0x8F, 0xE3, 0x62, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x89, 0xE3, 0xE0,
0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x89, 0xE3, 0x42, 0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x88, 0xE3,
0x77, 0x50, 0x8C, 0xE2, 0xAC, 0x3F, 0x8F, 0xE3, 0x6E, 0x50, 0x8C, 0xE2, 0x3C, 0x38, 0x8D,
0xE3, 0x64, 0x50, 0x8C, 0xE2, 0x67, 0x50, 0x8D, 0xE2, 0x3F, 0x50, 0x8C, 0xE2, 0xE1, 0x20,
0x85, 0xE3, 0x66, 0x50, 0x8C, 0xE2, 0xE1, 0x20, 0x73, 0xE2, 0x66, 0x50, 0x8C, 0xE2, 0xE1,
0x20, 0x8E, 0xE3, 0x66, 0x50, 0x8C, 0xE2, 0x52, 0x69, 0x63, 0x68, 0x67, 0x50, 0x8C, 0xE2,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x50, 0x05, 0x00, 0x00, 0x64, 0x86, 0x07, 0x00, 0x5F, 0x41, 0xFC, 0x5E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x1A, 0x00,
0xFC, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x14, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE0,
0x68, 0x02, 0x00, 0x03, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xA3, 0x01, 0x00, 0x28,
0x00, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x00,
0x60, 0x0F, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x00, 0xF8, 0x46, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x54, 0x06, 0x00, 0x00, 0xF0, 0x91, 0x01, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x92, 0x01, 0x00, 0x30, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x48, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
static COFF_FILE_SINGLE_STRING_IN_STRING_TABLE: [u8; 220] = [
0x64, 0x86, 0x1, 0x0, 0xb5, 0x39, 0x91, 0x62, 0x4e, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x20, 0x0, 0x50, 0x60, 0x48, 0x31, 0xc0, 0xe8, 0x0, 0x0, 0x0, 0x0, 0x4,
0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0x67, 0x1, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x2e, 0x61, 0x73, 0x6d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x74, 0x65, 0x78,
0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x1, 0x8, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x61, 0x62,
0x73, 0x6f, 0x6c, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x6d, 0x61,
0x69, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x10,
0x0, 0x0, 0x0, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x0,
];
static HEADERONLY_EMPTY_PE64: &[u8; 512] = &[
0x4D, 0x5A, 0x78, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x24, 0x00, 0x00,
0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x00, 0x00, 0x5D, 0x3F, 0xC8, 0x19, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
#[test]
fn string_table_excludes_length() {
let coff = Coff::parse(&&COFF_FILE_SINGLE_STRING_IN_STRING_TABLE[..]).unwrap();
let string_table = coff.strings.unwrap().to_vec().unwrap();
assert!(string_table == vec!["ExitProcess"]);
}
#[test]
fn symbol_name_excludes_length() {
let coff = Coff::parse(&COFF_FILE_SINGLE_STRING_IN_STRING_TABLE).unwrap();
let strings = coff.strings.unwrap();
let symbols = coff
.symbols
.unwrap()
.iter()
.filter(|(_, name, _)| name.is_none())
.map(|(_, _, sym)| sym.name(&strings).unwrap().to_owned())
.collect::<Vec<_>>();
assert_eq!(symbols, vec!["ExitProcess"])
}
#[test]
fn invalid_dos_header() {
if let Ok(_) = PE::parse(&INVALID_DOS_SIGNATURE) {
panic!("must not parse PE with invalid DOS header");
}
}
#[test]
fn invalid_pe_header() {
if let Ok(_) = PE::parse(&INVALID_PE_SIGNATURE) {
panic!("must not parse PE with invalid PE header");
}
}
#[test]
fn pe64_image_base_and_entry_nonoverflows() {
let binary = PE::parse(HEADERONLY_EMPTY_PE64).unwrap();
assert_eq!(binary.entry, 0x1020u32);
assert_eq!(binary.image_base, 0x140000000u64);
}
#[test]
fn test_packed_binary_permissive_parsing() {
let mut packed_pe = vec![0u8; 0x100];
packed_pe[0] = 0x4D; packed_pe[1] = 0x5A;
packed_pe[0x3C] = 0x30;
packed_pe[0x3D] = 0x00;
packed_pe[0x3E] = 0x00;
packed_pe[0x3F] = 0x00;
packed_pe[0x30] = 0x50; packed_pe[0x31] = 0x45; packed_pe[0x32] = 0x00;
packed_pe[0x33] = 0x00;
packed_pe[0x34] = 0x4C; packed_pe[0x35] = 0x01;
let strict_result = PE::parse(&packed_pe);
assert!(
strict_result.is_err(),
"Strict parsing should fail for packed binary"
);
let permissive_opts = ParseOptions::default().with_parse_mode(ParseMode::Permissive);
let permissive_result = PE::parse_with_opts(&packed_pe, &permissive_opts);
assert!(
permissive_result.is_ok(),
"Permissive parsing should succeed for packed binary"
);
}
}