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"); }
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 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 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 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 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 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 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 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); }
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(""); } */ }