/// <summary> /// Emit built sections into the R2R PE file. /// </summary> /// <param name="outputStream">Output stream for the final R2R PE file</param> public void Write(Stream outputStream) { BlobBuilder outputPeFile = new BlobBuilder(); Serialize(outputPeFile); CorHeaderBuilder corHeader = CorHeader; if (corHeader != null) { corHeader.Flags = (CorHeader.Flags & ~CorFlags.ILOnly) | CorFlags.ILLibrary; corHeader.MetadataDirectory = RelocateDirectoryEntry(corHeader.MetadataDirectory); corHeader.ResourcesDirectory = RelocateDirectoryEntry(corHeader.ResourcesDirectory); corHeader.StrongNameSignatureDirectory = RelocateDirectoryEntry(corHeader.StrongNameSignatureDirectory); corHeader.CodeManagerTableDirectory = RelocateDirectoryEntry(corHeader.CodeManagerTableDirectory); corHeader.VtableFixupsDirectory = RelocateDirectoryEntry(corHeader.VtableFixupsDirectory); corHeader.ExportAddressTableJumpsDirectory = RelocateDirectoryEntry(corHeader.ExportAddressTableJumpsDirectory); corHeader.ManagedNativeHeaderDirectory = RelocateDirectoryEntry(corHeader.ManagedNativeHeaderDirectory); _sectionBuilder.UpdateCorHeader(corHeader); } _sectionBuilder.RelocateOutputFile( outputPeFile, _peReader.PEHeaders.PEHeader.ImageBase, corHeader, CorHeaderFileOffset, outputStream); RelocateMetadataBlob(outputStream); _written = true; }
/// <summary> /// Update the COR header. /// </summary> /// <param name="corHeader">COR header builder to update</param> public void UpdateCorHeader(CorHeaderBuilder corHeader) { if (_readyToRunHeaderSymbol != null) { SymbolTarget headerTarget = _symbolMap[_readyToRunHeaderSymbol]; Section headerSection = _sections[headerTarget.SectionIndex]; Debug.Assert(headerSection.RVAWhenPlaced != 0); int r2rHeaderRVA = headerSection.RVAWhenPlaced + headerTarget.Offset; corHeader.ManagedNativeHeaderDirectory = new DirectoryEntry(r2rHeaderRVA, _readyToRunHeaderSize); } }
/// <summary> /// Relocate the produced PE file and output the result into a given stream. /// </summary> /// <param name="peFile">Blob builder representing the complete PE file</param> /// <param name="defaultImageBase">Default load address for the image</param> /// <param name="corHeaderBuilder">COR header</param> /// <param name="corHeaderFileOffset">File position of the COR header</param> /// <param name="outputStream">Stream to receive the relocated PE file</param> public void RelocateOutputFile( BlobBuilder peFile, ulong defaultImageBase, CorHeaderBuilder corHeaderBuilder, int corHeaderFileOffset, Stream outputStream) { RelocationHelper relocationHelper = new RelocationHelper(outputStream, defaultImageBase, peFile); if (corHeaderBuilder != null) { relocationHelper.CopyToFilePosition(corHeaderFileOffset); UpdateCorHeader(corHeaderBuilder); BlobBuilder corHeaderBlob = new BlobBuilder(); corHeaderBuilder.WriteTo(corHeaderBlob); int writtenSize = corHeaderBlob.Count; corHeaderBlob.WriteContentTo(outputStream); relocationHelper.AdvanceOutputPos(writtenSize); // Just skip the bytes that were emitted by the COR header writer byte[] skipBuffer = new byte[writtenSize]; relocationHelper.CopyBytesToBuffer(skipBuffer, writtenSize); } // Traverse relocations in all sections in their RVA order foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced)) { int rvaToFilePosDelta = section.FilePosWhenPlaced - section.RVAWhenPlaced; foreach (ObjectDataRelocations objectDataRelocs in section.Relocations) { foreach (Relocation relocation in objectDataRelocs.Relocs) { // Process a single relocation int relocationRVA = section.RVAWhenPlaced + objectDataRelocs.Offset + relocation.Offset; int relocationFilePos = relocationRVA + rvaToFilePosDelta; // Flush parts of PE file before the relocation to the output stream relocationHelper.CopyToFilePosition(relocationFilePos); // Look up relocation target SymbolTarget relocationTarget = _symbolMap[relocation.Target]; Section targetSection = _sections[relocationTarget.SectionIndex]; int targetRVA = targetSection.RVAWhenPlaced + relocationTarget.Offset; // Apply the relocation relocationHelper.ProcessRelocation(relocation.RelocType, relocationRVA, targetRVA); } } } // Flush remaining PE file blocks after the last relocation relocationHelper.CopyRestOfFile(); }
/// <summary> /// Emit built sections using the R2R PE writer. /// </summary> /// <param name="builder">Section builder to emit</param> /// <param name="machine">Target machine architecture</param> /// <param name="inputReader">Input MSIL reader</param> /// <param name="outputStream">Output stream for the final R2R PE file</param> public static void EmitR2R( this SectionBuilder builder, Machine machine, PEReader inputReader, Action <PEDirectoriesBuilder> directoriesUpdater, Stream outputStream) { R2RPEBuilder r2rBuilder = new R2RPEBuilder( machine: machine, peReader: inputReader, sectionNames: builder.GetSections(), sectionSerializer: builder.SerializeSection, directoriesUpdater: (PEDirectoriesBuilder directoriesBuilder) => { builder.UpdateDirectories(directoriesBuilder); if (directoriesUpdater != null) { directoriesUpdater(directoriesBuilder); } }); BlobBuilder outputPeFile = new BlobBuilder(); r2rBuilder.Serialize(outputPeFile); CorHeaderBuilder corHeader = r2rBuilder.CorHeader; if (corHeader != null) { corHeader.Flags = (r2rBuilder.CorHeader.Flags & ~CorFlags.ILOnly) | CorFlags.ILLibrary; corHeader.MetadataDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.MetadataDirectory); corHeader.ResourcesDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.ResourcesDirectory); corHeader.StrongNameSignatureDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.StrongNameSignatureDirectory); corHeader.CodeManagerTableDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.CodeManagerTableDirectory); corHeader.VtableFixupsDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.VtableFixupsDirectory); corHeader.ExportAddressTableJumpsDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.ExportAddressTableJumpsDirectory); corHeader.ManagedNativeHeaderDirectory = r2rBuilder.RelocateDirectoryEntry(corHeader.ManagedNativeHeaderDirectory); builder.UpdateCorHeader(corHeader); } builder.RelocateOutputFile( outputPeFile, inputReader.PEHeaders.PEHeader.ImageBase, corHeader, r2rBuilder.CorHeaderFileOffset, outputStream); r2rBuilder.RelocateMetadataBlob(outputStream); }
/// <summary> /// Output the section with a given name. For sections existent in the source MSIL PE file /// (.text, optionally .rsrc and .reloc), we first copy the content of the input MSIL PE file /// and then call the section serialization callback to emit the extra content after the input /// section content. /// </summary> /// <param name="name">Section name</param> /// <param name="location">RVA and file location where the section will be put</param> /// <returns>Blob builder representing the section data</returns> protected override BlobBuilder SerializeSection(string name, SectionLocation location) { BlobBuilder sectionDataBuilder = null; bool haveCustomSection = _customSections.Contains(name); int sectionIndex = _peReader.PEHeaders.SectionHeaders.Count() - 1; int sectionStartRva = location.RelativeVirtualAddress; while (sectionIndex >= 0 && _peReader.PEHeaders.SectionHeaders[sectionIndex].Name != name) { sectionIndex--; } if (sectionIndex >= 0) { SectionHeader sectionHeader = _peReader.PEHeaders.SectionHeaders[sectionIndex]; int sectionOffset = (_peReader.IsLoadedImage ? sectionHeader.VirtualAddress : sectionHeader.PointerToRawData); int rvaDelta = location.RelativeVirtualAddress - sectionHeader.VirtualAddress; _sectionRvaDeltas.Add(new SectionRVADelta( startRVA: sectionHeader.VirtualAddress, endRVA: sectionHeader.VirtualAddress + Math.Max(sectionHeader.VirtualSize, sectionHeader.SizeOfRawData), deltaRVA: rvaDelta)); unsafe { int bytesToRead = Math.Min(sectionHeader.SizeOfRawData, sectionHeader.VirtualSize); BlobReader inputSectionReader = _peReader.GetEntireImage().GetReader(sectionOffset, bytesToRead); if (name == ".rsrc") { // There seems to be a bug in BlobBuilder - when we LinkSuffix to an empty blob builder, // the blob data goes out of sync and WriteContentTo outputs garbage. sectionDataBuilder = PEResourceHelper.Relocate(inputSectionReader, rvaDelta); } else { sectionDataBuilder = new BlobBuilder(); sectionDataBuilder.WriteBytes(inputSectionReader.CurrentPointer, inputSectionReader.RemainingBytes); int corHeaderRvaDelta = _peReader.PEHeaders.PEHeader.CorHeaderTableDirectory.RelativeVirtualAddress - sectionHeader.VirtualAddress; if (corHeaderRvaDelta >= 0 && corHeaderRvaDelta < bytesToRead) { // Assume COR header resides in this section, deserialize it and store its location _corHeaderFileOffset = location.PointerToRawData + corHeaderRvaDelta; inputSectionReader.Offset = corHeaderRvaDelta; _corHeaderBuilder = new CorHeaderBuilder(ref inputSectionReader); } } int alignedSize = sectionHeader.VirtualSize; // When custom section data is present, align the section size to 4K to prevent // pre-generated MSIL relocations from tampering with native relocations. if (_customSections.Contains(name)) { alignedSize = (alignedSize + 0xFFF) & ~0xFFF; } if (alignedSize > bytesToRead) { // If the number of bytes read from the source PE file is less than the virtual size, // zero pad to the end of virtual size before emitting extra section data sectionDataBuilder.WriteBytes(0, alignedSize - bytesToRead); } location = new SectionLocation( location.RelativeVirtualAddress + sectionDataBuilder.Count, location.PointerToRawData + sectionDataBuilder.Count); } } if (_sectionSerializer != null) { BlobBuilder extraData = _sectionSerializer(name, location, sectionStartRva); if (extraData != null) { if (sectionDataBuilder == null) { // See above - there's a bug due to which LinkSuffix to an empty BlobBuilder screws up the blob content. sectionDataBuilder = extraData; } else { sectionDataBuilder.LinkSuffix(extraData); } } } // Make sure the section has at least 1 byte, otherwise the PE emitter goes mad, // messes up the section map and corrups the output executable. if (sectionDataBuilder == null) { sectionDataBuilder = new BlobBuilder(); } if (sectionDataBuilder.Count == 0) { sectionDataBuilder.WriteByte(0); } return(sectionDataBuilder); }