1use crate::error::{self, Error};
2use crate::pe::relocation;
3use alloc::borrow::Cow;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use scroll::{ctx, Pread, Pwrite};
7
8#[repr(C)]
9#[derive(Debug, PartialEq, Clone, Default)]
10pub struct SectionTable {
11 pub name: [u8; 8],
12 pub real_name: Option<String>,
13 pub virtual_size: u32,
14 pub virtual_address: u32,
15 pub size_of_raw_data: u32,
16 pub pointer_to_raw_data: u32,
17 pub pointer_to_relocations: u32,
18 pub pointer_to_linenumbers: u32,
19 pub number_of_relocations: u16,
20 pub number_of_linenumbers: u16,
21 pub characteristics: u32,
22}
23
24pub const SIZEOF_SECTION_TABLE: usize = 8 * 5;
25
26fn base64_decode_string_entry(s: &str) -> Result<usize, ()> {
30 assert!(s.len() <= 6, "String too long, possible overflow.");
31
32 let mut val = 0;
33 for c in s.bytes() {
34 let v = if b'A' <= c && c <= b'Z' {
35 c - b'A'
37 } else if b'a' <= c && c <= b'z' {
38 c - b'a' + 26
40 } else if b'0' <= c && c <= b'9' {
41 c - b'0' + 52
43 } else if c == b'+' {
44 62
46 } else if c == b'/' {
47 63
49 } else {
50 return Err(());
51 };
52 val = val * 64 + v as usize;
53 }
54 Ok(val)
55}
56
57impl SectionTable {
58 pub fn parse(
59 bytes: &[u8],
60 offset: &mut usize,
61 string_table_offset: usize,
62 ) -> error::Result<Self> {
63 let mut table = SectionTable::default();
64 let mut name = [0u8; 8];
65 name.copy_from_slice(bytes.gread_with(offset, 8)?);
66
67 table.name = name;
68 table.virtual_size = bytes.gread_with(offset, scroll::LE)?;
69 table.virtual_address = bytes.gread_with(offset, scroll::LE)?;
70 table.size_of_raw_data = bytes.gread_with(offset, scroll::LE)?;
71 table.pointer_to_raw_data = bytes.gread_with(offset, scroll::LE)?;
72 table.pointer_to_relocations = bytes.gread_with(offset, scroll::LE)?;
73 table.pointer_to_linenumbers = bytes.gread_with(offset, scroll::LE)?;
74 table.number_of_relocations = bytes.gread_with(offset, scroll::LE)?;
75 table.number_of_linenumbers = bytes.gread_with(offset, scroll::LE)?;
76 table.characteristics = bytes.gread_with(offset, scroll::LE)?;
77
78 if let Some(idx) = table.name_offset()? {
79 table.real_name = Some(bytes.pread::<&str>(string_table_offset + idx)?.to_string());
80 }
81 Ok(table)
82 }
83
84 pub fn data<'a, 'b: 'a>(&'a self, pe_bytes: &'b [u8]) -> error::Result<Option<Cow<[u8]>>> {
85 let section_start: usize = self.pointer_to_raw_data.try_into().map_err(|_| {
86 Error::Malformed(format!("Virtual address cannot fit in platform `usize`"))
87 })?;
88
89 let section_end: usize = section_start
92 + usize::try_from(self.size_of_raw_data).map_err(|_| {
93 Error::Malformed(format!("Virtual size cannot fit in platform `usize`"))
94 })?;
95
96 let original_bytes = pe_bytes.get(section_start..section_end).map(Cow::Borrowed);
97
98 if original_bytes.is_some() && self.virtual_size > self.size_of_raw_data {
99 let mut bytes: Vec<u8> = Vec::new();
100 bytes.resize(self.size_of_raw_data.try_into()?, 0);
101 bytes.copy_from_slice(&original_bytes.unwrap());
102 bytes.resize(self.virtual_size.try_into()?, 0);
103
104 Ok(Some(Cow::Owned(bytes)))
105 } else {
106 Ok(original_bytes)
107 }
108 }
109
110 pub fn name_offset(&self) -> error::Result<Option<usize>> {
111 if self.name[0] == b'/' {
113 let idx: usize = if self.name[1] == b'/' {
114 let b64idx = self.name.pread::<&str>(2)?;
115 base64_decode_string_entry(b64idx).map_err(|_| {
116 Error::Malformed(format!(
117 "Invalid indirect section name //{}: base64 decoding failed",
118 b64idx
119 ))
120 })?
121 } else {
122 let name = self.name.pread::<&str>(1)?;
123 name.parse().map_err(|err| {
124 Error::Malformed(format!("Invalid indirect section name /{}: {}", name, err))
125 })?
126 };
127 Ok(Some(idx))
128 } else {
129 Ok(None)
130 }
131 }
132
133 #[allow(clippy::useless_let_if_seq)]
134 pub fn set_name_offset(&mut self, mut idx: usize) -> error::Result<()> {
135 if idx <= 9_999_999 {
136 let mut name = [0; 7];
140 let mut len = 0;
141 if idx == 0 {
142 name[6] = b'0';
143 len = 1;
144 } else {
145 while idx != 0 {
146 let rem = (idx % 10) as u8;
147 idx /= 10;
148 name[6 - len] = b'0' + rem;
149 len += 1;
150 }
151 }
152 self.name = [0; 8];
153 self.name[0] = b'/';
154 self.name[1..][..len].copy_from_slice(&name[7 - len..]);
155 Ok(())
156 } else if idx as u64 <= 0xfff_fff_fff {
157 self.name[0] = b'/';
159 self.name[1] = b'/';
160 for i in 0..6 {
161 let rem = (idx % 64) as u8;
162 idx /= 64;
163 let c = match rem {
164 0..=25 => b'A' + rem,
165 26..=51 => b'a' + rem - 26,
166 52..=61 => b'0' + rem - 52,
167 62 => b'+',
168 63 => b'/',
169 _ => unreachable!(),
170 };
171 self.name[7 - i] = c;
172 }
173 Ok(())
174 } else {
175 Err(Error::Malformed(format!(
176 "Invalid section name offset: {}",
177 idx
178 )))
179 }
180 }
181
182 pub fn name(&self) -> error::Result<&str> {
183 match self.real_name.as_ref() {
184 Some(s) => Ok(s),
185 None => Ok(self.name.pread(0)?),
186 }
187 }
188
189 pub fn relocations<'a>(&self, bytes: &'a [u8]) -> error::Result<relocation::Relocations<'a>> {
190 let offset = self.pointer_to_relocations as usize;
191 let number = self.number_of_relocations as usize;
192 relocation::Relocations::parse(bytes, offset, number)
193 }
194
195 pub fn overlaps_with(&self, another_section: &SectionTable) -> bool {
197 let self_end = self.pointer_to_raw_data + self.size_of_raw_data;
198 let another_end = another_section.pointer_to_raw_data + another_section.size_of_raw_data;
199
200 !((self_end <= another_section.pointer_to_raw_data)
201 || (another_end <= self.pointer_to_raw_data))
202 }
203}
204
205impl ctx::SizeWith<scroll::Endian> for SectionTable {
206 fn size_with(_ctx: &scroll::Endian) -> usize {
207 SIZEOF_SECTION_TABLE
208 }
209}
210
211impl ctx::TryIntoCtx<scroll::Endian> for &SectionTable {
212 type Error = error::Error;
213 fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
214 let offset = &mut 0;
215 bytes.gwrite(&self.name[..], offset)?;
216 bytes.gwrite_with(self.virtual_size, offset, ctx)?;
217 bytes.gwrite_with(self.virtual_address, offset, ctx)?;
218 bytes.gwrite_with(self.size_of_raw_data, offset, ctx)?;
219 bytes.gwrite_with(self.pointer_to_raw_data, offset, ctx)?;
220 bytes.gwrite_with(self.pointer_to_relocations, offset, ctx)?;
221 bytes.gwrite_with(self.pointer_to_linenumbers, offset, ctx)?;
222 bytes.gwrite_with(self.number_of_relocations, offset, ctx)?;
223 bytes.gwrite_with(self.number_of_linenumbers, offset, ctx)?;
224 bytes.gwrite_with(self.characteristics, offset, ctx)?;
225 Ok(SIZEOF_SECTION_TABLE)
226 }
227}
228
229impl ctx::IntoCtx<scroll::Endian> for &SectionTable {
230 fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) {
231 bytes.pwrite_with(self, 0, ctx).unwrap();
232 }
233}
234
235pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008;
238pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020;
240pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040;
242pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080;
244pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100;
245pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200;
248pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800;
250pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000;
252pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000;
254pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000;
255pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000;
256pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000;
257pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000;
258
259pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000;
260pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000;
261pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000;
262pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000;
263pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000;
264pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000;
265pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000;
266pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000;
267pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000;
268pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000;
269pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000;
270pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000;
271pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000;
272pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000;
273pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000;
274
275pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000;
277pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000;
279pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000;
281pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000;
283pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000;
285pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000;
287pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000;
289pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000;
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[test]
297 fn set_name_offset() {
298 let mut section = SectionTable::default();
299 for &(offset, name) in [
300 (0usize, b"/0\0\0\0\0\0\0"),
301 (1, b"/1\0\0\0\0\0\0"),
302 (9_999_999, b"/9999999"),
303 (10_000_000, b"//AAmJaA"),
304 #[cfg(target_pointer_width = "64")]
305 (0xfff_fff_fff, b"////////"),
306 ]
307 .iter()
308 {
309 section.set_name_offset(offset).unwrap();
310 assert_eq!(§ion.name, name);
311 assert_eq!(section.name_offset().unwrap(), Some(offset));
312 }
313 #[cfg(target_pointer_width = "64")]
314 assert!(section.set_name_offset(0x1_000_000_000).is_err());
315 }
316}