authenticode/
pe_object.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crate::pe::{PeOffsetError, PeOffsets, PeTrait};
10use crate::usize_from_u32;
11use core::mem;
12use core::ops::Range;
13use object::pe::{ImageDataDirectory, IMAGE_DIRECTORY_ENTRY_SECURITY};
14use object::read::pe::ImageOptionalHeader;
15use object::read::pe::{ImageNtHeaders, PeFile};
16use object::{pod, LittleEndian, SectionIndex};
17
18impl<'data, I> PeTrait for PeFile<'data, I>
19where
20    I: ImageNtHeaders,
21{
22    fn data(&self) -> &'data [u8] {
23        self.data()
24    }
25
26    fn num_sections(&self) -> usize {
27        self.section_table().len()
28    }
29
30    fn section_data_range(
31        &self,
32        index: usize,
33    ) -> Result<Range<usize>, PeOffsetError> {
34        let section = self
35            .section_table()
36            .section(SectionIndex(index))
37            .expect("invalid index");
38        let start =
39            usize_from_u32(section.pointer_to_raw_data.get(LittleEndian));
40        let size = usize_from_u32(section.size_of_raw_data.get(LittleEndian));
41        let end = start.checked_add(size).ok_or(PeOffsetError)?;
42        Ok(start..end)
43    }
44
45    fn certificate_table_range(
46        &self,
47    ) -> Result<Option<Range<usize>>, PeOffsetError> {
48        if let Some(dir) = self.data_directory(IMAGE_DIRECTORY_ENTRY_SECURITY) {
49            let start = usize_from_u32(dir.virtual_address.get(LittleEndian));
50            let size = usize_from_u32(dir.size.get(LittleEndian));
51            let end = start.checked_add(size).ok_or(PeOffsetError)?;
52            Ok(Some(start..end))
53        } else {
54            Ok(None)
55        }
56    }
57
58    fn offsets(&self) -> Result<PeOffsets, PeOffsetError> {
59        object_offsets_impl(self).ok_or(PeOffsetError)
60    }
61}
62
63fn object_offsets_impl<I>(pe: &PeFile<I>) -> Option<PeOffsets>
64where
65    I: ImageNtHeaders,
66{
67    // Calculate the offset from the start of the pe data to the
68    // beginning of `bytes`.
69    let get_offset = |bytes: &[u8]| -> Option<usize> {
70        let base = pe.data().as_ptr() as usize;
71        let bytes_start = bytes.as_ptr() as usize;
72        bytes_start.checked_sub(base)
73    };
74
75    // Get checksum offset.
76    let optional_header = pe.nt_headers().optional_header();
77    let optional_header_bytes = pod::bytes_of(optional_header);
78    let optional_header_offset = get_offset(optional_header_bytes)?;
79    let check_sum_offset = optional_header_offset.checked_add(
80        // The offset of the `check_sum` field is the same within both
81        // the 32-bit and 64-bit headers.
82        64,
83    )?;
84
85    // Hash from checksum to the security data directory.
86    let data_dirs_offset =
87        optional_header_offset.checked_add(optional_header_bytes.len())?;
88    let sec_dir_offset = data_dirs_offset.checked_add(
89        mem::size_of::<ImageDataDirectory>()
90            .checked_mul(IMAGE_DIRECTORY_ENTRY_SECURITY)?,
91    )?;
92
93    // Hash from the security data directory to the end of the header.
94    let sec_dir_size = mem::size_of::<ImageDataDirectory>();
95    let size_of_headers =
96        usize_from_u32(pe.nt_headers().optional_header().size_of_headers());
97
98    Some(PeOffsets {
99        check_sum: check_sum_offset,
100        after_check_sum: check_sum_offset.checked_add(mem::size_of::<u32>())?,
101
102        security_data_dir: sec_dir_offset,
103        after_security_data_dir: sec_dir_offset.checked_add(sec_dir_size)?,
104
105        after_header: size_of_headers,
106    })
107}