| 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 |  |