frnsc-prefetch 0.13.3

Pure rust windows prefetch parser implementation
Documentation
use forensic_rs::err::{ForensicError, ForensicResult};

use crate::common::{
    u16_at_pos, u32_at_pos, u64_at_pos, utf16_at_offset, NtfsFile, PrefetchFileInformation,
    VolumeInformation,
};

pub fn volume_info_26(
    file_buffer: &[u8],
    info: &PrefetchFileInformation,
) -> ForensicResult<Vec<VolumeInformation>> {
    volume_info_23(file_buffer, info)
}
pub fn volume_info_30(
    file_buffer: &[u8],
    info: &PrefetchFileInformation,
) -> ForensicResult<Vec<VolumeInformation>> {
    let end_volume_pos = (info.volume_information_offset + info.volume_information_size) as usize;
    if end_volume_pos > file_buffer.len() {
        return Err(ForensicError::bad_format_str(
            "The volume information position is greater than the file buffer",
        ));
    }
    let volume_data = &file_buffer[info.volume_information_offset as usize..end_volume_pos];
    let mut volumes = Vec::with_capacity(info.volume_count as usize);
    for i in 0..(info.volume_count as usize) {
        let pos = i * 96;
        let volume_device_path_offset = u32_at_pos(volume_data, pos);
        let volume_device_path_characters = u32_at_pos(volume_data, pos + 4);
        if (volume_device_path_offset + volume_device_path_characters) as usize > volume_data.len()
        {
            return Err(ForensicError::bad_format_str(
                "The device path position is greater than the volume buffer",
            ));
        }
        let device_path = utf16_at_offset(
            volume_data,
            volume_device_path_offset as usize,
            volume_device_path_characters as usize,
        )?;
        let creation_time = u64_at_pos(volume_data, pos + 8);
        let serial_number = u32_at_pos(volume_data, pos + 16);
        let file_references_offset = u32_at_pos(volume_data, pos + 20);
        let file_references_data_size = u32_at_pos(volume_data, pos + 24);
        let end_files_pos = (file_references_data_size + file_references_offset) as usize;
        if end_files_pos > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The files reference position is greater than the volume buffer",
            ));
        }
        let file_data = &volume_data[file_references_offset as usize..end_files_pos];
        let file_references = extract_file_references_23(file_data)?;
        let directory_strings_offset = u32_at_pos(volume_data, pos + 28);
        let directory_strings_count = u32_at_pos(volume_data, pos + 32);
        if directory_strings_offset as usize > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The directory strings position is greater than the volume buffer",
            ));
        }
        let directory_data = &volume_data[directory_strings_offset as usize..];
        let directory_strings =
            extract_directory_strings_23(directory_data, directory_strings_count as usize)?;
        volumes.push(VolumeInformation {
            device_path,
            directory_strings,
            file_references,
            creation_time,
            serial_number,
        });
    }
    Ok(volumes)
}

pub fn volume_info_17(
    file_buffer: &[u8],
    info: &PrefetchFileInformation,
) -> ForensicResult<Vec<VolumeInformation>> {
    let end_volume_pos = (info.volume_information_offset + info.volume_information_size) as usize;
    if end_volume_pos > file_buffer.len() {
        return Err(ForensicError::bad_format_str(
            "The volume information position is greater than the file buffer",
        ));
    }
    let volume_data = &file_buffer[info.volume_information_offset as usize..end_volume_pos];
    let mut volumes = Vec::with_capacity(info.volume_count as usize);
    for i in 0..(info.volume_count as usize) {
        let pos = i * 40;
        let volume_device_path_offset = u32_at_pos(volume_data, pos);
        let volume_device_path_characters = u32_at_pos(volume_data, pos + 4);
        if (volume_device_path_offset + volume_device_path_characters) as usize > volume_data.len()
        {
            return Err(ForensicError::bad_format_str(
                "The device path position is greater than the volume buffer",
            ));
        }
        let device_path = utf16_at_offset(
            volume_data,
            volume_device_path_offset as usize,
            volume_device_path_characters as usize,
        )?;
        let creation_time = u64_at_pos(volume_data, pos + 8);
        let serial_number = u32_at_pos(volume_data, pos + 16);
        let file_references_offset = u32_at_pos(volume_data, pos + 20);
        let file_references_data_size = u32_at_pos(volume_data, pos + 24);
        let end_files_pos = (file_references_data_size + file_references_offset) as usize;
        if end_files_pos > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The files reference position is greater than the volume buffer",
            ));
        }
        let file_data = &volume_data[file_references_offset as usize..end_files_pos];
        let file_references = extract_file_references_17(file_data)?;
        let directory_strings_offset = u32_at_pos(volume_data, pos + 28);
        let directory_strings_count = u32_at_pos(volume_data, pos + 32);
        if directory_strings_offset as usize > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The directory strings position is greater than the volume buffer",
            ));
        }
        let directory_data = &volume_data[directory_strings_offset as usize..];
        let directory_strings =
            extract_directory_strings_23(directory_data, directory_strings_count as usize)?;
        volumes.push(VolumeInformation {
            device_path,
            directory_strings,
            file_references,
            creation_time,
            serial_number,
        });
    }
    Ok(volumes)
}

pub fn volume_info_23(
    file_buffer: &[u8],
    info: &PrefetchFileInformation,
) -> ForensicResult<Vec<VolumeInformation>> {
    let end_volume_pos = (info.volume_information_offset + info.volume_information_size) as usize;
    if end_volume_pos > file_buffer.len() {
        return Err(ForensicError::bad_format_str(
            "The volume information position is greater than the file buffer",
        ));
    }
    let volume_data = &file_buffer[info.volume_information_offset as usize..end_volume_pos];
    let mut volumes = Vec::with_capacity(info.volume_count as usize);
    for i in 0..(info.volume_count as usize) {
        let pos = i * 104;
        let volume_device_path_offset = u32_at_pos(volume_data, pos);
        let volume_device_path_characters = u32_at_pos(volume_data, pos + 4);
        if (volume_device_path_offset + volume_device_path_characters) as usize > volume_data.len()
        {
            return Err(ForensicError::bad_format_str(
                "The device path position is greater than the volume buffer",
            ));
        }
        let device_path = utf16_at_offset(
            volume_data,
            volume_device_path_offset as usize,
            volume_device_path_characters as usize,
        )?;
        let creation_time = u64_at_pos(volume_data, pos + 8);
        let serial_number = u32_at_pos(volume_data, pos + 16);
        let file_references_offset = u32_at_pos(volume_data, pos + 20);
        let file_references_data_size = u32_at_pos(volume_data, pos + 24);
        let end_files_pos = (file_references_data_size + file_references_offset) as usize;
        if end_files_pos > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The files reference position is greater than the volume buffer",
            ));
        }
        let file_data = &volume_data[file_references_offset as usize..end_files_pos];
        let file_references = extract_file_references_23(file_data)?;
        let directory_strings_offset = u32_at_pos(volume_data, pos + 28);
        let directory_strings_count = u32_at_pos(volume_data, pos + 32);
        if directory_strings_offset as usize > volume_data.len() {
            return Err(ForensicError::bad_format_str(
                "The directory strings position is greater than the volume buffer",
            ));
        }
        let directory_data = &volume_data[directory_strings_offset as usize..];
        let directory_strings =
            extract_directory_strings_23(directory_data, directory_strings_count as usize)?;
        volumes.push(VolumeInformation {
            device_path,
            directory_strings,
            file_references,
            creation_time,
            serial_number,
        });
    }
    Ok(volumes)
}

fn extract_file_references_17(file_reference: &[u8]) -> ForensicResult<Vec<NtfsFile>> {
    if file_reference.len() < 8 {
        return Err(ForensicError::Other(
            "Invalid size for file references".into(),
        ));
    }
    let file_reference_count = u32_at_pos(file_reference, 4);
    if (8 + (file_reference_count as usize * 8)) > file_reference.len() {
        return Err(ForensicError::bad_format_str(
            "The file reference size is greater than the buffer",
        ));
    }
    let file_reference = &file_reference[8..];
    let mut files = Vec::with_capacity(file_reference_count as usize);
    for pos in (0..(file_reference_count as usize * 8)).step_by(8) {
        let mft_entry_and_seq = u64_at_pos(file_reference, pos);
        let mft_entry = mft_entry_and_seq & 0xffffffffffff;
        if mft_entry == 0 {
            continue;
        }
        let seq_number = (mft_entry_and_seq >> 48) as u16;
        files.push(NtfsFile {
            mft_entry,
            seq_number,
        })
    }
    Ok(files)
}

fn extract_file_references_23(file_reference: &[u8]) -> ForensicResult<Vec<NtfsFile>> {
    if file_reference.len() < 16 {
        return Err(ForensicError::Other(
            "Invalid size for file references".into(),
        ));
    }
    let file_reference_count = u32_at_pos(file_reference, 4);
    if (16 + (file_reference_count as usize * 8)) > file_reference.len() {
        return Err(ForensicError::bad_format_str(
            "The file reference size is greater than the buffer",
        ));
    }
    let file_reference = &file_reference[16..];
    let mut files = Vec::with_capacity(file_reference_count as usize);
    for pos in (0..(file_reference_count as usize * 8)).step_by(8) {
        let mft_entry_and_seq = u64_at_pos(file_reference, pos);
        let mft_entry = mft_entry_and_seq & 0xffffffffffff;
        if mft_entry == 0 {
            continue;
        }
        let seq_number = (mft_entry_and_seq >> 48) as u16;
        files.push(NtfsFile {
            mft_entry,
            seq_number,
        })
    }
    Ok(files)
}

fn extract_directory_strings_23(
    directory_strings: &[u8],
    count: usize,
) -> ForensicResult<Vec<String>> {
    if directory_strings.len() < 2 {
        return Err(ForensicError::bad_format_str(
            "Invalid buffer size for directory strings",
        ));
    }
    let mut list = Vec::with_capacity(count);
    let mut pos = 0;
    for _ in 0..count {
        if pos + 2 > directory_strings.len() {
            return Err(ForensicError::bad_format_str(
                "The Directory String size is greater than the buffer size",
            ));
        }
        let characters = u16_at_pos(directory_strings, pos) as usize;
        if pos + 4 + (characters * 2) >= directory_strings.len() {
            return Err(ForensicError::bad_format_str(
                "The Directory String size is greater than the buffer size",
            ));
        }
        let text = utf16_at_offset(directory_strings, pos + 2, characters * 2 + 2)?;
        pos += 4 + (characters * 2);
        list.push(text);
    }
    Ok(list)
}