public ValuesOut addData(Registry.Abstractions.KeyValue data, RegistryKey subKey) { var ff = new ValuesOut(data?.ValueName) { BatchValueName = "Multiple", BatchKeyPath = subKey.KeyPath }; return(ff); }
private List<KeyValue> GetKeyValues(uint valueListCellIndex, uint valueListCount) { var values = new List<KeyValue>(); var offsets = new List<uint>(); if (valueListCellIndex > 0) { _logger.Debug("Getting value list offset at relative offset 0x{0:X}. Value count is {1:N0}", valueListCellIndex, valueListCount); var offsetList = GetDataNodeFromOffset(valueListCellIndex); for (var i = 0; i < 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); offsets.Add(os); } } if (offsets.Count != valueListCount) { //ncrunch: no coverage _logger.Warn("Value count mismatch! ValueListCount is {0:N0} but NKRecord.ValueOffsets.Count is {1:N0}", //ncrunch: no coverage valueListCount, offsets.Count); } //ncrunch: no coverage foreach (var valueOffset in offsets) { _logger.Debug("Looking for vk record at relative offset 0x{0:X}", valueOffset); var rawVK = GetRawRecord(valueOffset); var vk = new VKCellRecord(rawVK.Length, valueOffset, Header.MinorVersion, this); _logger.Debug("Found vk record at relative offset 0x{0:X}. Value name: {1}", valueOffset, vk.ValueName); var value = new KeyValue(vk); values.Add(value); } return values; }
public SearchHit(RegistryKey key, KeyValue value, string hitstring) { Key = key; Value = value; HitString = hitstring; }
public ValueBySizeInfo(RegistryKey key, KeyValue value) { Key = key; Value = value; }
/// <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); } } }
//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; }