1 use crate::error::{self, Error};
2 use crate::pe::relocation;
3 use alloc::string::{String, ToString};
4 use scroll::{ctx, Pread, Pwrite};
5 
6 #[repr(C)]
7 #[derive(Debug, PartialEq, Clone, Default)]
8 pub struct SectionTable {
9     pub name: [u8; 8],
10     pub real_name: Option<String>,
11     pub virtual_size: u32,
12     pub virtual_address: u32,
13     pub size_of_raw_data: u32,
14     pub pointer_to_raw_data: u32,
15     pub pointer_to_relocations: u32,
16     pub pointer_to_linenumbers: u32,
17     pub number_of_relocations: u16,
18     pub number_of_linenumbers: u16,
19     pub characteristics: u32,
20 }
21 
22 pub const SIZEOF_SECTION_TABLE: usize = 8 * 5;
23 
24 // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L70
25 // Decodes a string table entry in base 64 (//AAAAAA). Expects string without
26 // prefixed slashes.
base64_decode_string_entry(s: &str) -> Result<usize, ()>27 fn base64_decode_string_entry(s: &str) -> Result<usize, ()> {
28     assert!(s.len() <= 6, "String too long, possible overflow.");
29 
30     let mut val = 0;
31     for c in s.bytes() {
32         let v = if b'A' <= c && c <= b'Z' {
33             // 00..=25
34             c - b'A'
35         } else if b'a' <= c && c <= b'z' {
36             // 26..=51
37             c - b'a' + 26
38         } else if b'0' <= c && c <= b'9' {
39             // 52..=61
40             c - b'0' + 52
41         } else if c == b'+' {
42             // 62
43             62
44         } else if c == b'/' {
45             // 63
46             63
47         } else {
48             return Err(());
49         };
50         val = val * 64 + v as usize;
51     }
52     Ok(val)
53 }
54 
55 impl SectionTable {
parse( bytes: &[u8], offset: &mut usize, string_table_offset: usize, ) -> error::Result<Self>56     pub fn parse(
57         bytes: &[u8],
58         offset: &mut usize,
59         string_table_offset: usize,
60     ) -> error::Result<Self> {
61         let mut table = SectionTable::default();
62         let mut name = [0u8; 8];
63         name.copy_from_slice(bytes.gread_with(offset, 8)?);
64 
65         table.name = name;
66         table.virtual_size = bytes.gread_with(offset, scroll::LE)?;
67         table.virtual_address = bytes.gread_with(offset, scroll::LE)?;
68         table.size_of_raw_data = bytes.gread_with(offset, scroll::LE)?;
69         table.pointer_to_raw_data = bytes.gread_with(offset, scroll::LE)?;
70         table.pointer_to_relocations = bytes.gread_with(offset, scroll::LE)?;
71         table.pointer_to_linenumbers = bytes.gread_with(offset, scroll::LE)?;
72         table.number_of_relocations = bytes.gread_with(offset, scroll::LE)?;
73         table.number_of_linenumbers = bytes.gread_with(offset, scroll::LE)?;
74         table.characteristics = bytes.gread_with(offset, scroll::LE)?;
75 
76         if let Some(idx) = table.name_offset()? {
77             table.real_name = Some(bytes.pread::<&str>(string_table_offset + idx)?.to_string());
78         }
79         Ok(table)
80     }
81 
name_offset(&self) -> error::Result<Option<usize>>82     pub fn name_offset(&self) -> error::Result<Option<usize>> {
83         // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L1054
84         if self.name[0] == b'/' {
85             let idx: usize = if self.name[1] == b'/' {
86                 let b64idx = self.name.pread::<&str>(2)?;
87                 base64_decode_string_entry(b64idx).map_err(|_| {
88                     Error::Malformed(format!(
89                         "Invalid indirect section name //{}: base64 decoding failed",
90                         b64idx
91                     ))
92                 })?
93             } else {
94                 let name = self.name.pread::<&str>(1)?;
95                 name.parse().map_err(|err| {
96                     Error::Malformed(format!("Invalid indirect section name /{}: {}", name, err))
97                 })?
98             };
99             Ok(Some(idx))
100         } else {
101             Ok(None)
102         }
103     }
104 
105     #[allow(clippy::useless_let_if_seq)]
set_name_offset(&mut self, mut idx: usize) -> error::Result<()>106     pub fn set_name_offset(&mut self, mut idx: usize) -> error::Result<()> {
107         if idx <= 9_999_999 {
108             // 10^7 - 1
109             // write!(&mut self.name[1..], "{}", idx) without using io::Write.
110             // We write into a temporary since we calculate digits starting at the right.
111             let mut name = [0; 7];
112             let mut len = 0;
113             if idx == 0 {
114                 name[6] = b'0';
115                 len = 1;
116             } else {
117                 while idx != 0 {
118                     let rem = (idx % 10) as u8;
119                     idx /= 10;
120                     name[6 - len] = b'0' + rem;
121                     len += 1;
122                 }
123             }
124             self.name = [0; 8];
125             self.name[0] = b'/';
126             self.name[1..][..len].copy_from_slice(&name[7 - len..]);
127             Ok(())
128         } else if idx as u64 <= 0xfff_fff_fff {
129             // 64^6 - 1
130             self.name[0] = b'/';
131             self.name[1] = b'/';
132             for i in 0..6 {
133                 let rem = (idx % 64) as u8;
134                 idx /= 64;
135                 let c = match rem {
136                     0..=25 => b'A' + rem,
137                     26..=51 => b'a' + rem - 26,
138                     52..=61 => b'0' + rem - 52,
139                     62 => b'+',
140                     63 => b'/',
141                     _ => unreachable!(),
142                 };
143                 self.name[7 - i] = c;
144             }
145             Ok(())
146         } else {
147             Err(Error::Malformed(format!(
148                 "Invalid section name offset: {}",
149                 idx
150             )))
151         }
152     }
153 
name(&self) -> error::Result<&str>154     pub fn name(&self) -> error::Result<&str> {
155         match self.real_name.as_ref() {
156             Some(s) => Ok(s),
157             None => Ok(self.name.pread(0)?),
158         }
159     }
160 
relocations<'a>(&self, bytes: &'a [u8]) -> error::Result<relocation::Relocations<'a>>161     pub fn relocations<'a>(&self, bytes: &'a [u8]) -> error::Result<relocation::Relocations<'a>> {
162         let offset = self.pointer_to_relocations as usize;
163         let number = self.number_of_relocations as usize;
164         relocation::Relocations::parse(bytes, offset, number)
165     }
166 }
167 
168 impl ctx::SizeWith<scroll::Endian> for SectionTable {
size_with(_ctx: &scroll::Endian) -> usize169     fn size_with(_ctx: &scroll::Endian) -> usize {
170         SIZEOF_SECTION_TABLE
171     }
172 }
173 
174 impl ctx::TryIntoCtx<scroll::Endian> for SectionTable {
175     type Error = error::Error;
try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error>176     fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
177         let offset = &mut 0;
178         bytes.gwrite(&self.name[..], offset)?;
179         bytes.gwrite_with(self.virtual_size, offset, ctx)?;
180         bytes.gwrite_with(self.virtual_address, offset, ctx)?;
181         bytes.gwrite_with(self.size_of_raw_data, offset, ctx)?;
182         bytes.gwrite_with(self.pointer_to_raw_data, offset, ctx)?;
183         bytes.gwrite_with(self.pointer_to_relocations, offset, ctx)?;
184         bytes.gwrite_with(self.pointer_to_linenumbers, offset, ctx)?;
185         bytes.gwrite_with(self.number_of_relocations, offset, ctx)?;
186         bytes.gwrite_with(self.number_of_linenumbers, offset, ctx)?;
187         bytes.gwrite_with(self.characteristics, offset, ctx)?;
188         Ok(SIZEOF_SECTION_TABLE)
189     }
190 }
191 
192 impl ctx::IntoCtx<scroll::Endian> for SectionTable {
into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian)193     fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) {
194         bytes.pwrite_with(self, 0, ctx).unwrap();
195     }
196 }
197 
198 /// The section should not be padded to the next boundary. This flag is obsolete and is replaced
199 /// by `IMAGE_SCN_ALIGN_1BYTES`. This is valid only for object files.
200 pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008;
201 /// The section contains executable code.
202 pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020;
203 /// The section contains initialized data.
204 pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040;
205 ///  The section contains uninitialized data.
206 pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080;
207 pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100;
208 /// The section contains comments or other information. The .drectve section has this type.
209 /// This is valid for object files only.
210 pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200;
211 /// The section will not become part of the image. This is valid only for object files.
212 pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800;
213 /// The section contains COMDAT data. This is valid only for object files.
214 pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000;
215 /// The section contains data referenced through the global pointer (GP).
216 pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000;
217 pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000;
218 pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000;
219 pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000;
220 pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000;
221 
222 pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000;
223 pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000;
224 pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000;
225 pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000;
226 pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000;
227 pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000;
228 pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000;
229 pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000;
230 pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000;
231 pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000;
232 pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000;
233 pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000;
234 pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000;
235 pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000;
236 pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000;
237 
238 /// The section contains extended relocations.
239 pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000;
240 /// The section can be discarded as needed.
241 pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000;
242 /// The section cannot be cached.
243 pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000;
244 /// The section is not pageable.
245 pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000;
246 /// The section can be shared in memory.
247 pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000;
248 /// The section can be executed as code.
249 pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000;
250 /// The section can be read.
251 pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000;
252 /// The section can be written to.
253 pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000;
254 
255 #[cfg(test)]
256 mod tests {
257     use super::*;
258 
259     #[test]
set_name_offset()260     fn set_name_offset() {
261         let mut section = SectionTable::default();
262         for &(offset, name) in [
263             (0usize, b"/0\0\0\0\0\0\0"),
264             (1, b"/1\0\0\0\0\0\0"),
265             (9_999_999, b"/9999999"),
266             (10_000_000, b"//AAmJaA"),
267             #[cfg(target_pointer_width = "64")]
268             (0xfff_fff_fff, b"////////"),
269         ]
270         .iter()
271         {
272             section.set_name_offset(offset).unwrap();
273             assert_eq!(&section.name, name);
274             assert_eq!(section.name_offset().unwrap(), Some(offset));
275         }
276         #[cfg(target_pointer_width = "64")]
277         assert!(section.set_name_offset(0x1_000_000_000).is_err());
278     }
279 }
280