ILRepack/Steps/Win32Resources/PE/ImageWriter.cs (200 lines of code) (raw):
//
// Copyright (c) 2018 Alexander Vostres
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
using System;
using System.IO;
using RVA = System.UInt32;
namespace ILRepacking.Steps.Win32Resources.PE
{
class ImageWriter : BinaryStreamWriter
{
const uint pe_header_size = 0x98u;
const uint section_header_size = 0x28u;
const uint file_alignment = 0x200;
const uint section_alignment = 0x2000;
const RVA text_rva = 0x2000;
private readonly ImageReader _source;
private ByteBuffer _win32Resources;
private Section _origText;
private Section _text;
private Section _rsrc;
private Section _origReloc;
private Section _reloc;
private ushort _sections;
public ImageWriter(ImageReader source, Stream stream) : base(stream)
{
_source = source;
}
public void Prepare(ResourceDirectory merged)
{
_origText = _source.GetSection(".text");
if (_origText == null)
{
throw new BadImageFormatException();
}
_text = CloneSectionHeader(_origText);
RelocateSection(_text, null);
_rsrc = _source.GetSection(".rsrc");
_origReloc = _source.GetSection(".reloc");
_sections = (ushort) (_origReloc == null ? 2 : 3);
if (_rsrc == null)
{
_rsrc = CreateSection(".rsrc", _text);
}
_win32Resources = RsrcWriter.WriteWin32ResourcesDirectory(_rsrc.VirtualAddress, merged);
SetSectionSize(_rsrc, (uint) _win32Resources.length);
if (_origReloc != null)
{
_reloc = CloneSectionHeader(_origReloc);
RelocateSection(_reloc, _rsrc);
}
}
private void RelocateSection(Section ret, Section previous)
{
ret.VirtualAddress = previous != null
? previous.VirtualAddress + Align(previous.VirtualSize, section_alignment)
: text_rva;
ret.PointerToRawData = previous != null
? previous.PointerToRawData + previous.SizeOfRawData
: Align(GetHeaderSize(), file_alignment);
}
void SetSectionSize(Section ret, uint size)
{
ret.VirtualSize = size;
ret.SizeOfRawData = Align(size, file_alignment);
}
Section CreateSection(string name, Section previous)
{
return new Section
{
Name = name,
VirtualAddress = previous.VirtualAddress + Align (previous.VirtualSize, section_alignment),
PointerToRawData = previous.PointerToRawData + previous.SizeOfRawData
};
}
Section CloneSectionHeader(Section src)
{
return new Section
{
Name = src.Name, PointerToRawData = src.PointerToRawData, SizeOfRawData = src.SizeOfRawData,
VirtualAddress = src.VirtualAddress, VirtualSize = src.VirtualSize
};
}
static uint Align (uint value, uint align)
{
align--;
return (value + align) & ~align;
}
public void Write()
{
_source.Position = 0;
CopyBytes(60);
var offset = _source.ReadUInt32();
WriteUInt32(offset);
if (offset > Position)
{
CopyBytes((int) (offset - Position));
}
CopyBytes(6);
var sectionCount = _reloc == null ? 2 : 3;
WriteUInt16((ushort) sectionCount);
CopyBytes(16);
WriteOptionalHeaders();
WriteSectionHeaders();
CopySection(_origText, _text);
WriteSection(_rsrc, _win32Resources);
CopySection(_origReloc, _reloc);
}
private void CopyBytes(int bytes)
{
_source.Position = Position;
WriteBytes(_source.ReadBytes(bytes));
}
private void WriteSection(Section section, ByteBuffer data)
{
MoveTo(section.PointerToRawData);
Write(data.buffer, 0, data.length);
if (data.length < section.SizeOfRawData)
{
var paddingCount = section.SizeOfRawData - data.length;
var padding = new byte[paddingCount];
WriteBytes(padding);
}
}
void MoveTo (uint pointer)
{
BaseStream.Seek (pointer, SeekOrigin.Begin);
}
private void CopySection(Section from, Section to)
{
const int buffer_size = 4096;
if (from.SizeOfRawData != to.SizeOfRawData)
{
throw new InvalidOperationException();
}
_source.MoveTo(from.PointerToRawData);
MoveTo(to.PointerToRawData);
if (from.SizeOfRawData <= buffer_size) {
Write (_source.ReadBytes(buffer_size));
return;
}
var written = 0;
var buffer = new byte [buffer_size];
while (written != from.SizeOfRawData) {
var writeSize = Math.Min((int) from.SizeOfRawData - written, buffer_size);
_source.Read(buffer, 0, writeSize);
Write (buffer, 0, writeSize);
written += writeSize;
}
}
private void WriteOptionalHeaders()
{
var pe64 = _source.Pe64;
CopyBytes(8);
WriteUInt32 ((_reloc?.SizeOfRawData ?? 0)
+ (_rsrc?.SizeOfRawData ?? 0)); // InitializedDataSize
CopyBytes(44);
var last_section = _reloc ?? _rsrc ?? _text;
WriteUInt32 (last_section.VirtualAddress + Align (last_section.VirtualSize, section_alignment)); // ImageSize
WriteUInt32 (_text.PointerToRawData); // HeaderSize
CopyBytes(pe64 ? 64 : 48);
WriteUInt32 (_rsrc?.VirtualAddress ?? 0); // ResourceTable
WriteUInt32 (_rsrc?.VirtualSize ?? 0);
CopyBytes(16);
WriteUInt32 (_reloc?.VirtualAddress ?? 0); // BaseRelocationTable
WriteUInt32 (_reloc?.VirtualSize ?? 0);
CopyBytes(80);
}
void WriteSectionHeaders ()
{
WriteSectionHeader (_origText, 0x60000020);
if (_rsrc != null)
WriteSectionHeader (_rsrc, 0x40000040);
if (_reloc != null)
WriteSectionHeader (_reloc, 0x42000040);
}
void WriteSectionHeader (Section section, uint characteristics)
{
var name = new byte [8];
var sect_name = section.Name;
for (int i = 0; i < sect_name.Length; i++)
name [i] = (byte) sect_name [i];
WriteBytes (name);
WriteUInt32 (section.VirtualSize);
WriteUInt32 (section.VirtualAddress);
WriteUInt32 (section.SizeOfRawData);
WriteUInt32 (section.PointerToRawData);
WriteUInt32 (0); // PointerToRelocations
WriteUInt32 (0); // PointerToLineNumbers
WriteUInt16 (0); // NumberOfRelocations
WriteUInt16 (0); // NumberOfLineNumbers
WriteUInt32 (characteristics);
}
static ushort SizeOfOptionalHeader (bool pe64)
{
return (ushort) (!pe64 ? 0xe0 : 0xf0);
}
public uint GetHeaderSize ()
{
return pe_header_size + SizeOfOptionalHeader (_source.Pe64) + (_sections * section_header_size);
}
}
}