1//! PE rich header handling
2
3use core::mem;
4
5use crate::pod::bytes_of_slice;
6use crate::read::Bytes;
7use crate::{pe, LittleEndian as LE, ReadRef, U32};
8
9/// Parsed information about a Rich Header.
10#[derive(Debug, Clone, Copy)]
11pub struct RichHeaderInfo<'data> {
12 /// The offset at which the rich header starts.
13 pub offset: usize,
14 /// The length (in bytes) of the rich header.
15 ///
16 /// This includes the payload, but also the 16-byte start sequence and the
17 /// 8-byte final "Rich" and XOR key.
18 pub length: usize,
19 /// The XOR key used to mask the rich header.
20 ///
21 /// Unless the file has been tampered with, it should be equal to a checksum
22 /// of the file header.
23 pub xor_key: u32,
24 masked_entries: &'data [pe::MaskedRichHeaderEntry],
25}
26
27/// A PE rich header entry after it has been unmasked.
28///
29/// See [`pe::MaskedRichHeaderEntry`].
30#[derive(Debug, Clone, Copy)]
31#[repr(C)]
32pub struct RichHeaderEntry {
33 /// ID of the component.
34 pub comp_id: u32,
35 /// Number of times this component has been used when building this PE.
36 pub count: u32,
37}
38
39impl<'data> RichHeaderInfo<'data> {
40 /// Try to locate a rich header and its entries in the current PE file.
41 pub fn parse<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> {
42 // Locate the rich header, if any.
43 // It ends with the "Rich" string and an XOR key, before the NT header.
44 let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?;
45 let end_marker_offset = memmem(data.0, b"Rich", 4)?;
46 let xor_key = *data.read_at::<U32<LE>>(end_marker_offset + 4).ok()?;
47
48 // It starts at the masked "DanS" string and 3 masked zeroes.
49 let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE));
50 let start_header = [masked_start_marker, xor_key, xor_key, xor_key];
51 let start_sequence = bytes_of_slice(&start_header);
52 let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?;
53
54 // Extract the items between the markers.
55 let items_offset = start_marker_offset + start_sequence.len();
56 let items_len = end_marker_offset - items_offset;
57 let item_count = items_len / mem::size_of::<pe::MaskedRichHeaderEntry>();
58 let items = data.read_slice_at(items_offset, item_count).ok()?;
59 Some(RichHeaderInfo {
60 offset: start_marker_offset,
61 // Includes "Rich" marker and the XOR key.
62 length: end_marker_offset - start_marker_offset + 8,
63 xor_key: xor_key.get(LE),
64 masked_entries: items,
65 })
66 }
67
68 /// Returns an iterator over the unmasked entries.
69 pub fn unmasked_entries(&self) -> impl Iterator<Item = RichHeaderEntry> + 'data {
70 let xor_key = self.xor_key;
71 self.masked_entries
72 .iter()
73 .map(move |entry| RichHeaderEntry {
74 comp_id: entry.masked_comp_id.get(LE) ^ xor_key,
75 count: entry.masked_count.get(LE) ^ xor_key,
76 })
77 }
78}
79
80/// Find the offset of the first occurrence of needle in the data.
81///
82/// The offset must have the given alignment.
83fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> {
84 let mut offset: usize = 0;
85 loop {
86 if data.get(offset..)?.get(..needle.len())? == needle {
87 return Some(offset);
88 }
89 offset += align;
90 }
91}
92