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 #[allow(unused)]
160 fn import_call(iter: impl Iterator<Item = (umem, ReprCString)>, callback: &mut ImportCallback) {
161 iter.take_while(|(offset, name)| {
162 callback.call(ImportInfo {
163 name: name.clone(),
164 offset: *offset,
165 })
166 })
167 .for_each(|_| {});
168 }
169
170 let ret = Err(Error::from(ErrorKind::NotImplemented));
171
172 #[cfg(feature = "pelite")]
173 let ret = ret.or_else(|_| {
174 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
175 use pelite::pe32::imports::Import as Import32;
176 use pelite::pe64::imports::Import as Import64;
177 use pelite::Wrap::*;
178
179 if let Some(imports) = pe
180 .iat()
181 .map(Some)
182 .or_else(|e| {
183 if let pelite::Error::Null = e {
184 Ok(None)
185 } else {
186 Err(e)
187 }
188 })
189 .map_err(|_| ErrorKind::InvalidExeFile)?
190 {
191 let iter = imports
192 .iter()
193 .filter_map(|w| match w {
194 T32((addr, Ok(Import32::ByName { name, .. }))) => {
195 Some((*addr as umem, name))
196 }
197 T64((addr, Ok(Import64::ByName { name, .. }))) => {
198 Some((*addr as umem, name))
199 }
200 _ => None,
201 })
202 .filter_map(|(a, n)| n.to_str().ok().map(|n| (a, n.into())));
203
204 import_call(iter, &mut callback);
205 }
206
207 Ok(())
208 } else {
209 Err(Error::from(ErrorKind::InvalidExeFile))
210 }
211 });
212
213 #[cfg(feature = "goblin")]
214 let ret = ret.or_else(|_| match custom_parse(module_image)? {
215 Object::Elf(elf) => {
216 let iter = elf
217 .dynsyms
218 .iter()
219 .filter(|s| s.is_import())
220 .filter_map(|s| {
221 elf.dynstrtab
222 .get_at(s.st_name)
223 .map(|n| (s.st_value as umem, ReprCString::from(n)))
224 });
225
226 import_call(iter, &mut callback);
227
228 Ok(())
229 }
230 Object::PE(pe) => {
231 let iter = pe
232 .imports
233 .iter()
234 .map(|e| (e.offset as umem, e.name.as_ref().into()));
235
236 import_call(iter, &mut callback);
237
238 Ok(())
239 }
240 Object::Mach(Mach::Binary(bin)) => {
241 let mbase = macho_base(&bin).unwrap_or_default();
242
243 let iter = bin
244 .imports()
245 .ok()
246 .into_iter()
247 .flatten()
248 .map(|v| ((v.address as umem) - mbase + base.to_umem(), v.name.into()));
249
250 import_call(iter, &mut callback);
251
252 Ok(())
253 }
254 _ => Err(ErrorKind::InvalidExeFile.into()),
255 });
256
257 ret
258}
259
260#[inline]
261pub fn module_export_list_callback(
262 mem: &mut impl MemoryView,
263 info: &ModuleInfo,
264 callback: ExportCallback,
265) -> Result<()> {
266 export_list_callback(mem, info.base, info.size, callback)
267}
268
269pub fn export_list_callback(
270 mem: &mut impl MemoryView,
271 base: Address,
272 size: umem,
273 mut callback: ExportCallback,
274) -> Result<()> {
275 let mut module_image = aligned_alloc(size as usize);
276 let module_image = module_image.as_bytes_mut();
277
278 mem.read_raw_into(base, module_image).data_part()?;
279
280 #[allow(unused)]
282 fn export_call(iter: impl Iterator<Item = (umem, ReprCString)>, callback: &mut ExportCallback) {
283 iter.take_while(|(offset, name)| {
284 callback.call(ExportInfo {
285 name: name.clone(),
286 offset: *offset,
287 })
288 })
289 .for_each(|_| {});
290 }
291
292 let ret = Err(Error::from(ErrorKind::NotImplemented));
293
294 #[cfg(feature = "pelite")]
295 let ret = ret.or_else(|_| {
296 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
297 use pelite::pe64::exports::Export;
298
299 if let Some(exports) = pe
300 .exports()
301 .map(Some)
302 .or_else(|e| {
303 if let pelite::Error::Null = e {
304 Ok(None)
305 } else {
306 Err(e)
307 }
308 })
309 .map_err(|e| log::debug!("pelite: {}", e))
310 .map_err(|_| ErrorKind::InvalidExeFile)?
311 {
312 let exports = exports
313 .by()
314 .map_err(|e| log::debug!("pelite: {}", e))
315 .map_err(|_| ErrorKind::InvalidExeFile)?;
316
317 let iter = exports
318 .iter_names()
319 .filter_map(|(n, e)| n.ok().zip(e.ok()))
320 .filter_map(|(n, e)| match e {
321 Export::Symbol(off) => Some((*off as umem, n)),
322 _ => None,
323 })
324 .filter_map(|(o, n)| n.to_str().ok().map(|n| (o, n.into())));
325
326 export_call(iter, &mut callback);
327 }
328
329 Ok(())
330 } else {
331 Err(Error::from(ErrorKind::InvalidExeFile))
332 }
333 });
334
335 #[cfg(feature = "goblin")]
336 let ret = ret.or_else(|_| match custom_parse(module_image)? {
337 Object::Elf(elf) => {
338 let iter = elf
339 .dynsyms
340 .iter()
341 .filter(|s| !s.is_import())
342 .filter_map(|s| {
343 elf.dynstrtab
344 .get_at(s.st_name)
345 .map(|n| (s.st_value as umem, ReprCString::from(n)))
346 });
347
348 export_call(iter, &mut callback);
349
350 Ok(())
351 }
352 Object::PE(pe) => {
353 let iter = pe.exports.iter().filter_map(|e| {
354 e.name
355 .map(|name| (e.offset.unwrap_or(0usize) as umem, name.into()))
356 });
357
358 export_call(iter, &mut callback);
359
360 Ok(())
361 }
362 Object::Mach(Mach::Binary(bin)) => {
363 let mbase = macho_base(&bin).unwrap_or_default();
364
365 let iter = bin.exports().ok().into_iter().flatten().filter_map(|v| {
366 let MachExportInfo::Regular { address, .. } = v.info else {
367 return None;
368 };
369
370 Some(((address as umem) - mbase + base.to_umem(), v.name.into()))
371 });
372
373 export_call(iter, &mut callback);
374
375 Ok(())
376 }
377 _ => Err(ErrorKind::InvalidExeFile.into()),
378 });
379
380 ret
381}
382
383#[inline]
384pub fn module_section_list_callback(
385 mem: &mut impl MemoryView,
386 info: &ModuleInfo,
387 callback: SectionCallback,
388) -> Result<()> {
389 section_list_callback(mem, info.base, info.size, callback)
390}
391
392pub fn section_list_callback(
393 mem: &mut impl MemoryView,
394 base: Address,
395 size: umem,
396 mut callback: SectionCallback,
397) -> Result<()> {
398 let mut module_image = aligned_alloc(size as usize);
399 let module_image = module_image.as_bytes_mut();
400
401 mem.read_raw_into(base, module_image).data_part()?;
402
403 #[allow(unused)]
405 fn section_call(
406 iter: impl Iterator<Item = (umem, umem, ReprCString)>,
407 callback: &mut SectionCallback,
408 base: Address,
409 ) {
410 iter.take_while(|(section_base, section_size, name)| {
411 callback.call(SectionInfo {
412 name: name.clone(),
413 base: base + *section_base,
414 size: *section_size,
415 })
416 })
417 .for_each(|_| {});
418 }
419
420 let ret = Err(Error::from(ErrorKind::NotImplemented));
421
422 #[cfg(feature = "pelite")]
423 let ret = ret.or_else(|_| {
424 if let Ok(pe) = pelite::PeView::from_bytes(module_image) {
425 let iter = pe.section_headers().iter().filter_map(|sh| {
426 sh.name().ok().map(|name| {
427 (
428 sh.virtual_range().start as umem,
429 sh.virtual_range().end as umem,
430 name.into(),
431 )
432 })
433 });
434
435 section_call(iter, &mut callback, base);
436
437 Ok(())
438 } else {
439 Err(Error::from(ErrorKind::InvalidExeFile))
440 }
441 });
442
443 #[cfg(feature = "goblin")]
444 let ret = ret.or_else(|_| match custom_parse(module_image)? {
445 Object::Elf(elf) => {
446 let iter = elf.section_headers.iter().filter_map(|s| {
447 elf.shdr_strtab
448 .get_at(s.sh_name)
449 .map(|n| (s.sh_addr as umem, s.sh_size as umem, ReprCString::from(n)))
450 });
451
452 section_call(iter, &mut callback, base);
453
454 Ok(())
455 }
456 Object::PE(pe) => {
457 let iter = pe.sections.iter().filter_map(|e| {
458 e.real_name.as_ref().map(|name| {
459 (
460 e.virtual_address as umem,
461 e.virtual_size as umem,
462 name.as_str().into(),
463 )
464 })
465 });
466
467 section_call(iter, &mut callback, base);
468
469 Ok(())
470 }
471 Object::Mach(Mach::Binary(bin)) => {
472 let mut base_off = None;
473
474 let iter = bin.segments.sections().flatten().filter_map(|v| {
475 let (s, _) = v.ok()?;
476 let name = &s.sectname;
477 let name = name.split(|&v| v == 0).next()?;
478 let name = std::str::from_utf8(name).ok()?;
479
480 let addr = s.addr as umem;
481
482 if base_off.is_none() {
483 base_off = Some(addr);
484 }
485
486 Some((addr - base_off.unwrap(), s.size as umem, name.into()))
487 });
488
489 section_call(iter, &mut callback, base);
490
491 Ok(())
492 }
493 _ => Err(ErrorKind::InvalidExeFile.into()),
494 });
495
496 ret
497}