1use std::path::Path;
7
8use object::pe::{ImageExportDirectory, IMAGE_FILE_DLL};
9use object::read::pe::{ExportTarget, ImageNtHeaders, ImageOptionalHeader};
10use object::LittleEndian as LE;
11
12use crate::error::{Error, Result};
13use crate::types::{Architecture, ExportDirectory, ExportInfo, PeFile};
14
15pub fn parse_pe_file(path: impl AsRef<Path>) -> Result<PeFile> {
26 let path = path.as_ref();
27 let data = std::fs::read(path).map_err(|e| Error::FileOpen {
28 path: path.to_path_buf(),
29 source: e,
30 })?;
31 let mut pe = parse_pe_bytes(&data)?;
32 pe.path = path.to_path_buf();
33 Ok(pe)
34}
35
36pub fn parse_pe_bytes(data: &[u8]) -> Result<PeFile> {
38 if let Ok(pe) = object::read::pe::PeFile64::parse(data) {
41 return build_pe_file(&pe, data);
42 }
43 if let Ok(pe) = object::read::pe::PeFile32::parse(data) {
44 return build_pe_file(&pe, data);
45 }
46 Err(Error::InvalidFileFormat)
47}
48
49fn build_pe_file<'data, Pe>(
51 pe: &object::read::pe::PeFile<'data, Pe, &'data [u8]>,
52 _data: &'data [u8],
53) -> Result<PeFile>
54where
55 Pe: ImageNtHeaders,
56{
57 let nt = pe.nt_headers();
58 let fh = nt.file_header();
59
60 let machine = fh.machine.get(LE);
61 let architecture = Architecture::from_machine_type(machine);
62
63 let characteristics = fh.characteristics.get(LE);
64 let is_dll = (characteristics & IMAGE_FILE_DLL) != 0;
65
66 let opt = nt.optional_header();
67 let image_base = opt.image_base();
68 let entry_point_rva = opt.address_of_entry_point();
69
70 let (export_directory, exports) = match pe.export_table() {
72 Ok(Some(et)) => parse_exports(&et)?,
73 Ok(None) => (None, Vec::new()),
74 Err(e) => return Err(Error::PeParse(format!("failed to read export table: {e}"))),
75 };
76
77 Ok(PeFile {
78 path: std::path::PathBuf::new(),
79 architecture,
80 is_dll,
81 image_base,
82 entry_point_rva,
83 export_directory,
84 exports,
85 })
86}
87
88fn parse_exports(
90 et: &object::read::pe::ExportTable<'_>,
91) -> Result<(Option<ExportDirectory>, Vec<ExportInfo>)> {
92 let dir: &ImageExportDirectory = et.directory();
93
94 let export_directory = ExportDirectory {
95 characteristics: dir.characteristics.get(LE),
96 timestamp: dir.time_date_stamp.get(LE),
97 major_version: dir.major_version.get(LE),
98 minor_version: dir.minor_version.get(LE),
99 name_rva: dir.name.get(LE),
100 ordinal_base: dir.base.get(LE),
101 address_table_entries: dir.number_of_functions.get(LE),
102 number_of_name_pointers: dir.number_of_names.get(LE),
103 address_table_rva: dir.address_of_functions.get(LE),
104 name_pointer_rva: dir.address_of_names.get(LE),
105 ordinal_table_rva: dir.address_of_name_ordinals.get(LE),
106 };
107
108 let raw_exports = et
110 .exports()
111 .map_err(|e| Error::PeParse(format!("failed to iterate exports: {e}")))?;
112
113 let mut exports = Vec::with_capacity(raw_exports.len());
114 for exp in &raw_exports {
115 let name = exp
116 .name
117 .map(|n| {
118 String::from_utf8(n.to_vec())
119 .map_err(|e| Error::PeParse(format!("invalid UTF-8 in export name: {e}")))
120 })
121 .transpose()?;
122
123 let ordinal = exp.ordinal;
124
125 let (is_forwarded, forward_to, relative_address) = match exp.target {
126 ExportTarget::Address(rva) => (false, None, Some(rva)),
127 ExportTarget::ForwardByName(dll, fname) => {
128 let dll_s = String::from_utf8_lossy(dll);
129 let fname_s = String::from_utf8_lossy(fname);
130 (true, Some(format!("{dll_s}.{fname_s}")), None)
131 }
132 ExportTarget::ForwardByOrdinal(dll, ord) => {
133 let dll_s = String::from_utf8_lossy(dll);
134 (true, Some(format!("{dll_s}.#{ord}")), None)
135 }
136 };
137
138 exports.push(ExportInfo {
139 name,
140 ordinal,
141 is_forwarded,
142 forward_to,
143 relative_address,
144 });
145 }
146
147 Ok((Some(export_directory), exports))
148}
149
150impl PeFile {
151 pub fn parse(path: impl AsRef<Path>) -> Result<Self> {
160 parse_pe_file(path.as_ref())
161 }
162
163 pub fn from_bytes(data: &[u8]) -> Result<Self> {
165 parse_pe_bytes(data)
166 }
167
168 pub fn export_directory(&self) -> Option<&ExportDirectory> {
170 self.export_directory.as_ref()
171 }
172
173 pub fn exports(&self) -> &[ExportInfo] {
175 &self.exports
176 }
177
178 pub fn architecture(&self) -> Architecture {
180 self.architecture
181 }
182
183 pub fn is_dll(&self) -> bool {
185 self.is_dll
186 }
187
188 pub fn find_export(&self, name: &str) -> Option<&ExportInfo> {
190 self.exports.iter().find(|e| e.name.as_deref() == Some(name))
191 }
192
193 pub fn find_export_by_ordinal(&self, ordinal: u32) -> Option<&ExportInfo> {
195 self.exports.iter().find(|e| e.ordinal == ordinal)
196 }
197
198 pub fn export_names(&self) -> Vec<String> {
200 let mut names: Vec<_> = self
201 .exports
202 .iter()
203 .filter_map(|e| e.name.clone())
204 .collect();
205 names.sort();
206 names
207 }
208}