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