public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); try { var arcHist = key.Values.SingleOrDefault(t => t.ValueName == "ArcHistory"); if (arcHist != null) { var arcs = Encoding.Unicode.GetString(arcHist.ValueDataRaw).Split('\0'); foreach (var arc in arcs) { if (arc.Trim().Length == 0) { continue; } var v = new ValuesOut(arc); Values.Add(v); } } } catch (Exception ex) { Errors.Add($"Error processing 7-Zip archive history: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
// write entries information under subkey of specified key private static void WriteSubKeyData(RegistryKey key, CsvWriter csv, string computerName, string hiveName) { if (key.SubKeys.Count > 0) { foreach (var sk in key.SubKeys) { if (sk.Values.Count > 0) { WriteValueData(sk, csv, computerName, hiveName); } else // wirte only key and timestamp if no entries exist { var ce = new CacheEntry(); ce.ComputerName = computerName; ce.HiveName = hiveName; ce.Key = Helpers.StripRootKeyNameFromKeyPath(key.KeyPath); ce.Name = ""; ce.Value = ""; ce.LastModified = key.LastWriteTime.Value.LocalDateTime.ToString("yyyy/MM/dd HH:mm:ss.fff"); ce.TimeZone = key.LastWriteTime.Value.LocalDateTime.ToString("zzz"); csv.WriteRecord(ce); } } } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); foreach (var keyValue in key.Values) { try { var unrot = Helpers.Rot13Transform(keyValue.ValueName); var run = 0; string guid = null; try { guid = Regex.Match(unrot, @"\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b", RegexOptions.IgnoreCase).Value; var foldername = Utils.GetFolderNameFromGuid(guid); unrot = unrot.Replace(guid, foldername); } catch (ArgumentException ex) { // Syntax error in the regular expression } DateTimeOffset? lastRun = null; if (keyValue.ValueDataRaw.Length >= 16) { run = BitConverter.ToInt32(keyValue.ValueDataRaw, 4); lastRun = DateTimeOffset.FromFileTime(BitConverter.ToInt64(keyValue.ValueDataRaw, 8)); if (keyValue.ValueDataRaw.Length >= 68) { lastRun = DateTimeOffset.FromFileTime(BitConverter.ToInt64(keyValue.ValueDataRaw, 60)); } } if (lastRun?.Year < 1970) { lastRun = null; } var vo = new ValuesOut(keyValue.ValueName, unrot, run, lastRun); _values.Add(vo); } catch (Exception ex) { Errors.Add($"Value name: {keyValue.ValueName}, message: {ex.Message}"); } } }
/// <summary> /// Generic registry helper to enumerate all registry values /// </summary> /// <param name="regKey"></param> /// <param name="path"></param> /// <param name="type"></param> /// <param name="info"></param> /// <param name="sourceFile"></param> private void EnumerateValues(Registry.Abstractions.RegistryKey regKey, string path, string type, string info, string sourceFile) { DateTimeOffset?modified = regKey.LastWriteTime; foreach (Registry.Abstractions.KeyValue regValue in regKey.Values) { ProcessEntry(regValue.ValueData.ToString(), path, type, info, sourceFile, modified); } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); foreach (var rd in ProcessKey(key)) { _values.Add(rd); } }
// public constructors... public RegistryKey(NKCellRecord nk, RegistryKey parent) { NKRecord = nk; Parent = parent; InternalGUID = Guid.NewGuid().ToString(); SubKeys = new List<RegistryKey>(); Values = new List<KeyValue>(); ClassName = string.Empty; }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var appcompatValue = key.Values.Single(t => t.ValueName == ValueName); var ctl = key.KeyPath.Split('\\').SingleOrDefault(t => t.Contains("ControlSet")); var num = -1; if (ctl != null) { num = ctl.ToCharArray().Last(); } try { var cache = new acc(appcompatValue.ValueDataRaw, num); foreach (var c in cache.Caches) { foreach (var cacheEntry in c.Entries) { try { var vo = new ValuesOut(cacheEntry.CacheEntryPosition, cacheEntry.Path, cacheEntry.LastModifiedTimeUTC); _values.Add(vo); } catch (Exception ex) { Errors.Add($"Value name: {cacheEntry.CacheEntryPosition}, message: {ex.Message}"); } } } } catch (Exception ex) { Errors.Add($"Error processing AppCompatCache: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
// write entries information recursively under specified key private static void WriteSpecificKeyInfo(RegistryKey key, CsvWriter csv, string filepath) { string computerName = ExtractComputerName(filepath); string hiveName = Path.GetFileName(filepath); // processing if entries exist under specified key if (key.Values.Count > 0) { WriteValueData(key, csv, computerName, hiveName); } // loop processing if entries exist under specified key if (key.SubKeys.Count > 0) { foreach (var sk in key.SubKeys) { WriteSubKeyData(sk, csv, computerName, hiveName); } } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var currVal = string.Empty; try { foreach (var keyValue in key.Values) { var vData = string.Empty; currVal = keyValue.ValueName; switch (keyValue.ValueDataRaw[0]) { case 0x5f: vData = Encoding.Unicode.GetString(keyValue.ValueDataRaw); break; default: throw new Exception("fix me"); } var v1 = new ValuesOut(keyValue.ValueName, vData); Values.Add(v1); } } catch (Exception ex) { Errors.Add($"Error processing MountedDevices value {currVal}: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
// write entries information under specified key private static void WriteValueData(RegistryKey key, CsvWriter csv, string computerName, string hiveName) { foreach (var keyValue in key.Values) { var ce = new CacheEntry(); ce.ComputerName = computerName; ce.HiveName = hiveName; ce.Key = Helpers.StripRootKeyNameFromKeyPath(key.KeyPath); ce.Name = keyValue.ValueName; if (keyValue.ValueData.Length > 256) { ce.Value = $"(Large Data: {keyValue.ValueDataRaw.Length} bytes"; } else { ce.Value = keyValue.ValueData; } ce.LastModified = key.LastWriteTime.Value.LocalDateTime.ToString("yyyy/MM/dd HH:mm:ss.fff"); ce.TimeZone = key.LastWriteTime.Value.LocalDateTime.ToString("zzz"); csv.WriteRecord(ce); } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var vn = string.Empty; try { foreach (var keyValue in key.Values) { vn = keyValue.ValueName; switch (keyValue.ValueName) { case "Bias": var b0 = BitConverter.ToInt32(keyValue.ValueDataRaw, 0); _values.Add(new ValuesOut(keyValue.ValueName, b0.ToString(), keyValue.ValueData)); break; case "StandardName": _values.Add(new ValuesOut(keyValue.ValueName, keyValue.ValueData, keyValue.ValueData)); break; case "StandardBias": var b1 = BitConverter.ToInt32(keyValue.ValueDataRaw, 0); _values.Add(new ValuesOut(keyValue.ValueName, b1.ToString(), keyValue.ValueData)); break; case "StandardStart": //santiago utc - 4 // //start 00 00 0A 00 02 00 17 00 3B 00 3B 00 E7 03 06 00 //hour 24 //day of week 6 //week of month 2 //month 10 // // 00 00 // 0A == 10 Month // 00 // 02 week of month // 00 // 17 == 23 hour // 00 // 3B == 59 minute // 00 // 3B == 59 second // 00 // E7 03 == 999 millisecond? // 06 00 day of week var month0 = keyValue.ValueDataRaw[2]; var weekOfMonth0 = keyValue.ValueDataRaw[4]; var hour0 = keyValue.ValueDataRaw[6]; var minute0 = keyValue.ValueDataRaw[8]; var second0 = keyValue.ValueDataRaw[10]; var millisecond0 = BitConverter.ToInt16(keyValue.ValueDataRaw, 12); var dayOfWeek0 = keyValue.ValueDataRaw[14]; var ss = $"Month {month0}, week of month {weekOfMonth0}, day of week {dayOfWeek0}, Hours:Minutes:Seconds:Milliseconds {hour0}:{minute0}:{second0}:{millisecond0}"; _values.Add(new ValuesOut(keyValue.ValueName, ss, keyValue.ValueData)); break; case "DaylightName": _values.Add(new ValuesOut(keyValue.ValueName, keyValue.ValueData, keyValue.ValueData)); break; case "DaylightBias": var b2 = BitConverter.ToInt32(keyValue.ValueDataRaw, 0); _values.Add(new ValuesOut(keyValue.ValueName, b2.ToString(), keyValue.ValueData)); break; case "DaylightStart": var month1 = keyValue.ValueDataRaw[2]; var weekOfMonth1 = keyValue.ValueDataRaw[4]; var hour1 = keyValue.ValueDataRaw[6]; var minute1 = keyValue.ValueDataRaw[8]; var second1 = keyValue.ValueDataRaw[10]; var millisecond1 = BitConverter.ToInt16(keyValue.ValueDataRaw, 12); var dayOfWeek1 = keyValue.ValueDataRaw[14]; var ss1 = $"Month {month1}, week of month {weekOfMonth1}, day of week {dayOfWeek1}, Hours:Minutes:Seconds:Milliseconds {hour1}:{minute1}:{second1}:{millisecond1}"; _values.Add(new ValuesOut(keyValue.ValueName, ss1, keyValue.ValueData)); break; case "ActiveTimeBias": var b3 = BitConverter.ToInt32(keyValue.ValueDataRaw, 0); _values.Add(new ValuesOut(keyValue.ValueName, b3.ToString(), keyValue.ValueData)); break; } } } catch (Exception ex) { Errors.Add($"Error processing TimeZoneInformation value '{vn}': {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
public bool ParseHive() { if (_parsed) { throw new Exception("ParseHive already called"); } TotalBytesRead = 0; TotalBytesRead += 4096; _softParsingErrors = 0; _hardParsingErrors = 0; ////Look at first hbin, get its size, then read that many bytes to create hbin record long offsetInHive = 4096; var hiveLength = Header.Length + 0x1000; //keep reading the file until we reach the end while (offsetInHive < hiveLength) { var hbinSize = BitConverter.ToUInt32(ReadBytesFromHive(offsetInHive + 8, 4), 0); if (hbinSize == 0) { _logger.Info("Found hbin with size 0 at absolute offset 0x{0:X}", offsetInHive); // Go to end if we find a 0 size block (padding?) offsetInHive = HiveLength(); continue; } var hbinSig = BitConverter.ToInt32(ReadBytesFromHive(offsetInHive, 4), 0); if (hbinSig != HbinSignature) { _logger.Error("hbin header incorrect at absolute offset 0x{0:X}!!! Percent done: {1:P}", offsetInHive, (double) offsetInHive/hiveLength); // if (RecoverDeleted) //TODO ? always or only if recoverdeleted // { // //TODO need to try to recover records from the bad chunk // } break; } Check.That(hbinSig).IsEqualTo(HbinSignature); _logger.Debug( "Processing hbin at absolute offset 0x{0:X} with size 0x{1:X} Percent done: {2:P}", offsetInHive, hbinSize, (double) offsetInHive/hiveLength); var rawhbin = ReadBytesFromHive(offsetInHive, (int) hbinSize); try { var h = new HBinRecord(rawhbin, offsetInHive - 0x1000, Header.MinorVersion, RecoverDeleted, this); _logger.Trace("hbin info: {0}", h.ToString()); _logger.Debug("Getting records from hbin at absolute offset 0x{0:X}", offsetInHive); var records = h.Process(); _logger.Debug("Found {0:N0} records from hbin at absolute offset 0x{1:X}", records.Count, offsetInHive); foreach (var record in records) { //TODO change this to compare against constants? switch (record.Signature) { case "nk": case "sk": case "lk": case "vk": _logger.Debug("Adding cell record with signature {0} at absolute offset 0x{1:X}", record.Signature, record.AbsoluteOffset); CellRecords.Add(record.AbsoluteOffset - 4096, (ICellTemplate) record); break; case "db": case "li": case "ri": case "lh": case "lf": _logger.Debug("Adding list record with signature {0} at absolute offset 0x{1:X}", record.Signature, record.AbsoluteOffset); ListRecords.Add(record.AbsoluteOffset - 4096, (IListTemplate) record); break; } } HBinRecordCount += 1; HBinRecordTotalSize += hbinSize; } catch (Exception ex) { _logger.Error(ex, $"Error processing hbin at absolute offset 0x{offsetInHive:X}."); } offsetInHive += hbinSize; } _logger.Info("Initial processing complete. Building tree..."); //The root node can be found by either looking at Header.RootCellOffset or looking for an nk record with HiveEntryRootKey flag set. //here we are looking for the flag var rootNode = CellRecords.Values.OfType<NKCellRecord>() .SingleOrDefault( (f => (f.Flags & NKCellRecord.FlagEnum.HiveEntryRootKey) == NKCellRecord.FlagEnum.HiveEntryRootKey)); if (rootNode == null) { throw new KeyNotFoundException("Root nk record not found!"); } //validate what we found above via the flag method Check.That((long) Header.RootCellOffset).IsEqualTo(rootNode.RelativeOffset); rootNode.IsReferenced = true; _logger.Info("Found root node! Getting subkeys..."); Root = new RegistryKey(rootNode, null); _logger.Debug("Created root node object. Getting subkeys."); var keys = GetSubKeysAndValues(Root); Root.SubKeys.AddRange(keys); _logger.Info("Hive processing complete!"); //All processing is complete, so we do some tests to see if we really saw everything if (RecoverDeleted && HiveLength() != TotalBytesRead) { var remainingHive = ReadBytesFromHive(TotalBytesRead, (int) (HiveLength() - TotalBytesRead)); //Sometimes the remainder of the file is all zeros, which is useless, so check for that if (!Array.TrueForAll(remainingHive, a => a == 0)) { _logger.Warn( "Extra, non-zero data found beyond hive length! Check for erroneous data starting at 0x{0:x}!", TotalBytesRead); } //as a second check, compare Header length with what we read (taking the header into account as Header.Length is only for hbin records) if (Header.Length != TotalBytesRead - 0x1000) { //ncrunch: no coverage _logger.Warn( //ncrunch: no coverage "Hive length (0x{0:x}) does not equal bytes read (0x{1:x})!! Check the end of the hive for erroneous data", HiveLength(), TotalBytesRead); } //ncrunch: no coverage } if (RecoverDeleted) { BuildDeletedRegistryKeys(); } if (FlushRecordListsAfterParse) { _logger.Info("Flushing record lists..."); ListRecords.Clear(); var toRemove = CellRecords.Where(pair => pair.Value is NKCellRecord || pair.Value is VKCellRecord) .Select(pair => pair.Key) .ToList(); foreach (var key in toRemove) { CellRecords.Remove(key); } } _parsed = true; return true; }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var namesKey = key.SubKeys.SingleOrDefault(t => t.KeyName == "Names"); var nameMap = new Dictionary<int, DateTimeOffset>(); if (namesKey == null) { return; } foreach (var registryKey in namesKey.SubKeys) { if (nameMap.ContainsKey((int) registryKey.Values.First().VKRecord.DataTypeRaw)) { continue; } nameMap.Add((int) registryKey.Values.First().VKRecord.DataTypeRaw, registryKey.LastWriteTime.Value); } foreach (var key1 in key.SubKeys) { if (key1.KeyName == "Names") { continue; } try { var fVal = key1.Values.SingleOrDefault(t => t.ValueName == "F"); var userId = 0; var invalidLogins = 0; var totalLogins = 0; DateTimeOffset? lastLoginTime = null; DateTimeOffset? lastPwChangeTime = null; DateTimeOffset? acctExpiresTime = null; DateTimeOffset? lastIncorrectPwTime = null; if (fVal != null) { userId = BitConverter.ToInt32(fVal.ValueDataRaw, 0x30); invalidLogins = BitConverter.ToInt16(fVal.ValueDataRaw, 0x40); totalLogins = BitConverter.ToInt16(fVal.ValueDataRaw, 0x42); var tempTime = DateTimeOffset.FromFileTime(BitConverter.ToInt64(fVal.ValueDataRaw, 0x8)); if (tempTime.Year > 1700) { lastLoginTime = tempTime.ToUniversalTime(); } tempTime = DateTimeOffset.FromFileTime(BitConverter.ToInt64(fVal.ValueDataRaw, 0x18)); if (tempTime.Year > 1700) { lastPwChangeTime = tempTime.ToUniversalTime(); } tempTime = DateTimeOffset.MinValue; try { tempTime = DateTimeOffset.FromFileTime(BitConverter.ToInt64(fVal.ValueDataRaw, 0x20)); } catch (Exception) { } if (tempTime.Year > 1700) { acctExpiresTime = tempTime.ToUniversalTime(); } tempTime = DateTimeOffset.FromFileTime(BitConverter.ToInt64(fVal.ValueDataRaw, 0x28)); if (tempTime.Year > 1700) { lastIncorrectPwTime = tempTime.ToUniversalTime(); } } var vVal = key1.Values.SingleOrDefault(t => t.ValueName == "V"); var offToName = BitConverter.ToInt32(vVal.ValueDataRaw, 0xc) + 0xCC; var nameLen = BitConverter.ToInt32(vVal.ValueDataRaw, 0xc + 4); var name1 = Encoding.Unicode.GetString(vVal.ValueDataRaw, offToName, nameLen); var offToFull = BitConverter.ToInt32(vVal.ValueDataRaw, 0x18) + 0xCC; var fullLen = BitConverter.ToInt32(vVal.ValueDataRaw, 0x18 + 4); var full1 = Encoding.Unicode.GetString(vVal.ValueDataRaw, offToFull, fullLen); var offToComment = BitConverter.ToInt32(vVal.ValueDataRaw, 0x24) + 0xCC; var commentLen = BitConverter.ToInt32(vVal.ValueDataRaw, 0x24 + 4); var comment = Encoding.Unicode.GetString(vVal.ValueDataRaw, offToComment, commentLen); var offToUserComment = BitConverter.ToInt32(vVal.ValueDataRaw, 0x30) + 0xCC; var userCommentLen = BitConverter.ToInt32(vVal.ValueDataRaw, 0x30 + 4); var userComment = Encoding.Unicode.GetString(vVal.ValueDataRaw, offToUserComment, userCommentLen); var offHomeDir = BitConverter.ToInt32(vVal.ValueDataRaw, 0x48) + 0xCC; var homeDirLen = BitConverter.ToInt32(vVal.ValueDataRaw, 0x48 + 4); var homeDir = Encoding.Unicode.GetString(vVal.ValueDataRaw, offHomeDir, homeDirLen); var createdOn = nameMap[userId]; var u = new UserOut(userId, invalidLogins, totalLogins, lastLoginTime, lastPwChangeTime, lastIncorrectPwTime, acctExpiresTime, name1, full1, comment, userComment, homeDir, createdOn); _values.Add(u); } catch (Exception ex) { Errors.Add($"Error processing user account: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var valuesList = new List<ValuesOut>(); var currentKey = string.Empty; try { currentKey = key.KeyName; //get MRU key and read it in var mruVal = key.Values.SingleOrDefault(t => t.ValueName == "MRUList"); var mruListOrder = new ArrayList(); if (mruVal != null) { foreach (var c in mruVal.ValueData.ToCharArray()) { mruListOrder.Add(c.ToString()); } } foreach (var keyValue in key.Values) { if (keyValue.ValueName == "MRUList") { continue; } var mru = mruListOrder.IndexOf(keyValue.ValueName); DateTimeOffset? openedOn = null; if (mru == 0) { openedOn = key.LastWriteTime; } var vd = keyValue.ValueData; if (vd.EndsWith(@"\1")) { vd = keyValue.ValueData.Substring(0, keyValue.ValueData.Length - 2); } var v = new ValuesOut(keyValue.ValueName, vd, mru, openedOn); valuesList.Add(v); } } catch (Exception ex) { Errors.Add($"Error processing RunMRU subkey {currentKey}: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } var v1 = valuesList.OrderBy(t => t.MruPosition); foreach (var source in v1.ToList()) { _values.Add(source); } }
private void DumpKeyCommonFormat(RegistryKey key, StreamWriter sw, ref int keyCount, ref int valueCount) { if (((key.KeyFlags & RegistryKey.KeyFlagsEnum.HasActiveParent) == RegistryKey.KeyFlagsEnum.HasActiveParent) && ((key.KeyFlags & RegistryKey.KeyFlagsEnum.Deleted) == RegistryKey.KeyFlagsEnum.Deleted)) { return; } foreach (var subkey in key.SubKeys) { if (((subkey.KeyFlags & RegistryKey.KeyFlagsEnum.HasActiveParent) == RegistryKey.KeyFlagsEnum.HasActiveParent) && ((subkey.KeyFlags & RegistryKey.KeyFlagsEnum.Deleted) == RegistryKey.KeyFlagsEnum.Deleted)) { return; } keyCount += 1; sw.WriteLine("key|{0}|{1}|{2}|{3}", subkey.NKRecord.IsFree ? "U" : "A", subkey.NKRecord.AbsoluteOffset, subkey.KeyPath, subkey.LastWriteTime.Value.UtcDateTime.ToString("o")); foreach (var val in subkey.Values) { valueCount += 1; sw.WriteLine(@"value|{0}|{1}|{2}|{3}|{4}|{5}", val.VKRecord.IsFree ? "U" : "A", val.VKRecord.AbsoluteOffset, subkey.KeyName, val.ValueName, (int) val.VKRecord.DataType, BitConverter.ToString(val.VKRecord.ValueDataRaw).Replace("-", " ")); } DumpKeyCommonFormat(subkey, sw, ref keyCount, ref valueCount); } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); try { foreach (var keyValue in key.Values) { //[F00000000][T01D005C5B44B6300][O00000000]*C:\Users\eric\Desktop\aa\Out\Deduplicated.tsv var segs = keyValue.ValueData.Split('*'); var fName = segs.Last(); var segs2 = segs.First().Split('['); //"T01D005C5B44B6300]" var rawTime = segs2[2]; rawTime = rawTime.Substring(1); rawTime = rawTime.Substring(0, rawTime.Length - 1); var time = Convert.ToInt64(rawTime, 16); var firstOpen = DateTimeOffset.FromFileTime(time); // @"Software\Microsoft\Office\15.0\Word\User MRU\*\File MRU", //Software\Microsoft\Office\15.0\Word\Reading Locations //Software\Microsoft\Office\15.0\Word\Reading Locations //Value Name Value Type Data //File Path RegSz C:\ProjectWorkingFolder\GOON2\GOON2\GOON2Manual.docx //jump up a few levels and check for Reading Locations var readingLocKey = key.Parent.Parent.Parent.SubKeys.SingleOrDefault(t => t.KeyName == "Reading Locations"); DateTimeOffset? lastOpen = null; if (readingLocKey != null) { foreach (var registryKey in readingLocKey.SubKeys) { var readingLocVal = registryKey.Values.SingleOrDefault(t => t.ValueName == "File Path"); if (readingLocVal != null) { if (readingLocVal.ValueData == fName) { lastOpen = registryKey.LastWriteTime; break; } } } } var v = new ValuesOut(keyValue.ValueName, firstOpen, lastOpen, fName); _values.Add(v); } } catch (Exception ex) { Errors.Add($"Error processing MRU key: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
public ValueBySizeInfo(RegistryKey key, KeyValue value) { Key = key; Value = value; }
private IEnumerable<RecentDoc> ProcessRecentKey(RegistryKey key) { var l = new List<RecentDoc>(); try { var mruList = key.Values.Single(t => t.ValueName == "MRUListEx"); var mruPositions = new Dictionary<uint, int>(); var i = 0; var index = 0; var mruPos = BitConverter.ToUInt32(mruList.ValueDataRaw, index); index += 4; while (mruPos != 0xFFFFFFFF) { mruPositions.Add(mruPos, i); i++; mruPos = BitConverter.ToUInt32(mruList.ValueDataRaw, index); index += 4; } //mruPositions now contains a map of positions (the key) to the order it was opened (the value) foreach (var keyValue in key.Values) { if (keyValue.ValueName == "MRUListEx" || keyValue.ValueName == "ViewStream") { continue; } var mru = mruPositions[uint.Parse(keyValue.ValueName)]; var targetName = Encoding.Unicode.GetString(keyValue.ValueDataRaw).Split('\0')[0]; var offsetToRemainingData = targetName.Length*2 + 2; //TODO do not use Skip. use Buffer.BlockCopy as its faster var remainingData = keyValue.ValueDataRaw.Skip(offsetToRemainingData).ToArray(); index = 0; var chunkLen = BitConverter.ToUInt16(remainingData, index); var chunks = new List<byte[]>(); while (remainingData.Length > index) { var chunk = remainingData.Skip(index).Take(chunkLen).ToArray(); chunks.Add(chunk); index += chunkLen; chunkLen = BitConverter.ToUInt16(remainingData, index); if (chunkLen == 0) { break; } } index = 2; //skip chunk length var lnkNameType = chunks[0][index]; // if 32, its unicode, if 36, ascii index += 12; //skip type that always seems to be [32|36]-00-00-00-00-00-00-00-00-00-00-00- var lnkName = ""; if (lnkNameType == 36) { lnkName = Encoding.Unicode.GetString(chunks[0].Skip(index).ToArray()).Split('\0')[0]; index += lnkName.Length*2; } else { lnkName = Encoding.GetEncoding(1252).GetString(chunks[0].Skip(index).ToArray()).Split('\0')[0]; index += lnkName.Length; } while (chunks[0][index] != 4) { index += 1; //move until our signature } index -= 4; //jump back to start of extension block var beefBytes = chunks[0].Skip(index).ToArray(); var sig = BitConverter.ToUInt32(beefBytes, 4); var beef = (Beef0004) Utils.GetExtensionBlockFromBytes(sig, beefBytes); DateTimeOffset? openedOn = null; if (mru == 0) { openedOn = key.LastWriteTime; } DateTimeOffset? extLastOpened = null; var ext = Path.GetExtension(targetName).ToLowerInvariant(); var targetName1 = string.Empty; if (ext.Length == 0) { //folder var sk1 = key.SubKeys.SingleOrDefault(t => t.KeyName == "Folder"); var skmru = sk1?.Values.SingleOrDefault(t => t.ValueName == "MRUListEx"); if (skmru != null) { //get last accessed folder value name var mruPosf = BitConverter.ToInt32(skmru.ValueDataRaw, 0); //pull folder name from the value var val1 = sk1.Values.SingleOrDefault(t => t.ValueName == mruPosf.ToString()); targetName1 = Encoding.Unicode.GetString(val1.ValueDataRaw).Split('\0')[0]; } if (sk1 != null && targetName1 == targetName) { extLastOpened = sk1.LastWriteTime; } } else { var sk2 = key.SubKeys.SingleOrDefault(t => t.KeyName.ToLowerInvariant() == ext); var skmruf = sk2?.Values.SingleOrDefault(t => t.ValueName == "MRUListEx"); if (skmruf != null) { //get last accessed folder value name var mruPosff = BitConverter.ToInt32(skmruf.ValueDataRaw, 0); //pull folder name from the value var val1 = sk2.Values.SingleOrDefault(t => t.ValueName == mruPosff.ToString()); targetName1 = Encoding.Unicode.GetString(val1.ValueDataRaw).Split('\0')[0]; } if (sk2 != null && targetName1 == targetName) { extLastOpened = sk2.LastWriteTime; } } var rd = new RecentDoc(mru, keyValue.ValueName, targetName, beef.MFTInformation.MFTEntryNumber, beef.MFTInformation.MFTSequenceNumber, beef.MFTInformation.Note, beef.CreatedOnTime, beef.LastAccessTime, beef.LongName, key.KeyName, openedOn, extLastOpened); l.Add(rd); } } catch (Exception ex) { Errors.Add($"Error processing recent key ({key.KeyPath}): {ex.Message}"); } foreach (var registryKey in key.SubKeys) { var subItems = ProcessRecentKey(registryKey); l.AddRange(subItems); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } l = l.OrderByDescending(t => t.Extension).ThenBy(t => t.MruPosition).ToList(); return l; }
//TODO this needs refactored to remove duplicated code private List<RegistryKey> GetSubKeysAndValues(RegistryKey key) { RelativeOffsetKeyMap.Add(key.NKRecord.RelativeOffset, key); KeyPathKeyMap.Add(key.KeyPath.ToLowerInvariant(), key); _logger.Debug("Getting subkeys for {0}", key.KeyPath); key.KeyFlags = RegistryKey.KeyFlagsEnum.HasActiveParent; var keys = new List<RegistryKey>(); if (key.NKRecord.ClassCellIndex > 0) { _logger.Debug("Getting Class cell information at relative offset 0x{0:X}", key.NKRecord.ClassCellIndex); var d = GetDataNodeFromOffset(key.NKRecord.ClassCellIndex); d.IsReferenced = true; var clsName = Encoding.Unicode.GetString(d.Data, 0, key.NKRecord.ClassLength); key.ClassName = clsName; _logger.Debug("Class name found {0}", clsName); } //Build ValueOffsets for this NKRecord if (key.NKRecord.ValueListCellIndex > 0) { //there are values for this key, so get the offsets so we can pull them next _logger.Debug("Getting value list offset at relative offset 0x{0:X}. Value count is {1:N0}", key.NKRecord.ValueListCellIndex, key.NKRecord.ValueListCount); var offsetList = GetDataNodeFromOffset(key.NKRecord.ValueListCellIndex); offsetList.IsReferenced = true; for (var i = 0; i < key.NKRecord.ValueListCount; i++) { //use i * 4 so we get 4, 8, 12, 16, etc var os = BitConverter.ToUInt32(offsetList.Data, i*4); _logger.Debug("Got value offset 0x{0:X}", os); key.NKRecord.ValueOffsets.Add(os); } } if (key.NKRecord.ValueOffsets.Count != key.NKRecord.ValueListCount) { //ncrunch: no coverage _logger.Warn( "Value count mismatch! ValueListCount is {0:N0} but NKRecord.ValueOffsets.Count is {1:N0}", //ncrunch: no coverage key.NKRecord.ValueListCount, key.NKRecord.ValueOffsets.Count); } //ncrunch: no coverage // look for values in this key foreach (var valueOffset in key.NKRecord.ValueOffsets) { _logger.Debug("Looking for vk record at relative offset 0x{0:X}", valueOffset); var vc = CellRecords[(long) valueOffset]; var vk = vc as VKCellRecord; _logger.Debug("Found vk record at relative offset 0x{0:X}. Value name: {1}", valueOffset, vk.ValueName); vk.IsReferenced = true; var value = new KeyValue(vk); key.Values.Add(value); } _logger.Debug("Looking for sk record at relative offset 0x{0:X}", key.NKRecord.SecurityCellIndex); var sk = CellRecords[key.NKRecord.SecurityCellIndex] as SKCellRecord; sk.IsReferenced = true; //TODO THIS SHOULD ALSO CHECK THE # OF SUBKEYS == 0 if (ListRecords.ContainsKey(key.NKRecord.SubkeyListsStableCellIndex) == false) { return keys; } _logger.Debug("Looking for list record at relative offset 0x{0:X}", key.NKRecord.SubkeyListsStableCellIndex); var l = ListRecords[key.NKRecord.SubkeyListsStableCellIndex]; var sig = BitConverter.ToInt16(l.RawBytes, 4); switch (sig) { case LfSignature: case LhSignature: var lxRecord = l as LxListRecord; lxRecord.IsReferenced = true; foreach (var offset in lxRecord.Offsets) { _logger.Debug("In lf or lh, looking for nk record at relative offset 0x{0:X}", offset.Key); var cell = CellRecords[offset.Key]; var nk = cell as NKCellRecord; nk.IsReferenced = true; _logger.Debug("In lf or lh, found nk record at relative offset 0x{0:X}. Name: {1}", offset.Key, nk.Name); var tempKey = new RegistryKey(nk, key); var sks = GetSubKeysAndValues(tempKey); tempKey.SubKeys.AddRange(sks); keys.Add(tempKey); } break; case RiSignature: var riRecord = l as RIListRecord; riRecord.IsReferenced = true; foreach (var offset in riRecord.Offsets) { _logger.Debug("In ri, looking for list record at relative offset 0x{0:X}", offset); var tempList = ListRecords[offset]; //templist is now an li or lh list if (tempList.Signature == "li") { var sk3 = tempList as LIListRecord; foreach (var offset1 in sk3.Offsets) { _logger.Debug("In ri/li, looking for nk record at relative offset 0x{0:X}", offset1); var cell = CellRecords[offset1]; var nk = cell as NKCellRecord; nk.IsReferenced = true; var tempKey = new RegistryKey(nk, key); var sks = GetSubKeysAndValues(tempKey); tempKey.SubKeys.AddRange(sks); keys.Add(tempKey); } } else { var lxRecord_ = tempList as LxListRecord; lxRecord_.IsReferenced = true; foreach (var offset3 in lxRecord_.Offsets) { _logger.Debug("In ri/li, looking for nk record at relative offset 0x{0:X}", offset3.Key); var cell = CellRecords[offset3.Key]; var nk = cell as NKCellRecord; nk.IsReferenced = true; var tempKey = new RegistryKey(nk, key); var sks = GetSubKeysAndValues(tempKey); tempKey.SubKeys.AddRange(sks); keys.Add(tempKey); } } } break; case LiSignature: var liRecord = l as LIListRecord; liRecord.IsReferenced = true; foreach (var offset in liRecord.Offsets) { _logger.Debug("In li, looking for nk record at relative offset 0x{0:X}", offset); var cell = CellRecords[offset]; var nk = cell as NKCellRecord; nk.IsReferenced = true; var tempKey = new RegistryKey(nk, key); var sks = GetSubKeysAndValues(tempKey); tempKey.SubKeys.AddRange(sks); keys.Add(tempKey); } break; default: throw new Exception($"Unknown subkey list type {l.Signature}!"); } return keys; }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var valuesList = new List<ValuesOut>(); var currentKey = string.Empty; try { //this key has folders stored in the root as well var mruVal1 = key.Values.SingleOrDefault(t => t.ValueName == "MRUList"); var mruListOrder1 = new ArrayList(); if (mruVal1 != null) { foreach (var c in mruVal1.ValueData.ToCharArray()) { mruListOrder1.Add(c.ToString()); } } foreach (var keyValue in key.Values) { if (keyValue.ValueName == "MRUList") { continue; } var mru1 = mruListOrder1.IndexOf(keyValue.ValueName); DateTimeOffset? openedOn1 = null; if (mru1 == 0) { openedOn1 = key.LastWriteTime; } var v1 = new ValuesOut("OpenSaveMRU", keyValue.ValueData, keyValue.ValueName, mru1, openedOn1); valuesList.Add(v1); } foreach (var registryKey in key.SubKeys) { currentKey = registryKey.KeyName; //get MRU key and read it in var mruVal = registryKey.Values.SingleOrDefault(t => t.ValueName == "MRUList"); var mruListOrder = new ArrayList(); if (mruVal != null) { foreach (var c in mruVal.ValueData.ToCharArray()) { mruListOrder.Add(c.ToString()); } } foreach (var keyValue in registryKey.Values) { if (keyValue.ValueName == "MRUList") { continue; } var mru = mruListOrder.IndexOf(keyValue.ValueName); DateTimeOffset? openedOn = null; if (mru == 0) { openedOn = registryKey.LastWriteTime; } var v = new ValuesOut(registryKey.KeyName, keyValue.ValueData, keyValue.ValueName, mru, openedOn); valuesList.Add(v); } } } catch (Exception ex) { Errors.Add($"Error processing OpenSaveMRU subkey {currentKey}: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } var v2 = valuesList.OrderBy(t => t.MruPosition); foreach (var source in v2.ToList()) { _values.Add(source); } }
private void UpdateChildPaths(RegistryKey key) { _logger.Trace("Updating child paths or key {0}", key.KeyPath); foreach (var sk in key.SubKeys) { sk.KeyPath = $@"{key.KeyPath}\{sk.KeyName}"; RelativeOffsetKeyMap.Add(sk.NKRecord.RelativeOffset, sk); if (KeyPathKeyMap.ContainsKey(sk.KeyPath.ToLowerInvariant()) == false) { KeyPathKeyMap.Add(sk.KeyPath.ToLowerInvariant(), sk); } UpdateChildPaths(sk); } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var currentKey = string.Empty; try { foreach (var registryKey in key.SubKeys) // subkeys of FileExts { currentKey = registryKey.KeyName; var oe = new List<string>(); var op = new List<string>(); var uc = "(UserChoice key not present)"; if (registryKey.SubKeys.Count == 0) { var progId = registryKey.Values.SingleOrDefault(t => t.ValueName == "Progid"); if (progId != null) { op.Add(progId.ValueData); } var vo1 = new ValuesOut(registryKey.KeyName, string.Join(", ", oe), string.Join(", ", op), uc); _values.Add(vo1); continue; } foreach (var subKey in registryKey.SubKeys) // subkey's subkeys { switch (subKey.KeyName) { case "OpenWithList": // contains values with name == char and value data of an executable name //there is an MRUList that contains the order the executables were selected var mruList = subKey.Values.SingleOrDefault(t => t.ValueName == "MRUList"); if (mruList != null) { //foreach slot in MRU, get the value and append it to our oe variable foreach (var mruPos in mruList.ValueData.ToCharArray()) { var exeName = subKey.Values.SingleOrDefault(t => t.ValueName == mruPos.ToString()); if (exeName != null) { oe.Add(exeName.ValueData); } else { oe.Add($"(Executable name for MRU slot '{mruPos}' not found!)"); } } } break; case "OpenWithProgids": foreach (var proIdValue in subKey.Values) { op.Add(proIdValue.ValueName); } break; case "UserChoice": var progId = subKey.Values.SingleOrDefault(t => t.ValueName == "ProgId"); if (progId != null) { uc = progId.ValueData; } break; } } //we have enough to add an entry var vo = new ValuesOut(registryKey.KeyName, string.Join(", ", oe), string.Join(", ", op), uc); _values.Add(vo); } } catch (Exception ex) { Errors.Add($"Error processing FileExts subkey {currentKey}: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
public RegistryKey GetKey(string keyPath) { var rawRoot = GetRawRecord(Header.RootCellOffset); var rootNk = new NKCellRecord(rawRoot.Length, Header.RootCellOffset, this); var newPath = keyPath.ToLowerInvariant(); // when getting child keys, the name may start with the root key name. if so, strip it if (newPath.StartsWith(rootNk.Name.ToLowerInvariant())) { var segs = keyPath.Split('\\'); newPath = string.Join("\\", segs.Skip(1)); } var rootKey = new RegistryKey(rootNk, null); var keyNames = newPath.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries); rootKey.SubKeys.AddRange(GetSubkeys(rootKey.NKRecord.SubkeyListsStableCellIndex, rootKey)); var finalKey = rootKey; for (var i = 0; i < keyNames.Length; i++) { finalKey = finalKey.SubKeys.SingleOrDefault(r => r.KeyName.ToLowerInvariant() == keyNames[i].ToLowerInvariant()); if (finalKey == null) { return null; } if (finalKey.NKRecord.SubkeyListsStableCellIndex > 0) { finalKey.SubKeys.AddRange(GetSubkeys(finalKey.NKRecord.SubkeyListsStableCellIndex, finalKey)); } } finalKey.Values.AddRange(GetKeyValues(finalKey.NKRecord.ValueListCellIndex, finalKey.NKRecord.ValueListCount)); if (finalKey.NKRecord.ClassCellIndex > 0) { _logger.Debug("Getting Class cell information at relative offset 0x{0:X}", finalKey.NKRecord.ClassCellIndex); var d = GetDataNodeFromOffset(finalKey.NKRecord.ClassCellIndex); d.IsReferenced = true; var clsName = Encoding.Unicode.GetString(d.Data, 0, finalKey.NKRecord.ClassLength); finalKey.ClassName = clsName; _logger.Debug("Class name found {0}", clsName); } return finalKey; }
/// <summary> /// Associates vk records with NK records and builds a hierarchy of nk records /// <remarks>Results of this method will be available in DeletedRegistryKeys</remarks> /// </summary> private void BuildDeletedRegistryKeys() { _logger.Info("Associating deleted keys and values..."); var unreferencedNKCells = CellRecords.Where(t => t.Value.IsReferenced == false && t.Value is NKCellRecord); var associatedVKRecordOffsets = new List<long>(); var _deletedRegistryKeys = new Dictionary<long, RegistryKey>(); //Phase one is to associate any value records with key records foreach (var unreferencedNkCell in unreferencedNKCells) { try { var nk = unreferencedNkCell.Value as NKCellRecord; _logger.Debug("Processing deleted nk record at absolute offset 0x{0:X}", nk.AbsoluteOffset); nk.IsDeleted = true; var regKey = new RegistryKey(nk, null) { KeyFlags = RegistryKey.KeyFlagsEnum.Deleted }; //some sanity checking on things if (regKey.NKRecord.Size < 0x50 + regKey.NKRecord.NameLength) { continue; } //Build ValueOffsets for this NKRecord if (regKey.NKRecord.ValueListCellIndex > 0) { //there are values for this key, so get the offsets so we can pull them next _logger.Debug("Processing deleted nk record values for nk at absolute offset 0x{0:X}", nk.AbsoluteOffset); DataNode offsetList = null; var size = ReadBytesFromHive(regKey.NKRecord.ValueListCellIndex + 4096, 4); var sizeNum = Math.Abs(BitConverter.ToUInt32(size, 0)); if (sizeNum > regKey.NKRecord.ValueListCount*4 + 4) { //ValueListCount is the number of offsets we should be looking for. they are 4 bytes long //If the size of the data record at regKey.NKRecord.ValueListCellIndex exceeds the total number of bytes plus the size (another 4 bytes), reset it to a more sane value to avoid crazy long reads sizeNum = regKey.NKRecord.ValueListCount*4 + 4; } try { var rawData = ReadBytesFromHive(regKey.NKRecord.ValueListCellIndex + 4096, (int) sizeNum); var dr = new DataNode(rawData, regKey.NKRecord.ValueListCellIndex); offsetList = dr; } catch (Exception) //ncrunch: no coverage { //ncrunch: no coverage //sometimes the data node doesn't have enough data to even do this, or its wrong data _logger.Warn( //ncrunch: no coverage "When getting values for nk record at absolute offset 0x{0:X}, not enough/invalid data was found at offset 0x{1:X}to look for value offsets. Value recovery is not possible", nk.AbsoluteOffset, regKey.NKRecord.ValueListCellIndex); } //ncrunch: no coverage if (offsetList != null) { _logger.Debug("Found offset list for nk at absolute offset 0x{0:X}. Processing.", nk.AbsoluteOffset); try { for (var i = 0; i < regKey.NKRecord.ValueListCount; i++) { //use i * 4 so we get 4, 8, 12, 16, etc var os = BitConverter.ToUInt32(offsetList.Data, i*4); regKey.NKRecord.ValueOffsets.Add(os); } } catch (Exception) //ncrunch: no coverage { //ncrunch: no coverage _logger.Warn( //ncrunch: no coverage "When getting value offsets for nk record at absolute offset 0x{0:X}, not enough data was found at offset 0x{1:X} to look for all value offsets. Only partial value recovery possible", nk.AbsoluteOffset, regKey.NKRecord.ValueListCellIndex); } //ncrunch: no coverage } } _logger.Debug("Looking for vk records for nk record at absolute offset 0x{0:X}", nk.AbsoluteOffset); //For each value offset, get the vk record if it exists, create a KeyValue, and assign it to the current RegistryKey foreach (var valueOffset in nk.ValueOffsets) { if (CellRecords.ContainsKey((long) valueOffset)) { _logger.Debug( "Found vk record at relative offset 0x{0:X} for nk record at absolute offset 0x{1:X}", valueOffset, nk.AbsoluteOffset); var val = CellRecords[(long) valueOffset] as VKCellRecord; //we have a value for this key if (val != null) { //if its an in use record AND referenced, warn if (val.IsFree == false && val.IsReferenced) { _logger.Warn( "When getting values for nk record at absolute offset 0x{0:X}, VK record at relative offset 0x{1:X} isn't free and is referenced by another nk record. Skipping!", nk.AbsoluteOffset, valueOffset); } else { associatedVKRecordOffsets.Add(val.RelativeOffset); var kv = new KeyValue(val); regKey.Values.Add(kv); _logger.Debug( "Added vk record at relative offset 0x{0:X} for nk record at absolute offset 0x{1:X}", valueOffset, nk.AbsoluteOffset); } } } else { _logger.Debug( "vk record at relative offset 0x{0:X} not found for nk record at absolute offset 0x{1:X}", valueOffset, nk.AbsoluteOffset); } } _logger.Debug( "Associated {0:N0} value(s) out of {1:N0} possible values for nk record at absolute offset 0x{2:X}", regKey.Values.Count, nk.ValueListCount, nk.AbsoluteOffset); _deletedRegistryKeys.Add(nk.RelativeOffset, regKey); } catch (Exception ex) //ncrunch: no coverage { //ncrunch: no coverage _logger.Error( //ncrunch: no coverage ex, $"Error while processing deleted nk record at absolute offset 0x{unreferencedNkCell.Value.AbsoluteOffset:X}"); } //ncrunch: no coverage } _logger.Debug("Building tree of key/subkeys for deleted keys"); //DeletedRegistryKeys now contains all deleted nk records and their associated values. //Phase 2 is to build a tree of key/subkeys var matchFound = true; while (matchFound) { var keysToRemove = new List<long>(); matchFound = false; foreach (var deletedRegistryKey in _deletedRegistryKeys) { if (_deletedRegistryKeys.ContainsKey(deletedRegistryKey.Value.NKRecord.ParentCellIndex)) { //deletedRegistryKey is a child of RegistryKey with relative offset ParentCellIndex //add the key as as subkey of its parent var parent = _deletedRegistryKeys[deletedRegistryKey.Value.NKRecord.ParentCellIndex]; _logger.Debug( "Found subkey at absolute offset 0x{0:X} for parent key at absolute offset 0x{1:X}", deletedRegistryKey.Value.NKRecord.AbsoluteOffset, parent.NKRecord.AbsoluteOffset); deletedRegistryKey.Value.KeyPath = $@"{parent.KeyPath}\{deletedRegistryKey.Value.KeyName}"; parent.SubKeys.Add(deletedRegistryKey.Value); //mark the subkey for deletion so we do not blow up the collection while iterating it keysToRemove.Add(deletedRegistryKey.Value.NKRecord.RelativeOffset); //reset this so the loop continutes matchFound = true; } } foreach (var l in keysToRemove) { //take out the key from main collection since we copied it above to its parent's subkey list _deletedRegistryKeys.Remove(l); } } _logger.Debug("Associating top level deleted keys to active Registry keys"); //Phase 3 is looking at top level keys from Phase 2 and seeing if any of those can be assigned to non-deleted keys in the main tree foreach (var deletedRegistryKey in _deletedRegistryKeys) { if (CellRecords.ContainsKey(deletedRegistryKey.Value.NKRecord.ParentCellIndex)) { //an parent key has been located, so get it var parentNk = CellRecords[deletedRegistryKey.Value.NKRecord.ParentCellIndex] as NKCellRecord; _logger.Debug( "Found possible parent key at absolute offset 0x{0:X} for deleted key at absolute offset 0x{1:X}", deletedRegistryKey.Value.NKRecord.ParentCellIndex + 0x1000, deletedRegistryKey.Value.NKRecord.AbsoluteOffset); if (parentNk == null) { //the data at that index is not an nkrecord continue; } if (parentNk.IsReferenced && parentNk.IsFree == false) { //parent exists in our primary tree, so get that key var pk = GetKey(deletedRegistryKey.Value.NKRecord.ParentCellIndex); _logger.Debug( "Copying subkey at absolute offset 0x{0:X} for parent key at absolute offset 0x{1:X}", deletedRegistryKey.Value.NKRecord.AbsoluteOffset, pk.NKRecord.AbsoluteOffset); deletedRegistryKey.Value.KeyPath = $@"{pk.KeyPath}\{deletedRegistryKey.Value.KeyName}"; deletedRegistryKey.Value.KeyFlags |= RegistryKey.KeyFlagsEnum.HasActiveParent; UpdateChildPaths(deletedRegistryKey.Value); //add a copy of deletedRegistryKey under its original parent pk.SubKeys.Add(deletedRegistryKey.Value); RelativeOffsetKeyMap.Add(deletedRegistryKey.Value.NKRecord.RelativeOffset, deletedRegistryKey.Value); if (KeyPathKeyMap.ContainsKey(deletedRegistryKey.Value.KeyPath.ToLowerInvariant()) == false) { KeyPathKeyMap.Add(deletedRegistryKey.Value.KeyPath.ToLowerInvariant(), deletedRegistryKey.Value); } _logger.Debug( "Associated deleted key at absolute offset 0x{0:X} to active parent key at absolute offset 0x{1:X}", deletedRegistryKey.Value.NKRecord.AbsoluteOffset, pk.NKRecord.AbsoluteOffset); } } } DeletedRegistryKeys = _deletedRegistryKeys.Values.ToList(); var unreferencedVk = CellRecords.Where(t => t.Value.IsReferenced == false && t.Value is VKCellRecord); foreach (var keyValuePair in unreferencedVk) { if (associatedVKRecordOffsets.Contains(keyValuePair.Key) == false) { var vk = keyValuePair.Value as VKCellRecord; var val = new KeyValue(vk); UnassociatedRegistryValues.Add(val); } } }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); var valuesList = new List<ValuesOut>(); var currentKey = string.Empty; try { foreach (var registryKey in key.SubKeys) { currentKey = registryKey.KeyName; //get MRU key and read it in var mruVal = registryKey.Values.SingleOrDefault(t => t.ValueName == "MRUListEx"); var mruListOrder = new ArrayList(); if (mruVal != null) { var index = 0; var mruPos = 0; while (index < mruVal.ValueDataRaw.Length) { mruPos = BitConverter.ToInt32(mruVal.ValueDataRaw, index); index += 4; if (mruPos != -1) { mruListOrder.Add(mruPos); } } } foreach (var keyValue in registryKey.Values) { if (keyValue.ValueName == "MRUListEx") { continue; } bags = new List<ShellBag>(); var shellItemsRaw = new List<byte[]>(); var mru = (int) mruListOrder[int.Parse(keyValue.ValueName)]; DateTimeOffset? openedOn = null; if (mru == 0) { openedOn = registryKey.LastWriteTime; } try { var det = new StringBuilder(); var index = 0; while (index < keyValue.ValueDataRaw.Length) { var size = BitConverter.ToInt16(keyValue.ValueDataRaw, index); if (size == 0) { break; } var shellRaw = new byte[size]; Buffer.BlockCopy(keyValue.ValueDataRaw, index, shellRaw, 0, size); shellItemsRaw.Add(shellRaw); index += size; } ShellBag bag = null; foreach (var bytese in shellItemsRaw) { switch (bytese[2]) { case 0x00: bag = new ShellBag0X00(bytese); break; case 0x1f: bag = new ShellBag0X1F(bytese); break; case 0x2f: bag = new ShellBag0X2F(bytese); break; case 0x2e: bag = new ShellBag0X2E(bytese); break; case 0xb1: case 0x31: bag = new ShellBag0X31(bytese); break; case 0x32: bag = new ShellBag0X32(bytese); break; case 0x71: bag = new ShellBag0X71(bytese); break; case 0x74: bag = new ShellBag0X74(bytese); break; case 0x40: bag = new ShellBag0X40(bytese); break; case 0x61: bag = new ShellBag0X61(bytese); break; case 0xc3: bag = new ShellBag0Xc3(bytese); break; default: det.AppendLine( $"Key: {registryKey.KeyName}, Value name: {keyValue.ValueName}, Message: **** Unsupported ShellID: 0x{bytese[2]:x2}. Send this ID to [email protected] so support can be added!! ****"); Errors.Add( $"Key: {registryKey.KeyName}, Value name: {keyValue.ValueName}, Message: **** Unsupported ShellID: 0x{bytese[2]:x2}. Send this ID to [email protected] so support can be added!! ****"); break; } if (bag != null) { det.AppendLine(bag.ToString()); bags.Add(bag); } } var v = new ValuesOut(registryKey.KeyName, $"{GetAbsolutePathFromTargetIDs(bags)}", det.ToString(), keyValue.ValueName, mru, openedOn); valuesList.Add(v); } catch (Exception ex) { Errors.Add( $"Key: {registryKey.KeyName}, Value name: {keyValue.ValueName}, message: {ex.Message}"); } } } } catch (Exception ex) { Errors.Add($"Error processing OpenSavePidlMRU subkey {currentKey}: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } var v1 = valuesList.OrderBy(t => t.MruPosition); foreach (var source in v1.ToList()) { _values.Add(source); } }
// 指定キー内のName/Valueを書き出す private static void WriteValueData(RegistryKey key, StreamWriter sw, string filepath) { foreach (var keyValue in key.Values) { // var slack = ""; // if (keyValue.ValueSlack.Length > 0) // slack = keyValue.ValueSlack; if(keyValue.ValueData.Length > 1024) sw.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", filepath, Helpers.StripRootKeyNameFromKeyPath(key.KeyPath), keyValue.ValueName, $"(Large Data: {keyValue.ValueDataRaw.Length} bytes", key.LastWriteTime.Value.LocalDateTime, key.LastWriteTime.Value.UtcDateTime); else sw.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", filepath, Helpers.StripRootKeyNameFromKeyPath(key.KeyPath), keyValue.ValueName, keyValue.ValueData, key.LastWriteTime.Value.LocalDateTime, key.LastWriteTime.Value.UtcDateTime); } }
private List<RegistryKey> GetSubkeys(uint subkeyListsStableCellIndex, RegistryKey parent) { var keys = new List<RegistryKey>(); _logger.Debug("Looking for list record at relative offset 0x{0:X}", subkeyListsStableCellIndex); var rawList = GetRawRecord(subkeyListsStableCellIndex); var l = GetListFromRawBytes(rawList, subkeyListsStableCellIndex); var sig = BitConverter.ToInt16(l.RawBytes, 4); switch (sig) { case LfSignature: case LhSignature: var lxRecord = l as LxListRecord; foreach (var offset in lxRecord.Offsets) { _logger.Debug("In lf or lh, looking for nk record at relative offset 0x{0:X}", offset); var rawCell = GetRawRecord(offset.Key); var nk = new NKCellRecord(rawCell.Length, offset.Key, this); _logger.Debug("In lf or lh, found nk record at relative offset 0x{0:X}. Name: {1}", offset, nk.Name); var tempKey = new RegistryKey(nk, parent); keys.Add(tempKey); } break; case RiSignature: var riRecord = l as RIListRecord; foreach (var offset in riRecord.Offsets) { _logger.Debug("In ri, looking for list record at relative offset 0x{0:X}", offset); rawList = GetRawRecord(offset); var tempList = GetListFromRawBytes(rawList, offset); //templist is now an li or lh list if (tempList.Signature == "li") { var sk3 = tempList as LIListRecord; foreach (var offset1 in sk3.Offsets) { _logger.Debug("In ri/li, looking for nk record at relative offset 0x{0:X}", offset1); var rawCell = GetRawRecord(offset1); var nk = new NKCellRecord(rawCell.Length, offset1, this); var tempKey = new RegistryKey(nk, parent); keys.Add(tempKey); } } else { var lxRecord_ = tempList as LxListRecord; foreach (var offset3 in lxRecord_.Offsets) { _logger.Debug("In ri/li, looking for nk record at relative offset 0x{0:X}", offset3); var rawCell = GetRawRecord(offset3.Key); var nk = new NKCellRecord(rawCell.Length, offset3.Key, this); var tempKey = new RegistryKey(nk, parent); keys.Add(tempKey); } } } break; //this is a safety net, but li's are typically only seen in RI lists. as such, don't use it in metrics case LiSignature: var liRecord = l as LIListRecord; foreach (var offset in liRecord.Offsets) { _logger.Debug("In li, looking for nk record at relative offset 0x{0:X}", offset); var rawCell = GetRawRecord(offset); var nk = new NKCellRecord(rawCell.Length, offset, this); var tempKey = new RegistryKey(nk, parent); keys.Add(tempKey); } break; default: throw new Exception($"Unknown subkey list type {l.Signature}!"); } return keys; }
private IEnumerable<FolderInfo> ProcessKey(RegistryKey key) { var l = new List<FolderInfo>(); try { var mruList = key.Values.Single(t => t.ValueName == "MRUListEx"); var mruPositions = new Dictionary<uint, int>(); var i = 0; var index = 0; var mruPos = BitConverter.ToUInt32(mruList.ValueDataRaw, index); index += 4; while (mruPos != 0xFFFFFFFF) { mruPositions.Add(mruPos, i); i++; mruPos = BitConverter.ToUInt32(mruList.ValueDataRaw, index); index += 4; } //mruPositions now contains a map of positions (the key) to the order it was opened (the value) foreach (var keyValue in key.Values) { if (keyValue.ValueName == "MRUListEx") { continue; } var mru = mruPositions[uint.Parse(keyValue.ValueName)]; var chunks = Encoding.Unicode.GetString(keyValue.ValueDataRaw).Split('\0'); var exeName = chunks[0]; var folder = string.Empty; if (chunks.Length > 1) { folder = chunks[1]; } DateTimeOffset? openedOn = null; if (mru == 0) { openedOn = key.LastWriteTime; } var ff = new FolderInfo(exeName, folder, mru, openedOn); l.Add(ff); } } catch (Exception ex) { Errors.Add($"Error processing FirstFolder key: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } return l.OrderBy(t => t.MRUPosition); }
public void ProcessValues(RegistryKey key) { _values.Clear(); Errors.Clear(); try { var networkDHTID = key.Values.SingleOrDefault(t => t.ValueName == "Network.DHTID"); if (networkDHTID != null) { var dh = networkDHTID.ValueData.Replace("-", ""); var v = new ValuesOut($"Ares Network DHTID", dh); _values.Add(v); } var dlFolderVal = key.Values.SingleOrDefault(t => t.ValueName == "Download.Folder"); if (dlFolderVal != null) { var dlf = DecodeHexToAscii(dlFolderVal.ValueData); var v = new ValuesOut($"Download folder", dlf); _values.Add(v); } var customMshVal = key.Values.SingleOrDefault(t => t.ValueName == "Personal.CustomMessage"); if (customMshVal != null) { var customMsg = customMshVal.ValueData; var v = new ValuesOut($"Custom message", customMsg); _values.Add(v); } var nickVal = key.Values.SingleOrDefault(t => t.ValueName == "Personal.Nickname"); if (nickVal != null) { var customMsg = DecodeHexToAscii(nickVal.ValueData); var v = new ValuesOut($"User nickname", customMsg); _values.Add(v); } var awayMsgVal = key.Values.SingleOrDefault(t => t.ValueName == "PrivateMessage.AwayMessage"); if (awayMsgVal != null) { if ( awayMsgVal.ValueData.Equals( "5468697320697320616E206175746F6D617469632061776179206D6573736167652067656E65726174656420627920417265732070726F6772616D2C20757365722069736E27742068657265206E6F772E") == false) { //user has changed default var customMsg = DecodeHexToAscii(awayMsgVal.ValueData); var v = new ValuesOut($"Away message", customMsg); _values.Add(v); } } var lastConnectedVal = key.Values.SingleOrDefault(t => t.ValueName == "Stats.LstConnect"); if (lastConnectedVal != null) { var lastConnect = DateTimeOffset.FromUnixTimeSeconds(int.Parse(lastConnectedVal.ValueData)); var v = new ValuesOut($"Last connection time", lastConnect.ToUniversalTime().ToString()); _values.Add(v); } var portVal = key.Values.SingleOrDefault(t => t.ValueName == "Transfer.ServerPort"); if (portVal != null) { var portNum = int.Parse(portVal.ValueData); if (portNum > 0) { var v = new ValuesOut($"Port number", portNum.ToString()); _values.Add(v); } } var guidVal = key.Values.SingleOrDefault(t => t.ValueName == "Personal.GUID"); if (guidVal != null) { var v = new ValuesOut($"Personal GUID", guidVal.ValueData); _values.Add(v); } var searchKey = key.SubKeys.SingleOrDefault(t => t.KeyName == "Search.History"); if (searchKey != null) { foreach (var registryKey in searchKey.SubKeys) { if (registryKey.Values.Count == 0) { continue; } var terms = new List<string>(); foreach (var keyValue in registryKey.Values) { try { var st = DecodeHexToAscii(keyValue.ValueName); terms.Add(st); } catch (Exception ex) { Errors.Add( $"Key: {registryKey.KeyName}, Value name: {keyValue.ValueName}, message: {ex.Message}"); } } var searchType = registryKey.KeyName.Substring(0, registryKey.KeyName.Length - 4); if (searchType == "gen") { searchType = "all"; } var v = new ValuesOut($"Search history for '{searchType}'", string.Join(", ", terms)); _values.Add(v); } } } catch (Exception ex) { Errors.Add($"Error processing Ares search history: {ex.Message}"); } if (Errors.Count > 0) { AlertMessage = "Errors detected. See Errors information in lower right corner of plugin window"; } }
private static void DumpKey(RegistryKey key, bool recursive) { if (recursive) { _logger.Info(key); } else { _logger.Info($"Key: {Helpers.StripRootKeyNameFromKeyPath(key.KeyPath)}"); _logger.Info($"Last write time: {key.LastWriteTime}"); _logger.Info($"Number of Values: {key.Values.Count:N0}"); _logger.Info($"Number of Subkeys: {key.SubKeys.Count:N0}"); _logger.Info(""); var i = 0; foreach (var sk in key.SubKeys) { _logger.Info($"------------ Subkey #{i:N0} ------------"); _logger.Info($"Name: {sk.KeyName} (Last write: {sk.LastWriteTime})"); i += 1; } i = 0; _logger.Info(""); foreach (var keyValue in key.Values) { _logger.Info($"------------ Value #{i:N0} ------------"); _logger.Info($"Name: {keyValue.ValueName} ({keyValue.ValueType})"); var slack = ""; if (keyValue.ValueSlack.Length > 0) { slack = $"(Slack: {keyValue.ValueSlack})"; } _logger.Info($"Data: {keyValue.ValueData} {slack}"); i += 1; } } }
// 指定キーのサブキーに対してName/Valueを書き出す private static void WriteSubKeyData(RegistryKey key, StreamWriter sw, string filepath) { if (key.SubKeys.Count > 0) { foreach (var sk in key.SubKeys) { if (sk.Values.Count > 0) WriteValueData(sk, sw, filepath); else // サブキー配下にエントリが1つもない場合はキーとタイムスタンプのみ書き出す sw.WriteLine("{0}\t{1}\t\t\t{2}\t{3}", filepath, Helpers.StripRootKeyNameFromKeyPath(key.KeyPath), sk.LastWriteTime.Value.LocalDateTime, sk.LastWriteTime.Value.UtcDateTime); } } }
/// <summary> /// Exports contents of Registry to text format. /// </summary> /// <remarks>Be sure to set FlushRecordListsAfterParse to FALSE if you want deleted records included</remarks> /// <param name="outfile">The outfile.</param> /// <param name="deletedOnly">if set to <c>true</c> [deleted only].</param> public void ExportDataToCommonFormat(string outfile, bool deletedOnly) { var KeyCount = 0; //root key var ValueCount = 0; var KeyCountDeleted = 0; var ValueCountDeleted = 0; var header = new StringBuilder(); header.AppendLine("## Registry common export format"); header.AppendLine("## Key format"); header.AppendLine( "## key|Is Free (A for in use, U for unused)|Absolute offset in decimal|KeyPath|LastWriteTime in UTC"); header.AppendLine("## Value format"); header.AppendLine( "## value|Is Free (A for in use, U for unused)|Absolute offset in decimal|KeyPath|Value name|Data type (as decimal integer)|Value data as bytes separated by a singe space"); header.AppendLine("##"); header.AppendLine( "## Comparison of deleted keys/values is done to compare recovery of vk and nk records, not the algorithm used to associate deleted keys to other keys and their values."); header.AppendLine( "## When including deleted keys, only the recovered key name should be included, not the full path to the deleted key."); header.AppendLine("## When including deleted values, do not include the parent key information."); header.AppendLine("##"); header.AppendLine("## The following totals should also be included"); header.AppendLine("##"); header.AppendLine("## total_keys|total in use key count"); header.AppendLine("## total_values|total in use value count"); header.AppendLine("## total_deleted_keys|total recovered free key count"); header.AppendLine("## total_deleted_values|total recovered free value count"); header.AppendLine("##"); header.AppendLine( "## Before comparison with other common export implementations, the files should be sorted"); header.AppendLine("##"); using (var sw = new StreamWriter(outfile, false)) { sw.AutoFlush = true; sw.Write(header.ToString()); if (deletedOnly == false) { //dump active stuff if (Root.LastWriteTime != null) { KeyCount = 1; sw.WriteLine("key|{0}|{1}|{2}|{3}", Root.NKRecord.IsFree ? "U" : "A", Root.NKRecord.AbsoluteOffset, Root.KeyPath, Root.LastWriteTime.Value.UtcDateTime.ToString("o")); } foreach (var val in Root.Values) { ValueCount += 1; sw.WriteLine(@"value|{0}|{1}|{2}|{3}|{4}|{5}", val.VKRecord.IsFree ? "U" : "A", val.VKRecord.AbsoluteOffset, Root.KeyPath, val.ValueName, (int) val.VKRecord.DataType, BitConverter.ToString(val.VKRecord.ValueDataRaw).Replace("-", " ")); } DumpKeyCommonFormat(Root, sw, ref KeyCount, ref ValueCount); } var theRest = CellRecords.Where(a => a.Value.IsReferenced == false); //may not need to if we do not care about orphaned values foreach (var keyValuePair in theRest) { try { if (keyValuePair.Value.Signature == "vk") { ValueCountDeleted += 1; var val = keyValuePair.Value as VKCellRecord; sw.WriteLine(@"value|{0}|{1}|{2}|{3}|{4}|{5}", val.IsFree ? "U" : "A", val.AbsoluteOffset, "", val.ValueName, (int) val.DataType, BitConverter.ToString(val.ValueDataRaw).Replace("-", " ")); } if (keyValuePair.Value.Signature == "nk") { //this should never be once we re-enable deleted key rebuilding KeyCountDeleted += 1; var nk = keyValuePair.Value as NKCellRecord; var key = new RegistryKey(nk, null); sw.WriteLine("key|{0}|{1}|{2}|{3}", key.NKRecord.IsFree ? "U" : "A", key.NKRecord.AbsoluteOffset, key.KeyName, key.LastWriteTime.Value.UtcDateTime.ToString("o")); DumpKeyCommonFormat(key, sw, ref KeyCountDeleted, ref ValueCountDeleted); } } catch (Exception ex) { _logger.Warn("There was an error exporting free record at offset 0x{0:X}. Error: {1}", keyValuePair.Value.AbsoluteOffset, ex.Message); } } sw.WriteLine("total_keys|{0}", KeyCount); sw.WriteLine("total_values|{0}", ValueCount); sw.WriteLine("total_deleted_keys|{0}", KeyCountDeleted); sw.WriteLine("total_deleted_values|{0}", ValueCountDeleted); } }
// 指定キーのサブキーに対して再帰的にName/Valueを書き出す private static void WriteSpecificKeyInfo(RegistryKey key, StreamWriter sw, string filepath) { // 指定キー配下にエントリがある場合はそのエントリ分の処理をする if (key.Values.Count > 0) WriteValueData(key, sw, filepath); // 指定キー配下にサブキーがある場合は再帰的に処理をする if (key.SubKeys.Count > 0) { foreach (var sk in key.SubKeys) WriteSubKeyData(sk, sw, filepath); } }
public SearchHit(RegistryKey key, KeyValue value, string hitstring) { Key = key; Value = value; HitString = hitstring; }