1 | //! PE rich header handling |
2 | |
3 | use core::mem; |
4 | |
5 | use crate::endian::{LittleEndian as LE, U32}; |
6 | use crate::pe; |
7 | use crate::pod::bytes_of_slice; |
8 | use crate::read::{Bytes, ReadRef}; |
9 | |
10 | /// Parsed information about a Rich Header. |
11 | #[derive (Debug, Clone, Copy)] |
12 | pub 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)] |
33 | pub 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 | |
40 | impl<'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. |
84 | fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> { |
85 | let mut offset: usize = 0; |
86 | loop { |
87 | if data.get(offset..)?.get(..needle.len())? == needle { |
88 | return Some(offset); |
89 | } |
90 | offset += align; |
91 | } |
92 | } |
93 | |