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