Skip to main content

vmi_os_windows/pe/
headers.rs

1//! On-disk PE header layouts.
2//!
3//! Mirrors the relevant `IMAGE_*` types from `object::pe` as
4//! `#[repr(C, packed)]` zerocopy structs and re-exports the PE constants.
5//! This is the only module in the crate that imports from the `object`
6//! crate. Everything else goes through [`crate::pe`].
7
8use object::ReadRef as _;
9pub use object::{
10    pe::{
11        IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_ARCHITECTURE,
12        IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
13        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, IMAGE_DIRECTORY_ENTRY_DEBUG,
14        IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
15        IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_GLOBALPTR, IMAGE_DIRECTORY_ENTRY_IAT,
16        IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
17        IMAGE_DIRECTORY_ENTRY_RESOURCE, IMAGE_DIRECTORY_ENTRY_SECURITY, IMAGE_DIRECTORY_ENTRY_TLS,
18        IMAGE_DOS_SIGNATURE, IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC,
19        IMAGE_NT_SIGNATURE, IMAGE_NUMBEROF_DIRECTORY_ENTRIES, IMAGE_SIZEOF_SHORT_NAME,
20    },
21    // Intentional re-export: `Export`, `ExportTable` and `ExportTarget` are good as they are.
22    read::pe::{Export, ExportTable, ExportTarget},
23};
24
25use super::error::PeError;
26
27/// Reads an `object`-crate struct field as a native scalar.
28///
29/// `impl_struct!` calls `to_endian(LE)` on every source field. The trait
30/// covers both `object`'s endian wrappers (`U16<E>`, `U32<E>`, `U64<E>`,
31/// and arrays of those) and the raw types `object` stores natively because
32/// no `U8<E>` wrapper exists (`u8` and byte arrays).
33trait ToEndian<E: ::object::Endian> {
34    /// Native counterpart of `Self`.
35    type Output;
36
37    /// Returns `self` decoded with `endian`.
38    fn to_endian(&self, endian: E) -> Self::Output;
39}
40
41impl<E: ::object::Endian> ToEndian<E> for ::object::U16<E> {
42    type Output = u16;
43
44    fn to_endian(&self, endian: E) -> Self::Output {
45        self.get(endian)
46    }
47}
48
49impl<E: ::object::Endian> ToEndian<E> for ::object::U32<E> {
50    type Output = u32;
51
52    fn to_endian(&self, endian: E) -> Self::Output {
53        self.get(endian)
54    }
55}
56
57impl<E: ::object::Endian> ToEndian<E> for ::object::U64<E> {
58    type Output = u64;
59
60    fn to_endian(&self, endian: E) -> Self::Output {
61        self.get(endian)
62    }
63}
64
65impl<E: ::object::Endian> ToEndian<E> for u8 {
66    type Output = u8;
67
68    fn to_endian(&self, _endian: E) -> Self::Output {
69        *self
70    }
71}
72
73impl<E: ::object::Endian, const N: usize> ToEndian<E> for [u8; N] {
74    type Output = [u8; N];
75
76    fn to_endian(&self, _endian: E) -> Self::Output {
77        *self
78    }
79}
80
81impl<E: ::object::Endian, const N: usize> ToEndian<E> for [::object::U16<E>; N] {
82    type Output = [u16; N];
83
84    fn to_endian(&self, endian: E) -> Self::Output {
85        std::array::from_fn(|i| self[i].get(endian))
86    }
87}
88
89/// Defines a `#[repr(C, packed)]` PE struct that mirrors an `object`-crate
90/// type and verifies the layout match at compile time.
91///
92/// Each invocation expands to:
93///
94/// - A struct with the requested fields, `#[repr(C, packed)]`, and the
95///   `zerocopy` derives needed for byte-level reads.
96/// - `impl From<SourceType> for $name` that decodes every field through
97///   [`ToEndian`].
98/// - Static asserts that `$name` and `SourceType` agree on size,
99///   alignment, and the offset of every field.
100///
101/// `#[from(SourceType)]` is required and must appear once. `#[derive(...)]`
102/// blocks are merged with the macro's own derives. Any other attributes
103/// pass through to the generated struct.
104///
105/// # Usage
106///
107/// ```ignore
108/// impl_struct! {
109///     /// Field descriptor for an export entry.
110///     #[derive(Debug, Clone, Copy)]
111///     #[from(::object::pe::ImageDataDirectory)]
112///     pub struct ImageDataDirectory {
113///         pub virtual_address: u32,
114///         pub size: u32,
115///     }
116/// }
117/// ```
118macro_rules! impl_struct {
119    //
120    // @parse - tokenize the attribute list before the struct body.
121    //
122    // Each arm matches one leading `#[...]`, appends it to the matching
123    // accumulator, and recurses with the rest. Specialized arms come
124    // before the catch-all so that `#[from]`, `#[doc]`, and `#[derive]`
125    // are recognized rather than forwarded blindly.
126    //
127
128    // Capture `#[from(SourceType)]`. Allowed once.
129    (@parse
130        from   = []
131        doc    = [$($doc:tt)*]
132        derive = [$($derive:tt)*]
133        attrs  = [$($attrs:tt)*]
134        rest   = [#[from($from:ty)] $($rest:tt)*]
135    ) => {
136        impl_struct!(@parse
137            from   = [$from]
138            doc    = [$($doc)*]
139            derive = [$($derive)*]
140            attrs  = [$($attrs)*]
141            rest   = [$($rest)*]
142        );
143    };
144
145    // Reject a duplicate `#[from(...)]`.
146    (@parse
147        from   = [$_first:ty]
148        doc    = [$($doc:tt)*]
149        derive = [$($derive:tt)*]
150        attrs  = [$($attrs:tt)*]
151        rest   = [#[from($_dup:ty)] $($rest:tt)*]
152    ) => {
153        compile_error!("impl_struct!: duplicate `#[from(...)]` attribute");
154    };
155
156    // Append a doc comment.
157    (@parse
158        from   = [$($from:tt)*]
159        doc    = [$($doc:tt)*]
160        derive = [$($derive:tt)*]
161        attrs  = [$($attrs:tt)*]
162        rest   = [#[doc = $d:literal] $($rest:tt)*]
163    ) => {
164        impl_struct!(@parse
165            from   = [$($from)*]
166            doc    = [$($doc)* #[doc = $d]]
167            derive = [$($derive)*]
168            attrs  = [$($attrs)*]
169            rest   = [$($rest)*]
170        );
171    };
172
173    // Merge a `#[derive(...)]` block into the running derive list.
174    (@parse
175        from   = [$($from:tt)*]
176        doc    = [$($doc:tt)*]
177        derive = [$($derive:tt)*]
178        attrs  = [$($attrs:tt)*]
179        rest   = [#[derive($($d:path),* $(,)?)] $($rest:tt)*]
180    ) => {
181        impl_struct!(@parse
182            from   = [$($from)*]
183            doc    = [$($doc)*]
184            derive = [$($derive)* $($d,)*]
185            attrs  = [$($attrs)*]
186            rest   = [$($rest)*]
187        );
188    };
189
190    // Forward any other `#[...]` to the generated struct verbatim.
191    (@parse
192        from   = [$($from:tt)*]
193        doc    = [$($doc:tt)*]
194        derive = [$($derive:tt)*]
195        attrs  = [$($attrs:tt)*]
196        rest   = [#[$attr:meta] $($rest:tt)*]
197    ) => {
198        impl_struct!(@parse
199            from   = [$($from)*]
200            doc    = [$($doc)*]
201            derive = [$($derive)*]
202            attrs  = [$($attrs)* #[$attr]]
203            rest   = [$($rest)*]
204        );
205    };
206
207    //
208    // @parse - terminal arms.
209    //
210    // Hit when `rest` consists of just `pub struct ...`. The first emits
211    // the struct, the `From` impl, and the layout asserts. The second
212    // errors out if `#[from(...)]` was never supplied.
213    //
214
215    (@parse
216        from   = [$from_ty:ty]
217        doc    = [$($doc:tt)*]
218        derive = [$($derive:tt)*]
219        attrs  = [$($attrs:tt)*]
220        rest   = [
221            $vis:vis struct $name:ident {
222                $(
223                    $(#[$field_attr:meta])*
224                    $field_vis:vis $field:ident : $field_ty:ty
225                ),* $(,)?
226            }
227        ]
228    ) => {
229        $($doc)*
230        #[derive(
231            $($derive)*
232            ::zerocopy::FromBytes,
233            ::zerocopy::IntoBytes,
234            ::zerocopy::Immutable,
235            ::zerocopy::KnownLayout,
236        )]
237        #[repr(C, packed)]
238        $($attrs)*
239        $vis struct $name {
240            $(
241                $(#[$field_attr])*
242                $field_vis $field: $field_ty,
243            )*
244        }
245
246        impl ::core::convert::From<$from_ty> for $name {
247            fn from(value: $from_ty) -> Self {
248                Self {
249                    $(
250                        $field: value.$field.to_endian(::object::LittleEndian),
251                    )*
252                }
253            }
254        }
255
256        const _: () = {
257            use ::core::mem::{align_of, size_of};
258
259            assert!(
260                size_of::<$name>() == size_of::<$from_ty>(),
261                concat!(stringify!($name), " size mismatch"),
262            );
263
264            assert!(
265                align_of::<$name>() == align_of::<$from_ty>(),
266                concat!(stringify!($name), " alignment mismatch"),
267            );
268        };
269
270        $(
271            const _: () = {
272                use ::core::mem::offset_of;
273
274                assert!(
275                    offset_of!($name, $field) == offset_of!($from_ty, $field),
276                    concat!(stringify!($name), " field offset mismatch: ", stringify!($field)),
277                );
278            };
279        )*
280    };
281
282    (@parse
283        from   = []
284        doc    = [$($doc:tt)*]
285        derive = [$($derive:tt)*]
286        attrs  = [$($attrs:tt)*]
287        rest   = [$vis:vis struct $name:ident { $($body:tt)* }]
288    ) => {
289        compile_error!(concat!(
290            "impl_struct!: missing `#[from(SourceType)]` attribute on `",
291            stringify!($name),
292            "`",
293        ));
294    };
295
296    //
297    // Entry arm.
298    //
299    // Placed last so any recursive call starting with `@parse` matches one
300    // of the arms above instead of falling through here. Seeds the muncher
301    // with empty accumulators so the user-facing call doesn't thread any
302    // bookkeeping arguments.
303    //
304
305    ($($input:tt)*) => {
306        impl_struct!(@parse
307            from   = []
308            doc    = []
309            derive = []
310            attrs  = []
311            rest   = [$($input)*]
312        );
313    };
314}
315
316impl_struct! {
317    /// MS-DOS executable header at the start of every PE file.
318    ///
319    /// Of the original DOS layout, only `e_magic` (must be `MZ`) and
320    /// `e_lfanew` (file offset of the NT headers) are used by the PE loader.
321    /// The rest is preserved for compatibility with the legacy format.
322    ///
323    /// # Implementation Details
324    ///
325    /// Corresponds to `_IMAGE_DOS_HEADER`.
326    #[derive(Debug, Clone, Copy)]
327    #[from(::object::pe::ImageDosHeader)]
328    pub struct ImageDosHeader {
329        /// DOS magic, must be `MZ` (`0x5A4D`).
330        pub e_magic: u16,
331
332        /// Bytes on the last page of the file.
333        pub e_cblp: u16,
334
335        /// Pages in the file.
336        pub e_cp: u16,
337
338        /// Relocation count.
339        pub e_crlc: u16,
340
341        /// Header size in 16-byte paragraphs.
342        pub e_cparhdr: u16,
343
344        /// Minimum extra paragraphs needed.
345        pub e_minalloc: u16,
346
347        /// Maximum extra paragraphs needed.
348        pub e_maxalloc: u16,
349
350        /// Initial relative SS value.
351        pub e_ss: u16,
352
353        /// Initial SP value.
354        pub e_sp: u16,
355
356        /// Header checksum.
357        pub e_csum: u16,
358
359        /// Initial IP value.
360        pub e_ip: u16,
361
362        /// Initial relative CS value.
363        pub e_cs: u16,
364
365        /// File offset of the relocation table.
366        pub e_lfarlc: u16,
367
368        /// Overlay number.
369        pub e_ovno: u16,
370
371        /// Reserved words.
372        pub e_res: [u16; 4],
373
374        /// OEM identifier qualifying `e_oeminfo`.
375        pub e_oemid: u16,
376
377        /// OEM information specific to `e_oemid`.
378        pub e_oeminfo: u16,
379
380        /// Reserved words.
381        pub e_res2: [u16; 10],
382
383        /// File offset of the NT headers.
384        pub e_lfanew: u32,
385    }
386}
387
388impl ImageDosHeader {
389    /// Parses the DOS header.
390    pub fn parse(data: &[u8], offset: &mut u64) -> Result<Self, PeError> {
391        let dos_header = data
392            .read_at::<::object::pe::ImageDosHeader>(0)
393            .copied()
394            .map_err(|_| PeError::InvalidDosHeader)?;
395
396        if dos_header.e_magic.get(::object::LittleEndian) != IMAGE_DOS_SIGNATURE {
397            return Err(PeError::InvalidDosMagic);
398        }
399
400        *offset = dos_header.nt_headers_offset() as u64;
401
402        Ok(dos_header.into())
403    }
404
405    /// Returns the file offset of the NT headers.
406    pub fn nt_headers_offset(&self) -> u32 {
407        self.e_lfanew
408    }
409}
410
411/// Top-level PE header pointed to by [`ImageDosHeader::e_lfanew`].
412///
413/// Identifies whether the image is 32-bit or 64-bit through the optional
414/// header magic.
415///
416/// # Implementation Details
417///
418/// Corresponds to `_IMAGE_NT_HEADERS32` or `_IMAGE_NT_HEADERS64`.
419#[derive(Debug, Clone, Copy)]
420pub struct ImageNtHeaders {
421    /// PE signature, `PE\0\0` (`0x00004550`).
422    pub signature: u32,
423
424    /// COFF file header.
425    pub file_header: ImageFileHeader,
426
427    /// PE32 or PE32+ optional header.
428    pub optional_header: ImageOptionalHeader,
429}
430
431impl ImageNtHeaders {
432    /// Parses the NT headers.
433    pub fn parse(data: &[u8], offset: &mut u64) -> Result<Self, PeError> {
434        let magic =
435            ::object::read::pe::optional_header_magic(data).map_err(|_| PeError::InvalidPeMagic)?;
436
437        match magic {
438            IMAGE_NT_OPTIONAL_HDR32_MAGIC => {
439                Self::parse_inner::<::object::pe::ImageNtHeaders32>(data, offset)
440            }
441            IMAGE_NT_OPTIONAL_HDR64_MAGIC => {
442                Self::parse_inner::<::object::pe::ImageNtHeaders64>(data, offset)
443            }
444            _ => Err(PeError::InvalidPeMagic),
445        }
446    }
447
448    fn parse_inner<Pe>(data: &[u8], offset: &mut u64) -> Result<Self, PeError>
449    where
450        Self: From<Pe>,
451        Pe: ::object::read::pe::ImageNtHeaders,
452    {
453        let nt_headers = data
454            .read::<Pe>(offset)
455            .copied()
456            .map_err(|_| PeError::InvalidNtHeaders)?;
457
458        if nt_headers.signature() != IMAGE_NT_SIGNATURE {
459            return Err(PeError::InvalidPeMagic);
460        }
461
462        if !nt_headers.is_valid_optional_magic() {
463            return Err(PeError::InvalidOptionalHeaderMagic);
464        }
465
466        Ok(nt_headers.into())
467    }
468}
469
470impl From<::object::pe::ImageNtHeaders32> for ImageNtHeaders {
471    fn from(value: ::object::pe::ImageNtHeaders32) -> Self {
472        Self {
473            signature: value.signature.get(::object::LittleEndian),
474            file_header: value.file_header.into(),
475            optional_header: value.optional_header.into(),
476        }
477    }
478}
479
480impl From<::object::pe::ImageNtHeaders64> for ImageNtHeaders {
481    fn from(value: ::object::pe::ImageNtHeaders64) -> Self {
482        Self {
483            signature: value.signature.get(::object::LittleEndian),
484            file_header: value.file_header.into(),
485            optional_header: value.optional_header.into(),
486        }
487    }
488}
489
490impl_struct! {
491    /// COFF file header that follows the PE signature in [`ImageNtHeaders`].
492    ///
493    /// # Implementation Details
494    ///
495    /// Corresponds to `_IMAGE_FILE_HEADER`.
496    #[derive(Debug, Clone, Copy)]
497    #[from(::object::pe::ImageFileHeader)]
498    pub struct ImageFileHeader {
499        /// Target machine architecture, `IMAGE_FILE_MACHINE_*`.
500        pub machine: u16,
501
502        /// Number of entries in the section table.
503        pub number_of_sections: u16,
504
505        /// Build timestamp, in Unix epoch seconds.
506        pub time_date_stamp: u32,
507
508        /// File offset of the COFF symbol table. Deprecated, zero in modern images.
509        pub pointer_to_symbol_table: u32,
510
511        /// Symbol table entry count. Deprecated, zero in modern images.
512        pub number_of_symbols: u32,
513
514        /// Size in bytes of the optional header that follows.
515        pub size_of_optional_header: u16,
516
517        /// `IMAGE_FILE_*` characteristic flags.
518        pub characteristics: u16,
519    }
520}
521
522impl_struct! {
523    /// PE32 optional header for 32-bit images.
524    ///
525    /// Layout differs from [`ImageOptionalHeader64`] in `image_base` and the
526    /// stack/heap size fields, which are 32-bit here, and in the presence
527    /// of `base_of_data`.
528    ///
529    /// # Implementation Details
530    ///
531    /// Corresponds to `_IMAGE_OPTIONAL_HEADER`.
532    #[derive(Debug, Clone, Copy)]
533    #[from(::object::pe::ImageOptionalHeader32)]
534    pub struct ImageOptionalHeader32 {
535        /// PE32 magic, `0x10b`.
536        pub magic: u16,
537
538        /// Linker version, major part.
539        pub major_linker_version: u8,
540
541        /// Linker version, minor part.
542        pub minor_linker_version: u8,
543
544        /// Combined size of all code sections, in bytes.
545        pub size_of_code: u32,
546
547        /// Combined size of all initialized data sections, in bytes.
548        pub size_of_initialized_data: u32,
549
550        /// Combined size of all uninitialized data sections, in bytes.
551        pub size_of_uninitialized_data: u32,
552
553        /// RVA of the entry point.
554        pub address_of_entry_point: u32,
555
556        /// RVA of the start of the code section.
557        pub base_of_code: u32,
558
559        /// RVA of the start of the data section.
560        pub base_of_data: u32,
561
562        /// Preferred load address.
563        pub image_base: u32,
564
565        /// Section alignment when loaded into memory, in bytes.
566        pub section_alignment: u32,
567
568        /// Section alignment within the file, in bytes.
569        pub file_alignment: u32,
570
571        /// Required OS version, major part.
572        pub major_operating_system_version: u16,
573
574        /// Required OS version, minor part.
575        pub minor_operating_system_version: u16,
576
577        /// Image version, major part.
578        pub major_image_version: u16,
579
580        /// Image version, minor part.
581        pub minor_image_version: u16,
582
583        /// Required subsystem version, major part.
584        pub major_subsystem_version: u16,
585
586        /// Required subsystem version, minor part.
587        pub minor_subsystem_version: u16,
588
589        /// Reserved, must be zero.
590        pub win32_version_value: u32,
591
592        /// Total size of the image when loaded, including headers.
593        pub size_of_image: u32,
594
595        /// Combined size of the DOS, NT, and section headers, file-aligned.
596        pub size_of_headers: u32,
597
598        /// Image checksum. Often zero outside system images.
599        pub check_sum: u32,
600
601        /// `IMAGE_SUBSYSTEM_*` value identifying the runtime environment.
602        pub subsystem: u16,
603
604        /// `IMAGE_DLLCHARACTERISTICS_*` flags.
605        pub dll_characteristics: u16,
606
607        /// Reserved stack size.
608        pub size_of_stack_reserve: u32,
609
610        /// Initial committed stack size.
611        pub size_of_stack_commit: u32,
612
613        /// Reserved heap size.
614        pub size_of_heap_reserve: u32,
615
616        /// Initial committed heap size.
617        pub size_of_heap_commit: u32,
618
619        /// Reserved, must be zero.
620        pub loader_flags: u32,
621
622        /// Length of the data directory array that follows the optional header.
623        pub number_of_rva_and_sizes: u32,
624    }
625}
626
627impl_struct! {
628    /// PE32+ optional header for 64-bit images.
629    ///
630    /// Layout differs from [`ImageOptionalHeader32`] in `image_base` and the
631    /// stack/heap size fields, which are 64-bit here, and in the absence
632    /// of `base_of_data`.
633    ///
634    /// # Implementation Details
635    ///
636    /// Corresponds to `_IMAGE_OPTIONAL_HEADER64`.
637    #[derive(Debug, Clone, Copy)]
638    #[from(::object::pe::ImageOptionalHeader64)]
639    pub struct ImageOptionalHeader64 {
640        /// PE32+ magic, `0x20b`.
641        pub magic: u16,
642
643        /// Linker version, major part.
644        pub major_linker_version: u8,
645
646        /// Linker version, minor part.
647        pub minor_linker_version: u8,
648
649        /// Combined size of all code sections, in bytes.
650        pub size_of_code: u32,
651
652        /// Combined size of all initialized data sections, in bytes.
653        pub size_of_initialized_data: u32,
654
655        /// Combined size of all uninitialized data sections, in bytes.
656        pub size_of_uninitialized_data: u32,
657
658        /// RVA of the entry point.
659        pub address_of_entry_point: u32,
660
661        /// RVA of the start of the code section.
662        pub base_of_code: u32,
663
664        /// Preferred load address.
665        pub image_base: u64,
666
667        /// Section alignment when loaded into memory, in bytes.
668        pub section_alignment: u32,
669
670        /// Section alignment within the file, in bytes.
671        pub file_alignment: u32,
672
673        /// Required OS version, major part.
674        pub major_operating_system_version: u16,
675
676        /// Required OS version, minor part.
677        pub minor_operating_system_version: u16,
678
679        /// Image version, major part.
680        pub major_image_version: u16,
681
682        /// Image version, minor part.
683        pub minor_image_version: u16,
684
685        /// Required subsystem version, major part.
686        pub major_subsystem_version: u16,
687
688        /// Required subsystem version, minor part.
689        pub minor_subsystem_version: u16,
690
691        /// Reserved, must be zero.
692        pub win32_version_value: u32,
693
694        /// Total size of the image when loaded, including headers.
695        pub size_of_image: u32,
696
697        /// Combined size of the DOS, NT, and section headers, file-aligned.
698        pub size_of_headers: u32,
699
700        /// Image checksum. Often zero outside system images.
701        pub check_sum: u32,
702
703        /// `IMAGE_SUBSYSTEM_*` value identifying the runtime environment.
704        pub subsystem: u16,
705
706        /// `IMAGE_DLLCHARACTERISTICS_*` flags.
707        pub dll_characteristics: u16,
708
709        /// Reserved stack size.
710        pub size_of_stack_reserve: u64,
711
712        /// Initial committed stack size.
713        pub size_of_stack_commit: u64,
714
715        /// Reserved heap size.
716        pub size_of_heap_reserve: u64,
717
718        /// Initial committed heap size.
719        pub size_of_heap_commit: u64,
720
721        /// Reserved, must be zero.
722        pub loader_flags: u32,
723
724        /// Length of the data directory array that follows the optional header.
725        pub number_of_rva_and_sizes: u32,
726    }
727}
728
729/// Optional header in either PE32 or PE32+ form.
730///
731/// Discriminated by the magic in [`ImageOptionalHeader::magic`].
732///
733/// # Implementation Details
734///
735/// Corresponds to `_IMAGE_OPTIONAL_HEADER` or `_IMAGE_OPTIONAL_HEADER64`.
736#[derive(Debug, Clone, Copy)]
737pub enum ImageOptionalHeader {
738    /// PE32 optional header (32-bit image).
739    ImageOptionalHeader32(ImageOptionalHeader32),
740
741    /// PE32+ optional header (64-bit image).
742    ImageOptionalHeader64(ImageOptionalHeader64),
743}
744
745impl From<::object::pe::ImageOptionalHeader32> for ImageOptionalHeader {
746    fn from(value: ::object::pe::ImageOptionalHeader32) -> Self {
747        Self::ImageOptionalHeader32(value.into())
748    }
749}
750
751impl From<::object::pe::ImageOptionalHeader64> for ImageOptionalHeader {
752    fn from(value: ::object::pe::ImageOptionalHeader64) -> Self {
753        Self::ImageOptionalHeader64(value.into())
754    }
755}
756
757impl ImageOptionalHeader {
758    /// Returns the magic identifying PE32 (`0x10b`) or PE32+ (`0x20b`).
759    pub fn magic(&self) -> u16 {
760        match self {
761            Self::ImageOptionalHeader32(hdr32) => hdr32.magic,
762            Self::ImageOptionalHeader64(hdr64) => hdr64.magic,
763        }
764    }
765
766    /// Returns the major linker version.
767    pub fn major_linker_version(&self) -> u8 {
768        match self {
769            Self::ImageOptionalHeader32(hdr32) => hdr32.major_linker_version,
770            Self::ImageOptionalHeader64(hdr64) => hdr64.major_linker_version,
771        }
772    }
773
774    /// Returns the minor linker version.
775    pub fn minor_linker_version(&self) -> u8 {
776        match self {
777            Self::ImageOptionalHeader32(hdr32) => hdr32.minor_linker_version,
778            Self::ImageOptionalHeader64(hdr64) => hdr64.minor_linker_version,
779        }
780    }
781
782    /// Returns the combined size of all code sections, in bytes.
783    pub fn size_of_code(&self) -> u32 {
784        match self {
785            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_code,
786            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_code,
787        }
788    }
789
790    /// Returns the combined size of all initialized data sections, in bytes.
791    pub fn size_of_initialized_data(&self) -> u32 {
792        match self {
793            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_initialized_data,
794            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_initialized_data,
795        }
796    }
797
798    /// Returns the combined size of all uninitialized data sections, in bytes.
799    pub fn size_of_uninitialized_data(&self) -> u32 {
800        match self {
801            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_uninitialized_data,
802            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_uninitialized_data,
803        }
804    }
805
806    /// Returns the RVA of the entry point.
807    ///
808    /// When the image is loaded, this is the actual memory address.
809    pub fn address_of_entry_point(&self) -> u32 {
810        match self {
811            Self::ImageOptionalHeader32(hdr32) => hdr32.address_of_entry_point,
812            Self::ImageOptionalHeader64(hdr64) => hdr64.address_of_entry_point,
813        }
814    }
815
816    /// Returns the RVA of the start of the code section.
817    pub fn base_of_code(&self) -> u32 {
818        match self {
819            Self::ImageOptionalHeader32(hdr32) => hdr32.base_of_code,
820            Self::ImageOptionalHeader64(hdr64) => hdr64.base_of_code,
821        }
822    }
823
824    /// Returns the RVA of the start of the data section (PE32 only).
825    pub fn base_of_data(&self) -> Option<u32> {
826        match self {
827            Self::ImageOptionalHeader32(hdr32) => Some(hdr32.base_of_data),
828            Self::ImageOptionalHeader64(_) => None,
829        }
830    }
831
832    /// Returns the preferred load address in memory.
833    ///
834    /// The actual base address may change due to ASLR or conflicts.
835    pub fn image_base(&self) -> u64 {
836        match self {
837            Self::ImageOptionalHeader32(hdr32) => hdr32.image_base.into(),
838            Self::ImageOptionalHeader64(hdr64) => hdr64.image_base,
839        }
840    }
841
842    /// Returns the section alignment in memory, in bytes.
843    ///
844    /// This may change when the image is loaded by the OS.
845    pub fn section_alignment(&self) -> u32 {
846        match self {
847            Self::ImageOptionalHeader32(hdr32) => hdr32.section_alignment,
848            Self::ImageOptionalHeader64(hdr64) => hdr64.section_alignment,
849        }
850    }
851
852    /// Returns the section alignment on disk, in bytes.
853    pub fn file_alignment(&self) -> u32 {
854        match self {
855            Self::ImageOptionalHeader32(hdr32) => hdr32.file_alignment,
856            Self::ImageOptionalHeader64(hdr64) => hdr64.file_alignment,
857        }
858    }
859
860    /// Returns the major operating system version required.
861    pub fn major_operating_system_version(&self) -> u16 {
862        match self {
863            Self::ImageOptionalHeader32(hdr32) => hdr32.major_operating_system_version,
864            Self::ImageOptionalHeader64(hdr64) => hdr64.major_operating_system_version,
865        }
866    }
867
868    /// Returns the minor operating system version required.
869    pub fn minor_operating_system_version(&self) -> u16 {
870        match self {
871            Self::ImageOptionalHeader32(hdr32) => hdr32.minor_operating_system_version,
872            Self::ImageOptionalHeader64(hdr64) => hdr64.minor_operating_system_version,
873        }
874    }
875
876    /// Returns the major image version.
877    pub fn major_image_version(&self) -> u16 {
878        match self {
879            Self::ImageOptionalHeader32(hdr32) => hdr32.major_image_version,
880            Self::ImageOptionalHeader64(hdr64) => hdr64.major_image_version,
881        }
882    }
883
884    /// Returns the minor image version.
885    pub fn minor_image_version(&self) -> u16 {
886        match self {
887            Self::ImageOptionalHeader32(hdr32) => hdr32.minor_image_version,
888            Self::ImageOptionalHeader64(hdr64) => hdr64.minor_image_version,
889        }
890    }
891
892    /// Returns the major subsystem version.
893    pub fn major_subsystem_version(&self) -> u16 {
894        match self {
895            Self::ImageOptionalHeader32(hdr32) => hdr32.major_subsystem_version,
896            Self::ImageOptionalHeader64(hdr64) => hdr64.major_subsystem_version,
897        }
898    }
899
900    /// Returns the minor subsystem version.
901    pub fn minor_subsystem_version(&self) -> u16 {
902        match self {
903            Self::ImageOptionalHeader32(hdr32) => hdr32.minor_subsystem_version,
904            Self::ImageOptionalHeader64(hdr64) => hdr64.minor_subsystem_version,
905        }
906    }
907
908    /// Returns the reserved Windows version value (should be zero).
909    pub fn win32_version_value(&self) -> u32 {
910        match self {
911            Self::ImageOptionalHeader32(hdr32) => hdr32.win32_version_value,
912            Self::ImageOptionalHeader64(hdr64) => hdr64.win32_version_value,
913        }
914    }
915
916    /// Returns the total size of the image in memory, including headers.
917    pub fn size_of_image(&self) -> u32 {
918        match self {
919            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_image,
920            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_image,
921        }
922    }
923
924    /// Returns the size of all headers (DOS header, PE header, section table).
925    pub fn size_of_headers(&self) -> u32 {
926        match self {
927            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_headers,
928            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_headers,
929        }
930    }
931
932    /// Returns the checksum of the image.
933    pub fn check_sum(&self) -> u32 {
934        match self {
935            Self::ImageOptionalHeader32(hdr32) => hdr32.check_sum,
936            Self::ImageOptionalHeader64(hdr64) => hdr64.check_sum,
937        }
938    }
939
940    /// Returns the `IMAGE_SUBSYSTEM_*` value identifying the runtime environment.
941    pub fn subsystem(&self) -> u16 {
942        match self {
943            Self::ImageOptionalHeader32(hdr32) => hdr32.subsystem,
944            Self::ImageOptionalHeader64(hdr64) => hdr64.subsystem,
945        }
946    }
947
948    /// Returns the `IMAGE_DLLCHARACTERISTICS_*` flags.
949    pub fn dll_characteristics(&self) -> u16 {
950        match self {
951            Self::ImageOptionalHeader32(hdr32) => hdr32.dll_characteristics,
952            Self::ImageOptionalHeader64(hdr64) => hdr64.dll_characteristics,
953        }
954    }
955
956    /// Returns the reserved stack size.
957    pub fn size_of_stack_reserve(&self) -> u64 {
958        match self {
959            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_stack_reserve.into(),
960            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_stack_reserve,
961        }
962    }
963
964    /// Returns the committed stack size.
965    pub fn size_of_stack_commit(&self) -> u64 {
966        match self {
967            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_stack_commit.into(),
968            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_stack_commit,
969        }
970    }
971
972    /// Returns the reserved heap size.
973    pub fn size_of_heap_reserve(&self) -> u64 {
974        match self {
975            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_heap_reserve.into(),
976            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_heap_reserve,
977        }
978    }
979
980    /// Returns the committed heap size.
981    pub fn size_of_heap_commit(&self) -> u64 {
982        match self {
983            Self::ImageOptionalHeader32(hdr32) => hdr32.size_of_heap_commit.into(),
984            Self::ImageOptionalHeader64(hdr64) => hdr64.size_of_heap_commit,
985        }
986    }
987
988    /// Returns the reserved loader flags (should be zero).
989    pub fn loader_flags(&self) -> u32 {
990        match self {
991            Self::ImageOptionalHeader32(hdr32) => hdr32.loader_flags,
992            Self::ImageOptionalHeader64(hdr64) => hdr64.loader_flags,
993        }
994    }
995
996    /// Returns the number of data directories in the optional header.
997    pub fn number_of_rva_and_sizes(&self) -> u32 {
998        match self {
999            Self::ImageOptionalHeader32(hdr32) => hdr32.number_of_rva_and_sizes,
1000            Self::ImageOptionalHeader64(hdr64) => hdr64.number_of_rva_and_sizes,
1001        }
1002    }
1003
1004    /// Returns the size of the optional header.
1005    pub fn size(&self) -> usize {
1006        match self {
1007            Self::ImageOptionalHeader32(_) => size_of::<ImageOptionalHeader32>(),
1008            Self::ImageOptionalHeader64(_) => size_of::<ImageOptionalHeader64>(),
1009        }
1010    }
1011}
1012
1013impl_struct! {
1014    /// Single entry in the PE section table.
1015    ///
1016    /// # Implementation Details
1017    ///
1018    /// Corresponds to `_IMAGE_SECTION_HEADER`.
1019    #[derive(Debug, Clone, Copy)]
1020    #[from(::object::pe::ImageSectionHeader)]
1021    pub struct ImageSectionHeader {
1022        /// Section name, null-padded to 8 bytes.
1023        pub name: [u8; IMAGE_SIZEOF_SHORT_NAME],
1024
1025        /// Section size in memory before alignment.
1026        pub virtual_size: u32,
1027
1028        /// RVA of the section when loaded.
1029        pub virtual_address: u32,
1030
1031        /// Size of the section's raw data in the file, file-aligned.
1032        pub size_of_raw_data: u32,
1033
1034        /// File offset of the section's raw data.
1035        pub pointer_to_raw_data: u32,
1036
1037        /// File offset of the relocation table. Zero for executables.
1038        pub pointer_to_relocations: u32,
1039
1040        /// File offset of the COFF line numbers. Zero in modern images.
1041        pub pointer_to_linenumbers: u32,
1042
1043        /// Relocation entry count. Zero for executables.
1044        pub number_of_relocations: u16,
1045
1046        /// Line-number entry count. Zero in modern images.
1047        pub number_of_linenumbers: u16,
1048
1049        /// `IMAGE_SCN_*` flags.
1050        pub characteristics: u32,
1051    }
1052}
1053
1054//
1055// The methods on `ImageSectionHeader` in this impl block (including their
1056// documentation) are copied from the `object` crate v0.39.1, Copyright (c)
1057// 2015 The Gimli Developers, used under the MIT license. See NOTICES at the
1058// repo root for the full license text.
1059//
1060
1061impl ImageSectionHeader {
1062    /// Returns the offset and size of the section in a PE file.
1063    ///
1064    /// The size of the range will be the minimum of the file size and virtual
1065    /// size.
1066    pub fn pe_file_range(&self) -> (u32, u32) {
1067        // Pointer and size will be zero for uninitialized data.
1068        // We don't need to validate this.
1069        let offset = self.pointer_to_raw_data;
1070        let size = std::cmp::min(self.virtual_size, self.size_of_raw_data);
1071        (offset, size)
1072    }
1073
1074    /// Returns the file offset of the given virtual address, and the remaining
1075    /// size up to the end of the section.
1076    ///
1077    /// Returns `None` if the section does not contain the address.
1078    pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
1079        let section_va = self.virtual_address;
1080        let offset = va.checked_sub(section_va)?;
1081        let (section_offset, section_size) = self.pe_file_range();
1082
1083        // Address must be within section (and not at its end).
1084        if offset < section_size {
1085            Some((section_offset.checked_add(offset)?, section_size - offset))
1086        }
1087        else {
1088            None
1089        }
1090    }
1091
1092    /// Returns the section data in a PE file.
1093    ///
1094    /// The length of the data will be the minimum of the file size and virtual size.
1095    pub fn pe_data<'data>(&self, data: &'data [u8]) -> Option<&'data [u8]> {
1096        let (offset, size) = self.pe_file_range();
1097        data.read_bytes_at(offset.into(), size.into()).ok()
1098    }
1099
1100    /// Returns the data starting at the given virtual address, up to the end
1101    /// of the section.
1102    ///
1103    /// Ignores sections with invalid data.
1104    ///
1105    /// Returns `None` if the section does not contain the address.
1106    pub fn pe_data_at<'data>(&self, data: &'data [u8], va: u32) -> Option<&'data [u8]> {
1107        let (offset, size) = self.pe_file_range_at(va)?;
1108        data.read_bytes_at(offset.into(), size.into()).ok()
1109    }
1110}
1111
1112impl_struct! {
1113    /// Single entry in the data directory array of the optional header.
1114    ///
1115    /// Locates a standard PE table such as the export, import, or exception
1116    /// directory by RVA and size.
1117    ///
1118    /// # Implementation Details
1119    ///
1120    /// Corresponds to `_IMAGE_DATA_DIRECTORY`.
1121    #[derive(Debug, Clone, Copy)]
1122    #[from(::object::pe::ImageDataDirectory)]
1123    pub struct ImageDataDirectory {
1124        /// RVA of the table.
1125        pub virtual_address: u32,
1126
1127        /// Table size in bytes.
1128        pub size: u32,
1129    }
1130}
1131
1132impl_struct! {
1133    /// Single entry in the debug directory.
1134    ///
1135    /// # Implementation Details
1136    ///
1137    /// Corresponds to `_IMAGE_DEBUG_DIRECTORY`.
1138    #[derive(Debug, Clone, Copy)]
1139    #[from(::object::pe::ImageDebugDirectory)]
1140    pub struct ImageDebugDirectory {
1141        /// Reserved, must be zero.
1142        pub characteristics: u32,
1143
1144        /// Build timestamp, in Unix epoch seconds.
1145        pub time_date_stamp: u32,
1146
1147        /// Debug data format version, major part.
1148        pub major_version: u16,
1149
1150        /// Debug data format version, minor part.
1151        pub minor_version: u16,
1152
1153        /// `IMAGE_DEBUG_TYPE_*` identifying the debug data format.
1154        pub typ: u32,
1155
1156        /// Size of the debug data, in bytes.
1157        pub size_of_data: u32,
1158
1159        /// RVA of the debug data when loaded.
1160        pub address_of_raw_data: u32,
1161
1162        /// File offset of the debug data.
1163        pub pointer_to_raw_data: u32,
1164    }
1165}
1166
1167impl_struct! {
1168    /// Single entry in the `.pdata` exception directory.
1169    ///
1170    /// Maps a function's RVA range to its `UNWIND_INFO`.
1171    ///
1172    /// # Implementation Details
1173    ///
1174    /// Corresponds to `_IMAGE_RUNTIME_FUNCTION_ENTRY`.
1175    #[derive(Debug, Clone, Copy)]
1176    #[from(::object::pe::ImageRuntimeFunctionEntry)]
1177    pub struct ImageRuntimeFunctionEntry {
1178        /// Function start RVA.
1179        pub begin_address: u32,
1180
1181        /// Function end RVA, exclusive.
1182        pub end_address: u32,
1183
1184        /// RVA of the function's `UNWIND_INFO`.
1185        pub unwind_info_address_or_data: u32,
1186    }
1187}