// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.IO; using dnlib.DotNet.MD; using dnlib.IO; namespace dnlib.DotNet { /// /// Helps resolve types /// public interface ISignatureReaderHelper { /// /// Resolves a /// /// A TypeDefOrRef coded token /// Generic parameter context /// A or null if /// is invalid ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext); /// /// Converts the address of a to a /// /// /// Address of . This is also known as the /// method table and has the same value as /// A or null if not supported TypeSig ConvertRTInternalAddress(IntPtr address); } /// /// Reads signatures from the #Blob stream /// public struct SignatureReader { // .NET Core and .NET Framework limit arrays to 32 dimensions. Use a bigger limit // so it's possible to read some bad MD, but not big enough to allocate a ton of mem. const uint MaxArrayRank = 64; readonly ISignatureReaderHelper helper; readonly ICorLibTypes corLibTypes; DataReader reader; readonly GenericParamContext gpContext; RecursionCounter recursionCounter; /// /// Reads a signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig) => ReadSig(readerModule, sig, new GenericParamContext()); /// /// Reads a signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// Generic parameter context /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { try { var reader = new SignatureReader(readerModule, sig, gpContext); if (reader.reader.Length == 0) return null; var csig = reader.ReadSig(); if (csig is not null) csig.ExtraData = reader.GetExtraData(); return csig; } catch { return null; } } /// /// Reads a signature /// /// The module where the signature is located in /// The signature data /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature) => ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in /// The signature data /// Generic parameter context /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) => ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// The module where the signature is located in /// The signature reader /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature) => ReadSig(module, module.CorLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) => ReadSig(module, module.CorLibTypes, signature, gpContext); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature data /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) => ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature data /// Generic parameter context /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) => ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature reader /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) => ReadSig(helper, corLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) { try { var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext); if (reader.reader.Length == 0) return null; return reader.ReadSig(); } catch { return null; } } /// /// Reads a type signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig) => ReadTypeSig(readerModule, sig, new GenericParamContext()); /// /// Reads a type signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// Generic parameter context /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { try { var reader = new SignatureReader(readerModule, sig, gpContext); return reader.ReadType(); } catch { return null; } } /// /// Reads a type signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// If there's any extra data after the signature, it's saved /// here, else this will be null /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, out byte[] extraData) => ReadTypeSig(readerModule, sig, new GenericParamContext(), out extraData); /// /// Reads a type signature from the #Blob stream /// /// Reader module /// #Blob stream offset of signature /// Generic parameter context /// If there's any extra data after the signature, it's saved /// here, else this will be null /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext, out byte[] extraData) { try { var reader = new SignatureReader(readerModule, sig, gpContext); TypeSig ts; try { ts = reader.ReadType(); } catch (IOException) { reader.reader.Position = 0; ts = null; } extraData = reader.GetExtraData(); return ts; } catch { extraData = null; return null; } } /// /// Reads a signature /// /// The module where the signature is located in /// The signature data /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature) => ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in /// The signature data /// Generic parameter context /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) => ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// The module where the signature is located in /// The signature reader /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature) => ReadTypeSig(module, module.CorLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) => ReadTypeSig(module, module.CorLibTypes, signature, gpContext); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature data /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) => ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature data /// Generic parameter context /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) => ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature reader /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) => ReadTypeSig(helper, corLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) => ReadTypeSig(helper, corLibTypes, signature, gpContext, out var extraData); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature data /// Generic parameter context /// If there's any extra data after the signature, it's saved /// here, else this will be null /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext, out byte[] extraData) => ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext, out extraData); /// /// Reads a signature /// /// Token resolver /// A instance /// The signature reader /// Generic parameter context /// If there's any extra data after the signature, it's saved /// here, else this will be null /// A new instance or null if /// is invalid. public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext, out byte[] extraData) { try { var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext); TypeSig ts; try { ts = reader.ReadType(); } catch (IOException) { reader.reader.Position = 0; ts = null; } extraData = reader.GetExtraData(); return ts; } catch { extraData = null; return null; } } /// /// Constructor /// /// Reader module /// #Blob stream offset of signature /// Generic parameter context SignatureReader(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { helper = readerModule; corLibTypes = readerModule.CorLibTypes; reader = readerModule.BlobStream.CreateReader(sig); this.gpContext = gpContext; recursionCounter = new RecursionCounter(); } /// /// Constructor /// /// Token resolver /// A instance /// The signature data /// Generic parameter context SignatureReader(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, ref DataReader reader, GenericParamContext gpContext) { this.helper = helper; this.corLibTypes = corLibTypes; this.reader = reader; this.gpContext = gpContext; recursionCounter = new RecursionCounter(); } byte[] GetExtraData() { if (reader.Position == reader.Length) return null; return reader.ReadRemainingBytes(); } /// /// Reads the signature /// /// A new instance or null if invalid signature CallingConventionSig ReadSig() { if (!recursionCounter.Increment()) return null; CallingConventionSig result; var callingConvention = (CallingConvention)reader.ReadByte(); switch (callingConvention & CallingConvention.Mask) { case CallingConvention.Default: case CallingConvention.C: case CallingConvention.StdCall: case CallingConvention.ThisCall: case CallingConvention.FastCall: case CallingConvention.VarArg: case CallingConvention.Unmanaged: case CallingConvention.NativeVarArg: result = ReadMethod(callingConvention); break; case CallingConvention.Field: result = ReadField(callingConvention); break; case CallingConvention.LocalSig: result = ReadLocalSig(callingConvention); break; case CallingConvention.Property: result = ReadProperty(callingConvention); break; case CallingConvention.GenericInst: result = ReadGenericInstMethod(callingConvention); break; default: result = null; break; } recursionCounter.Decrement(); return result; } /// /// Reads a /// /// First byte of signature /// A new instance FieldSig ReadField(CallingConvention callingConvention) => new FieldSig(callingConvention, ReadType()); /// /// Reads a /// /// First byte of signature /// A new instance MethodSig ReadMethod(CallingConvention callingConvention) => ReadSig(new MethodSig(callingConvention)); /// /// Reads a /// /// First byte of signature /// A new instance PropertySig ReadProperty(CallingConvention callingConvention) => ReadSig(new PropertySig(callingConvention)); T ReadSig(T methodSig) where T : MethodBaseSig { if (methodSig.Generic) { if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000) return null; methodSig.GenParamCount = count; } if (!reader.TryReadCompressedUInt32(out uint numParams) || numParams > 0x10000 || numParams > reader.BytesLeft) return null; methodSig.RetType = ReadType(); var parameters = methodSig.Params; for (uint i = 0; i < numParams; i++) { var type = ReadType(); if (type is SentinelSig) { if (methodSig.ParamsAfterSentinel is null) methodSig.ParamsAfterSentinel = parameters = new List((int)(numParams - i)); i--; } else parameters.Add(type); } return methodSig; } /// /// Reads a /// /// First byte of signature /// A new instance LocalSig ReadLocalSig(CallingConvention callingConvention) { if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft) return null; var sig = new LocalSig(callingConvention, count); var locals = sig.Locals; for (uint i = 0; i < count; i++) locals.Add(ReadType()); return sig; } /// /// Reads a /// /// First byte of signature /// A new instance GenericInstMethodSig ReadGenericInstMethod(CallingConvention callingConvention) { if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft) return null; var sig = new GenericInstMethodSig(callingConvention, count); var args = sig.GenericArguments; for (uint i = 0; i < count; i++) args.Add(ReadType()); return sig; } /// /// Reads the next type /// /// true if a TypeSpec is allowed if the next type is a class/value-type /// A new instance or null if invalid element type TypeSig ReadType(bool allowTypeSpec = false) { if (!recursionCounter.Increment()) return null; uint num, i; TypeSig nextType, result = null; switch ((ElementType)reader.ReadByte()) { case ElementType.Void: result = corLibTypes.Void; break; case ElementType.Boolean: result = corLibTypes.Boolean; break; case ElementType.Char: result = corLibTypes.Char; break; case ElementType.I1: result = corLibTypes.SByte; break; case ElementType.U1: result = corLibTypes.Byte; break; case ElementType.I2: result = corLibTypes.Int16; break; case ElementType.U2: result = corLibTypes.UInt16; break; case ElementType.I4: result = corLibTypes.Int32; break; case ElementType.U4: result = corLibTypes.UInt32; break; case ElementType.I8: result = corLibTypes.Int64; break; case ElementType.U8: result = corLibTypes.UInt64; break; case ElementType.R4: result = corLibTypes.Single; break; case ElementType.R8: result = corLibTypes.Double; break; case ElementType.String: result = corLibTypes.String; break; case ElementType.TypedByRef:result = corLibTypes.TypedReference; break; case ElementType.I: result = corLibTypes.IntPtr; break; case ElementType.U: result = corLibTypes.UIntPtr; break; case ElementType.Object: result = corLibTypes.Object; break; case ElementType.Ptr: result = new PtrSig(ReadType()); break; case ElementType.ByRef: result = new ByRefSig(ReadType()); break; case ElementType.ValueType: result = new ValueTypeSig(ReadTypeDefOrRef(allowTypeSpec)); break; case ElementType.Class: result = new ClassSig(ReadTypeDefOrRef(allowTypeSpec)); break; case ElementType.FnPtr: result = new FnPtrSig(ReadSig()); break; case ElementType.SZArray: result = new SZArraySig(ReadType()); break; case ElementType.CModReqd: result = new CModReqdSig(ReadTypeDefOrRef(true), ReadType()); break; case ElementType.CModOpt: result = new CModOptSig(ReadTypeDefOrRef(true), ReadType()); break; case ElementType.Sentinel: result = new SentinelSig(); break; case ElementType.Pinned: result = new PinnedSig(ReadType()); break; case ElementType.Var: if (!reader.TryReadCompressedUInt32(out num)) break; result = new GenericVar(num, gpContext.Type); break; case ElementType.MVar: if (!reader.TryReadCompressedUInt32(out num)) break; result = new GenericMVar(num, gpContext.Method); break; case ElementType.ValueArray: nextType = ReadType(); if (!reader.TryReadCompressedUInt32(out num)) break; result = new ValueArraySig(nextType, num); break; case ElementType.Module: if (!reader.TryReadCompressedUInt32(out num)) break; result = new ModuleSig(num, ReadType()); break; case ElementType.GenericInst: nextType = ReadType(); if (!reader.TryReadCompressedUInt32(out num) || num > 0x10000 || num > reader.BytesLeft) break; var genericInstSig = new GenericInstSig(nextType as ClassOrValueTypeSig, num); var args = genericInstSig.GenericArguments; for (i = 0; i < num; i++) args.Add(ReadType()); result = genericInstSig; break; case ElementType.Array: nextType = ReadType(); uint rank; if (!reader.TryReadCompressedUInt32(out rank)) break; if (rank > MaxArrayRank) break; if (rank == 0) { result = new ArraySig(nextType, rank); break; } if (!reader.TryReadCompressedUInt32(out num)) break; if (num > rank) break; var sizes = new List((int)num); for (i = 0; i < num; i++) { if (!reader.TryReadCompressedUInt32(out uint size)) goto exit; sizes.Add(size); } if (!reader.TryReadCompressedUInt32(out num)) break; if (num > rank) break; var lowerBounds = new List((int)num); for (i = 0; i < num; i++) { if (!reader.TryReadCompressedInt32(out int size)) goto exit; lowerBounds.Add(size); } result = new ArraySig(nextType, rank, sizes, lowerBounds); break; case ElementType.Internal: IntPtr address; if (IntPtr.Size == 4) address = new IntPtr(reader.ReadInt32()); else address = new IntPtr(reader.ReadInt64()); result = helper.ConvertRTInternalAddress(address); break; case ElementType.End: case ElementType.R: default: result = null; break; } exit: recursionCounter.Decrement(); return result; } ITypeDefOrRef ReadTypeDefOrRef(bool allowTypeSpec) { if (!reader.TryReadCompressedUInt32(out uint codedToken)) return null; if (!allowTypeSpec && CodedToken.TypeDefOrRef.Decode2(codedToken).Table == Table.TypeSpec) return null; return helper.ResolveTypeDefOrRef(codedToken, default); } } }