use std::fmt;
use scroll::ctx::TryFromCtx;
use scroll::Endian;
use crate::common::*;
const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x00000008;
const IMAGE_SCN_CNT_CODE: u32 = 0x00000020;
const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x00000040;
const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x00000080;
const IMAGE_SCN_LNK_OTHER: u32 = 0x00000100;
const IMAGE_SCN_LNK_INFO: u32 = 0x00000200;
const IMAGE_SCN_LNK_REMOVE: u32 = 0x00000800;
const IMAGE_SCN_LNK_COMDAT: u32 = 0x00001000;
const IMAGE_SCN_NO_DEFER_SPEC_EXC: u32 = 0x00004000;
const IMAGE_SCN_GPREL: u32 = 0x00008000;
const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x00020000;
const IMAGE_SCN_MEM_LOCKED: u32 = 0x00040000;
const IMAGE_SCN_MEM_PRELOAD: u32 = 0x00080000;
const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x00100000;
const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x00200000;
const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x00300000;
const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x00400000;
const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x00500000;
const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x00600000;
const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x00700000;
const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x00800000;
const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x00900000;
const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A00000;
const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B00000;
const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C00000;
const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D00000;
const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E00000;
const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x01000000;
const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x02000000;
const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x04000000;
const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x08000000;
const IMAGE_SCN_MEM_SHARED: u32 = 0x10000000;
const IMAGE_SCN_MEM_EXECUTE: u32 = 0x20000000;
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
#[derive(Clone, Copy, Eq, Default, PartialEq)]
pub struct SectionCharacteristics(pub u32);
impl SectionCharacteristics {
pub fn executable(self) -> bool {
(self.0 & IMAGE_SCN_CNT_CODE) > 0
}
pub fn initialized_data(self) -> bool {
(self.0 & IMAGE_SCN_CNT_INITIALIZED_DATA) > 0
}
pub fn uninitialized_data(self) -> bool {
(self.0 & IMAGE_SCN_CNT_UNINITIALIZED_DATA) > 0
}
pub fn other(self) -> bool {
(self.0 & IMAGE_SCN_LNK_OTHER) > 0
}
pub fn info(self) -> bool {
(self.0 & IMAGE_SCN_LNK_INFO) > 0
}
pub fn remove(self) -> bool {
(self.0 & IMAGE_SCN_LNK_REMOVE) > 0
}
pub fn comdat(self) -> bool {
(self.0 & IMAGE_SCN_LNK_COMDAT) > 0
}
pub fn defer_speculative_exceptions(self) -> bool {
(self.0 & IMAGE_SCN_NO_DEFER_SPEC_EXC) > 0
}
pub fn global_pointer_relative(self) -> bool {
(self.0 & IMAGE_SCN_GPREL) > 0
}
pub fn purgeable(self) -> bool {
(self.0 & IMAGE_SCN_MEM_PURGEABLE) > 0
}
pub fn locked(self) -> bool {
(self.0 & IMAGE_SCN_MEM_LOCKED) > 0
}
pub fn preload(self) -> bool {
(self.0 & IMAGE_SCN_MEM_PRELOAD) > 0
}
pub fn alignment(self) -> Option<u16> {
match self.0 & 0x00F00008 {
self::IMAGE_SCN_ALIGN_1BYTES => Some(1),
self::IMAGE_SCN_ALIGN_2BYTES => Some(2),
self::IMAGE_SCN_ALIGN_4BYTES => Some(4),
self::IMAGE_SCN_ALIGN_8BYTES => Some(8),
self::IMAGE_SCN_ALIGN_16BYTES => Some(16),
self::IMAGE_SCN_ALIGN_32BYTES => Some(32),
self::IMAGE_SCN_ALIGN_64BYTES => Some(64),
self::IMAGE_SCN_ALIGN_128BYTES => Some(128),
self::IMAGE_SCN_ALIGN_256BYTES => Some(256),
self::IMAGE_SCN_ALIGN_512BYTES => Some(512),
self::IMAGE_SCN_ALIGN_1024BYTES => Some(1024),
self::IMAGE_SCN_ALIGN_2048BYTES => Some(2048),
self::IMAGE_SCN_ALIGN_4096BYTES => Some(4096),
self::IMAGE_SCN_ALIGN_8192BYTES => Some(8192),
self::IMAGE_SCN_TYPE_NO_PAD => Some(1),
_ => None,
}
}
pub fn lnk_nreloc_ovfl(self) -> bool {
(self.0 & IMAGE_SCN_LNK_NRELOC_OVFL) > 0
}
pub fn discardable(self) -> bool {
(self.0 & IMAGE_SCN_MEM_DISCARDABLE) > 0
}
pub fn not_cached(self) -> bool {
(self.0 & IMAGE_SCN_MEM_NOT_CACHED) > 0
}
pub fn not_paged(self) -> bool {
(self.0 & IMAGE_SCN_MEM_NOT_PAGED) > 0
}
pub fn shared(self) -> bool {
(self.0 & IMAGE_SCN_MEM_SHARED) > 0
}
pub fn execute(self) -> bool {
(self.0 & IMAGE_SCN_MEM_EXECUTE) > 0
}
pub fn read(self) -> bool {
(self.0 & IMAGE_SCN_MEM_READ) > 0
}
pub fn write(self) -> bool {
(self.0 & IMAGE_SCN_MEM_WRITE) > 0
}
}
impl fmt::Debug for SectionCharacteristics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("ImageCharacteristics")
.field("executable", &self.executable())
.field("initialized_data", &self.initialized_data())
.field("uninitialized_data", &self.uninitialized_data())
.field("info", &self.info())
.field("remove", &self.remove())
.field("comdat", &self.comdat())
.field(
"defer_speculative_exceptions",
&self.defer_speculative_exceptions(),
)
.field("global_pointer_relative", &self.global_pointer_relative())
.field("purgeable", &self.purgeable())
.field("locked", &self.locked())
.field("preload", &self.preload())
.field("alignment", &self.alignment())
.field("lnk_nreloc_ovfl", &self.lnk_nreloc_ovfl())
.field("discardable", &self.discardable())
.field("not_cached", &self.not_cached())
.field("not_paged", &self.not_paged())
.field("shared", &self.shared())
.field("execute", &self.execute())
.field("read", &self.read())
.field("write", &self.write())
.finish()
} else {
f.debug_tuple("ImageCharacteristics")
.field(&format_args!("{:#x}", self.0))
.finish()
}
}
}
impl<'t> TryFromCtx<'t, Endian> for SectionCharacteristics {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let (value, size) = u32::try_from_ctx(this, le)?;
Ok((SectionCharacteristics(value), size))
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct ImageSectionHeader {
pub name: [u8; 8],
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_line_numbers: u32,
pub number_of_relocations: u16,
pub number_of_line_numbers: u16,
pub characteristics: SectionCharacteristics,
}
impl ImageSectionHeader {
pub(crate) fn parse(parse_buffer: &mut ParseBuffer<'_>) -> Result<Self> {
let name_bytes = parse_buffer.take(8)?;
Ok(Self {
name: [
name_bytes[0],
name_bytes[1],
name_bytes[2],
name_bytes[3],
name_bytes[4],
name_bytes[5],
name_bytes[6],
name_bytes[7],
],
virtual_size: parse_buffer.parse_u32()?,
virtual_address: parse_buffer.parse_u32()?,
size_of_raw_data: parse_buffer.parse_u32()?,
pointer_to_raw_data: parse_buffer.parse_u32()?,
pointer_to_relocations: parse_buffer.parse_u32()?,
pointer_to_line_numbers: parse_buffer.parse_u32()?,
number_of_relocations: parse_buffer.parse_u16()?,
number_of_line_numbers: parse_buffer.parse_u16()?,
characteristics: parse_buffer.parse()?,
})
}
pub fn name(&self) -> &str {
let end = self
.name
.iter()
.position(|ch| *ch == 0)
.unwrap_or(self.name.len());
std::str::from_utf8(&self.name[0..end]).unwrap_or("")
}
}
impl fmt::Debug for ImageSectionHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImageSectionHeader")
.field("name()", &self.name())
.field("virtual_size", &format_args!("{:#x}", self.virtual_size))
.field(
"virtual_address",
&format_args!("{:#x}", self.virtual_address),
)
.field("size_of_raw_data", &self.size_of_raw_data)
.field(
"pointer_to_raw_data",
&format_args!("{:#x}", self.pointer_to_raw_data),
)
.field(
"pointer_to_relocations",
&format_args!("{:#x}", self.pointer_to_relocations),
)
.field(
"pointer_to_line_numbers",
&format_args!("{:#x}", self.pointer_to_line_numbers),
)
.field("number_of_relocations", &self.number_of_relocations)
.field("number_of_line_numbers", &self.number_of_line_numbers)
.field("characteristics", &self.characteristics)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_section_characteristics() {
let bytes: Vec<u8> = vec![0x40, 0x00, 0x00, 0xC8];
let mut parse_buffer = ParseBuffer::from(bytes.as_slice());
let characteristics = parse_buffer
.parse::<SectionCharacteristics>()
.expect("parse");
assert_eq!(characteristics, SectionCharacteristics(0xc800_0040));
assert!(characteristics.initialized_data());
assert!(characteristics.not_paged());
assert!(characteristics.read());
assert!(characteristics.write());
assert_eq!(characteristics.alignment(), None);
}
#[test]
fn test_section_characteristics_nopad() {
let characteristics = SectionCharacteristics(IMAGE_SCN_TYPE_NO_PAD);
assert_eq!(characteristics.alignment(), Some(1));
}
#[test]
fn test_section_characteristics_alignment() {
let characteristics = SectionCharacteristics(IMAGE_SCN_ALIGN_64BYTES);
assert_eq!(characteristics.alignment(), Some(64));
}
#[test]
fn test_image_section_header() {
let bytes: Vec<u8> = vec![
0x2E, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x48, 0x35, 0x09, 0x00, 0x00, 0xD0,
0x1E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xA2, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC8,
];
let mut parse_buffer = ParseBuffer::from(bytes.as_slice());
let ish = ImageSectionHeader::parse(&mut parse_buffer).expect("parse");
assert_eq!(&ish.name, b".data\0\0\0");
assert_eq!(ish.name(), ".data");
assert_eq!(ish.virtual_size, 0x93548);
assert_eq!(ish.virtual_address, 0x001e_d000);
assert_eq!(ish.size_of_raw_data, 0xfe00);
assert_eq!(ish.pointer_to_raw_data, 0x001e_a200);
assert_eq!(ish.pointer_to_relocations, 0);
assert_eq!(ish.pointer_to_line_numbers, 0);
assert_eq!(ish.number_of_relocations, 0);
assert_eq!(ish.number_of_line_numbers, 0);
assert_eq!(ish.characteristics, SectionCharacteristics(0xc800_0040));
}
}