1use crate::error::*;
4use crate::mem::MemoryView;
5use crate::os::*;
6use crate::types::umem;
7use cglue::prelude::v1::ReprCString;
8use dataview::PodMethods;
9use std::vec::Vec;
10
11#[cfg(feature = "goblin")]
12use goblin::{
13 container::Ctx,
14 elf::{dynamic, Dynamic, Elf, ProgramHeader, RelocSection, Symtab},
15 mach::{exports::ExportInfo as MachExportInfo, Mach, MachO},
16 pe::{options::ParseOptions, PE},
17 strtab::Strtab,
18 Object,
19};
20
21fn aligned_alloc(bytes: usize) -> Vec<u64> {
22 vec![0; (bytes + 8 - 1) / 8]
23}
24
25#[cfg(feature = "goblin")]
26fn parse_elf(bytes: &[u8]) -> goblin::error::Result<Elf<'_>> {
27 let header = Elf::parse_header(bytes)?;
28
29 let ctx = Ctx {
30 container: header.container()?,
31 le: header.endianness()?,
32 };
33
34 let program_headers =
35 ProgramHeader::parse(bytes, header.e_phoff as usize, header.e_phnum as usize, ctx)?;
36
37 let dynamic = Dynamic::parse(bytes, &program_headers, ctx)?;
38
39 let mut dynsyms = Symtab::default();
40 let mut dynstrtab = Strtab::default();
41 let mut dynrelas = RelocSection::default();
42 let mut dynrels = RelocSection::default();
43 let mut pltrelocs = RelocSection::default();
44
45 if let Some(ref dynamic) = dynamic {
46 let dyn_info = &dynamic.info;
47
48 dynstrtab = Strtab::parse(bytes, dyn_info.strtab, dyn_info.strsz, 0x0)?;
49
50 if let Ok(relas) = RelocSection::parse(bytes, dyn_info.rela, dyn_info.relasz, true, ctx) {
59 dynrelas = relas;
60 dynrels = RelocSection::parse(bytes, dyn_info.rel, dyn_info.relsz, false, ctx)?;
61 let is_rela = dyn_info.pltrel as u64 == dynamic::DT_RELA;
62 pltrelocs =
63 RelocSection::parse(bytes, dyn_info.jmprel, dyn_info.pltrelsz, is_rela, ctx)?;
64
65 let mut num_syms = {
71 0
72 };
73 let max_reloc_sym = dynrelas
74 .iter()
75 .chain(dynrels.iter())
76 .chain(pltrelocs.iter())
77 .fold(0, |num, reloc| core::cmp::max(num, reloc.r_sym));
78 if max_reloc_sym != 0 {
79 num_syms = core::cmp::max(num_syms, max_reloc_sym + 1);
80 }
81
82 dynsyms = Symtab::parse(bytes, dyn_info.symtab, num_syms, ctx)?;
83 }
84 }
85
86 let mut elf = Elf::lazy_parse(header)?;
87
88 elf.program_headers = program_headers;
89 elf.dynamic = dynamic;
90 elf.dynsyms = dynsyms;
91 elf.dynstrtab = dynstrtab;
92 elf.dynrelas = dynrelas;
93 elf.dynrels = dynrels;
94 elf.pltrelocs = pltrelocs;
95
96 Ok(elf)
97}
98
99#[cfg(feature = "goblin")]
100fn custom_parse(buf: &[u8]) -> Result<Object<'_>> {
101 PE::parse_with_opts(
102 buf,
103 &ParseOptions {
104 resolve_rva: false,
105 parse_attribute_certificates: false,
106 },
107 )
108 .map(Object::PE)
109 .map_err(|e| {
110 log::debug!("PE: {}", e);
111 e
112 })
113 .or_else(|_| parse_elf(buf).map(Object::Elf))
114 .map_err(|e| {
115 log::debug!("Elf: {}", e);
116 e
117 })
118 .or_else(|_| {
119 #[cfg(feature = "unstable_goblin_lossy_macho")]
121 return Mach::parse_lossy(buf).map(Object::Mach);
122 #[cfg(not(feature = "unstable_goblin_lossy_macho"))]
123 return Mach::parse(buf).map(Object::Mach);
124 })
125 .map_err(|e| {
126 log::debug!("Mach: {}", e);
127 e
128 })
129 .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile))
130}
131
132#[cfg(feature = "goblin")]
133fn macho_base(bin: &MachO) -> Option<umem> {
134 let s = bin.segments.sections().flatten().next()?.ok()?.0;
135 Some(s.addr as umem)
136}
137
138#[inline]
139pub fn module_import_list_callback(
140 mem: &mut impl MemoryView,
141 info: &ModuleInfo,
142 callback: ImportCallback,
143) -> Result<()> {
144 import_list_callback(mem, info.base, info.size, callback)
145}
146
147pub fn import_list_callback(
148 mem: &mut impl MemoryView,
149 base: Address,
150 size: umem,
151 mut callback: ImportCallback,
152) -> Result<()> {
153 let mut module_image = aligned_alloc(size as usize);
154 let module_image = module_image.as_bytes_mut();
155
156 mem.read_raw_into(base, module_image).data_part()?;
157
158 fn import_call(iter: impl Iterator<Item = (umem, ReprCString)>, callback: &mut ImportCallback) {
159 iter.take_while(|(offset, name)| {
160 callback.call(ImportInfo {
161 name: name.clone(),
162 offset: *offset,
163 })
164 })
165 .for_each(|_| {});
166 }
167
168 let ret = Err(Error::from(ErrorKind::NotImplemented));
169
170 #[cfg(feature = "pelite")]
171 let ret = ret.or_else(|_| {
172 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
173 use pelite::pe32::imports::Import as Import32;
174 use pelite::pe64::imports::Import as Import64;
175 use pelite::Wrap::*;
176
177 if let Some(imports) = pe
178 .iat()
179 .map(Some)
180 .or_else(|e| {
181 if let pelite::Error::Null = e {
182 Ok(None)
183 } else {
184 Err(e)
185 }
186 })
187 .map_err(|_| ErrorKind::InvalidExeFile)?
188 {
189 let iter = imports
190 .iter()
191 .filter_map(|w| match w {
192 T32((addr, Ok(Import32::ByName { name, .. }))) => {
193 Some((*addr as umem, name))
194 }
195 T64((addr, Ok(Import64::ByName { name, .. }))) => {
196 Some((*addr as umem, name))
197 }
198 _ => None,
199 })
200 .filter_map(|(a, n)| n.to_str().ok().map(|n| (a, n.into())));
201
202 import_call(iter, &mut callback);
203 }
204
205 Ok(())
206 } else {
207 Err(Error::from(ErrorKind::InvalidExeFile))
208 }
209 });
210
211 #[cfg(feature = "goblin")]
212 let ret = ret.or_else(|_| match custom_parse(module_image)? {
213 Object::Elf(elf) => {
214 let iter = elf
215 .dynsyms
216 .iter()
217 .filter(|s| s.is_import())
218 .filter_map(|s| {
219 elf.dynstrtab
220 .get_at(s.st_name)
221 .map(|n| (s.st_value as umem, ReprCString::from(n)))
222 });
223
224 import_call(iter, &mut callback);
225
226 Ok(())
227 }
228 Object::PE(pe) => {
229 let iter = pe
230 .imports
231 .iter()
232 .map(|e| (e.offset as umem, e.name.as_ref().into()));
233
234 import_call(iter, &mut callback);
235
236 Ok(())
237 }
238 Object::Mach(Mach::Binary(bin)) => {
239 let mbase = macho_base(&bin).unwrap_or_default();
240
241 let iter = bin
242 .imports()
243 .ok()
244 .into_iter()
245 .flatten()
246 .map(|v| ((v.address as umem) - mbase + base.to_umem(), v.name.into()));
247
248 import_call(iter, &mut callback);
249
250 Ok(())
251 }
252 _ => Err(ErrorKind::InvalidExeFile.into()),
253 });
254
255 ret
256}
257
258#[inline]
259pub fn module_export_list_callback(
260 mem: &mut impl MemoryView,
261 info: &ModuleInfo,
262 callback: ExportCallback,
263) -> Result<()> {
264 export_list_callback(mem, info.base, info.size, callback)
265}
266
267pub fn export_list_callback(
268 mem: &mut impl MemoryView,
269 base: Address,
270 size: umem,
271 mut callback: ExportCallback,
272) -> Result<()> {
273 let mut module_image = aligned_alloc(size as usize);
274 let module_image = module_image.as_bytes_mut();
275
276 mem.read_raw_into(base, module_image).data_part()?;
277
278 fn export_call(iter: impl Iterator<Item = (umem, ReprCString)>, callback: &mut ExportCallback) {
279 iter.take_while(|(offset, name)| {
280 callback.call(ExportInfo {
281 name: name.clone(),
282 offset: *offset,
283 })
284 })
285 .for_each(|_| {});
286 }
287
288 let ret = Err(Error::from(ErrorKind::NotImplemented));
289
290 #[cfg(feature = "pelite")]
291 let ret = ret.or_else(|_| {
292 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
293 use pelite::pe64::exports::Export;
294
295 if let Some(exports) = pe
296 .exports()
297 .map(Some)
298 .or_else(|e| {
299 if let pelite::Error::Null = e {
300 Ok(None)
301 } else {
302 Err(e)
303 }
304 })
305 .map_err(|e| log::debug!("pelite: {}", e))
306 .map_err(|_| ErrorKind::InvalidExeFile)?
307 {
308 let exports = exports
309 .by()
310 .map_err(|e| log::debug!("pelite: {}", e))
311 .map_err(|_| ErrorKind::InvalidExeFile)?;
312
313 let iter = exports
314 .iter_names()
315 .filter_map(|(n, e)| n.ok().zip(e.ok()))
316 .filter_map(|(n, e)| match e {
317 Export::Symbol(off) => Some((*off as umem, n)),
318 _ => None,
319 })
320 .filter_map(|(o, n)| n.to_str().ok().map(|n| (o, n.into())));
321
322 export_call(iter, &mut callback);
323 }
324
325 Ok(())
326 } else {
327 Err(Error::from(ErrorKind::InvalidExeFile))
328 }
329 });
330
331 #[cfg(feature = "goblin")]
332 let ret = ret.or_else(|_| match custom_parse(module_image)? {
333 Object::Elf(elf) => {
334 let iter = elf
335 .dynsyms
336 .iter()
337 .filter(|s| !s.is_import())
338 .filter_map(|s| {
339 elf.dynstrtab
340 .get_at(s.st_name)
341 .map(|n| (s.st_value as umem, ReprCString::from(n)))
342 });
343
344 export_call(iter, &mut callback);
345
346 Ok(())
347 }
348 Object::PE(pe) => {
349 let iter = pe.exports.iter().filter_map(|e| {
350 e.name
351 .map(|name| (e.offset.unwrap_or(0usize) as umem, name.into()))
352 });
353
354 export_call(iter, &mut callback);
355
356 Ok(())
357 }
358 Object::Mach(Mach::Binary(bin)) => {
359 let mbase = macho_base(&bin).unwrap_or_default();
360
361 let iter = bin.exports().ok().into_iter().flatten().filter_map(|v| {
362 let MachExportInfo::Regular { address, .. } = v.info else {
363 return None;
364 };
365
366 Some(((address as umem) - mbase + base.to_umem(), v.name.into()))
367 });
368
369 export_call(iter, &mut callback);
370
371 Ok(())
372 }
373 _ => Err(ErrorKind::InvalidExeFile.into()),
374 });
375
376 ret
377}
378
379#[inline]
380pub fn module_section_list_callback(
381 mem: &mut impl MemoryView,
382 info: &ModuleInfo,
383 callback: SectionCallback,
384) -> Result<()> {
385 section_list_callback(mem, info.base, info.size, callback)
386}
387
388pub fn section_list_callback(
389 mem: &mut impl MemoryView,
390 base: Address,
391 size: umem,
392 mut callback: SectionCallback,
393) -> Result<()> {
394 let mut module_image = aligned_alloc(size as usize);
395 let module_image = module_image.as_bytes_mut();
396
397 mem.read_raw_into(base, module_image).data_part()?;
398
399 fn section_call(
400 iter: impl Iterator<Item = (umem, umem, ReprCString)>,
401 callback: &mut SectionCallback,
402 base: Address,
403 ) {
404 iter.take_while(|(section_base, section_size, name)| {
405 callback.call(SectionInfo {
406 name: name.clone(),
407 base: base + *section_base,
408 size: *section_size,
409 })
410 })
411 .for_each(|_| {});
412 }
413
414 let ret = Err(Error::from(ErrorKind::NotImplemented));
415
416 #[cfg(feature = "pelite")]
417 let ret = ret.or_else(|_| {
418 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
419 let iter = pe.section_headers().iter().filter_map(|sh| {
420 sh.name().ok().map(|name| {
421 (
422 sh.virtual_range().start as umem,
423 sh.virtual_range().end as umem,
424 name.into(),
425 )
426 })
427 });
428
429 section_call(iter, &mut callback, base);
430
431 Ok(())
432 } else {
433 Err(Error::from(ErrorKind::InvalidExeFile))
434 }
435 });
436
437 #[cfg(feature = "goblin")]
438 let ret = ret.or_else(|_| match custom_parse(module_image)? {
439 Object::Elf(elf) => {
440 let iter = elf.section_headers.iter().filter_map(|s| {
441 elf.shdr_strtab
442 .get_at(s.sh_name)
443 .map(|n| (s.sh_addr as umem, s.sh_size as umem, ReprCString::from(n)))
444 });
445
446 section_call(iter, &mut callback, base);
447
448 Ok(())
449 }
450 Object::PE(pe) => {
451 let iter = pe.sections.iter().filter_map(|e| {
452 e.real_name.as_ref().map(|name| {
453 (
454 e.virtual_address as umem,
455 e.virtual_size as umem,
456 name.as_str().into(),
457 )
458 })
459 });
460
461 section_call(iter, &mut callback, base);
462
463 Ok(())
464 }
465 Object::Mach(Mach::Binary(bin)) => {
466 let mut base_off = None;
467
468 let iter = bin.segments.sections().flatten().filter_map(|v| {
469 let (s, _) = v.ok()?;
470 let name = &s.sectname;
471 let name = name.split(|&v| v == 0).next()?;
472 let name = std::str::from_utf8(name).ok()?;
473
474 let addr = s.addr as umem;
475
476 if base_off.is_none() {
477 base_off = Some(addr);
478 }
479
480 Some((addr - base_off.unwrap(), s.size as umem, name.into()))
481 });
482
483 section_call(iter, &mut callback, base);
484
485 Ok(())
486 }
487 _ => Err(ErrorKind::InvalidExeFile.into()),
488 });
489
490 ret
491}