/*
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 .
*/
using System;
using System.Collections.ObjectModel;
using System.Text;
namespace dnSpy.Contracts.Hex.Files.DotNet {
///
/// A .NET heap
///
public abstract class DotNetHeap {
///
/// Constructor
///
/// Heap span
/// Heap kind
protected DotNetHeap(HexBufferSpan span, DotNetHeapKind heapKind) {
if (span.IsDefault)
throw new ArgumentException();
Span = span;
HeapKind = heapKind;
}
///
/// Gets the heap span
///
public HexBufferSpan Span { get; }
///
/// Gets the heap kind
///
public DotNetHeapKind HeapKind { get; }
///
/// Gets the metadata headers
///
public abstract DotNetMetadataHeaders Metadata { get; }
///
/// Gets a structure or null
///
/// Position
///
public virtual ComplexData? GetStructure(HexPosition position) => null;
///
/// Checks whether is valid. Note that some heaps
/// treat as an index, eg. .
///
/// Offset (or index if #GUID heap)
///
public virtual bool IsValidOffset(uint offset) => offset == 0 || offset < Span.Length;
///
/// Reads a compressed and increments
///
/// Position
///
protected int? ReadCompressedUInt32(ref HexPosition position) {
if (!Span.Contains(position))
return null;
var res = Utils.ReadCompressedUInt32(Span.Buffer, ref position);
if (position > Span.Span.End)
return null;
return (int)res;
}
}
///
/// .NET tables (#~ or #-) heap
///
public abstract class TablesHeap : DotNetHeap {
///
/// Gets the metadata type
///
public TablesHeapType TablesHeapType { get; }
///
/// Gets all metadata table infos
///
public abstract ReadOnlyCollection MDTables { get; }
///
/// Span of header
///
public abstract HexSpan HeaderSpan { get; }
///
/// Span of all tables
///
public abstract HexSpan TablesSpan { get; }
///
/// Gets the header
///
public abstract TablesHeaderData Header { get; }
///
/// Gets the major version, this value is cached
///
public abstract byte MajorVersion { get; }
///
/// Gets the minor version, this value is cached
///
public abstract byte MinorVersion { get; }
///
/// Gets the flags, this value is cached
///
public abstract MDStreamFlags Flags { get; }
///
/// Gets the valid mask, this value is cached
///
public abstract ulong ValidMask { get; }
///
/// Gets the sorted mask, this value is cached
///
public abstract ulong SortedMask { get; }
///
/// Constructor
///
/// Heap span
/// Tables heap type
protected TablesHeap(HexBufferSpan span, TablesHeapType tablesHeapType)
: base(span, DotNetHeapKind.Tables) => TablesHeapType = tablesHeapType;
///
/// Gets a record or null if is invalid
///
/// Token
///
public TableRecordData? GetRecord(uint token) => GetRecord(new MDToken(token));
///
/// Gets a record or null if is invalid
///
/// Token
///
public abstract TableRecordData? GetRecord(MDToken token);
}
///
/// .NET #Strings heap
///
public abstract class StringsHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected StringsHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.Strings) {
}
///
/// Gets the span of the string, not including the terminating zero byte.
/// Returns an empty span if is invalid.
///
/// Offset
///
public HexBufferSpan GetStringSpan(uint offset) => GetStringSpan(offset, int.MaxValue);
///
/// Gets the span of the string, not including the terminating zero byte.
/// Returns an empty span if is invalid.
///
/// Offset
/// Maximum number of bytes to read
///
public HexBufferSpan GetStringSpan(uint offset, int maxByteLength) {
var start = Span.Span.Start + offset;
var pos = start;
var buffer = Span.Buffer;
var end = HexPosition.Min(Span.Span.End, start + maxByteLength);
while (pos < end && buffer.ReadByte(pos) != 0)
pos++;
if (start <= pos)
return new HexBufferSpan(buffer, HexSpan.FromBounds(start, pos));
return new HexBufferSpan(buffer, Span.Span.Start, 0);
}
///
/// Reads string data which should be a UTF-8 encoded string. The array doesn't include the terminating zero.
/// Returns an empty array if is invalid
///
/// Offset
///
public byte[] ReadBytes(uint offset) => ReadBytes(offset, int.MaxValue);
///
/// Reads string data which should be a UTF-8 encoded string. The array doesn't include the terminating zero.
/// Returns an empty array if is invalid
///
/// Offset
/// Maximum number of bytes to read
///
public byte[] ReadBytes(uint offset, int maxByteLength) => GetStringSpan(offset, maxByteLength).GetData();
///
/// Reads a string. Returns an empty string if is invalid
///
/// Offset
///
public string Read(uint offset) => Read(offset, int.MaxValue);
///
/// Reads a string. Returns an empty string if is invalid
///
/// Offset
/// Maximum number of bytes to read
///
public string Read(uint offset, int maxByteLength) => Encoding.UTF8.GetString(ReadBytes(offset, maxByteLength));
}
///
/// .NET #US heap
///
public abstract class USHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected USHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.US) {
}
///
/// Returns the span of the string data or an empty span if is 0 or invalid.
///
/// Offset
///
public HexBufferSpan GetStringSpan(uint offset) {
// The CLR assumes offset 0 contains byte 0x00
if (offset == 0 || !IsValidOffset(offset))
return new HexBufferSpan(Span.Start, 0);
var pos = Span.Start.Position + offset;
int length = ReadCompressedUInt32(ref pos) ?? -1;
if (length < 0)
return new HexBufferSpan(Span.Start, 0);
if (pos + length > Span.End.Position)
return new HexBufferSpan(Span.Start, 0);
return new HexBufferSpan(Span.Buffer, new HexSpan(pos, (ulong)length));
}
///
/// Reads data at . Returns an empty array if is invalid.
/// The returned data doesn't include the compressed data length at .
///
/// Offset
///
public byte[] ReadData(uint offset) => GetStringSpan(offset).GetData();
///
/// Reads the string at . Returns an empty string if
/// is 0 or invalid.
///
/// Offset
///
public string Read(uint offset) => Encoding.Unicode.GetString(ReadData(offset));
}
///
/// .NET #GUID heap
///
public abstract class GUIDHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected GUIDHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.GUID) {
}
///
/// Checks whether is valid. This method is identical to
///
/// Index
///
public bool IsValidIndex(uint index) => IsValidOffset(index);
///
/// Checks whether is valid
///
/// Index
///
public override bool IsValidOffset(uint index) => index == 0 || (ulong)index * 16 <= Span.Length;
///
/// Reads a . Returns null if is 0 or invalid
///
/// Index
///
public Guid? Read(uint index) {
if (index == 0 || !IsValidIndex(index))
return null;
return new Guid(Span.Buffer.ReadBytes(Span.Start.Position + (index - 1) * 16, 16));
}
}
///
/// .NET #Blob heap
///
public abstract class BlobHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected BlobHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.Blob) {
}
///
/// Gets the span of data in this heap. The span doesn't include the compressed data length
/// at . An empty span is returned if
/// is invalid or 0.
///
/// Offset
///
public HexBufferSpan GetDataSpan(uint offset) {
// The CLR assumes offset 0 contains byte 0x00
if (offset == 0 || !IsValidOffset(offset))
return new HexBufferSpan(Span.Start, 0);
var pos = Span.Start.Position + offset;
int length = ReadCompressedUInt32(ref pos) ?? -1;
if (length < 0)
return new HexBufferSpan(Span.Start, 0);
if (pos + length > Span.End.Position)
return new HexBufferSpan(Span.Start, 0);
return new HexBufferSpan(Span.Buffer, new HexSpan(pos, (ulong)length));
}
///
/// Reads data at . Returns an empty array if is invalid.
/// The returned data doesn't include the compressed data length at .
///
/// Offset
///
public byte[] Read(uint offset) => GetDataSpan(offset).GetData();
}
///
/// .NET #! heap
///
public abstract class HotHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected HotHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.Hot) {
}
}
///
/// .NET #Pdb heap
///
public abstract class PdbHeap : DotNetHeap {
///
/// Gets the header
///
public abstract PdbStreamHeaderData Header { get; }
///
/// Gets the PDB id, this value is cached
///
public abstract ReadOnlyCollection PdbId { get; }
///
/// Gets the entry point, this value is cached
///
public abstract MDToken EntryPoint { get; }
///
/// Gets a bit mask of all referenced type system tables, this value is cached
///
public abstract ulong ReferencedTypeSystemTables { get; }
///
/// Gets the rows, this value is cached
///
public abstract ReadOnlyCollection TypeSystemTableRows { get; }
///
/// Constructor
///
/// Heap span
protected PdbHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.Pdb) {
}
}
///
/// Unknown .NET heap
///
public abstract class UnknownHeap : DotNetHeap {
///
/// Constructor
///
/// Heap span
protected UnknownHeap(HexBufferSpan span)
: base(span, DotNetHeapKind.Unknown) {
}
}
}