mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-19 22:00:12 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,383 @@
|
||||
//
|
||||
// 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.Ntfs
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
internal sealed class SecurityDescriptors : IDiagnosticTraceable
|
||||
{
|
||||
// File consists of pairs of duplicate blocks (one after the other), providing
|
||||
// redundancy. When a pair is full, the next pair is used.
|
||||
private const int BlockSize = 0x40000;
|
||||
|
||||
private File _file;
|
||||
private IndexView<HashIndexKey, HashIndexData> _hashIndex;
|
||||
private IndexView<IdIndexKey, IdIndexData> _idIndex;
|
||||
private uint _nextId;
|
||||
private long _nextSpace;
|
||||
|
||||
public SecurityDescriptors(File file)
|
||||
{
|
||||
_file = file;
|
||||
_hashIndex = new IndexView<HashIndexKey, HashIndexData>(file.GetIndex("$SDH"));
|
||||
_idIndex = new IndexView<IdIndexKey, IdIndexData>(file.GetIndex("$SII"));
|
||||
|
||||
foreach (var entry in _idIndex.Entries)
|
||||
{
|
||||
if (entry.Key.Id > _nextId)
|
||||
{
|
||||
_nextId = entry.Key.Id;
|
||||
}
|
||||
|
||||
long end = entry.Value.SdsOffset + entry.Value.SdsLength;
|
||||
if (end > _nextSpace)
|
||||
{
|
||||
_nextSpace = end;
|
||||
}
|
||||
}
|
||||
|
||||
if (_nextId == 0)
|
||||
{
|
||||
_nextId = 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextId++;
|
||||
}
|
||||
|
||||
_nextSpace = Utilities.RoundUp(_nextSpace, 16);
|
||||
}
|
||||
|
||||
public static SecurityDescriptors Initialize(File file)
|
||||
{
|
||||
file.CreateIndex("$SDH", (AttributeType)0, AttributeCollationRule.SecurityHash);
|
||||
file.CreateIndex("$SII", (AttributeType)0, AttributeCollationRule.UnsignedLong);
|
||||
file.CreateStream(AttributeType.Data, "$SDS");
|
||||
|
||||
return new SecurityDescriptors(file);
|
||||
}
|
||||
|
||||
public RawSecurityDescriptor GetDescriptorById(uint id)
|
||||
{
|
||||
IdIndexData data;
|
||||
if (_idIndex.TryGetValue(new IdIndexKey(id), out data))
|
||||
{
|
||||
return ReadDescriptor(data).Descriptor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public uint AddDescriptor(RawSecurityDescriptor newDescriptor)
|
||||
{
|
||||
// Search to see if this is a known descriptor
|
||||
SecurityDescriptor newDescObj = new SecurityDescriptor(newDescriptor);
|
||||
uint newHash = newDescObj.CalcHash();
|
||||
byte[] newByteForm = new byte[newDescObj.Size];
|
||||
newDescObj.WriteTo(newByteForm, 0);
|
||||
|
||||
foreach (var entry in _hashIndex.FindAll(new HashFinder(newHash)))
|
||||
{
|
||||
SecurityDescriptor stored = ReadDescriptor(entry.Value);
|
||||
|
||||
byte[] storedByteForm = new byte[stored.Size];
|
||||
stored.WriteTo(storedByteForm, 0);
|
||||
|
||||
if (Utilities.AreEqual(newByteForm, storedByteForm))
|
||||
{
|
||||
return entry.Value.Id;
|
||||
}
|
||||
}
|
||||
|
||||
long offset = _nextSpace;
|
||||
|
||||
// Write the new descriptor to the end of the existing descriptors
|
||||
SecurityDescriptorRecord record = new SecurityDescriptorRecord();
|
||||
record.SecurityDescriptor = newByteForm;
|
||||
record.Hash = newHash;
|
||||
record.Id = _nextId;
|
||||
|
||||
// If we'd overflow into our duplicate block, skip over it to the
|
||||
// start of the next block
|
||||
if (((offset + record.Size) / BlockSize) % 2 == 1)
|
||||
{
|
||||
_nextSpace = Utilities.RoundUp(offset, BlockSize * 2);
|
||||
offset = _nextSpace;
|
||||
}
|
||||
|
||||
record.OffsetInFile = offset;
|
||||
|
||||
byte[] buffer = new byte[record.Size];
|
||||
record.WriteTo(buffer, 0);
|
||||
|
||||
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.ReadWrite))
|
||||
{
|
||||
s.Position = _nextSpace;
|
||||
s.Write(buffer, 0, buffer.Length);
|
||||
s.Position = BlockSize + _nextSpace;
|
||||
s.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
// Make the next descriptor land at the end of this one
|
||||
_nextSpace = Utilities.RoundUp(_nextSpace + buffer.Length, 16);
|
||||
_nextId++;
|
||||
|
||||
// Update the indexes
|
||||
HashIndexData hashIndexData = new HashIndexData();
|
||||
hashIndexData.Hash = record.Hash;
|
||||
hashIndexData.Id = record.Id;
|
||||
hashIndexData.SdsOffset = record.OffsetInFile;
|
||||
hashIndexData.SdsLength = (int)record.EntrySize;
|
||||
|
||||
HashIndexKey hashIndexKey = new HashIndexKey();
|
||||
hashIndexKey.Hash = record.Hash;
|
||||
hashIndexKey.Id = record.Id;
|
||||
|
||||
_hashIndex[hashIndexKey] = hashIndexData;
|
||||
|
||||
IdIndexData idIndexData = new IdIndexData();
|
||||
idIndexData.Hash = record.Hash;
|
||||
idIndexData.Id = record.Id;
|
||||
idIndexData.SdsOffset = record.OffsetInFile;
|
||||
idIndexData.SdsLength = (int)record.EntrySize;
|
||||
|
||||
IdIndexKey idIndexKey = new IdIndexKey();
|
||||
idIndexKey.Id = record.Id;
|
||||
|
||||
_idIndex[idIndexKey] = idIndexData;
|
||||
|
||||
_file.UpdateRecordInMft();
|
||||
|
||||
return record.Id;
|
||||
}
|
||||
|
||||
public void Dump(TextWriter writer, string indent)
|
||||
{
|
||||
writer.WriteLine(indent + "SECURITY DESCRIPTORS");
|
||||
|
||||
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.Read))
|
||||
{
|
||||
byte[] buffer = Utilities.ReadFully(s, (int)s.Length);
|
||||
|
||||
foreach (var entry in _idIndex.Entries)
|
||||
{
|
||||
int pos = (int)entry.Value.SdsOffset;
|
||||
|
||||
SecurityDescriptorRecord rec = new SecurityDescriptorRecord();
|
||||
if (!rec.Read(buffer, pos))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string secDescStr = "--unknown--";
|
||||
if (rec.SecurityDescriptor[0] != 0)
|
||||
{
|
||||
RawSecurityDescriptor sd = new RawSecurityDescriptor(rec.SecurityDescriptor, 0);
|
||||
secDescStr = sd.GetSddlForm(AccessControlSections.All);
|
||||
}
|
||||
|
||||
writer.WriteLine(indent + " SECURITY DESCRIPTOR RECORD");
|
||||
writer.WriteLine(indent + " Hash: " + rec.Hash);
|
||||
writer.WriteLine(indent + " Id: " + rec.Id);
|
||||
writer.WriteLine(indent + " File Offset: " + rec.OffsetInFile);
|
||||
writer.WriteLine(indent + " Size: " + rec.EntrySize);
|
||||
writer.WriteLine(indent + " Value: " + secDescStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityDescriptor ReadDescriptor(IndexData data)
|
||||
{
|
||||
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.Read))
|
||||
{
|
||||
s.Position = data.SdsOffset;
|
||||
byte[] buffer = Utilities.ReadFully(s, data.SdsLength);
|
||||
|
||||
SecurityDescriptorRecord record = new SecurityDescriptorRecord();
|
||||
record.Read(buffer, 0);
|
||||
|
||||
return new SecurityDescriptor(new RawSecurityDescriptor(record.SecurityDescriptor, 0));
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class IndexData
|
||||
{
|
||||
public uint Hash;
|
||||
public uint Id;
|
||||
public long SdsOffset;
|
||||
public int SdsLength;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "[Data-Hash:{0},Id:{1},SdsOffset:{2},SdsLength:{3}]", Hash, Id, SdsOffset, SdsLength);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class HashIndexKey : IByteArraySerializable
|
||||
{
|
||||
public uint Hash;
|
||||
public uint Id;
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return 8; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
|
||||
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 4);
|
||||
return 8;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0);
|
||||
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 4);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "[Key-Hash:{0},Id:{1}]", Hash, Id);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class HashIndexData : IndexData, IByteArraySerializable
|
||||
{
|
||||
public int Size
|
||||
{
|
||||
get { return 0x14; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0x00);
|
||||
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0x04);
|
||||
SdsOffset = Utilities.ToInt64LittleEndian(buffer, offset + 0x08);
|
||||
SdsLength = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
|
||||
return 0x14;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0x00);
|
||||
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(SdsOffset, buffer, offset + 0x08);
|
||||
Utilities.WriteBytesLittleEndian(SdsLength, buffer, offset + 0x10);
|
||||
////Array.Copy(new byte[] { (byte)'I', 0, (byte)'I', 0 }, 0, buffer, offset + 0x14, 4);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IdIndexKey : IByteArraySerializable
|
||||
{
|
||||
public uint Id;
|
||||
|
||||
public IdIndexKey()
|
||||
{
|
||||
}
|
||||
|
||||
public IdIndexKey(uint id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return 4; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
|
||||
return 4;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "[Key-Id:{0}]", Id);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IdIndexData : IndexData, IByteArraySerializable
|
||||
{
|
||||
public int Size
|
||||
{
|
||||
get { return 0x14; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0x00);
|
||||
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0x04);
|
||||
SdsOffset = Utilities.ToInt64LittleEndian(buffer, offset + 0x08);
|
||||
SdsLength = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
|
||||
return 0x14;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0x00);
|
||||
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0x04);
|
||||
Utilities.WriteBytesLittleEndian(SdsOffset, buffer, offset + 0x08);
|
||||
Utilities.WriteBytesLittleEndian(SdsLength, buffer, offset + 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
private class HashFinder : IComparable<HashIndexKey>
|
||||
{
|
||||
private uint _toMatch;
|
||||
|
||||
public HashFinder(uint toMatch)
|
||||
{
|
||||
_toMatch = toMatch;
|
||||
}
|
||||
|
||||
public int CompareTo(uint otherHash)
|
||||
{
|
||||
if (_toMatch < otherHash)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (_toMatch > otherHash)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int CompareTo(HashIndexKey other)
|
||||
{
|
||||
return CompareTo(other.Hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user