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!(§ion.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