Ejemplo n.º 1
0
        private static void Main(string[] args)
        {
            ExceptionlessClient.Default.Startup("88KHFwswzxfnYGejAlsVDao47ySGliI6vFbQPt9C");

            SetupNLog();

            _logger = LogManager.GetLogger("MFTECmd");

            _fluentCommandLineParser = new FluentCommandLineParser <ApplicationArguments>
            {
                IsCaseSensitive = false
            };

            _fluentCommandLineParser.Setup(arg => arg.File)
            .As('f')
            .WithDescription("File to process. Required\r\n");

            _fluentCommandLineParser.Setup(arg => arg.CsvDirectory)
            .As("csv")
            .WithDescription(
                "Directory to save CSV formatted results to. Be sure to include the full path in double quotes. Required unless --de or --body is specified\r\n");

            _fluentCommandLineParser.Setup(arg => arg.BodyDirectory)
            .As("body")
            .WithDescription(
                "Directory to save bodyfile formatted results to. Be sure to include the full path in double quotes. --bdl is also required when using this option");

            _fluentCommandLineParser.Setup(arg => arg.BodyDriveLetter)
            .As("bdl")
            .WithDescription(
                "Drive letter (C, D, etc.) to use with bodyfile formatted results. Only the drive letter itself should be provided\r\n");

            _fluentCommandLineParser.Setup(arg => arg.BaseName)
            .As("bn")
            .WithDescription(
                "Base name for file when exporting to CSV. If set, this will be the name of the file created in --csv directory\r\n");


            _fluentCommandLineParser.Setup(arg => arg.DumpDir)
            .As("dd")
            .WithDescription(
                "Directory to save exported FILE record. Be sure to include the full path in double quotes. --do is also required when using this option");

            _fluentCommandLineParser.Setup(arg => arg.DumpOffset)
            .As("do")
            .WithDescription(
                "Offset of the FILE record to dumpas decimal or hex. Example: 5120 or 0x1400 Use --de or --vl 1 to see offsets\r\n");



            _fluentCommandLineParser.Setup(arg => arg.DumpEntry)
            .As("de")
            .WithDescription(
                "Dump full details for the entry/sequence number provided. Format is 'Entry-Seq' as decimal or hex. Example: 624-5 or 0x270-0x5\r\n")
            .SetDefault(string.Empty);

            _fluentCommandLineParser.Setup(arg => arg.DateTimeFormat)
            .As("dt")
            .WithDescription(
                "The custom date/time format to use when displaying time stamps. Default is: yyyy-MM-dd HH:mm:ss.fffffff")
            .SetDefault("yyyy-MM-dd HH:mm:ss.fffffff");

            _fluentCommandLineParser.Setup(arg => arg.IncludeShortNames)
            .As("sn")
            .WithDescription(
                "Include DOS file name types. Default is false").SetDefault(false);

            _fluentCommandLineParser.Setup(arg => arg.Verbose)
            .As("vl")
            .WithDescription(
                "Verbose log messages. 1 == Debug, 2 == Trace").SetDefault(0);

            _fluentCommandLineParser.Setup(arg => arg.CsvSeparator)
            .As("cs")
            .WithDescription(
                "When true, use comma instead of tab for field separator. Default is true").SetDefault(true);

            _fluentCommandLineParser.Setup(arg => arg.AllTimeStampsAllTime)
            .As("at")
            .WithDescription(
                "When true, include all timestamps from 0x30 record vs only when they differ from 0x10 record. Default is false").SetDefault(false);

            var header =
                $"MFTECmd version {Assembly.GetExecutingAssembly().GetName().Version}" +
                "\r\n\r\nAuthor: Eric Zimmerman ([email protected])" +
                "\r\nhttps://github.com/EricZimmerman/MFTECmd";

            var footer = @"Examples: MFTECmd.exe -f ""C:\Temp\SomeMFT""" + "\r\n\t " +
                         @" MFTECmd.exe -f ""C:\Temp\SomeMFT"" --csv ""c:\temp\out"" --bn MyOutputFile.csv" + "\r\n\t " +
                         @" MFTECmd.exe -f ""C:\Temp\SomeMFT"" --csv ""c:\temp\out""" + "\r\n\t " +
                         @" MFTECmd.exe -f ""C:\Temp\SomeMFT"" --body ""c:\temp\bout"" --bdl c" + "\r\n\t " +
                         @" MFTECmd.exe -f ""C:\Temp\SomeMFT"" --de 5-5" + "\r\n\t " +
                         "\r\n\t" +
                         "  Short options (single letter) are prefixed with a single dash. Long commands are prefixed with two dashes\r\n";

            _fluentCommandLineParser.SetupHelp("?", "help")
            .WithHeader(header)
            .Callback(text => _logger.Info(text + "\r\n" + footer));

            var result = _fluentCommandLineParser.Parse(args);

            if (result.HelpCalled)
            {
                return;
            }

            if (result.HasErrors)
            {
                _logger.Error("");
                _logger.Error(result.ErrorText);

                _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options);

                return;
            }

            if (_fluentCommandLineParser.Object.File.IsNullOrEmpty())
            {
                _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options);

                _logger.Warn("-f is required. Exiting");
                return;
            }

            if (_fluentCommandLineParser.Object.File.IsNullOrEmpty() == false &&
                !File.Exists(_fluentCommandLineParser.Object.File))
            {
                _logger.Warn($"File '{_fluentCommandLineParser.Object.File}' not found. Exiting");
                return;
            }

            if (_fluentCommandLineParser.Object.CsvDirectory.IsNullOrEmpty() &&
                _fluentCommandLineParser.Object.DumpEntry.IsNullOrEmpty() &&
                _fluentCommandLineParser.Object.BodyDirectory.IsNullOrEmpty() &&
                _fluentCommandLineParser.Object.DumpDir.IsNullOrEmpty())
            {
                _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options);

                _logger.Warn("--csv, --body, --dd, or --de is required. Exiting");
                return;
            }

            if (_fluentCommandLineParser.Object.BodyDirectory.IsNullOrEmpty() == false &&
                _fluentCommandLineParser.Object.BodyDriveLetter.IsNullOrEmpty())
            {
                _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options);

                _logger.Warn("--bdl is required when using --body. Exiting");
                return;
            }

            if (_fluentCommandLineParser.Object.DumpDir.IsNullOrEmpty() == false &&
                _fluentCommandLineParser.Object.DumpOffset.IsNullOrEmpty())
            {
                _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options);

                _logger.Warn("--do is required when using --dd. Exiting");
                return;
            }

            if (_fluentCommandLineParser.Object.CsvSeparator == false)
            {
                exportExt = "tsv";
            }

            _logger.Info(header);
            _logger.Info("");
            _logger.Info($"Command line: {string.Join(" ", Environment.GetCommandLineArgs().Skip(1))}\r\n");

            if (IsAdministrator() == false)
            {
                _logger.Fatal("Warning: Administrator privileges not found!\r\n");
            }

            if (_fluentCommandLineParser.Object.Verbose >= 1)
            {
                LogManager.Configuration.LoggingRules.First().EnableLoggingForLevel(LogLevel.Debug);
            }

            if (_fluentCommandLineParser.Object.Verbose >= 2)
            {
                LogManager.Configuration.LoggingRules.First().EnableLoggingForLevel(LogLevel.Trace);
            }

            LogManager.ReconfigExistingLoggers();

            var sw = new Stopwatch();

            sw.Start();

            try
            {
                _mft = MftFile.Load(_fluentCommandLineParser.Object.File);
            }
            catch (Exception e)
            {
                _logger.Error($"There was an error loading the file! Error: {e.Message}");
                return;
            }

            sw.Stop();

            _logger.Info(
                $"\r\nProcessed '{_fluentCommandLineParser.Object.File}' in {sw.Elapsed.TotalSeconds:N4} seconds");

            StreamWriter swBody = null;
            StreamWriter swCsv  = null;

            if (_fluentCommandLineParser.Object.BodyDirectory.IsNullOrEmpty() == false)
            {
                _fluentCommandLineParser.Object.BodyDriveLetter =
                    _fluentCommandLineParser.Object.BodyDriveLetter.Substring(0, 1);

                if (Directory.Exists(_fluentCommandLineParser.Object.BodyDirectory) == false)
                {
                    _logger.Warn(
                        $"Path to '{_fluentCommandLineParser.Object.BodyDirectory}' doesn't exist. Creating...");
                    Directory.CreateDirectory(_fluentCommandLineParser.Object.BodyDirectory);
                }

                var outName = $"{DateTimeOffset.Now:yyyyMMddHHmmss}_MFTECmd_Output.body";
                var outFile = Path.Combine(_fluentCommandLineParser.Object.BodyDirectory, outName);

                _logger.Warn($"\r\nBodyfile output will be saved to '{outFile}'");

                try
                {
                    swBody = new StreamWriter(outFile, false, Encoding.UTF8, 4096 * 4);

                    _bodyWriter = new CsvWriter(swBody);
                    _bodyWriter.Configuration.Delimiter = "|";

                    var foo = _bodyWriter.Configuration.AutoMap <BodyFile>();
                    foo.Map(t => t.Md5).Index(0);
                    foo.Map(t => t.Name).Index(1);
                    foo.Map(t => t.Inode).Index(2);
                    foo.Map(t => t.Mode).Index(3);
                    foo.Map(t => t.Uid).Index(4);
                    foo.Map(t => t.Gid).Index(5);
                    foo.Map(t => t.Size).Index(6);
                    foo.Map(t => t.AccessTime).Index(7);
                    foo.Map(t => t.ModifiedTime).Index(8);
                    foo.Map(t => t.RecordModifiedTime).Index(9);
                    foo.Map(t => t.CreatedTime).Index(10);

                    _bodyWriter.Configuration.RegisterClassMap(foo);
                }
                catch (Exception e)
                {
                    _logger.Error(
                        $"\r\nError setting up bodyfile export. Please report to [email protected].\r\n\r\nError: {e.Message}");
                    _bodyWriter = null;
                }
            }

            if (_fluentCommandLineParser.Object.CsvDirectory.IsNullOrEmpty() == false)
            {
                if (Directory.Exists(_fluentCommandLineParser.Object.CsvDirectory) == false)
                {
                    _logger.Warn(
                        $"Path to '{_fluentCommandLineParser.Object.CsvDirectory}' doesn't exist. Creating...");
                    Directory.CreateDirectory(_fluentCommandLineParser.Object.CsvDirectory);
                }

                var outName = $"{DateTimeOffset.Now:yyyyMMddHHmmss}_MFTECmd_Output.{exportExt}";
                var outFile = Path.Combine(_fluentCommandLineParser.Object.CsvDirectory, outName);

                if (_fluentCommandLineParser.Object.BaseName.IsNullOrEmpty() == false)
                {
                    outFile = Path.Combine(_fluentCommandLineParser.Object.CsvDirectory, _fluentCommandLineParser.Object.BaseName);
                }

                _logger.Warn($"\r\nCSV output will be saved to '{outFile}'");

                try
                {
                    swCsv = new StreamWriter(outFile, false, Encoding.UTF8, 4096 * 4);

                    _csvWriter = new CsvWriter(swCsv);
                    if (_fluentCommandLineParser.Object.CsvSeparator == false)
                    {
                        _csvWriter.Configuration.Delimiter = "\t";
                    }

                    var foo = _csvWriter.Configuration.AutoMap <MFTRecordOut>();

                    foo.Map(t => t.EntryNumber).Index(0);
                    foo.Map(t => t.SequenceNumber).Index(1);
                    foo.Map(t => t.InUse).Index(2);
                    foo.Map(t => t.ParentEntryNumber).Index(3);
                    foo.Map(t => t.ParentSequenceNumber).Index(4);
                    foo.Map(t => t.ParentPath).Index(5);
                    foo.Map(t => t.FileName).Index(6);
                    foo.Map(t => t.Extension).Index(7);
                    foo.Map(t => t.FileSize).Index(8);
                    foo.Map(t => t.ReferenceCount).Index(9);
                    foo.Map(t => t.ReparseTarget).Index(10);

                    foo.Map(t => t.IsDirectory).Index(11);
                    foo.Map(t => t.HasAds).Index(12);
                    foo.Map(t => t.IsAds).Index(13);
                    foo.Map(t => t.Timestomped).Index(14).Name("SI<FN");
                    foo.Map(t => t.uSecZeros).Index(15);
                    foo.Map(t => t.Copied).Index(16);
                    foo.Map(t => t.SiFlags).ConvertUsing(t => t.SiFlags.ToString().Replace(", ", "|")).Index(17);
                    foo.Map(t => t.NameType).Index(18);

                    foo.Map(t => t.Created0x10).ConvertUsing(t =>
                                                             $"{t.Created0x10?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}").Index(19);
                    foo.Map(t => t.Created0x30).ConvertUsing(t =>
                                                             $"{t.Created0x30?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}").Index(20);

                    foo.Map(t => t.LastModified0x10).ConvertUsing(t =>
                                                                  $"{t.LastModified0x10?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}")
                    .Index(21);
                    foo.Map(t => t.LastModified0x30).ConvertUsing(t =>
                                                                  $"{t.LastModified0x30?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}")
                    .Index(22);

                    foo.Map(t => t.LastRecordChange0x10).ConvertUsing(t =>
                                                                      $"{t.LastRecordChange0x10?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}")
                    .Index(23);
                    foo.Map(t => t.LastRecordChange0x30).ConvertUsing(t =>
                                                                      $"{t.LastRecordChange0x30?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}")
                    .Index(24);

                    foo.Map(t => t.LastAccess0x10).ConvertUsing(t =>
                                                                $"{t.LastAccess0x10?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}").Index(25);

                    foo.Map(t => t.LastAccess0x30).ConvertUsing(t =>
                                                                $"{t.LastAccess0x30?.ToString(_fluentCommandLineParser.Object.DateTimeFormat)}").Index(26);

                    foo.Map(t => t.UpdateSequenceNumber).Index(27);
                    foo.Map(t => t.LogfileSequenceNumber).Index(28);
                    foo.Map(t => t.SecurityId).Index(29);

                    foo.Map(t => t.ObjectIdFileDroid).Index(30);
                    foo.Map(t => t.LoggedUtilStream).Index(31);
                    foo.Map(t => t.ZoneIdContents).Index(32);

                    foo.Map(t => t.FnAttributeId).Ignore();
                    foo.Map(t => t.OtherAttributeId).Ignore();

                    _csvWriter.Configuration.RegisterClassMap(foo);

                    _csvWriter.WriteHeader <MFTRecordOut>();
                    _csvWriter.NextRecord();
                }
                catch (Exception e)
                {
                    _logger.Error(
                        $"\r\nError setting up CSV export. Please report to [email protected].\r\n\r\nError: {e.Message}");
                    _csvWriter = null;
                }
            }

            if (swBody != null || swCsv != null)
            {
                try
                {
                    ProcessRecords(_mft.FileRecords);
                    ProcessRecords(_mft.FreeFileRecords);
                }
                catch (Exception ex)
                {
                    _logger.Error(
                        $"\r\nError exporting data. Please report to [email protected].\r\n\r\nError: {ex.Message}");
                }
            }

            swCsv?.Flush();
            swCsv?.Close();

            swBody?.Flush();
            swBody?.Close();



            #region ExportRecord

            if (_fluentCommandLineParser.Object.DumpDir.IsNullOrEmpty() == false)
            {
                _logger.Info("");

                bool offsetOk;
                long offset;

                if (_fluentCommandLineParser.Object.DumpOffset.StartsWith("0x"))
                {
                    offsetOk = long.TryParse(_fluentCommandLineParser.Object.DumpOffset.Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out offset);
                }
                else
                {
                    offsetOk = long.TryParse(_fluentCommandLineParser.Object.DumpOffset, out offset);
                }

                if (offsetOk)
                {
                    using (var b = new BinaryReader(File.OpenRead(_fluentCommandLineParser.Object.File)))
                    {
                        b.BaseStream.Seek(offset, 0);

                        var fileBytes = b.ReadBytes(1024);

                        var outFile = $"MFTECmd_FILE_Offset0x{offset:X}.bin";
                        var outFull = Path.Combine(_fluentCommandLineParser.Object.DumpDir, outFile);

                        File.WriteAllBytes(outFull, fileBytes);

                        _logger.Warn($"FILE record at offset 0x{offset:X} dumped to '{outFull}'\r\n");
                    }
                }
                else
                {
                    _logger.Warn(
                        $"Could not parse '{_fluentCommandLineParser.Object.DumpOffset}' to valid value. Exiting");
                    return;
                }
            }

            #endregion

            #region DumpEntry

            if (_fluentCommandLineParser.Object.DumpEntry.IsNullOrEmpty() == false)
            {
                _logger.Info("");

                FileRecord fr = null;

                var segs = _fluentCommandLineParser.Object.DumpEntry.Split('-');

                if (segs.Length != 2)
                {
                    _logger.Warn(
                        $"Could not parse '{_fluentCommandLineParser.Object.DumpEntry}' to valid values. Format is Entry#-Sequence# in either decimal or hex format. Exiting");
                    return;
                }

                bool entryOk;
                bool seqOk;
                int  entry;
                int  seq;

                if (_fluentCommandLineParser.Object.DumpEntry.StartsWith("0x"))
                {
                    var seg0 = segs[0].Replace("0x", "");
                    var seg1 = segs[1].Replace("0x", "");

                    entryOk = int.TryParse(seg0, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out entry);
                    seqOk   = int.TryParse(seg1, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out seq);
                }
                else
                {
                    entryOk = int.TryParse(segs[0], out entry);
                    seqOk   = int.TryParse(segs[1], out seq);
                }

                if (entryOk == false || seqOk == false)
                {
                    _logger.Warn(
                        $"Could not parse '{_fluentCommandLineParser.Object.DumpEntry}' to valid values. Exiting");
                    return;
                }

                var key = $"{entry:X8}-{seq:X8}";

                if (_mft.FileRecords.ContainsKey(key))
                {
                    fr = _mft.FileRecords[key];
                }
                else if (_mft.FreeFileRecords.ContainsKey(key))
                {
                    fr = _mft.FreeFileRecords[key];
                }

                if (fr == null)
                {
                    _logger.Warn(
                        $"Could not find file record with entry/seq '{_fluentCommandLineParser.Object.DumpEntry}'. Exiting");
                    return;
                }

                _logger.Warn($"Dumping details for file record with key '{key}'\r\n");

                _logger.Info(fr);
            }

            #endregion
        }
Ejemplo n.º 2
0
        public static Mft ReadMft(BinaryReader r)
        {
            var mft = new Mft();

            mft.header = r.ReadBytes(40);

            //check version and header size
            if (BitConverter.ToUInt32(mft.header, 0) != 441336215U || BitConverter.ToInt32(mft.header, 4) != 40)
            {
                throw new IOException("Unknown header");
            }

            r.BaseStream.Position = mft.MftOffset;

            if (r.ReadUInt32() != 443835981U)
            {
                throw new IOException("Unknown MFT header");
            }
            r.BaseStream.Position += 8;     //junk

            var count = r.ReadUInt32() - 1; //this header was counted as an entry

            r.BaseStream.Position += 8;     //0

            var entries = mft.entries = new MftEntry[count];

            for (var i = 0; i < count; i++)
            {
                entries[i] = MftEntry.Read(r);
            }

            var e = entries[1];

            if (e.compression != 0)
            {
                throw new IOException("File is compressed");
            }

            var size = e.size;

            r.BaseStream.Position = e.offset;

            while (size > 0)
            {
                var id = r.ReadInt32();
                var i  = r.ReadInt32();

                if (i > 0)
                {
                    var entry = entries[i - 1];
                    if (entry.baseId == 0)
                    {
                        entry.baseId = id;
                        entry.fileId = id;
                    }
                    else if (id < entry.baseId)
                    {
                        entry.baseId = id;
                    }
                    else if (id > entry.fileId)
                    {
                        entry.fileId = id;
                    }
                }

                size -= 8;
            }

            return(mft);
        }