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::builder::Config;
9 : use super::parser_config::Config as ParserConfig;
10 : use super::data_directory::{DataDirectories, DataDirectory};
11 : use super::debug::{self, Entries, DebugEntry};
12 : use super::delay_import::{DelayImport, DelayImports};
13 : use super::export::Export;
14 : use super::import::{Import, Imports};
15 : use super::load_configuration::LoadConfiguration;
16 : use super::relocation::Relocations;
17 : use super::resources::{Manager as ResourcesManager, NodeBase};
18 : use super::resources::Node as ResourceNode;
19 : use super::rich_header::RichHeader;
20 : use super::section::{Section, Sections};
21 : use super::signature::Signatures;
22 : use super::tls::TLS;
23 : use super::{data_directory, signature};
24 : use super::debug::CodeViewPDB;
25 : use super::symbol::Symbol;
26 : use super::exception::RuntimeExceptionFunction;
27 : use super::coff;
28 :
29 : use crate::common::{into_optional, FromFFI, AsFFI};
30 : use crate::declare_iterator;
31 : use crate::generic;
32 : use crate::to_conv_result;
33 : use crate::to_slice;
34 : use crate::Error;
35 :
36 : use super::Algorithms;
37 : use super::{DosHeader, Header, OptionalHeader};
38 :
39 : /// This is the main interface to read and write PE binary attributes.
40 : ///
41 : /// Note that this structure implements the [`generic::Binary`] trait from which other generic
42 : /// functions are exposed
43 : ///
44 : /// ```
45 : /// fn use_trait(pe: &Binary) {
46 : /// let generic_binary = pe as &dyn generic::Binary;
47 : /// println!("{}", generic_binary.entrypoint());
48 : /// }
49 : ///
50 : /// ```
51 : pub struct Binary {
52 : ptr: cxx::UniquePtr<ffi::PE_Binary>,
53 : }
54 :
55 : impl FromFFI<ffi::PE_Binary> for Binary {
56 250 : fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_Binary>) -> Self {
57 250 : Self { ptr }
58 250 : }
59 : }
60 :
61 : impl Binary {
62 : /// Parse from a file path given as a string
63 0 : pub fn parse(path: &str) -> Option<Self> {
64 0 : let ffi = ffi::PE_Binary::parse(path);
65 0 : if ffi.is_null() {
66 0 : return None;
67 0 : }
68 0 : Some(Binary::from_ffi(ffi))
69 0 : }
70 :
71 : /// Parse from a string file path and with a provided configuration
72 120 : pub fn parse_with_config(path: &str, config: ParserConfig) -> Option<Self> {
73 120 : let ffi_config = config.to_ffi();
74 120 : let ffi = ffi::PE_Binary::parse_with_config(path, &ffi_config);
75 120 : if ffi.is_null() {
76 0 : return None;
77 120 : }
78 120 : Some(Binary::from_ffi(ffi))
79 120 : }
80 :
81 : /// DosHeader which starts the PE files
82 130 : pub fn dos_header(&self) -> DosHeader {
83 130 : DosHeader::from_ffi(self.ptr.dos_header())
84 130 : }
85 :
86 : /// Header that follows the [`Binary::header`]. It is named
87 : /// *optional* from the COFF specfication but it is mandatory in a PE file.
88 130 : pub fn optional_header(&self) -> OptionalHeader {
89 130 : OptionalHeader::from_ffi(self.ptr.optional_header())
90 130 : }
91 :
92 : /// Re-compute the value of [`OptionalHeader::checksum`]
93 130 : pub fn compute_checksum(&self) -> u32 {
94 130 : self.ptr.compute_checksum()
95 130 : }
96 :
97 : /// Next header after the [`Binary::dos_header`]
98 130 : pub fn header(&self) -> Header {
99 130 : Header::from_ffi(self.ptr.header())
100 130 : }
101 :
102 : /// Return TLS information if present
103 130 : pub fn tls(&self) -> Option<TLS> {
104 130 : into_optional(self.ptr.tls())
105 130 : }
106 :
107 : /// Return rich header information if present.
108 130 : pub fn rich_header(&self) -> Option<RichHeader> {
109 130 : into_optional(self.ptr.rich_header())
110 130 : }
111 :
112 : /// Return export information
113 130 : pub fn export(&self) -> Option<Export> {
114 130 : into_optional(self.ptr.get_export())
115 130 : }
116 :
117 : /// Return the root of the PE's resource tree
118 260 : pub fn resources(&self) -> Option<ResourceNode> {
119 260 : into_optional(self.ptr.resources())
120 260 : }
121 :
122 : /// Return a manager interface to read and manipulate the resources tree with a user friendly
123 : /// interface.
124 130 : pub fn resources_manager(&self) -> Option<ResourcesManager> {
125 130 : into_optional(self.ptr.resources_manager())
126 130 : }
127 :
128 : /// Return the imports as an **iterator** over the [`Import`] structure
129 130 : pub fn imports(&self) -> Imports {
130 130 : Imports::new(self.ptr.imports())
131 130 : }
132 :
133 : /// Return the data directories as an iterator over the [`DataDirectory`] structure
134 130 : pub fn data_directories(&self) -> DataDirectories {
135 130 : DataDirectories::new(self.ptr.data_directories())
136 130 : }
137 :
138 : /// Return the sections as an iterator over the [`Section`] structure
139 130 : pub fn sections(&self) -> Sections {
140 130 : Sections::new(self.ptr.sections())
141 130 : }
142 :
143 : /// Return the relocations as an iterator over the [`super::Relocation`] structure
144 130 : pub fn relocations(&self) -> Relocations {
145 130 : Relocations::new(self.ptr.relocations())
146 130 : }
147 :
148 : /// Return the delayed imports as an iterator over the [`DelayImport`] structure
149 130 : pub fn delay_imports(&self) -> DelayImports {
150 130 : DelayImports::new(self.ptr.delay_imports())
151 130 : }
152 :
153 : /// Return an iterator over the [`signature::Signature`] if the current PE is authenticode-signed.
154 140 : pub fn signatures(&self) -> Signatures {
155 140 : Signatures::new(self.ptr.signatures())
156 140 : }
157 :
158 : /// Return an iterator over the [`debug::Entries`] of the binary.
159 130 : pub fn debug(&self) -> DebugEntries {
160 130 : DebugEntries::new(self.ptr.debug())
161 130 : }
162 :
163 : /// Compute the authentihash for the current PE with the given algorithms.
164 130 : pub fn authentihash(&self, algo: Algorithms) -> Vec<u8> {
165 130 : Vec::from(self.ptr.authentihash(algo.into()).as_slice())
166 130 : }
167 :
168 : /// Return load configuration info if present.
169 130 : pub fn load_configuration(&self) -> Option<LoadConfiguration> {
170 130 : into_optional(self.ptr.load_configuration())
171 130 : }
172 :
173 : /// Return the raw data between the [`Binary::dos_header`] and the regular [`Binary::header`]
174 130 : pub fn dos_stub(&self) -> &[u8] {
175 130 : to_slice!(self.ptr.dos_stub());
176 130 : }
177 :
178 : /// Return the original overlay data of the file
179 130 : pub fn overlay(&self) -> &[u8] {
180 130 : to_slice!(self.ptr.overlay());
181 130 : }
182 :
183 : /// Return the offset computed by LIEF to identify overlay data
184 130 : pub fn overlay_offset(&self) -> u64 {
185 130 : self.ptr.overlay_offset()
186 130 : }
187 :
188 : /// Convert a **relative** virtual address into an offset
189 10 : pub fn rva_to_offset(&self, rva: u64) -> u64 {
190 10 : self.ptr.rva_to_offset(rva)
191 10 : }
192 :
193 : /// Convert an **absolute** virtual address into an offset.
194 10 : pub fn va_to_offset(&self, va: u64) -> u64 {
195 10 : self.ptr.va_to_offset(va)
196 10 : }
197 :
198 : /// Return the size of the current binary when loaded in memory.
199 130 : pub fn virtual_size(&self) -> u64 {
200 130 : self.ptr.virtual_size()
201 130 : }
202 :
203 : /// Compute the size of all the headers.
204 130 : pub fn sizeof_headers(&self) -> u64 {
205 130 : self.ptr.sizeof_headers()
206 130 : }
207 :
208 : /// Find a section by its offset
209 20 : pub fn section_from_offset(&self, offset: u64) -> Option<Section> {
210 20 : into_optional(self.ptr.section_from_offset(offset))
211 20 : }
212 :
213 : /// Find a section by its **relative** virtual address
214 20 : pub fn section_from_rva(&self, rva: u64) -> Option<Section> {
215 20 : into_optional(self.ptr.section_from_rva(rva))
216 20 : }
217 :
218 : /// Find a section by its name
219 20 : pub fn section_by_name(&self, name: &str) -> Option<Section> {
220 20 : into_optional(self.ptr.section_by_name(name))
221 20 : }
222 :
223 : /// Find the data directory with the given type
224 140 : pub fn data_directory_by_type(&self, dir_type: data_directory::Type) -> Option<DataDirectory> {
225 140 : into_optional(self.ptr.data_directory_by_type(dir_type.into()))
226 140 : }
227 :
228 : /// Verify the binary against the embedded signature(s) (if any)
229 : ///
230 : /// First, it checks that the embedded signatures are correct (c.f. [`signature::Signature::check`])
231 : /// and then, it checks that the authentihash matches [`crate::pe::signature::content_info::ContentInfo::digest`]
232 10 : pub fn verify_signature(
233 10 : &self,
234 10 : checks: signature::VerificationChecks,
235 10 : ) -> signature::VerificationFlags {
236 10 : signature::VerificationFlags::from(self.ptr.verify_signature(checks.into()))
237 10 : }
238 :
239 : /// Verify the binary with the [`signature::Signature`] object provided in the first parameter.
240 : /// It can be used to verify a detached signature:
241 : ///
242 : /// ```
243 : /// if let Some(sig) = Signature::from_file(path_str.unwrap()) {
244 : /// pe.verify_signature(&sig, signature::VerificationChecks::DEFAULT);
245 : /// }
246 : /// ```
247 10 : pub fn verify_with_signature(
248 10 : &self,
249 10 : sig: &signature::Signature,
250 10 : checks: signature::VerificationChecks,
251 10 : ) -> signature::VerificationFlags {
252 10 : signature::VerificationFlags::from(
253 10 : self.ptr.verify_with_signature(sig.into(), checks.into()),
254 10 : )
255 10 : }
256 :
257 : /// Find an import by its DLL name (case insensitive)
258 20 : pub fn import_by_name(&self, name: &str) -> Option<Import> {
259 20 : into_optional(self.ptr.import_by_name(name))
260 20 : }
261 :
262 : /// Find a delayed import by its name
263 20 : pub fn delay_import_by_name(&self, name: &str) -> Option<DelayImport> {
264 20 : into_optional(self.ptr.delay_import_by_name(name))
265 20 : }
266 :
267 : /// Return the sized content from the virtual address
268 30 : pub fn content_from_virtual_address(&self, address: u64, size: u64) -> &[u8] {
269 30 : to_slice!(self.ptr.get_content_from_virtual_address(address, size));
270 30 : }
271 :
272 130 : pub fn functions(&self) -> generic::Functions {
273 130 : generic::Functions::new(self.ptr.functions())
274 130 : }
275 :
276 : /// Return the data directory associated with the export table
277 0 : pub fn export_dir(&self) -> Option<DataDirectory> {
278 0 : into_optional(self.ptr.export_dir())
279 0 : }
280 :
281 : /// Return the data directory associated with the import table
282 0 : pub fn import_dir(&self) -> Option<DataDirectory> {
283 0 : into_optional(self.ptr.import_dir())
284 0 : }
285 :
286 : /// Return the data directory associated with the resources tree
287 0 : pub fn rsrc_dir(&self) -> Option<DataDirectory> {
288 0 : into_optional(self.ptr.rsrc_dir())
289 0 : }
290 :
291 : /// Return the data directory associated with the exceptions
292 0 : pub fn exceptions_dir(&self) -> Option<DataDirectory> {
293 0 : into_optional(self.ptr.exceptions_dir())
294 0 : }
295 :
296 : /// Return the data directory associated with the certificate table
297 : /// (authenticode)
298 0 : pub fn cert_dir(&self) -> Option<DataDirectory> {
299 0 : into_optional(self.ptr.cert_dir())
300 0 : }
301 :
302 : /// Return the data directory associated with the relocation table
303 0 : pub fn relocation_dir(&self) -> Option<DataDirectory> {
304 0 : into_optional(self.ptr.relocation_dir())
305 0 : }
306 :
307 : /// Return the data directory associated with the debug table
308 0 : pub fn debug_dir(&self) -> Option<DataDirectory> {
309 0 : into_optional(self.ptr.debug_dir())
310 0 : }
311 :
312 : /// Return the data directory associated with TLS
313 0 : pub fn tls_dir(&self) -> Option<DataDirectory> {
314 0 : into_optional(self.ptr.tls_dir())
315 0 : }
316 :
317 : /// Return the data directory associated with the load config
318 0 : pub fn load_config_dir(&self) -> Option<DataDirectory> {
319 0 : into_optional(self.ptr.load_config_dir())
320 0 : }
321 :
322 : /// Return the data directory associated with the IAT
323 0 : pub fn iat_dir(&self) -> Option<DataDirectory> {
324 0 : into_optional(self.ptr.iat_dir())
325 0 : }
326 :
327 : /// Return the data directory associated with delayed imports
328 0 : pub fn export_delay_dirdir(&self) -> Option<DataDirectory> {
329 0 : into_optional(self.ptr.delay_dir())
330 0 : }
331 :
332 : /// Get the integer value at the given virtual address
333 0 : pub fn get_int_from_virtual_address<T>(&self, addr: u64) -> Result<T, Error>
334 0 : where
335 0 : T: Num + cast::FromPrimitive + cast::ToPrimitive,
336 0 : {
337 0 : // Can't be in the generic trait because of:
338 0 : // > for a trait to be "object safe" it needs to allow building a vtable to allow the call
339 0 : // > to be resolvable dynamically; for more information visit
340 0 : // > https://doc.rust-lang.org/reference/items/traits.html#object-safety
341 0 : if size_of::<T>() == size_of::<u8>() {
342 0 : to_conv_result!(
343 0 : ffi::AbstractBinary::get_u8,
344 0 : self.ptr.as_ref().unwrap().as_ref(),
345 0 : |value| {
346 0 : T::from_u8(value).expect(format!("Can't cast value: {}", value).as_str())
347 0 : },
348 0 : addr
349 : );
350 0 : }
351 0 :
352 0 : if size_of::<T>() == size_of::<u16>() {
353 0 : to_conv_result!(
354 0 : ffi::AbstractBinary::get_u16,
355 0 : self.ptr.as_ref().unwrap().as_ref(),
356 0 : |value| {
357 0 : T::from_u16(value).expect(format!("Can't cast value: {}", value).as_str())
358 0 : },
359 0 : addr
360 : );
361 0 : }
362 0 :
363 0 : if size_of::<T>() == size_of::<u32>() {
364 0 : to_conv_result!(
365 0 : ffi::AbstractBinary::get_u32,
366 0 : self.ptr.as_ref().unwrap().as_ref(),
367 0 : |value| {
368 0 : T::from_u32(value).expect(format!("Can't cast value: {}", value).as_str())
369 0 : },
370 0 : addr
371 : );
372 0 : }
373 0 :
374 0 : if size_of::<T>() == size_of::<u64>() {
375 0 : to_conv_result!(
376 0 : ffi::AbstractBinary::get_u64,
377 0 : self.ptr.as_ref().unwrap().as_ref(),
378 0 : |value| {
379 0 : T::from_u64(value).expect(format!("Can't cast value: {}", value).as_str())
380 0 : },
381 0 : addr
382 : );
383 0 : }
384 0 :
385 0 : Err(Error::NotSupported)
386 0 : }
387 :
388 : /// Add an imported library (i.e. `DLL`) to the binary
389 0 : pub fn add_import<'a>(&'a mut self, name: &str) -> Import<'a> {
390 0 : Import::from_ffi(self.ptr.pin_mut().add_import(name))
391 0 : }
392 :
393 : /// Remove the imported library with the given `name`
394 0 : pub fn remove_import(&mut self, name: &str) {
395 0 : self.ptr.pin_mut().remove_import(name);
396 0 : }
397 :
398 : /// Remove all libraries in the binary
399 0 : pub fn remove_all_imports(&mut self) {
400 0 : self.ptr.pin_mut().remove_all_imports();
401 0 : }
402 :
403 : /// Remove the TLS from the binary
404 0 : pub fn remove_tls(&mut self) {
405 0 : self.ptr.pin_mut().remove_tls();
406 0 : }
407 :
408 : /// Set or change the TLS information
409 0 : pub fn set_tls(&mut self, tls: &TLS) {
410 0 : self.ptr.pin_mut().set_tls(tls.as_ffi());
411 0 : }
412 :
413 : /// Change or set the resources tree to given node
414 0 : pub fn set_resources(&mut self, node: &dyn NodeBase) {
415 0 : self.ptr.pin_mut().set_resources(node.get_base());
416 0 : }
417 :
418 : /// Add a new debug entry
419 0 : pub fn add_debug_info<'a>(&'a mut self, entry: &dyn DebugEntry) -> Option<Entries<'a>> {
420 0 : into_optional(self.ptr.pin_mut().add_debug_info(entry.get_base()))
421 0 : }
422 :
423 : /// Remove a specific debug entry
424 0 : pub fn remove_debug<'a>(&'a mut self, entry: &dyn DebugEntry) -> bool {
425 0 : self.ptr.pin_mut().remove_debug(entry.get_base())
426 0 : }
427 :
428 : /// Remove all debug info
429 0 : pub fn clear_debug<'a>(&'a mut self) -> bool {
430 0 : self.ptr.pin_mut().clear_debug()
431 0 : }
432 :
433 : /// Return the [`CodeViewPDB`] object if present
434 0 : pub fn codeview_pdb(&self) -> Option<CodeViewPDB> {
435 0 : into_optional(self.ptr.codeview_pdb())
436 0 : }
437 :
438 : /// Write back the current PE binary into the file specified in parameter
439 0 : pub fn write(&mut self, output: &Path) {
440 0 : self.ptr.as_mut().unwrap().write(output.to_str().unwrap());
441 0 : }
442 :
443 : /// Write back the current PE binary into the file specified in parameter with the
444 : /// configuration provided in the second parameter.
445 0 : pub fn write_with_config(&mut self, output: &Path, config: Config) {
446 0 : let ffi_config = config.to_ffi();
447 0 : self.ptr.as_mut().unwrap().write_with_config(output.to_str().unwrap(),
448 0 : &ffi_config.as_ref().unwrap());
449 0 : }
450 :
451 : /// Iterator over the strings located in the COFF string table
452 130 : pub fn coff_string_table(&self) -> COFFStrings {
453 130 : COFFStrings::new(self.ptr.coff_string_table())
454 130 : }
455 :
456 : /// Return an iterator over the binary (COFF) symbols (if any).
457 130 : pub fn symbols(&self) -> Symbols {
458 130 : Symbols::new(self.ptr.symbols())
459 130 : }
460 :
461 : /// Try to find the COFF string at the given offset in the COFF string table.
462 : ///
463 : /// <div class="warning">
464 : /// This offset must include the first 4 bytes holding the size of the table.
465 : /// Hence, the first string starts a the offset 4.
466 : /// </div>
467 0 : pub fn find_coff_string_at(&self, offset: u32) -> Option<coff::String> {
468 0 : into_optional(self.ptr.find_coff_string_at(offset))
469 0 : }
470 :
471 : /// Iterator over the exception (`_RUNTIME_FUNCTION`) functions
472 : ///
473 : /// This function requires that the option [`ParserConfig::parse_exceptions`] was turned on
474 : /// (default is `false`) when parsing the binary.
475 130 : pub fn exceptions(&self) -> Exceptions {
476 130 : Exceptions::new(self.ptr.exceptions())
477 130 : }
478 :
479 : /// Try to find the exception info at the given RVA
480 : ///
481 : /// This function requires that the option [`ParserConfig::parse_exceptions`] was turned on
482 : /// (default is `false`) when parsing the binary.
483 0 : pub fn find_exception_at(&self, rva: u32) -> Option<RuntimeExceptionFunction> {
484 0 : into_optional(self.ptr.find_exception_at(rva))
485 0 : }
486 :
487 : /// True if this binary is compiled in ARM64EC mode (emulation compatible)
488 0 : pub fn is_arm64ec(&self) -> bool {
489 0 : self.ptr.is_arm64ec()
490 0 : }
491 :
492 : /// True if this binary is compiled in ARM64X mode (contains both ARM64 and ARM64EC).
493 0 : pub fn is_arm64x(&self) -> bool {
494 0 : self.ptr.is_arm64x()
495 0 : }
496 :
497 : /// If the current binary contains dynamic relocations
498 : /// (e.g. LIEF::PE::DynamicFixupARM64X), this function returns the
499 : /// **relocated** view of the current PE.
500 : ///
501 : /// This can be used to get the alternative PE binary, targeting a different
502 : /// architectures.
503 : ///
504 : /// <div class="warning">
505 : /// This function is <b>moving</b> and taking the ownership of the nested
506 : /// PE binary. This means that subsequent calls to this function will return None.
507 : /// </div>
508 : ///
509 : /// This function requires that the option [`ParserConfig::parse_arm64x_binary`] was turned on
510 : /// (default is `false`) when parsing the binary.
511 130 : pub fn nested_pe_binary(&self) -> Option<Binary> {
512 130 : into_optional(self.ptr.nested_pe_binary())
513 130 : }
514 :
515 : /// Set or change the export table
516 0 : pub fn set_export(&mut self, export: &Export) {
517 0 : self.ptr.pin_mut().set_export(export.as_ffi());
518 0 : }
519 : }
520 :
521 : impl std::fmt::Debug for Binary {
522 130 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523 130 : f.debug_struct("Binary").finish()
524 130 : }
525 : }
526 :
527 : impl generic::Binary for Binary {
528 130 : fn as_generic(&self) -> &ffi::AbstractBinary {
529 130 : self.ptr.as_ref().unwrap().as_ref()
530 130 : }
531 :
532 0 : fn as_pin_mut_generic(&mut self) -> Pin<&mut ffi::AbstractBinary> {
533 0 : unsafe {
534 0 : Pin::new_unchecked({
535 0 : (self.ptr.as_ref().unwrap().as_ref()
536 0 : as *const ffi::AbstractBinary
537 0 : as *mut ffi::AbstractBinary).as_mut().unwrap()
538 0 : })
539 0 : }
540 0 : }
541 : }
542 :
543 310 : declare_iterator!(
544 310 : DebugEntries,
545 310 : debug::Entries<'a>,
546 310 : ffi::PE_Debug,
547 310 : ffi::PE_Binary,
548 310 : ffi::PE_Binary_it_debug
549 310 : );
550 :
551 :
552 246550 : declare_iterator!(
553 246550 : COFFStrings,
554 246550 : coff::String<'a>,
555 246550 : ffi::PE_COFFString,
556 246550 : ffi::PE_Binary,
557 246550 : ffi::PE_Binary_it_strings_table
558 246550 : );
559 :
560 305170 : declare_iterator!(
561 305170 : Symbols,
562 305170 : Symbol<'a>,
563 305170 : ffi::PE_Symbol,
564 305170 : ffi::PE_Binary,
565 305170 : ffi::PE_Binary_it_symbols
566 305170 : );
567 :
568 771090 : declare_iterator!(
569 771090 : Exceptions,
570 771090 : RuntimeExceptionFunction<'a>,
571 771090 : ffi::PE_ExceptionInfo,
572 771090 : ffi::PE_Binary,
573 771090 : ffi::PE_Binary_it_exceptions
574 771090 : );
|