Skip to content

Commit

Permalink
Revert "pe: add Terse Executable (TE) support (#397)"
Browse files Browse the repository at this point in the history
This reverts commit 47ee850.
  • Loading branch information
m4b committed Apr 29, 2024
1 parent d2b962b commit 91e4f9b
Show file tree
Hide file tree
Showing 11 changed files with 4 additions and 377 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ version = "0.12"
default_features = false

[features]
default = ["std", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "te", "archive", "endian_fd"]
default = ["std", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive", "endian_fd"]
std = ["alloc", "scroll/std"]
alloc = ["scroll/derive", "log"]
endian_fd = ["alloc"]
Expand All @@ -49,7 +49,6 @@ mach32 = ["alloc", "endian_fd", "archive"]
mach64 = ["alloc", "endian_fd", "archive"]
pe32 = ["alloc", "endian_fd"]
pe64 = ["alloc", "endian_fd"]
te = ["alloc", "endian_fd"]
archive = ["alloc"]

[badges.travis-ci]
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ Here are some things you could do with this crate (or help to implement so they
* mach32 - 32-bit mach-o `repr(C)` struct defs
* pe32 - 32-bit PE `repr(C)` struct defs
* pe64 - 64-bit PE `repr(C)` struct defs
+ te - Terse Executable (TE) `repr(C)` struct defs
* archive - a Unix Archive parser
* endian_fd - parses according to the endianness in the binary
* std - to allow `no_std` environments
Expand Down
7 changes: 1 addition & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,14 @@ pub enum Hint {
Mach(HintData),
MachFat(usize),
PE,
TE,
COFF,
Archive,
Unknown(u64),
}

macro_rules! if_everything {
($($i:item)*) => ($(
#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "te", feature = "mach64", feature = "mach32", feature = "archive"))]
#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
$i
)*)
}
Expand All @@ -263,7 +262,6 @@ if_everything! {
} else {
match *&bytes[0..2].pread_with::<u16>(0, LE)? {
pe::header::DOS_MAGIC => Ok(Hint::PE),
pe::header::TE_MAGIC => Ok(Hint::TE),
pe::header::COFF_MACHINE_X86 |
pe::header::COFF_MACHINE_X86_64 |
pe::header::COFF_MACHINE_ARM64 => Ok(Hint::COFF),
Expand Down Expand Up @@ -292,8 +290,6 @@ if_everything! {
Elf(elf::Elf<'a>),
/// A PE32/PE32+!
PE(pe::PE<'a>),
/// A TE!
TE(pe::TE<'a>),
/// A COFF
COFF(pe::Coff<'a>),
/// A 32/64-bit Mach-o binary _OR_ it is a multi-architecture binary container!
Expand All @@ -313,7 +309,6 @@ if_everything! {
Hint::Mach(_) | Hint::MachFat(_) => Ok(Object::Mach(mach::Mach::parse(bytes)?)),
Hint::Archive => Ok(Object::Archive(archive::Archive::parse(bytes)?)),
Hint::PE => Ok(Object::PE(pe::PE::parse(bytes)?)),
Hint::TE => Ok(Object::TE(pe::TE::parse(bytes)?)),
Hint::COFF => Ok(Object::COFF(pe::Coff::parse(bytes)?)),
Hint::Unknown(magic) => Ok(Object::Unknown(magic)),
}
Expand Down
2 changes: 1 addition & 1 deletion src/pe/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl ImageDebugDirectory {
)
}

pub(crate) fn parse_with_opts(
fn parse_with_opts(
bytes: &[u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
Expand Down
143 changes: 1 addition & 142 deletions src/pe/header.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error;
use crate::pe::{data_directories, optional_header, section_table, symbol};
use crate::pe::{optional_header, section_table, symbol};
use crate::strtab;
use alloc::vec::Vec;
use log::debug;
Expand Down Expand Up @@ -835,147 +835,6 @@ impl ctx::TryIntoCtx<scroll::Endian> for Header {
}
}

/// The TE header is a reduced PE32/PE32+ header containing only fields
/// required for execution in the Platform Initialization
/// ([PI](https://uefi.org/specs/PI/1.8/V1_Introduction.html)) architecture.
/// The TE header is described in this specification:
/// <https://uefi.org/specs/PI/1.8/V1_TE_Image.html#te-header>
#[cfg(feature = "te")]
#[repr(C)]
#[derive(Debug, Default, PartialEq, Copy, Clone, Pread, Pwrite)]
pub struct TeHeader {
/// Te signature, always [TE_MAGIC]
pub signature: u16,
/// The machine type
pub machine: u16,
/// The number of sections
pub number_of_sections: u8,
/// The subsystem
pub subsystem: u8,
/// the amount of bytes stripped from the header when converting from a
/// PE32/PE32+ header to a TE header. Used to resolve addresses
pub stripped_size: u16,
/// The entry point of the binary
pub entry_point: u32,
/// The base of the code section
pub base_of_code: u32,
/// The image base
pub image_base: u64,
/// The size and address of the relocation directory
pub reloc_dir: data_directories::DataDirectory,
/// The size and address of the debug directory
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 {
/// Parse the TE header from the given bytes.
pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
let mut header: TeHeader = bytes.gread_with(offset, scroll::LE)?;
let adj_offset = header.stripped_size as u32 - core::mem::size_of::<TeHeader>() as u32;
header.fixup_header(adj_offset);
Ok(header)
}

/// Parse the sections from the TE 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;

// a section table is at least 40 bytes
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)
}

// Adjust addresses in the header to account for the stripped size
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);
}

// Adjust addresses in the section to account for the stripped size
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);
}
}
}

/// Convert machine to str representation. Any case of "COFF_UNKNOWN"
/// should be expected to change to a more specific value.
pub fn machine_to_str(machine: u16) -> &'static str {
Expand Down
92 changes: 0 additions & 92 deletions src/pe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,98 +467,6 @@ impl<'a> ctx::TryIntoCtx<scroll::Endian> for PE<'a> {
}
}

/// An analyzed TE binary
///
/// A TE binary is a PE/PE32+ binary that has had it's header stripped and
/// re-formatted to the TE specification. This presents a challenge for
/// parsing, as all relative addresses (RVAs) are not updated to take this into
/// account, and are thus incorrect. The parsing of a TE must take this into
/// account by using the [header::TeHeader::stripped_size`] field of the TE
/// header to adjust the RVAs during parsing.
#[cfg(feature = "te")]
#[derive(Debug)]
pub struct TE<'a> {
/// The TE header
pub header: header::TeHeader,
/// A list of the sections in this TE binary
pub sections: Vec<section_table::SectionTable>,
/// Debug information, contained in the PE header
pub debug_data: debug::DebugData<'a>,
/// The offset to apply to addresses not parsed by the TE parser
/// itself: [header::TeHeader::stripped_size] - size_of::<[header::TeHeader]>()
pub rva_offset: usize,
}

#[cfg(feature = "te")]
impl<'a> TE<'a> {
/// Reads a TE binary from the underlying `bytes`
pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
let opts = &options::ParseOptions {
resolve_rva: false,
parse_attribute_certificates: false,
};

let mut offset = 0;

// Parse the TE header and adjust the offsets
let header = header::TeHeader::parse(bytes, &mut offset)?;
let rva_offset = header.stripped_size as usize - core::mem::size_of::<header::TeHeader>();

// Parse the sections and adjust the offsets
let sections = header.sections(bytes, &mut offset)?;

// Parse the debug data. Must adjust offsets before parsing the image_debug_directory
let mut debug_data = debug::DebugData::default();
debug_data.image_debug_directory = debug::ImageDebugDirectory::parse_with_opts(
bytes,
header.debug_dir,
&sections,
0,
opts,
)?;
TE::fixup_debug_data(&mut debug_data, rva_offset as u32);
debug_data.codeview_pdb70_debug_info = debug::CodeviewPDB70DebugInfo::parse_with_opts(
bytes,
&debug_data.image_debug_directory,
opts,
)?;

Ok(TE {
header,
sections,
debug_data,
rva_offset,
})
}

/// Adjust all addresses in the TE binary debug data.
fn fixup_debug_data(dd: &mut debug::DebugData, rva_offset: u32) {
debug!(
"ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
dd.image_debug_directory.address_of_raw_data,
dd.image_debug_directory
.address_of_raw_data
.wrapping_sub(rva_offset),
);
dd.image_debug_directory.address_of_raw_data = dd
.image_debug_directory
.address_of_raw_data
.wrapping_sub(rva_offset);

debug!(
"ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
dd.image_debug_directory.pointer_to_raw_data,
dd.image_debug_directory
.pointer_to_raw_data
.wrapping_sub(rva_offset),
);
dd.image_debug_directory.pointer_to_raw_data = dd
.image_debug_directory
.pointer_to_raw_data
.wrapping_sub(rva_offset);
}
}

/// An analyzed COFF object
#[derive(Debug)]
pub struct Coff<'a> {
Expand Down
24 changes: 0 additions & 24 deletions tests/bins/te/README.md

This file was deleted.

Binary file removed tests/bins/te/test_image.te
Binary file not shown.
Binary file removed tests/bins/te/test_image_loaded.bin
Binary file not shown.
Binary file removed tests/bins/te/test_image_relocated.bin
Binary file not shown.
Loading

0 comments on commit 91e4f9b

Please sign in to comment.