From c4a9a22cd06f59ea5060481df99f5a7b09db8e26 Mon Sep 17 00:00:00 2001 From: matthew seyer Date: Sun, 21 May 2017 21:15:58 -0500 Subject: Changed Attribute Naming standards and now storing attributes as an array. --- src/attr_x10.rs | 14 +++--- src/attr_x30.rs | 14 +++--- src/attribute.rs | 47 ++++++++++++++++-- src/entry.rs | 148 +++++++++++++++++++++++++++++++++++++------------------ src/main.rs | 1 - src/mft.rs | 6 +-- 6 files changed, 158 insertions(+), 72 deletions(-) diff --git a/src/attr_x10.rs b/src/attr_x10.rs index aeaa771..c3982bc 100644 --- a/src/attr_x10.rs +++ b/src/attr_x10.rs @@ -5,8 +5,8 @@ use byteorder::{ReadBytesExt, LittleEndian}; use std::io::Read; use std::mem; -#[derive(Serialize, Debug)] -pub struct StandardInformationAttribute { +#[derive(Serialize, Clone, Debug)] +pub struct StandardInfoAttr { pub created: WinTimestamp, pub modified: WinTimestamp, pub mft_modified: WinTimestamp, @@ -22,9 +22,9 @@ pub struct StandardInformationAttribute { #[serde(serialize_with = "serialize_u64")] pub usn: u64 } -impl StandardInformationAttribute { - pub fn new(mut reader: R) -> Result { - let mut attribute: StandardInformationAttribute = unsafe { +impl StandardInfoAttr { + pub fn new(mut reader: R) -> Result { + let mut attribute: StandardInfoAttr = unsafe { mem::zeroed() }; @@ -47,7 +47,7 @@ impl StandardInformationAttribute { #[cfg(test)] mod tests { - use super::StandardInformationAttribute; + use super::StandardInfoAttr; #[test] fn si_attribute_test_01() { @@ -59,7 +59,7 @@ mod tests { 0x68,0x58,0xA0,0x0A,0x02,0x00,0x00,0x00 ]; - let attribute = match StandardInformationAttribute::new(attribute_buffer) { + let attribute = match StandardInfoAttr::new(attribute_buffer) { Ok(attribute) => attribute, Err(error) => panic!(error) }; diff --git a/src/attr_x30.rs b/src/attr_x30.rs index 8b99e1e..9218156 100644 --- a/src/attr_x30.rs +++ b/src/attr_x30.rs @@ -8,8 +8,8 @@ use encoding::all::UTF_16LE; use std::io::Read; use std::mem; -#[derive(Serialize, Debug)] -pub struct FileNameAttribute { +#[derive(Serialize, Clone, Debug)] +pub struct FileNameAttr { pub parent: MftReference, pub created: WinTimestamp, pub modified: WinTimestamp, @@ -26,9 +26,9 @@ pub struct FileNameAttribute { pub name: String, pub fullname: Option } -impl FileNameAttribute { - pub fn new(mut reader: R) -> Result { - let mut attribute: FileNameAttribute = unsafe { +impl FileNameAttr { + pub fn new(mut reader: R) -> Result { + let mut attribute: FileNameAttr = unsafe { mem::zeroed() }; @@ -62,7 +62,7 @@ impl FileNameAttribute { #[cfg(test)] mod tests { - use super::FileNameAttribute; + use super::FileNameAttr; #[test] fn fn_attribute_test_01() { @@ -75,7 +75,7 @@ mod tests { 0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ]; - let attribute = match FileNameAttribute::new(attribute_buffer) { + let attribute = match FileNameAttr::new(attribute_buffer) { Ok(attribute) => attribute, Err(error) => panic!(error) }; diff --git a/src/attribute.rs b/src/attribute.rs index 4a2ad08..8d662db 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -1,4 +1,7 @@ use errors::{MftError}; +use utils; +use attr_x10::{StandardInfoAttr}; +use attr_x30::{FileNameAttr}; use rwinstructs::serialize::{serialize_u64}; use byteorder::{ReadBytesExt, LittleEndian}; use encoding::{Encoding, DecoderTrap}; @@ -9,6 +12,30 @@ use std::io::Seek; use std::io::SeekFrom; use std::mem; +#[derive(Clone, Debug)] +pub struct RawAttribute( + pub Vec +); +impl ser::Serialize for RawAttribute { + fn serialize(&self, serializer: S) -> Result + where S: ser::Serializer + { + serializer.serialize_str( + &format!("{}", + utils::to_hex_string(&self.0)) + ) + } +} + +#[derive(Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum AttributeContent { + Raw(RawAttribute), + AttrX10(StandardInfoAttr), + AttrX30(FileNameAttr), + None +} + bitflags! { pub flags AttributeDataFlags: u16 { const IS_COMPRESSED = 0x0001, @@ -24,7 +51,7 @@ pub fn serialize_attr_data_flags(&item: &AttributeDataFlags, serializer: S) serializer.serialize_str(&format!("{:?}", item)) } -#[derive(Serialize, Debug)] +#[derive(Serialize, Clone, Debug)] pub struct AttributeHeader { pub attribute_type: u32, pub attribute_size: u32, @@ -73,7 +100,11 @@ impl AttributeHeader { )? ); } else { - panic!("Unhandled resident flag: {}",attribute_header.resident_flag); + panic!( + "Unhandled resident flag: {} (offet: {})", + attribute_header.resident_flag, + current_offset + ); } if attribute_header.name_size > 0 { @@ -101,7 +132,7 @@ impl AttributeHeader { } } -#[derive(Serialize, Debug)] +#[derive(Serialize, Clone, Debug)] #[serde(untagged)] pub enum ResidentialHeader{ None, @@ -109,7 +140,7 @@ pub enum ResidentialHeader{ NonResident(NonResidentHeader) } -#[derive(Serialize, Debug)] +#[derive(Serialize, Clone, Debug)] pub struct ResidentHeader{ pub data_size: u32, pub data_offset: u16, @@ -131,7 +162,7 @@ impl ResidentHeader { } } -#[derive(Serialize, Debug)] +#[derive(Serialize, Clone, Debug)] pub struct NonResidentHeader{ #[serde(serialize_with = "serialize_u64")] pub vnc_first: u64, @@ -171,6 +202,12 @@ impl NonResidentHeader { } } +#[derive(Serialize, Clone, Debug)] +pub struct MftAttribute{ + pub header: AttributeHeader, + pub content: AttributeContent +} + #[cfg(test)] mod tests { use std::io::Cursor; diff --git a/src/entry.rs b/src/entry.rs index ec9978b..b2f3dac 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,10 +1,10 @@ use errors::{MftError}; -use enumerator::{PathEnumerator,PathMapping}; +use enumerator::{PathMapping}; use mft::{MftHandler}; use attribute; +use attr_x10::{StandardInfoAttr}; +use attr_x30::{FileNameAttr}; use utils; -use attr_x10; -use attr_x30; use rwinstructs::reference::{MftReference}; use rwinstructs::serialize::{serialize_u64}; use byteorder::{ReadBytesExt, LittleEndian}; @@ -108,8 +108,7 @@ impl EntryHeader{ #[derive(Serialize, Debug)] pub struct MftEntry{ pub header: EntryHeader, - pub attr_standard_info: Option>, - pub attr_filename: Option> + pub attributes: Vec } impl MftEntry{ pub fn new(mut buffer: Vec, entry: Option) -> Result { @@ -152,20 +151,22 @@ impl MftEntry{ } pub fn get_pathmap(&self) -> Option { - match self.attr_filename { - Some(ref attributes) => { - for attribute in attributes { - if attribute.namespace != 2 { - return Some( - PathMapping { - name: attribute.name.clone(), - parent: MftReference(attribute.parent.0) - } - ); + for attribute in self.attributes.iter() { + if attribute.header.attribute_type == 0x30 { + match attribute.content { + attribute::AttributeContent::AttrX30(ref attrib) => { + if attrib.namespace != 2 { + return Some( + PathMapping { + name: attrib.name.clone(), + parent: MftReference(attrib.parent.0) + } + ); + } } + _ => {} } - }, - _ => {} + } } None @@ -188,6 +189,7 @@ impl MftEntry{ let mut current_offset = buffer.seek( SeekFrom::Start(self.header.fst_attr_offset as u64) )?; + let attr_count: u32 = 0; loop { @@ -195,34 +197,73 @@ impl MftEntry{ &mut buffer )?; - match attribute_header.attribute_type { - 0x10 => { - let si_attr = attr_x10::StandardInformationAttribute::new(&mut buffer)?; + if attribute_header.attribute_type == 0xFFFFFFFF { + break; + } - if self.attr_standard_info.is_none(){ - self.attr_standard_info = Some(Vec::new()); - } + match attribute_header.residential_header { + attribute::ResidentialHeader::Resident(ref header) => { + // Create buffer for raw attribute content + let mut content_buffer = vec![0;header.data_size as usize]; - self.attr_standard_info.as_mut().unwrap().push(si_attr); - }, - 0x30 => { - let fn_attr = attr_x30::FileNameAttribute::new(&mut buffer)?; - if self.attr_filename.is_none(){ - self.attr_filename = Some(Vec::new()); + // read into content buffer + buffer.read_exact( + &mut content_buffer + ).unwrap(); + + // Create attribute content to parse buffer into + let mut attr_content: attribute::AttributeContent = unsafe { + mem::zeroed() + }; + + // Get attribute contents + match attribute_header.attribute_type { + 0x10 => { + let attr = StandardInfoAttr::new( + content_buffer.as_slice() + )?; + + attr_content = attribute::AttributeContent::AttrX10( + attr + ); + }, + 0x30 => { + let attr = FileNameAttr::new( + content_buffer.as_slice() + )?; + + attr_content = attribute::AttributeContent::AttrX30( + attr + ); + }, + _ => { + attr_content = attribute::AttributeContent::Raw( + attribute::RawAttribute( + content_buffer + ) + ); + } } - self.attr_filename.as_mut().unwrap().push(fn_attr); + // push attribute into attributes + self.attributes.push( + attribute::MftAttribute { + header: attribute_header.clone(), + content: attr_content + } + ); }, - 0xFFFFFFFF => { - // println!("END OF ATTRIBUTES"); - break; + attribute::ResidentialHeader::NonResident(_) => { + // No content, so push header into attributes + self.attributes.push( + attribute::MftAttribute { + header: attribute_header.clone(), + content: attribute::AttributeContent::None + } + ); }, - _ => { - // println!( - // "UNHANDLED ATTRIBUTE: 0x{:04X} at offset {}", - // attribute_header.attribute_type, - // current_offset - // ); + attribute::ResidentialHeader::None => { + // Not sure about this... } } @@ -230,21 +271,30 @@ impl MftEntry{ SeekFrom::Start(current_offset + attribute_header.attribute_size as u64) )?; } + Ok(attr_count) } pub fn set_fullnames(&mut self, mft_handler: &mut MftHandler){ - match self.attr_filename { - Some(ref mut attributes) => { - for attribute in attributes { - let fullpath = mft_handler.get_fullpath( - attribute.parent - ); - let fullname = fullpath + "/" + attribute.name.as_str(); - attribute.fullname = Some(fullname); + // Iterate through each MFT attribute with mutable reference + for attribute in self.attributes.iter_mut() { + // If attribute is type 0x30 + if attribute.header.attribute_type == 0x30 { + // Check if resident content + match attribute.content { + attribute::AttributeContent::AttrX30(ref mut attrib) => { + // Get fullpath + let fullpath = mft_handler.get_fullpath( + attrib.parent + ); + // Set fullname + let fullname = fullpath + "/" + attrib.name.as_str(); + // Set attribute to fullname + attrib.fullname = Some(fullname); + } + _ => {} } - }, - _ => {} + } } } } diff --git a/src/main.rs b/src/main.rs index 7d24845..c4c68d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,6 @@ fn process_directory(directory: &str, serializer: S) where S: serde::Serializ } fn process_file(filename: &str, serializer: &mut S) -> bool { - // Check if file is a prefetch file let mut mft_handler = match MftHandler::new(filename) { Ok(mft_handler) => mft_handler, Err(error) => { diff --git a/src/mft.rs b/src/mft.rs index 802f381..888f8fb 100644 --- a/src/mft.rs +++ b/src/mft.rs @@ -151,14 +151,14 @@ impl MftHandler{ fn get_mapping_from_entry(&mut self, entry: u64) -> Option{ self.filehandle.seek( SeekFrom::Start(entry * self._entry_size as u64) - ); + ).unwrap(); let mut entry_buffer = vec![0; self._entry_size as usize]; self.filehandle.read_exact( &mut entry_buffer - ); + ).unwrap(); - let mut mft_entry = self.entry_from_buffer( + let mft_entry = self.entry_from_buffer( entry_buffer, entry ); -- cgit v1.2.3