1 | //! PE rich header handling |
2 | |
3 | use core::mem; |
4 | |
5 | use crate::pod::bytes_of_slice; |
6 | use crate::read::Bytes; |
7 | use crate::{pe, LittleEndian as LE, ReadRef, U32}; |
8 | |
9 | /// Parsed information about a Rich Header. |
10 | #[derive (Debug, Clone, Copy)] |
11 | pub 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)] |
32 | pub 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 | |
39 | impl<'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. |
83 | fn 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 | |