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