Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/RustyMft.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormatthew seyer <matthew.seyer@gmail.com>2017-04-21 06:42:12 +0300
committermatthew seyer <matthew.seyer@gmail.com>2017-04-21 06:42:12 +0300
commitc103f961ffca9828fb4ce36a836ea75532df0841 (patch)
treeb39b3c2a6e22289a37e37f7865ad7e3f0007698c
parent1e51b18c8abe4e7b7b437ae26301dcac6ba8bbf8 (diff)
initial commit
-rw-r--r--Cargo.toml28
-rw-r--r--examples/parse_entry.rs85
-rw-r--r--examples/serialized_structs.rs84
-rw-r--r--src/attr_x10.rs79
-rw-r--r--src/attr_x30.rs95
-rw-r--r--src/attribute.rs222
-rw-r--r--src/entry.rs219
-rw-r--r--src/errors.rs53
-rw-r--r--src/lib.rs16
-rw-r--r--src/main.rs87
-rw-r--r--src/mft.rs76
-rw-r--r--src/utils.rs10
-rw-r--r--testdata/attribute_nonresident_001bin0 -> 80 bytes
-rw-r--r--testdata/attribute_nonresident_002bin0 -> 80 bytes
-rw-r--r--testdata/attribute_resident_001bin0 -> 24 bytes
-rw-r--r--testdata/entry_long_name_and_res_ads_002bin0 -> 1024 bytes
-rw-r--r--testdata/entry_super_long_name_001bin0 -> 1024 bytes
17 files changed, 1054 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..e2d4717
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "RustyMft"
+version = "0.1.0"
+authors = ["matthew seyer <matthew.seyer@gmail.com>"]
+
+[lib]
+name = "rustymft"
+crate-type = ["cdylib","lib"]
+
+[dependencies]
+log = "*"
+clap = "*"
+encoding = "0.2"
+byteorder = "*"
+bitflags = "0.7"
+serde = "0.9"
+serde_derive = "0.9"
+serde_json = "0.9"
+seek_bufread = "~1.2"
+
+[dependencies.r-winstructs]
+version = "*"
+branch = "master"
+git = "https://github.com/forensicmatt/r-winstructs"
+
+[dependencies.chrono]
+version = "*"
+features = ["serde", "rustc-serialize"]
diff --git a/examples/parse_entry.rs b/examples/parse_entry.rs
new file mode 100644
index 0000000..53fa76e
--- /dev/null
+++ b/examples/parse_entry.rs
@@ -0,0 +1,85 @@
+extern crate serde_json;
+extern crate rwinstructs;
+extern crate rustymft;
+use rwinstructs::serialize;
+use rustymft::entry;
+
+fn print_entry_01(){
+ let entry_buffer: &[u8] = &[
+ 0x46,0x49,0x4C,0x45,0x30,0x00,0x03,0x00,0x2E,0xB5,0x10,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x00,0x01,0x00,0x38,0x00,0x01,0x00,0x28,0x03,0x00,0x00,0x00,0x04,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,
+ 0x05,0x00,0x65,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x60,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x18,0x00,0x00,0x00,
+ 0xC5,0x9A,0xD7,0x96,0x6E,0xB9,0xD2,0x01,0x92,0x56,0x54,0xB8,0x6E,0xB9,0xD2,0x01,
+ 0x92,0x56,0x54,0xB8,0x6E,0xB9,0xD2,0x01,0xC5,0x9A,0xD7,0x96,0x6E,0xB9,0xD2,0x01,
+ 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x0C,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x70,0x2B,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x28,0x02,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x0A,0x02,0x00,0x00,0x18,0x00,0x01,0x00,
+ 0x27,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0xC5,0x9A,0xD7,0x96,0x6E,0xB9,0xD2,0x01,
+ 0xC5,0x9A,0xD7,0x96,0x6E,0xB9,0xD2,0x01,0xED,0x6E,0x47,0xA7,0x6E,0xB9,0xD2,0x01,
+ 0xC5,0x9A,0xD7,0x96,0x6E,0xB9,0xD2,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xE4,0x00,0x74,0x00,0x69,0x00,0x6D,0x00,0x65,0x00,0x5F,0x00,0x66,0x00,0x6F,0x00,
+ 0x72,0x00,0x5F,0x00,0x61,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x05,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,
+ 0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,
+ 0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,
+ 0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,
+ 0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,
+ 0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,
+ 0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,
+ 0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,
+ 0x65,0x00,0x72,0x00,0x5F,0x00,0x73,0x00,0x75,0x00,0x70,0x00,0x65,0x00,0x72,0x00,
+ 0x5F,0x00,0x6C,0x00,0x6F,0x00,0x6E,0x00,0x67,0x00,0x6E,0x00,0x61,0x00,0x6D,0x00,
+ 0x65,0x00,0x2E,0x00,0x74,0x00,0x78,0x00,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,
+ 0x10,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x61,0x63,0x56,0x9C,0xC8,0x24,0xE7,0x11,
+ 0xBF,0xBD,0x40,0xE2,0x30,0x3A,0x39,0x8D,0x80,0x00,0x00,0x00,0x38,0x00,0x00,0x00,
+ 0x00,0x00,0x18,0x00,0x00,0x00,0x06,0x00,0x1F,0x00,0x00,0x00,0x18,0x00,0x00,0x00,
+ 0x6A,0x75,0x73,0x74,0x20,0x74,0x65,0x73,0x74,0x69,0x6E,0x67,0x20,0x61,0x20,0x73,
+ 0x75,0x70,0x65,0x72,0x20,0x6C,0x6F,0x6E,0x67,0x20,0x6E,0x61,0x6D,0x65,0x21,0x00,
+ 0xFF,0xFF,0xFF,0xFF,0x82,0x79,0x47,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00
+ ];
+
+ let mft_entry = entry::MftEntry::new(entry_buffer.to_vec(),None).unwrap();
+
+ println!("{}",serde_json::to_string_pretty(&mft_entry).unwrap());
+}
+
+fn main() {
+ unsafe {
+ serialize::U64_SERIALIZATION = serialize::U64Serialization::AsString
+ }
+ print_entry_01()
+}
diff --git a/examples/serialized_structs.rs b/examples/serialized_structs.rs
new file mode 100644
index 0000000..5bbc190
--- /dev/null
+++ b/examples/serialized_structs.rs
@@ -0,0 +1,84 @@
+extern crate serde_json;
+extern crate rwinstructs;
+extern crate rustymft;
+use rwinstructs::serialize;
+use rustymft::entry;
+use rustymft::attribute;
+use std::io::Cursor;
+
+fn print_entry_header(){
+ let header_buffer: &[u8] = &[
+ 0x46,0x49,0x4C,0x45,0x30,0x00,0x03,0x00,0xCC,0xB3,0x7D,0x84,0x0C,0x00,0x00,0x00,
+ 0x05,0x00,0x01,0x00,0x38,0x00,0x05,0x00,0x48,0x03,0x00,0x00,0x00,0x04,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xD5,0x95,0x00,0x00,
+ 0x53,0x57,0x81,0x37,0x00,0x00,0x00,0x00
+ ];
+
+ let entry_header = match entry::EntryHeader::new(header_buffer,None) {
+ Ok(entry_header) => entry_header,
+ Err(error) => panic!(error)
+ };
+
+ println!("{}",serde_json::to_string_pretty(&entry_header).unwrap());
+}
+fn print_attribute_header_01(){
+ let raw: &[u8] = &[
+ 0x10,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x48,0x00,0x00,0x00,0x18,0x00,0x00,0x00
+ ];
+
+ let attribute_buffer = Cursor::new(raw);
+
+ let attribute_header = match attribute::AttributeHeader::new(attribute_buffer) {
+ Ok(attribute_header) => attribute_header,
+ Err(error) => panic!(error)
+ };
+
+ println!("{}",serde_json::to_string_pretty(&attribute_header).unwrap());
+}
+fn print_attribute_header_02() {
+ let raw: &[u8] = &[
+ 0x80,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x06,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xBF,0x1E,0x01,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,
+ 0x33,0x20,0xC8,0x00,0x00,0x00,0x0C,0x32,0xA0,0x56,0xE3,0xE6,0x24,0x00,0xFF,0xFF
+ ];
+
+ let attribute_buffer = Cursor::new(raw);
+
+ let attribute_header = match attribute::AttributeHeader::new(attribute_buffer) {
+ Ok(attribute_header) => attribute_header,
+ Err(error) => panic!(error)
+ };
+
+ println!("{}",serde_json::to_string_pretty(&attribute_header).unwrap());
+}
+fn print_attribute_header_03() {
+ let raw: &[u8] = &[
+ 0x80,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x04,0x40,0x00,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x11,0xD1,0x00,0x00,0x00,0x00,0x00,
+ 0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x1F,0x11,0x0D,0x00,0x00,0x00,
+ 0x00,0xF0,0x1F,0x11,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x24,0x00,0x42,0x00,0x61,0x00,0x64,0x00,0x04,0xFF,0x11,0xD1,0x00,0x00,0x00,0x00
+ ];
+
+ let attribute_buffer = Cursor::new(raw);
+
+ let attribute_header = match attribute::AttributeHeader::new(attribute_buffer) {
+ Ok(attribute_header) => attribute_header,
+ Err(error) => panic!(error)
+ };
+
+ println!("{}",serde_json::to_string_pretty(&attribute_header).unwrap());
+}
+
+fn main(){
+ unsafe {
+ serialize::U64_SERIALIZATION = serialize::U64Serialization::AsString
+ }
+ print_entry_header();
+ print_attribute_header_01();
+ print_attribute_header_02();
+ print_attribute_header_03();
+}
diff --git a/src/attr_x10.rs b/src/attr_x10.rs
new file mode 100644
index 0000000..aeaa771
--- /dev/null
+++ b/src/attr_x10.rs
@@ -0,0 +1,79 @@
+use errors::{MftError};
+use rwinstructs::timestamp::{WinTimestamp};
+use rwinstructs::serialize::{serialize_u64};
+use byteorder::{ReadBytesExt, LittleEndian};
+use std::io::Read;
+use std::mem;
+
+#[derive(Serialize, Debug)]
+pub struct StandardInformationAttribute {
+ pub created: WinTimestamp,
+ pub modified: WinTimestamp,
+ pub mft_modified: WinTimestamp,
+ pub accessed: WinTimestamp,
+ pub file_flags: u32,
+ pub max_version: u32,
+ pub version: u32,
+ pub class_id: u32,
+ pub owner_id: u32,
+ pub security_id: u32,
+ #[serde(serialize_with = "serialize_u64")]
+ pub quota: u64,
+ #[serde(serialize_with = "serialize_u64")]
+ pub usn: u64
+}
+impl StandardInformationAttribute {
+ pub fn new<R: Read>(mut reader: R) -> Result<StandardInformationAttribute,MftError> {
+ let mut attribute: StandardInformationAttribute = unsafe {
+ mem::zeroed()
+ };
+
+ attribute.created = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.mft_modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.accessed = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.file_flags = reader.read_u32::<LittleEndian>()?;
+ attribute.max_version = reader.read_u32::<LittleEndian>()?;
+ attribute.version = reader.read_u32::<LittleEndian>()?;
+ attribute.class_id = reader.read_u32::<LittleEndian>()?;
+ attribute.owner_id = reader.read_u32::<LittleEndian>()?;
+ attribute.security_id = reader.read_u32::<LittleEndian>()?;
+ attribute.quota = reader.read_u64::<LittleEndian>()?;
+ attribute.usn = reader.read_u64::<LittleEndian>()?;
+
+ Ok(attribute)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::StandardInformationAttribute;
+
+ #[test]
+ fn si_attribute_test_01() {
+ let attribute_buffer: &[u8] = &[
+ 0x2F,0x6D,0xB6,0x6F,0x0C,0x97,0xCE,0x01,0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01,
+ 0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01,0x56,0xCD,0x1A,0x75,0x73,0xB5,0xCE,0x01,
+ 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xB0,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x68,0x58,0xA0,0x0A,0x02,0x00,0x00,0x00
+ ];
+
+ let attribute = match StandardInformationAttribute::new(attribute_buffer) {
+ Ok(attribute) => attribute,
+ Err(error) => panic!(error)
+ };
+
+ assert_eq!(attribute.created.0, 130207518909951279);
+ assert_eq!(attribute.modified.0, 130240946730880342);
+ assert_eq!(attribute.mft_modified.0, 130240946730880342);
+ assert_eq!(attribute.accessed.0, 130240946730880342);
+ assert_eq!(attribute.file_flags, 32);
+ assert_eq!(attribute.max_version, 0);
+ assert_eq!(attribute.version, 0);
+ assert_eq!(attribute.class_id, 0);
+ assert_eq!(attribute.security_id, 1456);
+ assert_eq!(attribute.quota, 0);
+ assert_eq!(attribute.usn, 8768215144);
+ }
+}
diff --git a/src/attr_x30.rs b/src/attr_x30.rs
new file mode 100644
index 0000000..3d4503b
--- /dev/null
+++ b/src/attr_x30.rs
@@ -0,0 +1,95 @@
+use errors::{MftError};
+use rwinstructs::timestamp::{WinTimestamp};
+use rwinstructs::reference::{MftReference};
+use rwinstructs::serialize::{serialize_u64};
+use byteorder::{ReadBytesExt, LittleEndian};
+use encoding::{Encoding, DecoderTrap};
+use encoding::all::UTF_16LE;
+use std::io::Read;
+use std::mem;
+
+#[derive(Serialize, Debug)]
+pub struct FileNameAttribute {
+ pub parent: MftReference,
+ pub created: WinTimestamp,
+ pub modified: WinTimestamp,
+ pub mft_modified: WinTimestamp,
+ pub accessed: WinTimestamp,
+ #[serde(serialize_with = "serialize_u64")]
+ pub logical_size: u64,
+ #[serde(serialize_with = "serialize_u64")]
+ pub physical_size: u64,
+ pub flags: u32,
+ pub reparse_value: u32,
+ pub name_length: u8,
+ pub namespace: u8,
+ pub name: String
+}
+impl FileNameAttribute {
+ pub fn new<R: Read>(mut reader: R) -> Result<FileNameAttribute,MftError> {
+ let mut attribute: FileNameAttribute = unsafe {
+ mem::zeroed()
+ };
+
+ attribute.parent = MftReference(reader.read_u64::<LittleEndian>()?);
+ attribute.created = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.mft_modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.accessed = WinTimestamp(reader.read_u64::<LittleEndian>()?);
+ attribute.logical_size = reader.read_u64::<LittleEndian>()?;
+ attribute.physical_size = reader.read_u64::<LittleEndian>()?;
+ attribute.flags = reader.read_u32::<LittleEndian>()?;
+ attribute.reparse_value = reader.read_u32::<LittleEndian>()?;
+ attribute.name_length = reader.read_u8()?;
+ attribute.namespace = reader.read_u8()?;
+
+ let mut name_buffer = vec![0; (attribute.name_length as usize * 2) as usize];
+ reader.read_exact(&mut name_buffer)?;
+
+ attribute.name = match UTF_16LE.decode(&name_buffer,DecoderTrap::Ignore){
+ Ok(filename) => filename,
+ Err(error) => return Err(
+ MftError::decode_error(
+ format!("Error decoding name in filename attribute. [{}]",error)
+ )
+ )
+ };
+
+ Ok(attribute)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::FileNameAttribute;
+
+ #[test]
+ fn fn_attribute_test_01() {
+ let attribute_buffer: &[u8] = &[
+ 0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01,
+ 0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01,0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01,
+ 0xD5,0x2D,0x48,0x58,0x43,0x5F,0xCE,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x08,0x03,0x24,0x00,0x4C,0x00,0x6F,0x00,0x67,0x00,0x46,0x00,0x69,0x00,0x6C,0x00,
+ 0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ];
+
+ let attribute = match FileNameAttribute::new(attribute_buffer) {
+ Ok(attribute) => attribute,
+ Err(error) => panic!(error)
+ };
+
+ assert_eq!(attribute.parent.0, 1407374883553285);
+ assert_eq!(attribute.created.0, 130146182088895957);
+ assert_eq!(attribute.modified.0, 130146182088895957);
+ assert_eq!(attribute.mft_modified.0, 130146182088895957);
+ assert_eq!(attribute.accessed.0, 130146182088895957);
+ assert_eq!(attribute.logical_size, 67108864);
+ assert_eq!(attribute.physical_size, 67108864);
+ assert_eq!(attribute.flags, 6);
+ assert_eq!(attribute.reparse_value, 0);
+ assert_eq!(attribute.name_length, 8);
+ assert_eq!(attribute.namespace, 3);
+ assert_eq!(attribute.name, "$LogFile");
+ }
+}
diff --git a/src/attribute.rs b/src/attribute.rs
new file mode 100644
index 0000000..4a2ad08
--- /dev/null
+++ b/src/attribute.rs
@@ -0,0 +1,222 @@
+use errors::{MftError};
+use rwinstructs::serialize::{serialize_u64};
+use byteorder::{ReadBytesExt, LittleEndian};
+use encoding::{Encoding, DecoderTrap};
+use encoding::all::UTF_16LE;
+use serde::{ser};
+use std::io::Read;
+use std::io::Seek;
+use std::io::SeekFrom;
+use std::mem;
+
+bitflags! {
+ pub flags AttributeDataFlags: u16 {
+ const IS_COMPRESSED = 0x0001,
+ const COMPRESSION_MASK = 0x00FF,
+ const ENCRYPTED = 0x4000,
+ const SPARSE = 0x8000
+ }
+}
+
+pub fn serialize_attr_data_flags<S>(&item: &AttributeDataFlags, serializer: S)
+ -> Result<S::Ok, S::Error> where S: ser::Serializer
+{
+ serializer.serialize_str(&format!("{:?}", item))
+}
+
+#[derive(Serialize, Debug)]
+pub struct AttributeHeader {
+ pub attribute_type: u32,
+ pub attribute_size: u32,
+ pub resident_flag: u8, // 0 -> resident; 1 -> non-resident
+ pub name_size: u8,
+ pub name_offset: u16,
+ #[serde(serialize_with = "serialize_attr_data_flags")]
+ pub data_flags: AttributeDataFlags,
+ pub id: u16,
+ pub name: String,
+ // 16
+ pub residential_header: ResidentialHeader
+}
+impl AttributeHeader {
+ pub fn new<R: Read+Seek>(mut reader: R) -> Result<AttributeHeader,MftError> {
+ let mut attribute_header: AttributeHeader = unsafe {
+ mem::zeroed()
+ };
+
+ let current_offset = reader.seek(SeekFrom::Current(0))?;
+ // println!("at offset {}",current_offset);
+
+ attribute_header.attribute_type = reader.read_u32::<LittleEndian>()?;
+ if attribute_header.attribute_type == 0xFFFFFFFF {
+ return Ok(attribute_header);
+ }
+ attribute_header.attribute_size = reader.read_u32::<LittleEndian>()?;
+ attribute_header.resident_flag = reader.read_u8()?;
+ attribute_header.name_size = reader.read_u8()?;
+ attribute_header.name_offset = reader.read_u16::<LittleEndian>()?;
+ attribute_header.data_flags = AttributeDataFlags::from_bits_truncate(
+ reader.read_u16::<LittleEndian>()?
+ );
+ attribute_header.id = reader.read_u16::<LittleEndian>()?;
+
+ if attribute_header.resident_flag == 0 {
+ attribute_header.residential_header = ResidentialHeader::Resident(
+ ResidentHeader::new(
+ &mut reader
+ )?
+ );
+ } else if attribute_header.resident_flag == 1 {
+ attribute_header.residential_header = ResidentialHeader::NonResident(
+ NonResidentHeader::new(
+ &mut reader
+ )?
+ );
+ } else {
+ panic!("Unhandled resident flag: {}",attribute_header.resident_flag);
+ }
+
+ if attribute_header.name_size > 0 {
+ // Seek to offset
+ reader.seek(
+ SeekFrom::Start(
+ current_offset + attribute_header.name_offset as u64
+ )
+ )?;
+
+ let mut name_buffer = vec![0; (attribute_header.name_size * 2) as usize];
+ reader.read_exact(&mut name_buffer)?;
+
+ attribute_header.name = match UTF_16LE.decode(&name_buffer,DecoderTrap::Ignore){
+ Ok(filename) => filename,
+ Err(error) => return Err(
+ MftError::decode_error(
+ format!("Error decoding filename in header. [{}]",error)
+ )
+ )
+ };
+ }
+
+ Ok(attribute_header)
+ }
+}
+
+#[derive(Serialize, Debug)]
+#[serde(untagged)]
+pub enum ResidentialHeader{
+ None,
+ Resident(ResidentHeader),
+ NonResident(NonResidentHeader)
+}
+
+#[derive(Serialize, Debug)]
+pub struct ResidentHeader{
+ pub data_size: u32,
+ pub data_offset: u16,
+ pub index_flag: u8,
+ pub padding: u8,
+}
+impl ResidentHeader {
+ pub fn new<R: Read>(mut reader: R) -> Result<ResidentHeader,MftError> {
+ let mut residential_header: ResidentHeader = unsafe {
+ mem::zeroed()
+ };
+
+ residential_header.data_size = reader.read_u32::<LittleEndian>()?;
+ residential_header.data_offset = reader.read_u16::<LittleEndian>()?;
+ residential_header.index_flag = reader.read_u8()?;
+ residential_header.padding = reader.read_u8()?;
+
+ Ok(residential_header)
+ }
+}
+
+#[derive(Serialize, Debug)]
+pub struct NonResidentHeader{
+ #[serde(serialize_with = "serialize_u64")]
+ pub vnc_first: u64,
+ #[serde(serialize_with = "serialize_u64")]
+ pub vnc_last: u64,
+ pub datarun_offset: u16,
+ pub unit_compression_size: u16,
+ pub padding: u32,
+ #[serde(serialize_with = "serialize_u64")]
+ pub size_allocated: u64,
+ #[serde(serialize_with = "serialize_u64")]
+ pub size_real: u64,
+ #[serde(serialize_with = "serialize_u64")]
+ pub size_compressed: u64,
+ // pub size_total_allocated: Option<u64>
+}
+impl NonResidentHeader {
+ pub fn new<R: Read>(mut reader: R) -> Result<NonResidentHeader,MftError> {
+ let mut residential_header: NonResidentHeader = unsafe {
+ mem::zeroed()
+ };
+
+ residential_header.vnc_first = reader.read_u64::<LittleEndian>()?;
+ residential_header.vnc_last = reader.read_u64::<LittleEndian>()?;
+ residential_header.datarun_offset = reader.read_u16::<LittleEndian>()?;
+ residential_header.unit_compression_size = reader.read_u16::<LittleEndian>()?;
+ residential_header.padding = reader.read_u32::<LittleEndian>()?;
+ residential_header.size_allocated = reader.read_u64::<LittleEndian>()?;
+ residential_header.size_real = reader.read_u64::<LittleEndian>()?;
+ residential_header.size_compressed = reader.read_u64::<LittleEndian>()?;
+
+ // if residential_header.unit_compression_size > 0 {
+ // residential_header.size_total_allocated = Some(reader.read_u64::<LittleEndian>()?);
+ // }
+
+ Ok(residential_header)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::Cursor;
+ use super::AttributeHeader;
+
+ #[test]
+ fn attribute_test_01_resident() {
+ let raw: &[u8] = &[
+ 0x10,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x48,0x00,0x00,0x00,0x18,0x00,0x00,0x00
+ ];
+ let attribute_buffer = Cursor::new(raw);
+
+ let attribute_header = match AttributeHeader::new(attribute_buffer) {
+ Ok(attribute_header) => attribute_header,
+ Err(error) => panic!(error)
+ };
+
+ assert_eq!(attribute_header.attribute_type, 16);
+ assert_eq!(attribute_header.attribute_size, 96);
+ assert_eq!(attribute_header.resident_flag, 0);
+ assert_eq!(attribute_header.name_size, 0);
+ assert_eq!(attribute_header.name_offset, 0);
+ }
+
+ #[test]
+ fn attribute_test_01_nonresident() {
+ let raw: &[u8] = &[
+ 0x80,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x06,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xBF,0x1E,0x01,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0x11,0x00,0x00,0x00,0x00,
+ 0x33,0x20,0xC8,0x00,0x00,0x00,0x0C,0x32,0xA0,0x56,0xE3,0xE6,0x24,0x00,0xFF,0xFF
+ ];
+
+ let attribute_buffer = Cursor::new(raw);
+
+ let attribute_header = match AttributeHeader::new(attribute_buffer) {
+ Ok(attribute_header) => attribute_header,
+ Err(error) => panic!(error)
+ };
+
+ assert_eq!(attribute_header.attribute_type, 128);
+ assert_eq!(attribute_header.attribute_size, 80);
+ assert_eq!(attribute_header.resident_flag, 1);
+ assert_eq!(attribute_header.name_size, 0);
+ assert_eq!(attribute_header.name_offset, 64);
+ }
+}
diff --git a/src/entry.rs b/src/entry.rs
new file mode 100644
index 0000000..74f4a6c
--- /dev/null
+++ b/src/entry.rs
@@ -0,0 +1,219 @@
+use errors::{MftError};
+use attribute;
+use utils;
+use attr_x10;
+use attr_x30;
+use rwinstructs::reference::{MftReference};
+use rwinstructs::serialize::{serialize_u64};
+use byteorder::{ReadBytesExt, LittleEndian};
+use serde::{ser};
+use std::io::Cursor;
+use std::io::Read;
+use std::io::Seek;
+use std::io::SeekFrom;
+use std::mem;
+
+//https://github.com/libyal/libfsntfs/blob/master/documentation/New%20Technologies%20File%20System%20(NTFS).asciidoc#5-the-master-file-table-mft
+
+bitflags! {
+ pub flags EntryFlags: u16 {
+ const ALLOCATED = 0x01,
+ const INDEX_PRESENT = 0x02,
+ const UNKNOWN_1 = 0x04,
+ const UNKNOWN_2 = 0x08
+ }
+}
+pub fn serialize_entry_flags<S>(&item: &EntryFlags, serializer: S) -> Result<S::Ok, S::Error>
+ where S: ser::Serializer
+{
+ serializer.serialize_str(&format!("{:?}", item))
+}
+
+#[derive(Serialize, Debug)]
+pub struct EntryHeader{
+ pub signature: u32,
+ pub usa_offset: u16,
+ pub usa_size: u16,
+ #[serde(serialize_with = "serialize_u64")]
+ pub logfile_sequence_number: u64,
+ pub sequence: u16,
+ pub hard_link_count: u16,
+ pub fst_attr_offset: u16,
+ #[serde(serialize_with = "serialize_entry_flags")]
+ pub flags: EntryFlags,
+ pub entry_size_real: u32,
+ pub entry_size_allocated: u32,
+ pub base_reference: MftReference,
+ pub next_attribute_id: u16,
+ #[serde(skip_serializing)]
+ pub padding: Option<u16>,
+ pub record_number: Option<u32>,
+ pub update_sequence_value: u32
+}
+impl EntryHeader{
+ pub fn new<R: Read>(mut reader: R, entry: Option<u64>) -> Result<EntryHeader,MftError> {
+ let mut entry_header: EntryHeader = unsafe {
+ mem::zeroed()
+ };
+
+ entry_header.signature = reader.read_u32::<LittleEndian>()?;
+ entry_header.usa_offset = reader.read_u16::<LittleEndian>()?;
+ entry_header.usa_size = reader.read_u16::<LittleEndian>()?;
+ entry_header.logfile_sequence_number = reader.read_u64::<LittleEndian>()?;
+ entry_header.sequence = reader.read_u16::<LittleEndian>()?;
+ entry_header.hard_link_count = reader.read_u16::<LittleEndian>()?;
+ entry_header.fst_attr_offset = reader.read_u16::<LittleEndian>()?;
+ entry_header.flags = EntryFlags::from_bits_truncate(
+ reader.read_u16::<LittleEndian>()?
+ );
+ entry_header.entry_size_real = reader.read_u32::<LittleEndian>()?;
+ entry_header.entry_size_allocated = reader.read_u32::<LittleEndian>()?;
+ entry_header.base_reference = MftReference(reader.read_u64::<LittleEndian>()?);
+ entry_header.next_attribute_id = reader.read_u16::<LittleEndian>()?;
+
+ if entry_header.usa_offset == 48 {
+ entry_header.padding = Some(reader.read_u16::<LittleEndian>()?);
+ entry_header.record_number = Some(reader.read_u32::<LittleEndian>()?);
+ } else {
+ panic!(
+ "Unhandled update sequence array offset: {}",
+ entry_header.usa_offset
+ )
+ }
+
+ Ok(entry_header)
+ }
+}
+
+#[derive(Serialize, Debug)]
+pub struct MftEntry{
+ pub header: EntryHeader,
+ pub attr_standard_info: Option<Vec<attr_x10::StandardInformationAttribute>>,
+ pub attr_filename: Option<Vec<attr_x30::FileNameAttribute>>
+}
+impl MftEntry{
+ pub fn new(mut buffer: Vec<u8>, entry: Option<u64>) -> Result<MftEntry,MftError> {
+ let mut mft_entry: MftEntry = unsafe {
+ mem::zeroed()
+ };
+
+ // Get Header
+ mft_entry.header = EntryHeader::new(
+ buffer.as_slice(),
+ entry
+ )?;
+
+ // Fixup buffer
+ mft_entry.buffer_fixup(
+ &mut buffer
+ );
+
+ mft_entry.read_attributes(
+ Cursor::new(buffer.as_slice())
+ )?;
+
+ Ok(mft_entry)
+ }
+
+ pub fn buffer_fixup(&self, mut buffer: &mut[u8]){
+ // start offset (skip the first value (+2))
+ let so = (self.header.usa_offset + 2) as usize;
+ // array length
+ let al = (self.header.usa_size - 1 * 2) as usize;
+
+ let fixup_values = &buffer[
+ (self.header.usa_offset + 2) as usize..
+ ((self.header.usa_offset + 2)+((self.header.usa_size - 1) * 2)) as usize
+ ].to_vec();
+
+ for i in 0..(self.header.usa_size-1) {
+ let ofs = (i * 512) as usize;
+ *buffer.get_mut(ofs + 510).unwrap() = fixup_values[i as usize];
+ *buffer.get_mut(ofs + 511).unwrap() = fixup_values[(i+1) as usize];
+ }
+ }
+
+ fn read_attributes<Rs: Read+Seek>(&mut self, mut buffer: Rs) -> Result<u32,MftError>{
+ let mut current_offset = buffer.seek(
+ SeekFrom::Start(self.header.fst_attr_offset as u64)
+ )?;
+ let attr_count: u32 = 0;
+
+ while true {
+ let attribute_header = attribute::AttributeHeader::new(
+ &mut buffer
+ )?;
+
+ match attribute_header.attribute_type {
+ 0x10 => {
+ let si_attr = attr_x10::StandardInformationAttribute::new(&mut buffer)?;
+
+ if self.attr_standard_info.is_none(){
+ self.attr_standard_info = Some(Vec::new());
+ }
+
+ 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());
+ }
+
+ self.attr_filename.as_mut().unwrap().push(fn_attr);
+ },
+ 0xFFFFFFFF => {
+ // println!("END OF ATTRIBUTES");
+ break;
+ },
+ _ => {
+ // println!(
+ // "UNHANDLED ATTRIBUTE: 0x{:04X} at offset {}",
+ // attribute_header.attribute_type,
+ // current_offset
+ // );
+ }
+ }
+
+ current_offset = buffer.seek(
+ SeekFrom::Start(current_offset + attribute_header.attribute_size as u64)
+ )?;
+ }
+ Ok(attr_count)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::EntryHeader;
+
+ #[test]
+ fn mft_header_test_01() {
+ let header_buffer: &[u8] = &[
+ 0x46,0x49,0x4C,0x45,0x30,0x00,0x03,0x00,0xCC,0xB3,0x7D,0x84,0x0C,0x00,0x00,0x00,
+ 0x05,0x00,0x01,0x00,0x38,0x00,0x05,0x00,0x48,0x03,0x00,0x00,0x00,0x04,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xD5,0x95,0x00,0x00,
+ 0x53,0x57,0x81,0x37,0x00,0x00,0x00,0x00
+ ];
+
+ let entry_header = match EntryHeader::new(header_buffer,None) {
+ Ok(entry_header) => entry_header,
+ Err(error) => panic!(error)
+ };
+
+ assert_eq!(entry_header.signature, 1162627398);
+ assert_eq!(entry_header.usa_offset, 48);
+ assert_eq!(entry_header.usa_size, 3);
+ assert_eq!(entry_header.logfile_sequence_number, 53762438092);
+ assert_eq!(entry_header.sequence, 5);
+ assert_eq!(entry_header.hard_link_count, 1);
+ assert_eq!(entry_header.fst_attr_offset, 56);
+ assert_eq!(entry_header.flags.bits(), 5);
+ assert_eq!(entry_header.entry_size_real, 840);
+ assert_eq!(entry_header.entry_size_allocated, 1024);
+ assert_eq!(entry_header.base_reference.0, 0);
+ assert_eq!(entry_header.next_attribute_id, 6);
+ assert_eq!(entry_header.padding, Some(0));
+ assert_eq!(entry_header.record_number, Some(38357));
+ }
+}
diff --git a/src/errors.rs b/src/errors.rs
new file mode 100644
index 0000000..769fa94
--- /dev/null
+++ b/src/errors.rs
@@ -0,0 +1,53 @@
+use std::fmt;
+use std::fmt::Display;
+use std::io;
+
+#[derive(Debug)]
+pub enum ErrorKind {
+ IoError,
+ InvalidFileSignature,
+ Utf16Error
+}
+
+#[derive(Debug)]
+pub struct MftError {
+ /// Formated error message
+ pub message: String,
+ /// The type of error
+ pub kind: ErrorKind,
+ /// Any additional information passed along, such as the argument name that caused the error
+ pub info: Option<Vec<String>>,
+}
+
+impl MftError{
+ #[allow(dead_code)]
+ pub fn invalid_file_signature(err: String)->Self{
+ MftError {
+ message: format!("{}",err),
+ kind: ErrorKind::InvalidFileSignature,
+ info: Some(vec![]),
+ }
+ }
+ #[allow(dead_code)]
+ pub fn decode_error(err: String)->Self{
+ MftError {
+ message: format!("{}",err),
+ kind: ErrorKind::Utf16Error,
+ info: Some(vec![]),
+ }
+ }
+}
+
+impl From<io::Error> for MftError {
+ fn from(err: io::Error) -> Self {
+ MftError {
+ message: format!("{}",err),
+ kind: ErrorKind::IoError,
+ info: Some(vec![]),
+ }
+ }
+}
+
+impl Display for MftError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{}", self.message) }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..dafa329
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,16 @@
+#[macro_use] extern crate serde_derive;
+#[macro_use] extern crate serde_json;
+#[macro_use] extern crate bitflags;
+#[macro_use] extern crate log;
+extern crate seek_bufread;
+extern crate byteorder;
+extern crate rwinstructs;
+extern crate encoding;
+extern crate serde;
+pub mod errors;
+pub mod utils;
+pub mod mft;
+pub mod entry;
+pub mod attribute;
+pub mod attr_x10;
+pub mod attr_x30;
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..f5b3a19
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,87 @@
+#[macro_use] extern crate log;
+extern crate rustymft;
+extern crate rwinstructs;
+extern crate serde_json;
+extern crate serde;
+extern crate clap;
+use log::LogLevel::Debug;
+use clap::{App, Arg};
+use rustymft::mft::{MftHandler};
+use rwinstructs::reference;
+use rwinstructs::serialize;
+use serde::Serializer;
+use serde::ser::SerializeSeq;
+use std::fs;
+use std::io;
+
+fn process_directory<S>(directory: &str, serializer: S) where S: serde::Serializer {
+ let mut seq = serializer.serialize_seq(None).unwrap();
+ for entry in fs::read_dir(directory).unwrap() {
+ let entry = entry.unwrap();
+ let path = entry.path();
+ if path.is_file() {
+ let path_string = path.into_os_string().into_string().unwrap();
+ if path_string.ends_with("mft"){
+ process_file(&path_string,&mut seq);
+ }
+ }
+ }
+ seq.end().unwrap();
+}
+
+fn process_file<S: serde::ser::SerializeSeq>(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) => {
+ warn!("Could not parse file: {} [error: {}]", filename, error);
+ return false;
+ }
+ };
+
+ for i in 0 .. mft_handler.get_entry_count() {
+ let mft_entry = mft_handler.entry(i).unwrap();
+ serializer.serialize_element(&mft_entry).unwrap();
+ }
+
+ return true;
+}
+
+fn is_directory(source: &str)->bool{
+ fs::metadata(source).unwrap().file_type().is_dir()
+}
+
+fn main() {
+ let source_arg = Arg::with_name("source")
+ .short("s")
+ .long("source")
+ .value_name("FILE")
+ .help("The source path. Can be a file or a directory.")
+ .required(true)
+ .takes_value(true);
+
+ let options = App::new("RustyMft")
+ .version("0.0.0")
+ .author("Matthew Seyer <https://github.com/forensicmatt/RustyMft>")
+ .about("Parse $MFT.")
+ .arg(source_arg)
+ .get_matches();
+
+ // Set Reference Display Options
+ unsafe{reference::NESTED_REFERENCE = true;}
+ unsafe{serialize::U64_SERIALIZATION = serialize::U64Serialization::AsString;}
+
+ let source = options.value_of("source").unwrap();
+
+ let mut serializer = serde_json::Serializer::pretty(
+ io::stdout()
+ );
+
+ if is_directory(source) {
+ panic!("Directory source is not implemented yet.");
+ } else {
+ let mut seq = serializer.serialize_seq(None).unwrap();
+ process_file(source,&mut seq);
+ seq.end().unwrap();
+ }
+}
diff --git a/src/mft.rs b/src/mft.rs
new file mode 100644
index 0000000..6654f65
--- /dev/null
+++ b/src/mft.rs
@@ -0,0 +1,76 @@
+use seek_bufread::BufReader;
+use errors::{MftError};
+use entry::{MftEntry};
+use std::fs::File;
+use std::io::Read;
+use std::io::Seek;
+use std::io::SeekFrom;
+use std::mem;
+
+pub struct MftHandler {
+ filehandle: BufReader<File>,
+ _entry_size: u32,
+ _offset: u64,
+ _size: u64
+}
+impl MftHandler{
+ pub fn new(filename: &str) -> Result<MftHandler,MftError> {
+ let mut mft_handler: MftHandler = unsafe {
+ mem::zeroed()
+ };
+
+ let mut mft_fh = match File::open(filename) {
+ Ok(usn_fh) => usn_fh,
+ // Handle error here
+ Err(error) => panic!("Error: {}",error)
+ };
+
+ // get file size
+ mft_handler._size = match mft_fh.seek(SeekFrom::End(0)){
+ Err(e) => panic!("Error: {}",e),
+ Ok(size) => size
+ };
+
+ mft_handler.filehandle = BufReader::with_capacity(
+ 4096,
+ mft_fh
+ );
+
+ mft_handler.set_entry_size(1024);
+
+ Ok(mft_handler)
+ }
+
+ pub fn set_entry_size(&mut self, entry_size: u32){
+ self._entry_size = entry_size
+ }
+
+ pub fn get_entry_count(&self)->u64 {
+ self._size / self._entry_size as u64
+ }
+
+ pub fn entry(&mut self, entry: u64) -> Result<MftEntry,MftError> {
+ 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)?;
+
+ let mft_entry = self.entry_from_buffer(
+ entry_buffer,
+ entry
+ )?;
+
+ Ok(mft_entry)
+ }
+
+ pub fn entry_from_buffer(&mut self, mut buffer: Vec<u8>, entry: u64) -> Result<MftEntry,MftError> {
+ let mft_entry = MftEntry::new(
+ buffer,
+ Some(entry)
+ )?;
+
+ Ok(mft_entry)
+ }
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..7bda5c9
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,10 @@
+pub fn to_hex_string(bytes: &Vec<u8>) -> String {
+ let strs: Vec<String> = bytes.iter()
+ .map(|b| format!("{:02X}", b))
+ .collect();
+ strs.join("")
+}
+
+pub fn print_buffer_as_hex(buffer: &[u8]) {
+ println!("{}",to_hex_string(&buffer.to_vec()));
+}
diff --git a/testdata/attribute_nonresident_001 b/testdata/attribute_nonresident_001
new file mode 100644
index 0000000..313f32a
--- /dev/null
+++ b/testdata/attribute_nonresident_001
Binary files differ
diff --git a/testdata/attribute_nonresident_002 b/testdata/attribute_nonresident_002
new file mode 100644
index 0000000..e7765bc
--- /dev/null
+++ b/testdata/attribute_nonresident_002
Binary files differ
diff --git a/testdata/attribute_resident_001 b/testdata/attribute_resident_001
new file mode 100644
index 0000000..c1f73a8
--- /dev/null
+++ b/testdata/attribute_resident_001
Binary files differ
diff --git a/testdata/entry_long_name_and_res_ads_002 b/testdata/entry_long_name_and_res_ads_002
new file mode 100644
index 0000000..6402332
--- /dev/null
+++ b/testdata/entry_long_name_and_res_ads_002
Binary files differ
diff --git a/testdata/entry_super_long_name_001 b/testdata/entry_super_long_name_001
new file mode 100644
index 0000000..3c956ff
--- /dev/null
+++ b/testdata/entry_super_long_name_001
Binary files differ