Skip to content

Commit 854f60b

Browse files
committed
Add feature to support compressed archives
Add a new feature "archives" to support scanning (compresses) archives. Extracting archives is done via the create compress-tools, which uses the C library libarchive. Thus in order to build and run checksec installing libarchive is required. $ checksec -f /var/cache/apt/archives/xterm_376-1_amd64.deb ELF64: | Canary: true CFI: false SafeStack: false Fortify: Partial Fortified: 3 Fortifiable: 1 NX: true PIE: Full Relro: Full RPATH: None RUNPATH: None | File: /var/cache/apt/archives/xterm_376-1_amd64.deb➔data.tar.xz➔./usr/bin/resize ELF64: | Canary: true CFI: false SafeStack: false Fortify: Partial Fortified: 8 Fortifiable: 11 NX: true PIE: Full Relro: Full RPATH: None RUNPATH: None | File: /var/cache/apt/archives/xterm_376-1_amd64.deb➔data.tar.xz➔./usr/bin/xterm
1 parent cc0131f commit 854f60b

File tree

3 files changed

+95
-0
lines changed

3 files changed

+95
-0
lines changed

Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ panic = 'abort' # Abort on panic
2828
clap = {version = "4.0.14", features = ["cargo"]}
2929
colored = {version = "2.0.0", optional = true}
3030
colored_json = {version = "3.0.1", optional = true}
31+
compress-tools = {version = "0.14.0", optional = true}
3132
either = "1.8.1"
3233
glob = "0.3.0"
3334
goblin = "0.6.0"
@@ -65,6 +66,7 @@ name = "checksec"
6566
path = "src/main.rs"
6667

6768
[features]
69+
archives = ["compress-tools"]
6870
color = ["colored", "colored_json", "xattr"]
6971
default = ["elf", "macho", "pe", "color", "maps"]
7072
elf = ["shared"]

src/main.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use std::collections::HashMap;
3333
#[cfg(all(target_os = "linux", feature = "elf"))]
3434
use std::collections::HashSet;
3535
use std::ffi::OsStr;
36+
#[cfg(feature = "archives")]
37+
use std::io::Cursor;
3638
use std::io::ErrorKind;
3739
#[cfg(all(feature = "color", not(target_os = "windows")))]
3840
use std::os::unix::fs::PermissionsExt;
@@ -46,6 +48,8 @@ use colored::{ColoredString, Colorize};
4648

4749
#[cfg(feature = "color")]
4850
use colored_json::to_colored_json_auto;
51+
#[cfg(feature = "archives")]
52+
use compress_tools::{ArchiveContents, ArchiveIterator};
4953

5054
mod binary;
5155
mod proc;
@@ -269,6 +273,8 @@ enum ParseError {
269273
IO(std::io::Error),
270274
#[cfg(all(target_os = "linux", feature = "elf"))]
271275
LdSo(LdSoError),
276+
#[cfg(feature = "archives")]
277+
Decompress(compress_tools::Error),
272278
#[allow(dead_code)]
273279
Unimplemented(&'static str),
274280
}
@@ -282,6 +288,8 @@ impl fmt::Display for ParseError {
282288
Self::LdSo(e) => {
283289
write!(f, "Failed to initialize library lookup: {e}")
284290
}
291+
#[cfg(feature = "archives")]
292+
Self::Decompress(e) => e.fmt(f),
285293
Self::Unimplemented(str) => {
286294
write!(f, "Support for files of type {str} not implemented")
287295
}
@@ -308,6 +316,13 @@ impl From<LdSoError> for ParseError {
308316
}
309317
}
310318

319+
#[cfg(feature = "archives")]
320+
impl From<compress_tools::Error> for ParseError {
321+
fn from(err: compress_tools::Error) -> ParseError {
322+
ParseError::Decompress(err)
323+
}
324+
}
325+
311326
type Cache = Arc<Mutex<HashMap<PathBuf, Vec<Binary>>>>;
312327

313328
fn parse(
@@ -427,6 +442,48 @@ fn parse_bytes(bytes: &[u8], file: &Path) -> Result<Vec<Binary>, ParseError> {
427442
#[cfg(not(feature = "macho"))]
428443
Object::Mach(_) => Err(ParseError::Unimplemented("MachO")),
429444
Object::Archive(archive) => Ok(parse_archive(&archive, file, bytes)),
445+
#[cfg(feature = "archives")]
446+
Object::Unknown(magic) => {
447+
let mut results = Vec::new();
448+
let mut handled = false;
449+
450+
let mut name = String::default();
451+
let mut buffer = Vec::new();
452+
453+
for content in ArchiveIterator::from_read(Cursor::new(bytes))? {
454+
match content {
455+
ArchiveContents::StartOfEntry(s, _) => {
456+
name = s;
457+
buffer.clear();
458+
}
459+
ArchiveContents::DataChunk(v) => buffer.extend(v),
460+
ArchiveContents::EndOfEntry => {
461+
if buffer != bytes {
462+
handled = true;
463+
464+
if let Ok(mut res) = parse_bytes(
465+
&buffer,
466+
Path::new(&format!(
467+
"{}\u{2794}{}",
468+
file.display(),
469+
name
470+
)),
471+
) {
472+
results.append(&mut res);
473+
}
474+
}
475+
}
476+
ArchiveContents::Err(e) => Err(e)?,
477+
}
478+
}
479+
480+
if handled {
481+
Ok(results)
482+
} else {
483+
Err(ParseError::Goblin(Error::BadMagic(magic)))
484+
}
485+
}
486+
#[cfg(not(feature = "archives"))]
430487
Object::Unknown(magic) => {
431488
Err(ParseError::Goblin(Error::BadMagic(magic)))
432489
}

0 commit comments

Comments
 (0)