mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-08-10 10:01:14 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// 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.IO;
|
||||
|
||||
/// <summary>
|
||||
/// An internal structure within registry files, bins are the major unit of allocation in a registry hive.
|
||||
/// </summary>
|
||||
/// <remarks>Bins are divided into multiple cells, that contain actual registry data.</remarks>
|
||||
internal sealed class Bin
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private Stream _fileStream;
|
||||
private long _streamPos;
|
||||
|
||||
private BinHeader _header;
|
||||
private byte[] _buffer;
|
||||
|
||||
private List<Range<int, int>> _freeCells;
|
||||
|
||||
public Bin(RegistryHive hive, Stream stream)
|
||||
{
|
||||
_hive = hive;
|
||||
_fileStream = stream;
|
||||
_streamPos = stream.Position;
|
||||
|
||||
stream.Position = _streamPos;
|
||||
byte[] buffer = Utilities.ReadFully(stream, 0x20);
|
||||
_header = new BinHeader();
|
||||
_header.ReadFrom(buffer, 0);
|
||||
|
||||
_fileStream.Position = _streamPos;
|
||||
_buffer = Utilities.ReadFully(_fileStream, _header.BinSize);
|
||||
|
||||
// Gather list of all free cells.
|
||||
_freeCells = new List<Range<int, int>>();
|
||||
int pos = 0x20;
|
||||
while (pos < _buffer.Length)
|
||||
{
|
||||
int size = Utilities.ToInt32LittleEndian(_buffer, pos);
|
||||
if (size > 0)
|
||||
{
|
||||
_freeCells.Add(new Range<int, int>(pos, size));
|
||||
}
|
||||
|
||||
pos += Math.Abs(size);
|
||||
}
|
||||
}
|
||||
|
||||
public Cell TryGetCell(int index)
|
||||
{
|
||||
int size = Utilities.ToInt32LittleEndian(_buffer, index - _header.FileOffset);
|
||||
if (size >= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Cell.Parse(_hive, index, _buffer, index + 4 - _header.FileOffset);
|
||||
}
|
||||
|
||||
public void FreeCell(int index)
|
||||
{
|
||||
int freeIndex = index - _header.FileOffset;
|
||||
|
||||
int len = Utilities.ToInt32LittleEndian(_buffer, freeIndex);
|
||||
if (len >= 0)
|
||||
{
|
||||
throw new ArgumentException("Attempt to free non-allocated cell");
|
||||
}
|
||||
|
||||
len = Math.Abs(len);
|
||||
|
||||
// If there's a free cell before this one, combine
|
||||
int i = 0;
|
||||
while (i < _freeCells.Count && _freeCells[i].Offset < freeIndex)
|
||||
{
|
||||
if (_freeCells[i].Offset + _freeCells[i].Count == freeIndex)
|
||||
{
|
||||
freeIndex = _freeCells[i].Offset;
|
||||
len += _freeCells[i].Count;
|
||||
_freeCells.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a free cell after this one, combine
|
||||
if (i < _freeCells.Count && _freeCells[i].Offset == freeIndex + len)
|
||||
{
|
||||
len += _freeCells[i].Count;
|
||||
_freeCells.RemoveAt(i);
|
||||
}
|
||||
|
||||
// Record the new free cell
|
||||
_freeCells.Insert(i, new Range<int, int>(freeIndex, len));
|
||||
|
||||
// Free cells are indicated by length > 0
|
||||
Utilities.WriteBytesLittleEndian(len, _buffer, freeIndex);
|
||||
|
||||
_fileStream.Position = _streamPos + freeIndex;
|
||||
_fileStream.Write(_buffer, freeIndex, 4);
|
||||
}
|
||||
|
||||
public bool UpdateCell(Cell cell)
|
||||
{
|
||||
int index = cell.Index - _header.FileOffset;
|
||||
int allocSize = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
|
||||
int newSize = cell.Size + 4;
|
||||
if (newSize > allocSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cell.WriteTo(_buffer, index + 4);
|
||||
|
||||
_fileStream.Position = _streamPos + index;
|
||||
_fileStream.Write(_buffer, index, newSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] ReadRawCellData(int cellIndex, int maxBytes)
|
||||
{
|
||||
int index = cellIndex - _header.FileOffset;
|
||||
int len = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
byte[] result = new byte[Math.Min(len - 4, maxBytes)];
|
||||
Array.Copy(_buffer, index + 4, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool WriteRawCellData(int cellIndex, byte[] data, int offset, int count)
|
||||
{
|
||||
int index = cellIndex - _header.FileOffset;
|
||||
int allocSize = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
|
||||
int newSize = count + 4;
|
||||
if (newSize > allocSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Array.Copy(data, offset, _buffer, index + 4, count);
|
||||
|
||||
_fileStream.Position = _streamPos + index;
|
||||
_fileStream.Write(_buffer, index, newSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal int AllocateCell(int size)
|
||||
{
|
||||
if (size < 8 || size % 8 != 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid cell size");
|
||||
}
|
||||
|
||||
// Very inefficient algorithm - will lead to fragmentation
|
||||
for (int i = 0; i < _freeCells.Count; ++i)
|
||||
{
|
||||
int result = _freeCells[i].Offset + _header.FileOffset;
|
||||
if (_freeCells[i].Count > size)
|
||||
{
|
||||
// Record the newly allocated cell
|
||||
Utilities.WriteBytesLittleEndian(-size, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
// Keep the remainder of the free buffer as unallocated
|
||||
_freeCells[i] = new Range<int, int>(_freeCells[i].Offset + size, _freeCells[i].Count - size);
|
||||
Utilities.WriteBytesLittleEndian(_freeCells[i].Count, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (_freeCells[i].Count == size)
|
||||
{
|
||||
// Record the whole of the free buffer as a newly allocated cell
|
||||
Utilities.WriteBytesLittleEndian(-size, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
_freeCells.RemoveAt(i);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// 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.IO;
|
||||
|
||||
internal sealed class BinHeader : IByteArraySerializable
|
||||
{
|
||||
public const int HeaderSize = 0x20;
|
||||
|
||||
public int FileOffset;
|
||||
public int BinSize;
|
||||
|
||||
private const uint Signature = 0x6E696268;
|
||||
|
||||
public BinHeader()
|
||||
{
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return HeaderSize; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
uint sig = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
|
||||
if (sig != Signature)
|
||||
{
|
||||
throw new IOException("Invalid signature for registry bin");
|
||||
}
|
||||
|
||||
FileOffset = Utilities.ToInt32LittleEndian(buffer, offset + 0x04);
|
||||
BinSize = Utilities.ToInt32LittleEndian(buffer, offset + 0x08);
|
||||
long unknown = Utilities.ToInt64LittleEndian(buffer, offset + 0x0C);
|
||||
long unknown1 = Utilities.ToInt64LittleEndian(buffer, offset + 0x14);
|
||||
int unknown2 = Utilities.ToInt32LittleEndian(buffer, offset + 0x1C);
|
||||
return HeaderSize;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Signature, buffer, offset + 0x00);
|
||||
Utilities.WriteBytesLittleEndian(FileOffset, buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(BinSize, buffer, offset + 0x08);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for the different kinds of cell present in a hive.
|
||||
/// </summary>
|
||||
internal abstract class Cell : IByteArraySerializable
|
||||
{
|
||||
private int _index;
|
||||
|
||||
public Cell(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get { return _index; }
|
||||
set { _index = value; }
|
||||
}
|
||||
|
||||
public abstract int Size
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract int ReadFrom(byte[] buffer, int offset);
|
||||
|
||||
public abstract void WriteTo(byte[] buffer, int offset);
|
||||
|
||||
internal static Cell Parse(RegistryHive hive, int index, byte[] buffer, int pos)
|
||||
{
|
||||
string type = Utilities.BytesToString(buffer, pos, 2);
|
||||
|
||||
Cell result = null;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case "nk":
|
||||
result = new KeyNodeCell(index);
|
||||
break;
|
||||
|
||||
case "sk":
|
||||
result = new SecurityCell(index);
|
||||
break;
|
||||
|
||||
case "vk":
|
||||
result = new ValueCell(index);
|
||||
break;
|
||||
|
||||
case "lh":
|
||||
case "lf":
|
||||
result = new SubKeyHashedListCell(hive, index);
|
||||
break;
|
||||
|
||||
case "li":
|
||||
case "ri":
|
||||
result = new SubKeyIndirectListCell(hive, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RegistryCorruptException("Unknown cell type '" + type + "'");
|
||||
}
|
||||
|
||||
result.ReadFrom(buffer, pos);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
|
||||
internal sealed class HiveHeader : IByteArraySerializable
|
||||
{
|
||||
public const int HeaderSize = 512;
|
||||
|
||||
public int Sequence1;
|
||||
public int Sequence2;
|
||||
public DateTime Timestamp;
|
||||
public int MajorVersion;
|
||||
public int MinorVersion;
|
||||
public int RootCell;
|
||||
public int Length;
|
||||
public uint Checksum;
|
||||
public string Path;
|
||||
public Guid Guid1;
|
||||
public Guid Guid2;
|
||||
|
||||
private const uint Signature = 0x66676572;
|
||||
|
||||
public HiveHeader()
|
||||
{
|
||||
Sequence1 = 1;
|
||||
Sequence2 = 1;
|
||||
Timestamp = DateTime.UtcNow;
|
||||
MajorVersion = 1;
|
||||
MinorVersion = 3;
|
||||
RootCell = -1;
|
||||
Path = string.Empty;
|
||||
Guid1 = Guid.NewGuid();
|
||||
Guid2 = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return HeaderSize; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
uint sig = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
|
||||
if (sig != Signature)
|
||||
{
|
||||
throw new IOException("Invalid signature for registry hive");
|
||||
}
|
||||
|
||||
Sequence1 = Utilities.ToInt32LittleEndian(buffer, offset + 0x0004);
|
||||
Sequence2 = Utilities.ToInt32LittleEndian(buffer, offset + 0x0008);
|
||||
|
||||
Timestamp = DateTime.FromFileTimeUtc(Utilities.ToInt64LittleEndian(buffer, offset + 0x000C));
|
||||
|
||||
MajorVersion = Utilities.ToInt32LittleEndian(buffer, 0x0014);
|
||||
MinorVersion = Utilities.ToInt32LittleEndian(buffer, 0x0018);
|
||||
|
||||
int isLog = Utilities.ToInt32LittleEndian(buffer, 0x001C);
|
||||
|
||||
RootCell = Utilities.ToInt32LittleEndian(buffer, 0x0024);
|
||||
Length = Utilities.ToInt32LittleEndian(buffer, 0x0028);
|
||||
|
||||
Path = Encoding.Unicode.GetString(buffer, 0x0030, 0x0040).Trim('\0');
|
||||
|
||||
Guid1 = Utilities.ToGuidLittleEndian(buffer, 0x0070);
|
||||
Guid2 = Utilities.ToGuidLittleEndian(buffer, 0x0094);
|
||||
|
||||
Checksum = Utilities.ToUInt32LittleEndian(buffer, 0x01FC);
|
||||
|
||||
if (Sequence1 != Sequence2)
|
||||
{
|
||||
throw new NotImplementedException("Support for replaying registry log file");
|
||||
}
|
||||
|
||||
if (Checksum != CalcChecksum(buffer, offset))
|
||||
{
|
||||
throw new IOException("Invalid checksum on registry file");
|
||||
}
|
||||
|
||||
return HeaderSize;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Signature, buffer, offset);
|
||||
Utilities.WriteBytesLittleEndian(Sequence1, buffer, offset + 0x0004);
|
||||
Utilities.WriteBytesLittleEndian(Sequence2, buffer, offset + 0x0008);
|
||||
Utilities.WriteBytesLittleEndian(Timestamp.ToFileTimeUtc(), buffer, offset + 0x000C);
|
||||
Utilities.WriteBytesLittleEndian(MajorVersion, buffer, offset + 0x0014);
|
||||
Utilities.WriteBytesLittleEndian(MinorVersion, buffer, offset + 0x0018);
|
||||
|
||||
Utilities.WriteBytesLittleEndian((uint)1, buffer, offset + 0x0020); // Unknown - seems to be '1'
|
||||
|
||||
Utilities.WriteBytesLittleEndian(RootCell, buffer, offset + 0x0024);
|
||||
Utilities.WriteBytesLittleEndian(Length, buffer, offset + 0x0028);
|
||||
|
||||
Encoding.Unicode.GetBytes(Path, 0, Path.Length, buffer, offset + 0x0030);
|
||||
Utilities.WriteBytesLittleEndian((ushort)0, buffer, offset + 0x0030 + (Path.Length * 2));
|
||||
|
||||
Utilities.WriteBytesLittleEndian(Guid1, buffer, offset + 0x0070);
|
||||
Utilities.WriteBytesLittleEndian(Guid2, buffer, offset + 0x0094);
|
||||
|
||||
Utilities.WriteBytesLittleEndian(CalcChecksum(buffer, offset), buffer, offset + 0x01FC);
|
||||
}
|
||||
|
||||
private static uint CalcChecksum(byte[] buffer, int offset)
|
||||
{
|
||||
uint sum = 0;
|
||||
|
||||
for (int i = 0; i < 0x01FC; i += 4)
|
||||
{
|
||||
sum = sum ^ Utilities.ToUInt32LittleEndian(buffer, offset + i);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
internal sealed class KeyNodeCell : Cell
|
||||
{
|
||||
public RegistryKeyFlags Flags;
|
||||
public DateTime Timestamp;
|
||||
public int ParentIndex;
|
||||
public int NumSubKeys;
|
||||
public int SubKeysIndex;
|
||||
public int NumValues;
|
||||
public int ValueListIndex;
|
||||
public int SecurityIndex;
|
||||
public int ClassNameIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes to represent largest subkey name in Unicode - no null terminator.
|
||||
/// </summary>
|
||||
public int MaxSubKeyNameBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes to represent largest value name in Unicode - no null terminator.
|
||||
/// </summary>
|
||||
public int MaxValNameBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes to represent largest value content (strings in Unicode, with null terminator - if stored).
|
||||
/// </summary>
|
||||
public int MaxValDataBytes;
|
||||
|
||||
public int IndexInParent;
|
||||
public int ClassNameLength;
|
||||
public string Name;
|
||||
|
||||
public KeyNodeCell(string name, int parentCellIndex)
|
||||
: this(-1)
|
||||
{
|
||||
Flags = RegistryKeyFlags.Normal;
|
||||
Timestamp = DateTime.UtcNow;
|
||||
ParentIndex = parentCellIndex;
|
||||
SubKeysIndex = -1;
|
||||
ValueListIndex = -1;
|
||||
SecurityIndex = -1;
|
||||
ClassNameIndex = -1;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public KeyNodeCell(int index)
|
||||
: base(index)
|
||||
{
|
||||
}
|
||||
|
||||
public override int Size
|
||||
{
|
||||
get { return 0x4C + Name.Length; }
|
||||
}
|
||||
|
||||
public override int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Flags = (RegistryKeyFlags)Utilities.ToUInt16LittleEndian(buffer, offset + 0x02);
|
||||
Timestamp = DateTime.FromFileTimeUtc(Utilities.ToInt64LittleEndian(buffer, offset + 0x04));
|
||||
ParentIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
|
||||
NumSubKeys = Utilities.ToInt32LittleEndian(buffer, offset + 0x14);
|
||||
SubKeysIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x1C);
|
||||
NumValues = Utilities.ToInt32LittleEndian(buffer, offset + 0x24);
|
||||
ValueListIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x28);
|
||||
SecurityIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x2C);
|
||||
ClassNameIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x30);
|
||||
MaxSubKeyNameBytes = Utilities.ToInt32LittleEndian(buffer, offset + 0x34);
|
||||
MaxValNameBytes = Utilities.ToInt32LittleEndian(buffer, offset + 0x3C);
|
||||
MaxValDataBytes = Utilities.ToInt32LittleEndian(buffer, offset + 0x40);
|
||||
IndexInParent = Utilities.ToInt32LittleEndian(buffer, offset + 0x44);
|
||||
int nameLength = Utilities.ToInt16LittleEndian(buffer, offset + 0x48);
|
||||
ClassNameLength = Utilities.ToInt16LittleEndian(buffer, offset + 0x4A);
|
||||
Name = Utilities.BytesToString(buffer, offset + 0x4C, nameLength);
|
||||
|
||||
return 0x4C + nameLength;
|
||||
}
|
||||
|
||||
public override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.StringToBytes("nk", buffer, offset, 2);
|
||||
Utilities.WriteBytesLittleEndian((ushort)Flags, buffer, offset + 0x02);
|
||||
Utilities.WriteBytesLittleEndian(Timestamp.ToFileTimeUtc(), buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(ParentIndex, buffer, offset + 0x10);
|
||||
Utilities.WriteBytesLittleEndian(NumSubKeys, buffer, offset + 0x14);
|
||||
Utilities.WriteBytesLittleEndian(SubKeysIndex, buffer, offset + 0x1C);
|
||||
Utilities.WriteBytesLittleEndian(NumValues, buffer, offset + 0x24);
|
||||
Utilities.WriteBytesLittleEndian(ValueListIndex, buffer, offset + 0x28);
|
||||
Utilities.WriteBytesLittleEndian(SecurityIndex, buffer, offset + 0x2C);
|
||||
Utilities.WriteBytesLittleEndian(ClassNameIndex, buffer, offset + 0x30);
|
||||
Utilities.WriteBytesLittleEndian(IndexInParent, buffer, offset + 0x44);
|
||||
Utilities.WriteBytesLittleEndian((ushort)Name.Length, buffer, offset + 0x48);
|
||||
Utilities.WriteBytesLittleEndian(ClassNameLength, buffer, offset + 0x4A);
|
||||
Utilities.StringToBytes(Name, buffer, offset + 0x4C, Name.Length);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Key:" + Name + "[" + Flags + "] <" + Timestamp + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
internal abstract class ListCell : Cell
|
||||
{
|
||||
public ListCell(int index)
|
||||
: base(index)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of subkeys in this list.
|
||||
/// </summary>
|
||||
internal abstract int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a key with a given name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name to search for.</param>
|
||||
/// <param name="cellIndex">The index of the cell, if found.</param>
|
||||
/// <returns>The search result.</returns>
|
||||
internal abstract int FindKey(string name, out int cellIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all of the keys in the list.
|
||||
/// </summary>
|
||||
/// <param name="names">The list to populate.</param>
|
||||
internal abstract void EnumerateKeys(List<string> names);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all of the keys in the list.
|
||||
/// </summary>
|
||||
/// <returns>Enumeration of key cells.</returns>
|
||||
internal abstract IEnumerable<KeyNodeCell> EnumerateKeys();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a subkey to this list.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the subkey.</param>
|
||||
/// <param name="cellIndex">The cell index of the subkey.</param>
|
||||
/// <returns>The new cell index of the list, which may have changed.</returns>
|
||||
internal abstract int LinkSubKey(string name, int cellIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a subkey from this list.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the subkey.</param>
|
||||
/// <returns>The new cell index of the list, which may have changed.</returns>
|
||||
internal abstract int UnlinkSubKey(string name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// 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.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when some corruption is found in the registry hive.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RegistryCorruptException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryCorruptException class.
|
||||
/// </summary>
|
||||
public RegistryCorruptException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryCorruptException class.
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
public RegistryCorruptException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryCorruptException class.
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
public RegistryCorruptException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryCorruptException class.
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info.</param>
|
||||
/// <param name="context">The streaming context.</param>
|
||||
protected RegistryCorruptException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
/// <summary>
|
||||
/// A registry hive.
|
||||
/// </summary>
|
||||
public sealed class RegistryHive : IDisposable
|
||||
{
|
||||
private const long BinStart = 4 * Sizes.OneKiB;
|
||||
|
||||
private Stream _fileStream;
|
||||
private Ownership _ownsStream;
|
||||
private HiveHeader _header;
|
||||
private List<BinHeader> _bins;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryHive class.
|
||||
/// </summary>
|
||||
/// <param name="hive">The stream containing the registry hive.</param>
|
||||
/// <remarks>
|
||||
/// The created object does not assume ownership of the stream.
|
||||
/// </remarks>
|
||||
public RegistryHive(Stream hive)
|
||||
: this(hive, Ownership.None)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RegistryHive class.
|
||||
/// </summary>
|
||||
/// <param name="hive">The stream containing the registry hive.</param>
|
||||
/// <param name="ownership">Whether the new object assumes object of the stream.</param>
|
||||
public RegistryHive(Stream hive, Ownership ownership)
|
||||
{
|
||||
_fileStream = hive;
|
||||
_fileStream.Position = 0;
|
||||
_ownsStream = ownership;
|
||||
|
||||
byte[] buffer = Utilities.ReadFully(_fileStream, HiveHeader.HeaderSize);
|
||||
|
||||
_header = new HiveHeader();
|
||||
_header.ReadFrom(buffer, 0);
|
||||
|
||||
_bins = new List<BinHeader>();
|
||||
int pos = 0;
|
||||
while (pos < _header.Length)
|
||||
{
|
||||
_fileStream.Position = BinStart + pos;
|
||||
byte[] headerBuffer = Utilities.ReadFully(_fileStream, BinHeader.HeaderSize);
|
||||
BinHeader header = new BinHeader();
|
||||
header.ReadFrom(headerBuffer, 0);
|
||||
_bins.Add(header);
|
||||
|
||||
pos += header.BinSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root key in the registry hive.
|
||||
/// </summary>
|
||||
public RegistryKey Root
|
||||
{
|
||||
get { return new RegistryKey(this, GetCell<KeyNodeCell>(_header.RootCell)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new (empty) registry hive.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to contain the new hive.</param>
|
||||
/// <returns>The new hive.</returns>
|
||||
/// <remarks>
|
||||
/// The returned object does not assume ownership of the stream.
|
||||
/// </remarks>
|
||||
public static RegistryHive Create(Stream stream)
|
||||
{
|
||||
return Create(stream, Ownership.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new (empty) registry hive.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to contain the new hive.</param>
|
||||
/// <param name="ownership">Whether the returned object owns the stream.</param>
|
||||
/// <returns>The new hive.</returns>
|
||||
public static RegistryHive Create(Stream stream, Ownership ownership)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream", "Attempt to create registry hive in null stream");
|
||||
}
|
||||
|
||||
// Construct a file with minimal structure - hive header, plus one (empty) bin
|
||||
BinHeader binHeader = new BinHeader();
|
||||
binHeader.FileOffset = 0;
|
||||
binHeader.BinSize = (int)(4 * Sizes.OneKiB);
|
||||
|
||||
HiveHeader hiveHeader = new HiveHeader();
|
||||
hiveHeader.Length = binHeader.BinSize;
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
byte[] buffer = new byte[hiveHeader.Size];
|
||||
hiveHeader.WriteTo(buffer, 0);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = new byte[binHeader.Size];
|
||||
binHeader.WriteTo(buffer, 0);
|
||||
stream.Position = BinStart;
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = new byte[4];
|
||||
Utilities.WriteBytesLittleEndian(binHeader.BinSize - binHeader.Size, buffer, 0);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// Make sure the file is initialized out to the end of the firs bin
|
||||
stream.Position = BinStart + binHeader.BinSize - 1;
|
||||
stream.WriteByte(0);
|
||||
|
||||
// Temporary hive to perform construction of higher-level structures
|
||||
RegistryHive newHive = new RegistryHive(stream);
|
||||
KeyNodeCell rootCell = new KeyNodeCell("root", -1);
|
||||
rootCell.Flags = RegistryKeyFlags.Normal | RegistryKeyFlags.Root;
|
||||
newHive.UpdateCell(rootCell, true);
|
||||
|
||||
RegistrySecurity sd = new RegistrySecurity();
|
||||
sd.SetSecurityDescriptorSddlForm("O:BAG:BAD:PAI(A;;KA;;;SY)(A;CI;KA;;;BA)", AccessControlSections.All);
|
||||
SecurityCell secCell = new SecurityCell(sd);
|
||||
newHive.UpdateCell(secCell, true);
|
||||
secCell.NextIndex = secCell.Index;
|
||||
secCell.PreviousIndex = secCell.Index;
|
||||
newHive.UpdateCell(secCell, false);
|
||||
|
||||
rootCell.SecurityIndex = secCell.Index;
|
||||
newHive.UpdateCell(rootCell, false);
|
||||
|
||||
// Ref the root cell from the hive header
|
||||
hiveHeader.RootCell = rootCell.Index;
|
||||
buffer = new byte[hiveHeader.Size];
|
||||
hiveHeader.WriteTo(buffer, 0);
|
||||
stream.Position = 0;
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// Finally, return the new hive
|
||||
return new RegistryHive(stream, ownership);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new (empty) registry hive.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to create the new hive in.</param>
|
||||
/// <returns>The new hive.</returns>
|
||||
public static RegistryHive Create(string path)
|
||||
{
|
||||
return Create(new FileStream(path, FileMode.Create, FileAccess.ReadWrite), Ownership.Dispose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this instance, freeing any underlying stream (if any).
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_fileStream != null && _ownsStream == Ownership.Dispose)
|
||||
{
|
||||
_fileStream.Dispose();
|
||||
_fileStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal K GetCell<K>(int index)
|
||||
where K : Cell
|
||||
{
|
||||
Bin bin = GetBin(index);
|
||||
|
||||
if (bin != null)
|
||||
{
|
||||
return (K)bin.TryGetCell(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void FreeCell(int index)
|
||||
{
|
||||
Bin bin = GetBin(index);
|
||||
|
||||
if (bin != null)
|
||||
{
|
||||
bin.FreeCell(index);
|
||||
}
|
||||
}
|
||||
|
||||
internal int UpdateCell(Cell cell, bool canRelocate)
|
||||
{
|
||||
if (cell.Index == -1 && canRelocate)
|
||||
{
|
||||
cell.Index = AllocateRawCell(cell.Size);
|
||||
}
|
||||
|
||||
Bin bin = GetBin(cell.Index);
|
||||
|
||||
if (bin != null)
|
||||
{
|
||||
if (bin.UpdateCell(cell))
|
||||
{
|
||||
return cell.Index;
|
||||
}
|
||||
else if (canRelocate)
|
||||
{
|
||||
int oldCell = cell.Index;
|
||||
cell.Index = AllocateRawCell(cell.Size);
|
||||
bin = GetBin(cell.Index);
|
||||
if (!bin.UpdateCell(cell))
|
||||
{
|
||||
cell.Index = oldCell;
|
||||
throw new RegistryCorruptException("Failed to migrate cell to new location");
|
||||
}
|
||||
|
||||
FreeCell(oldCell);
|
||||
return cell.Index;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Can't update cell, needs relocation but relocation disabled", "canRelocate");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RegistryCorruptException("No bin found containing index: " + cell.Index);
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] RawCellData(int index, int maxBytes)
|
||||
{
|
||||
Bin bin = GetBin(index);
|
||||
|
||||
if (bin != null)
|
||||
{
|
||||
return bin.ReadRawCellData(index, maxBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool WriteRawCellData(int index, byte[] data, int offset, int count)
|
||||
{
|
||||
Bin bin = GetBin(index);
|
||||
|
||||
if (bin != null)
|
||||
{
|
||||
return bin.WriteRawCellData(index, data, offset, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RegistryCorruptException("No bin found containing index: " + index);
|
||||
}
|
||||
}
|
||||
|
||||
internal int AllocateRawCell(int capacity)
|
||||
{
|
||||
int minSize = Utilities.RoundUp(capacity + 4, 8); // Allow for size header and ensure multiple of 8
|
||||
|
||||
// Incredibly inefficient algorithm...
|
||||
foreach (var binHeader in _bins)
|
||||
{
|
||||
Bin bin = LoadBin(binHeader);
|
||||
int cellIndex = bin.AllocateCell(minSize);
|
||||
|
||||
if (cellIndex >= 0)
|
||||
{
|
||||
return cellIndex;
|
||||
}
|
||||
}
|
||||
|
||||
BinHeader newBinHeader = AllocateBin(minSize);
|
||||
Bin newBin = LoadBin(newBinHeader);
|
||||
return newBin.AllocateCell(minSize);
|
||||
}
|
||||
|
||||
private BinHeader FindBin(int index)
|
||||
{
|
||||
int binsIdx = _bins.BinarySearch(null, new BinFinder(index));
|
||||
if (binsIdx >= 0)
|
||||
{
|
||||
return _bins[binsIdx];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Bin GetBin(int cellIndex)
|
||||
{
|
||||
BinHeader binHeader = FindBin(cellIndex);
|
||||
|
||||
if (binHeader != null)
|
||||
{
|
||||
return LoadBin(binHeader);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Bin LoadBin(BinHeader binHeader)
|
||||
{
|
||||
_fileStream.Position = BinStart + binHeader.FileOffset;
|
||||
return new Bin(this, _fileStream);
|
||||
}
|
||||
|
||||
private BinHeader AllocateBin(int minSize)
|
||||
{
|
||||
BinHeader lastBin = _bins[_bins.Count - 1];
|
||||
|
||||
BinHeader newBinHeader = new BinHeader();
|
||||
newBinHeader.FileOffset = lastBin.FileOffset + lastBin.BinSize;
|
||||
newBinHeader.BinSize = Utilities.RoundUp(minSize + newBinHeader.Size, 4 * (int)Sizes.OneKiB);
|
||||
|
||||
byte[] buffer = new byte[newBinHeader.Size];
|
||||
newBinHeader.WriteTo(buffer, 0);
|
||||
_fileStream.Position = BinStart + newBinHeader.FileOffset;
|
||||
_fileStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
byte[] cellHeader = new byte[4];
|
||||
Utilities.WriteBytesLittleEndian(newBinHeader.BinSize - newBinHeader.Size, cellHeader, 0);
|
||||
_fileStream.Write(cellHeader, 0, 4);
|
||||
|
||||
// Update hive with new length
|
||||
_header.Length = newBinHeader.FileOffset + newBinHeader.BinSize;
|
||||
_header.Timestamp = DateTime.UtcNow;
|
||||
_header.Sequence1++;
|
||||
_header.Sequence2++;
|
||||
_fileStream.Position = 0;
|
||||
byte[] hiveHeader = Utilities.ReadFully(_fileStream, _header.Size);
|
||||
_header.WriteTo(hiveHeader, 0);
|
||||
_fileStream.Position = 0;
|
||||
_fileStream.Write(hiveHeader, 0, hiveHeader.Length);
|
||||
|
||||
// Make sure the file is initialized to desired position
|
||||
_fileStream.Position = BinStart + _header.Length - 1;
|
||||
_fileStream.WriteByte(0);
|
||||
|
||||
_bins.Add(newBinHeader);
|
||||
return newBinHeader;
|
||||
}
|
||||
|
||||
private class BinFinder : IComparer<BinHeader>
|
||||
{
|
||||
private int _index;
|
||||
|
||||
public BinFinder(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
#region IComparer<BinHeader> Members
|
||||
|
||||
public int Compare(BinHeader x, BinHeader y)
|
||||
{
|
||||
if (x.FileOffset + x.BinSize < _index)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (x.FileOffset > _index)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,853 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// A key within a registry hive.
|
||||
/// </summary>
|
||||
public sealed class RegistryKey
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private KeyNodeCell _cell;
|
||||
|
||||
internal RegistryKey(RegistryHive hive, KeyNodeCell cell)
|
||||
{
|
||||
_hive = hive;
|
||||
_cell = cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this key.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
RegistryKey parent = Parent;
|
||||
if (parent != null && ((parent.Flags & RegistryKeyFlags.Root) == 0))
|
||||
{
|
||||
return parent.Name + @"\" + _cell.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _cell.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of child keys.
|
||||
/// </summary>
|
||||
public int SubKeyCount
|
||||
{
|
||||
get { return _cell.NumSubKeys; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of values in this key.
|
||||
/// </summary>
|
||||
public int ValueCount
|
||||
{
|
||||
get { return _cell.NumValues; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time the key was last modified.
|
||||
/// </summary>
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get { return _cell.Timestamp; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent key, or <c>null</c> if this is the root key.
|
||||
/// </summary>
|
||||
public RegistryKey Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_cell.Flags & RegistryKeyFlags.Root) == 0)
|
||||
{
|
||||
return new RegistryKey(_hive, _hive.GetCell<KeyNodeCell>(_cell.ParentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flags of this registry key.
|
||||
/// </summary>
|
||||
public RegistryKeyFlags Flags
|
||||
{
|
||||
get { return _cell.Flags; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class name of this registry key.
|
||||
/// </summary>
|
||||
/// <remarks>Class name is rarely used.</remarks>
|
||||
public string ClassName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cell.ClassNameIndex > 0)
|
||||
{
|
||||
return Encoding.Unicode.GetString(_hive.RawCellData(_cell.ClassNameIndex, _cell.ClassNameLength));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator over all sub child keys.
|
||||
/// </summary>
|
||||
public IEnumerable<RegistryKey> SubKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cell.NumSubKeys != 0)
|
||||
{
|
||||
ListCell list = _hive.GetCell<ListCell>(_cell.SubKeysIndex);
|
||||
foreach (var key in list.EnumerateKeys())
|
||||
{
|
||||
yield return new RegistryKey(_hive, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator over all values in this key.
|
||||
/// </summary>
|
||||
private IEnumerable<RegistryValue> 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<ValueCell>(valueIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Security Descriptor applied to the registry key.
|
||||
/// </summary>
|
||||
/// <returns>The security descriptor as a RegistrySecurity instance.</returns>
|
||||
public RegistrySecurity GetAccessControl()
|
||||
{
|
||||
if (_cell.SecurityIndex > 0)
|
||||
{
|
||||
SecurityCell secCell = _hive.GetCell<SecurityCell>(_cell.SecurityIndex);
|
||||
return secCell.SecurityDescriptor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all child sub keys.
|
||||
/// </summary>
|
||||
/// <returns>The names of the sub keys.</returns>
|
||||
public string[] GetSubKeyNames()
|
||||
{
|
||||
List<string> names = new List<string>();
|
||||
|
||||
if (_cell.NumSubKeys != 0)
|
||||
{
|
||||
_hive.GetCell<ListCell>(_cell.SubKeysIndex).EnumerateKeys(names);
|
||||
}
|
||||
|
||||
return names.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to retrieve.</param>
|
||||
/// <returns>The value as a .NET object.</returns>
|
||||
/// <remarks>The mapping from registry type of .NET type is as follows:
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Value Type</term>
|
||||
/// <term>.NET type</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <description>String</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>ExpandString</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>Link</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWord</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWordBigEndian</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>MultiString</description>
|
||||
/// <description>string[]</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>QWord</description>
|
||||
/// <description>ulong</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public object GetValue(string name)
|
||||
{
|
||||
return GetValue(name, null, Microsoft.Win32.RegistryValueOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to retrieve.</param>
|
||||
/// <param name="defaultValue">The default value to return, if no existing value is stored.</param>
|
||||
/// <returns>The value as a .NET object.</returns>
|
||||
/// <remarks>The mapping from registry type of .NET type is as follows:
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Value Type</term>
|
||||
/// <term>.NET type</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <description>String</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>ExpandString</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>Link</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWord</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWordBigEndian</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>MultiString</description>
|
||||
/// <description>string[]</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>QWord</description>
|
||||
/// <description>ulong</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public object GetValue(string name, object defaultValue)
|
||||
{
|
||||
return GetValue(name, defaultValue, Microsoft.Win32.RegistryValueOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to retrieve.</param>
|
||||
/// <param name="defaultValue">The default value to return, if no existing value is stored.</param>
|
||||
/// <param name="options">Flags controlling how the value is processed before it's returned.</param>
|
||||
/// <returns>The value as a .NET object.</returns>
|
||||
/// <remarks>The mapping from registry type of .NET type is as follows:
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Value Type</term>
|
||||
/// <term>.NET type</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <description>String</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>ExpandString</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>Link</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWord</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWordBigEndian</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>MultiString</description>
|
||||
/// <description>string[]</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>QWord</description>
|
||||
/// <description>ulong</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to store.</param>
|
||||
/// <param name="value">The value to store.</param>
|
||||
public void SetValue(string name, object value)
|
||||
{
|
||||
SetValue(name, value, RegistryValueType.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to store.</param>
|
||||
/// <param name="value">The value to store.</param>
|
||||
/// <param name="valueType">The registry type of the data.</param>
|
||||
public void SetValue(string name, object value, RegistryValueType valueType)
|
||||
{
|
||||
RegistryValue valObj = GetRegistryValue(name);
|
||||
if (valObj == null)
|
||||
{
|
||||
valObj = AddRegistryValue(name);
|
||||
}
|
||||
|
||||
valObj.SetValue(value, valueType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to delete.</param>
|
||||
public void DeleteValue(string name)
|
||||
{
|
||||
DeleteValue(name, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a named value stored within this key.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to delete.</param>
|
||||
/// <param name="throwOnMissingValue">Throws ArgumentException if <c>name</c> doesn't exist.</param>
|
||||
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<ValueCell>(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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of a named value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the value to inspect.</param>
|
||||
/// <returns>The value's type.</returns>
|
||||
public RegistryValueType GetValueType(string name)
|
||||
{
|
||||
RegistryValue regVal = GetRegistryValue(name);
|
||||
if (regVal != null)
|
||||
{
|
||||
return regVal.DataType;
|
||||
}
|
||||
|
||||
return RegistryValueType.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all values in this key.
|
||||
/// </summary>
|
||||
/// <returns>An array of strings containing the value names.</returns>
|
||||
public string[] GetValueNames()
|
||||
{
|
||||
List<string> names = new List<string>();
|
||||
foreach (var value in Values)
|
||||
{
|
||||
names.Add(value.Name);
|
||||
}
|
||||
|
||||
return names.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or opens a subkey.
|
||||
/// </summary>
|
||||
/// <param name="subkey">The relative path the the subkey.</param>
|
||||
/// <returns>The subkey.</returns>
|
||||
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<KeyNodeCell>(cellIndex);
|
||||
if (split.Length == 1)
|
||||
{
|
||||
return new RegistryKey(_hive, cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RegistryKey(_hive, cell).CreateSubKey(split[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a sub key.
|
||||
/// </summary>
|
||||
/// <param name="path">The relative path to the sub key.</param>
|
||||
/// <returns>The sub key, or <c>null</c> if not found.</returns>
|
||||
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<KeyNodeCell>(cellIndex);
|
||||
if (split.Length == 1)
|
||||
{
|
||||
return new RegistryKey(_hive, cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RegistryKey(_hive, cell).OpenSubKey(split[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a subkey and any child subkeys recursively. The string subkey is not case-sensitive.
|
||||
/// </summary>
|
||||
/// <param name="subkey">The subkey to delete.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified subkey. The string subkey is not case-sensitive.
|
||||
/// </summary>
|
||||
/// <param name="subkey">The subkey to delete.</param>
|
||||
public void DeleteSubKey(string subkey)
|
||||
{
|
||||
DeleteSubKey(subkey, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified subkey. The string subkey is not case-sensitive.
|
||||
/// </summary>
|
||||
/// <param name="subkey">The subkey to delete.</param>
|
||||
/// <param name="throwOnMissingSubKey"><c>true</c> to throw an argument exception if <c>subkey</c> doesn't exist.</param>
|
||||
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<KeyNodeCell>(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<ValueCell>(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<ValueCell>(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<ListCell>(_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<ListCell>(_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<ListCell>(_cell.SubKeysIndex);
|
||||
_cell.SubKeysIndex = list.UnlinkSubKey(name);
|
||||
_cell.NumSubKeys--;
|
||||
}
|
||||
|
||||
private void ReferenceSecurityCell(int cellIndex)
|
||||
{
|
||||
SecurityCell sc = _hive.GetCell<SecurityCell>(cellIndex);
|
||||
sc.UsageCount++;
|
||||
_hive.UpdateCell(sc, false);
|
||||
}
|
||||
|
||||
private void DereferenceSecurityCell(int cellIndex)
|
||||
{
|
||||
SecurityCell sc = _hive.GetCell<SecurityCell>(cellIndex);
|
||||
sc.UsageCount--;
|
||||
if (sc.UsageCount == 0)
|
||||
{
|
||||
SecurityCell prev = _hive.GetCell<SecurityCell>(sc.PreviousIndex);
|
||||
prev.NextIndex = sc.NextIndex;
|
||||
_hive.UpdateCell(prev, false);
|
||||
|
||||
SecurityCell next = _hive.GetCell<SecurityCell>(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<Cell>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// The per-key flags present on registry keys.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RegistryKeyFlags : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0001 = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0002 = 0x0002,
|
||||
|
||||
/// <summary>
|
||||
/// The key is the root key in the registry hive.
|
||||
/// </summary>
|
||||
Root = 0x0004,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0008 = 0x0008,
|
||||
|
||||
/// <summary>
|
||||
/// The key is a link to another key.
|
||||
/// </summary>
|
||||
Link = 0x0010,
|
||||
|
||||
/// <summary>
|
||||
/// This is a normal key.
|
||||
/// </summary>
|
||||
Normal = 0x0020,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0040 = 0x0040,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0080 = 0x0080,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0100 = 0x0100,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0200 = 0x0200,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0400 = 0x0400,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown0800 = 0x0800,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown1000 = 0x1000,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown2000 = 0x2000,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown4000 = 0x4000,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown purpose.
|
||||
/// </summary>
|
||||
Unknown8000 = 0x8000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// A registry value.
|
||||
/// </summary>
|
||||
internal sealed class RegistryValue
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private ValueCell _cell;
|
||||
|
||||
internal RegistryValue(RegistryHive hive, ValueCell cell)
|
||||
{
|
||||
_hive = hive;
|
||||
_cell = cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the value, or empty string if unnamed.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return _cell.Name ?? string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the value.
|
||||
/// </summary>
|
||||
public RegistryValueType DataType
|
||||
{
|
||||
get { return _cell.DataType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value data mapped to a .net object.
|
||||
/// </summary>
|
||||
/// <remarks>The mapping from registry type of .NET type is as follows:
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Value Type</term>
|
||||
/// <term>.NET type</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <description>String</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>ExpandString</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>Link</description>
|
||||
/// <description>string</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWord</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>DWordBigEndian</description>
|
||||
/// <description>uint</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>MultiString</description>
|
||||
/// <description>string[]</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>QWord</description>
|
||||
/// <description>ulong</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConvertToObject(GetData(), DataType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The raw value data as a byte array.
|
||||
/// </summary>
|
||||
/// <returns>The value as a raw byte array.</returns>
|
||||
public byte[] GetData()
|
||||
{
|
||||
if (_cell.DataLength < 0)
|
||||
{
|
||||
int len = _cell.DataLength & 0x7FFFFFFF;
|
||||
byte[] buffer = new byte[4];
|
||||
Utilities.WriteBytesLittleEndian(_cell.DataIndex, buffer, 0);
|
||||
|
||||
byte[] result = new byte[len];
|
||||
Array.Copy(buffer, result, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
return _hive.RawCellData(_cell.DataIndex, _cell.DataLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value as raw bytes, with no validation that enough data is specified for the given value type.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to store.</param>
|
||||
/// <param name="offset">The offset within <c>data</c> of the first byte to store.</param>
|
||||
/// <param name="count">The number of bytes to store.</param>
|
||||
/// <param name="valueType">The type of the data.</param>
|
||||
public void SetData(byte[] data, int offset, int count, RegistryValueType valueType)
|
||||
{
|
||||
// If we can place the data in the DataIndex field, do that to save space / allocation
|
||||
if ((valueType == RegistryValueType.Dword || valueType == RegistryValueType.DwordBigEndian) && count <= 4)
|
||||
{
|
||||
if (_cell.DataLength >= 0)
|
||||
{
|
||||
_hive.FreeCell(_cell.DataIndex);
|
||||
}
|
||||
|
||||
_cell.DataLength = (int)((uint)count | 0x80000000);
|
||||
_cell.DataIndex = Utilities.ToInt32LittleEndian(data, offset);
|
||||
_cell.DataType = valueType;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_cell.DataIndex == -1 || _cell.DataLength < 0)
|
||||
{
|
||||
_cell.DataIndex = _hive.AllocateRawCell(count);
|
||||
}
|
||||
|
||||
if (!_hive.WriteRawCellData(_cell.DataIndex, data, offset, count))
|
||||
{
|
||||
int newDataIndex = _hive.AllocateRawCell(count);
|
||||
_hive.WriteRawCellData(newDataIndex, data, offset, count);
|
||||
_hive.FreeCell(_cell.DataIndex);
|
||||
_cell.DataIndex = newDataIndex;
|
||||
}
|
||||
|
||||
_cell.DataLength = count;
|
||||
_cell.DataType = valueType;
|
||||
}
|
||||
|
||||
_hive.UpdateCell(_cell, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value stored.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to store.</param>
|
||||
/// <param name="valueType">The registry type of the data.</param>
|
||||
public void SetValue(object value, RegistryValueType valueType)
|
||||
{
|
||||
if (valueType == RegistryValueType.None)
|
||||
{
|
||||
if (value is int)
|
||||
{
|
||||
valueType = RegistryValueType.Dword;
|
||||
}
|
||||
else if (value is byte[])
|
||||
{
|
||||
valueType = RegistryValueType.Binary;
|
||||
}
|
||||
else if (value is string[])
|
||||
{
|
||||
valueType = RegistryValueType.MultiString;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueType = RegistryValueType.String;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] data = ConvertToData(value, valueType);
|
||||
SetData(data, 0, data.Length, valueType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the registry value.
|
||||
/// </summary>
|
||||
/// <returns>The registry value as a string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return Name + ":" + DataType + ":" + DataAsString();
|
||||
}
|
||||
|
||||
private static object ConvertToObject(byte[] data, RegistryValueType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RegistryValueType.String:
|
||||
case RegistryValueType.ExpandString:
|
||||
case RegistryValueType.Link:
|
||||
return Encoding.Unicode.GetString(data).Trim('\0');
|
||||
|
||||
case RegistryValueType.Dword:
|
||||
return Utilities.ToInt32LittleEndian(data, 0);
|
||||
|
||||
case RegistryValueType.DwordBigEndian:
|
||||
return Utilities.ToInt32BigEndian(data, 0);
|
||||
|
||||
case RegistryValueType.MultiString:
|
||||
string multiString = Encoding.Unicode.GetString(data).Trim('\0');
|
||||
return multiString.Split('\0');
|
||||
|
||||
case RegistryValueType.QWord:
|
||||
return string.Empty + Utilities.ToUInt64LittleEndian(data, 0);
|
||||
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] ConvertToData(object value, RegistryValueType valueType)
|
||||
{
|
||||
if (valueType == RegistryValueType.None)
|
||||
{
|
||||
throw new ArgumentException("Specific registry value type must be specified", "valueType");
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
switch (valueType)
|
||||
{
|
||||
case RegistryValueType.String:
|
||||
case RegistryValueType.ExpandString:
|
||||
string strValue = value.ToString();
|
||||
data = new byte[(strValue.Length * 2) + 2];
|
||||
Encoding.Unicode.GetBytes(strValue, 0, strValue.Length, data, 0);
|
||||
break;
|
||||
|
||||
case RegistryValueType.Dword:
|
||||
data = new byte[4];
|
||||
Utilities.WriteBytesLittleEndian((int)value, data, 0);
|
||||
break;
|
||||
|
||||
case RegistryValueType.DwordBigEndian:
|
||||
data = new byte[4];
|
||||
Utilities.WriteBytesBigEndian((int)value, data, 0);
|
||||
break;
|
||||
|
||||
case RegistryValueType.MultiString:
|
||||
string multiStrValue = string.Join("\0", (string[])value) + "\0";
|
||||
data = new byte[(multiStrValue.Length * 2) + 2];
|
||||
Encoding.Unicode.GetBytes(multiStrValue, 0, multiStrValue.Length, data, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
data = (byte[])value;
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private string DataAsString()
|
||||
{
|
||||
switch (DataType)
|
||||
{
|
||||
case RegistryValueType.String:
|
||||
case RegistryValueType.ExpandString:
|
||||
case RegistryValueType.Link:
|
||||
case RegistryValueType.Dword:
|
||||
case RegistryValueType.DwordBigEndian:
|
||||
case RegistryValueType.QWord:
|
||||
return ConvertToObject(GetData(), DataType).ToString();
|
||||
|
||||
case RegistryValueType.MultiString:
|
||||
return string.Join(",", (string[])ConvertToObject(GetData(), DataType));
|
||||
|
||||
default:
|
||||
byte[] data = GetData();
|
||||
string result = string.Empty;
|
||||
for (int i = 0; i < Math.Min(data.Length, 8); ++i)
|
||||
{
|
||||
result += string.Format(CultureInfo.InvariantCulture, "{0:X2} ", (int)data[i]);
|
||||
}
|
||||
|
||||
return result + string.Format(CultureInfo.InvariantCulture, " ({0} bytes)", data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// The types of registry values.
|
||||
/// </summary>
|
||||
public enum RegistryValueType : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown type.
|
||||
/// </summary>
|
||||
None = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// A unicode string.
|
||||
/// </summary>
|
||||
String = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// A string containing environment variables.
|
||||
/// </summary>
|
||||
ExpandString = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Binary data.
|
||||
/// </summary>
|
||||
Binary = 0x03,
|
||||
|
||||
/// <summary>
|
||||
/// A 32-bit integer.
|
||||
/// </summary>
|
||||
Dword = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// A 32-bit integer.
|
||||
/// </summary>
|
||||
DwordBigEndian = 0x05,
|
||||
|
||||
/// <summary>
|
||||
/// A registry link.
|
||||
/// </summary>
|
||||
Link = 0x06,
|
||||
|
||||
/// <summary>
|
||||
/// A multistring.
|
||||
/// </summary>
|
||||
MultiString = 0x07,
|
||||
|
||||
/// <summary>
|
||||
/// An unknown binary format.
|
||||
/// </summary>
|
||||
ResourceList = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// An unknown binary format.
|
||||
/// </summary>
|
||||
FullResourceDescriptor = 0x09,
|
||||
|
||||
/// <summary>
|
||||
/// An unknown binary format.
|
||||
/// </summary>
|
||||
ResourceRequirementsList = 0x0A,
|
||||
|
||||
/// <summary>
|
||||
/// A 64-bit integer.
|
||||
/// </summary>
|
||||
QWord = 0x0B,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// 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.Security.AccessControl;
|
||||
|
||||
internal sealed class SecurityCell : Cell
|
||||
{
|
||||
private int _prevIndex;
|
||||
private int _nextIndex;
|
||||
private int _usageCount;
|
||||
private RegistrySecurity _secDesc;
|
||||
|
||||
public SecurityCell(RegistrySecurity secDesc)
|
||||
: this(-1)
|
||||
{
|
||||
_secDesc = secDesc;
|
||||
}
|
||||
|
||||
public SecurityCell(int index)
|
||||
: base(index)
|
||||
{
|
||||
_prevIndex = -1;
|
||||
_nextIndex = -1;
|
||||
}
|
||||
|
||||
public int PreviousIndex
|
||||
{
|
||||
get { return _prevIndex; }
|
||||
set { _prevIndex = value; }
|
||||
}
|
||||
|
||||
public int NextIndex
|
||||
{
|
||||
get { return _nextIndex; }
|
||||
set { _nextIndex = value; }
|
||||
}
|
||||
|
||||
public int UsageCount
|
||||
{
|
||||
get { return _usageCount; }
|
||||
set { _usageCount = value; }
|
||||
}
|
||||
|
||||
public RegistrySecurity SecurityDescriptor
|
||||
{
|
||||
get { return _secDesc; }
|
||||
}
|
||||
|
||||
public override int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
int sdLen = _secDesc.GetSecurityDescriptorBinaryForm().Length;
|
||||
return 0x14 + sdLen;
|
||||
}
|
||||
}
|
||||
|
||||
public override int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
_prevIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x04);
|
||||
_nextIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x08);
|
||||
_usageCount = Utilities.ToInt32LittleEndian(buffer, offset + 0x0C);
|
||||
int secDescSize = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
|
||||
|
||||
byte[] secDesc = new byte[secDescSize];
|
||||
Array.Copy(buffer, offset + 0x14, secDesc, 0, secDescSize);
|
||||
_secDesc = new RegistrySecurity();
|
||||
_secDesc.SetSecurityDescriptorBinaryForm(secDesc);
|
||||
|
||||
return 0x14 + secDescSize;
|
||||
}
|
||||
|
||||
public override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
byte[] sd = _secDesc.GetSecurityDescriptorBinaryForm();
|
||||
|
||||
Utilities.StringToBytes("sk", buffer, offset, 2);
|
||||
Utilities.WriteBytesLittleEndian(_prevIndex, buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(_nextIndex, buffer, offset + 0x08);
|
||||
Utilities.WriteBytesLittleEndian(_usageCount, buffer, offset + 0x0C);
|
||||
Utilities.WriteBytesLittleEndian(sd.Length, buffer, offset + 0x10);
|
||||
Array.Copy(sd, 0, buffer, offset + 0x14, sd.Length);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "SecDesc:" + _secDesc.GetSecurityDescriptorSddlForm(AccessControlSections.All) + " (refCount:" + _usageCount + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
|
||||
internal sealed class SubKeyHashedListCell : ListCell
|
||||
{
|
||||
private string _hashType;
|
||||
private short _numElements;
|
||||
private List<int> _subKeyIndexes;
|
||||
private List<uint> _nameHashes;
|
||||
private RegistryHive _hive;
|
||||
|
||||
public SubKeyHashedListCell(RegistryHive hive, string hashType)
|
||||
: base(-1)
|
||||
{
|
||||
_hive = hive;
|
||||
_hashType = hashType;
|
||||
_subKeyIndexes = new List<int>();
|
||||
_nameHashes = new List<uint>();
|
||||
}
|
||||
|
||||
public SubKeyHashedListCell(RegistryHive hive, int index)
|
||||
: base(index)
|
||||
{
|
||||
_hive = hive;
|
||||
}
|
||||
|
||||
public override int Size
|
||||
{
|
||||
get { return 0x4 + (_numElements * 0x8); }
|
||||
}
|
||||
|
||||
internal override int Count
|
||||
{
|
||||
get { return _subKeyIndexes.Count; }
|
||||
}
|
||||
|
||||
public override int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
_hashType = Utilities.BytesToString(buffer, offset, 2);
|
||||
_numElements = Utilities.ToInt16LittleEndian(buffer, offset + 2);
|
||||
|
||||
_subKeyIndexes = new List<int>(_numElements);
|
||||
_nameHashes = new List<uint>(_numElements);
|
||||
for (int i = 0; i < _numElements; ++i)
|
||||
{
|
||||
_subKeyIndexes.Add(Utilities.ToInt32LittleEndian(buffer, offset + 0x4 + (i * 0x8)));
|
||||
_nameHashes.Add(Utilities.ToUInt32LittleEndian(buffer, offset + 0x4 + (i * 0x8) + 0x4));
|
||||
}
|
||||
|
||||
return 0x4 + (_numElements * 0x8);
|
||||
}
|
||||
|
||||
public override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.StringToBytes(_hashType, buffer, offset, 2);
|
||||
Utilities.WriteBytesLittleEndian(_numElements, buffer, offset + 0x2);
|
||||
for (int i = 0; i < _numElements; ++i)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(_subKeyIndexes[i], buffer, offset + 0x4 + (i * 0x8));
|
||||
Utilities.WriteBytesLittleEndian(_nameHashes[i], buffer, offset + 0x4 + (i * 0x8) + 0x4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the subkey.</param>
|
||||
/// <param name="cellIndex">The cell index of the subkey.</param>
|
||||
/// <returns>The index of the new entry.</returns>
|
||||
internal int Add(string name, int cellIndex)
|
||||
{
|
||||
for (int i = 0; i < _numElements; ++i)
|
||||
{
|
||||
KeyNodeCell cell = _hive.GetCell<KeyNodeCell>(_subKeyIndexes[i]);
|
||||
if (string.Compare(cell.Name, name, StringComparison.OrdinalIgnoreCase) > 0)
|
||||
{
|
||||
_subKeyIndexes.Insert(i, cellIndex);
|
||||
_nameHashes.Insert(i, CalcHash(name));
|
||||
_numElements++;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
_subKeyIndexes.Add(cellIndex);
|
||||
_nameHashes.Add(CalcHash(name));
|
||||
return _numElements++;
|
||||
}
|
||||
|
||||
internal override int FindKey(string name, out int cellIndex)
|
||||
{
|
||||
// Check first and last, to early abort if the name is outside the range of this list
|
||||
int result = FindKeyAt(name, 0, out cellIndex);
|
||||
if (result <= 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = FindKeyAt(name, _subKeyIndexes.Count - 1, out cellIndex);
|
||||
if (result >= 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
KeyFinder finder = new KeyFinder(_hive, name);
|
||||
int idx = _subKeyIndexes.BinarySearch(-1, finder);
|
||||
cellIndex = finder.CellIndex;
|
||||
return (idx < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
internal override void EnumerateKeys(List<string> names)
|
||||
{
|
||||
for (int i = 0; i < _subKeyIndexes.Count; ++i)
|
||||
{
|
||||
names.Add(_hive.GetCell<KeyNodeCell>(_subKeyIndexes[i]).Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal override IEnumerable<KeyNodeCell> EnumerateKeys()
|
||||
{
|
||||
for (int i = 0; i < _subKeyIndexes.Count; ++i)
|
||||
{
|
||||
yield return _hive.GetCell<KeyNodeCell>(_subKeyIndexes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
internal override int LinkSubKey(string name, int cellIndex)
|
||||
{
|
||||
Add(name, cellIndex);
|
||||
return _hive.UpdateCell(this, true);
|
||||
}
|
||||
|
||||
internal override int UnlinkSubKey(string name)
|
||||
{
|
||||
int index = IndexOf(name);
|
||||
if (index >= 0)
|
||||
{
|
||||
RemoveAt(index);
|
||||
return _hive.UpdateCell(this, true);
|
||||
}
|
||||
|
||||
return Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a subkey cell, returning it's index in this list.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the key to find.</param>
|
||||
/// <returns>The index of the found key, or <c>-1</c>.</returns>
|
||||
internal int IndexOf(string name)
|
||||
{
|
||||
foreach (var index in Find(name, 0))
|
||||
{
|
||||
KeyNodeCell cell = _hive.GetCell<KeyNodeCell>(_subKeyIndexes[index]);
|
||||
if (cell.Name.ToUpperInvariant() == name.ToUpperInvariant())
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal void RemoveAt(int index)
|
||||
{
|
||||
_nameHashes.RemoveAt(index);
|
||||
_subKeyIndexes.RemoveAt(index);
|
||||
_numElements--;
|
||||
}
|
||||
|
||||
private uint CalcHash(string name)
|
||||
{
|
||||
uint hash = 0;
|
||||
if (_hashType == "lh")
|
||||
{
|
||||
for (int i = 0; i < name.Length; ++i)
|
||||
{
|
||||
hash *= 37;
|
||||
hash += char.ToUpper(name[i], CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string hashStr = name + "\0\0\0\0";
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
hash |= (uint)((hashStr[i] & 0xFF) << (i * 8));
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private int FindKeyAt(string name, int listIndex, out int cellIndex)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(_subKeyIndexes[listIndex]);
|
||||
if (cell == null)
|
||||
{
|
||||
cellIndex = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ListCell listCell = cell as ListCell;
|
||||
if (listCell != null)
|
||||
{
|
||||
return listCell.FindKey(name, out cellIndex);
|
||||
}
|
||||
|
||||
cellIndex = _subKeyIndexes[listIndex];
|
||||
return string.Compare(name, ((KeyNodeCell)cell).Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private IEnumerable<int> Find(string name, int start)
|
||||
{
|
||||
if (_hashType == "lh")
|
||||
{
|
||||
return FindByHash(name, start);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FindByPrefix(name, start);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<int> FindByHash(string name, int start)
|
||||
{
|
||||
uint hash = CalcHash(name);
|
||||
|
||||
for (int i = start; i < _nameHashes.Count; ++i)
|
||||
{
|
||||
if (_nameHashes[i] == hash)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<int> FindByPrefix(string name, int start)
|
||||
{
|
||||
int compChars = Math.Min(name.Length, 4);
|
||||
string compStr = name.Substring(0, compChars).ToUpperInvariant() + "\0\0\0\0";
|
||||
|
||||
for (int i = start; i < _nameHashes.Count; ++i)
|
||||
{
|
||||
bool match = true;
|
||||
uint hash = _nameHashes[i];
|
||||
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
char ch = (char)((hash >> (j * 8)) & 0xFF);
|
||||
if (char.ToUpperInvariant(ch) != compStr[j])
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyFinder : IComparer<int>
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private string _searchName;
|
||||
|
||||
public KeyFinder(RegistryHive hive, string searchName)
|
||||
{
|
||||
_hive = hive;
|
||||
_searchName = searchName;
|
||||
}
|
||||
|
||||
public int CellIndex { get; set; }
|
||||
|
||||
#region IComparer<int> Members
|
||||
|
||||
public int Compare(int x, int y)
|
||||
{
|
||||
// TODO: Be more efficient at ruling out no-hopes by using the hash values
|
||||
KeyNodeCell cell = _hive.GetCell<KeyNodeCell>(x);
|
||||
int result = string.Compare(((KeyNodeCell)cell).Name, _searchName, StringComparison.OrdinalIgnoreCase);
|
||||
if (result == 0)
|
||||
{
|
||||
CellIndex = x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
internal sealed class SubKeyIndirectListCell : ListCell
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private string _listType;
|
||||
private List<int> _listIndexes;
|
||||
|
||||
public SubKeyIndirectListCell(RegistryHive hive, int index)
|
||||
: base(index)
|
||||
{
|
||||
_hive = hive;
|
||||
}
|
||||
|
||||
public string ListType
|
||||
{
|
||||
get { return _listType; }
|
||||
}
|
||||
|
||||
public List<int> CellIndexes
|
||||
{
|
||||
get { return _listIndexes; }
|
||||
}
|
||||
|
||||
public override int Size
|
||||
{
|
||||
get { return 4 + (_listIndexes.Count * 4); }
|
||||
}
|
||||
|
||||
internal override int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
int total = 0;
|
||||
foreach (var cellIndex in _listIndexes)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(cellIndex);
|
||||
ListCell listCell = cell as ListCell;
|
||||
if (listCell != null)
|
||||
{
|
||||
total += listCell.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
public override int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
_listType = Utilities.BytesToString(buffer, offset, 2);
|
||||
int numElements = Utilities.ToInt16LittleEndian(buffer, offset + 2);
|
||||
_listIndexes = new List<int>(numElements);
|
||||
|
||||
for (int i = 0; i < numElements; ++i)
|
||||
{
|
||||
_listIndexes.Add(Utilities.ToInt32LittleEndian(buffer, offset + 0x4 + (i * 0x4)));
|
||||
}
|
||||
|
||||
return 4 + (_listIndexes.Count * 4);
|
||||
}
|
||||
|
||||
public override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.StringToBytes(_listType, buffer, offset, 2);
|
||||
Utilities.WriteBytesLittleEndian((ushort)_listIndexes.Count, buffer, offset + 2);
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(_listIndexes[i], buffer, offset + 4 + (i * 4));
|
||||
}
|
||||
}
|
||||
|
||||
internal override int FindKey(string name, out int cellIndex)
|
||||
{
|
||||
if (_listIndexes.Count <= 0)
|
||||
{
|
||||
cellIndex = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check first and last, to early abort if the name is outside the range of this list
|
||||
int result = DoFindKey(name, 0, out cellIndex);
|
||||
if (result <= 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = DoFindKey(name, _listIndexes.Count - 1, out cellIndex);
|
||||
if (result >= 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
KeyFinder finder = new KeyFinder(_hive, name);
|
||||
int idx = _listIndexes.BinarySearch(-1, finder);
|
||||
cellIndex = finder.CellIndex;
|
||||
return (idx < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
internal override void EnumerateKeys(List<string> names)
|
||||
{
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(_listIndexes[i]);
|
||||
ListCell listCell = cell as ListCell;
|
||||
if (listCell != null)
|
||||
{
|
||||
listCell.EnumerateKeys(names);
|
||||
}
|
||||
else
|
||||
{
|
||||
names.Add(((KeyNodeCell)cell).Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override IEnumerable<KeyNodeCell> EnumerateKeys()
|
||||
{
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(_listIndexes[i]);
|
||||
ListCell listCell = cell as ListCell;
|
||||
if (listCell != null)
|
||||
{
|
||||
foreach (var keyNodeCell in listCell.EnumerateKeys())
|
||||
{
|
||||
yield return keyNodeCell;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return (KeyNodeCell)cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override int LinkSubKey(string name, int cellIndex)
|
||||
{
|
||||
// Look for the first sublist that has a subkey name greater than name
|
||||
if (ListType == "ri")
|
||||
{
|
||||
if (_listIndexes.Count == 0)
|
||||
{
|
||||
throw new NotImplementedException("Empty indirect list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < _listIndexes.Count - 1; ++i)
|
||||
{
|
||||
int tempIndex;
|
||||
ListCell cell = _hive.GetCell<ListCell>(_listIndexes[i]);
|
||||
if (cell.FindKey(name, out tempIndex) <= 0)
|
||||
{
|
||||
_listIndexes[i] = cell.LinkSubKey(name, cellIndex);
|
||||
return _hive.UpdateCell(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
ListCell lastCell = _hive.GetCell<ListCell>(_listIndexes[_listIndexes.Count - 1]);
|
||||
_listIndexes[_listIndexes.Count - 1] = lastCell.LinkSubKey(name, cellIndex);
|
||||
return _hive.UpdateCell(this, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
KeyNodeCell cell = _hive.GetCell<KeyNodeCell>(_listIndexes[i]);
|
||||
if (string.Compare(name, cell.Name, StringComparison.OrdinalIgnoreCase) < 0)
|
||||
{
|
||||
_listIndexes.Insert(i, cellIndex);
|
||||
return _hive.UpdateCell(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
_listIndexes.Add(cellIndex);
|
||||
return _hive.UpdateCell(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
internal override int UnlinkSubKey(string name)
|
||||
{
|
||||
if (ListType == "ri")
|
||||
{
|
||||
if (_listIndexes.Count == 0)
|
||||
{
|
||||
throw new NotImplementedException("Empty indirect list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
int tempIndex;
|
||||
ListCell cell = _hive.GetCell<ListCell>(_listIndexes[i]);
|
||||
if (cell.FindKey(name, out tempIndex) <= 0)
|
||||
{
|
||||
_listIndexes[i] = cell.UnlinkSubKey(name);
|
||||
if (cell.Count == 0)
|
||||
{
|
||||
_hive.FreeCell(_listIndexes[i]);
|
||||
_listIndexes.RemoveAt(i);
|
||||
}
|
||||
|
||||
return _hive.UpdateCell(this, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _listIndexes.Count; ++i)
|
||||
{
|
||||
KeyNodeCell cell = _hive.GetCell<KeyNodeCell>(_listIndexes[i]);
|
||||
if (string.Compare(name, cell.Name, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
_listIndexes.RemoveAt(i);
|
||||
return _hive.UpdateCell(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Index;
|
||||
}
|
||||
|
||||
private int DoFindKey(string name, int listIndex, out int cellIndex)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(_listIndexes[listIndex]);
|
||||
ListCell listCell = cell as ListCell;
|
||||
if (listCell != null)
|
||||
{
|
||||
return listCell.FindKey(name, out cellIndex);
|
||||
}
|
||||
|
||||
cellIndex = _listIndexes[listIndex];
|
||||
return string.Compare(name, ((KeyNodeCell)cell).Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private class KeyFinder : IComparer<int>
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private string _searchName;
|
||||
|
||||
public KeyFinder(RegistryHive hive, string searchName)
|
||||
{
|
||||
_hive = hive;
|
||||
_searchName = searchName;
|
||||
}
|
||||
|
||||
public int CellIndex { get; set; }
|
||||
|
||||
#region IComparer<int> Members
|
||||
|
||||
public int Compare(int x, int y)
|
||||
{
|
||||
Cell cell = _hive.GetCell<Cell>(x);
|
||||
ListCell listCell = cell as ListCell;
|
||||
|
||||
int result;
|
||||
if (listCell != null)
|
||||
{
|
||||
int cellIndex;
|
||||
result = listCell.FindKey(_searchName, out cellIndex);
|
||||
if (result == 0)
|
||||
{
|
||||
CellIndex = cellIndex;
|
||||
}
|
||||
|
||||
return -result;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = string.Compare(((KeyNodeCell)cell).Name, _searchName, StringComparison.OrdinalIgnoreCase);
|
||||
if (result == 0)
|
||||
{
|
||||
CellIndex = x;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
internal sealed class ValueCell : Cell
|
||||
{
|
||||
private int _dataLength;
|
||||
private int _dataIndex;
|
||||
private RegistryValueType _type;
|
||||
private ValueFlags _flags;
|
||||
private string _name;
|
||||
|
||||
public ValueCell(string name)
|
||||
: this(-1)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public ValueCell(int index)
|
||||
: base(index)
|
||||
{
|
||||
_dataIndex = -1;
|
||||
}
|
||||
|
||||
public int DataLength
|
||||
{
|
||||
get { return _dataLength; }
|
||||
set { _dataLength = value; }
|
||||
}
|
||||
|
||||
public int DataIndex
|
||||
{
|
||||
get { return _dataIndex; }
|
||||
set { _dataIndex = value; }
|
||||
}
|
||||
|
||||
public RegistryValueType DataType
|
||||
{
|
||||
get { return _type; }
|
||||
set { _type = value; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public override int Size
|
||||
{
|
||||
get { return 0x14 + (string.IsNullOrEmpty(_name) ? 0 : _name.Length); }
|
||||
}
|
||||
|
||||
public override int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
int nameLen = Utilities.ToUInt16LittleEndian(buffer, offset + 0x02);
|
||||
_dataLength = Utilities.ToInt32LittleEndian(buffer, offset + 0x04);
|
||||
_dataIndex = Utilities.ToInt32LittleEndian(buffer, offset + 0x08);
|
||||
_type = (RegistryValueType)Utilities.ToInt32LittleEndian(buffer, offset + 0x0C);
|
||||
_flags = (ValueFlags)Utilities.ToUInt16LittleEndian(buffer, offset + 0x10);
|
||||
|
||||
if ((_flags & ValueFlags.Named) != 0)
|
||||
{
|
||||
_name = Utilities.BytesToString(buffer, offset + 0x14, nameLen).Trim('\0');
|
||||
}
|
||||
|
||||
return 0x14 + nameLen;
|
||||
}
|
||||
|
||||
public override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
int nameLen;
|
||||
|
||||
if (string.IsNullOrEmpty(_name))
|
||||
{
|
||||
_flags &= ~ValueFlags.Named;
|
||||
nameLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_flags |= ValueFlags.Named;
|
||||
nameLen = _name.Length;
|
||||
}
|
||||
|
||||
Utilities.StringToBytes("vk", buffer, offset, 2);
|
||||
Utilities.WriteBytesLittleEndian(nameLen, buffer, offset + 0x02);
|
||||
Utilities.WriteBytesLittleEndian(_dataLength, buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(_dataIndex, buffer, offset + 0x08);
|
||||
Utilities.WriteBytesLittleEndian((int)_type, buffer, offset + 0x0C);
|
||||
Utilities.WriteBytesLittleEndian((ushort)_flags, buffer, offset + 0x10);
|
||||
if (nameLen != 0)
|
||||
{
|
||||
Utilities.StringToBytes(_name, buffer, offset + 0x14, nameLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
[Flags]
|
||||
internal enum ValueFlags : ushort
|
||||
{
|
||||
Named = 0x0001,
|
||||
Unknown0002 = 0x0002,
|
||||
Unknown0004 = 0x0004,
|
||||
Unknown0008 = 0x0008,
|
||||
Unknown0010 = 0x0010,
|
||||
Unknown0020 = 0x0020,
|
||||
Unknown0040 = 0x0040,
|
||||
Unknown0080 = 0x0080,
|
||||
Unknown0100 = 0x0100,
|
||||
Unknown0200 = 0x0200,
|
||||
Unknown0400 = 0x0400,
|
||||
Unknown0800 = 0x0800,
|
||||
Unknown1000 = 0x1000,
|
||||
Unknown2000 = 0x2000,
|
||||
Unknown4000 = 0x4000,
|
||||
Unknown8000 = 0x8000
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user