public void CidSizeTest()
        {
            var r = new CIDSizeMRU();

            var reg = new RegistryHive(@"..\..\Hives\ntuser1.dat");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\CIDSizeMRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(8);

            var ff = (CIDSizeInfo) r.Values[0];

            Check.That(ff.MRUPosition).IsEqualTo(7);
            Check.That(ff.Executable).Contains("CCleaner");
        }
        public void AppCompatTest()
        {
            var r = new AppCompat();

            var reg = new RegistryHive(@"D:\Dropbox\RegistryHives\SYSTEM");
            reg.ParseHive();

            var key = reg.GetKey(@"ControlSet001\Control\Session Manager\AppCompatCache");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(1024);

            var ff = (ValuesOut) r.Values[0];

            Check.That(ff.CacheEntryPosition).IsEqualTo(0);
            Check.That(ff.ProgramName).Contains("java");
        }
Beispiel #3
0
        public Amcache(string hive, bool recoverDeleted)
        {
            _logger = LogManager.GetCurrentClassLogger();

            var reg = new RegistryHive(hive)
            {
                RecoverDeleted = recoverDeleted
            };
            reg.ParseHive();

            var fileKey = reg.GetKey(@"Root\File");
            var programsKey = reg.GetKey(@"Root\Programs");

            UnassociatedFileEntries = new List<FileEntry>();
            ProgramsEntries = new List<ProgramsEntry>();

            if (fileKey == null || programsKey == null)
            {
                _logger.Error($"Hive does not contain a File and/or Programs key. Processing cannot continue");
                return;
            }

            //First, we get data for all the Program entries under Programs key

            foreach (var registryKey in programsKey.SubKeys)
            {
                var ProgramName0 = "";
                var ProgramVersion1 = "";
                var Guid10 = "";
                var UninstallGuid11 = "";
                var Guid12 = "";
                var Dword13 = 0;
                var Dword14 = 0;
                var Dword15 = 0;
                var UnknownBytes = new byte[0];
                long Qword17 = 0;
                var Dword18 = 0;
                var VenderName2 = "";
                var LocaleID3 = "";
                var Dword5 = 0;
                var InstallSource6 = "";
                var UninstallKey7 = "";
                DateTimeOffset? EpochA = null;
                DateTimeOffset? EpochB = null;
                var PathListd = "";
                var Guidf = "";
                var RawFiles = "";

                try
                {
                    foreach (var value in registryKey.Values)
                    {
                        switch (value.ValueName)
                        {
                            case "0":
                                ProgramName0 = value.ValueData;
                                break;
                            case "1":
                                ProgramVersion1 = value.ValueData;
                                break;
                            case "2":
                                VenderName2 = value.ValueData;
                                break;
                            case "3":
                                LocaleID3 = value.ValueData;
                                break;
                            case "5":
                                Dword5 = int.Parse(value.ValueData);
                                break;
                            case "6":
                                InstallSource6 = value.ValueData;
                                break;
                            case "7":
                                UninstallKey7 = value.ValueData;
                                break;
                            case "a":
                                try
                                {
                                    var seca = long.Parse(value.ValueData);
                                    if (seca > 0)
                                    {
                                        EpochA = DateTimeOffset.FromUnixTimeSeconds(seca).ToUniversalTime();
                                    }
                                }
                                catch (Exception ex)
                                {
                                    //sometimes the number is way too big
                                }

                                break;
                            case "b":
                                var seconds = long.Parse(value.ValueData);
                                if (seconds > 0)
                                {
                                    EpochB =
                                        DateTimeOffset.FromUnixTimeSeconds(seconds).ToUniversalTime();
                                }

                                break;
                            case "d":
                                PathListd = value.ValueData;
                                break;
                            case "f":
                                Guidf = value.ValueData;
                                break;
                            case "10":
                                Guid10 = value.ValueData;
                                break;
                            case "11":
                                UninstallGuid11 = value.ValueData;
                                break;
                            case "12":
                                Guid12 = value.ValueData;
                                break;
                            case "13":
                                Dword13 = int.Parse(value.ValueData);
                                break;
                            case "14":
                                Dword13 = int.Parse(value.ValueData);
                                break;
                            case "15":
                                Dword13 = int.Parse(value.ValueData);
                                break;
                            case "16":
                                UnknownBytes = value.ValueDataRaw;
                                break;
                            case "17":
                                Qword17 = long.Parse(value.ValueData);
                                break;
                            case "18":
                                Dword18 = int.Parse(value.ValueData);
                                break;
                            case "Files":
                                RawFiles = value.ValueData;
                                break;
                            default:
                                _logger.Warn(
                                    $"Unknown value name in Program at path {registryKey.KeyPath}: {value.ValueName}");
                                break;
                        }
                    }

                    var pe = new ProgramsEntry(ProgramName0, ProgramVersion1, VenderName2, LocaleID3, InstallSource6,
                        UninstallKey7, Guid10, Guid12, UninstallGuid11, Dword5, Dword13, Dword14, Dword15, UnknownBytes,
                        Qword17, Dword18, EpochA, EpochB, PathListd, Guidf, RawFiles, registryKey.KeyName,
                        registryKey.LastWriteTime.Value);

                    ProgramsEntries.Add(pe);
                }
                catch (Exception ex)
                {
                    _logger.Error($"Error parsing ProgramsEntry at {registryKey.KeyPath}. Error: {ex.Message}");
                    _logger.Error(
                        $"Please send the following text to [email protected]. \r\n\r\nKey data: {registryKey}");
                }
            }

            //For each Programs entry, add the related Files entries from Files\Volume subkey, put the rest in unassociated

            foreach (var registryKey in fileKey.SubKeys)
            {
                //These are the guids for volumes
                foreach (var subKey in registryKey.SubKeys)
                {
                    var prodName = "";
                    int? langId = null;
                    var fileVerString = "";
                    var fileVerNum = "";
                    var fileDesc = "";
                    var compName = "";
                    var fullPath = "";
                    var switchBack = "";
                    var peHash = "";
                    var progID = "";
                    var sha = "";

                    long unknown1 = 0;
                    long unknown2 = 0;
                    var unknown3 = 0;
                    var unknown4 = 0;
                    var unknown5 = 0;
                    var unknown6 = 0;
                    int? fileSize = null;
                    int? peHeaderSize = null;
                    int? peHeaderChecksum = null;

                    DateTimeOffset? created = null;
                    DateTimeOffset? lm = null;
                    DateTimeOffset? lm2 = null;
                    DateTimeOffset? compTime = null;

                    var hasLinkedProgram = false;

                    try
                    {
                        //these are the files executed from the volume
                        foreach (var keyValue in subKey.Values)
                        {
                            var keyVal = int.Parse(keyValue.ValueName, NumberStyles.HexNumber);

                            switch (keyVal)
                            {
                                case ProductName:
                                    prodName = keyValue.ValueData;
                                    break;
                                case CompanyName:
                                    compName = keyValue.ValueData;
                                    break;
                                case FileVersionNumber:
                                    fileVerNum = keyValue.ValueData;
                                    break;
                                case LanguageCode:
                                    langId = int.Parse(keyValue.ValueData);
                                    break;
                                case SwitchBackContext:
                                    switchBack = keyValue.ValueData;
                                    break;
                                case FileVersionString:
                                    fileVerString = keyValue.ValueData;
                                    break;
                                case FileSize:
                                    fileSize = int.Parse(keyValue.ValueData);
                                    break;
                                case PEHeaderSize:
                                    peHeaderSize = int.Parse(keyValue.ValueData);
                                    break;
                                case PEHeaderHash:
                                    peHash = keyValue.ValueData;
                                    break;
                                case PEHeaderChecksum:
                                    peHeaderChecksum = int.Parse(keyValue.ValueData);
                                    break;
                                case Unknown1:
                                    unknown1 = long.Parse(keyValue.ValueData);
                                    break;
                                case Unknown2:
                                    unknown2 = long.Parse(keyValue.ValueData);
                                    break;
                                case FileDescription:
                                    fileDesc = keyValue.ValueData;
                                    break;
                                case Unknown3:
                                    unknown3 = int.Parse(keyValue.ValueData);
                                    break;
                                case CompileTime:
                                    compTime =
                                        DateTimeOffset.FromUnixTimeSeconds(long.Parse(keyValue.ValueData))
                                            .ToUniversalTime();
                                    break;
                                case Unknown4:
                                    unknown4 = int.Parse(keyValue.ValueData);
                                    break;
                                case LastModified:
                                    lm = DateTimeOffset.FromFileTime(long.Parse(keyValue.ValueData)).ToUniversalTime();
                                    break;
                                case Created:
                                    created =
                                        DateTimeOffset.FromFileTime(long.Parse(keyValue.ValueData)).ToUniversalTime();
                                    break;
                                case FullPath:
                                    fullPath = keyValue.ValueData;
                                    break;
                                case Unknown5:
                                    unknown5 = int.Parse(keyValue.ValueData);
                                    break;
                                case Unknown6:
                                    unknown6 = int.Parse(keyValue.ValueData);
                                    break;
                                case LastModified2:
                                    lm2 = DateTimeOffset.FromFileTime(long.Parse(keyValue.ValueData)).ToUniversalTime();
                                    break;
                                case ProgramID:
                                    progID = keyValue.ValueData;

                                    var program = ProgramsEntries.SingleOrDefault(t => t.ProgramID == progID);
                                    if (program != null)
                                    {
                                        hasLinkedProgram = true;
                                    }

                                    break;
                                case SHA1:
                                    sha = keyValue.ValueData;
                                    break;
                                default:
                                    _logger.Warn(
                                        $"Unknown value name when processing FileEntry at path '{subKey.KeyPath}': 0x{keyVal:X}");
                                    break;
                            }
                        }

                        if (fullPath.Length == 0)
                        {
                            continue;
                        }

                        TotalFileEntries += 1;

                        var fe = new FileEntry(prodName, progID, sha, fullPath, lm2, registryKey.KeyName,
                            registryKey.LastWriteTime.Value, subKey.KeyName, subKey.LastWriteTime.Value,
                            unknown5, compName, langId, fileVerString, peHash, fileVerNum, fileDesc, unknown1, unknown2,
                            unknown3, unknown4, switchBack, fileSize, compTime, peHeaderSize,
                            lm, created, peHeaderChecksum, unknown6, subKey.KeyName);

                        if (hasLinkedProgram)
                        {
                            var program = ProgramsEntries.SingleOrDefault(t => t.ProgramID == fe.ProgramID);
                            fe.ProgramName = program.ProgramName_0;
                            program.FileEntries.Add(fe);
                        }
                        else
                        {
                            fe.ProgramName = "Unassociated";
                            UnassociatedFileEntries.Add(fe);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"Error parsing FileEntry at {subKey.KeyPath}. Error: {ex.Message}");
                        _logger.Error(
                            $"Please send the following text to [email protected]. \r\n\r\nKey data: {subKey}");
                    }
                }
            }
        }
        public void SamPluginShouldFindEricAccount()
        {
            var r = new UserAccounts();

            var reg = new RegistryHive(@"..\..\Hives\SAM");
            reg.ParseHive();

            var key = reg.GetKey(@"SAM\Domains\Account\Users");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsGreaterThan(0);
            Check.That(r.Values.Count).IsEqualTo(4);

            var users = (BindingList<UserOut>) r.Values;

            var user = users.Single(t => t.UserId == 1000);

            Check.That(user.UserName.ToLowerInvariant()).IsEqualTo("eric");

            var lastLogin = DateTimeOffset.Parse("3/25/2015 2:31:24 PM +00:00");

            Check.That(user.LastLoginTime?.ToString("G")).Equals(lastLogin.ToString("G"));

            Check.That(user.LastLoginTime?.Year).Equals(lastLogin.Year);
            Check.That(user.LastLoginTime?.Month).Equals(lastLogin.Month);
            Check.That(user.LastLoginTime?.Day).Equals(lastLogin.Day);
            Check.That(user.LastLoginTime?.Hour).Equals(lastLogin.Hour);
            Check.That(user.LastLoginTime?.Minute).Equals(lastLogin.Minute);
            Check.That(user.LastLoginTime?.Second).Equals(lastLogin.Second);

            var lastPwChange = DateTimeOffset.Parse("3/23/2015 9:15:55 PM +00:00");

            Check.That(user.LastPasswordChange?.ToString("G")).Equals(lastPwChange.ToString("G"));

            Check.That(user.LastIncorrectPassword).IsNull();
            Check.That(user.ExpiresOn).IsNull();

            Check.That(user.TotalLoginCount).IsEqualTo(3);
            Check.That(user.InvalidLoginCount).IsEqualTo(0);

            user = users.Single(t => t.UserId == 500);

            Check.That(user.UserName.ToLowerInvariant()).IsEqualTo("administrator");
        }
        public void RunMRUTest()
        {
            var r = new RunMRU();
            var reg = new RegistryHive(@"d:\Dropbox\RegistryHives\ALL\NTUSER (8).DAT");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(4);
        }
        public void RunUserAssistTest()
        {
            var r = new UserAssist();
            var reg = new RegistryHive(@"C:\Users\e\Desktop\te\NTUSER.DAT");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\Count");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(4);
        }
        public void OpenSaveMRUTest()
        {
            var r = new OpenSaveMRU();
            var reg = new RegistryHive(@"D:\Dropbox\RegistryHives\NTUSER_Loveall.DAT");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSaveMRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(4);
        }
        public void OpenSavePidlMruTest()
        {
            var r = new OpenSavePidlMRU();
            var reg = new RegistryHive(@"..\..\Hives\ntuser1.dat");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(16);
        }
        public void OfficeMRU()
        {
            var r = new OfficeMRU();

            var reg = new RegistryHive(@"D:\Dropbox\RegistryHives\NTUSER.DAT");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Office\15.0\Word\User MRU\AD_E4AD528701B6F14FEA4579C55D1CE68A7BC6B175E25472D710E2C350F620B4DE\File MRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(37);

            var ff = (RegistryPlugin.OfficeMRU.ValuesOut)r.Values[0];

            Check.That(ff.FileName).IsEqualTo(@"C:\ProjectWorkingFolder\ShellBagsExplorer\ShellBagsExplorerManual.docx");
            Check.That(ff.ValueName).Contains("Item 1");
        }
        public void MountedDevicesTest()
        {
            var r = new MountedDevices();

            var reg = new RegistryHive(@"D:\Dropbox\RegistryHives\SYSTEM");
            reg.ParseHive();

            var key = reg.GetKey(@"MountedDevices");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(65);
        }
        public void LastVisitedMRUTest()
        {
            var r = new LastVisitedMRU();
            var reg = new RegistryHive(@"d:\Dropbox\RegistryHives\ntuserNokiaShellBags.dat");
            reg.ParseHive();

            var key = reg.GetKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedMRU");

            Check.That(r.Values.Count).IsEqualTo(0);

            r.ProcessValues(key);

            Check.That(r.Values.Count).IsEqualTo(11);
        }
Beispiel #12
0
        private static void Main(string[] args)
        {
            //TODO Live Registry support

            var dumpWarning = false;

            var nlogPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "nlog.config");

            if (File.Exists(nlogPath) == false)
            {
                var config = new LoggingConfiguration();
                var loglevel = LogLevel.Info;

                var layout = @"${message}";

                var consoleTarget = new ColoredConsoleTarget();

                config.AddTarget("console", consoleTarget);

                consoleTarget.Layout = layout;

                var rule1 = new LoggingRule("*", loglevel, consoleTarget);
                config.LoggingRules.Add(rule1);

                LogManager.Configuration = config;
                dumpWarning = true;
            }

            _logger = LogManager.GetCurrentClassLogger();

            if (dumpWarning)
            {
                _logger.Warn("Nlog.config missing! Using default values...");
            }

            if (!CheckForDotnet46())
            {
                _logger.Warn(".net 4.6 not detected. Please install .net 4.6 and try again.");
                return;
            }

            var p = new FluentCommandLineParser<ApplicationArguments>
            {
                IsCaseSensitive = false
            };

//            p.Setup(arg => arg.HiveFile)
//                .As("Hive")
//                .WithDescription("\tHive to search. --Hive or --Dir is required.");

            p.Setup(arg => arg.Directory)
                .As("Dir")
                .WithDescription("\tDirectory to look for hives (recursively)");

//            p.Setup(arg => arg.Literal)
//                .As("Literal")
//                .WithDescription("\tIf present, --sd and --ss search value will not be interpreted as ASCII or Unicode byte strings");

//            p.Setup(arg => arg.RecoverDeleted)
//                .As("Recover")
//                .WithDescription("\tIf present, recover deleted keys/values");

//            p.Setup(arg => arg.DumpKey)
//                .As("DumpKey")
//                .WithDescription("\tDump given key (and all subkeys) and values as json");

//            p.Setup(arg => arg.DumpDir)
//                .As("DumpDir")
//                .WithDescription("\tDirectory to save json output");

//            p.Setup(arg => arg.Recursive)
//                .As("Recursive")
//                .WithDescription("Dump keys/values recursively (ignored if --ValueName used). This option provides FULL details about keys and values");

//            p.Setup(arg => arg.RegEx)
//                .As("RegEx")
//                .WithDescription("\tIf present, treat <string> in --sk, --sv, --sd, and --ss as a regular expression")
//                .SetDefault(false);

//            p.Setup(arg => arg.Sort)
//                .As("Sort")
//                .WithDescription("\tIf present, sort the output").SetDefault(false);

//            p.Setup(arg => arg.SuppressData)
//                .As("SuppressData")
//                .WithDescription("If present, do not show data when using --sd or --ss\r\n").SetDefault(false);

//            p.Setup(arg => arg.KeyName)
//                .As("KeyName")
//                .WithDescription("\tKey name. All values under this key will be dumped");

            // added option by CDI -- begin
            p.Setup(arg => arg.SaveTo)
                .As("SaveTo")
                .WithDescription("\tParsed data will be written to AutoRunParser_Output.csv in this directory");

            p.Setup(arg => arg.NoHeader)
                .As("NoHeader")
                .WithDescription("\tOutput to CSV without header");
            // added option by CDI -- end

//            p.Setup(arg => arg.ValueName)
//                .As("ValueName")
//                .WithDescription("Value name. Only this value will be dumped");

//            p.Setup(arg => arg.SaveToName)
//                .As("SaveToName")
//                .WithDescription("Saves ValueName value data in binary form to file\r\n");

//            p.Setup(arg => arg.StartDate)
//                .As("StartDate")
//                .WithDescription("Start date to look for last write timestamps (UTC). If EndDate is not supplied, last writes AFTER this date will be returned");

//            p.Setup(arg => arg.EndDate)
//                .As("EndDate")
//                .WithDescription("\tEnd date to look for last write timestamps (UTC). If StartDate is not supplied, last writes BEFORE this date will be returned");

//            p.Setup(arg => arg.MinimumSize)
//                .As("MinSize")
//                .WithDescription("\tFind values with data size >= MinSize (specified in bytes)");

//            p.Setup(arg => arg.SimpleSearchKey)
//                .As("sk")
//                .WithDescription("\tSearch for <string> in key names.");

//            p.Setup(arg => arg.SimpleSearchValue)
//                .As("sv")
//                .WithDescription("\tSearch for <string> in value names");

//            p.Setup(arg => arg.SimpleSearchValueData)
//                .As("sd")
//                .WithDescription("\tSearch for <string> in value record's value data");

//            p.Setup(arg => arg.SimpleSearchValueSlack)
//                .As("ss")
//                .WithDescription("\tSearch for <string> in value record's value slack");

            var header =
                $"AutoRunParser version {Assembly.GetExecutingAssembly().GetName().Version}" +
                $" modified by CDI, Inc." +
                "\r\n(Original Author: Eric Zimmerman)" +
                "\r\n\r\nNote: Enclose all strings containing spaces (and all RegEx) with double quotes";

            var footer = @"Example: AutoRunParser.exe --Dir C:\data\ --SaveTo C:\output\";

//            var footer = @"Example: RECmd.exe --Hive ""C:\Temp\UsrClass 1.dat"" --sk URL --Recover" + "\r\n\t " +
//                         @"RECmd.exe --Hive ""D:\temp\UsrClass 1.dat"" --StartDate ""11/13/2014 15:35:01"" " + "\r\n\t " +
//                         @"RECmd.exe --Hive ""D:\temp\UsrClass 1.dat"" --RegEx --sv ""(App|Display)Name"" " + "\r\n\t " +
//                         @"RECmd.exe --Hive ""D:\temp\UsrClass 1.dat"" --StartDate ""05/20/2014 19:00:00"" --EndDate ""05/20/2014 23:59:59"" " + "\r\n\t " +
//                         @"RECmd.exe --Hive ""D:\temp\UsrClass 1.dat"" --StartDate ""05/20/2014 07:00:00 AM"" --EndDate ""05/20/2014 07:59:59 PM"" ";

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

            var result = p.Parse(args);

            if (result.HelpCalled)
            {
                return;
            }

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

                if (result.ErrorText.Contains("--dir"))
                {
                    _logger.Error("Remove the trailing backslash from the --dir argument and try again");
                }              
 
                p.HelpOption.ShowHelp(p.Options);

                return;
            }

            var hivesToProcess = new List<string>();
            var systemHives = new List<string>();
            var softwareHives = new List<string>();
            var ntuserHives = new List<string>();

            if (p.Object.HiveFile?.Length > 0)
            {
                hivesToProcess.Add(p.Object.HiveFile);
            }
            else if (p.Object.Directory?.Length > 0)
            {
                if (Directory.Exists(p.Object.Directory) == false)
                {
                    _logger.Error($"Directory '{p.Object.Directory}' does not exist.");
                    return;
                }

//                var files = Directory.GetFiles(p.Object.Directory, "*", SearchOption.AllDirectories);

                foreach (string fileName in Directory.GetFiles(p.Object.Directory, "*", SearchOption.AllDirectories))
                {
                    Stream st = File.OpenRead(fileName);
                    if (st.Length < 4)
                        continue;

                    BinaryReader br = new BinaryReader(st);
                    if (br.ReadInt32() != 1718052210) // means not "regf"
                        continue;

                    if (Path.GetFileName(fileName).ToUpper().Contains("SYSTEM"))
                    {
                        systemHives.Add(fileName);
                        hivesToProcess.Add(fileName);
                    }
                    else if (Path.GetFileName(fileName).ToUpper().Contains("SOFTWARE"))
                    {
                        softwareHives.Add(fileName);
                        hivesToProcess.Add(fileName);
                    }
                    else if (Path.GetFileName(fileName).ToUpper().Contains("NTUSER.DAT"))
                    {
                        ntuserHives.Add(fileName);
                        hivesToProcess.Add(fileName);
                    }
                }
            }
            else
            {
                p.HelpOption.ShowHelp(p.Options);
                return;
            }

            _logger.Info(header);
            _logger.Info("");

            if (hivesToProcess.Count == 0)
            {
                _logger.Warn("No hives were found. Exiting...");

                return;
            }

            // added by CDI for automation
            if (p.Object.Directory.Length > 0 && p.Object.SaveTo.Length > 0)
            {
                if (Directory.Exists(p.Object.SaveTo) == false)
                {
                    try
                    {
                        Directory.CreateDirectory(p.Object.SaveTo);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"Error creating SaveTo '{p.Object.SaveTo}': {ex.Message}. Exiting");
                        return;
                    }
                }

                var outFileBase = string.Empty;
                outFileBase = $"AutoRunParser_Output.csv";
                var outFileName = Path.Combine(p.Object.SaveTo, outFileBase);

                var sw = new StreamWriter(outFileName, true, System.Text.Encoding.Unicode);
                sw.AutoFlush = true;
                sw.WriteLine("FilePath\tKey\tName\tValue\tLastWrittenTimeLocal\tLastWrittenTimeUTC");

                foreach (var systemHive in systemHives)
                {

                    var reg = new RegistryHive(systemHive)
                    {
                        RecoverDeleted = p.Object.RecoverDeleted
                    };

                    reg.ParseHive();
//                    var hive = new RegistryHiveOnDemand(systemHive);
                    var subKey = reg.GetKey("Select");
                    if (subKey == null)
                    {
                        _logger.Warn($"no SYSTEM hive: {systemHive}");
                        continue;
                    }
                    var currentCtlSet = int.Parse(subKey.Values.Single(c => c.ValueName == "Current").ValueData);

                    StreamReader cReader = new StreamReader(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + @"\system.txt", System.Text.Encoding.Default);

                    // 1行ごとに処理
                    while (cReader.Peek() >= 0)
                    {
                        string keyName = cReader.ReadLine();
                        var key = reg.GetKey($@"ControlSet00{currentCtlSet}\{keyName}");

                        if (key == null)
                        {
                            _logger.Warn($"Key not found: {keyName}");
                            continue;
                        }
                        WriteSpecificKeyInfo(key, sw, systemHive);
                    }
                    cReader.Close();
                }

                foreach (var softwareHive in softwareHives)
                {
                    var reg = new RegistryHive(softwareHive)
                    {
                        RecoverDeleted = p.Object.RecoverDeleted
                    };
                    reg.ParseHive();

                    StreamReader cReader = new StreamReader(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + @"\software.txt", System.Text.Encoding.Default);

                    // 1行ごとに処理
                    while (cReader.Peek() >= 0)
                    {
                        string keyName = cReader.ReadLine();
                        var key = reg.GetKey(keyName);

                        if (key == null)
                        {
                            _logger.Warn($"Key not found: {keyName}");
                            continue;
                        }
                        WriteSpecificKeyInfo(key, sw, softwareHive);
                    }
                    cReader.Close();

                }

                foreach (var ntuserHive in ntuserHives)
                {
                    var reg = new RegistryHive(ntuserHive)
                    {
                        RecoverDeleted = p.Object.RecoverDeleted
                    };
                    reg.ParseHive();

                    StreamReader cReader = new StreamReader(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + @"\ntuser.txt", System.Text.Encoding.Default);

                    // 1行ごとに処理
                    while (cReader.Peek() >= 0)
                    {
                        string keyName = cReader.ReadLine();
                        var key = reg.GetKey(keyName);

                        if (key == null)
                        {
                            _logger.Warn($"Key not found: {keyName}");
                            continue;
                        }
                        WriteSpecificKeyInfo(key, sw, ntuserHive);
                    }
                    cReader.Close();

                }

                _logger.Warn($"Saved to '{outFileName}'");
                sw.Close();
                return;
            }

            var totalHits = 0;
            var hivesWithHits = 0;
            double totalSeconds = 0;

            foreach (var hiveToProcess in hivesToProcess)
            {

                _logger.Info("");
                _logger.Info($"Processing hive '{hiveToProcess}'");
                _logger.Info("");

                if (File.Exists(hiveToProcess) == false)
                {
                    _logger.Warn($"'{hiveToProcess}' does not exist. Skipping");
                    continue;
                }

                try
                {
                    var reg = new RegistryHive(hiveToProcess)
                    {
                        RecoverDeleted = p.Object.RecoverDeleted
                    };

                    _sw = new Stopwatch();
                    _sw.Start();

                    reg.ParseHive();

                    _logger.Info("");

                    if (p.Object.DumpKey.Length > 0 && p.Object.DumpDir.Length > 0)
                    {
                        if (Directory.Exists(p.Object.DumpDir) == false)
                        {
                            try
                            {
                                Directory.CreateDirectory(p.Object.DumpDir);
                            }
                            catch (Exception ex)
                            {
                                _logger.Error($"Error creating DumpDir '{p.Object.DumpDir}': {ex.Message}. Exiting");
                                return;
                            }
                        }

                        var key = reg.GetKey(p.Object.DumpKey);

                        if (key == null)
                        {
                            _logger.Warn($"Key not found: {p.Object.DumpKey}. Exiting");
                            return;
                        }
                        
                        var nout = $"{key.KeyName}_dump.json";
                        var fout = Path.Combine(p.Object.DumpDir, nout);

                        _logger.Info("Found key. Dumping data. Be patient as this can take a while...");

                        var jsons = new JsonSerializer<RegistryKey>();

                        //TODO need a way to get a simple representation of things here, like
                        //name, path, date, etc vs EVERYTHING
                        

                        using (var sw = new StreamWriter(fout))
                        {
                            sw.AutoFlush = true;
                            jsons.SerializeToWriter(key,sw);
                        }
                                              
                        _logger.Warn($"'{p.Object.DumpKey}' saved to '{fout}'");

                    }
                    else if (p.Object.KeyName.Length > 0)
                    {
                        var key = reg.GetKey(p.Object.KeyName);

                        if (key == null)
                        {
                            _logger.Warn($"Key '{p.Object.KeyName}' not found.");
                            DumpStopWatchInfo();
                            continue;
                        }

                        if (p.Object.ValueName.Length > 0)
                        {
                            var val = key.Values.SingleOrDefault(c => c.ValueName == p.Object.ValueName);

                            if (val == null)
                            {
                                _logger.Warn($"Value '{p.Object.ValueName}' not found for key '{p.Object.KeyName}'.");

                                DumpStopWatchInfo();
                                continue;
                            }

                            _sw.Stop();
                            totalSeconds += _sw.Elapsed.TotalSeconds;

                            _logger.Info(val);

                            hivesWithHits += 1;
                            totalHits += 1;

                            if (p.Object.SaveToName.Length > 0)
                            {
                                var baseDir = Path.GetDirectoryName(p.Object.SaveToName);
                                if (Directory.Exists(baseDir) == false)
                                {
                                    Directory.CreateDirectory(baseDir);
                                }

                                _logger.Info($"Saving contents of '{val.ValueName}' to '{p.Object.SaveToName}'");
                                File.WriteAllBytes(p.Object.SaveToName, val.ValueDataRaw);
                            }

                            DumpStopWatchInfo();

                            continue;
                        }

                        hivesWithHits += 1;
                        totalHits += 1;

                        _sw.Stop();
                        totalSeconds += _sw.Elapsed.TotalSeconds;

                        DumpRootKeyName(reg);

                        DumpKey(key, p.Object.Recursive);

                        DumpStopWatchInfo();
                    }
                    else if (p.Object.MinimumSize > 0)
                    {
                        var hits = reg.FindByValueSize(p.Object.MinimumSize).ToList();
                        _sw.Stop();
                        totalSeconds += _sw.Elapsed.TotalSeconds;

                        if (p.Object.Sort)
                        {
                            hits = hits.OrderBy(t => t.Value.ValueDataRaw.Length).ToList();
                        }

                        DumpRootKeyName(reg);

                        hivesWithHits += 1;
                        totalHits += hits.Count;

                        foreach (var valueBySizeInfo in hits)
                        {
                            _logger.Info(
                                $"Key: {Helpers.StripRootKeyNameFromKeyPath(valueBySizeInfo.Key.KeyPath)}, Value: {valueBySizeInfo.Value.ValueName}, Size: {valueBySizeInfo.Value.ValueDataRaw.Length:N0}");
                        }

                        _logger.Info("");

                        var plural = "s";
                        if (hits.Count() == 1)
                        {
                            plural = "";
                        }
                        _logger.Info(
                            $"Found {hits.Count():N0} value{plural} with size greater or equal to {p.Object.MinimumSize:N0} bytes");
                        DumpStopWatchInfo();
                    }
                    else if (p.Object.StartDate != null || p.Object.EndDate != null)
                    {
                        DateTimeOffset start;
                        DateTimeOffset end;
                        var startOk = DateTimeOffset.TryParse(p.Object.StartDate + "-0", out start);
                        var endOk = DateTimeOffset.TryParse(p.Object.EndDate + "-0", out end);

                        DateTimeOffset? startGood = null;
                        DateTimeOffset? endGood = null;
                        var hits = new List<SearchHit>();

                        if (!startOk && p.Object.StartDate != null)
                        {
                            throw new InvalidCastException("'StartDate' is not a valid datetime value");
                        }
                        if (!endOk && p.Object.EndDate != null)
                        {
                            throw new InvalidCastException("'EndDate' is not a valid datetime value");
                        }

                        if (startOk && endOk)
                        {
                            startGood = start;
                            endGood = end;
                            hits = reg.FindByLastWriteTime(startGood, endGood).ToList();
                        }
                        else if (startOk)
                        {
                            startGood = start;

                            hits = reg.FindByLastWriteTime(startGood, null).ToList();
                        }
                        else if (endOk)
                        {
                            endGood = end;
                            hits = reg.FindByLastWriteTime(null, endGood).ToList();
                        }

                        _sw.Stop();
                        totalSeconds += _sw.Elapsed.TotalSeconds;

                        if (p.Object.Sort)
                        {
                            hits = hits.OrderBy(t => t.Key.LastWriteTime ?? new DateTimeOffset()).ToList();
                        }

                        DumpRootKeyName(reg);

                        hivesWithHits += 1;
                        totalHits += hits.Count;

                        foreach (var searchHit in hits)
                        {
                            searchHit.StripRootKeyName = true;
                            _logger.Info($"Last write: {searchHit.Key.LastWriteTime}  Key: {searchHit}");
                        }

                        var suffix = string.Empty;

                        if (startGood != null || endGood != null)
                        {
                            suffix = $"between {startGood} and {endGood}";
                        }
                        if (startGood != null && endGood == null)
                        {
                            suffix = $"after {startGood}";
                        }
                        else if (endGood != null && startGood == null)
                        {
                            suffix = $"before {endGood}";
                        }

                        _logger.Info("");

                        var plural = "s";
                        if (hits.Count() == 1)
                        {
                            plural = "";
                        }
                        _logger.Info($"Found {hits.Count():N0} key{plural} with last write {suffix}");

                        DumpStopWatchInfo();
                    }
                    else if (p.Object.SimpleSearchKey.Length > 0 || p.Object.SimpleSearchValue.Length > 0 ||
                             p.Object.SimpleSearchValueData.Length > 0 || p.Object.SimpleSearchValueSlack.Length > 0)
                    {
                        List<SearchHit> hits = null;

                        if (p.Object.SimpleSearchKey.Length > 0)
                        {
                            hits = reg.FindInKeyName(p.Object.SimpleSearchKey, p.Object.RegEx).ToList();
                            if (p.Object.Sort)
                            {
                                hits = hits.OrderBy(t => t.Key.KeyName).ToList();
                            }
                        }
                        else if (p.Object.SimpleSearchValue.Length > 0)
                        {
                            hits = reg.FindInValueName(p.Object.SimpleSearchValue, p.Object.RegEx).ToList();
                            if (p.Object.Sort)
                            {
                                hits = hits.OrderBy(t => t.Value.ValueName).ToList();
                            }
                        }
                        else if (p.Object.SimpleSearchValueData.Length > 0)
                        {
                            hits =
                                reg.FindInValueData(p.Object.SimpleSearchValueData, p.Object.RegEx, p.Object.Literal)
                                    .ToList();
                            if (p.Object.Sort)
                            {
                                hits = hits.OrderBy(t => t.Value.ValueData).ToList();
                            }
                        }
                        else if (p.Object.SimpleSearchValueSlack.Length > 0)
                        {
                            hits =
                                reg.FindInValueDataSlack(p.Object.SimpleSearchValueSlack, p.Object.RegEx,
                                    p.Object.Literal)
                                    .ToList();
                            if (p.Object.Sort)
                            {
                                hits = hits.OrderBy(t => t.Value.ValueData).ToList();
                            }
                        }

                        if (hits == null)
                        {
                            _logger.Warn("No search results found");
                            DumpStopWatchInfo();
                            continue;
                        }

                        _sw.Stop();
                        totalSeconds += _sw.Elapsed.TotalSeconds;

                        DumpRootKeyName(reg);

                        //set up highlighting
                        var words = new HashSet<string>();
                        foreach (var searchHit in hits)
                        {
                            if (p.Object.SimpleSearchKey.Length > 0)
                            {
                                words.Add(p.Object.SimpleSearchKey);
                            }
                            else if (p.Object.SimpleSearchValue.Length > 0)
                            {
                                words.Add(p.Object.SimpleSearchValue);
                            }
                            else if (p.Object.SimpleSearchValueData.Length > 0)
                            {
                                if (p.Object.RegEx)
                                {
                                    words.Add(p.Object.SimpleSearchValueData);
                                }
                                else
                                {
                                    words.Add(searchHit.HitString);
                                }
                            }
                            else if (p.Object.SimpleSearchValueSlack.Length > 0)
                            {
                                if (p.Object.RegEx)
                                {
                                    words.Add(p.Object.SimpleSearchValueSlack);
                                }
                                else
                                {
                                    words.Add(searchHit.HitString);
                                }
                            }
                        }

                        AddHighlightingRules(words.ToList(), p.Object.RegEx);

                        hivesWithHits += 1;
                        totalHits += hits.Count;

                        foreach (var searchHit in hits)
                        {
                            searchHit.StripRootKeyName = true;

                            if (p.Object.SimpleSearchValueData.Length > 0 || p.Object.SimpleSearchValueSlack.Length > 0)
                            {
                                if (p.Object.SuppressData)
                                {
                                    _logger.Info(
                                        $"Key: {Helpers.StripRootKeyNameFromKeyPath(searchHit.Key.KeyPath)}, Value: {searchHit.Value.ValueName}");
                                }
                                else
                                {
                                    if (p.Object.SimpleSearchValueSlack.Length > 0)
                                    {
                                        _logger.Info(
                                            $"Key: {Helpers.StripRootKeyNameFromKeyPath(searchHit.Key.KeyPath)}, Value: {searchHit.Value.ValueName}, Slack: {searchHit.Value.ValueSlack}");
                                    }
                                    else
                                    {
                                        _logger.Info(
                                            $"Key: {Helpers.StripRootKeyNameFromKeyPath(searchHit.Key.KeyPath)}, Value: {searchHit.Value.ValueName}, Data: {searchHit.Value.ValueData}");
                                    }
                                }
                            }
                            else if (p.Object.SimpleSearchKey.Length > 0)
                            {
                                _logger.Info($"Key: {Helpers.StripRootKeyNameFromKeyPath(searchHit.Key.KeyPath)}");
                            }
                            else if (p.Object.SimpleSearchValue.Length > 0)
                            {
                                _logger.Info(
                                    $"Key: {Helpers.StripRootKeyNameFromKeyPath(searchHit.Key.KeyPath)}, Value: {searchHit.Value.ValueName}");
                            }
                        }

                        var target = (ColoredConsoleTarget) LogManager.Configuration.FindTargetByName("console");
                        target.WordHighlightingRules.Clear();

                        var suffix = string.Empty;
                        var withRegex = string.Empty;

                        var plural = "s";
                        if (hits.Count() == 1)
                        {
                            plural = "";
                        }

                        if (p.Object.SimpleSearchValueData.Length > 0)
                        {
                            suffix = $"value data hit{plural}";
                        }
                        else if (p.Object.SimpleSearchValueSlack.Length > 0)
                        {
                            suffix = $"value slack hit{plural}";
                        }
                        else if (p.Object.SimpleSearchKey.Length > 0)
                        {
                            suffix = $"key{plural}";
                        }
                        else if (p.Object.SimpleSearchValue.Length > 0)
                        {
                            suffix = $"value{plural}";
                        }

                        if (p.Object.RegEx)
                        {
                            withRegex = " (via RegEx)";
                        }

                       _logger.Info("");

                        _logger.Info($"Found {hits.Count():N0} {suffix}{withRegex}");

                        DumpStopWatchInfo();

                        
                    }
                    else
                    {
                        _logger.Warn("Nothing to do! =(");
                    }

                    //TODO search deleted?? should only need to look in reg.UnassociatedRegistryValues
                }
                catch (Exception ex)
                {
                    if (!ex.Message.Contains("bad signature"))
                    {
                        _logger.Error($"There was an error: {ex.Message}");
                    }
                }
            }
            /*
            if (p.Object.Directory?.Length > 0)
            {
                _logger.Info("");

                var suffix2 = totalHits == 1 ? "" : "s";
                var suffix3 = hivesWithHits == 1 ? "" : "s";
                var suffix4 = hivesToProcess.Count == 1 ? "" : "s";

                _logger.Info("---------------------------------------------");
                _logger.Info($"Directory: {p.Object.Directory}");
                _logger.Info(
                  $"Found {totalHits:N0} hit{suffix2} in {hivesWithHits:N0} hive{suffix3} out of {hivesToProcess.Count:N0} file{suffix4}");
                _logger.Info($"Total search time: {totalSeconds:N3} seconds");
                _logger.Info("");
            }
            */
        }
Beispiel #13
0
        private static void Main(string[] args)
        {
            var testFiles = new List<string>();

            var result = Parser.Default.ParseArguments<Options>(args);
            if (!result.Errors.Any())
            {
                if (result.Value.HiveName == null && result.Value.DirectoryName == null)
                {
                    Console.WriteLine(result.Value.GetUsage());
                    Environment.Exit(1);
                }

                if (!string.IsNullOrEmpty(result.Value.HiveName))
                {
                    if (!string.IsNullOrEmpty(result.Value.DirectoryName))
                    {
                        Console.WriteLine("Must specify either -d or -f, but not both");
                        Environment.Exit(1);
                    }
                }

                if (!string.IsNullOrEmpty(result.Value.DirectoryName))
                {
                    if (!string.IsNullOrEmpty(result.Value.HiveName))
                    {
                        Console.WriteLine("Must specify either -d or -f, but not both");
                        Environment.Exit(1);
                    }
                }

                if (!string.IsNullOrEmpty(result.Value.HiveName))
                {
                    testFiles.Add(result.Value.HiveName);
                }
                else
                {
                    if (Directory.Exists(result.Value.DirectoryName))
                    {
                        foreach (var file in Directory.GetFiles(result.Value.DirectoryName))
                        {
                            testFiles.Add(file);
                        }
                    }
                    else
                    {
                        Console.WriteLine("Directory '{0}' does not exist!", result.Value.DirectoryName);
                        Environment.Exit(1);
                    }
                }
            }
            else
            {
                Console.WriteLine(result.Value.GetUsage());
                Environment.Exit(1);
            }

            var verboseLevel = result.Value.VerboseLevel;
            if (verboseLevel < 0)
            {
                verboseLevel = 0;
            }
            if (verboseLevel > 2)
            {
                verboseLevel = 2;
            }

            var config = GetNlogConfig(verboseLevel, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
            LogManager.Configuration = config;

            var logger = LogManager.GetCurrentClassLogger();

            foreach (var testFile in testFiles)
            {
                if (File.Exists(testFile) == false)
                {
                    logger.Error("'{0}' does not exist!", testFile);
                    continue;
                }

                logger.Info("Processing '{0}'", testFile);
                Console.Title = $"Processing '{testFile}'";

                var sw = new Stopwatch();
                try
                {
                    var registryHive = new RegistryHive(testFile);
                    if (registryHive.Header.ValidateCheckSum() == false)
                    {
                        logger.Warn("CheckSum mismatch!");
                    }

                    if (registryHive.Header.Sequence1 != registryHive.Header.Sequence2)
                    {
                        logger.Warn("Sequence mismatch!");
                    }

                    sw.Start();

                    registryHive.RecoverDeleted = result.Value.RecoverDeleted;

                    registryHive.FlushRecordListsAfterParse = !result.Value.DontFlushLists;

                    registryHive.ParseHive();

                    logger.Info("Finished processing '{0}'", testFile);

                    Console.Title = $"Finished processing '{testFile}'";

                    sw.Stop();

                    var freeCells = registryHive.CellRecords.Where(t => t.Value.IsFree);
                    var referencedCells = registryHive.CellRecords.Where(t => t.Value.IsReferenced);

                    var nkFree = freeCells.Count(t => t.Value is NKCellRecord);
                    var vkFree = freeCells.Count(t => t.Value is VKCellRecord);
                    var skFree = freeCells.Count(t => t.Value is SKCellRecord);
                    var lkFree = freeCells.Count(t => t.Value is LKCellRecord);

                    var freeLists = registryHive.ListRecords.Where(t => t.Value.IsFree);
                    var referencedList = registryHive.ListRecords.Where(t => t.Value.IsReferenced);

                    var goofyCellsShouldBeUsed =
                        registryHive.CellRecords.Where(t => t.Value.IsFree == false && t.Value.IsReferenced == false);

                    var goofyListsShouldBeUsed =
                        registryHive.ListRecords.Where(t => t.Value.IsFree == false && t.Value.IsReferenced == false);

                    var sb = new StringBuilder();

                    sb.AppendLine("Results:");
                    sb.AppendLine();

                    sb.AppendLine(
                        $"Found {registryHive.HBinRecordCount:N0} hbin records. Total size of seen hbin records: 0x{registryHive.HBinRecordTotalSize:X}, Header hive size: 0x{registryHive.Header.Length:X}");

                    if (registryHive.FlushRecordListsAfterParse == false)
                    {
                        sb.AppendLine(
                            $"Found {registryHive.CellRecords.Count:N0} Cell records (nk: {registryHive.CellRecords.Count(w => w.Value is NKCellRecord):N0}, vk: {registryHive.CellRecords.Count(w => w.Value is VKCellRecord):N0}, sk: {registryHive.CellRecords.Count(w => w.Value is SKCellRecord):N0}, lk: {registryHive.CellRecords.Count(w => w.Value is LKCellRecord):N0})");
                        sb.AppendLine($"Found {registryHive.ListRecords.Count:N0} List records");
                        sb.AppendLine();
                        sb.AppendLine(string.Format($"Header CheckSums match: {registryHive.Header.ValidateCheckSum()}"));
                        sb.AppendLine(string.Format($"Header sequence 1: {registryHive.Header.Sequence1}, Header sequence 2: {registryHive.Header.Sequence2}"));

                        sb.AppendLine();

                        sb.AppendLine(
                            $"There are {referencedCells.Count():N0} cell records marked as being referenced ({referencedCells.Count()/(double) registryHive.CellRecords.Count:P})");
                        sb.AppendLine(
                            $"There are {referencedList.Count():N0} list records marked as being referenced ({referencedList.Count()/(double) registryHive.ListRecords.Count:P})");

                        if (result.Value.RecoverDeleted)
                        {
                            sb.AppendLine();
                            sb.AppendLine("Free record info");
                            sb.AppendLine(
                                $"{freeCells.Count():N0} free Cell records (nk: {nkFree:N0}, vk: {vkFree:N0}, sk: {skFree:N0}, lk: {lkFree:N0})");
                            sb.AppendLine($"{freeLists.Count():N0} free List records");
                        }

                        sb.AppendLine();
                        sb.AppendLine(
                            $"Cells: Free + referenced + marked as in use but not referenced == Total? {registryHive.CellRecords.Count == freeCells.Count() + referencedCells.Count() + goofyCellsShouldBeUsed.Count()}");
                        sb.AppendLine(
                            $"Lists: Free + referenced + marked as in use but not referenced == Total? {registryHive.ListRecords.Count == freeLists.Count() + referencedList.Count() + goofyListsShouldBeUsed.Count()}");
                    }

                    sb.AppendLine();
                    sb.AppendLine(
                        $"There were {registryHive.HardParsingErrors:N0} hard parsing errors (a record marked 'in use' that didn't parse correctly.)");
                    sb.AppendLine(
                        $"There were {registryHive.SoftParsingErrors:N0} soft parsing errors (a record marked 'free' that didn't parse correctly.)");

                    logger.Info(sb.ToString());

            //                    foreach (var cellTemplate in fName1Test.ListRecords)
            //                    {
            //                        Console.WriteLine(cellTemplate.ToString());
            //                    }

                    if (result.Value.ExportHiveData)
                    {
                        Console.WriteLine();

                        var baseDir = Path.Combine(Path.GetDirectoryName(testFile), "out");

                        if (Directory.Exists(baseDir) == false)
                        {
                            Directory.CreateDirectory(baseDir);
                        }

                        var baseFname = Path.GetFileName(testFile);

                        var myName = string.Empty;

                        var deletedOnly = result.Value.ExportDeletedOnly;

                        if (deletedOnly)
                        {
                            myName = "_EricZ_recovered.txt";
                        }
                        else
                        {
                            myName = "_EricZ_all.txt";
                        }

                        var outfile = Path.Combine(baseDir, $"{baseFname}{myName}");

                        logger.Info("Exporting hive data to '{0}'", outfile);

                        registryHive.ExportDataToCommonFormat(outfile, deletedOnly);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("There was an error: {0}", ex.Message);
                }

                logger.Info("Processing took {0:N4} seconds\r\n", sw.Elapsed.TotalSeconds);

                Console.WriteLine();
                Console.WriteLine();

                if (result.Value.PauseAfterEachFile)
                {
                    Console.WriteLine("Press any key to continue to next file");
                    Console.ReadKey();

                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }