use crate::{container, error};
use crate::pe::data_directories;
use scroll::{Endian, LE, Pread, Pwrite, SizeWith, ctx};
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct StandardFields32 {
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 const SIZEOF_STANDARD_FIELDS_32: usize = 28;
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct StandardFields64 {
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 const SIZEOF_STANDARD_FIELDS_64: usize = 24;
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct StandardFields {
#[doc(alias = "Magic")]
pub magic: u16,
#[doc(alias = "MajorLinkerVersion")]
pub major_linker_version: u8,
#[doc(alias = "MinorLinkerVersion")]
pub minor_linker_version: u8,
#[doc(alias = "SizeOfCode")]
pub size_of_code: u64,
#[doc(alias = "SizeOfInitializedData")]
pub size_of_initialized_data: u64,
#[doc(alias = "SizeOfUninitializedData")]
pub size_of_uninitialized_data: u64,
pub address_of_entry_point: u32,
pub base_of_code: u64,
pub base_of_data: u32,
}
impl From<StandardFields32> for StandardFields {
fn from(fields: StandardFields32) -> Self {
StandardFields {
magic: fields.magic,
major_linker_version: fields.major_linker_version,
minor_linker_version: fields.minor_linker_version,
size_of_code: u64::from(fields.size_of_code),
size_of_initialized_data: u64::from(fields.size_of_initialized_data),
size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data),
address_of_entry_point: fields.address_of_entry_point,
base_of_code: u64::from(fields.base_of_code),
base_of_data: fields.base_of_data,
}
}
}
impl From<StandardFields> for StandardFields32 {
fn from(fields: StandardFields) -> Self {
StandardFields32 {
magic: fields.magic,
major_linker_version: fields.major_linker_version,
minor_linker_version: fields.minor_linker_version,
size_of_code: fields.size_of_code as u32,
size_of_initialized_data: fields.size_of_initialized_data as u32,
size_of_uninitialized_data: fields.size_of_uninitialized_data as u32,
address_of_entry_point: fields.address_of_entry_point as u32,
base_of_code: fields.base_of_code as u32,
base_of_data: fields.base_of_data,
}
}
}
impl From<StandardFields64> for StandardFields {
fn from(fields: StandardFields64) -> Self {
StandardFields {
magic: fields.magic,
major_linker_version: fields.major_linker_version,
minor_linker_version: fields.minor_linker_version,
size_of_code: u64::from(fields.size_of_code),
size_of_initialized_data: u64::from(fields.size_of_initialized_data),
size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data),
address_of_entry_point: fields.address_of_entry_point,
base_of_code: u64::from(fields.base_of_code),
base_of_data: 0,
}
}
}
impl From<StandardFields> for StandardFields64 {
fn from(fields: StandardFields) -> Self {
StandardFields64 {
magic: fields.magic,
major_linker_version: fields.major_linker_version,
minor_linker_version: fields.minor_linker_version,
size_of_code: fields.size_of_code as u32,
size_of_initialized_data: fields.size_of_initialized_data as u32,
size_of_uninitialized_data: fields.size_of_uninitialized_data as u32,
address_of_entry_point: fields.address_of_entry_point as u32,
base_of_code: fields.base_of_code as u32,
}
}
}
pub const MAGIC_32: u16 = 0x10b;
pub const MAGIC_64: u16 = 0x20b;
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct WindowsFields32 {
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,
}
pub const SIZEOF_WINDOWS_FIELDS_32: usize = 68;
pub const OFFSET_WINDOWS_FIELDS_32_CHECKSUM: usize = 36;
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct WindowsFields64 {
#[doc(alias = "ImageBase")]
pub image_base: u64,
#[doc(alias = "SectionAlignment")]
pub section_alignment: u32,
#[doc(alias = "FileAlignment")]
pub file_alignment: u32,
#[doc(alias = "MajorOperatingSystemVersion")]
pub major_operating_system_version: u16,
#[doc(alias = "MinorOperatingSystemVersion")]
pub minor_operating_system_version: u16,
#[doc(alias = "MajorImageVersion")]
pub major_image_version: u16,
#[doc(alias = "MinorImageVersion")]
pub minor_image_version: u16,
#[doc(alias = "MajorSubsystemVersion")]
pub major_subsystem_version: u16,
#[doc(alias = "MinorSubsystemVersion")]
pub minor_subsystem_version: u16,
#[doc(alias = "Win32VersionValue")]
pub win32_version_value: u32,
#[doc(alias = "SizeOfImage")]
pub size_of_image: u32,
#[doc(alias = "SizeOfHeaders")]
pub size_of_headers: u32,
#[doc(alias = "CheckSum")]
pub check_sum: u32,
#[doc(alias = "Subsystem")]
pub subsystem: u16,
#[doc(alias = "DllCharacteristics")]
pub dll_characteristics: u16,
#[doc(alias = "SizeOfStackReserve")]
pub size_of_stack_reserve: u64,
#[doc(alias = "SizeOfStackCommit")]
pub size_of_stack_commit: u64,
#[doc(alias = "SizeOfHeapReserve")]
pub size_of_heap_reserve: u64,
#[doc(alias = "SizeOfHeapCommit")]
pub size_of_heap_commit: u64,
#[doc(alias = "LoaderFlags")]
pub loader_flags: u32,
#[doc(alias = "NumberOfRvaAndSizes")]
pub number_of_rva_and_sizes: u32,
}
pub const SIZEOF_WINDOWS_FIELDS_64: usize = 88;
pub const OFFSET_WINDOWS_FIELDS_64_CHECKSUM: usize = 40;
impl From<WindowsFields32> for WindowsFields {
fn from(windows: WindowsFields32) -> Self {
WindowsFields {
image_base: u64::from(windows.image_base),
section_alignment: windows.section_alignment,
file_alignment: windows.file_alignment,
major_operating_system_version: windows.major_operating_system_version,
minor_operating_system_version: windows.minor_operating_system_version,
major_image_version: windows.major_image_version,
minor_image_version: windows.minor_image_version,
major_subsystem_version: windows.major_subsystem_version,
minor_subsystem_version: windows.minor_subsystem_version,
win32_version_value: windows.win32_version_value,
size_of_image: windows.size_of_image,
size_of_headers: windows.size_of_headers,
check_sum: windows.check_sum,
subsystem: windows.subsystem,
dll_characteristics: windows.dll_characteristics,
size_of_stack_reserve: u64::from(windows.size_of_stack_reserve),
size_of_stack_commit: u64::from(windows.size_of_stack_commit),
size_of_heap_reserve: u64::from(windows.size_of_heap_reserve),
size_of_heap_commit: u64::from(windows.size_of_heap_commit),
loader_flags: windows.loader_flags,
number_of_rva_and_sizes: windows.number_of_rva_and_sizes,
}
}
}
impl TryFrom<WindowsFields64> for WindowsFields32 {
type Error = crate::error::Error;
fn try_from(value: WindowsFields64) -> Result<Self, Self::Error> {
Ok(WindowsFields32 {
image_base: value.image_base.try_into()?,
section_alignment: value.section_alignment,
file_alignment: value.file_alignment,
major_operating_system_version: value.major_operating_system_version,
minor_operating_system_version: value.minor_operating_system_version,
major_image_version: value.major_image_version,
minor_image_version: value.minor_image_version,
major_subsystem_version: value.major_subsystem_version,
minor_subsystem_version: value.minor_subsystem_version,
win32_version_value: value.win32_version_value,
size_of_image: value.size_of_image,
size_of_headers: value.size_of_headers,
check_sum: value.check_sum,
subsystem: value.subsystem,
dll_characteristics: value.dll_characteristics,
size_of_stack_reserve: value.size_of_stack_reserve.try_into()?,
size_of_stack_commit: value.size_of_stack_commit.try_into()?,
size_of_heap_reserve: value.size_of_heap_reserve.try_into()?,
size_of_heap_commit: value.size_of_heap_commit.try_into()?,
loader_flags: value.loader_flags,
number_of_rva_and_sizes: value.number_of_rva_and_sizes,
})
}
}
pub type WindowsFields = WindowsFields64;
#[derive(Debug, PartialEq, Copy, Clone)]
#[doc(alias = "IMAGE_OPTIONAL_HEADER32")]
#[doc(alias = "IMAGE_OPTIONAL_HEADER64")]
pub struct OptionalHeader {
pub standard_fields: StandardFields,
pub windows_fields: WindowsFields,
pub data_directories: data_directories::DataDirectories,
}
pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: u16 = 0x10b;
pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x20b;
pub const IMAGE_ROM_OPTIONAL_HDR_MAGIC: u16 = 0x107;
impl OptionalHeader {
pub fn container(&self) -> error::Result<container::Container> {
match self.standard_fields.magic {
MAGIC_32 => Ok(container::Container::Little),
MAGIC_64 => Ok(container::Container::Big),
magic => Err(error::Error::BadMagic(u64::from(magic))),
}
}
}
impl<'a> ctx::TryFromCtx<'a, Endian> for OptionalHeader {
type Error = crate::error::Error;
fn try_from_ctx(bytes: &'a [u8], _: Endian) -> error::Result<(Self, usize)> {
let magic = bytes.pread_with::<u16>(0, LE)?;
let offset = &mut 0;
let (standard_fields, windows_fields): (StandardFields, WindowsFields) = match magic {
MAGIC_32 => {
let standard_fields = bytes.gread_with::<StandardFields32>(offset, LE)?.into();
let windows_fields = bytes.gread_with::<WindowsFields32>(offset, LE)?.into();
(standard_fields, windows_fields)
}
MAGIC_64 => {
let standard_fields = bytes.gread_with::<StandardFields64>(offset, LE)?.into();
let windows_fields = bytes.gread_with::<WindowsFields64>(offset, LE)?;
(standard_fields, windows_fields)
}
_ => return Err(error::Error::BadMagic(u64::from(magic))),
};
let data_directories = data_directories::DataDirectories::parse(
bytes,
windows_fields.number_of_rva_and_sizes as usize,
offset,
)?;
Ok((
OptionalHeader {
standard_fields,
windows_fields,
data_directories,
},
*offset,
))
}
}
impl ctx::TryIntoCtx<scroll::Endian> for OptionalHeader {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
match self.standard_fields.magic {
MAGIC_32 => {
bytes.gwrite_with::<StandardFields32>(self.standard_fields.into(), offset, ctx)?;
bytes.gwrite_with(WindowsFields32::try_from(self.windows_fields)?, offset, ctx)?;
bytes.gwrite_with(self.data_directories, offset, ctx)?;
}
MAGIC_64 => {
bytes.gwrite_with::<StandardFields64>(self.standard_fields.into(), offset, ctx)?;
bytes.gwrite_with(self.windows_fields, offset, ctx)?;
bytes.gwrite_with(self.data_directories, offset, ctx)?;
}
n => Err(Self::Error::BadMagic(n.into()))?,
}
Ok(*offset)
}
}
#[cfg(test)]
mod tests {
use super::*;
use scroll::{Pread, Pwrite};
#[test]
fn sizeof_standards32() {
assert_eq!(
::std::mem::size_of::<StandardFields32>(),
SIZEOF_STANDARD_FIELDS_32
);
}
#[test]
fn sizeof_windows32() {
assert_eq!(
::std::mem::size_of::<WindowsFields32>(),
SIZEOF_WINDOWS_FIELDS_32
);
}
#[test]
fn sizeof_standards64() {
assert_eq!(
::std::mem::size_of::<StandardFields64>(),
SIZEOF_STANDARD_FIELDS_64
);
}
#[test]
fn sizeof_windows64() {
assert_eq!(
::std::mem::size_of::<WindowsFields64>(),
SIZEOF_WINDOWS_FIELDS_64
);
}
const OPTIONAL_HEADER_BYTES: &[u8] = &[
11, 2, 14, 38, 0, 32, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 64, 23, 0, 0, 0, 16, 0, 0, 0, 0, 0,
64, 1, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0,
0, 0, 192, 0, 0, 0, 16, 0, 0, 117, 98, 1, 0, 2, 0, 96, 193, 0, 0, 8, 0, 0, 0, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 56, 0, 0, 160, 0, 0, 0, 0, 96, 0, 0, 16, 71, 0, 0, 0, 80,
0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 0, 0, 60, 0, 0, 0, 104, 51, 0, 0, 112, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 48, 0, 0,
64, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 49, 0, 0, 72, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const OPTIONAL_HEADER: OptionalHeader = OptionalHeader {
standard_fields: StandardFields {
magic: 523,
major_linker_version: 14,
minor_linker_version: 38,
size_of_code: 8192,
size_of_initialized_data: 36864,
size_of_uninitialized_data: 0,
address_of_entry_point: 5952,
base_of_code: 4096,
base_of_data: 0,
},
windows_fields: WindowsFields64 {
image_base: 5368709120,
section_alignment: 4096,
file_alignment: 4096,
major_operating_system_version: 10,
minor_operating_system_version: 0,
major_image_version: 10,
minor_image_version: 0,
major_subsystem_version: 10,
minor_subsystem_version: 0,
win32_version_value: 0,
size_of_image: 49152,
size_of_headers: 4096,
check_sum: 90741,
subsystem: 2,
dll_characteristics: 49504,
size_of_stack_reserve: 524288,
size_of_stack_commit: 8192,
size_of_heap_reserve: 1048576,
size_of_heap_commit: 4096,
loader_flags: 0,
number_of_rva_and_sizes: 16,
},
data_directories: data_directories::DataDirectories {
data_directories: [
None,
Some((
128,
data_directories::DataDirectory {
virtual_address: 14584,
size: 160,
},
)),
Some((
136,
data_directories::DataDirectory {
virtual_address: 24576,
size: 18192,
},
)),
Some((
144,
data_directories::DataDirectory {
virtual_address: 20480,
size: 264,
},
)),
None,
Some((
160,
data_directories::DataDirectory {
virtual_address: 45056,
size: 60,
},
)),
Some((
168,
data_directories::DataDirectory {
virtual_address: 13160,
size: 112,
},
)),
None,
None,
None,
Some((
200,
data_directories::DataDirectory {
virtual_address: 12304,
size: 320,
},
)),
None,
Some((
216,
data_directories::DataDirectory {
virtual_address: 12624,
size: 328,
},
)),
None,
None,
None,
],
},
};
#[test]
fn read() {
let optional_header: OptionalHeader = OPTIONAL_HEADER_BYTES
.pread(0)
.expect("failed to read optional header");
assert_eq!(optional_header, OPTIONAL_HEADER);
}
#[test]
fn write() {
let mut buf = [0_u8; 1024];
let len = buf
.pwrite(OPTIONAL_HEADER, 0)
.expect("failed to write optional header");
assert_eq!(
OPTIONAL_HEADER_BYTES,
buf.get(..len).expect("not enough data")
);
}
#[test]
fn read_and_write_consistent() {
let optional_header: OptionalHeader = OPTIONAL_HEADER_BYTES
.pread(0)
.expect("failed to read optional header");
let mut buf = [0_u8; 1024];
let len = buf
.pwrite(optional_header, 0)
.expect("failed to write optional_header");
assert_eq!(len, OPTIONAL_HEADER_BYTES.len());
assert_eq!(
OPTIONAL_HEADER_BYTES,
buf.get(..len).expect("not enough data")
);
}
}