object/read/pe/
rich.rs

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