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, StringComparison.OrdinalIgnoreCase)) { 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 => string.Equals(r.KeyName, keyNames[i], StringComparison.OrdinalIgnoreCase)); 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.Trace("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); }
// 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; }
private List <RegistryKey> GetSubkeys(uint subkeyListsStableCellIndex, RegistryKey parent) { var keys = new List <RegistryKey>(); Logger.Trace("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.Trace("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.Trace("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.Trace("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 lxRecord1 = tempList as LxListRecord; foreach (var offset3 in lxRecord1.Offsets) { Logger.Trace("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 List <IRecordBase> ExtractRecordsFromSlack(byte[] remainingData, long relativeoffset) { var records = new List <IRecordBase>(); // if (remainingData.Length == 4064 && _registryHive.HivePath.Contains("DeletedBags")) // { // Debug.WriteLine(1); // } var offsetList2 = new List <int>(); byte[] raw = null; _registryHive.Logger.Trace("Looking for cell signatures at absolute offset 0x{0:X}", relativeoffset + 0x1000); for (var i = 0; i < remainingData.Length; i++) { if (remainingData[i] == 0x6b) //6b == k { if (remainingData[i - 1] == 0x6e || remainingData[i - 1] == 0x76) //6e = n, 76 = v { //if we are here we have a good signature, nk or vk //check what is before that to see if its 0x00 or 0xFF if (remainingData[i - 2] == 0x00 || remainingData[i - 2] == 0xFF) { //winner! since we initially hit on ZZ, subtract 5 to get to the beginning of the record, XX XX XX XX YY ZZ offsetList2.Add(i - 5); } } } } //offsetList2 now has offset of every record signature we are interested in foreach (var i in offsetList2) { try { var actualStart = i; var size = BitConverter.ToInt32(remainingData, actualStart); if (Math.Abs(size) <= 3 || remainingData.Length - actualStart < size) { //if its empty or the size is beyond the data that is left, move on continue; } raw = new ArraySegment <byte>(remainingData, actualStart, Math.Abs(size)).ToArray(); if (raw.Length < 6) { continue; } // since we need 4 bytes for the size and 2 for sig, if its smaller than 6, go to next one var sig2 = BitConverter.ToInt16(raw, 4); switch (sig2) { case NkSignature: if (raw.Length <= 0x30) { continue; } var nk = new NkCellRecord(raw.Length, relativeoffset + actualStart, _registryHive); if (nk.LastWriteTimestamp.Year > 1900) { _registryHive.Logger.Trace("Found nk record in slack at absolute offset 0x{0:X}", relativeoffset + actualStart + 0x1000); records.Add(nk); } break; case VkSignature: if (raw.Length < 0x18) { //cant have a record shorter than this, even when no name is present continue; } var vk = new VkCellRecord(raw.Length, relativeoffset + actualStart, _minorVersion, _registryHive); _registryHive.Logger.Trace("Found vk record in slack at absolute offset 0x{0:X}", relativeoffset + actualStart + 0x1000); records.Add(vk); break; } } catch (Exception ex) //ncrunch: no coverage { //ncrunch: no coverage // this is a corrupted/unusable record _registryHive.Logger.Debug( //ncrunch: no coverage ex, $"When recovering from slack at absolute offset 0x{relativeoffset + i + 0x1000:X8}, an error happened! raw Length: 0x{raw?.Length:x}"); RegistryHive.SoftParsingErrorsInternal += 1; //ncrunch: no coverage } //ncrunch: no coverage } return(records); }
public List <IRecordBase> Process() { var records = new List <IRecordBase>(); //additional cell data starts 32 bytes (0x20) in var offsetInHbin = 0x20; _registryHive.TotalBytesRead += 0x20; while (offsetInHbin < Size) { var recordSize = BitConverter.ToUInt32(_rawBytes, offsetInHbin); var readSize = (int)recordSize; if (!_recoverDeleted && readSize > 0) { //since we do not want to get deleted stuff, if the cell size > 0, its free, so skip it offsetInHbin += readSize; continue; } // if we get a negative number here the record is allocated, but we cant read negative bytes, so get absolute value readSize = Math.Abs(readSize); _registryHive.Logger.Trace( $"Getting rawRecord at hbin relative offset 0x{offsetInHbin:X} (Absolute offset: 0x{offsetInHbin + RelativeOffset + 0x1000:X}). readsize: {readSize}"); var rawRecord = new ArraySegment <byte>(_rawBytes, offsetInHbin, readSize).ToArray(); _registryHive.TotalBytesRead += readSize; var cellSignature = Encoding.ASCII.GetString(rawRecord, 4, 2); var cellSignature2 = BitConverter.ToInt16(rawRecord, 4); //ncrunch: no coverage start if (_registryHive.Logger.IsDebugEnabled) { var foundMatch = false; foundMatch = Regex.IsMatch(cellSignature, @"\A[a-z]{2}\z"); //only process records with 2 letter signatures. this avoids crazy output for data cells if (foundMatch) { _registryHive.Logger.Trace( $"Processing {cellSignature} record at hbin relative offset 0x{offsetInHbin:X} (Absolute offset: 0x{offsetInHbin + RelativeOffset + 0x1000:X})"); } else { _registryHive.Logger.Trace( $"Processing data record at hbin relative offset 0x{offsetInHbin:X} (Absolute offset: 0x{offsetInHbin + RelativeOffset + 0x1000:X})"); } } //ncrunch: no coverage end ICellTemplate cellRecord = null; IListTemplate listRecord = null; DataNode dataRecord = null; try { switch (cellSignature2) { case LfSignature: case LhSignature: listRecord = new LxListRecord(rawRecord, offsetInHbin + RelativeOffset); break; case LiSignature: listRecord = new LiListRecord(rawRecord, offsetInHbin + RelativeOffset); break; case RiSignature: listRecord = new RiListRecord(rawRecord, offsetInHbin + RelativeOffset); break; case DbSignature: listRecord = new DbListRecord(rawRecord, offsetInHbin + RelativeOffset); break; case LkSignature: cellRecord = new LkCellRecord(rawRecord, offsetInHbin + RelativeOffset); break; //ncrunch: no coverage case NkSignature: if (rawRecord.Length >= 0x30) // the minimum length for a recoverable record { cellRecord = new NkCellRecord(rawRecord.Length, offsetInHbin + RelativeOffset, _registryHive); } break; case SkSignature: if (rawRecord.Length >= 0x14) // the minimum length for a recoverable record { cellRecord = new SkCellRecord(rawRecord, offsetInHbin + RelativeOffset); } break; case VkSignature: if (rawRecord.Length >= 0x18) // the minimum length for a recoverable record { cellRecord = new VkCellRecord(rawRecord.Length, offsetInHbin + RelativeOffset, _minorVersion, _registryHive); } break; default: dataRecord = new DataNode(rawRecord, offsetInHbin + RelativeOffset); break; } } catch (Exception ex) { //check size and see if its free. if so, dont worry about it. too small to be of value, but store it somewhere else? var size = BitConverter.ToInt32(rawRecord, 0); if (size < 0) { //ncrunch: no coverage RegistryHive.HardParsingErrorsInternal += 1; //ncrunch: no coverage _registryHive.Logger.Error( //ncrunch: no coverage ex, $"Hard error processing record with cell signature {cellSignature} at Absolute Offset: 0x{offsetInHbin + RelativeOffset + 4096:X} with raw data: {BitConverter.ToString(rawRecord)}"); //TODO store it somewhere else as a placeholder if its in use. include relative offset and other critical stuff } //ncrunch: no coverage else { _registryHive.Logger.Warn( ex, $"Soft error processing record with cell signature {cellSignature} at Absolute Offset: 0x{offsetInHbin + RelativeOffset + 4096:X} with raw data: {BitConverter.ToString(rawRecord)}"); //This record is marked 'Free' so its not as important of an error RegistryHive.SoftParsingErrorsInternal += 1; } } List <IRecordBase> carvedRecords = null; if (cellRecord != null) { if (cellRecord.IsFree) { carvedRecords = ExtractRecordsFromSlack(cellRecord.RawBytes, cellRecord.RelativeOffset); } else { records.Add((IRecordBase)cellRecord); } } if (listRecord != null) { if (_recoverDeleted) { carvedRecords = ExtractRecordsFromSlack(listRecord.RawBytes, listRecord.RelativeOffset); } records.Add((IRecordBase)listRecord); } if (dataRecord != null && _recoverDeleted) { carvedRecords = ExtractRecordsFromSlack(dataRecord.RawBytes, dataRecord.RelativeOffset); } if (carvedRecords != null) { if (carvedRecords.Count > 0) { records.AddRange(carvedRecords); } } offsetInHbin += readSize; } _rawBytes = null; return(records); }