LCOV - code coverage report
Current view: top level - src/pe - binary.rs (source / functions) Coverage Total Hit
Test: lief.lcov Lines: 67.9 % 196 133
Test Date: 2025-01-11:00:00:00 Functions: 84.8 % 46 39

            Line data    Source code
       1              : use lief_ffi as ffi;
       2              : 
       3              : use num_traits::{cast, Num};
       4              : use std::mem::size_of;
       5              : use std::pin::Pin;
       6              : use std::path::Path;
       7              : 
       8              : use super::data_directory::{DataDirectories, DataDirectory};
       9              : use super::debug;
      10              : use super::delay_import::{DelayImport, DelayImports};
      11              : use super::export::Export;
      12              : use super::import::{Import, Imports};
      13              : use super::load_configuration::LoadConfiguration;
      14              : use super::relocation::Relocations;
      15              : use super::resources::Manager as ResourcesManager;
      16              : use super::resources::Node as ResourceNode;
      17              : use super::rich_header::RichHeader;
      18              : use super::section::{Section, Sections};
      19              : use super::signature::Signatures;
      20              : use super::tls::TLS;
      21              : use super::{data_directory, signature};
      22              : 
      23              : use crate::common::{into_optional, FromFFI};
      24              : use crate::declare_iterator;
      25              : use crate::generic;
      26              : use crate::to_conv_result;
      27              : use crate::to_slice;
      28              : use crate::Error;
      29              : 
      30              : use super::Algorithms;
      31              : use super::{DosHeader, Header, OptionalHeader};
      32              : 
      33              : /// This is the main interface to read and write PE binary attributes.
      34              : ///
      35              : /// Note that this structure implements the [`generic::Binary`] trait from which other generic
      36              : /// functions are exposed
      37              : ///
      38              : /// ```
      39              : /// fn use_trait(pe: &Binary) {
      40              : ///     let generic_binary = pe as &dyn generic::Binary;
      41              : ///     println!("{}", generic_binary.entrypoint());
      42              : /// }
      43              : ///
      44              : /// ```
      45              : pub struct Binary {
      46              :     ptr: cxx::UniquePtr<ffi::PE_Binary>,
      47              : }
      48              : 
      49              : impl FromFFI<ffi::PE_Binary> for Binary {
      50          160 :     fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_Binary>) -> Self {
      51          160 :         Self { ptr }
      52          160 :     }
      53              : }
      54              : 
      55              : impl Binary {
      56              :     /// Parse from a file path given as a string
      57           80 :     pub fn parse(path: &str) -> Option<Self> {
      58           80 :         let ffi = ffi::PE_Binary::parse(path);
      59           80 :         if ffi.is_null() {
      60            0 :             return None;
      61           80 :         }
      62           80 :         Some(Binary::from_ffi(ffi))
      63           80 :     }
      64              : 
      65              :     /// DosHeader which starts the PE files
      66           80 :     pub fn dos_header(&self) -> DosHeader {
      67           80 :         DosHeader::from_ffi(self.ptr.dos_header())
      68           80 :     }
      69              : 
      70              :     /// Header that follows the [`Binary::header`]. It is named
      71              :     /// *optional* from the COFF specfication but it is mandatory in a PE file.
      72           80 :     pub fn optional_header(&self) -> OptionalHeader {
      73           80 :         OptionalHeader::from_ffi(self.ptr.optional_header())
      74           80 :     }
      75              : 
      76              :     /// Re-compute the value of [`OptionalHeader::checksum`]
      77           80 :     pub fn compute_checksum(&self) -> u32 {
      78           80 :         self.ptr.compute_checksum()
      79           80 :     }
      80              : 
      81              :     /// Next header after the [`Binary::dos_header`]
      82           80 :     pub fn header(&self) -> Header {
      83           80 :         Header::from_ffi(self.ptr.header())
      84           80 :     }
      85              : 
      86              :     /// Return TLS information if present
      87           80 :     pub fn tls(&self) -> Option<TLS> {
      88           80 :         into_optional(self.ptr.tls())
      89           80 :     }
      90              : 
      91              :     /// Return rich header information if present.
      92           80 :     pub fn rich_header(&self) -> Option<RichHeader> {
      93           80 :         into_optional(self.ptr.rich_header())
      94           80 :     }
      95              : 
      96              :     /// Return export information
      97           80 :     pub fn export(&self) -> Option<Export> {
      98           80 :         into_optional(self.ptr.get_export())
      99           80 :     }
     100              : 
     101              :     /// Return the root of the PE's resource's tree
     102          160 :     pub fn resources(&self) -> Option<ResourceNode> {
     103          160 :         into_optional(self.ptr.resources())
     104          160 :     }
     105              : 
     106              :     /// Return a manager interface to read and manipulate the resources tree with a user friendly
     107              :     /// interface.
     108           80 :     pub fn resources_manager(&self) -> Option<ResourcesManager> {
     109           80 :         into_optional(self.ptr.resources_manager())
     110           80 :     }
     111              : 
     112              :     /// Return the imports as an **iterator** over the [`Import`] structure
     113           80 :     pub fn imports(&self) -> Imports {
     114           80 :         Imports::new(self.ptr.imports())
     115           80 :     }
     116              : 
     117              :     /// Return the data directories as an iterator over the [`DataDirectory`] structure
     118           80 :     pub fn data_directories(&self) -> DataDirectories {
     119           80 :         DataDirectories::new(self.ptr.data_directories())
     120           80 :     }
     121              : 
     122              :     /// Return the sections as an iterator over the [`Section`] structure
     123           80 :     pub fn sections(&self) -> Sections {
     124           80 :         Sections::new(self.ptr.sections())
     125           80 :     }
     126              : 
     127              :     /// Return the relocations as an iterator over the [`super::Relocation`] structure
     128           80 :     pub fn relocations(&self) -> Relocations {
     129           80 :         Relocations::new(self.ptr.relocations())
     130           80 :     }
     131              : 
     132              :     /// Return the delayed imports as an iterator over the [`DelayImport`] structure
     133           80 :     pub fn delay_imports(&self) -> DelayImports {
     134           80 :         DelayImports::new(self.ptr.delay_imports())
     135           80 :     }
     136              : 
     137              :     /// Return an iterator over the [`signature::Signature`] if the current PE is authenticode-signed.
     138           90 :     pub fn signatures(&self) -> Signatures {
     139           90 :         Signatures::new(self.ptr.signatures())
     140           90 :     }
     141              : 
     142              :     /// Return an iterator over the [`debug::Entries`] of the binary.
     143           80 :     pub fn debug(&self) -> DebugEntries {
     144           80 :         DebugEntries::new(self.ptr.debug())
     145           80 :     }
     146              : 
     147              :     /// Compute the authentihash for the current PE with the given algorithms.
     148           80 :     pub fn authentihash(&self, algo: Algorithms) -> Vec<u8> {
     149           80 :         Vec::from(self.ptr.authentihash(algo.into()).as_slice())
     150           80 :     }
     151              : 
     152              :     /// Return load configuration info if present.
     153           80 :     pub fn load_configuration(&self) -> Option<LoadConfiguration> {
     154           80 :         into_optional(self.ptr.load_configuration())
     155           80 :     }
     156              : 
     157              :     /// Return the raw data between the [`Binary::dos_header`] and the regular [`Binary::header`]
     158           80 :     pub fn dos_stub(&self) -> &[u8] {
     159           80 :         to_slice!(self.ptr.dos_stub());
     160           80 :     }
     161              : 
     162              :     /// Return the original overlay data of the file
     163           80 :     pub fn overlay(&self) -> &[u8] {
     164           80 :         to_slice!(self.ptr.overlay());
     165           80 :     }
     166              : 
     167              :     /// Return the offset computed by LIEF to identify overlay data
     168           80 :     pub fn overlay_offset(&self) -> u64 {
     169           80 :         self.ptr.overlay_offset()
     170           80 :     }
     171              : 
     172              :     /// Convert a **relative** virtual address into an offset
     173           10 :     pub fn rva_to_offset(&self, rva: u64) -> u64 {
     174           10 :         self.ptr.rva_to_offset(rva)
     175           10 :     }
     176              : 
     177              :     /// Convert an **absolute** virtual address into an offset.
     178           10 :     pub fn va_to_offset(&self, va: u64) -> u64 {
     179           10 :         self.ptr.va_to_offset(va)
     180           10 :     }
     181              : 
     182              :     /// Return the size of the current binary when loaded in memory.
     183           80 :     pub fn virtual_size(&self) -> u64 {
     184           80 :         self.ptr.virtual_size()
     185           80 :     }
     186              : 
     187              :     /// Compute the size of all the headers.
     188           80 :     pub fn sizeof_headers(&self) -> u64 {
     189           80 :         self.ptr.sizeof_headers()
     190           80 :     }
     191              : 
     192              :     /// Find a section by its offset
     193           20 :     pub fn section_from_offset(&self, offset: u64) -> Option<Section> {
     194           20 :         into_optional(self.ptr.section_from_offset(offset))
     195           20 :     }
     196              : 
     197              :     /// Find a section by its **relative** virtual address
     198           20 :     pub fn section_from_rva(&self, rva: u64) -> Option<Section> {
     199           20 :         into_optional(self.ptr.section_from_rva(rva))
     200           20 :     }
     201              : 
     202              :     /// Find a section by its name
     203           20 :     pub fn section_by_name(&self, name: &str) -> Option<Section> {
     204           20 :         into_optional(self.ptr.section_by_name(name))
     205           20 :     }
     206              : 
     207              :     /// Find the data directory with the given type
     208           10 :     pub fn data_directory_by_type(&self, dir_type: data_directory::Type) -> Option<DataDirectory> {
     209           10 :         into_optional(self.ptr.data_directory_by_type(dir_type.into()))
     210           10 :     }
     211              : 
     212              :     /// Verify the binary against the embedded signature(s) (if any)
     213              :     ///
     214              :     /// First, it checks that the embedded signatures are correct (c.f. [`signature::Signature::check`])
     215              :     /// and then, it checks that the authentihash matches [`crate::pe::signature::content_info::ContentInfo::digest`]
     216           10 :     pub fn verify_signature(
     217           10 :         &self,
     218           10 :         checks: signature::VerificationChecks,
     219           10 :     ) -> signature::VerificationFlags {
     220           10 :         signature::VerificationFlags::from(self.ptr.verify_signature(checks.into()))
     221           10 :     }
     222              : 
     223              :     /// Verify the binary with the [`signature::Signature`] object provided in the first parameter.
     224              :     /// It can be used to verify a detached signature:
     225              :     ///
     226              :     /// ```
     227              :     /// if let Some(sig) = Signature::from_file(path_str.unwrap()) {
     228              :     ///     pe.verify_signature(&sig, signature::VerificationChecks::DEFAULT);
     229              :     /// }
     230              :     /// ```
     231           10 :     pub fn verify_with_signature(
     232           10 :         &self,
     233           10 :         sig: &signature::Signature,
     234           10 :         checks: signature::VerificationChecks,
     235           10 :     ) -> signature::VerificationFlags {
     236           10 :         signature::VerificationFlags::from(
     237           10 :             self.ptr.verify_with_signature(sig.into(), checks.into()),
     238           10 :         )
     239           10 :     }
     240              : 
     241              :     /// Find an import by its DLL name
     242           20 :     pub fn import_by_name(&self, name: &str) -> Option<Import> {
     243           20 :         into_optional(self.ptr.import_by_name(name))
     244           20 :     }
     245              : 
     246              :     /// Find a delayed import by its name
     247           20 :     pub fn delay_import_by_name(&self, name: &str) -> Option<DelayImport> {
     248           20 :         into_optional(self.ptr.delay_import_by_name(name))
     249           20 :     }
     250              : 
     251              :     /// Return the sized content from the virtual address
     252           30 :     pub fn content_from_virtual_address(&self, address: u64, size: u64) -> &[u8] {
     253           30 :         to_slice!(self.ptr.get_content_from_virtual_address(address, size));
     254           30 :     }
     255              : 
     256              :     /// Get the integer value at the given virtual address
     257            0 :     pub fn get_int_from_virtual_address<T>(&self, addr: u64) -> Result<T, Error>
     258            0 :     where
     259            0 :         T: Num + cast::FromPrimitive + cast::ToPrimitive,
     260            0 :     {
     261            0 :         // Can't be in the generic trait because of:
     262            0 :         //   > for a trait to be "object safe" it needs to allow building a vtable to allow the call
     263            0 :         //   > to be resolvable dynamically; for more information visit
     264            0 :         //   > https://doc.rust-lang.org/reference/items/traits.html#object-safety
     265            0 :         if size_of::<T>() == size_of::<u8>() {
     266            0 :             to_conv_result!(
     267            0 :                 ffi::AbstractBinary::get_u8,
     268            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     269            0 :                 |value| {
     270            0 :                     T::from_u8(value).expect(format!("Can't cast value: {}", value).as_str())
     271            0 :                 },
     272            0 :                 addr
     273              :             );
     274            0 :         }
     275            0 : 
     276            0 :         if size_of::<T>() == size_of::<u16>() {
     277            0 :             to_conv_result!(
     278            0 :                 ffi::AbstractBinary::get_u16,
     279            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     280            0 :                 |value| {
     281            0 :                     T::from_u16(value).expect(format!("Can't cast value: {}", value).as_str())
     282            0 :                 },
     283            0 :                 addr
     284              :             );
     285            0 :         }
     286            0 : 
     287            0 :         if size_of::<T>() == size_of::<u32>() {
     288            0 :             to_conv_result!(
     289            0 :                 ffi::AbstractBinary::get_u32,
     290            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     291            0 :                 |value| {
     292            0 :                     T::from_u32(value).expect(format!("Can't cast value: {}", value).as_str())
     293            0 :                 },
     294            0 :                 addr
     295              :             );
     296            0 :         }
     297            0 : 
     298            0 :         if size_of::<T>() == size_of::<u64>() {
     299            0 :             to_conv_result!(
     300            0 :                 ffi::AbstractBinary::get_u64,
     301            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     302            0 :                 |value| {
     303            0 :                     T::from_u64(value).expect(format!("Can't cast value: {}", value).as_str())
     304            0 :                 },
     305            0 :                 addr
     306              :             );
     307            0 :         }
     308            0 : 
     309            0 :         Err(Error::NotSupported)
     310            0 :     }
     311              : 
     312              :     /// Write back the current PE binary into the file specified in parameter
     313            0 :     pub fn write(&mut self, output: &Path) {
     314            0 :         self.ptr.as_mut().unwrap().write(output.to_str().unwrap());
     315            0 :     }
     316              : }
     317              : 
     318              : impl std::fmt::Debug for Binary {
     319           80 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     320           80 :         f.debug_struct("Binary").finish()
     321           80 :     }
     322              : }
     323              : 
     324              : impl generic::Binary for Binary {
     325           80 :     fn as_generic(&self) -> &ffi::AbstractBinary {
     326           80 :         self.ptr.as_ref().unwrap().as_ref()
     327           80 :     }
     328              : 
     329            0 :     fn as_pin_mut_generic(&mut self) -> Pin<&mut ffi::AbstractBinary> {
     330            0 :         unsafe {
     331            0 :             Pin::new_unchecked({
     332            0 :                 (self.ptr.as_ref().unwrap().as_ref()
     333            0 :                     as *const ffi::AbstractBinary
     334            0 :                     as *mut ffi::AbstractBinary).as_mut().unwrap()
     335            0 :             })
     336            0 :         }
     337            0 :     }
     338              : }
     339              : 
     340          180 : declare_iterator!(
     341          180 :     DebugEntries,
     342          180 :     debug::Entries<'a>,
     343          180 :     ffi::PE_Debug,
     344          180 :     ffi::PE_Binary,
     345          180 :     ffi::PE_Binary_it_debug
     346          180 : );
        

Generated by: LCOV version 2.1-1