Skip to content

Commit b562f26

Browse files
authored
Support Unix archives (#27)
* pe: use slice of bytes instead of memmap2::Mmap Reduce coupling and different sources than mapped file regions. * Support Unix archives Scan simple (uncompressed) Unix archives: checksec -f unix.a ELF64: | Canary: false CFI: false SafeStack: true Fortify: None Fortified: 0 Fortifiable: 1 NX: true PIE: Full Relro: Full RPATH: None RUNPATH: None | File: unix.a➔answer
1 parent 29e12b2 commit b562f26

File tree

3 files changed

+50
-17
lines changed

3 files changed

+50
-17
lines changed

src/main.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![warn(clippy::pedantic)]
22
extern crate clap;
3+
extern crate core;
34
extern crate goblin;
45
extern crate ignore;
56
extern crate serde_json;
@@ -129,6 +130,7 @@ fn print_process_results(processes: &Processes, settings: &output::Settings) {
129130
enum ParseError {
130131
Goblin(goblin::error::Error),
131132
IO(std::io::Error),
133+
#[allow(dead_code)]
132134
Unimplemented(&'static str),
133135
}
134136

@@ -159,7 +161,13 @@ impl From<std::io::Error> for ParseError {
159161
fn parse(file: &Path) -> Result<Vec<Binary>, ParseError> {
160162
let fp = fs::File::open(file)?;
161163
let buffer = unsafe { Mmap::map(&fp)? };
162-
match Object::parse(&buffer)? {
164+
165+
parse_bytes(&buffer, file)
166+
}
167+
168+
#[allow(clippy::too_many_lines)]
169+
fn parse_bytes(bytes: &[u8], file: &Path) -> Result<Vec<Binary>, ParseError> {
170+
match Object::parse(bytes)? {
163171
#[cfg(feature = "elf")]
164172
Object::Elf(elf) => {
165173
let results = elf::CheckSecResults::parse(&elf);
@@ -173,7 +181,7 @@ fn parse(file: &Path) -> Result<Vec<Binary>, ParseError> {
173181
}
174182
#[cfg(feature = "pe")]
175183
Object::PE(pe) => {
176-
let results = pe::CheckSecResults::parse(&pe, &buffer);
184+
let results = pe::CheckSecResults::parse(&pe, bytes);
177185
let bin_type =
178186
if pe.is_64 { BinType::PE64 } else { BinType::PE32 };
179187
Ok(vec![Binary::new(
@@ -224,7 +232,33 @@ fn parse(file: &Path) -> Result<Vec<Binary>, ParseError> {
224232
Object::PE(_) => Err(ParseError::Unimplemented("PE")),
225233
#[cfg(not(feature = "macho"))]
226234
Object::Mach(_) => Err(ParseError::Unimplemented("MachO")),
227-
Object::Archive(_) => Err(ParseError::Unimplemented("Unix Archive")),
235+
Object::Archive(archive) => Ok(archive
236+
.members()
237+
.iter()
238+
.filter_map(|member_name| {
239+
match archive.extract(member_name, bytes) {
240+
Ok(ext_bytes) => parse_bytes(
241+
ext_bytes,
242+
Path::new(&format!(
243+
"{}\u{2794}{}",
244+
file.display(),
245+
member_name
246+
)),
247+
)
248+
.ok(),
249+
Err(err) => {
250+
eprintln!(
251+
"Failed to extract member {} of {}: {}",
252+
member_name,
253+
file.display(),
254+
err
255+
);
256+
None
257+
}
258+
}
259+
})
260+
.flatten()
261+
.collect()),
228262
Object::Unknown(magic) => {
229263
Err(ParseError::Goblin(Error::BadMagic(magic)))
230264
}

src/pe.rs

+13-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use goblin::pe::{
77
data_directories::DataDirectory, options::ParseOptions,
88
section_table::SectionTable,
99
};
10-
use memmap2::Mmap;
1110
use scroll::Pread;
1211
use serde::{Deserialize, Serialize};
1312
use std::fmt;
@@ -327,7 +326,7 @@ pub struct CheckSecResults {
327326
}
328327
impl CheckSecResults {
329328
#[must_use]
330-
pub fn parse(pe: &PE, buffer: &Mmap) -> Self {
329+
pub fn parse(pe: &PE, buffer: &[u8]) -> Self {
331330
Self {
332331
aslr: pe.has_aslr(),
333332
authenticode: pe.has_authenticode(buffer),
@@ -446,7 +445,7 @@ pub trait Properties {
446445
/// [`memmap2::Mmap`](https://docs.rs/memmap2/0.5.7/memmap2/struct.Mmap.html)
447446
/// of the original file to read & parse required information from the
448447
/// underlying binary file
449-
fn has_authenticode(&self, mem: &memmap2::Mmap) -> bool;
448+
fn has_authenticode(&self, bytes: &[u8]) -> bool;
450449
/// check for `IMAGE_DLLCHARACTERISTICS_GUARD_CF` *(0x4000)* in
451450
/// `DllCharacteristics` within the `IMAGE_OPTIONAL_HEADER32/64`
452451
fn has_cfg(&self) -> bool;
@@ -470,7 +469,7 @@ pub trait Properties {
470469
/// [`memmap2::Mmap`](https://docs.rs/memmap2/0.5.7/memmap2/struct.Mmap.html)
471470
/// of the original file to read & parse required information from the
472471
/// underlying binary file
473-
fn has_gs(&self, mem: &memmap2::Mmap) -> bool;
472+
fn has_gs(&self, bytes: &[u8]) -> bool;
474473
/// check for `IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA` *(`0x0020`)* in
475474
/// `DllCharacteristics` within the `IMAGE_OPTIONAL_HEADER32/64`
476475
fn has_high_entropy_va(&self) -> bool;
@@ -486,15 +485,15 @@ pub trait Properties {
486485
/// [`memmap2::Mmap`](https://docs.rs/memmap2/0.5.7/memmap2/struct.Mmap.html)
487486
/// of the original file to read & parse required information from the
488487
/// underlying binary file
489-
fn has_rfg(&self, mem: &memmap2::Mmap) -> bool;
488+
fn has_rfg(&self, bytes: &[u8]) -> bool;
490489
/// check `shandler_count` from `LOAD_CONFIG` in `IMAGE_DATA_DIRECTORY`
491490
/// linked from the the `IMAGE_OPTIONAL_HEADER32/64`
492491
///
493492
/// requires a
494493
/// [`memmap2::Mmap`](https://docs.rs/memmap2/0.5.7/memmap2/struct.Mmap.html)
495494
/// of the original file to read and parse required information from the
496495
/// underlying binary file
497-
fn has_safe_seh(&self, mem: &memmap2::Mmap) -> bool;
496+
fn has_safe_seh(&self, bytes: &[u8]) -> bool;
498497
/// check `IMAGE_DLLCHARACTERISTICS_NO_SEH` from the
499498
/// `IMAGE_OPTIONAL_HEADER32/64`
500499
fn has_seh(&self) -> bool;
@@ -508,7 +507,7 @@ impl Properties for PE<'_> {
508507
}
509508
ASLR::None
510509
}
511-
fn has_authenticode(&self, mem: &memmap2::Mmap) -> bool {
510+
fn has_authenticode(&self, bytes: &[u8]) -> bool {
512511
// requires running platform to be Windows for verification
513512
// just check for existence right now
514513
if let Some(optional_header) = self.header.optional_header {
@@ -518,7 +517,7 @@ impl Properties for PE<'_> {
518517
optional_header.data_directories.get_load_config_table()
519518
{
520519
if let Ok(load_config_val) = get_load_config_val(
521-
mem,
520+
bytes,
522521
*load_config_hdr,
523522
sections,
524523
file_alignment,
@@ -591,15 +590,15 @@ impl Properties for PE<'_> {
591590
}
592591
false
593592
}
594-
fn has_gs(&self, mem: &memmap2::Mmap) -> bool {
593+
fn has_gs(&self, bytes: &[u8]) -> bool {
595594
if let Some(optional_header) = self.header.optional_header {
596595
let file_alignment = optional_header.windows_fields.file_alignment;
597596
let sections = &self.sections;
598597
if let Some(load_config_hdr) =
599598
optional_header.data_directories.get_load_config_table()
600599
{
601600
if let Ok(load_config_val) = get_load_config_val(
602-
mem,
601+
bytes,
603602
*load_config_hdr,
604603
sections,
605604
file_alignment,
@@ -632,15 +631,15 @@ impl Properties for PE<'_> {
632631
}
633632
false
634633
}
635-
fn has_rfg(&self, mem: &memmap2::Mmap) -> bool {
634+
fn has_rfg(&self, bytes: &[u8]) -> bool {
636635
if let Some(optional_header) = self.header.optional_header {
637636
let file_alignment = optional_header.windows_fields.file_alignment;
638637
let sections = &self.sections;
639638
if let Some(load_config_hdr) =
640639
optional_header.data_directories.get_load_config_table()
641640
{
642641
if let Ok(load_config_val) = get_load_config_val(
643-
mem,
642+
bytes,
644643
*load_config_hdr,
645644
sections,
646645
file_alignment,
@@ -657,15 +656,15 @@ impl Properties for PE<'_> {
657656
}
658657
false
659658
}
660-
fn has_safe_seh(&self, mem: &memmap2::Mmap) -> bool {
659+
fn has_safe_seh(&self, bytes: &[u8]) -> bool {
661660
if let Some(optional_header) = self.header.optional_header {
662661
let file_alignment = optional_header.windows_fields.file_alignment;
663662
let sections = &self.sections;
664663
if let Some(load_config_hdr) =
665664
optional_header.data_directories.get_load_config_table()
666665
{
667666
if let Ok(load_config_val) = get_load_config_val(
668-
mem,
667+
bytes,
669668
*load_config_hdr,
670669
sections,
671670
file_alignment,

test/binaries/unix.a

19.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)