use alloc::vec::Vec;
use core::iter::FusedIterator;
use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith, ctx};
use crate::error;
use crate::options::Permissive;
use crate::pe::{data_directories, debug, optional_header, section_table, symbol};
use crate::strtab;
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pwrite)]
#[doc(alias("IMAGE_DOS_HEADER"))]
pub struct DosHeader {
#[doc(alias("e_magic"))]
pub signature: u16,
#[doc(alias("e_cblp"))]
pub bytes_on_last_page: u16,
#[doc(alias("e_cp"))]
pub pages_in_file: u16,
#[doc(alias("e_crlc"))]
pub relocations: u16,
#[doc(alias("e_cparhdr"))]
pub size_of_header_in_paragraphs: u16,
#[doc(alias("e_minalloc"))]
pub minimum_extra_paragraphs_needed: u16,
#[doc(alias("e_maxalloc"))]
pub maximum_extra_paragraphs_needed: u16,
#[doc(alias("e_ss"))]
pub initial_relative_ss: u16,
#[doc(alias("e_sp"))]
pub initial_sp: u16,
#[doc(alias("e_csum"))]
pub checksum: u16,
#[doc(alias("e_ip"))]
pub initial_ip: u16,
#[doc(alias("e_cs"))]
pub initial_relative_cs: u16,
#[doc(alias("e_lfarlc"))]
pub file_address_of_relocation_table: u16,
#[doc(alias("e_ovno"))]
pub overlay_number: u16,
#[doc(alias("e_res"))]
pub reserved: [u16; 4],
#[doc(alias("e_oemid"))]
pub oem_id: u16,
#[doc(alias("e_oeminfo"))]
pub oem_info: u16,
#[doc(alias("e_res2"))]
pub reserved2: [u16; 10],
#[doc(alias("e_lfanew"))]
pub pe_pointer: u32,
}
#[doc(alias("IMAGE_DOS_SIGNATURE"))]
pub const DOS_MAGIC: u16 = 0x5a4d;
pub const PE_POINTER_OFFSET: u32 = 0x3c;
pub const DOS_STUB_OFFSET: u32 = PE_POINTER_OFFSET + (core::mem::size_of::<u32>() as u32);
impl DosHeader {
pub fn parse(bytes: &[u8]) -> error::Result<Self> {
let mut offset = 0;
let signature = bytes.gread_with(&mut offset, scroll::LE).map_err(|_| {
error::Error::Malformed(format!("cannot parse DOS signature (offset {:#x})", 0))
})?;
if signature != DOS_MAGIC {
return Err(error::Error::Malformed(format!(
"DOS header is malformed (signature {:#x})",
signature
)));
}
let bytes_on_last_page = bytes.gread_with(&mut offset, scroll::LE)?;
let pages_in_file = bytes.gread_with(&mut offset, scroll::LE)?;
let relocations = bytes.gread_with(&mut offset, scroll::LE)?;
let size_of_header_in_paragraphs = bytes.gread_with(&mut offset, scroll::LE)?;
let minimum_extra_paragraphs_needed = bytes.gread_with(&mut offset, scroll::LE)?;
let maximum_extra_paragraphs_needed = bytes.gread_with(&mut offset, scroll::LE)?;
let initial_relative_ss = bytes.gread_with(&mut offset, scroll::LE)?;
let initial_sp = bytes.gread_with(&mut offset, scroll::LE)?;
let checksum = bytes.gread_with(&mut offset, scroll::LE)?;
let initial_ip = bytes.gread_with(&mut offset, scroll::LE)?;
let initial_relative_cs = bytes.gread_with(&mut offset, scroll::LE)?;
let file_address_of_relocation_table = bytes.gread_with(&mut offset, scroll::LE)?;
let overlay_number = bytes.gread_with(&mut offset, scroll::LE)?;
let reserved = bytes.gread_with(&mut offset, scroll::LE)?; let oem_id = bytes.gread_with(&mut offset, scroll::LE)?;
let oem_info = bytes.gread_with(&mut offset, scroll::LE)?;
let reserved2 = bytes.gread_with(&mut offset, scroll::LE)?;
debug_assert!(
offset == PE_POINTER_OFFSET as usize,
"expected offset ({:#x}) after reading DOS header to be at 0x3C",
offset
);
let pe_pointer: u32 = bytes
.pread_with(PE_POINTER_OFFSET as usize, scroll::LE)
.map_err(|_| {
error::Error::Malformed(format!(
"cannot parse PE header pointer (offset {:#x})",
PE_POINTER_OFFSET
))
})?;
let pe_signature: u32 =
bytes
.pread_with(pe_pointer as usize, scroll::LE)
.map_err(|_| {
error::Error::Malformed(format!(
"cannot parse PE header signature (offset {:#x})",
pe_pointer
))
})?;
if pe_signature != PE_MAGIC {
return Err(error::Error::Malformed(format!(
"PE header is malformed (signature {:#x})",
pe_signature
)));
}
Ok(DosHeader {
signature,
bytes_on_last_page,
pages_in_file,
relocations,
size_of_header_in_paragraphs,
minimum_extra_paragraphs_needed,
maximum_extra_paragraphs_needed,
initial_relative_ss,
initial_sp,
checksum,
initial_ip,
initial_relative_cs,
file_address_of_relocation_table,
overlay_number,
reserved,
oem_id,
oem_info,
reserved2,
pe_pointer,
})
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DosStub<'a> {
pub data: &'a [u8],
}
impl<'a> Default for DosStub<'a> {
#[rustfmt::skip]
fn default() -> Self {
Self {
data: &[
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, ],
}
}
}
impl<'a> ctx::TryIntoCtx<scroll::Endian> for DosStub<'a> {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], _: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
bytes.gwrite_with(&*self.data, offset, ())?;
Ok(*offset)
}
}
impl<'a> DosStub<'a> {
pub fn parse(bytes: &'a [u8], pe_pointer: u32) -> error::Result<Self> {
let start_offset = DOS_STUB_OFFSET as usize;
let end_offset = pe_pointer as usize;
if end_offset < start_offset {
return Err(error::Error::Malformed(format!(
"PE pointer ({:#x}) cannot be before the DOS stub start ({:#x})",
pe_pointer, start_offset
)));
}
if bytes.len() < end_offset {
return Err(error::Error::Malformed(format!(
"File is too short ({} bytes) to contain the PE header at {:#x}",
bytes.len(),
end_offset
)));
}
let dos_stub_area = &bytes[start_offset..end_offset];
Ok(Self {
data: dos_stub_area,
})
}
}
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
#[doc(alias("IMAGE_FILE_HEADER"))]
pub struct CoffHeader {
#[doc(alias("Machine"))]
pub machine: u16,
#[doc(alias("NumberOfSections"))]
pub number_of_sections: u16,
#[doc(alias("TimeDateStamp"))]
pub time_date_stamp: u32,
#[doc(alias("PointerToSymbolTable"))]
pub pointer_to_symbol_table: u32,
#[doc(alias("NumberOfSymbols"))]
pub number_of_symbol_table: u32,
#[doc(alias("SizeOfOptionalHeader"))]
pub size_of_optional_header: u16,
#[doc(alias("Characteristics"))]
pub characteristics: u16,
}
pub const SIZEOF_COFF_HEADER: usize = 20;
pub const PE_MAGIC: u32 = 0x0000_4550;
pub const SIZEOF_PE_MAGIC: usize = 4;
#[doc(alias("IMAGE_FILE_MACHINE_UNKNOWN"))]
pub const COFF_MACHINE_UNKNOWN: u16 = 0x0;
#[doc(alias("IMAGE_FILE_MACHINE_ALPHA"))]
pub const COFF_MACHINE_ALPHA: u16 = 0x184;
#[doc(alias("IMAGE_FILE_MACHINE_ALPHA64"))]
#[doc(alias("IMAGE_FILE_MACHINE_AXP64"))]
pub const COFF_MACHINE_ALPHA64: u16 = 0x284;
#[doc(alias("IMAGE_FILE_MACHINE_AM33"))]
pub const COFF_MACHINE_AM33: u16 = 0x1d3;
#[doc(alias("IMAGE_FILE_MACHINE_AMD64"))]
pub const COFF_MACHINE_X86_64: u16 = 0x8664;
#[doc(alias("IMAGE_FILE_MACHINE_ARM"))]
pub const COFF_MACHINE_ARM: u16 = 0x1c0;
#[doc(alias("IMAGE_FILE_MACHINE_ARM64"))]
pub const COFF_MACHINE_ARM64: u16 = 0xaa64;
#[doc(alias("IMAGE_FILE_MACHINE_ARMNT"))]
pub const COFF_MACHINE_ARMNT: u16 = 0x1c4;
#[doc(alias("IMAGE_FILE_MACHINE_EBC"))]
pub const COFF_MACHINE_EBC: u16 = 0xebc;
#[doc(alias("IMAGE_FILE_MACHINE_I386"))]
pub const COFF_MACHINE_X86: u16 = 0x14c;
#[doc(alias("IMAGE_FILE_MACHINE_IA64"))]
pub const COFF_MACHINE_IA64: u16 = 0x200;
#[doc(alias("IMAGE_FILE_MACHINE_LOONGARCH32"))]
pub const COFF_MACHINE_LOONGARCH32: u16 = 0x6232;
#[doc(alias("IMAGE_FILE_MACHINE_LOONGARCH64"))]
pub const COFF_MACHINE_LOONGARCH64: u16 = 0x6264;
#[doc(alias("IMAGE_FILE_MACHINE_M32R"))]
pub const COFF_MACHINE_M32R: u16 = 0x9041;
#[doc(alias("IMAGE_FILE_MACHINE_MIPS16"))]
pub const COFF_MACHINE_MIPS16: u16 = 0x266;
#[doc(alias("IMAGE_FILE_MACHINE_MIPSFPU"))]
pub const COFF_MACHINE_MIPSFPU: u16 = 0x366;
#[doc(alias("IMAGE_FILE_MACHINE_MIPSFPU16"))]
pub const COFF_MACHINE_MIPSFPU16: u16 = 0x466;
#[doc(alias("IMAGE_FILE_MACHINE_POWERPC"))]
pub const COFF_MACHINE_POWERPC: u16 = 0x1f0;
#[doc(alias("IMAGE_FILE_MACHINE_POWERPCFP"))]
pub const COFF_MACHINE_POWERPCFP: u16 = 0x1f1;
#[doc(alias("IMAGE_FILE_MACHINE_R4000"))]
pub const COFF_MACHINE_R4000: u16 = 0x166;
#[doc(alias("IMAGE_FILE_MACHINE_RISCV32"))]
pub const COFF_MACHINE_RISCV32: u16 = 0x5032;
#[doc(alias("IMAGE_FILE_MACHINE_RISCV64"))]
pub const COFF_MACHINE_RISCV64: u16 = 0x5064;
#[doc(alias("IMAGE_FILE_MACHINE_RISCV128"))]
pub const COFF_MACHINE_RISCV128: u16 = 0x5128;
#[doc(alias("IMAGE_FILE_MACHINE_SH3"))]
pub const COFF_MACHINE_SH3: u16 = 0x1a2;
#[doc(alias("IMAGE_FILE_MACHINE_SH3DSP"))]
pub const COFF_MACHINE_SH3DSP: u16 = 0x1a3;
#[doc(alias("IMAGE_FILE_MACHINE_SH4"))]
pub const COFF_MACHINE_SH4: u16 = 0x1a6;
#[doc(alias("IMAGE_FILE_MACHINE_SH5"))]
pub const COFF_MACHINE_SH5: u16 = 0x1a8;
#[doc(alias("IMAGE_FILE_MACHINE_THUMB"))]
pub const COFF_MACHINE_THUMB: u16 = 0x1c2;
#[doc(alias("IMAGE_FILE_MACHINE_WCEMIPSV2"))]
pub const COFF_MACHINE_WCEMIPSV2: u16 = 0x169;
impl CoffHeader {
pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
Ok(bytes.gread_with(offset, scroll::LE)?)
}
pub fn sections(
&self,
bytes: &[u8],
offset: &mut usize,
) -> error::Result<Vec<section_table::SectionTable>> {
self.sections_with_opts(bytes, offset, &crate::pe::options::ParseOptions::default())
}
pub(crate) fn sections_with_opts(
&self,
bytes: &[u8],
offset: &mut usize,
opts: &crate::pe::options::ParseOptions,
) -> error::Result<Vec<section_table::SectionTable>> {
let nsections = self.number_of_sections as usize;
if nsections > bytes.len() / 40 {
return Err(error::Error::BufferTooShort(nsections, "sections"));
}
let mut sections = Vec::with_capacity(nsections);
let string_table_offset = self.pointer_to_symbol_table as usize
+ symbol::SymbolTable::size(self.number_of_symbol_table as usize);
for i in 0..nsections {
let section = section_table::SectionTable::parse_with_opts(
bytes,
offset,
string_table_offset as usize,
opts,
)?;
debug!("({}) {:#?}", i, section);
sections.push(section);
}
Ok(sections)
}
pub fn symbols<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<symbol::SymbolTable<'a>>> {
let offset = self.pointer_to_symbol_table as usize;
let number = self.number_of_symbol_table as usize;
if offset == 0 {
Ok(None)
} else {
symbol::SymbolTable::parse(bytes, offset, number).map(Some)
}
}
pub fn strings<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<strtab::Strtab<'a>>> {
if self.pointer_to_symbol_table == 0 {
return Ok(None);
}
let mut offset = self.pointer_to_symbol_table as usize
+ symbol::SymbolTable::size(self.number_of_symbol_table as usize);
let length_field_size = core::mem::size_of::<u32>();
let length = bytes
.pread_with::<u32>(offset, scroll::LE)?
.checked_sub(length_field_size as u32)
.ok_or(error::Error::Malformed(format!(
"COFF length field size ({length_field_size:#x}) is larger than the parsed length value"
)))? as usize;
offset += length_field_size;
Ok(Some(strtab::Strtab::parse(bytes, offset, length, 0)?))
}
}
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct Header<'a> {
pub dos_header: DosHeader,
pub dos_stub: DosStub<'a>,
pub rich_header: Option<RichHeader<'a>>,
pub signature: u32,
pub coff_header: CoffHeader,
pub optional_header: Option<optional_header::OptionalHeader>,
}
impl<'a> Header<'a> {
fn parse_impl(
bytes: &'a [u8],
dos_header: DosHeader,
dos_stub: DosStub<'a>,
parse_rich_header: bool,
opts: &crate::pe::options::ParseOptions,
) -> error::Result<Self> {
let rich_header = if parse_rich_header {
RichHeader::parse_with_opts(&bytes, opts)?
} else {
None
};
let mut offset = dos_header.pe_pointer as usize;
let signature = bytes
.gread_with::<u32>(&mut offset, scroll::LE)
.map_err(|_| {
error::Error::Malformed(format!("cannot parse PE signature (offset {:#x})", offset))
})?;
let coff_header = CoffHeader::parse(&bytes, &mut offset)?;
let optional_header = if coff_header.size_of_optional_header > 0 {
Some(bytes.pread::<optional_header::OptionalHeader>(offset)?)
} else {
None
};
Ok(Header {
dos_header,
dos_stub,
rich_header,
signature,
coff_header,
optional_header,
})
}
pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
Self::parse_with_opts(bytes, &crate::pe::options::ParseOptions::default())
}
pub(crate) fn parse_with_opts(
bytes: &'a [u8],
opts: &crate::pe::options::ParseOptions,
) -> error::Result<Self> {
let dos_header = DosHeader::parse(&bytes)?;
let dos_stub = DosStub::parse(bytes, dos_header.pe_pointer)
.or_permissive_and_default(opts.parse_mode.is_permissive(), "DOS stub parse failed")?;
Header::parse_impl(bytes, dos_header, dos_stub, true, opts)
}
pub fn parse_without_dos(bytes: &'a [u8]) -> error::Result<Self> {
let dos_header = DosHeader::default();
Header::parse_impl(
bytes,
dos_header,
DosStub::default(),
false,
&crate::pe::options::ParseOptions::default(),
)
}
}
impl<'a> ctx::TryIntoCtx<scroll::Endian> for Header<'a> {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
bytes.gwrite_with(self.dos_header, offset, ctx)?;
bytes.gwrite_with(self.dos_stub, offset, ctx)?;
bytes.gwrite_with(self.signature, offset, scroll::LE)?;
bytes.gwrite_with(self.coff_header, offset, ctx)?;
if let Some(opt_header) = self.optional_header {
bytes.gwrite_with(opt_header, offset, ctx)?;
}
Ok(*offset)
}
}
pub const DANS_MARKER: u32 = 0x536E6144;
pub const DANS_MARKER_SIZE: usize = core::mem::size_of::<u32>();
pub const RICH_MARKER: u32 = 0x68636952;
pub const RICH_MARKER_SIZE: usize = core::mem::size_of::<u32>();
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct RichHeader<'a> {
pub key: u32,
pub data: &'a [u8],
pub padding_size: usize,
pub start_offset: u32,
pub end_offset: u32,
}
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite)]
pub struct RichMetadata {
pub build: u16,
pub product: u16,
pub use_count: u32,
}
impl RichMetadata {
fn parse(bytes: &[u8], key: u32) -> error::Result<Self> {
let mut offset = 0;
let build_and_product = bytes.gread_with::<u32>(&mut offset, scroll::LE)? ^ key;
let build = (build_and_product & 0xFFFF) as u16;
let product = (build_and_product >> 16) as u16;
let use_count = bytes.gread_with::<u32>(&mut offset, scroll::LE)? ^ key;
Ok(Self {
build,
product,
use_count,
})
}
}
const RICH_METADATA_SIZE: usize = 8;
#[derive(Debug)]
pub struct RichMetadataIterator<'a> {
key: u32,
data: &'a [u8],
}
impl Iterator for RichMetadataIterator<'_> {
type Item = error::Result<RichMetadata>;
fn next(&mut self) -> Option<Self::Item> {
if self.data.is_empty() {
return None;
}
Some(match RichMetadata::parse(&self.data, self.key) {
Ok(metadata) => {
self.data = &self.data[RICH_METADATA_SIZE..];
Ok(metadata)
}
Err(error) => {
self.data = &[];
Err(error.into())
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.data.len() / RICH_METADATA_SIZE;
(len, Some(len))
}
}
impl FusedIterator for RichMetadataIterator<'_> {}
impl ExactSizeIterator for RichMetadataIterator<'_> {}
impl<'a> RichHeader<'a> {
pub fn parse(bytes: &'a [u8]) -> error::Result<Option<Self>> {
Self::parse_with_opts(bytes, &crate::pe::options::ParseOptions::default())
}
pub(crate) fn parse_with_opts(
bytes: &'a [u8],
opts: &crate::pe::options::ParseOptions,
) -> error::Result<Option<Self>> {
let dos_header = DosHeader::parse(bytes)?;
let dos_header_end_offset = PE_POINTER_OFFSET as usize;
let pe_header_start_offset = dos_header.pe_pointer as usize;
if pe_header_start_offset <= dos_header_end_offset
|| (pe_header_start_offset - dos_header_end_offset) < 8
{
return Ok(None);
}
let scan_start = dos_header_end_offset + 4;
let scan_end = pe_header_start_offset;
if scan_start > scan_end {
return Err(error::Error::Malformed(format!(
"Rich header scan start ({:#X}) is greater than scan end ({:#X})",
scan_start, scan_end
)))
.or_permissive_and_value(
opts.parse_mode.is_permissive(),
"Packed binaries may have PE pointer before DOS header end",
None,
);
}
let scan_stub = &bytes[scan_start..scan_end];
let (rich_end_offset, key) = match scan_stub
.windows(8)
.enumerate()
.filter_map(
|(index, window)| match window.pread_with::<u32>(0, scroll::LE) {
Ok(marker) if marker == RICH_MARKER => Some(Ok(index)),
Err(e) => Some(Err(error::Error::from(e))),
_ => None,
},
)
.next()
{
Some(Ok(rich_end_offset)) => {
let rich_key =
scan_stub.pread_with::<u32>(rich_end_offset + RICH_MARKER_SIZE, scroll::LE)?;
(rich_end_offset, rich_key)
}
Some(Err(e)) => return Err(e),
None => return Ok(None),
};
if rich_end_offset >= scan_stub.len() {
return Err(error::Error::Malformed(format!(
"Rich end offset ({:#X}) exceeds scan stub length ({:#X})",
rich_end_offset,
scan_stub.len()
)));
}
let rich_header = &scan_stub[..rich_end_offset];
let rich_start_offset = match scan_stub
.windows(4)
.enumerate()
.filter_map(
|(index, window)| match window.pread_with::<u32>(0, scroll::LE) {
Ok(value) if (value ^ key) == DANS_MARKER => Some(Ok(index + DANS_MARKER_SIZE)),
Err(e) => Some(Err(error::Error::from(e))),
_ => None,
},
)
.next()
{
Some(Ok(offset)) => offset,
Some(Err(e)) => return Err(e),
None => {
return Err(error::Error::Malformed(format!(
"Rich header does not contain the DanS marker"
)));
}
};
if rich_start_offset >= rich_header.len() {
return Err(error::Error::Malformed(format!(
"Rich start offset ({:#X}) exceeds rich header length ({:#X})",
rich_start_offset,
rich_header.len()
)));
}
let rich_header = &rich_header[rich_start_offset..];
let padding_size = rich_header
.chunks(4)
.map(|chunk| chunk.pread_with::<u32>(0, scroll::LE))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.take_while(|value| value == &key)
.count()
* core::mem::size_of_val(&key);
let data = rich_header;
let start_offset = scan_start as u32 + rich_start_offset as u32 - DANS_MARKER_SIZE as u32;
let end_offset = scan_start as u32 + rich_end_offset as u32;
Ok(Some(RichHeader {
key,
data,
padding_size,
start_offset,
end_offset,
}))
}
pub fn metadatas(&self) -> RichMetadataIterator<'a> {
RichMetadataIterator {
key: self.key,
data: &self.data[self.padding_size..],
}
}
}
#[cfg(feature = "te")]
#[repr(C)]
#[derive(Debug, Default, PartialEq, Copy, Clone, Pread, Pwrite)]
pub struct TeHeader {
pub signature: u16,
pub machine: u16,
pub number_of_sections: u8,
pub subsystem: u8,
pub stripped_size: u16,
pub entry_point: u32,
pub base_of_code: u32,
pub image_base: u64,
pub reloc_dir: data_directories::DataDirectory,
pub debug_dir: data_directories::DataDirectory,
}
#[cfg(feature = "te")]
#[doc(alias("IMAGE_TE_SIGNATURE"))]
pub const TE_MAGIC: u16 = 0x5a56;
#[cfg(feature = "te")]
impl TeHeader {
pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
const HEADER_SIZE: usize = core::mem::size_of::<TeHeader>();
let mut header: TeHeader = bytes.gread_with(offset, scroll::LE)?;
let stripped_size = header.stripped_size as u32;
let adj_offset = stripped_size
.checked_sub(HEADER_SIZE as u32)
.ok_or_else(|| {
error::Error::Malformed(format!(
"Stripped size ({stripped_size:#x}) is smaller than TE header size ({HEADER_SIZE:#x})",
))
})?;
header.fixup_header(adj_offset);
Ok(header)
}
pub fn sections(
&self,
bytes: &[u8],
offset: &mut usize,
) -> error::Result<Vec<section_table::SectionTable>> {
let adj_offset = self.stripped_size as u32 - core::mem::size_of::<TeHeader>() as u32;
let nsections = self.number_of_sections as usize;
if nsections > bytes.len() / 40 {
return Err(error::Error::BufferTooShort(nsections, "sections"));
}
let mut sections = Vec::with_capacity(nsections);
for i in 0..nsections {
let mut section = section_table::SectionTable::parse(bytes, offset, 0)?;
TeHeader::fixup_section(&mut section, adj_offset);
debug!("({}) {:#?}", i, section);
sections.push(section);
}
Ok(sections)
}
fn fixup_header(&mut self, adj_offset: u32) {
debug!(
"Entry point fixed up from: 0x{:x} to 0x{:X}",
self.entry_point,
self.entry_point.wrapping_sub(adj_offset)
);
self.entry_point = self.entry_point.wrapping_sub(adj_offset);
debug!(
"Base of code fixed up from: 0x{:x} to 0x{:X}",
self.base_of_code,
self.base_of_code.wrapping_sub(adj_offset)
);
self.base_of_code = self.base_of_code.wrapping_sub(adj_offset);
debug!(
"Relocation Directory fixed up from: 0x{:x} to 0x{:X}",
self.reloc_dir.virtual_address,
self.reloc_dir.virtual_address.wrapping_sub(adj_offset)
);
self.reloc_dir.virtual_address = self.reloc_dir.virtual_address.wrapping_sub(adj_offset);
debug!(
"Debug Directory fixed up from: 0x{:x} to 0x{:X}",
self.debug_dir.virtual_address,
self.debug_dir.virtual_address.wrapping_sub(adj_offset)
);
self.debug_dir.virtual_address = self.debug_dir.virtual_address.wrapping_sub(adj_offset);
}
fn fixup_section(section: &mut section_table::SectionTable, adj_offset: u32) {
debug!(
"Section virtual address fixed up from: 0x{:X} to 0x{:X}",
section.virtual_address,
section.virtual_address.wrapping_sub(adj_offset)
);
section.virtual_address = section.virtual_address.wrapping_sub(adj_offset);
if section.pointer_to_linenumbers > 0 {
debug!(
"Section pointer to line numbers fixed up from: 0x{:X} to 0x{:X}",
section.pointer_to_linenumbers,
section.pointer_to_linenumbers.wrapping_sub(adj_offset)
);
section.pointer_to_linenumbers =
section.pointer_to_linenumbers.wrapping_sub(adj_offset);
}
if section.pointer_to_raw_data > 0 {
debug!(
"Section pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
section.pointer_to_raw_data,
section.pointer_to_raw_data.wrapping_sub(adj_offset)
);
section.pointer_to_raw_data = section.pointer_to_raw_data.wrapping_sub(adj_offset);
}
if section.pointer_to_relocations > 0 {
debug!(
"Section pointer to relocations fixed up from: 0x{:X} to 0x{:X}",
section.pointer_to_relocations,
section.pointer_to_relocations.wrapping_sub(adj_offset)
);
section.pointer_to_relocations =
section.pointer_to_relocations.wrapping_sub(adj_offset);
}
}
}
pub fn machine_to_str(machine: u16) -> &'static str {
match machine {
COFF_MACHINE_UNKNOWN => "UNKNOWN",
COFF_MACHINE_ALPHA => "ALPHA",
COFF_MACHINE_ALPHA64 => "ALPHA64",
COFF_MACHINE_AM33 => "AM33",
COFF_MACHINE_X86_64 => "X86_64",
COFF_MACHINE_ARM => "ARM",
COFF_MACHINE_ARM64 => "ARM64",
COFF_MACHINE_ARMNT => "ARM_NT",
COFF_MACHINE_EBC => "EBC",
COFF_MACHINE_X86 => "X86",
COFF_MACHINE_IA64 => "IA64",
COFF_MACHINE_LOONGARCH32 => "LOONGARCH32",
COFF_MACHINE_LOONGARCH64 => "LOONGARCH64",
COFF_MACHINE_M32R => "M32R",
COFF_MACHINE_MIPS16 => "MIPS_16",
COFF_MACHINE_MIPSFPU => "MIPS_FPU",
COFF_MACHINE_MIPSFPU16 => "MIPS_FPU_16",
COFF_MACHINE_POWERPC => "POWERPC",
COFF_MACHINE_POWERPCFP => "POWERCFP",
COFF_MACHINE_R4000 => "R4000",
COFF_MACHINE_RISCV32 => "RISC-V_32",
COFF_MACHINE_RISCV64 => "RISC-V_64",
COFF_MACHINE_RISCV128 => "RISC-V_128",
COFF_MACHINE_SH3 => "SH3",
COFF_MACHINE_SH3DSP => "SH3DSP",
COFF_MACHINE_SH4 => "SH4",
COFF_MACHINE_SH5 => "SH5",
COFF_MACHINE_THUMB => "THUMB",
COFF_MACHINE_WCEMIPSV2 => "WCE_MIPS_V2",
_ => "COFF_UNKNOWN",
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{error, pe::Coff};
const CRSS_HEADER: [u8; 688] = [
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,
0xd0, 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, 0x0d, 0x0d, 0x0a,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x4a, 0xc3, 0xeb, 0xee, 0x2b, 0xad,
0xb8, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xac, 0xb8, 0xfe, 0x2b,
0xad, 0xb8, 0x33, 0xd4, 0x66, 0xb8, 0xeb, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x63, 0xb8, 0xea,
0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x7a, 0xb8, 0xed, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x64, 0xb8,
0xef, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x61, 0xb8, 0xef, 0x2b, 0xad, 0xb8, 0x52, 0x69, 0x63,
0x68, 0xee, 0x2b, 0xad, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45,
0x00, 0x00, 0x4c, 0x01, 0x05, 0x00, 0xd9, 0x8f, 0x15, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe0, 0x00, 0x02, 0x01, 0x0b, 0x01, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xe4, 0xab, 0x00, 0x00,
0x01, 0x00, 0x40, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1a, 0x00, 0x00, 0xb8, 0x22, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x38, 0x00, 0x00,
0x00, 0x10, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x24,
0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x60, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xc0, 0x2e, 0x69, 0x64, 0x61,
0x74, 0x61, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2e, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x42, 0x2e, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x50,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
const NO_RICH_HEADER: [u8; 262] = [
0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86,
];
const NO_RICH_HEADER_INVALID_PE_POINTER: [u8; 304] = [
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,
0x3C, 0xFF, 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, 0x8D, 0xC7, 0xEA, 0x07, 0xC9, 0xA6, 0x84,
0x54, 0xC9, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x81, 0x55, 0xCB, 0xA6,
0x84, 0x54, 0xC0, 0xDE, 0x17, 0x54, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x80, 0x55, 0xC7,
0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x87, 0x55, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x81, 0x55,
0x7E, 0xA6, 0x84, 0x54, 0xB9, 0x27, 0x85, 0x55, 0xCA, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x85,
0x54, 0x08, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x81, 0x55, 0xE8, 0xA6, 0x84, 0x54, 0xA5, 0xD2,
0x80, 0x55, 0xD9, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x87, 0x55, 0xC0, 0xA6, 0x84, 0x54, 0x10,
0xD2, 0x80, 0x55, 0x49, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x84, 0x55, 0xC8, 0xA6, 0x84, 0x54,
0x10, 0xD2, 0x7B, 0x54, 0xC8, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x86, 0x55, 0xC8, 0xA6, 0x84,
0x54, 0x52, 0x69, 0x63, 0x68, 0xC9, 0xA6, 0x84, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x07, 0x00, 0xEC, 0xA5, 0x5B, 0x66,
0x00, 0x00, 0x00, 0x00,
];
const CORRECT_RICH_HEADER: [u8; 256] = [
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,
0xF8, 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, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35,
0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D,
0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48,
0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3,
0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34,
0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D,
0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52,
0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05,
0x00,
];
const CORRUPTED_RICH_HEADER: [u8; 256] = [
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,
0xF8, 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, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35,
0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D,
0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48,
0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3,
0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34,
0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D,
0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52,
0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05,
0x00,
];
const HEADER_WITH_OMITTED_DOS_STUB: [u8; 512] = [
0x4d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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,
0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x02, 0x01, 0x0b, 0x01, 0x0e, 0x00, 0x00, 0xe0,
0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x90, 0xab, 0x04, 0x00, 0x00,
0xd0, 0x02, 0x00, 0x00, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x40, 0x89, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0x04, 0x00, 0x94, 0x01, 0x00,
0x00, 0x00, 0xb0, 0x04, 0x00, 0x64, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0x04, 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, 0x78, 0xad, 0x04, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x50, 0x58, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0x00, 0xe0, 0x55, 0x50, 0x58, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00,
0x00, 0xd0, 0x02, 0x00, 0x00, 0xde, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xe0, 0x2e, 0x72,
0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xb0, 0x04, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x2e, 0x39, 0x35, 0x00, 0x55, 0x50, 0x58, 0x21,
0x0d, 0x09, 0x08, 0x08, 0x25, 0x1d, 0xab, 0x52, 0xef, 0x64, 0xdc, 0xe5, 0x52, 0x8d, 0x04,
0x00, 0x90, 0xdb, 0x01, 0x00, 0x38, 0x42, 0x04, 0x00, 0x26, 0x19, 0x00, 0xa8,
];
const BORLAND_PE32_VALID_NO_RICH_HEADER: [u8; 528] = [
0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x08, 0x00, 0xC0, 0x9C, 0x07, 0x67, 0x00, 0x00, 0x00,
0x00,
];
const INVALID_COFF_OBJECT: [u8; 20] = [
0x4C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0F, 0x00, 0xFF, 0x80,
];
const MALFORMED_SMALL_TE: [u8; 58] = [
0x56, 0x5A, 0x52, 0x5A, 0x50, 0x00, 0x17, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00,
0x10, 0x86, 0x02, 0x0C, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x1B, 0x01, 0x01, 0x00, 0x00,
0xFF, 0xB5, 0x00, 0x00, 0x00, 0x04, 0x34, 0x00, 0x00, 0xFF, 0xB5, 0x00, 0x00, 0x00, 0x04,
0x34, 0x15, 0x40, 0x13, 0x41, 0x0E, 0x10, 0x15, 0x40, 0x13, 0x41, 0x0E, 0x10,
];
const WELL_FORMED_WITH_RICH_HEADER: &[u8] =
include_bytes!("../../tests/bins/pe/well_formed_import.exe.bin");
#[test]
fn crss_header() {
let header = Header::parse(&&CRSS_HEADER[..]).unwrap();
assert!(header.dos_header.signature == DOS_MAGIC);
assert!(header.signature == PE_MAGIC);
assert!(header.coff_header.machine == COFF_MACHINE_X86);
assert!(machine_to_str(header.coff_header.machine) == "X86");
println!("header: {:?}", &header);
}
#[test]
fn parse_without_dos_rich() {
let dos_header = DosHeader::parse(&WELL_FORMED_WITH_RICH_HEADER).unwrap();
let buf = &WELL_FORMED_WITH_RICH_HEADER[dos_header.pe_pointer as usize..];
let header = Header::parse_without_dos(buf).unwrap();
assert_eq!(header.coff_header.number_of_sections, 6);
assert_eq!(header.rich_header, None);
assert_eq!(header.dos_header, DosHeader::default());
}
#[test]
fn parse_borland_weird_dos_stub() {
let dos_stub = DosStub::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER, 0x200).unwrap();
assert_ne!(dos_stub.data, BORLAND_PE32_VALID_NO_RICH_HEADER.to_vec());
}
#[test]
fn parse_borland_no_rich_header() {
let header = RichHeader::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap();
assert_eq!(header, None);
}
#[test]
fn parse_no_rich_header() {
let header = RichHeader::parse(&NO_RICH_HEADER).unwrap();
assert_eq!(header, None);
}
#[test]
fn parse_no_rich_header_invalid_pe_pointer() {
let header = RichHeader::parse(&NO_RICH_HEADER_INVALID_PE_POINTER);
assert_eq!(header.is_err(), true);
if let Err(error::Error::Malformed(msg)) = header {
assert_eq!(msg, "cannot parse PE header signature (offset 0xff3c)");
} else {
panic!("Expected a Malformed error but got {:?}", header);
}
}
#[test]
fn parse_correct_rich_header() {
let header = RichHeader::parse(&CORRECT_RICH_HEADER).unwrap();
assert_ne!(header, None);
let header = header.unwrap();
let expected = vec![
RichMetadata {
build: 25203,
product: 260,
use_count: 10,
},
RichMetadata {
build: 25203,
product: 259,
use_count: 5,
},
RichMetadata {
build: 25203,
product: 261,
use_count: 127,
},
RichMetadata {
build: 25305,
product: 259,
use_count: 9,
},
RichMetadata {
build: 25305,
product: 261,
use_count: 35,
},
RichMetadata {
build: 25305,
product: 260,
use_count: 18,
},
RichMetadata {
build: 25203,
product: 257,
use_count: 11,
},
RichMetadata {
build: 0,
product: 1,
use_count: 152,
},
RichMetadata {
build: 0,
product: 0,
use_count: 20,
},
RichMetadata {
build: 25547,
product: 258,
use_count: 1,
},
];
assert_eq!(
header
.metadatas()
.filter_map(Result::ok)
.collect::<Vec<RichMetadata>>(),
expected
);
}
#[test]
fn parse_corrupted_rich_header() {
let header_result = RichHeader::parse(&CORRUPTED_RICH_HEADER);
assert_eq!(header_result.is_err(), true);
}
#[test]
fn parse_malformed_small_te() {
let mut offset = 0;
let header = TeHeader::parse(&MALFORMED_SMALL_TE, &mut offset);
assert_eq!(header.is_err(), true);
if let Err(error::Error::Malformed(msg)) = header {
assert_eq!(
msg,
"Stripped size (0x17) is smaller than TE header size (0x28)"
);
} else {
panic!("Expected a Malformed error but got {:?}", header);
}
}
#[test]
fn parse_invalid_small_coff() {
let header = Coff::parse(&INVALID_COFF_OBJECT);
assert_eq!(header.is_err(), true);
if let Err(error::Error::Malformed(msg)) = header {
assert_eq!(
msg,
"COFF length field size (0x4) is larger than the parsed length value"
);
} else {
panic!("Expected a Malformed error but got {:?}", header);
}
}
#[test]
fn parse_with_omitted_dos_stub() {
let header = Header::parse(&HEADER_WITH_OMITTED_DOS_STUB).unwrap();
assert_eq!(header.dos_header.pe_pointer, 0x40);
assert_eq!(header.coff_header.number_of_sections, 3);
}
static EMPTY_STRING_TABLE_OBJ: [u8; 424] = [
0x64, 0x86, 0x03, 0x00, 0xf3, 0xcb, 0x20, 0x69, 0x02, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x24, 0x6d, 0x6e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x60,
0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50, 0xc0, 0x2e, 0x64, 0x65, 0x62, 0x75,
0x67, 0x24, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x10, 0x42, 0xc3, 0x04, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x68,
0x00, 0x00, 0x00, 0x2d, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x43, 0x3a, 0x5c, 0x55,
0x73, 0x65, 0x72, 0x73, 0x5c, 0x43, 0x6f, 0x6e, 0x74, 0x6f, 0x73, 0x6f, 0x5c, 0x77, 0x69,
0x6e, 0x2d, 0x6b, 0x65, 0x78, 0x70, 0x5c, 0x61, 0x63, 0x6c, 0x5f, 0x65, 0x64, 0x69, 0x74,
0x2e, 0x6f, 0x62, 0x6a, 0x00, 0x37, 0x00, 0x3c, 0x11, 0x03, 0x02, 0x00, 0x00, 0xd0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x2a, 0x00, 0x84, 0x86, 0x00,
0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20,
0x4d, 0x61, 0x63, 0x72, 0x6f, 0x20, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72,
0x00, 0x00, 0x00, 0x40, 0x63, 0x6f, 0x6d, 0x70, 0x2e, 0x69, 0x64, 0x84, 0x86, 0x03, 0x01,
0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x40, 0x66, 0x65, 0x61, 0x74, 0x2e, 0x30, 0x30, 0x10,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x24,
0x6d, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x24, 0x53, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x01, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x6d, 0x70,
0x74, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00,
0x04, 0x00, 0x00, 0x00,
];
#[test]
fn parse_coff_with_empty_string_table() {
let coff = Coff::parse(&EMPTY_STRING_TABLE_OBJ).unwrap();
assert_eq!(coff.header.number_of_sections, 3);
assert_eq!(coff.header.number_of_symbol_table, 9);
assert!(coff.strings.is_some());
let strings = coff.strings.unwrap();
assert_eq!(strings.len(), 0); }
}