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