private IEnumerable <RecentDoc> ProcessRecentKey(RegistryKey key) { var l = new List <RecentDoc>(); try { var mruList = key.Values.SingleOrDefault(t => t.ValueName == "MRUListEx"); var mruPositions = new Dictionary <uint, int>(); var index = 0; if (mruList != null) { var i = 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.LongName, key.KeyName, openedOn, extLastOpened); rd.BatchKeyPath = key.KeyPath; rd.BatchValueName = keyValue.ValueName; 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); }
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; }