//Construct the binary assembly instance public PEFile(string fileName) { this.FileName = fileName; //Open the assembly's binary file in shared-read mode and copy it into a buffer so we can close the file post haste. FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] streamBuffer = new byte[fileStream.Length]; fileStream.Read(streamBuffer, 0, (int)fileStream.Length); fileStream.Close(); //Push the buffer into a memory stream and then access it through a binary reader this.streamReader = new BinaryReader(new MemoryStream(streamBuffer)); // The rest of the code moves through the loaded assemebly as follows: // 1) DOS header points to the PE header. // 2) PE header contains a the CLI directory that points to the The CLI header. // 3) In order to work out the RVAs, we need the "Sections" of the file. // 4) CLI header in turn contains the RVA to MetaData directory. // 5) Metadata header points to the metadata root. // 6) metadata root lists the metadata stream headers. // 7) metadata stream headers contain the offsets to the metadata stream. Hooray! // We only need certain elements of the file. Those elements we // need are stored in structs/classes, the elements we don't need // are parsed by methods whose purpose is: // a) to move the stream position to where we need it, // b) to be substituted with classes as the scope of requirements increases. ParseMsDosHeader(this.streamReader); this.peFileHeader = new PEFileHeader(this.streamReader); ParsePeOptionalHeader(this.streamReader); BuildSections(this.streamReader); // Now we have the sections we can work out where RVAs lie, // so we can get the CLI Header, which in turn allows us to get the // metadata directory. ParseCliHeader(this.streamReader); this.metaDataDirectory = new Directory(this.streamReader); this.streamReader.BaseStream.Position = GetOffsetFromRVA(this.metaDataDirectory.Rva); this.metaDataRoot = new MetaDataRoot(this.streamReader); // now we have the metadataroot complete with the metadata stream headers, // we can build the metadata streams. BuildMetaDataStreams(this.streamReader); // the following builds the descriptive structure found at the start of the #~ stream // first off, move to the correct point in the file as the previous step will have left // us in the wrong place this.streamReader.BaseStream.Position = this.tablesStream.StartPositionInFile; this.tablesHeader = new TablesHeader(this.streamReader, this.stringHeap); // now we have the tables header, we can build the tables this.tables = new Tables(this.tablesHeader, this.stringHeap, this.streamReader); }
public Tables(TablesHeader tablesHeader, MetaDataStream stringHeap, BinaryReader reader) { this.tablesHeader = tablesHeader; this.stringHeap = stringHeap; this.reader = reader; this.indexSizeIndicator = tablesHeader.HeapSizes; this.RowsPerTable = tablesHeader.NumberOfRows; // set up a table of coded index information // the first figure in the array is the number of bits needed to encode // which table the index is pointing to. Note, some indexes use more bits // than are necessary, for example CustomAttributeType uses 3 bits even though // it only points to 2 tables, so in theory could be encoded in 1 bit. this.CodedIndexInformation = new object[12]; CodedIndexInformation[(int)Tables.CodedIndexType.TypeDefOrRef] = new int[] { 2, (int)Tables.TableName.TypeDef, (int)Tables.TableName.TypeRef, (int)Tables.TableName.TypeSpec }; CodedIndexInformation[(int)Tables.CodedIndexType.HasConstant] = new int[] { 2, (int)Tables.TableName.Field, (int)Tables.TableName.Param, (int)Tables.TableName.Property }; CodedIndexInformation[(int)Tables.CodedIndexType.HasCustomAttribute] = new int[] { 5, (int)Tables.TableName.MethodDef, (int)Tables.TableName.Field, (int)Tables.TableName.TypeRef, (int)Tables.TableName.TypeDef, (int)Tables.TableName.Param, (int)Tables.TableName.InterfaceImpl, (int)Tables.TableName.MemberRef, (int)Tables.TableName.Module, (int)Tables.TableName.DeclSecurity, (int)Tables.TableName.Property, (int)Tables.TableName.EventX, (int)Tables.TableName.StandAloneSig, (int)Tables.TableName.ModuleRef, (int)Tables.TableName.TypeSpec, (int)Tables.TableName.Assembly, (int)Tables.TableName.AssemblyRef, (int)Tables.TableName.File, (int)Tables.TableName.ExportedType, (int)Tables.TableName.ManifestResource }; CodedIndexInformation[(int)Tables.CodedIndexType.HasFieldMarshal] = new int[] { 1, (int)Tables.TableName.Field, (int)Tables.TableName.Param }; CodedIndexInformation[(int)Tables.CodedIndexType.HasDeclSecurity] = new int[] { 2, (int)Tables.TableName.TypeDef, (int)Tables.TableName.MethodDef, (int)Tables.TableName.Assembly }; CodedIndexInformation[(int)Tables.CodedIndexType.MemberRefParent] = new int[] { 3, (int)Tables.TableName.TypeRef, (int)Tables.TableName.ModuleRef, (int)Tables.TableName.MethodDef, (int)Tables.TableName.TypeSpec }; CodedIndexInformation[(int)Tables.CodedIndexType.HasSemantics] = new int[] { 1, (int)Tables.TableName.EventX, (int)Tables.TableName.Property }; CodedIndexInformation[(int)Tables.CodedIndexType.MethodDefOrRef] = new int[] { 1, (int)Tables.TableName.MethodDef, (int)Tables.TableName.MemberRef }; CodedIndexInformation[(int)Tables.CodedIndexType.MemberForwarded] = new int[] { 1, (int)Tables.TableName.Field, (int)Tables.TableName.MethodDef }; CodedIndexInformation[(int)Tables.CodedIndexType.Implementation] = new int[] { 2, (int)Tables.TableName.File, (int)Tables.TableName.AssemblyRef, (int)Tables.TableName.ExportedType }; CodedIndexInformation[(int)Tables.CodedIndexType.CustomAttributeType] = new int[] { 3, (int)Tables.TableName.MethodDef, (int)Tables.TableName.MemberRef }; CodedIndexInformation[(int)Tables.CodedIndexType.ResolutionScope] = new int[] { 2, (int)Tables.TableName.Module, (int)Tables.TableName.ModuleRef, (int)Tables.TableName.AssemblyRef, (int)Tables.TableName.TypeRef }; this.Module = new Module(Tables.TableName.Module, this); this.TypeRef = new TypeRef(Tables.TableName.TypeRef, this); this.TypeDef = new TypeDef(Tables.TableName.TypeDef, this); this.Field = new Field(Tables.TableName.Field, this); this.MethodDef = new MethodDef(Tables.TableName.MethodDef, this); this.Param = new Param(Tables.TableName.Param, this); this.InterfaceImpl = new InterfaceImpl(Tables.TableName.InterfaceImpl, this); this.MemberRef = new MemberRef(Tables.TableName.MemberRef, this); this.Constant = new Constant(Tables.TableName.Constant, this); this.CustomAttribute = new CustomAttribute(Tables.TableName.CustomAttribute, this); this.FieldMarshal = new FieldMarshal(Tables.TableName.FieldMarshal, this); this.DeclSecurity = new DeclSecurity(Tables.TableName.DeclSecurity, this); this.ClassLayout = new ClassLayout(Tables.TableName.ClassLayout, this); this.FieldLayout = new FieldLayout(Tables.TableName.FieldLayout, this); this.StandAloneSig = new StandAloneSig(Tables.TableName.StandAloneSig, this); this.EventMap = new EventMap(Tables.TableName.EventMap, this); this.EventX = new EventX(Tables.TableName.EventX, this); this.PropertyMap = new PropertyMap(Tables.TableName.PropertyMap, this); this.Property = new Property(Tables.TableName.Property, this); this.MethodSemantics = new MethodSemantics(Tables.TableName.MethodSemantics, this); this.MethodImpl = new MethodImpl(Tables.TableName.MethodImpl, this); this.ModuleRef = new ModuleRef(Tables.TableName.ModuleRef, this); this.TypeSpec = new TypeSpec(Tables.TableName.TypeSpec, this); this.ImplMap = new ImplMap(Tables.TableName.ImplMap, this); }