use object::ReadRef as _;
pub use object::{
pe::{
IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_ARCHITECTURE,
IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, IMAGE_DIRECTORY_ENTRY_DEBUG,
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_GLOBALPTR, IMAGE_DIRECTORY_ENTRY_IAT,
IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
IMAGE_DIRECTORY_ENTRY_RESOURCE, IMAGE_DIRECTORY_ENTRY_SECURITY, IMAGE_DIRECTORY_ENTRY_TLS,
IMAGE_DOS_SIGNATURE, IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC,
IMAGE_NT_SIGNATURE, IMAGE_NUMBEROF_DIRECTORY_ENTRIES, IMAGE_SIZEOF_SHORT_NAME,
},
read::pe::{Export, ExportTable, ExportTarget},
};
use super::error::PeError;
trait ToEndian<E: ::object::Endian> {
type Output;
fn to_endian(&self, endian: E) -> Self::Output;
}
impl<E: ::object::Endian> ToEndian<E> for ::object::U16<E> {
type Output = u16;
fn to_endian(&self, endian: E) -> Self::Output {
self.get(endian)
}
}
impl<E: ::object::Endian> ToEndian<E> for ::object::U32<E> {
type Output = u32;
fn to_endian(&self, endian: E) -> Self::Output {
self.get(endian)
}
}
impl<E: ::object::Endian> ToEndian<E> for ::object::U64<E> {
type Output = u64;
fn to_endian(&self, endian: E) -> Self::Output {
self.get(endian)
}
}
impl<E: ::object::Endian> ToEndian<E> for u8 {
type Output = u8;
fn to_endian(&self, _endian: E) -> Self::Output {
*self
}
}
impl<E: ::object::Endian, const N: usize> ToEndian<E> for [u8; N] {
type Output = [u8; N];
fn to_endian(&self, _endian: E) -> Self::Output {
*self
}
}
impl<E: ::object::Endian, const N: usize> ToEndian<E> for [::object::U16<E>; N] {
type Output = [u16; N];
fn to_endian(&self, endian: E) -> Self::Output {
std::array::from_fn(|i| self[i].get(endian))
}
}
macro_rules! impl_struct {
(@parse
from = []
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [#[from($from:ty)] $($rest:tt)*]
) => {
impl_struct!(@parse
from = [$from]
doc = [$($doc)*]
derive = [$($derive)*]
attrs = [$($attrs)*]
rest = [$($rest)*]
);
};
(@parse
from = [$_first:ty]
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [#[from($_dup:ty)] $($rest:tt)*]
) => {
compile_error!("impl_struct!: duplicate `#[from(...)]` attribute");
};
(@parse
from = [$($from:tt)*]
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [#[doc = $d:literal] $($rest:tt)*]
) => {
impl_struct!(@parse
from = [$($from)*]
doc = [$($doc)* #[doc = $d]]
derive = [$($derive)*]
attrs = [$($attrs)*]
rest = [$($rest)*]
);
};
(@parse
from = [$($from:tt)*]
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [#[derive($($d:path),* $(,)?)] $($rest:tt)*]
) => {
impl_struct!(@parse
from = [$($from)*]
doc = [$($doc)*]
derive = [$($derive)* $($d,)*]
attrs = [$($attrs)*]
rest = [$($rest)*]
);
};
(@parse
from = [$($from:tt)*]
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [#[$attr:meta] $($rest:tt)*]
) => {
impl_struct!(@parse
from = [$($from)*]
doc = [$($doc)*]
derive = [$($derive)*]
attrs = [$($attrs)* #[$attr]]
rest = [$($rest)*]
);
};
(@parse
from = [$from_ty:ty]
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [
$vis:vis struct $name:ident {
$(
$(#[$field_attr:meta])*
$field_vis:vis $field:ident : $field_ty:ty
),* $(,)?
}
]
) => {
$($doc)*
#[derive(
$($derive)*
::zerocopy::FromBytes,
::zerocopy::IntoBytes,
::zerocopy::Immutable,
::zerocopy::KnownLayout,
)]
#[repr(C, packed)]
$($attrs)*
$vis struct $name {
$(
$(#[$field_attr])*
$field_vis $field: $field_ty,
)*
}
impl ::core::convert::From<$from_ty> for $name {
fn from(value: $from_ty) -> Self {
Self {
$(
$field: value.$field.to_endian(::object::LittleEndian),
)*
}
}
}
const _: () = {
use ::core::mem::{align_of, size_of};
assert!(
size_of::<$name>() == size_of::<$from_ty>(),
concat!(stringify!($name), " size mismatch"),
);
assert!(
align_of::<$name>() == align_of::<$from_ty>(),
concat!(stringify!($name), " alignment mismatch"),
);
};
$(
const _: () = {
use ::core::mem::offset_of;
assert!(
offset_of!($name, $field) == offset_of!($from_ty, $field),
concat!(stringify!($name), " field offset mismatch: ", stringify!($field)),
);
};
)*
};
(@parse
from = []
doc = [$($doc:tt)*]
derive = [$($derive:tt)*]
attrs = [$($attrs:tt)*]
rest = [$vis:vis struct $name:ident { $($body:tt)* }]
) => {
compile_error!(concat!(
"impl_struct!: missing `#[from(SourceType)]` attribute on `",
stringify!($name),
"`",
));
};
($($input:tt)*) => {
impl_struct!(@parse
from = []
doc = []
derive = []
attrs = []
rest = [$($input)*]
);
};
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageDosHeader)]
pub struct ImageDosHeader {
pub e_magic: u16,
pub e_cblp: u16,
pub e_cp: u16,
pub e_crlc: u16,
pub e_cparhdr: u16,
pub e_minalloc: u16,
pub e_maxalloc: u16,
pub e_ss: u16,
pub e_sp: u16,
pub e_csum: u16,
pub e_ip: u16,
pub e_cs: u16,
pub e_lfarlc: u16,
pub e_ovno: u16,
pub e_res: [u16; 4],
pub e_oemid: u16,
pub e_oeminfo: u16,
pub e_res2: [u16; 10],
pub e_lfanew: u32,
}
}
impl ImageDosHeader {
pub fn parse(data: &[u8], offset: &mut u64) -> Result<Self, PeError> {
let dos_header = data
.read_at::<::object::pe::ImageDosHeader>(0)
.copied()
.map_err(|_| PeError::InvalidDosHeader)?;
if dos_header.e_magic.get(::object::LittleEndian) != IMAGE_DOS_SIGNATURE {
return Err(PeError::InvalidDosMagic);
}
*offset = dos_header.nt_headers_offset() as u64;
Ok(dos_header.into())
}
pub fn nt_headers_offset(&self) -> u32 {
self.e_lfanew
}
}
#[derive(Debug, Clone, Copy)]
pub struct ImageNtHeaders {
pub signature: u32,
pub file_header: ImageFileHeader,
pub optional_header: ImageOptionalHeader,
}
impl ImageNtHeaders {
pub fn parse(data: &[u8], offset: &mut u64) -> Result<Self, PeError> {
let magic =
::object::read::pe::optional_header_magic(data).map_err(|_| PeError::InvalidPeMagic)?;
match magic {
IMAGE_NT_OPTIONAL_HDR32_MAGIC => {
Self::parse_inner::<::object::pe::ImageNtHeaders32>(data, offset)
}
IMAGE_NT_OPTIONAL_HDR64_MAGIC => {
Self::parse_inner::<::object::pe::ImageNtHeaders64>(data, offset)
}
_ => Err(PeError::InvalidPeMagic),
}
}
fn parse_inner<Pe>(data: &[u8], offset: &mut u64) -> Result<Self, PeError>
where
Self: From<Pe>,
Pe: ::object::read::pe::ImageNtHeaders,
{
let nt_headers = data
.read::<Pe>(offset)
.copied()
.map_err(|_| PeError::InvalidNtHeaders)?;
if nt_headers.signature() != IMAGE_NT_SIGNATURE {
return Err(PeError::InvalidPeMagic);
}
if !nt_headers.is_valid_optional_magic() {
return Err(PeError::InvalidOptionalHeaderMagic);
}
Ok(nt_headers.into())
}
}
impl From<::object::pe::ImageNtHeaders32> for ImageNtHeaders {
fn from(value: ::object::pe::ImageNtHeaders32) -> Self {
Self {
signature: value.signature.get(::object::LittleEndian),
file_header: value.file_header.into(),
optional_header: value.optional_header.into(),
}
}
}
impl From<::object::pe::ImageNtHeaders64> for ImageNtHeaders {
fn from(value: ::object::pe::ImageNtHeaders64) -> Self {
Self {
signature: value.signature.get(::object::LittleEndian),
file_header: value.file_header.into(),
optional_header: value.optional_header.into(),
}
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageFileHeader)]
pub struct ImageFileHeader {
pub machine: u16,
pub number_of_sections: u16,
pub time_date_stamp: u32,
pub pointer_to_symbol_table: u32,
pub number_of_symbols: u32,
pub size_of_optional_header: u16,
pub characteristics: u16,
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageOptionalHeader32)]
pub struct ImageOptionalHeader32 {
pub magic: u16,
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub size_of_code: u32,
pub size_of_initialized_data: u32,
pub size_of_uninitialized_data: u32,
pub address_of_entry_point: u32,
pub base_of_code: u32,
pub base_of_data: u32,
pub image_base: u32,
pub section_alignment: u32,
pub file_alignment: u32,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub win32_version_value: u32,
pub size_of_image: u32,
pub size_of_headers: u32,
pub check_sum: u32,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u32,
pub size_of_stack_commit: u32,
pub size_of_heap_reserve: u32,
pub size_of_heap_commit: u32,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageOptionalHeader64)]
pub struct ImageOptionalHeader64 {
pub magic: u16,
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub size_of_code: u32,
pub size_of_initialized_data: u32,
pub size_of_uninitialized_data: u32,
pub address_of_entry_point: u32,
pub base_of_code: u32,
pub image_base: u64,
pub section_alignment: u32,
pub file_alignment: u32,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub win32_version_value: u32,
pub size_of_image: u32,
pub size_of_headers: u32,
pub check_sum: u32,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u64,
pub size_of_stack_commit: u64,
pub size_of_heap_reserve: u64,
pub size_of_heap_commit: u64,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
}
}
#[derive(Debug, Clone, Copy)]
pub enum ImageOptionalHeader {
ImageOptionalHeader32(ImageOptionalHeader32),
ImageOptionalHeader64(ImageOptionalHeader64),
}
impl From<::object::pe::ImageOptionalHeader32> for ImageOptionalHeader {
fn from(value: ::object::pe::ImageOptionalHeader32) -> Self {
Self::ImageOptionalHeader32(value.into())
}
}
impl From<::object::pe::ImageOptionalHeader64> for ImageOptionalHeader {
fn from(value: ::object::pe::ImageOptionalHeader64) -> Self {
Self::ImageOptionalHeader64(value.into())
}
}
impl ImageOptionalHeader {
pub fn magic(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.magic,
Self::ImageOptionalHeader64(hdr64) => hdr64.magic,
}
}
pub fn major_linker_version(&self) -> u8 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.major_linker_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.major_linker_version,
}
}
pub fn minor_linker_version(&self) -> u8 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.minor_linker_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.minor_linker_version,
}
}
pub fn size_of_code(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_code,
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_code,
}
}
pub fn size_of_initialized_data(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_initialized_data,
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_initialized_data,
}
}
pub fn size_of_uninitialized_data(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_uninitialized_data,
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_uninitialized_data,
}
}
pub fn address_of_entry_point(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.address_of_entry_point,
Self::ImageOptionalHeader64(hdr64) => hdr64.address_of_entry_point,
}
}
pub fn base_of_code(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.base_of_code,
Self::ImageOptionalHeader64(hdr64) => hdr64.base_of_code,
}
}
pub fn base_of_data(&self) -> Option<u32> {
match self {
Self::ImageOptionalHeader32(hdr32) => Some(hdr32.base_of_data),
Self::ImageOptionalHeader64(_) => None,
}
}
pub fn image_base(&self) -> u64 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.image_base.into(),
Self::ImageOptionalHeader64(hdr64) => hdr64.image_base,
}
}
pub fn section_alignment(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.section_alignment,
Self::ImageOptionalHeader64(hdr64) => hdr64.section_alignment,
}
}
pub fn file_alignment(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.file_alignment,
Self::ImageOptionalHeader64(hdr64) => hdr64.file_alignment,
}
}
pub fn major_operating_system_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.major_operating_system_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.major_operating_system_version,
}
}
pub fn minor_operating_system_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.minor_operating_system_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.minor_operating_system_version,
}
}
pub fn major_image_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.major_image_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.major_image_version,
}
}
pub fn minor_image_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.minor_image_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.minor_image_version,
}
}
pub fn major_subsystem_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.major_subsystem_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.major_subsystem_version,
}
}
pub fn minor_subsystem_version(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.minor_subsystem_version,
Self::ImageOptionalHeader64(hdr64) => hdr64.minor_subsystem_version,
}
}
pub fn win32_version_value(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.win32_version_value,
Self::ImageOptionalHeader64(hdr64) => hdr64.win32_version_value,
}
}
pub fn size_of_image(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_image,
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_image,
}
}
pub fn size_of_headers(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_headers,
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_headers,
}
}
pub fn check_sum(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.check_sum,
Self::ImageOptionalHeader64(hdr64) => hdr64.check_sum,
}
}
pub fn subsystem(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.subsystem,
Self::ImageOptionalHeader64(hdr64) => hdr64.subsystem,
}
}
pub fn dll_characteristics(&self) -> u16 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.dll_characteristics,
Self::ImageOptionalHeader64(hdr64) => hdr64.dll_characteristics,
}
}
pub fn size_of_stack_reserve(&self) -> u64 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_stack_reserve.into(),
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_stack_reserve,
}
}
pub fn size_of_stack_commit(&self) -> u64 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_stack_commit.into(),
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_stack_commit,
}
}
pub fn size_of_heap_reserve(&self) -> u64 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_heap_reserve.into(),
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_heap_reserve,
}
}
pub fn size_of_heap_commit(&self) -> u64 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_heap_commit.into(),
Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_heap_commit,
}
}
pub fn loader_flags(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.loader_flags,
Self::ImageOptionalHeader64(hdr64) => hdr64.loader_flags,
}
}
pub fn number_of_rva_and_sizes(&self) -> u32 {
match self {
Self::ImageOptionalHeader32(hdr32) => hdr32.number_of_rva_and_sizes,
Self::ImageOptionalHeader64(hdr64) => hdr64.number_of_rva_and_sizes,
}
}
pub fn size(&self) -> usize {
match self {
Self::ImageOptionalHeader32(_) => size_of::<ImageOptionalHeader32>(),
Self::ImageOptionalHeader64(_) => size_of::<ImageOptionalHeader64>(),
}
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageSectionHeader)]
pub struct ImageSectionHeader {
pub name: [u8; IMAGE_SIZEOF_SHORT_NAME],
pub virtual_size: u32,
pub virtual_address: u32,
pub size_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub pointer_to_relocations: u32,
pub pointer_to_linenumbers: u32,
pub number_of_relocations: u16,
pub number_of_linenumbers: u16,
pub characteristics: u32,
}
}
impl ImageSectionHeader {
pub fn pe_file_range(&self) -> (u32, u32) {
let offset = self.pointer_to_raw_data;
let size = std::cmp::min(self.virtual_size, self.size_of_raw_data);
(offset, size)
}
pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
let section_va = self.virtual_address;
let offset = va.checked_sub(section_va)?;
let (section_offset, section_size) = self.pe_file_range();
if offset < section_size {
Some((section_offset.checked_add(offset)?, section_size - offset))
}
else {
None
}
}
pub fn pe_data<'data>(&self, data: &'data [u8]) -> Option<&'data [u8]> {
let (offset, size) = self.pe_file_range();
data.read_bytes_at(offset.into(), size.into()).ok()
}
pub fn pe_data_at<'data>(&self, data: &'data [u8], va: u32) -> Option<&'data [u8]> {
let (offset, size) = self.pe_file_range_at(va)?;
data.read_bytes_at(offset.into(), size.into()).ok()
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageDataDirectory)]
pub struct ImageDataDirectory {
pub virtual_address: u32,
pub size: u32,
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageDebugDirectory)]
pub struct ImageDebugDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub typ: u32,
pub size_of_data: u32,
pub address_of_raw_data: u32,
pub pointer_to_raw_data: u32,
}
}
impl_struct! {
#[derive(Debug, Clone, Copy)]
#[from(::object::pe::ImageRuntimeFunctionEntry)]
pub struct ImageRuntimeFunctionEntry {
pub begin_address: u32,
pub end_address: u32,
pub unwind_info_address_or_data: u32,
}
}