2021-09-20 18:20:01 +02:00

579 lines
17 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.ObjectModel;
using System.Text;
namespace dnSpy.Contracts.Hex.Files.DotNet {
/// <summary>
/// #GUID heap record data
/// </summary>
public sealed class GuidHeapRecordData : GuidData {
/// <summary>
/// Gets the heap
/// </summary>
public GUIDHeap Heap { get; }
/// <summary>
/// Gets the GUID index (1-based)
/// </summary>
public uint Index { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
/// <param name="heap">Owner heap</param>
/// <param name="index">Guid index (1-based)</param>
public GuidHeapRecordData(HexBuffer buffer, HexPosition position, GUIDHeap heap, uint index)
: base(buffer, position) {
if (index == 0)
throw new ArgumentOutOfRangeException(nameof(index));
Heap = heap ?? throw new ArgumentNullException(nameof(heap));
Index = index;
}
}
/// <summary>
/// #Strings heap record data
/// </summary>
public sealed class StringsHeapRecordData : StructureData {
const string NAME = "StringZ";
/// <summary>
/// Gets the owner heap
/// </summary>
public StringsHeap Heap { get; }
/// <summary>
/// Gets tokens of records referencing this string
/// </summary>
public ReadOnlyCollection<uint> Tokens { get; }
/// <summary>
/// Gets the string
/// </summary>
public StructField<StringData> String { get; }
/// <summary>
/// Gets the terminator or null if there's none
/// </summary>
public StructField<ByteData>? Terminator { get; }
/// <summary>
/// Gets the fields
/// </summary>
protected override BufferField[] Fields { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="stringSpan">Span of string, not including the terminating zero</param>
/// <param name="hasTerminatingZero">true if there's a terminating zero, false if there's no terminating zero
/// or if the string is too long</param>
/// <param name="heap">Owner heap</param>
/// <param name="tokens">Tokens of records referencing this string</param>
public StringsHeapRecordData(HexBuffer buffer, HexSpan stringSpan, bool hasTerminatingZero, StringsHeap heap, uint[] tokens)
: base(NAME, new HexBufferSpan(buffer, HexSpan.FromBounds(stringSpan.Start, stringSpan.End + (hasTerminatingZero ? 1 : 0)))) {
if (tokens is null)
throw new ArgumentNullException(nameof(tokens));
Heap = heap ?? throw new ArgumentNullException(nameof(heap));
Tokens = new ReadOnlyCollection<uint>(tokens);
String = new StructField<StringData>("String", new StringData(new HexBufferSpan(buffer, stringSpan), Encoding.UTF8));
if (hasTerminatingZero)
Terminator = new StructField<ByteData>("Terminator", new ByteData(buffer, stringSpan.End));
if (Terminator is not null) {
Fields = new BufferField[] {
String,
Terminator,
};
}
else {
Fields = new BufferField[] {
String,
};
}
}
}
/// <summary>
/// #US heap record data
/// </summary>
public sealed class USHeapRecordData : StructureData {
const string NAME = "US";
/// <summary>
/// Gets the owner heap
/// </summary>
public USHeap Heap { get; }
/// <summary>
/// Gets the length
/// </summary>
public StructField<BlobEncodedUInt32Data> Length { get; }
/// <summary>
/// Gets the string data
/// </summary>
public StructField<StringData> String { get; }
/// <summary>
/// Gets the terminal byte or null if none exists
/// </summary>
public StructField<ByteData>? TerminalByte { get; }
/// <summary>
/// Gets the fields
/// </summary>
protected override BufferField[] Fields { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="lengthSpan">Span of length</param>
/// <param name="stringSpan">Span of string data</param>
/// <param name="terminalByteSpan">Span of terminal byte (0 or 1 byte)</param>
/// <param name="heap">Owner heap</param>
public USHeapRecordData(HexBuffer buffer, HexSpan lengthSpan, HexSpan stringSpan, HexSpan terminalByteSpan, USHeap heap)
: base(NAME, new HexBufferSpan(buffer, HexSpan.FromBounds(lengthSpan.Start, terminalByteSpan.End))) {
if (lengthSpan.End != stringSpan.Start)
throw new ArgumentOutOfRangeException(nameof(stringSpan));
if (stringSpan.End != terminalByteSpan.Start)
throw new ArgumentOutOfRangeException(nameof(stringSpan));
if (!terminalByteSpan.IsEmpty && terminalByteSpan.Length != 1)
throw new ArgumentOutOfRangeException(nameof(terminalByteSpan));
Heap = heap ?? throw new ArgumentNullException(nameof(heap));
Length = new StructField<BlobEncodedUInt32Data>("Length", new BlobEncodedUInt32Data(new HexBufferSpan(buffer, lengthSpan)));
String = new StructField<StringData>("String", new StringData(new HexBufferSpan(buffer, stringSpan), Encoding.Unicode));
if (!terminalByteSpan.IsEmpty)
TerminalByte = new StructField<ByteData>("TerminalByte", new ByteData(new HexBufferSpan(buffer, terminalByteSpan)));
if (TerminalByte is not null) {
Fields = new BufferField[] {
Length,
String,
TerminalByte,
};
}
else {
Fields = new BufferField[] {
Length,
String,
};
}
}
}
/// <summary>
/// #Blob heap record data
/// </summary>
public sealed class BlobHeapRecordData : StructureData {
const string NAME = "Blob";
/// <summary>
/// Gets the owner heap
/// </summary>
public BlobHeap Heap { get; }
/// <summary>
/// Gets the tokens referencing the blob or an empty collection if none (eg. referenced from data in the #Blob)
/// </summary>
public ReadOnlyCollection<uint> Tokens { get; }
/// <summary>
/// Gets the length
/// </summary>
public StructField<BlobEncodedUInt32Data> Length { get; }
/// <summary>
/// Gets the data
/// </summary>
public StructField<BufferData> Data { get; }
/// <summary>
/// Gets the fields
/// </summary>
protected override BufferField[] Fields { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="span">Span</param>
/// <param name="lengthSpan">Span of length</param>
/// <param name="data">Data</param>
/// <param name="tokens">Tokens referencing this blob or an empty collection</param>
/// <param name="heap">Owner heap</param>
public BlobHeapRecordData(HexBuffer buffer, HexSpan span, HexSpan lengthSpan, BufferData data, ReadOnlyCollection<uint> tokens, BlobHeap heap)
: base(NAME, new HexBufferSpan(buffer, span)) {
if (lengthSpan.Start != span.Start)
throw new ArgumentOutOfRangeException(nameof(lengthSpan));
if (data is null)
throw new ArgumentNullException(nameof(data));
Heap = heap ?? throw new ArgumentNullException(nameof(heap));
Tokens = tokens ?? throw new ArgumentNullException(nameof(tokens));
Length = new StructField<BlobEncodedUInt32Data>("Length", new BlobEncodedUInt32Data(new HexBufferSpan(buffer, lengthSpan)));
Data = new StructField<BufferData>("Data", data);
Fields = new BufferField[] {
Length,
Data,
};
}
}
/// <summary>
/// #Strings heap reference
/// </summary>
public abstract class StringsHeapData : SimpleData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
protected StringsHeapData(HexBufferSpan span)
: base(span) {
}
/// <summary>
/// Reads the rid
/// </summary>
/// <returns></returns>
protected abstract uint ReadOffset();
/// <summary>
/// Returns the span the field value references or null. The span can be empty.
/// </summary>
/// <param name="file">File</param>
/// <returns></returns>
public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) {
var stringsStream = file.GetHeaders<DotNetMetadataHeaders>()?.StringsStream;
if (stringsStream is null)
return null;
uint offset = ReadOffset();
if (offset >= stringsStream.Span.Length)
return null;
var pos = stringsStream.Span.Span.Start + offset;
int len = GetStringLength(pos, stringsStream.Span.Span.End);
return new HexSpan(pos, (ulong)len);
}
int GetStringLength(HexPosition position, HexPosition heapEnd) {
var buffer = Span.Buffer;
const int MAX_LEN = 0x400;
var end = HexPosition.Min(heapEnd, position + MAX_LEN);
var start = position;
while (position < end) {
if (buffer.ReadByte(position++) == 0)
break;
}
return (int)(position - start).ToUInt64();
}
}
/// <summary>
/// 16-bit #Strings heap reference
/// </summary>
public class StringsHeapData16 : StringsHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public StringsHeapData16(HexBufferSpan span)
: base(span) {
if (span.Length != 2)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public StringsHeapData16(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 2))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadOffset());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadOffset() => Span.Buffer.ReadUInt16(Span.Start);
}
/// <summary>
/// 32-bit #Strings heap reference
/// </summary>
public class StringsHeapData32 : StringsHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public StringsHeapData32(HexBufferSpan span)
: base(span) {
if (span.Length != 4)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public StringsHeapData32(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 4))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadOffset());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadOffset() => Span.Buffer.ReadUInt32(Span.Start);
}
/// <summary>
/// #Blob heap reference
/// </summary>
public abstract class BlobHeapData : SimpleData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
protected BlobHeapData(HexBufferSpan span)
: base(span) {
}
/// <summary>
/// Reads the rid
/// </summary>
/// <returns></returns>
protected abstract uint ReadOffset();
/// <summary>
/// Returns the span the field value references or null. The span can be empty.
/// </summary>
/// <param name="file">File</param>
/// <returns></returns>
public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) {
var blobStream = file.GetHeaders<DotNetMetadataHeaders>()?.BlobStream;
if (blobStream is null)
return null;
uint offset = ReadOffset();
if (offset >= blobStream.Span.Length)
return null;
var start = blobStream.Span.Span.Start + offset;
var pos = start;
int size = (int)Utils.ReadCompressedUInt32(Span.Buffer, ref pos);
ulong blobSize = pos + size > blobStream.Span.Span.End ? 0 : (ulong)size + (pos - start).ToUInt64();
return new HexSpan(start, blobSize);
}
}
/// <summary>
/// 16-bit #Blob heap reference
/// </summary>
public class BlobHeapData16 : BlobHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public BlobHeapData16(HexBufferSpan span)
: base(span) {
if (span.Length != 2)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public BlobHeapData16(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 2))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadOffset());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadOffset() => Span.Buffer.ReadUInt16(Span.Start);
}
/// <summary>
/// 32-bit #Blob heap reference
/// </summary>
public class BlobHeapData32 : BlobHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public BlobHeapData32(HexBufferSpan span)
: base(span) {
if (span.Length != 4)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public BlobHeapData32(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 4))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadOffset());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadOffset() => Span.Buffer.ReadUInt32(Span.Start);
}
/// <summary>
/// #GUID heap reference
/// </summary>
public abstract class GUIDHeapData : SimpleData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
protected GUIDHeapData(HexBufferSpan span)
: base(span) {
}
/// <summary>
/// Reads the rid
/// </summary>
/// <returns></returns>
protected abstract uint ReadIndex();
/// <summary>
/// Returns the span the field value references or null. The span can be empty.
/// </summary>
/// <param name="file">File</param>
/// <returns></returns>
public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) {
var guidStream = file.GetHeaders<DotNetMetadataHeaders>()?.GUIDStream;
if (guidStream is null)
return null;
uint index = ReadIndex();
if (index == 0 || !guidStream.IsValidIndex(index))
return null;
return new HexSpan(guidStream.Span.Span.Start + (index - 1) * 16, 16);
}
}
/// <summary>
/// 16-bit #GUID heap reference
/// </summary>
public class GUIDHeapData16 : GUIDHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public GUIDHeapData16(HexBufferSpan span)
: base(span) {
if (span.Length != 2)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public GUIDHeapData16(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 2))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadIndex());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadIndex() => Span.Buffer.ReadUInt16(Span.Start);
}
/// <summary>
/// 32-bit #GUID heap reference
/// </summary>
public class GUIDHeapData32 : GUIDHeapData {
/// <summary>
/// Constructor
/// </summary>
/// <param name="span">Data span</param>
public GUIDHeapData32(HexBufferSpan span)
: base(span) {
if (span.Length != 4)
throw new ArgumentOutOfRangeException(nameof(span));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="position">Position</param>
public GUIDHeapData32(HexBuffer buffer, HexPosition position)
: this(new HexBufferSpan(buffer, new HexSpan(position, 4))) {
}
/// <summary>
/// Writes the value
/// </summary>
/// <param name="formatter">Formatter</param>
public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadIndex());
/// <summary>
/// Reads the token value
/// </summary>
/// <returns></returns>
protected override uint ReadIndex() => Span.Buffer.ReadUInt32(Span.Start);
}
}