pub mod blockheader;
pub mod commonheader;
pub mod firstheader;
pub use blockheader::{BLOCKS_NUM, BlockHeader, BlockType};
pub use commonheader::CommonHeader;
pub use firstheader::FirstHeader;
use crate::error::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NsisVersionHint {
Nsis1x,
Nsis2x,
Nsis3x,
Park,
Unknown,
}
pub fn scan_for_first_header(overlay: &[u8]) -> Result<(usize, FirstHeader<'_>), Error> {
let mut offset: usize = 0;
while let Some(slice) = overlay.get(offset..) {
if slice.len() < FirstHeader::SIZE {
break;
}
if let Ok(fh) = FirstHeader::parse(slice)
&& has_plausible_lengths(&fh)
{
return Ok((offset, fh));
}
let Some(next) = offset.checked_add(512) else {
break;
};
offset = next;
}
for (offset, slice) in overlay.windows(FirstHeader::SIZE).enumerate() {
if offset % 512 == 0 {
continue;
}
if let Ok(fh) = FirstHeader::parse(slice)
&& has_plausible_lengths(&fh)
{
return Ok((offset, fh));
}
}
Err(Error::SignatureNotFound)
}
fn has_plausible_lengths(header: &FirstHeader<'_>) -> bool {
header.length_of_header() > 0
&& header.length_of_all_following_data() >= FirstHeader::SIZE as i32
}
#[cfg(test)]
mod tests {
use super::*;
fn make_first_header(flags: u32) -> [u8; 28] {
let mut buf = [0u8; 28];
buf[0..4].copy_from_slice(&flags.to_le_bytes());
buf[4..8].copy_from_slice(&0xDEADBEEFu32.to_le_bytes());
buf[8..12].copy_from_slice(&0x6C6C754Eu32.to_le_bytes()); buf[12..16].copy_from_slice(&0x74666F73u32.to_le_bytes()); buf[16..20].copy_from_slice(&0x74736E49u32.to_le_bytes()); buf[20..24].copy_from_slice(&1024i32.to_le_bytes()); buf[24..28].copy_from_slice(&2048i32.to_le_bytes()); buf
}
#[test]
fn scan_finds_header_at_offset_zero() {
let fh = make_first_header(0);
let mut overlay = vec![0u8; 2048];
overlay[..28].copy_from_slice(&fh);
let (off, _hdr) = scan_for_first_header(&overlay).unwrap();
assert_eq!(off, 0);
}
#[test]
fn scan_finds_header_at_512() {
let fh = make_first_header(0);
let mut overlay = vec![0u8; 2560];
overlay[512..512 + 28].copy_from_slice(&fh);
let (off, _hdr) = scan_for_first_header(&overlay).unwrap();
assert_eq!(off, 512);
}
#[test]
fn scan_finds_header_at_1024() {
let fh = make_first_header(0x01); let mut overlay = vec![0u8; 3072];
overlay[1024..1024 + 28].copy_from_slice(&fh);
let (off, hdr) = scan_for_first_header(&overlay).unwrap();
assert_eq!(off, 1024);
assert!(hdr.is_uninstaller());
}
#[test]
fn scan_fails_on_empty_overlay() {
let overlay = vec![0u8; 512];
assert_eq!(
scan_for_first_header(&overlay),
Err(Error::SignatureNotFound)
);
}
#[test]
fn scan_finds_header_at_non_aligned_offset() {
let fh = make_first_header(0);
let mut overlay = vec![0u8; 2304];
overlay[256..256 + 28].copy_from_slice(&fh);
let (off, _hdr) = scan_for_first_header(&overlay).unwrap();
assert_eq!(off, 256);
}
#[test]
fn scan_ignores_non_aligned_header_with_implausible_lengths() {
let mut fh = make_first_header(0);
fh[20..24].copy_from_slice(&0i32.to_le_bytes());
let mut overlay = vec![0u8; 2048];
overlay[256..256 + 28].copy_from_slice(&fh);
assert_eq!(
scan_for_first_header(&overlay),
Err(Error::SignatureNotFound)
);
}
#[test]
fn scan_accepts_aligned_header_that_extends_past_overlay() {
let mut fh = make_first_header(0);
fh[24..28].copy_from_slice(&4096i32.to_le_bytes());
let mut overlay = vec![0u8; 2048];
overlay[..28].copy_from_slice(&fh);
let (off, _hdr) = scan_for_first_header(&overlay).unwrap();
assert_eq!(off, 0);
}
}