| use crate::error::{self, Error}; |
| use crate::pe::relocation; |
| use alloc::string::{String, ToString}; |
| use scroll::{ctx, Pread, Pwrite}; |
| |
| #[repr(C)] |
| #[derive(Debug, PartialEq, Clone, Default)] |
| pub struct SectionTable { |
| pub name: [u8; 8], |
| pub real_name: Option<String>, |
| 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, |
| } |
| |
| pub const SIZEOF_SECTION_TABLE: usize = 8 * 5; |
| |
| // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L70 |
| // Decodes a string table entry in base 64 (//AAAAAA). Expects string without |
| // prefixed slashes. |
| fn base64_decode_string_entry(s: &str) -> Result<usize, ()> { |
| assert!(s.len() <= 6, "String too long, possible overflow."); |
| |
| let mut val = 0; |
| for c in s.bytes() { |
| let v = if b'A' <= c && c <= b'Z' { |
| // 00..=25 |
| c - b'A' |
| } else if b'a' <= c && c <= b'z' { |
| // 26..=51 |
| c - b'a' + 26 |
| } else if b'0' <= c && c <= b'9' { |
| // 52..=61 |
| c - b'0' + 52 |
| } else if c == b'+' { |
| // 62 |
| 62 |
| } else if c == b'/' { |
| // 63 |
| 63 |
| } else { |
| return Err(()); |
| }; |
| val = val * 64 + v as usize; |
| } |
| Ok(val) |
| } |
| |
| impl SectionTable { |
| pub fn parse( |
| bytes: &[u8], |
| offset: &mut usize, |
| string_table_offset: usize, |
| ) -> error::Result<Self> { |
| let mut table = SectionTable::default(); |
| let mut name = [0u8; 8]; |
| name.copy_from_slice(bytes.gread_with(offset, 8)?); |
| |
| table.name = name; |
| table.virtual_size = bytes.gread_with(offset, scroll::LE)?; |
| table.virtual_address = bytes.gread_with(offset, scroll::LE)?; |
| table.size_of_raw_data = bytes.gread_with(offset, scroll::LE)?; |
| table.pointer_to_raw_data = bytes.gread_with(offset, scroll::LE)?; |
| table.pointer_to_relocations = bytes.gread_with(offset, scroll::LE)?; |
| table.pointer_to_linenumbers = bytes.gread_with(offset, scroll::LE)?; |
| table.number_of_relocations = bytes.gread_with(offset, scroll::LE)?; |
| table.number_of_linenumbers = bytes.gread_with(offset, scroll::LE)?; |
| table.characteristics = bytes.gread_with(offset, scroll::LE)?; |
| |
| if let Some(idx) = table.name_offset()? { |
| table.real_name = Some(bytes.pread::<&str>(string_table_offset + idx)?.to_string()); |
| } |
| Ok(table) |
| } |
| |
| pub fn name_offset(&self) -> error::Result<Option<usize>> { |
| // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L1054 |
| if self.name[0] == b'/' { |
| let idx: usize = if self.name[1] == b'/' { |
| let b64idx = self.name.pread::<&str>(2)?; |
| base64_decode_string_entry(b64idx).map_err(|_| { |
| Error::Malformed(format!( |
| "Invalid indirect section name //{}: base64 decoding failed", |
| b64idx |
| )) |
| })? |
| } else { |
| let name = self.name.pread::<&str>(1)?; |
| name.parse().map_err(|err| { |
| Error::Malformed(format!("Invalid indirect section name /{}: {}", name, err)) |
| })? |
| }; |
| Ok(Some(idx)) |
| } else { |
| Ok(None) |
| } |
| } |
| |
| #[allow(clippy::useless_let_if_seq)] |
| pub fn set_name_offset(&mut self, mut idx: usize) -> error::Result<()> { |
| if idx <= 9_999_999 { |
| // 10^7 - 1 |
| // write!(&mut self.name[1..], "{}", idx) without using io::Write. |
| // We write into a temporary since we calculate digits starting at the right. |
| let mut name = [0; 7]; |
| let mut len = 0; |
| if idx == 0 { |
| name[6] = b'0'; |
| len = 1; |
| } else { |
| while idx != 0 { |
| let rem = (idx % 10) as u8; |
| idx /= 10; |
| name[6 - len] = b'0' + rem; |
| len += 1; |
| } |
| } |
| self.name = [0; 8]; |
| self.name[0] = b'/'; |
| self.name[1..][..len].copy_from_slice(&name[7 - len..]); |
| Ok(()) |
| } else if idx as u64 <= 0xfff_fff_fff { |
| // 64^6 - 1 |
| self.name[0] = b'/'; |
| self.name[1] = b'/'; |
| for i in 0..6 { |
| let rem = (idx % 64) as u8; |
| idx /= 64; |
| let c = match rem { |
| 0..=25 => b'A' + rem, |
| 26..=51 => b'a' + rem - 26, |
| 52..=61 => b'0' + rem - 52, |
| 62 => b'+', |
| 63 => b'/', |
| _ => unreachable!(), |
| }; |
| self.name[7 - i] = c; |
| } |
| Ok(()) |
| } else { |
| Err(Error::Malformed(format!( |
| "Invalid section name offset: {}", |
| idx |
| ))) |
| } |
| } |
| |
| pub fn name(&self) -> error::Result<&str> { |
| match self.real_name.as_ref() { |
| Some(s) => Ok(s), |
| None => Ok(self.name.pread(0)?), |
| } |
| } |
| |
| pub fn relocations<'a>(&self, bytes: &'a [u8]) -> error::Result<relocation::Relocations<'a>> { |
| let offset = self.pointer_to_relocations as usize; |
| let number = self.number_of_relocations as usize; |
| relocation::Relocations::parse(bytes, offset, number) |
| } |
| } |
| |
| impl ctx::SizeWith<scroll::Endian> for SectionTable { |
| fn size_with(_ctx: &scroll::Endian) -> usize { |
| SIZEOF_SECTION_TABLE |
| } |
| } |
| |
| impl ctx::TryIntoCtx<scroll::Endian> for SectionTable { |
| 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(&self.name[..], offset)?; |
| bytes.gwrite_with(self.virtual_size, offset, ctx)?; |
| bytes.gwrite_with(self.virtual_address, offset, ctx)?; |
| bytes.gwrite_with(self.size_of_raw_data, offset, ctx)?; |
| bytes.gwrite_with(self.pointer_to_raw_data, offset, ctx)?; |
| bytes.gwrite_with(self.pointer_to_relocations, offset, ctx)?; |
| bytes.gwrite_with(self.pointer_to_linenumbers, offset, ctx)?; |
| bytes.gwrite_with(self.number_of_relocations, offset, ctx)?; |
| bytes.gwrite_with(self.number_of_linenumbers, offset, ctx)?; |
| bytes.gwrite_with(self.characteristics, offset, ctx)?; |
| Ok(SIZEOF_SECTION_TABLE) |
| } |
| } |
| |
| impl ctx::IntoCtx<scroll::Endian> for SectionTable { |
| fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { |
| bytes.pwrite_with(self, 0, ctx).unwrap(); |
| } |
| } |
| |
| /// The section should not be padded to the next boundary. This flag is obsolete and is replaced |
| /// by `IMAGE_SCN_ALIGN_1BYTES`. This is valid only for object files. |
| pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008; |
| /// The section contains executable code. |
| pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020; |
| /// The section contains initialized data. |
| pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040; |
| /// The section contains uninitialized data. |
| pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080; |
| pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100; |
| /// The section contains comments or other information. The .drectve section has this type. |
| /// This is valid for object files only. |
| pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200; |
| /// The section will not become part of the image. This is valid only for object files. |
| pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800; |
| /// The section contains COMDAT data. This is valid only for object files. |
| pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000; |
| /// The section contains data referenced through the global pointer (GP). |
| pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000; |
| pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000; |
| pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000; |
| pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000; |
| pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000; |
| |
| pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000; |
| pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000; |
| pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000; |
| pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000; |
| pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000; |
| pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000; |
| pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000; |
| pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000; |
| pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000; |
| pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000; |
| pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000; |
| pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000; |
| pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000; |
| pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000; |
| pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000; |
| |
| /// The section contains extended relocations. |
| pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000; |
| /// The section can be discarded as needed. |
| pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000; |
| /// The section cannot be cached. |
| pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000; |
| /// The section is not pageable. |
| pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000; |
| /// The section can be shared in memory. |
| pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000; |
| /// The section can be executed as code. |
| pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000; |
| /// The section can be read. |
| pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000; |
| /// The section can be written to. |
| pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000; |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn set_name_offset() { |
| let mut section = SectionTable::default(); |
| for &(offset, name) in [ |
| (0usize, b"/0\0\0\0\0\0\0"), |
| (1, b"/1\0\0\0\0\0\0"), |
| (9_999_999, b"/9999999"), |
| (10_000_000, b"//AAmJaA"), |
| #[cfg(target_pointer_width = "64")] |
| (0xfff_fff_fff, b"////////"), |
| ] |
| .iter() |
| { |
| section.set_name_offset(offset).unwrap(); |
| assert_eq!(§ion.name, name); |
| assert_eq!(section.name_offset().unwrap(), Some(offset)); |
| } |
| #[cfg(target_pointer_width = "64")] |
| assert!(section.set_name_offset(0x1_000_000_000).is_err()); |
| } |
| } |