using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.MemoryMappedFiles; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using AsmResolver.IO; using AsmResolver.Patching; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyMetadata("IsTrimmable", "True")] [assembly: AssemblyCompany("AsmResolver")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Washi 2016-2022")] [assembly: AssemblyDescription("The base library for the AsmResolver executable file inspection toolsuite.")] [assembly: AssemblyFileVersion("5.1.0.0")] [assembly: AssemblyInformationalVersion("5.1.0")] [assembly: AssemblyProduct("AsmResolver")] [assembly: AssemblyTitle("AsmResolver")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Washi1337/AsmResolver")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("5.1.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace AsmResolver { public class ByteArrayEqualityComparer : IEqualityComparer, IComparer { public static ByteArrayEqualityComparer Instance { get; } = new ByteArrayEqualityComparer(); private ByteArrayEqualityComparer() { } public unsafe bool Equals(byte[]? x, byte[]? y) { if (x == y) { return true; } if (x == null || y == null || x.Length != y.Length) { return false; } fixed (byte* ptr = x) { fixed (byte* ptr3 = y) { byte* ptr2 = ptr; byte* ptr4 = ptr3; int num = x.Length; int num2 = 0; while (num2 < num / 8) { if (*(long*)ptr2 != *(long*)ptr4) { return false; } num2++; ptr2 += 8; ptr4 += 8; } if (((uint)num & 4u) != 0) { if (*(int*)ptr2 != *(int*)ptr4) { return false; } ptr2 += 4; ptr4 += 4; } if (((uint)num & 2u) != 0) { if (*(short*)ptr2 != *(short*)ptr4) { return false; } ptr2 += 2; ptr4 += 2; } if (((uint)num & (true ? 1u : 0u)) != 0 && *ptr2 != *ptr4) { return false; } return true; } } } public int GetHashCode(byte[] obj) { int num = 0; foreach (byte b in obj) { num = (num * 31) ^ b; } return num; } public int Compare(byte[]? x, byte[]? y) { if (x == y) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } int num = Math.Min(x.Length, y.Length); for (int i = 0; i < num; i++) { int num2 = x[i].CompareTo(y[i]); if (num2 != 0) { return num2; } } return x.Length.CompareTo(y.Length); } } public class DataSegment : SegmentBase, IReadableSegment, ISegment, IOffsetProvider, IWritable { public byte[] Data { get; } public static DataSegment FromReader(ref BinaryStreamReader reader) { return FromReader(ref reader, (int)(reader.StartOffset + reader.Length - reader.Offset)); } public static DataSegment FromReader(ref BinaryStreamReader reader, int count) { ulong offset = reader.Offset; uint rva = reader.Rva; byte[] array = new byte[count]; reader.ReadBytes(array, 0, count); return new DataSegment(array) { Offset = offset, Rva = rva }; } public DataSegment(byte[] data) { Data = data; } public override uint GetPhysicalSize() { return (uint)Data.Length; } public override void Write(IBinaryStreamWriter writer) { writer.WriteBytes(Data, 0, Data.Length); } public BinaryStreamReader CreateReader(ulong fileOffset, uint size) { if (fileOffset < base.Offset || fileOffset > (ulong)((long)base.Offset + (long)Data.Length)) { throw new ArgumentOutOfRangeException("fileOffset"); } if (fileOffset + size > (ulong)((long)base.Offset + (long)Data.Length)) { throw new EndOfStreamException(); } return new BinaryStreamReader(new ByteArrayDataSource(Data, base.Offset), fileOffset, (uint)(fileOffset - base.Offset + base.Rva), size); } } public class DataSourceSegment : SegmentBase, IReadableSegment, ISegment, IOffsetProvider, IWritable { private readonly IDataSource _dataSource; private readonly ulong _originalOffset; private readonly uint _originalSize; private DisplacedDataSource? _displacedDataSource; public DataSourceSegment(IDataSource dataSource, ulong offset, uint rva, uint size) { _dataSource = dataSource; base.Offset = (_originalOffset = offset); base.Rva = rva; _originalSize = size; } public override void UpdateOffsets(in RelocationParameters parameters) { base.UpdateOffsets(in parameters); long num = (long)(parameters.Offset - _originalOffset); _displacedDataSource = ((num != 0L) ? new DisplacedDataSource(_dataSource, num) : null); } public override uint GetPhysicalSize() { return _originalSize; } public override void Write(IBinaryStreamWriter writer) { CreateReader(base.Offset, _originalSize).WriteToOutput(writer); } public BinaryStreamReader CreateReader(ulong fileOffset, uint size) { if (fileOffset < base.Offset || fileOffset > base.Offset + _dataSource.Length) { throw new ArgumentOutOfRangeException("fileOffset"); } if (fileOffset + size > base.Offset + _dataSource.Length) { throw new EndOfStreamException(); } IDataSource displacedDataSource = _displacedDataSource; return new BinaryStreamReader(displacedDataSource ?? _dataSource, fileOffset, (uint)(fileOffset - base.Offset + base.Rva), size); } } public class DiagnosticBag : IErrorListener { public IList Exceptions { get; } = new List(); public bool HasErrors => Exceptions.Count > 0; public bool IsFatal { get; private set; } public void MarkAsFatal() { IsFatal = true; } public void RegisterException(Exception exception) { Exceptions.Add(exception); } } public class EmptyErrorListener : IErrorListener { public static EmptyErrorListener Instance { get; } = new EmptyErrorListener(); private EmptyErrorListener() { } public void MarkAsFatal() { } public void RegisterException(Exception exception) { } } public interface IErrorListener { void MarkAsFatal(); void RegisterException(Exception exception); } public static class ErrorListenerExtensions { public static void BadImage(this IErrorListener self, string message) { self.RegisterException(new BadImageFormatException(message)); } public static void NotSupported(this IErrorListener self, string message) { self.RegisterException(new NotSupportedException(message)); } public static T? RegisterExceptionAndReturnDefault(this IErrorListener self, Exception exception) { self.RegisterException(exception); return default(T); } public static T? NotSupportedAndReturn(this IErrorListener self) { self.RegisterException(new NotSupportedException()); return default(T); } public static T? NotSupportedAndReturn(this IErrorListener self, string message) { self.RegisterException(new NotSupportedException(message)); return default(T); } public static T? BadImageAndReturn(this IErrorListener self, string message) { self.RegisterException(new BadImageFormatException(message)); return default(T); } } public enum IndexSize { Short = 2, Long = 4 } public interface IOffsetConverter { uint FileOffsetToRva(ulong fileOffset); ulong RvaToFileOffset(uint rva); } public interface IOffsetProvider { ulong Offset { get; } uint Rva { get; } } public interface IReadableSegment : ISegment, IOffsetProvider, IWritable { BinaryStreamReader CreateReader(ulong fileOffset, uint size); } public static class Extensions { private const string ReservedStringCharacters = "\\\"\t\r\n\b"; [ThreadStatic] private static StringBuilder? _buffer; public static BinaryStreamReader CreateReader(this IReadableSegment segment) { return segment.CreateReader(segment.Offset, segment.GetPhysicalSize()); } public static BinaryStreamReader CreateReader(this IReadableSegment segment, ulong fileOffset) { return segment.CreateReader(fileOffset, (uint)(segment.GetPhysicalSize() - (fileOffset - segment.Offset))); } public static byte[] ToArray(this IReadableSegment segment) { return segment.CreateReader().ReadToEnd(); } public static uint Align(this uint value, uint alignment) { alignment--; return (value + alignment) & ~alignment; } public static ulong Align(this ulong value, ulong alignment) { alignment--; return (value + alignment) & ~alignment; } public static uint GetCompressedSize(this uint value) { if (value >= 128) { if (value < 16384) { return 2u; } return 4u; } return 1u; } public static uint Get7BitEncodedSize(this uint value) { if (value < 2097152) { if (value >= 128) { if (value < 16384) { return 2u; } return 3u; } return 1u; } if (value < 268435456) { return 4u; } return 5u; } public static uint GetBinaryFormatterSize(this string value) { return value.GetBinaryFormatterSize(Encoding.UTF8); } public static uint GetBinaryFormatterSize(this string value, Encoding encoding) { uint byteCount = (uint)encoding.GetByteCount(value); return byteCount.Get7BitEncodedSize() + byteCount; } public static string CreateEscapedString(this string literal) { if (_buffer == null) { _buffer = new StringBuilder(literal.Length + 2); } _buffer.Clear(); _buffer.Append('"'); foreach (char value in literal) { if ("\\\"\t\r\n\b".Contains(value)) { _buffer.Append('\\'); } _buffer.Append(value); } _buffer.Append('"'); return _buffer.ToString(); } public static ISegmentReference ToReference(this ISegment segment) { return new SegmentReference(segment); } public static ISegmentReference ToReference(this ISegment segment, int additive) { if (additive != 0) { return new RelativeReference(segment, additive); } return new SegmentReference(segment); } public static byte[] WriteIntoArray(this ISegment segment) { using MemoryStream memoryStream = new MemoryStream(); segment.Write(new BinaryStreamWriter(memoryStream)); return memoryStream.ToArray(); } public static byte[] WriteIntoArray(this ISegment segment, MemoryStreamWriterPool pool) { using MemoryStreamWriterPool.RentedWriter rentedWriter = pool.Rent(); segment.Write(rentedWriter.Writer); return rentedWriter.GetData(); } public static PatchedSegment AsPatchedSegment(this ISegment segment) { return segment.AsPatchedSegment(alwaysCreateNew: false); } public static PatchedSegment AsPatchedSegment(this ISegment segment, bool alwaysCreateNew) { if (alwaysCreateNew) { return new PatchedSegment(segment); } return (segment as PatchedSegment) ?? new PatchedSegment(segment); } } public interface ISegment : IOffsetProvider, IWritable { bool CanUpdateOffsets { get; } uint GetVirtualSize(); void UpdateOffsets(in RelocationParameters parameters); } public interface ISegmentReference : IOffsetProvider { bool CanRead { get; } bool IsBounded { get; } BinaryStreamReader CreateReader(); ISegment? GetSegment(); } public interface ISegmentReferenceFactory { ISegmentReference GetReferenceToRva(uint rva); } public interface ISymbol { ISegmentReference? GetReference(); } public interface IWritable { uint GetPhysicalSize(); void Write(IBinaryStreamWriter writer); } public class LazyVariable { private T? _value; private readonly Func? _getValue; [MemberNotNullWhen(false, "_getValue")] public bool IsInitialized { [MemberNotNullWhen(false, "_getValue")] get; [MemberNotNullWhen(false, "_getValue")] private set; } public T Value { get { if (!IsInitialized) { InitializeValue(); } return _value; } set { lock (this) { _value = value; IsInitialized = true; } } } public LazyVariable(T value) { _value = value; IsInitialized = true; } public LazyVariable(Func getValue) { _getValue = getValue ?? throw new ArgumentNullException("getValue"); } private void InitializeValue() { lock (this) { if (!IsInitialized) { _value = _getValue(); IsInitialized = true; } } } } public readonly struct OffsetRange { public ulong Start { get; } public ulong End { get; } public int Length => (int)(End - Start); public bool IsEmpty => Start == End; public static implicit operator OffsetRange((ulong Start, ulong End) tuple) { return new OffsetRange(tuple.Start, tuple.End); } public OffsetRange(ulong start, ulong end) { if (start > end) { throw new ArgumentException("Start offset must be smaller or equal to end offset."); } Start = start; End = end; } public bool Contains(ulong offset) { if (Start <= offset) { return End > offset; } return false; } public bool Contains(OffsetRange range) { if (Contains(range.Start)) { return Contains(range.End); } return false; } public bool Intersects(OffsetRange other) { if (!Contains(other.Start) && !Contains(other.End) && !other.Contains(Start)) { return other.Contains(End); } return true; } public OffsetRange Intersect(OffsetRange other) { if (!Intersects(other)) { return new OffsetRange(0uL, 0uL); } return new OffsetRange(Math.Max(Start, other.Start), Math.Min(End, other.End)); } public (OffsetRange left, OffsetRange right) Exclude(OffsetRange other) { if (Intersect(other).IsEmpty) { return (this, (End, End)); } if (Contains(other)) { return ((Start, other.Start), (other.End, End)); } if (Contains(other.Start)) { return (new OffsetRange(Start, other.Start), new OffsetRange(other.End, other.End)); } return ((other.Start, other.Start), (other.Start, End)); } public void Deconstruct(out ulong start, out ulong end) { start = Start; end = End; } public override string ToString() { return $"[{Start:X8}, {End:X8})"; } } public sealed class RelativeReference : ISegmentReference, IOffsetProvider { public IOffsetProvider Base { get; } public int Additive { get; } public ulong Offset => Base.Offset + (ulong)Additive; public uint Rva => (uint)(Base.Rva + Additive); public bool CanRead { get { if (Base is ISegmentReference segmentReference) { return segmentReference.CanRead; } return false; } } public bool IsBounded => false; public RelativeReference(IOffsetProvider @base, int additive) { Base = @base; Additive = additive; } public BinaryStreamReader CreateReader() { if (CanRead) { BinaryStreamReader result = ((ISegmentReference)Base).CreateReader(); result.Offset += (ulong)Additive; return result; } throw new InvalidOperationException(); } public ISegment? GetSegment() { throw new InvalidOperationException(); } } public struct RelocationParameters { public ulong ImageBase { get; } public ulong Offset { get; set; } public uint Rva { get; set; } public bool Is32Bit { get; } public bool Is64Bit => !Is32Bit; public RelocationParameters(ulong offset, uint rva) : this(0uL, offset, rva, is32Bit: true) { } public RelocationParameters(ulong imageBase, ulong offset, uint rva, bool is32Bit) { ImageBase = imageBase; Offset = offset; Rva = rva; Is32Bit = is32Bit; } public RelocationParameters WithOffsetRva(ulong offset, uint rva) { return new RelocationParameters(ImageBase, offset, rva, Is32Bit); } public void Align(uint alignment) { Offset = Offset.Align(alignment); Rva = Rva.Align(alignment); } public readonly RelocationParameters WithAdvance(uint count) { return new RelocationParameters(ImageBase, Offset + count, Rva + count, Is32Bit); } public void Advance(uint count) { Offset += count; Rva += count; } public void Advance(uint physicalCount, uint virtualCount) { Offset += physicalCount; Rva += virtualCount; } public override string ToString() { return $"{"ImageBase"}: {ImageBase:X8}, {"Offset"}: {Offset:X8}, {"Rva"}: {Rva:X8}"; } } public abstract class SegmentBase : ISegment, IOffsetProvider, IWritable { public ulong Offset { get; protected set; } public uint Rva { get; protected set; } public bool CanUpdateOffsets => true; public virtual void UpdateOffsets(in RelocationParameters parameters) { Offset = parameters.Offset; Rva = parameters.Rva; } public abstract uint GetPhysicalSize(); public uint GetVirtualSize() { return GetPhysicalSize(); } public abstract void Write(IBinaryStreamWriter writer); } public class SegmentBuilder : ISegment, IOffsetProvider, IWritable, IEnumerable, IEnumerable { [DebuggerDisplay("Segment, Alignment: Alignment")] private readonly struct AlignedSegment { public ISegment Segment { get; } public uint Alignment { get; } public AlignedSegment(ISegment segment, uint alignment) { Segment = segment; Alignment = alignment; } } private readonly List _items = new List(); private uint _physicalSize; private uint _virtualSize; public int Count => _items.Count; public ulong Offset { get; private set; } public uint Rva { get; private set; } public bool CanUpdateOffsets => true; public void Add(ISegment segment) { Add(segment, 1u); } public void Add(ISegment segment, uint alignment) { _items.Add(new AlignedSegment(segment, alignment)); } public void UpdateOffsets(in RelocationParameters parameters) { Offset = parameters.Offset; Rva = parameters.Rva; RelocationParameters parameters2 = parameters; foreach (AlignedSegment item in _items) { parameters2.Align(item.Alignment); item.Segment.UpdateOffsets(in parameters2); uint physicalSize = item.Segment.GetPhysicalSize(); uint virtualSize = item.Segment.GetVirtualSize(); parameters2.Advance(physicalSize, virtualSize); } _physicalSize = (uint)(parameters2.Offset - parameters.Offset); _virtualSize = parameters2.Rva - parameters.Rva; } public uint GetPhysicalSize() { return _physicalSize; } public uint GetVirtualSize() { return _virtualSize; } public void Write(IBinaryStreamWriter writer) { for (int i = 0; i < _items.Count; i++) { AlignedSegment alignedSegment = _items[i]; writer.Align(alignedSegment.Alignment); alignedSegment.Segment.Write(writer); } } public IEnumerator GetEnumerator() { return _items.Select((AlignedSegment s) => s.Segment).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void ISegment.UpdateOffsets(in RelocationParameters parameters) { UpdateOffsets(in parameters); } } public sealed class SegmentReference : ISegmentReference, IOffsetProvider { public static ISegmentReference Null { get; } = new SegmentReference(null); public ulong Offset => Segment?.Offset ?? 0; public uint Rva => Segment?.Rva ?? 0; public bool IsBounded => true; [MemberNotNullWhen(true, "Segment")] public bool CanRead { [MemberNotNullWhen(true, "Segment")] get { return Segment is IReadableSegment; } } public ISegment? Segment { get; } public SegmentReference(ISegment? segment) { Segment = segment; } public BinaryStreamReader CreateReader() { if (!CanRead) { throw new InvalidOperationException("Cannot read the segment using a binary reader."); } return ((IReadableSegment)Segment).CreateReader(); } ISegment? ISegmentReference.GetSegment() { return Segment; } public override string ToString() { return Segment?.ToString() ?? "null"; } } public class Symbol : ISymbol { public ISegmentReference Address { get; } public Symbol(ISegmentReference address) { Address = address; } public ISegmentReference GetReference() { return Address; } public override string? ToString() { return Address.ToString(); } } public sealed class ThrowErrorListener : IErrorListener { public static ThrowErrorListener Instance { get; } = new ThrowErrorListener(); private ThrowErrorListener() { } public void MarkAsFatal() { } public void RegisterException(Exception exception) { throw exception; } } [DebuggerDisplay("{DebugDisplay}")] public sealed class Utf8String : IEquatable, IEquatable, IEquatable, IComparable, IEnumerable, IEnumerable { public static readonly Utf8String Empty = new Utf8String(Array.Empty()); private readonly byte[] _data; private string? _cachedString; public string Value => _cachedString ?? (_cachedString = Encoding.UTF8.GetString(_data)); public int ByteCount => _data.Length; public int Length { get { if (_cachedString != null) { return Value.Length; } return Encoding.UTF8.GetCharCount(_data); } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal string DebugDisplay => Value.CreateEscapedString(); public char this[int index] => Value[index]; public Utf8String(byte[] data) : this(data, 0, data.Length) { } public Utf8String(byte[] data, int index, int count) { _data = new byte[count]; Buffer.BlockCopy(data, index, _data, 0, count); } public Utf8String(string value) { _data = Encoding.UTF8.GetBytes(value); _cachedString = value; } public byte[] GetBytes() { byte[] array = new byte[_data.Length]; GetBytes(array, 0, array.Length); return array; } public int GetBytes(byte[] buffer, int index, int length) { length = Math.Min(length, ByteCount); Buffer.BlockCopy(_data, 0, buffer, index, length); return length; } public byte[] GetBytesUnsafe() { return _data; } public Utf8String Concat(Utf8String? other) { if (IsNullOrEmpty(other)) { return this; } return Concat(other._data); } public Utf8String Concat(string? other) { if (string.IsNullOrEmpty(other)) { return this; } return Concat(Encoding.UTF8.GetBytes(other)); } public Utf8String Concat(byte[]? other) { if (other == null || other.Length == 0) { return this; } byte[] array = new byte[Length + other.Length]; Buffer.BlockCopy(_data, 0, array, 0, _data.Length); Buffer.BlockCopy(other, 0, array, _data.Length, other.Length); return array; } public int IndexOf(char needle) { return Value.IndexOf(needle); } public int IndexOf(char needle, int startIndex) { return Value.IndexOf(needle, startIndex); } public int IndexOf(string needle) { return Value.IndexOf(needle); } public int IndexOf(string needle, int startIndex) { return Value.IndexOf(needle, startIndex); } public int IndexOf(string needle, StringComparison comparison) { return Value.IndexOf(needle, comparison); } public int IndexOf(string needle, int startIndex, StringComparison comparison) { return Value.IndexOf(needle, startIndex, comparison); } public int LastIndexOf(char needle) { return Value.LastIndexOf(needle); } public int LastIndexOf(char needle, int startIndex) { return Value.LastIndexOf(needle, startIndex); } public int LastIndexOf(string needle) { return Value.LastIndexOf(needle); } public int LastIndexOf(string needle, int startIndex) { return Value.LastIndexOf(needle, startIndex); } public int LastIndexOf(string needle, StringComparison comparison) { return Value.LastIndexOf(needle, comparison); } public int LastIndexOf(string needle, int startIndex, StringComparison comparison) { return Value.LastIndexOf(needle, startIndex, comparison); } public bool Contains(string needle) { return IndexOf(needle) >= 0; } public static bool IsNullOrEmpty([NotNullWhen(false)] Utf8String? value) { if ((object)value != null) { return value.ByteCount == 0; } return true; } public bool Equals(Utf8String? other) { if ((object)other != null) { return ByteArrayEqualityComparer.Instance.Equals(_data, other._data); } return false; } public bool Equals(byte[]? other) { if (other != null) { return ByteArrayEqualityComparer.Instance.Equals(_data, other); } return false; } public bool Equals(string? other) { if (other != null) { return Value.Equals(other); } return false; } public int CompareTo(Utf8String? other) { if ((object)other == null) { return 1; } return ByteArrayEqualityComparer.Instance.Compare(_data, other._data); } public IEnumerator GetEnumerator() { return Value.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override bool Equals(object? obj) { if (this != obj) { if (obj is Utf8String other) { return Equals(other); } return false; } return true; } public override int GetHashCode() { return ByteArrayEqualityComparer.Instance.GetHashCode(_data); } public override string ToString() { return Value; } [return: NotNullIfNotNull("value")] public static implicit operator string?(Utf8String? value) { if ((object)value == null) { return null; } if (value.ByteCount == 0) { return string.Empty; } return value.Value; } [return: NotNullIfNotNull("value")] public static implicit operator Utf8String?(string? value) { if (value == null) { return null; } if (value.Length == 0) { return Empty; } return new Utf8String(value); } [return: NotNullIfNotNull("data")] public static implicit operator Utf8String?(byte[]? data) { if (data == null) { return null; } if (data.Length == 0) { return Empty; } return new Utf8String(data); } public static bool operator ==(Utf8String? a, Utf8String? b) { if ((object)a == b) { return true; } if ((object)a == null || (object)b == null) { return false; } return a.Equals(b); } public static bool operator !=(Utf8String? a, Utf8String? b) { return !(a == b); } public static bool operator ==(Utf8String? a, string? b) { if ((object)a == null) { return b == null; } if (b != null) { return a.Equals(b); } return false; } public static bool operator !=(Utf8String? a, string? b) { return !(a == b); } public static bool operator ==(Utf8String? a, byte[]? b) { if ((object)a == null) { return b == null; } if (b != null) { return a.Equals(b); } return false; } public static bool operator !=(Utf8String? a, byte[]? b) { return !(a == b); } public static Utf8String operator +(Utf8String? a, Utf8String? b) { if (IsNullOrEmpty(a)) { if (!IsNullOrEmpty(b)) { return b; } return Empty; } return a.Concat(b); } public static Utf8String operator +(Utf8String? a, string? b) { if (IsNullOrEmpty(a)) { if (!string.IsNullOrEmpty(b)) { return b; } return Empty; } return a.Concat(b); } public static Utf8String operator +(Utf8String? a, byte[]? b) { if (IsNullOrEmpty(a)) { if (b != null && b.Length != 0) { return b; } return Empty; } return a.Concat(b); } } public sealed class VirtualAddress : ISegmentReference, IOffsetProvider { ulong IOffsetProvider.Offset => Rva; public uint Rva { get; } public bool CanRead => false; bool ISegmentReference.IsBounded => false; public VirtualAddress(uint rva) { Rva = rva; } BinaryStreamReader ISegmentReference.CreateReader() { throw new InvalidOperationException(); } ISegment? ISegmentReference.GetSegment() { throw new InvalidOperationException(); } public override string ToString() { return $"0x{Rva:X8}"; } } public class VirtualAddressFactory : ISegmentReferenceFactory { public static VirtualAddressFactory Instance { get; } = new VirtualAddressFactory(); public ISegmentReference GetReferenceToRva(uint rva) { return new VirtualAddress(rva); } } public class VirtualSegment : IReadableSegment, ISegment, IOffsetProvider, IWritable { private uint _rva; public ISegment? PhysicalContents { get; set; } public uint VirtualSize { get; set; } public ulong Offset => PhysicalContents?.Offset ?? 0; public uint Rva => _rva; public bool CanUpdateOffsets => PhysicalContents?.CanUpdateOffsets ?? false; [MemberNotNullWhen(true, "PhysicalContents")] public bool IsReadable { [MemberNotNullWhen(true, "PhysicalContents")] get { return PhysicalContents is IReadableSegment; } } public VirtualSegment(ISegment? physicalContents, uint virtualSize) { PhysicalContents = physicalContents; VirtualSize = virtualSize; } public void UpdateOffsets(in RelocationParameters parameters) { _rva = parameters.Rva; PhysicalContents?.UpdateOffsets(in parameters); } public uint GetPhysicalSize() { return PhysicalContents?.GetPhysicalSize() ?? 0; } public uint GetVirtualSize() { return VirtualSize; } public BinaryStreamReader CreateReader(ulong fileOffset, uint size) { if (!(PhysicalContents is IReadableSegment readableSegment)) { throw new InvalidOperationException("Physical contents of the virtual segment is not readable."); } return readableSegment.CreateReader(fileOffset, size); } public void Write(IBinaryStreamWriter writer) { PhysicalContents?.Write(writer); } void ISegment.UpdateOffsets(in RelocationParameters parameters) { UpdateOffsets(in parameters); } } } namespace AsmResolver.Patching { [DebuggerDisplay("Patch {RelativeOffset} with {NewData.Length} bytes")] public sealed class BytesPatch : IPatch { public uint RelativeOffset { get; } public byte[] NewData { get; } public BytesPatch(uint relativeOffset, byte[] newData) { RelativeOffset = relativeOffset; NewData = newData; } public void Apply(in PatchContext context) { context.Writer.Offset = context.Segment.Offset + RelativeOffset; context.Writer.WriteBytes(NewData); } void IPatch.Apply(in PatchContext context) { Apply(in context); } } public interface IPatch { void Apply(in PatchContext context); } public readonly struct PatchContext { public ISegment Segment { get; } public ulong ImageBase { get; } public IBinaryStreamWriter Writer { get; } public PatchContext(ISegment segment, ulong imageBase, IBinaryStreamWriter writer) { Segment = segment; ImageBase = imageBase; Writer = writer; } } [DebuggerDisplay("Patched {Contents} (Count = {Patches.Count})")] public class PatchedSegment : IReadableSegment, ISegment, IOffsetProvider, IWritable { private ulong _imageBase; public ISegment Contents { get; } public IList Patches { get; } = new List(); public ulong Offset => Contents.Offset; public uint Rva => Contents.Rva; public bool CanUpdateOffsets => Contents.CanUpdateOffsets; public PatchedSegment(ISegment contents) { Contents = contents; } public uint GetPhysicalSize() { return Contents.GetPhysicalSize(); } public uint GetVirtualSize() { return Contents.GetVirtualSize(); } public void UpdateOffsets(in RelocationParameters parameters) { Contents.UpdateOffsets(in parameters); _imageBase = parameters.ImageBase; } public BinaryStreamReader CreateReader(ulong fileOffset, uint size) { if (!(Contents is IReadableSegment readableSegment)) { throw new InvalidOperationException("Segment is not readable."); } return readableSegment.CreateReader(fileOffset, size); } public void Write(IBinaryStreamWriter writer) { Contents.Write(writer); ApplyPatches(writer); } private void ApplyPatches(IBinaryStreamWriter writer) { ulong offset = writer.Offset; for (int i = 0; i < Patches.Count; i++) { IPatch patch = Patches[i]; PatchContext context = new PatchContext(Contents, _imageBase, writer); patch.Apply(in context); } writer.Offset = offset; } public PatchedSegment Patch(IPatch patch) { Patches.Add(patch); return this; } public PatchedSegment Patch(uint relativeOffset, byte[] newData) { Patches.Add(new BytesPatch(relativeOffset, newData)); return this; } void ISegment.UpdateOffsets(in RelocationParameters parameters) { UpdateOffsets(in parameters); } } } namespace AsmResolver.IO { public struct BinaryStreamReader { [ThreadStatic] private static int[]? _buffer; public IDataSource DataSource { get; } public ulong StartOffset { get; } public uint StartRva { get; } public uint Length { get; private set; } public ulong EndOffset => StartOffset + Length; public ulong EndRva => StartRva + Length; public ulong Offset { get; set; } public uint RelativeOffset { get { return (uint)(Offset - StartOffset); } set { Offset = value + StartOffset; } } public uint Rva { get { return RelativeOffset + StartRva; } set { RelativeOffset = value - StartRva; } } public bool IsValid => DataSource != null; public BinaryStreamReader(byte[] data) : this(new ByteArrayDataSource(data)) { } public BinaryStreamReader(IDataSource dataSource) : this(dataSource, 0uL, 0u, (uint)dataSource.Length) { } public BinaryStreamReader(IDataSource dataSource, ulong offset, uint rva, uint length) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (length != 0) { if (!dataSource.IsValidAddress(offset)) { throw new ArgumentOutOfRangeException("offset"); } if (!dataSource.IsValidAddress(offset + length - 1)) { throw new EndOfStreamException("Offset and address reach outside of the boundaries of the data source."); } } DataSource = dataSource; ulong num2 = (Offset = offset); StartOffset = num2; StartRva = rva; Length = length; } public bool CanRead(uint count) { return RelativeOffset + count <= Length; } private void AssertCanRead(uint count) { if (!CanRead(count)) { throw new EndOfStreamException(); } } public int PeekByte() { if (!CanRead(1u)) { return -1; } return DataSource[Offset]; } public byte ReadByte() { AssertCanRead(1u); return DataSource[Offset++]; } public ushort ReadUInt16() { AssertCanRead(2u); ushort result = (ushort)(DataSource[Offset] | (DataSource[Offset + 1] << 8)); Offset += 2uL; return result; } public uint ReadUInt32() { AssertCanRead(4u); int result = DataSource[Offset] | (DataSource[Offset + 1] << 8) | (DataSource[Offset + 2] << 16) | (DataSource[Offset + 3] << 24); Offset += 4uL; return (uint)result; } public ulong ReadUInt64() { AssertCanRead(8u); ulong result = DataSource[Offset] | ((ulong)DataSource[Offset + 1] << 8) | ((ulong)DataSource[Offset + 2] << 16) | ((ulong)DataSource[Offset + 3] << 24) | ((ulong)DataSource[Offset + 4] << 32) | ((ulong)DataSource[Offset + 5] << 40) | ((ulong)DataSource[Offset + 6] << 48) | ((ulong)DataSource[Offset + 7] << 56); Offset += 8uL; return result; } public sbyte ReadSByte() { AssertCanRead(1u); return (sbyte)DataSource[Offset++]; } public short ReadInt16() { AssertCanRead(2u); short result = (short)(DataSource[Offset] | (DataSource[Offset + 1] << 8)); Offset += 2uL; return result; } public int ReadInt32() { AssertCanRead(4u); int result = DataSource[Offset] | (DataSource[Offset + 1] << 8) | (DataSource[Offset + 2] << 16) | (DataSource[Offset + 3] << 24); Offset += 4uL; return result; } public long ReadInt64() { AssertCanRead(8u); ulong result = DataSource[Offset] | ((ulong)DataSource[Offset + 1] << 8) | ((ulong)DataSource[Offset + 2] << 16) | ((ulong)DataSource[Offset + 3] << 24) | ((ulong)DataSource[Offset + 4] << 32) | ((ulong)DataSource[Offset + 5] << 40) | ((ulong)DataSource[Offset + 6] << 48) | ((ulong)DataSource[Offset + 7] << 56); Offset += 8uL; return (long)result; } public unsafe float ReadSingle() { uint num = ReadUInt32(); return *(float*)(&num); } public unsafe double ReadDouble() { ulong num = ReadUInt64(); return *(double*)(&num); } public decimal ReadDecimal() { AssertCanRead(16u); if (_buffer == null) { _buffer = new int[4]; } for (int i = 0; i < 4; i++) { _buffer[i] = ReadInt32(); } return new decimal(_buffer); } public int ReadBytes(byte[] buffer, int index, int count) { int num = DataSource.ReadBytes(Offset, buffer, index, count); Offset += (uint)num; return num; } public IReadableSegment ReadSegment(uint count) { DataSourceSegment result = new DataSourceSegment(DataSource, Offset, Rva, count); Offset += count; return result; } public byte[] ReadToEnd() { byte[] array = new byte[Length - RelativeOffset]; ReadBytes(array, 0, array.Length); return array; } public byte[] ReadBytesUntil(byte delimiter) { return ReadBytesUntil(delimiter, includeDelimiterInReturn: true); } public byte[] ReadBytesUntil(byte delimiter, bool includeDelimiterInReturn) { BinaryStreamReader binaryStreamReader = Fork(); bool num = binaryStreamReader.AdvanceUntil(delimiter, includeDelimiterInReturn); byte[] array = new byte[binaryStreamReader.RelativeOffset - RelativeOffset]; ReadBytes(array, 0, array.Length); if (num) { ReadByte(); } return array; } public bool AdvanceUntil(byte delimiter, bool consumeDelimiter) { while (RelativeOffset < Length) { if (ReadByte() == delimiter) { if (!consumeDelimiter) { RelativeOffset--; return true; } return false; } } return false; } public string ReadAsciiString() { return Encoding.ASCII.GetString(ReadBytesUntil(0, includeDelimiterInReturn: false)); } public string ReadUnicodeString() { StringBuilder stringBuilder = new StringBuilder(); while (true) { char c = (char)ReadUInt16(); if (c == '\0') { break; } stringBuilder.Append(c); } return stringBuilder.ToString(); } public Utf8String ReadUtf8String() { byte[] array = ReadBytesUntil(0, includeDelimiterInReturn: false); if (array.Length == 0) { return Utf8String.Empty; } return new Utf8String(array); } public ulong ReadNativeInt(bool is32Bit) { if (!is32Bit) { return ReadUInt64(); } return ReadUInt32(); } public uint ReadCompressedUInt32() { byte b = ReadByte(); if ((b & 0x80) == 0) { return b; } if ((b & 0x40) == 0) { return (uint)(((b & 0x7F) << 8) | ReadByte()); } return (uint)(((b & 0x3F) << 24) | (ReadByte() << 16) | (ReadByte() << 8) | ReadByte()); } public bool TryReadCompressedUInt32(out uint value) { value = 0u; if (!CanRead(1u)) { return false; } byte b = ReadByte(); Offset--; if (((b & 0x80) == 0 && CanRead(1u)) || ((b & 0x40) == 0 && CanRead(2u)) || CanRead(4u)) { value = ReadCompressedUInt32(); return true; } return false; } public int Read7BitEncodedInt32() { int num = 0; byte b; for (int i = 0; i < 4; i++) { b = ReadByte(); num |= (b & 0x7F) << i * 7; if ((b & 0x80) == 0) { return num; } } b = ReadByte(); if (b > 31) { throw new FormatException("Invalid 7-bit encoded integer."); } return num | (b << 28); } public uint ReadIndex(IndexSize size) { return size switch { IndexSize.Short => ReadUInt16(), IndexSize.Long => ReadUInt32(), _ => throw new ArgumentOutOfRangeException("size"), }; } public Utf8String? ReadSerString() { if (!CanRead(1u) || DataSource[Offset] == byte.MaxValue) { Offset++; return null; } if (!TryReadCompressedUInt32(out var value)) { return null; } byte[] array = new byte[value]; value = (uint)ReadBytes(array, 0, (int)value); return new Utf8String(array, 0, (int)value); } public string ReadBinaryFormatterString() { return ReadBinaryFormatterString(Encoding.UTF8); } public string ReadBinaryFormatterString(Encoding encoding) { int num = Read7BitEncodedInt32(); if (num >= 0) { if (num == 0) { return string.Empty; } byte[] array = new byte[num]; int count = ReadBytes(array, 0, num); return encoding.GetString(array, 0, count); } throw new FormatException("Negative string length."); } public void Align(uint alignment) { Offset = Offset.Align(alignment); } public void AlignRelative(uint alignment) { RelativeOffset = RelativeOffset.Align(alignment); } public readonly BinaryStreamReader Fork() { return this; } public readonly BinaryStreamReader ForkAbsolute(ulong offset) { return ForkAbsolute(offset, (uint)(Length - (offset - StartOffset))); } public readonly BinaryStreamReader ForkAbsolute(ulong offset, uint size) { return new BinaryStreamReader(DataSource, offset, (uint)(StartRva + (offset - StartOffset)), size); } public readonly BinaryStreamReader ForkRelative(uint relativeOffset) { return ForkRelative(relativeOffset, Length - relativeOffset); } public readonly BinaryStreamReader ForkRelative(uint relativeOffset, uint size) { return new BinaryStreamReader(DataSource, StartOffset + relativeOffset, StartRva + relativeOffset, size); } public void ChangeSize(uint newSize) { if (newSize > Length) { throw new EndOfStreamException(); } Length = newSize; } public void WriteToOutput(IBinaryStreamWriter writer) { byte[] array = new byte[4096]; while (RelativeOffset < Length) { int count = (int)Math.Min(array.Length, Length - RelativeOffset); int num = ReadBytes(array, 0, count); if (num == 0) { writer.WriteZeroes((int)(Length - RelativeOffset)); break; } writer.WriteBytes(array, 0, num); } } } public class BinaryStreamWriter : IBinaryStreamWriter { public Stream BaseStream { get; } public ulong Offset { get { return (uint)BaseStream.Position; } set { if (BaseStream.Position != (long)value) { BaseStream.Position = (long)value; } } } public uint Length => (uint)BaseStream.Length; public BinaryStreamWriter(Stream stream) { BaseStream = stream ?? throw new ArgumentNullException("stream"); } public void WriteBytes(byte[] buffer, int startIndex, int count) { BaseStream.Write(buffer, startIndex, count); } public void WriteByte(byte value) { BaseStream.WriteByte(value); } public void WriteUInt16(ushort value) { BaseStream.WriteByte((byte)(value & 0xFFu)); BaseStream.WriteByte((byte)((uint)(value >> 8) & 0xFFu)); } public void WriteUInt32(uint value) { BaseStream.WriteByte((byte)(value & 0xFFu)); BaseStream.WriteByte((byte)((value >> 8) & 0xFFu)); BaseStream.WriteByte((byte)((value >> 16) & 0xFFu)); BaseStream.WriteByte((byte)((value >> 24) & 0xFFu)); } public void WriteUInt64(ulong value) { BaseStream.WriteByte((byte)(value & 0xFF)); BaseStream.WriteByte((byte)((value >> 8) & 0xFF)); BaseStream.WriteByte((byte)((value >> 16) & 0xFF)); BaseStream.WriteByte((byte)((value >> 24) & 0xFF)); BaseStream.WriteByte((byte)((value >> 32) & 0xFF)); BaseStream.WriteByte((byte)((value >> 40) & 0xFF)); BaseStream.WriteByte((byte)((value >> 48) & 0xFF)); BaseStream.WriteByte((byte)((value >> 56) & 0xFF)); } public void WriteSByte(sbyte value) { BaseStream.WriteByte((byte)value); } public void WriteInt16(short value) { BaseStream.WriteByte((byte)((uint)value & 0xFFu)); BaseStream.WriteByte((byte)((uint)(value >> 8) & 0xFFu)); } public void WriteInt32(int value) { BaseStream.WriteByte((byte)((uint)value & 0xFFu)); BaseStream.WriteByte((byte)((uint)(value >> 8) & 0xFFu)); BaseStream.WriteByte((byte)((uint)(value >> 16) & 0xFFu)); BaseStream.WriteByte((byte)((uint)(value >> 24) & 0xFFu)); } public void WriteInt64(long value) { BaseStream.WriteByte((byte)(value & 0xFF)); BaseStream.WriteByte((byte)((value >> 8) & 0xFF)); BaseStream.WriteByte((byte)((value >> 16) & 0xFF)); BaseStream.WriteByte((byte)((value >> 24) & 0xFF)); BaseStream.WriteByte((byte)((value >> 32) & 0xFF)); BaseStream.WriteByte((byte)((value >> 40) & 0xFF)); BaseStream.WriteByte((byte)((value >> 48) & 0xFF)); BaseStream.WriteByte((byte)((value >> 56) & 0xFF)); } public unsafe void WriteSingle(float value) { WriteUInt32(*(uint*)(&value)); } public unsafe void WriteDouble(double value) { WriteUInt64(*(ulong*)(&value)); } public void WriteDecimal(decimal value) { int[] bits = decimal.GetBits(value); WriteInt32(bits[0]); WriteInt32(bits[1]); WriteInt32(bits[2]); WriteInt32(bits[3]); } } public sealed class ByteArrayDataSource : IDataSource { private readonly byte[] _data; public ulong BaseAddress { get; } public byte this[ulong address] => _data[address - BaseAddress]; public ulong Length => (ulong)_data.Length; public ByteArrayDataSource(byte[] data) : this(data, 0uL) { } public ByteArrayDataSource(byte[] data, ulong baseAddress) { _data = data ?? throw new ArgumentNullException("data"); BaseAddress = baseAddress; } [Obsolete("Use the constructor of AsmResolver.IO.BinaryStreamReader instead.")] public static BinaryStreamReader CreateReader(byte[] data) { return new BinaryStreamReader(data); } public bool IsValidAddress(ulong address) { return address - BaseAddress < (ulong)_data.Length; } public int ReadBytes(ulong address, byte[] buffer, int index, int count) { int num = (int)(address - BaseAddress); int num2 = Math.Min(count, _data.Length - num); Buffer.BlockCopy(_data, num, buffer, index, num2); return num2; } } public class ByteArrayFileService : IFileService, IDisposable { private readonly Dictionary _files = new Dictionary(); public IEnumerable GetOpenedFiles() { return _files.Keys; } public IInputFile OpenFile(string filePath) { if (!_files.TryGetValue(filePath, out ByteArrayInputFile value)) { value = new ByteArrayInputFile(filePath); _files.Add(filePath, value); } return value; } public void InvalidateFile(string filePath) { _files.Remove(filePath); } void IDisposable.Dispose() { _files.Clear(); } } public sealed class ByteArrayInputFile : IInputFile, IDisposable { private readonly ByteArrayDataSource _dataSource; public string? FilePath { get; } public uint Length => (uint)_dataSource.Length; public ByteArrayInputFile(string filePath) : this(filePath, File.ReadAllBytes(filePath), 0uL) { } public ByteArrayInputFile(byte[] contents) : this(null, contents, 0uL) { } public ByteArrayInputFile(string? filePath, byte[] data, ulong baseAddress) { FilePath = filePath; _dataSource = new ByteArrayDataSource(data, baseAddress); } public BinaryStreamReader CreateReader(ulong address, uint rva, uint length) { return new BinaryStreamReader(_dataSource, address, rva, length); } void IDisposable.Dispose() { } } public class DataSourceSlice : IDataSource { private readonly IDataSource _source; public ulong BaseAddress { get; } public ulong Length { get; } public byte this[ulong address] { get { if (!IsValidAddress(address)) { throw new IndexOutOfRangeException(); } return _source[address]; } } public DataSourceSlice(IDataSource source, ulong start, ulong length) { _source = source; if (!source.IsValidAddress(start)) { throw new ArgumentOutOfRangeException("start"); } if (length != 0 && !source.IsValidAddress(start + length - 1)) { throw new ArgumentOutOfRangeException("length"); } BaseAddress = start; Length = length; } public bool IsValidAddress(ulong address) { if (address >= BaseAddress) { return address - BaseAddress < Length; } return false; } public int ReadBytes(ulong address, byte[] buffer, int index, int count) { int val = Math.Max(0, (int)(Length - (address - BaseAddress))); return _source.ReadBytes(address, buffer, index, Math.Min(val, count)); } } public class DisplacedDataSource : IDataSource { private readonly IDataSource _dataSource; private readonly long _displacement; public ulong BaseAddress => _dataSource.BaseAddress + (ulong)_displacement; public byte this[ulong address] => _dataSource[address - (ulong)_displacement]; public ulong Length => _dataSource.Length; public DisplacedDataSource(IDataSource dataSource, long displacement) { _dataSource = dataSource ?? throw new ArgumentNullException("dataSource"); _displacement = displacement; } public bool IsValidAddress(ulong address) { return _dataSource.IsValidAddress(address - (ulong)_displacement); } public int ReadBytes(ulong address, byte[] buffer, int index, int count) { return _dataSource.ReadBytes(address - (ulong)_displacement, buffer, index, count); } } public interface IBinaryStreamWriter { ulong Offset { get; set; } uint Length { get; } void WriteBytes(byte[] buffer, int startIndex, int count); void WriteByte(byte value); void WriteUInt16(ushort value); void WriteUInt32(uint value); void WriteUInt64(ulong value); void WriteSByte(sbyte value); void WriteInt16(short value); void WriteInt32(int value); void WriteInt64(long value); void WriteSingle(float value); void WriteDouble(double value); void WriteDecimal(decimal value); } public static class IOExtensions { public static void WriteNativeInt(this IBinaryStreamWriter writer, ulong value, bool is32Bit) { if (is32Bit) { writer.WriteUInt32((uint)value); } else { writer.WriteUInt64(value); } } public static void WriteBytes(this IBinaryStreamWriter writer, byte[] buffer) { writer.WriteBytes(buffer, 0, buffer.Length); } public static void WriteZeroes(this IBinaryStreamWriter writer, int count) { while (count >= 8) { writer.WriteUInt64(0uL); count -= 8; } while (count >= 4) { writer.WriteUInt32(0u); count -= 4; } while (count >= 2) { writer.WriteUInt16(0); count -= 2; } while (count >= 1) { writer.WriteByte(0); count--; } } public static void WriteAsciiString(this IBinaryStreamWriter writer, string value) { writer.WriteBytes(Encoding.ASCII.GetBytes(value)); } public static void Align(this IBinaryStreamWriter writer, uint align) { ulong offset = writer.Offset; writer.WriteZeroes((int)(offset.Align(align) - writer.Offset)); } public static void WriteIndex(this IBinaryStreamWriter writer, uint value, IndexSize size) { switch (size) { case IndexSize.Short: writer.WriteUInt16((ushort)value); break; case IndexSize.Long: writer.WriteUInt32(value); break; default: throw new ArgumentOutOfRangeException("size", size, null); } } public static void WriteCompressedUInt32(this IBinaryStreamWriter writer, uint value) { if (value < 128) { writer.WriteByte((byte)value); return; } if (value < 16384) { writer.WriteByte((byte)(0x80u | (value >> 8))); writer.WriteByte((byte)(value & 0xFFu)); return; } writer.WriteByte((byte)(0xC0u | (value >> 24))); writer.WriteByte((byte)((value >> 16) & 0xFFu)); writer.WriteByte((byte)((value >> 8) & 0xFFu)); writer.WriteByte((byte)(value & 0xFFu)); } public static void Write7BitEncodedInt32(this IBinaryStreamWriter writer, int value) { uint num = (uint)value; do { byte b = (byte)(num & 0x7Fu); if (num > 127) { b = (byte)(b | 0x80u); } writer.WriteByte(b); num >>= 7; } while (num != 0); } public static void WriteSerString(this IBinaryStreamWriter writer, string? value) { if (value == null) { writer.WriteByte(byte.MaxValue); return; } byte[] bytes = Encoding.UTF8.GetBytes(value); writer.WriteCompressedUInt32((uint)bytes.Length); writer.WriteBytes(bytes); } public static void WriteBinaryFormatterString(this IBinaryStreamWriter writer, string value) { writer.WriteBinaryFormatterString(value, Encoding.UTF8); } public static void WriteBinaryFormatterString(this IBinaryStreamWriter writer, string value, Encoding encoding) { byte[] bytes = encoding.GetBytes(value); writer.Write7BitEncodedInt32(bytes.Length); writer.WriteBytes(bytes); } public static void WriteSerString(this IBinaryStreamWriter writer, Utf8String? value) { if ((object)value == null) { writer.WriteByte(byte.MaxValue); return; } byte[] bytesUnsafe = value.GetBytesUnsafe(); writer.WriteCompressedUInt32((uint)bytesUnsafe.Length); writer.WriteBytes(bytesUnsafe); } public static BinaryStreamReader CreateReader(this IInputFile factory) { return factory.CreateReader(0uL, 0u, factory.Length); } } public interface IDataSource { ulong BaseAddress { get; } byte this[ulong address] { get; } ulong Length { get; } bool IsValidAddress(ulong address); int ReadBytes(ulong address, byte[] buffer, int index, int count); } public interface IFileService : IDisposable { IEnumerable GetOpenedFiles(); IInputFile OpenFile(string filePath); void InvalidateFile(string filePath); } public interface IInputFile : IDisposable { string? FilePath { get; } uint Length { get; } BinaryStreamReader CreateReader(ulong address, uint rva, uint length); } public sealed class MemoryMappedDataSource : IDataSource, IDisposable { private readonly MemoryMappedViewAccessor _accessor; public ulong BaseAddress => 0uL; public byte this[ulong address] => _accessor.ReadByte((long)address); public ulong Length { get; } public MemoryMappedDataSource(MemoryMappedViewAccessor accessor, ulong length) { Length = length; _accessor = accessor ?? throw new ArgumentNullException("accessor"); } public bool IsValidAddress(ulong address) { return address < Length; } public int ReadBytes(ulong address, byte[] buffer, int index, int count) { return _accessor.ReadArray((long)address, buffer, index, count); } public void Dispose() { _accessor?.Dispose(); } } public class MemoryMappedFileService : IFileService, IDisposable { private readonly Dictionary _files = new Dictionary(); public IEnumerable GetOpenedFiles() { return _files.Keys; } public IInputFile OpenFile(string filePath) { if (!_files.TryGetValue(filePath, out MemoryMappedInputFile value)) { value = new MemoryMappedInputFile(filePath); _files.Add(filePath, value); } return value; } public void InvalidateFile(string filePath) { if (_files.TryGetValue(filePath, out MemoryMappedInputFile value)) { value.Dispose(); _files.Remove(filePath); } } public void Dispose() { foreach (MemoryMappedInputFile value in _files.Values) { value.Dispose(); } _files.Clear(); } } public sealed class MemoryMappedInputFile : IInputFile, IDisposable { private readonly MemoryMappedFile _file; private readonly MemoryMappedDataSource _dataSource; public string FilePath { get; } public uint Length => (uint)_dataSource.Length; public MemoryMappedInputFile(string filePath) { FilePath = filePath ?? throw new ArgumentNullException("filePath"); _file = MemoryMappedFile.CreateFromFile(filePath); long length = new FileInfo(filePath).Length; _dataSource = new MemoryMappedDataSource(_file.CreateViewAccessor(0L, length), (ulong)length); } public BinaryStreamReader CreateReader(ulong address, uint rva, uint length) { return new BinaryStreamReader(_dataSource, address, rva, length); } public void Dispose() { _file?.Dispose(); _dataSource?.Dispose(); } } public class MemoryStreamWriterPool { public ref struct RentedWriter { private bool _isDisposed; private readonly BinaryStreamWriter _writer; public MemoryStreamWriterPool Pool { get; } public BinaryStreamWriter Writer { get { if (_isDisposed) { throw new ObjectDisposedException("Writer"); } return _writer; } } internal RentedWriter(MemoryStreamWriterPool pool, BinaryStreamWriter writer) { _isDisposed = false; Pool = pool; _writer = writer; } public byte[] GetData() { return ((MemoryStream)Writer.BaseStream).ToArray(); } public void Dispose() { if (!_isDisposed) { Pool.Return(Writer); _isDisposed = true; } } } private readonly ConcurrentQueue _writers = new ConcurrentQueue(); public RentedWriter Rent() { if (!_writers.TryDequeue(out BinaryStreamWriter result)) { result = new BinaryStreamWriter(new MemoryStream()); } result.BaseStream.SetLength(0L); return new RentedWriter(this, result); } private void Return(BinaryStreamWriter writer) { _writers.Enqueue(writer); } } public sealed class UncachedFileService : IFileService, IDisposable { public static UncachedFileService Instance { get; } = new UncachedFileService(); private UncachedFileService() { } IEnumerable IFileService.GetOpenedFiles() { return Enumerable.Empty(); } public IInputFile OpenFile(string filePath) { return new ByteArrayInputFile(filePath); } void IFileService.InvalidateFile(string filePath) { } void IDisposable.Dispose() { } } public sealed class UnmanagedDataSource : IDataSource { private unsafe readonly void* _basePointer; public unsafe ulong BaseAddress => (ulong)_basePointer; public unsafe byte this[ulong address] { get { if (!IsValidAddress(address)) { throw new ArgumentOutOfRangeException("address"); } return *(byte*)address; } } public ulong Length { get; } public unsafe UnmanagedDataSource(IntPtr basePointer, ulong length) : this(basePointer.ToPointer(), length) { } public unsafe UnmanagedDataSource(void* basePointer, ulong length) { _basePointer = basePointer; Length = length; } public unsafe bool IsValidAddress(ulong address) { if (address >= (ulong)_basePointer) { return (ulong)((long)address - (long)_basePointer) < Length; } return false; } public unsafe int ReadBytes(ulong address, byte[] buffer, int index, int count) { if (!IsValidAddress(address)) { return 0; } ulong num = address - (ulong)_basePointer; int num2 = (int)Math.Min((ulong)count, Length - num); Marshal.Copy((IntPtr)(long)address, buffer, index, num2); return num2; } } } namespace AsmResolver.Collections { public class BitList : IList, ICollection, IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly BitList _list; private readonly int _version; private int _index; public bool Current => _list[_index]; object IEnumerator.Current => Current; public Enumerator(BitList list) { _index = -1; _version = list._version; _list = list; } public bool MoveNext() { if (_version != _list._version) { throw new InvalidOperationException("Collection was modified."); } if (_index >= _list.Count) { return false; } _index++; return true; } public void Reset() { _index = -1; } public void Dispose() { } } private const int WordSize = 32; private uint[] _words; private int _version; public int Count { get; private set; } public bool IsReadOnly => false; public bool this[int index] { get { if (index >= Count) { throw new IndexOutOfRangeException(); } var (num, num2) = SplitWordBitIndex(index); return ((_words[num] >> num2) & 1) != 0; } set { if (index >= Count) { throw new IndexOutOfRangeException(); } var (num, num2) = SplitWordBitIndex(index); _words[num] = (_words[num] & (uint)(~(1 << num2))) | (value ? ((uint)(1 << num2)) : 0u); _version++; } } public BitList() { _words = new uint[1]; } public BitList(int capacity) { _words = new uint[((uint)capacity).Align(32u)]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static (int wordIndex, int bitIndex) SplitWordBitIndex(int index) { int result; return (Math.DivRem(index, 32, out result), result); } public void Add(bool item) { EnsureCapacity(Count + 1); Count++; this[Count - 1] = item; _version++; } public void Clear() { Count = 0; } public bool Contains(bool item) { return IndexOf(item) != -1; } public void CopyTo(bool[] array, int arrayIndex) { for (int i = 0; i < Count; i++) { array[arrayIndex + i] = this[i]; } } public bool Remove(bool item) { int num = IndexOf(item); if (num == -1) { return false; } RemoveAt(num); return true; } public int IndexOf(bool item) { for (int i = 0; i < Count; i++) { var (num, num2) = SplitWordBitIndex(i); if (((_words[num] >> num2) & 1) != 0 == item) { return i; } } return -1; } public void Insert(int index, bool item) { if (index > Count) { throw new IndexOutOfRangeException(); } EnsureCapacity(Count++); (int wordIndex, int bitIndex) tuple = SplitWordBitIndex(index); int item2 = tuple.wordIndex; int item3 = tuple.bitIndex; uint num = _words[item2] & 0x80000000u; uint num2 = (uint)((1 << item3) - 1); uint num3 = ~num2; _words[item2] = ((_words[item2] & num3) << 1) | (item ? ((uint)(1 << item3)) : 0u) | (_words[item2] & num2); for (int i = item2 + 1; i < _words.Length; i++) { int num4 = (int)_words[i] & int.MinValue; _words[i] = (_words[i] << 1) | (num >> 31); num = (uint)num4; } _version++; } public void RemoveAt(int index) { Count--; (int wordIndex, int bitIndex) tuple = SplitWordBitIndex(index); int item = tuple.wordIndex; int item2 = tuple.bitIndex; uint num = ((item + 1 < _words.Length && ((uint)index).Align(32u) < Count) ? (_words[item + 1] & 1u) : 0u); uint num2 = (uint)((1 << item2) - 1); uint num3 = (uint)(~((1 << item2 + 1) - 1)); _words[item] = ((_words[item] & num3) >> 1) | (_words[item] & num2) | (num << 31); for (int i = item + 1; i < _words.Length; i++) { uint num4 = ((i + 1 < _words.Length && ((uint)index).Align(32u) < Count) ? (_words[i + 1] & 1) : 0); _words[i] = (_words[i] >> 1) | (num << 31); num = num4; } _version++; } public void EnsureCapacity(int capacity) { if (capacity >= 32 * _words.Length) { int newSize = (int)(((uint)capacity).Align(32u) / 8); Array.Resize(ref _words, newSize); } } public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public interface IOwnedCollectionElement { TOwner? Owner { get; set; } } [DebuggerDisplay("Count = {Count}")] public abstract class LazyList : IList, ICollection, IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly LazyList _list; private List.Enumerator _enumerator; private bool hasEnumerator; public TItem? Current { get { if (!hasEnumerator) { return default(TItem); } return _enumerator.Current; } } object? IEnumerator.Current => Current; public Enumerator(LazyList list) { _list = list; _enumerator = default(List.Enumerator); hasEnumerator = false; } public bool MoveNext() { if (!hasEnumerator) { _list.EnsureIsInitialized(); _enumerator = _list._items.GetEnumerator(); hasEnumerator = true; } return _enumerator.MoveNext(); } public void Reset() { throw new NotSupportedException(); } public void Dispose() { if (hasEnumerator) { _enumerator.Dispose(); } } } private readonly List _items; public TItem this[int index] { get { EnsureIsInitialized(); return Items[index]; } set { lock (_items) { EnsureIsInitialized(); OnSetItem(index, value); } } } public virtual int Count { get { EnsureIsInitialized(); return Items.Count; } } public int Capacity { get { return _items.Capacity; } set { _items.Capacity = value; } } public bool IsReadOnly => false; protected bool IsInitialized { get; private set; } protected IList Items => _items; public LazyList() { _items = new List(); } public LazyList(int capacity) { _items = new List(capacity); } protected abstract void Initialize(); protected virtual void PostInitialize() { } private void EnsureIsInitialized() { if (IsInitialized) { return; } lock (_items) { if (!IsInitialized) { Initialize(); IsInitialized = true; PostInitialize(); } } } public void Add(TItem item) { Insert(Count, item); } public void AddRange(IEnumerable items) { InsertRange(Count, items); } public void Clear() { lock (_items) { OnClearItems(); IsInitialized = true; } } public bool Contains(TItem item) { EnsureIsInitialized(); return Items.Contains(item); } public void CopyTo(TItem[] array, int arrayIndex) { EnsureIsInitialized(); Items.CopyTo(array, arrayIndex); } public bool Remove(TItem item) { lock (_items) { EnsureIsInitialized(); int num = Items.IndexOf(item); if (num == -1) { return false; } OnRemoveItem(num); } return true; } public int IndexOf(TItem item) { EnsureIsInitialized(); return Items.IndexOf(item); } public void Insert(int index, TItem item) { lock (_items) { EnsureIsInitialized(); OnInsertItem(index, item); } } private void InsertRange(int index, IEnumerable items) { lock (_items) { EnsureIsInitialized(); OnInsertRange(index, items); } } public void RemoveAt(int index) { lock (_items) { EnsureIsInitialized(); OnRemoveItem(index); } } protected virtual void OnSetItem(int index, TItem item) { _items[index] = item; } protected virtual void OnInsertItem(int index, TItem item) { _items.Insert(index, item); } protected virtual void OnInsertRange(int index, IEnumerable items) { _items.InsertRange(index, items); } protected virtual void OnRemoveItem(int index) { _items.RemoveAt(index); } protected virtual void OnClearItems() { _items.Clear(); } public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public sealed class OneToManyRelation where TKey : notnull where TValue : notnull { public class ValueSet : ICollection, IEnumerable, IEnumerable { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private List.Enumerator _enumerator; public TValue Current => _enumerator.Current; object IEnumerator.Current => ((IEnumerator)_enumerator).Current; internal Enumerator(List.Enumerator enumerator) { _enumerator = enumerator; } public bool MoveNext() { return _enumerator.MoveNext(); } public void Reset() { throw new NotSupportedException(); } public void Dispose() { _enumerator.Dispose(); } } public static readonly ValueSet Empty = new ValueSet(); internal List Items { get; } = new List(); public int Count => Items.Count; public bool IsReadOnly => true; public void Add(TValue item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TValue item) { return Items.Contains(item); } public void CopyTo(TValue[] array, int arrayIndex) { Items.CopyTo(array, arrayIndex); } public bool Remove(TValue item) { throw new NotSupportedException(); } public Enumerator GetEnumerator() { return new Enumerator(Items.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)Items).GetEnumerator(); } } private readonly Dictionary _memberLists; private readonly Dictionary _memberOwners; public OneToManyRelation() { _memberLists = new Dictionary(); _memberOwners = new Dictionary(); } public OneToManyRelation(int capacity) { _memberLists = new Dictionary(capacity); _memberOwners = new Dictionary(capacity); } public bool Add(TKey key, TValue value) { if (!_memberOwners.ContainsKey(value)) { if (!_memberLists.TryGetValue(key, out ValueSet value2)) { value2 = new ValueSet(); _memberLists.Add(key, value2); } value2.Items.Add(value); _memberOwners.Add(value, key); return true; } return false; } public ValueSet GetValues(TKey key) { if (!_memberLists.TryGetValue(key, out ValueSet value)) { return ValueSet.Empty; } return value; } public TKey? GetKey(TValue value) { if (!_memberOwners.TryGetValue(value, out var value2)) { return default(TKey); } return value2; } } public sealed class OneToOneRelation where TKey : notnull where TValue : notnull { private readonly Dictionary _keyToValue; private readonly Dictionary _valueToKey; public OneToOneRelation() { _keyToValue = new Dictionary(); _valueToKey = new Dictionary(); } public OneToOneRelation(int capacity) { _keyToValue = new Dictionary(capacity); _valueToKey = new Dictionary(capacity); } public bool Add(TKey key, TValue value) { if (!_keyToValue.ContainsKey(key) && !_valueToKey.ContainsKey(value)) { _keyToValue.Add(key, value); _valueToKey.Add(value, key); return true; } return false; } public TValue? GetValue(TKey key) { _keyToValue.TryGetValue(key, out var value); return value; } public TKey? GetKey(TValue value) { _valueToKey.TryGetValue(value, out var value2); return value2; } public IEnumerable GetKeys() { return _keyToValue.Keys; } public IEnumerable GetValues() { return _valueToKey.Keys; } } [DebuggerDisplay("Count = {Count}")] public class OwnedCollection : LazyList where TOwner : class where TItem : class, IOwnedCollectionElement { public TOwner Owner { get; } public OwnedCollection(TOwner owner) { Owner = owner ?? throw new ArgumentNullException("owner"); } public OwnedCollection(TOwner owner, int capacity) : base(capacity) { Owner = owner ?? throw new ArgumentNullException("owner"); } protected void AssertNotNullAndHasNoOwner(TItem? item) { if (item == null) { throw new ArgumentNullException("item"); } if (item.Owner != null && item.Owner != Owner) { throw new ArgumentException("Item is already added to another collection."); } } protected override void Initialize() { } protected override void OnSetItem(int index, TItem item) { AssertNotNullAndHasNoOwner(item); item.Owner = Owner; base.Items[index].Owner = null; base.OnSetItem(index, item); } protected override void OnInsertItem(int index, TItem item) { AssertNotNullAndHasNoOwner(item); item.Owner = Owner; base.OnInsertItem(index, item); } protected override void OnInsertRange(int index, IEnumerable items) { List list = items.ToList(); foreach (TItem item in list) { AssertNotNullAndHasNoOwner(item); } base.OnInsertRange(index, list); foreach (TItem item2 in list) { item2.Owner = Owner; } } protected override void OnRemoveItem(int index) { base[index].Owner = null; base.OnRemoveItem(index); } protected override void OnClearItems() { foreach (TItem item in base.Items) { item.Owner = null; } base.OnClearItems(); } } public class RefList : ICollection, IEnumerable, IList, ICollection, IEnumerable, IReadOnlyList, IReadOnlyCollection where T : struct { public struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly RefList _list; private readonly int _version; private int _index; public T Current { get { if (_index < 0 || _index >= _list._count) { return default(T); } return _list[_index]; } } object IEnumerator.Current => Current; public Enumerator(RefList list) { _list = list; _version = list._version; _index = -1; } public bool MoveNext() { if (_version != _list._version) { throw new InvalidOperationException("Collection was modified."); } _index++; if (_index >= 0) { return _index < _list._count; } return false; } public void Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } } public const int DefaultCapacity = 4; private T[] _items; private int _count; private int _version; public int Count => _count; public int Capacity { get { return _items.Length; } set { if (value != _items.Length) { if (value < _count) { throw new ArgumentException("Capacity must be equal or larger than the current number of elements in the list."); } EnsureCapacity(value); IncrementVersion(); } } } public int Version => _version; bool ICollection.IsReadOnly => false; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; public T this[int index] { get { AssertIsValidIndex(index); return _items[index]; } set { AssertIsValidIndex(index); _items[index] = value; IncrementVersion(); } } public RefList() : this(4) { } public RefList(int capacity) { _items = new T[capacity]; } public ref T GetElementRef(int index) { AssertIsValidIndex(index); return ref _items[index]; } public ref T GetElementRef(int index, out int version) { AssertIsValidIndex(index); version = _version; return ref _items[index]; } public void Add(in T item) { EnsureCapacity(_count + 1); _items[_count] = item; _count++; IncrementVersion(); } void ICollection.Add(T item) { Add(in item); } public void Clear() { Array.Clear(_items, 0, _items.Length); _count = 0; IncrementVersion(); } bool ICollection.Contains(T item) { return this.IndexOf(item) >= 0; } public bool Contains(in T item) { return this.IndexOf(item) >= 0; } public void CopyTo(T[] array, int arrayIndex) { _items.CopyTo(array, arrayIndex); } public void CopyTo(Array array, int index) { _items.CopyTo(array, index); } public bool Remove(in T item) { int num = this.IndexOf(item); if (num > 0) { RemoveAt(num); return true; } return false; } bool ICollection.Remove(T item) { return Remove(in item); } public int IndexOf(in T item) { return Array.IndexOf(_items, item, 0, _count); } public void Insert(int index, in T item) { EnsureCapacity(_count + 1); if (index < _count) { Array.Copy(_items, index, _items, index + 1, _count - index); } _items[index] = item; _count++; IncrementVersion(); } void IList.Insert(int index, T item) { Insert(index, in item); } public int IndexOf(T item) { return Array.IndexOf(_items, item, 0, _count); } public void RemoveAt(int index) { if (index < 0 || index >= _count) { throw new ArgumentOutOfRangeException("index"); } _count--; if (index < _count) { Array.Copy(_items, index + 1, _items, index, _count - index); } _items[_count] = default(T); IncrementVersion(); } public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private void AssertIsValidIndex(int index) { if (index < 0 || index >= Count) { throw new IndexOutOfRangeException(); } } private void EnsureCapacity(int requiredCount) { if (_items.Length < requiredCount) { int num = ((_items.Length == 0) ? 1 : (_items.Length * 2)); if (num < requiredCount) { num = requiredCount; } Array.Resize(ref _items, num); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void IncrementVersion() { _version++; } } }