Files
addr2line
adler
aho_corasick
arrayvec
atty
backtrace
bitflags
camino
cargo_metadata
cargo_nextest
cargo_platform
cfg_expr
cfg_if
chrono
clap
clap_derive
color_eyre
config
crossbeam_channel
crossbeam_deque
crossbeam_epoch
crossbeam_utils
ctrlc
datatest_stable
debug_ignore
duct
either
enable_ansi_support
env_logger
eyre
fixedbitset
gimli
guppy
guppy_workspace_hack
hashbrown
humantime
humantime_serde
indent_write
indenter
indexmap
is_ci
itertools
itoa
lazy_static
lexical_core
libc
log
memchr
memoffset
miniz_oxide
nested
nextest_metadata
nextest_runner
nix
nom
num_cpus
num_integer
num_traits
object
once_cell
os_pipe
os_str_bytes
owo_colors
pathdiff
petgraph
proc_macro2
proc_macro_error
proc_macro_error_attr
quick_junit
quick_xml
quote
rayon
rayon_core
regex
regex_syntax
rustc_demangle
ryu
same_file
scopeguard
semver
serde
serde_derive
serde_json
shared_child
shellwords
smallvec
static_assertions
strip_ansi_escapes
strsim
structopt
structopt_derive
supports_color
syn
target_lexicon
target_spec
termcolor
textwrap
time
toml
twox_hash
unicode_xid
utf8parse
vte
vte_generate_state_changes
walkdir
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! PE rich header handling

use core::mem;

use crate::pod::bytes_of_slice;
use crate::read::Bytes;
use crate::{pe, LittleEndian as LE, ReadRef, U32};

/// Parsed information about a Rich Header.
#[derive(Debug, Clone, Copy)]
pub struct RichHeaderInfo<'data> {
    /// The offset at which the rich header starts.
    pub offset: usize,
    /// The length (in bytes) of the rich header.
    ///
    /// This includes the payload, but also the 16-byte start sequence and the
    /// 8-byte final "Rich" and XOR key.
    pub length: usize,
    /// The XOR key used to mask the rich header.
    ///
    /// Unless the file has been tampered with, it should be equal to a checksum
    /// of the file header.
    pub xor_key: u32,
    masked_entries: &'data [pe::MaskedRichHeaderEntry],
}

/// A PE rich header entry after it has been unmasked.
///
/// See [`pe::MaskedRichHeaderEntry`].
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct RichHeaderEntry {
    /// ID of the component.
    pub comp_id: u32,
    /// Number of times this component has been used when building this PE.
    pub count: u32,
}

impl<'data> RichHeaderInfo<'data> {
    /// Try to locate a rich header and its entries in the current PE file.
    pub fn parse<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> {
        // Locate the rich header, if any.
        // It ends with the "Rich" string and an XOR key, before the NT header.
        let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?;
        let end_marker_offset = memmem(data.0, b"Rich", 4)?;
        let xor_key = *data.read_at::<U32<LE>>(end_marker_offset + 4).ok()?;

        // It starts at the masked "DanS" string and 3 masked zeroes.
        let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE));
        let start_header = [masked_start_marker, xor_key, xor_key, xor_key];
        let start_sequence = bytes_of_slice(&start_header);
        let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?;

        // Extract the items between the markers.
        let items_offset = start_marker_offset + start_sequence.len();
        let items_len = end_marker_offset - items_offset;
        let item_count = items_len / mem::size_of::<pe::MaskedRichHeaderEntry>();
        let items = data.read_slice_at(items_offset, item_count).ok()?;
        Some(RichHeaderInfo {
            offset: start_marker_offset,
            // Includes "Rich" marker and the XOR key.
            length: end_marker_offset - start_marker_offset + 8,
            xor_key: xor_key.get(LE),
            masked_entries: items,
        })
    }

    /// Returns an iterator over the unmasked entries.
    pub fn unmasked_entries(&self) -> impl Iterator<Item = RichHeaderEntry> + 'data {
        let xor_key = self.xor_key;
        self.masked_entries
            .iter()
            .map(move |entry| RichHeaderEntry {
                comp_id: entry.masked_comp_id.get(LE) ^ xor_key,
                count: entry.masked_count.get(LE) ^ xor_key,
            })
    }
}

/// Find the offset of the first occurence of needle in the data.
///
/// The offset must have the given alignment.
fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> {
    let mut offset = 0;
    loop {
        if data.get(offset..)?.get(..needle.len())? == needle {
            return Some(offset);
        }
        offset += align;
    }
}