1pub mod dos;
2pub mod file;
3pub mod optional;
4pub mod section;
5pub mod import;
6pub mod export;
7pub mod relocs;
8pub mod rsrc;
9pub mod ser;
10
11use std::{
12 fmt::{Display, Write}, fs::File, io::{BufReader, Cursor}, string::{FromUtf16Error, FromUtf8Error}
13};
14
15use derivative::Derivative;
16
17use crate::{types::{BufReadExt, Header, HeaderField, ReadExtError}, Result};
18
19use self::{
20 dos::DosHeader, export::ExportDirectory, file::FileHeader, import::ImportDirectory,
21 optional::{ parse_data_directories, x64::OptionalHeader64, x86::OptionalHeader32, DataDirectory, DirectoryType, OptionalHeader },
22 relocs::Relocations,
23 rsrc::ResourceDirectory,
24 section::{rva_to_section, SectionHeader, SectionTable}
25};
26
27#[macro_export]
33macro_rules! new_header_field {
34 ($value:expr, $offset:ident, $rva:expr) => {
35 #[allow(unused_assignments)]
36 {
37 use std::mem::size_of_val;
38
39 let old_offset = $offset;
40 let v = $value;
41
42 $offset += size_of_val(&v) as u64;
43
44 HeaderField{
45 value: v,
46 offset: old_offset,
47 rva: $rva
48 }
49 }
50 };
51
52 ($value:expr, $offset:ident) => {
53 {
54 let old_offset = $offset;
55 new_header_field!($value, $offset, old_offset)
56 }
57 };
58}
59
60#[derive(Debug, thiserror::Error)]
61pub enum PeError {
62 #[error("not enough data for {target}; expected {expected}, got {actual}")]
63 #[non_exhaustive]
64 BufferTooSmall {
65 target: String,
66 expected: u64,
67 actual: u64,
68 },
69
70 #[error("invalid timestamp 0x{0:08x}")]
71 #[non_exhaustive]
72 InvalidTimestamp(u64),
73
74 #[error("invalid rva 0x{0:08x}")]
75 #[non_exhaustive]
76 InvalidRVA(u64),
77
78 #[error("invalid offset 0x{0:08x}")]
79 #[non_exhaustive]
80 InvalidOffset(u64),
81
82 #[error("failed to parse {name} header at offset {offset:08x}; {reason}")]
83 #[non_exhaustive]
84 InvalidHeader {
85 name: String,
86 offset: u64,
87 reason: String,
88 },
89
90 #[error("can't find section for rva {0:08x}")]
91 #[non_exhaustive]
92 NoSectionForRVA(u64),
93
94 #[error("can't find section for offset {0:08x}")]
95 #[non_exhaustive]
96 NoSectionForOffset(u64),
97
98 #[error(transparent)]
99 ReadExt(#[from] ReadExtError),
100
101 #[error(transparent)]
102 IO(#[from] std::io::Error),
103
104 #[error("PE file must have optional header")]
105 MustHaveOptional,
106
107 #[error(transparent)]
108 FromUtf8 (#[from] FromUtf8Error),
109
110 #[error(transparent)]
111 FromUtf16 (#[from] FromUtf16Error),
112
113 #[error("{typ} {value:08x} is beyond {name} range [{start:08x}..{end:08x}]")]
114 #[non_exhaustive]
115 BeyondRange {
116 name: String,
117 typ: String,
118 value: u64,
119 start: u64,
120 end: u64,
121 }
122}
123
124
125pub const SECTION_HEADER_LENGTH: u64 = section::HEADER_LENGTH;
126
127#[derive(Derivative)]
128#[derivative(Debug)]
129pub struct PeImage {
130 pub dos: HeaderField<DosHeader>,
131 pub file: HeaderField<FileHeader>,
132 pub optional: HeaderField<OptionalHeader>,
133 pub data_dirs: HeaderField<Vec<HeaderField<DataDirectory>>>,
134 pub sections: HeaderField<SectionTable>,
135 pub imports: HeaderField<ImportDirectory>,
136 pub exports: HeaderField<ExportDirectory>,
137 pub relocations: HeaderField<Relocations>,
138 pub resources: HeaderField<ResourceDirectory>,
139
140 #[derivative(Debug="ignore")]
141 reader: Box<dyn BufReadExt>,
142}
143
144impl PeImage {
145 pub fn new(reader: Box<dyn BufReadExt>) -> Self {
146 Self {
147 dos: Default::default(),
148 file: Default::default(),
149 optional: Default::default(),
150 data_dirs: Default::default(),
151 sections: Default::default(),
152 imports: Default::default(),
153 exports: Default::default(),
154 relocations: Default::default(),
155 resources: Default::default(),
156 reader
157 }
158 }
159
160 pub fn directory_offset(&self, dir: DirectoryType) -> Option<u32> {
161 if let Some(dir) = self.directory(dir) {
162 let rva = dir.rva.value;
163 section::rva_to_offset(&self.sections.value, rva)
164 }
165 else {
166 None
167 }
168 }
169
170 pub fn directory_section(&self, dir: DirectoryType) -> Option<&SectionHeader> {
171 if let Some(dir) = self.directory(dir) {
172 let rva = dir.rva.value;
173 section::rva_to_section(&self.sections.value, rva)
174 }
175 else {
176 None
177 }
178 }
179
180 #[inline]
181 pub fn directory(&self, dir: DirectoryType) -> Option<&DataDirectory> {
182 let dir = &self.data_dirs.value[dir as usize].value;
183 if dir.rva.value == 0 {None} else {Some(&dir)}
184 }
185
186 #[inline]
187 pub fn rva_to_offset(&self, rva: u32) -> Option<u32> {
188 section::rva_to_offset(&self.sections.value, rva)
189 }
190
191 #[inline]
192 pub fn offset_to_rva(&self, offset: u64) -> Option<u32> {
193 section::offset_to_rva(&self.sections.value, offset as u32)
194 }
195
196 pub fn read_string_at_rva(&mut self, rva: u32) -> std::result::Result<String, PeError> {
197 let offset = self.rva_to_offset(rva).ok_or(PeError::InvalidRVA(rva.into()))?;
198 Ok(self.reader.read_string_at_offset(offset.into())?)
199 }
200
201 #[inline]
202 pub fn has_imports(&self) -> bool {
203 self.data_dirs.value[DirectoryType::Import as usize].value.rva.value != 0
204 }
205
206 pub fn parse_import_directory(&mut self) -> std::result::Result<(), PeError> {
207 if !self.has_imports() {
208 return Ok(());
209 }
210
211 let import_dd = &self.data_dirs.value[DirectoryType::Import as usize].value;
212 let import_rva = import_dd.rva.value;
213 let import_size = import_dd.size.value;
214 let import_offset = self.rva_to_offset(import_rva).ok_or(PeError::InvalidRVA(import_rva.into()))?;
215
216 let bytes = self.reader.read_bytes_at_offset(import_offset as u64, import_size as usize)?;
218
219 let mut imp_dir = ImportDirectory::parse_bytes(bytes, import_rva as u64)?;
220
221 for i in 0..imp_dir.len() {
222 let id = &mut imp_dir[i].value;
223 id.update_name(&self.sections.value, &mut self.reader)?;
224 id.parse_imports(&self.sections.value, self.optional.value.get_image_type(), &mut self.reader)?;
225 }
226 self.imports = HeaderField{ value: imp_dir, offset:import_offset as u64, rva:import_rva as u64};
227
228 Ok(())
229 }
230
231 #[inline]
232 pub fn has_exports(&self) -> bool {
233 self.data_dirs.value[DirectoryType::Export as usize].value.rva.value != 0
234 }
235
236 pub fn parse_exports(&mut self) -> Result<()> {
237 let dd_export = &self.data_dirs.value[DirectoryType::Export as usize].value;
238 if !self.has_exports() {
239 return Ok(());
240 }
241
242 let export_rva = dd_export.rva.value;
243 let export_offset = self.rva_to_offset(export_rva).ok_or(PeError::InvalidRVA(export_rva.into()))?;
244
245 let bytes = self.reader.read_bytes_at_offset(export_offset.into(), export::HEADER_LENGTH as usize)?;
247
248 let mut export_dir = ExportDirectory::parse_bytes(bytes, export_offset.into())?;
249 if !export_dir.is_valid() {
250 return Err(
251 PeError::InvalidHeader { name: "Export".into(), offset: export_offset.into(), reason: "structure is invalid".into() }
252 );
253 }
254
255 export_dir.parse_exports(&self.sections.value, &mut self.reader)?;
256
257 self.exports = HeaderField {
258 value: export_dir,
259 offset: export_offset.into(),
260 rva: export_rva.into()
261 };
262
263 Ok(())
264 }
265
266 #[inline]
267 pub fn has_relocations(&self) -> bool{
268 self.data_dirs.value[DirectoryType::Relocation as usize].value.rva.value != 0
269 }
270
271 pub fn parse_relocations(&mut self) -> Result<()> {
272 if !self.has_relocations() {
273 return Ok(());
274 }
275
276 let dd_relocs = &self.data_dirs.value[DirectoryType::Relocation as usize].value;
277 let relocs_rva = dd_relocs.rva.value;
278 let relocs_size = dd_relocs.size.value as usize;
279 let relocs_offset = self.rva_to_offset(relocs_rva.into()).ok_or(PeError::NoSectionForRVA(relocs_rva.into()))?;
280
281 let bytes = self.reader.read_bytes_at_offset(relocs_offset.into(), relocs_size)?;
283
284 let mut relocs = Relocations::parse_bytes(bytes, relocs_offset.into())?;
285 relocs.fix_rvas(relocs_rva.into())?;
286 self.relocations = HeaderField {value: relocs, offset: relocs_offset.into(), rva: relocs_rva.into()};
287
288 Ok(())
289 }
290
291 #[inline]
292 pub fn has_rsrc(&self) -> bool {
293 self.data_dirs.value[DirectoryType::Resource as usize].value.rva.value != 0
294 }
295
296 pub fn parse_resources(&mut self) -> Result<()> {
297 if !self.has_rsrc() {
298 return Ok(())
299 }
300
301 let dd_rsrc = &self.data_dirs.value[DirectoryType::Resource as usize].value;
302 let rsrc_rva = dd_rsrc.rva.value;
303 let rsrc_offset = self.rva_to_offset(rsrc_rva.into()).ok_or(PeError::NoSectionForRVA(rsrc_rva.into()))?;
304 let rsrc_section = rva_to_section(&self.sections.value, rsrc_rva)
305 .ok_or(PeError::NoSectionForRVA(rsrc_rva.into()))?;
306
307 let bytes = self.reader.read_bytes_at_offset(rsrc_offset.into(), rsrc::DIR_LENGTH as usize)?;
308
309 let mut rsrc_dir = ResourceDirectory::parse_bytes(bytes, rsrc_offset.into())?;
310 rsrc_dir.parse_rsrc(rsrc_section, &mut self.reader)?;
311 self.resources = HeaderField{value: rsrc_dir, offset: rsrc_offset.into(), rva: rsrc_rva.into()};
312
313 Ok(())
314 }
315
316 #[inline]
317 pub fn format_resource_tree(&self, f: &mut dyn Write, seperator: &String, level: u8) -> std::fmt::Result {
318 writeln!(f, "Resource Directory: {{")?;
319 rsrc::display_rsrc_tree(&self.resources.value, f, seperator, level)?;
320 writeln!(f, "}}")
321 }
322
323 pub fn format_basic_headers(&self, f: &mut dyn Write) -> std::fmt::Result {
324 writeln!(f, "DosHeader: {}", self.dos.value)?;
325 writeln!(f, "FileHeader: {}", self.file.value)?;
326 writeln!(f, "OptionalHeader: {}", self.optional.value)?;
327
328 Ok(())
329 }
330
331 pub fn format_data_dirs(&self, f: &mut dyn Write) -> std::fmt::Result {
332 writeln!(f, "DataDirectories: [")?;
334 for dir in &self.data_dirs.value {
335 if dir.value.rva.value != 0 {
336 write!(f, " {}, ", dir)?;
337 let section = self.directory_section(dir.value.member);
338 if let Some(sec) = section {
339 writeln!(f, " Section: {},", sec.name_str().unwrap_or_else(|err| format!("{err}")))?;
340 }
341 println!("");
342 }
343 }
344 writeln!(f, "]")
345 }
346
347 pub fn format_sections(&self, f: &mut dyn Write) -> std::fmt::Result {
348 writeln!(f, "Sections: [")?;
349 for sec in &self.sections.value {
350 write!(f, " {sec}, ")?;
351 let dirs = sec.value.directories(&self.data_dirs.value);
352 if dirs.len() > 0 { writeln!(f, "Directories: {dirs:?},")?;} else {writeln!(f, "")?;}
353 }
354 writeln!(f, "]")
355 }
356
357 pub fn format_imports(&self, f: &mut dyn Write) -> std::fmt::Result {
358 if self.has_imports() && self.imports.value.is_valid() {
359 writeln!(f, "Import Directory: [")?;
360 let idir = &self.imports.value;
361 for idesc in idir {
362 writeln!(f, " {}\n [", idesc.value)?;
363 for imp_name in idesc.value.get_imports_str() {
364 writeln!(f, " {imp_name}",)?;
365 }
366 writeln!(f, " ]")?;
367 }
368 writeln!(f, "]")?;
369 }
370
371 Ok(())
372 }
373
374 pub fn format_exports(&self, f: &mut dyn Write) -> std::fmt::Result {
375 if self.has_exports() && self.exports.value.is_valid() {
376 writeln!(f, "Export Directory: {{")?;
377 let export_dir = &self.exports.value;
378 writeln!(f, " DLL Name: {}", export_dir.name)?;
379 writeln!(f, " Exports: [")?;
380
381 for export in &export_dir.exports {
382 writeln!(f, " {export}")?;
383 }
384
385 writeln!(f, " ]")?;
386 writeln!(f, "}}")?;
387 }
388
389 Ok(())
390 }
391
392 pub fn format_relocations(&self, f: &mut dyn Write) -> std::fmt::Result {
393 if self.has_relocations() && self.relocations.value.is_valid() {
394 writeln!(f, "Relocation Directory: [")?;
395 for rb in &self.relocations.value.blocks {
396 writeln!(f, " [{rb}")?;
397 for rc in &rb.value.relocs {
398 writeln!(f, " {}", rc.value)?;
399 }
400 writeln!(f, " ]")?;
401 }
402 writeln!(f, "]")?;
403 }
404
405 Ok(())
406 }
407
408 pub(crate) fn parse_fixed_headers(&mut self, pos: u64) -> Result<u64> {
410 let mut offset = pos;
411
412 let mut buf = self.reader.read_bytes_at_offset(pos, dos::HEADER_LENGTH as usize)?;
413 self.dos = HeaderField{ value: DosHeader::parse_bytes(buf, pos)?, offset: offset, rva: offset };
414 offset += self.dos.value.e_lfanew.value as u64;
415
416 buf = self.reader.read_bytes_at_offset(offset, file::HEADER_LENGTH as usize)?;
417 self.file = HeaderField{ value: FileHeader::parse_bytes(buf, offset)?, offset: offset, rva: offset};
418 offset += file::HEADER_LENGTH;
419
420 buf = self.reader.read_bytes_at_offset(offset, self.file.value.optional_header_size.value as usize)?;
421
422 match buf.len() {
423 0xE0 => {
425 let opt = OptionalHeader32::parse_bytes(buf.clone(), offset)?;
426 self.optional = HeaderField{ value: OptionalHeader::X86(opt), offset: offset, rva: offset};
427 offset += optional::x86::HEADER_LENGTH;
428
429 let dir_buf = &buf[optional::x86::HEADER_LENGTH as usize..];
430 let dirs = parse_data_directories(&dir_buf, 16, offset)?;
431 self.data_dirs = HeaderField{ value: dirs, offset: offset, rva: offset};
432 offset += 16 * 8;
433 },
434
435 0xF0 => {
437 let opt = OptionalHeader64::parse_bytes(buf.clone(), offset)?;
438 self.optional = HeaderField {value: OptionalHeader::X64(opt), offset: offset, rva: offset};
439 offset += optional::x64::HEADER_LENGTH;
440
441 let dir_buf = &buf[optional::x64::HEADER_LENGTH as usize..];
442 let dirs = parse_data_directories(&dir_buf, 16, offset)?;
443 self.data_dirs = HeaderField{ value: dirs, offset: offset, rva: offset};
444 offset += 16 * 8;
445 },
446
447 _ => {
448 return Err(PeError::MustHaveOptional)
449 }
450 }
451
452 Ok(offset)
453 }
454
455 pub(crate) fn parse_sections(&mut self, pos: u64) -> Result<u64> {
458 let mut offset = pos;
459 let sec_count = self.file.value.sections.value;
460 let size = section::HEADER_LENGTH * sec_count as u64;
461
462 let buf = self.reader.read_bytes_at_offset(offset, size as usize)?;
463 let sections = section::parse_sections(&buf, sec_count, offset)?;
464 self.sections = HeaderField{ value:sections, offset: offset, rva: offset};
465
466 offset += size;
467
468 Ok(offset)
469 }
470
471 pub(crate) fn parse_dynamic_headers(&mut self) -> Result<()> {
474 self.parse_import_directory()?;
475 self.parse_exports()?;
476 self.parse_relocations()?;
477 self.parse_resources()?;
478 Ok(())
479 }
480
481 pub(crate) fn parse_all_headers(&mut self, pos: u64) -> Result<()> {
482 let offset = self.parse_fixed_headers(pos)?;
483 self.parse_sections(offset)?;
484 self.parse_dynamic_headers()?;
485 Ok(())
486 }
487
488 pub fn parse_file(file: File, pos: u64) -> crate::Result<Self> where Self: Sized {
494 let reader = Box::new(BufReader::new(file));
495 let mut pe = Self::new(reader);
496
497 pe.parse_all_headers(pos)?;
498
499 Ok(pe)
500 }
501
502 pub fn parse_bytes(bytes: Vec<u8>, pos: u64) -> crate::Result<Self> where Self: Sized {
508 let reader = Box::new(Cursor::new(bytes));
509 let mut pe = Self::new(reader);
510
511 pe.parse_all_headers(pos)?;
512
513 Ok(pe)
514 }
515
516
517 pub fn parse_readable(reader: Box<dyn BufReadExt>, pos: u64) -> crate::Result<Self> where Self: Sized {
523 let mut pe = Self::new(reader);
524
525 pe.parse_all_headers(pos)?;
526
527 Ok(pe)
528 }
529}
530
531
532impl TryFrom<File> for PeImage{
533 type Error = PeError;
534
535 fn try_from(value: File) -> Result<Self> {
536 Self::parse_file(value, 0)
537 }
538}
539
540impl TryFrom<Vec<u8>> for PeImage {
541 type Error = PeError;
542
543 fn try_from(value: Vec<u8>) -> Result<Self> {
544 Self::parse_bytes(value, 0)
545 }
546}
547
548impl TryFrom<Box<dyn BufReadExt>> for PeImage{
549 type Error = PeError;
550
551 fn try_from(value: Box<dyn BufReadExt>) -> Result<Self> {
552 Self::parse_readable(value, 0)
553 }
554}
555
556
557impl Display for PeImage {
558 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559
560 self.format_basic_headers(f)?;
562 self.format_data_dirs(f)?;
564 self.format_sections(f)?;
566 if self.has_imports() { self.format_imports(f)?; }
568 if self.has_exports() { self.format_exports(f)?; }
570 if self.has_relocations() { self.format_relocations(f)?; }
572 if self.has_rsrc() && self.resources.value.is_valid() {
574 self.format_resource_tree(f, &String::from(" "), 1)?;
575 }
576
577 Ok(())
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use std::io::Cursor;
586
587 use crate::{
588 pe::{optional::{DirectoryType, ImageType, OptionalHeader, MAX_DIRS}, section::Flags},
589 types::{Header, BufReadExt},
590 };
591
592 use super::PeImage;
593
594 const RAW_BYTES_64: [u8; 704] = [
595 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
596 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
597 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0xF0, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
600 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
601 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
602 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
603 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x93, 0xC1, 0x57, 0x47, 0xF2, 0xAF,
604 0x04, 0x47, 0xF2, 0xAF, 0x04, 0x47, 0xF2, 0xAF, 0x04, 0x4E, 0x8A, 0x3C, 0x04, 0x4B, 0xF2,
605 0xAF, 0x04, 0x2B, 0x86, 0xAE, 0x05, 0x45, 0xF2, 0xAF, 0x04, 0x2B, 0x86, 0xAA, 0x05, 0x51,
606 0xF2, 0xAF, 0x04, 0x2B, 0x86, 0xAB, 0x05, 0x4E, 0xF2, 0xAF, 0x04, 0x2B, 0x86, 0xAC, 0x05,
607 0x44, 0xF2, 0xAF, 0x04, 0x1C, 0x9A, 0xAE, 0x05, 0x4E, 0xF2, 0xAF, 0x04, 0x47, 0xF2, 0xAE,
608 0x04, 0xEB, 0xF2, 0xAF, 0x04, 0x47, 0xF2, 0xAF, 0x04, 0xDD, 0xF2, 0xAF, 0x04, 0x91, 0x86,
609 0xAD, 0x05, 0x46, 0xF2, 0xAF, 0x04, 0x52, 0x69, 0x63, 0x68, 0x47, 0xF2, 0xAF, 0x04, 0x00,
610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05, 0x00, 0x91, 0xC0, 0x02, 0x62, 0x00, 0x00, 0x00,
612 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x1C, 0x00, 0x2A,
613 0x04, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x1D, 0x04, 0x00, 0x00,
614 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
615 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
617 0x00, 0x00, 0x03, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
618 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
619 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x42, 0x05, 0x00, 0xB4, 0x00,
621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x05, 0x00, 0xFC,
622 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00,
623 0xF8, 0x05, 0x00, 0x00, 0x40, 0xC7, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9,
625 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0xA0, 0xC7, 0x04, 0x00, 0x38, 0x01, 0x00, 0x00, 0x00,
626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x08, 0x03, 0x00, 0x00,
627 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x74, 0x65, 0x78, 0x74, 0x00,
629 0x00, 0x00, 0x47, 0x29, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2A, 0x04, 0x00, 0x00,
630 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
631 0x20, 0x00, 0x00, 0x60, 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0xD6, 0x0D, 0x01,
632 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x2E, 0x04, 0x00, 0x00, 0x00,
633 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2E,
634 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, 0x00, 0x50, 0x05, 0x00,
635 0x00, 0x02, 0x00, 0x00, 0x00, 0x3C, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x2E, 0x70, 0x64, 0x61, 0x74, 0x61,
637 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
638 0x3E, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639 0x40, 0x00, 0x00, 0x40, 0x2E, 0x72, 0x65, 0x6C, 0x6F, 0x63, 0x00, 0x00, 0xF8, 0x05, 0x00,
640 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7E, 0x05, 0x00, 0x00, 0x00,
641 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42,
642 ];
643
644 #[test]
645 fn parse_valid_header_x64() {
646 let reader = Box::new(Cursor::new(RAW_BYTES_64.to_vec()));
647 let mut pe = PeImage::new(reader);
648 let offset = pe.parse_fixed_headers(0).unwrap();
649 pe.parse_sections(offset).unwrap();
650 assert!(pe.dos.value.is_valid());
651 assert_eq!(pe.dos.offset, 0);
652 assert_eq!(pe.dos.rva, 0);
653 assert!(pe.file.value.is_valid());
654 assert_eq!(pe.file.offset, 0xf0);
655 assert_eq!(pe.file.rva, 0xf0);
656 assert_eq!(pe.optional.offset, 0x108);
657 assert_eq!(pe.optional.rva, 0x108);
658
659 if let OptionalHeader::X64(opt) = pe.optional.value {
660 assert_eq!(opt.magic.value, ImageType::PE64);
661 }
662 else {
663 assert!(false, "Didn't expect OptionalHeader32");
664 }
665
666 assert_eq!(pe.data_dirs.offset, 0x178);
667 assert_eq!(pe.data_dirs.value.len(), MAX_DIRS as usize);
668 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].offset, 0x1d8);
669 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].value.rva.value, 0x00044000);
670 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].value.size.value, 0x00000308);
671 assert_eq!(pe.sections.value.len(), 5);
681 let sec_names = [
682 ".text",
683 ".rdata",
684 ".data",
685 ".pdata",
686 ".reloc"
687 ];
688
689 let sec_flags = [
690 Flags::CODE | Flags::MEM_READ | Flags::MEM_EXECUTE,
691 Flags::INITIALIZED_DATA | Flags::MEM_READ,
692 Flags::INITIALIZED_DATA | Flags::MEM_READ | Flags::MEM_WRITE,
693 Flags::INITIALIZED_DATA | Flags::MEM_READ,
694 Flags::INITIALIZED_DATA | Flags::MEM_READ | Flags::MEM_DISCARDABLE,
695 ];
696
697 for i in 0..5 {
698 let sec = &pe.sections.value[i].value;
699 assert_eq!(sec.name_str().unwrap(), sec_names[i]);
700 assert_eq!(sec.flags().unwrap(), sec_flags[i]);
701 }
702 }
703
704 #[test]
705 fn read_string_at_offset() {
706 let mut cursor = Cursor::new(&RAW_BYTES_64);
708 assert_eq!(cursor.read_string_at_offset(0x1f8).unwrap().as_str(), ".text");
709 }
710
711 const RAW_BYTES_32: [u8; 784] = [
712 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
713 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
714 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
715 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
716 0x10, 0x01, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
717 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
718 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
719 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
720 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x96, 0x94, 0xCA, 0x72, 0xF7, 0xFA,
721 0x99, 0x72, 0xF7, 0xFA, 0x99, 0x72, 0xF7, 0xFA, 0x99, 0xC6, 0x6B, 0x0B, 0x99, 0x78, 0xF7,
722 0xFA, 0x99, 0xC6, 0x6B, 0x09, 0x99, 0xF6, 0xF7, 0xFA, 0x99, 0xC6, 0x6B, 0x08, 0x99, 0x6A,
723 0xF7, 0xFA, 0x99, 0x49, 0xA9, 0xF9, 0x98, 0x60, 0xF7, 0xFA, 0x99, 0x49, 0xA9, 0xFF, 0x98,
724 0x51, 0xF7, 0xFA, 0x99, 0x49, 0xA9, 0xFE, 0x98, 0x60, 0xF7, 0xFA, 0x99, 0xAF, 0x08, 0x34,
725 0x99, 0x73, 0xF7, 0xFA, 0x99, 0xAF, 0x08, 0x31, 0x99, 0x75, 0xF7, 0xFA, 0x99, 0x72, 0xF7,
726 0xFB, 0x99, 0x06, 0xF7, 0xFA, 0x99, 0xE5, 0xA9, 0xF3, 0x98, 0x77, 0xF7, 0xFA, 0x99, 0xE0,
727 0xA9, 0x05, 0x99, 0x73, 0xF7, 0xFA, 0x99, 0x72, 0xF7, 0x6D, 0x99, 0x73, 0xF7, 0xFA, 0x99,
728 0xE5, 0xA9, 0xF8, 0x98, 0x73, 0xF7, 0xFA, 0x99, 0x52, 0x69, 0x63, 0x68, 0x72, 0xF7, 0xFA,
729 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
730 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x06, 0x00, 0xA0, 0x65, 0x08, 0x58, 0x00,
731 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x02, 0x01, 0x0B, 0x01, 0x0E, 0x00,
732 0x00, 0xBC, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x20, 0x00,
733 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10,
734 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00,
736 0xF1, 0xE2, 0x01, 0x00, 0x02, 0x00, 0x40, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00,
737 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
738 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x26, 0x01, 0x00, 0x50,
739 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0xE8, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
740 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0xD0, 0x01,
741 0x00, 0x98, 0x0F, 0x00, 0x00, 0x80, 0x1D, 0x01, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
742 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
743 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1D, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00,
744 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x74, 0x01, 0x00,
745 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
746 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x74, 0x65, 0x78, 0x74,
747 0x00, 0x00, 0x00, 0xEB, 0xBB, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00,
748 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
749 0x00, 0x20, 0x00, 0x00, 0x60, 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x8E, 0x5F,
750 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,
752 0x2E, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x78, 0x13, 0x00, 0x00, 0x00, 0x30, 0x01,
753 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
754 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x2E, 0x67, 0x66, 0x69, 0x64,
755 0x73, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00,
756 0x00, 0x28, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
757 0x00, 0x40, 0x00, 0x00, 0x40, 0x2E, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0xE8, 0x64,
758 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00,
759 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,
760 0x2E, 0x72, 0x65, 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x98, 0x0F, 0x00, 0x00, 0x00, 0xD0, 0x01,
761 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
762 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
764 0x00, 0x00, 0x00, 0x00,
765 ];
766
767 #[test]
768 fn parse_valid_header_x86() {
769 let reader = Box::new(Cursor::new(RAW_BYTES_32.to_vec()));
770 let mut pe = PeImage::new(reader);
771
772 let offset = pe.parse_fixed_headers(0).unwrap();
773 pe.parse_sections(offset).unwrap();
774
775 assert!(pe.dos.value.is_valid());
776 assert_eq!(pe.dos.offset, 0);
777 assert_eq!(pe.dos.rva, 0);
778 assert!(pe.file.value.is_valid());
779 assert_eq!(pe.file.offset, 0x110);
780 assert_eq!(pe.file.rva, 0x110);
781 assert_eq!(pe.optional.offset, 0x128);
782 assert_eq!(pe.optional.rva, 0x128);
783
784 if let OptionalHeader::X86(opt) = pe.optional.value {
785 assert!(opt.is_valid());
786 }
787 else {
788 assert!(false, "Didn't expect OptionalHeader64");
789 }
790
791 assert_eq!(pe.data_dirs.offset, 0x188);
792 assert_eq!(pe.data_dirs.value.len(), MAX_DIRS as usize);
793 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].offset, 0x1e8);
794 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].value.rva.value, 0x0000D000);
795 assert_eq!(pe.data_dirs.value[DirectoryType::ImportAddressTable as usize].value.size.value, 0x00000174);
796
797 let sections = pe.sections.value;
798 assert_eq!(sections.len(), 6);
799 let names = [".text", ".rdata", ".data", ".gfids", ".rsrc", ".reloc"];
800 let sec_flags = [
801 Flags::CODE | Flags::MEM_READ | Flags::MEM_EXECUTE,
802 Flags::INITIALIZED_DATA | Flags::MEM_READ,
803 Flags::INITIALIZED_DATA | Flags::MEM_READ | Flags::MEM_WRITE,
804 Flags::INITIALIZED_DATA | Flags::MEM_READ,
805 Flags::INITIALIZED_DATA | Flags::MEM_READ,
806 Flags::INITIALIZED_DATA | Flags::MEM_READ | Flags::MEM_DISCARDABLE,
807 ];
808 for i in 0..6 {
809 let hf_section = §ions[i];
810 let sh = &hf_section.value;
811 assert!(sh.is_valid());
812 assert_eq!(sh.name_str().unwrap(), names[i]);
813 assert_eq!(sh.flags().unwrap(), sec_flags[i]);
814 }
815 }
816
817 #[test]
818 fn section_of_directories() {
819 let reader = Box::new(Cursor::new(RAW_BYTES_32.to_vec()));
820 let mut pe = PeImage::new(reader);
821 let offset = pe.parse_fixed_headers(0).unwrap();
822 pe.parse_sections(offset).unwrap();
823
824 assert_eq!(pe.directory_section(DirectoryType::Import).unwrap().name_str().unwrap(), ".rdata");
825 assert_eq!(pe.directory_section(DirectoryType::Resource).unwrap().name_str().unwrap(), ".rsrc");
826 assert_eq!(pe.directory_section(DirectoryType::Security).unwrap().name_str().unwrap(), ".rsrc");
827 assert_eq!(pe.directory_section(DirectoryType::Relocation).unwrap().name_str().unwrap(), ".reloc");
828 assert_eq!(pe.directory_section(DirectoryType::Debug).unwrap().name_str().unwrap(), ".rdata");
829 assert_eq!(pe.directory_section(DirectoryType::Configuration).unwrap().name_str().unwrap(), ".rdata");
830 assert_eq!(pe.directory_section(DirectoryType::ImportAddressTable).unwrap().name_str().unwrap(), ".rdata");
831 }
832}