#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CarvedFile {
pub kind: &'static str,
pub offset: u64,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct FileMagic {
pub kind: &'static str,
pub magic: &'static [u8],
}
pub const FILE_MAGICS: &[FileMagic] = &[
FileMagic {
kind: "ZIP",
magic: b"PK\x03\x04",
},
FileMagic {
kind: "PDF",
magic: b"%PDF-",
},
FileMagic {
kind: "PNG",
magic: b"\x89PNG\r\n\x1a\n",
},
FileMagic {
kind: "JPEG",
magic: b"\xFF\xD8\xFF",
},
FileMagic {
kind: "GIF",
magic: b"GIF87a",
},
FileMagic {
kind: "GIF",
magic: b"GIF89a",
},
FileMagic {
kind: "BZIP2",
magic: b"BZh",
},
FileMagic {
kind: "7Z",
magic: b"7z\xBC\xAF\x27\x1C",
},
FileMagic {
kind: "RAR",
magic: b"Rar!\x1A\x07",
},
FileMagic {
kind: "XZ",
magic: b"\xFD7zXZ\x00",
},
FileMagic {
kind: "ELF",
magic: b"\x7FELF",
},
FileMagic {
kind: "RIFF",
magic: b"RIFF",
},
FileMagic {
kind: "SQLite",
magic: b"SQLite format 3\x00",
},
FileMagic {
kind: "OLE",
magic: b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",
},
FileMagic {
kind: "CAB",
magic: b"MSCF",
},
];
#[must_use]
pub fn carve(data: &[u8], base_offset: u64) -> Vec<CarvedFile> {
let mut out = Vec::new();
for sig in FILE_MAGICS {
let m = sig.magic;
if m.is_empty() || m.len() > data.len() {
continue;
}
for (i, window) in data.windows(m.len()).enumerate() {
if window == m {
out.push(CarvedFile {
kind: sig.kind,
offset: base_offset + i as u64,
});
}
}
}
out.sort_by_key(|c| c.offset);
out
}
const ASCII_MIN: u8 = 0x20;
const ASCII_MAX: u8 = 0x7E;
#[must_use]
pub fn extract_ascii_strings(data: &[u8], min_len: usize) -> Vec<String> {
let mut out = Vec::new();
let mut run: Vec<u8> = Vec::new();
for &b in data {
if (ASCII_MIN..=ASCII_MAX).contains(&b) {
run.push(b);
} else {
flush(&mut run, min_len, &mut out);
}
}
flush(&mut run, min_len, &mut out);
out
}
fn flush(run: &mut Vec<u8>, min_len: usize, out: &mut Vec<String>) {
if run.len() >= min_len {
out.push(String::from_utf8_lossy(run).into_owned());
}
run.clear();
}