// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // namespace DiscUtils.Registry { using System; using System.Collections.Generic; using System.Security.AccessControl; using System.Text; /// /// A key within a registry hive. /// public sealed class RegistryKey { private RegistryHive _hive; private KeyNodeCell _cell; internal RegistryKey(RegistryHive hive, KeyNodeCell cell) { _hive = hive; _cell = cell; } /// /// Gets the name of this key. /// public string Name { get { RegistryKey parent = Parent; if (parent != null && ((parent.Flags & RegistryKeyFlags.Root) == 0)) { return parent.Name + @"\" + _cell.Name; } else { return _cell.Name; } } } /// /// Gets the number of child keys. /// public int SubKeyCount { get { return _cell.NumSubKeys; } } /// /// Gets the number of values in this key. /// public int ValueCount { get { return _cell.NumValues; } } /// /// Gets the time the key was last modified. /// public DateTime Timestamp { get { return _cell.Timestamp; } } /// /// Gets the parent key, or null if this is the root key. /// public RegistryKey Parent { get { if ((_cell.Flags & RegistryKeyFlags.Root) == 0) { return new RegistryKey(_hive, _hive.GetCell(_cell.ParentIndex)); } else { return null; } } } /// /// Gets the flags of this registry key. /// public RegistryKeyFlags Flags { get { return _cell.Flags; } } /// /// Gets the class name of this registry key. /// /// Class name is rarely used. public string ClassName { get { if (_cell.ClassNameIndex > 0) { return Encoding.Unicode.GetString(_hive.RawCellData(_cell.ClassNameIndex, _cell.ClassNameLength)); } return null; } } /// /// Gets an enumerator over all sub child keys. /// public IEnumerable SubKeys { get { if (_cell.NumSubKeys != 0) { ListCell list = _hive.GetCell(_cell.SubKeysIndex); foreach (var key in list.EnumerateKeys()) { yield return new RegistryKey(_hive, key); } } } } /// /// Gets an enumerator over all values in this key. /// private IEnumerable Values { get { if (_cell.NumValues != 0) { byte[] valueList = _hive.RawCellData(_cell.ValueListIndex, _cell.NumValues * 4); for (int i = 0; i < _cell.NumValues; ++i) { int valueIndex = Utilities.ToInt32LittleEndian(valueList, i * 4); yield return new RegistryValue(_hive, _hive.GetCell(valueIndex)); } } } } /// /// Gets the Security Descriptor applied to the registry key. /// /// The security descriptor as a RegistrySecurity instance. public RegistrySecurity GetAccessControl() { if (_cell.SecurityIndex > 0) { SecurityCell secCell = _hive.GetCell(_cell.SecurityIndex); return secCell.SecurityDescriptor; } return null; } /// /// Gets the names of all child sub keys. /// /// The names of the sub keys. public string[] GetSubKeyNames() { List names = new List(); if (_cell.NumSubKeys != 0) { _hive.GetCell(_cell.SubKeysIndex).EnumerateKeys(names); } return names.ToArray(); } /// /// Gets a named value stored within this key. /// /// The name of the value to retrieve. /// The value as a .NET object. /// The mapping from registry type of .NET type is as follows: /// /// /// Value Type /// .NET type /// /// /// String /// string /// /// /// ExpandString /// string /// /// /// Link /// string /// /// /// DWord /// uint /// /// /// DWordBigEndian /// uint /// /// /// MultiString /// string[] /// /// /// QWord /// ulong /// /// /// public object GetValue(string name) { return GetValue(name, null, Microsoft.Win32.RegistryValueOptions.None); } /// /// Gets a named value stored within this key. /// /// The name of the value to retrieve. /// The default value to return, if no existing value is stored. /// The value as a .NET object. /// The mapping from registry type of .NET type is as follows: /// /// /// Value Type /// .NET type /// /// /// String /// string /// /// /// ExpandString /// string /// /// /// Link /// string /// /// /// DWord /// uint /// /// /// DWordBigEndian /// uint /// /// /// MultiString /// string[] /// /// /// QWord /// ulong /// /// /// public object GetValue(string name, object defaultValue) { return GetValue(name, defaultValue, Microsoft.Win32.RegistryValueOptions.None); } /// /// Gets a named value stored within this key. /// /// The name of the value to retrieve. /// The default value to return, if no existing value is stored. /// Flags controlling how the value is processed before it's returned. /// The value as a .NET object. /// The mapping from registry type of .NET type is as follows: /// /// /// Value Type /// .NET type /// /// /// String /// string /// /// /// ExpandString /// string /// /// /// Link /// string /// /// /// DWord /// uint /// /// /// DWordBigEndian /// uint /// /// /// MultiString /// string[] /// /// /// QWord /// ulong /// /// /// public object GetValue(string name, object defaultValue, Microsoft.Win32.RegistryValueOptions options) { RegistryValue regVal = GetRegistryValue(name); if (regVal != null) { if (regVal.DataType == RegistryValueType.ExpandString && (options & Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames) == 0) { return Environment.ExpandEnvironmentVariables((string)regVal.Value); } else { return regVal.Value; } } return defaultValue; } /// /// Sets a named value stored within this key. /// /// The name of the value to store. /// The value to store. public void SetValue(string name, object value) { SetValue(name, value, RegistryValueType.None); } /// /// Sets a named value stored within this key. /// /// The name of the value to store. /// The value to store. /// The registry type of the data. public void SetValue(string name, object value, RegistryValueType valueType) { RegistryValue valObj = GetRegistryValue(name); if (valObj == null) { valObj = AddRegistryValue(name); } valObj.SetValue(value, valueType); } /// /// Deletes a named value stored within this key. /// /// The name of the value to delete. public void DeleteValue(string name) { DeleteValue(name, true); } /// /// Deletes a named value stored within this key. /// /// The name of the value to delete. /// Throws ArgumentException if name doesn't exist. public void DeleteValue(string name, bool throwOnMissingValue) { bool foundValue = false; if (_cell.NumValues != 0) { byte[] valueList = _hive.RawCellData(_cell.ValueListIndex, _cell.NumValues * 4); int i = 0; while (i < _cell.NumValues) { int valueIndex = Utilities.ToInt32LittleEndian(valueList, i * 4); ValueCell valueCell = _hive.GetCell(valueIndex); if (string.Compare(valueCell.Name, name, StringComparison.OrdinalIgnoreCase) == 0) { foundValue = true; _hive.FreeCell(valueIndex); _cell.NumValues--; _hive.UpdateCell(_cell, false); break; } ++i; } // Move following value's to fill gap if (i < _cell.NumValues) { while (i < _cell.NumValues) { int valueIndex = Utilities.ToInt32LittleEndian(valueList, (i + 1) * 4); Utilities.WriteBytesLittleEndian(valueIndex, valueList, i * 4); ++i; } _hive.WriteRawCellData(_cell.ValueListIndex, valueList, 0, _cell.NumValues * 4); } // TODO: Update maxbytes for value name and value content if this was the largest value for either. // Windows seems to repair this info, if not accurate, though. } if (throwOnMissingValue && !foundValue) { throw new ArgumentException("No such value: " + name, "name"); } } /// /// Gets the type of a named value. /// /// The name of the value to inspect. /// The value's type. public RegistryValueType GetValueType(string name) { RegistryValue regVal = GetRegistryValue(name); if (regVal != null) { return regVal.DataType; } return RegistryValueType.None; } /// /// Gets the names of all values in this key. /// /// An array of strings containing the value names. public string[] GetValueNames() { List names = new List(); foreach (var value in Values) { names.Add(value.Name); } return names.ToArray(); } /// /// Creates or opens a subkey. /// /// The relative path the the subkey. /// The subkey. public RegistryKey CreateSubKey(string subkey) { if (string.IsNullOrEmpty(subkey)) { return this; } string[] split = subkey.Split(new char[] { '\\' }, 2); int cellIndex = FindSubKeyCell(split[0]); if (cellIndex < 0) { KeyNodeCell newKeyCell = new KeyNodeCell(split[0], _cell.Index); newKeyCell.SecurityIndex = _cell.SecurityIndex; ReferenceSecurityCell(newKeyCell.SecurityIndex); _hive.UpdateCell(newKeyCell, true); LinkSubKey(split[0], newKeyCell.Index); if (split.Length == 1) { return new RegistryKey(_hive, newKeyCell); } else { return new RegistryKey(_hive, newKeyCell).CreateSubKey(split[1]); } } else { KeyNodeCell cell = _hive.GetCell(cellIndex); if (split.Length == 1) { return new RegistryKey(_hive, cell); } else { return new RegistryKey(_hive, cell).CreateSubKey(split[1]); } } } /// /// Opens a sub key. /// /// The relative path to the sub key. /// The sub key, or null if not found. public RegistryKey OpenSubKey(string path) { if (string.IsNullOrEmpty(path)) { return this; } string[] split = path.Split(new char[] { '\\' }, 2); int cellIndex = FindSubKeyCell(split[0]); if (cellIndex < 0) { return null; } else { KeyNodeCell cell = _hive.GetCell(cellIndex); if (split.Length == 1) { return new RegistryKey(_hive, cell); } else { return new RegistryKey(_hive, cell).OpenSubKey(split[1]); } } } /// /// Deletes a subkey and any child subkeys recursively. The string subkey is not case-sensitive. /// /// The subkey to delete. public void DeleteSubKeyTree(string subkey) { RegistryKey subKeyObj = OpenSubKey(subkey); if (subKeyObj == null) { return; } if ((subKeyObj.Flags & RegistryKeyFlags.Root) != 0) { throw new ArgumentException("Attempt to delete root key"); } foreach (var child in subKeyObj.GetSubKeyNames()) { subKeyObj.DeleteSubKeyTree(child); } DeleteSubKey(subkey); } /// /// Deletes the specified subkey. The string subkey is not case-sensitive. /// /// The subkey to delete. public void DeleteSubKey(string subkey) { DeleteSubKey(subkey, true); } /// /// Deletes the specified subkey. The string subkey is not case-sensitive. /// /// The subkey to delete. /// true to throw an argument exception if subkey doesn't exist. public void DeleteSubKey(string subkey, bool throwOnMissingSubKey) { if (string.IsNullOrEmpty(subkey)) { throw new ArgumentException("Invalid SubKey", "subkey"); } string[] split = subkey.Split(new char[] { '\\' }, 2); int subkeyCellIndex = FindSubKeyCell(split[0]); if (subkeyCellIndex < 0) { if (throwOnMissingSubKey) { throw new ArgumentException("No such SubKey", "subkey"); } else { return; } } KeyNodeCell subkeyCell = _hive.GetCell(subkeyCellIndex); if (split.Length == 1) { if (subkeyCell.NumSubKeys != 0) { throw new InvalidOperationException("The registry key has subkeys"); } if (subkeyCell.ClassNameIndex != -1) { _hive.FreeCell(subkeyCell.ClassNameIndex); subkeyCell.ClassNameIndex = -1; subkeyCell.ClassNameLength = 0; } if (subkeyCell.SecurityIndex != -1) { DereferenceSecurityCell(subkeyCell.SecurityIndex); subkeyCell.SecurityIndex = -1; } if (subkeyCell.SubKeysIndex != -1) { FreeSubKeys(subkeyCell); } if (subkeyCell.ValueListIndex != -1) { FreeValues(subkeyCell); } UnlinkSubKey(subkey); _hive.FreeCell(subkeyCellIndex); _hive.UpdateCell(_cell, false); } else { new RegistryKey(_hive, subkeyCell).DeleteSubKey(split[1], throwOnMissingSubKey); } } private RegistryValue GetRegistryValue(string name) { if (name != null && name.Length == 0) { name = null; } if (_cell.NumValues != 0) { byte[] valueList = _hive.RawCellData(_cell.ValueListIndex, _cell.NumValues * 4); for (int i = 0; i < _cell.NumValues; ++i) { int valueIndex = Utilities.ToInt32LittleEndian(valueList, i * 4); ValueCell cell = _hive.GetCell(valueIndex); if (string.Compare(cell.Name, name, StringComparison.OrdinalIgnoreCase) == 0) { return new RegistryValue(_hive, cell); } } } return null; } private RegistryValue AddRegistryValue(string name) { byte[] valueList = _hive.RawCellData(_cell.ValueListIndex, _cell.NumValues * 4); if (valueList == null) { valueList = new byte[0]; } int insertIdx = 0; while (insertIdx < _cell.NumValues) { int valueCellIndex = Utilities.ToInt32LittleEndian(valueList, insertIdx * 4); ValueCell cell = _hive.GetCell(valueCellIndex); if (string.Compare(name, cell.Name, StringComparison.OrdinalIgnoreCase) < 0) { break; } ++insertIdx; } // Allocate a new value cell (note _hive.UpdateCell does actual allocation). ValueCell valueCell = new ValueCell(name); _hive.UpdateCell(valueCell, true); // Update the value list, re-allocating if necessary byte[] newValueList = new byte[(_cell.NumValues * 4) + 4]; Array.Copy(valueList, 0, newValueList, 0, insertIdx * 4); Utilities.WriteBytesLittleEndian(valueCell.Index, newValueList, insertIdx * 4); Array.Copy(valueList, insertIdx * 4, newValueList, (insertIdx * 4) + 4, (_cell.NumValues - insertIdx) * 4); if (_cell.ValueListIndex == -1 || !_hive.WriteRawCellData(_cell.ValueListIndex, newValueList, 0, newValueList.Length)) { int newListCellIndex = _hive.AllocateRawCell(Utilities.RoundUp(newValueList.Length, 8)); _hive.WriteRawCellData(newListCellIndex, newValueList, 0, newValueList.Length); if (_cell.ValueListIndex != -1) { _hive.FreeCell(_cell.ValueListIndex); } _cell.ValueListIndex = newListCellIndex; } // Record the new value and save this cell _cell.NumValues++; _hive.UpdateCell(_cell, false); // Finally, set the data in the value cell return new RegistryValue(_hive, valueCell); } private int FindSubKeyCell(string name) { if (_cell.NumSubKeys != 0) { ListCell listCell = _hive.GetCell(_cell.SubKeysIndex); int cellIndex; if (listCell.FindKey(name, out cellIndex) == 0) { return cellIndex; } } return -1; } private void LinkSubKey(string name, int cellIndex) { if (_cell.SubKeysIndex == -1) { SubKeyHashedListCell newListCell = new SubKeyHashedListCell(_hive, "lf"); newListCell.Add(name, cellIndex); _hive.UpdateCell(newListCell, true); _cell.NumSubKeys = 1; _cell.SubKeysIndex = newListCell.Index; } else { ListCell list = _hive.GetCell(_cell.SubKeysIndex); _cell.SubKeysIndex = list.LinkSubKey(name, cellIndex); _cell.NumSubKeys++; } _hive.UpdateCell(_cell, false); } private void UnlinkSubKey(string name) { if (_cell.SubKeysIndex == -1 || _cell.NumSubKeys == 0) { throw new InvalidOperationException("No subkey list"); } ListCell list = _hive.GetCell(_cell.SubKeysIndex); _cell.SubKeysIndex = list.UnlinkSubKey(name); _cell.NumSubKeys--; } private void ReferenceSecurityCell(int cellIndex) { SecurityCell sc = _hive.GetCell(cellIndex); sc.UsageCount++; _hive.UpdateCell(sc, false); } private void DereferenceSecurityCell(int cellIndex) { SecurityCell sc = _hive.GetCell(cellIndex); sc.UsageCount--; if (sc.UsageCount == 0) { SecurityCell prev = _hive.GetCell(sc.PreviousIndex); prev.NextIndex = sc.NextIndex; _hive.UpdateCell(prev, false); SecurityCell next = _hive.GetCell(sc.NextIndex); next.PreviousIndex = sc.PreviousIndex; _hive.UpdateCell(next, false); _hive.FreeCell(cellIndex); } else { _hive.UpdateCell(sc, false); } } private void FreeValues(KeyNodeCell cell) { if (cell.NumValues != 0 && cell.ValueListIndex != -1) { byte[] valueList = _hive.RawCellData(cell.ValueListIndex, cell.NumValues * 4); for (int i = 0; i < cell.NumValues; ++i) { int valueIndex = Utilities.ToInt32LittleEndian(valueList, i * 4); _hive.FreeCell(valueIndex); } _hive.FreeCell(cell.ValueListIndex); cell.ValueListIndex = -1; cell.NumValues = 0; cell.MaxValDataBytes = 0; cell.MaxValNameBytes = 0; } } private void FreeSubKeys(KeyNodeCell subkeyCell) { if (subkeyCell.SubKeysIndex == -1) { throw new InvalidOperationException("No subkey list"); } Cell list = _hive.GetCell(subkeyCell.SubKeysIndex); SubKeyIndirectListCell indirectList = list as SubKeyIndirectListCell; if (indirectList != null) { ////foreach (int listIndex in indirectList.CellIndexes) for (int i = 0; i < indirectList.CellIndexes.Count; ++i) { int listIndex = indirectList.CellIndexes[i]; _hive.FreeCell(listIndex); } } _hive.FreeCell(list.Index); } } }