/*
* PERWAPI - An API for Reading and Writing PE Files
*
* Copyright (c) Diane Corney, Queensland University of Technology, 2004.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the PERWAPI Copyright as included with this
* distribution in the file PERWAPIcopyright.rtf.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY as is explained in the copyright notice.
*
* The author may be contacted at d.corney@qut.edu.au
*
* Version Date: 26/01/07
*/
using System;
using System.IO;
using System.Collections;
namespace QUT.PERWAPI
{
/**************************************************************************/
// PE File Section Descriptor
/**************************************************************************/
///
/// Descriptor for a Section in a PEFile eg .text, .sdata
///
internal class Section
{
internal static readonly uint relocPageSize = 4096; // 4K pages for fixups
///
/// Eight characters exactly, null padded if necessary.
///
char[] name;
string nameString;
///
/// Total size of the section in bytes. If this value is
/// greater than SizeOFRawData the section is zero-padded.
///
uint loadedSize = 0;
///
/// Position in memory when loaded, relative to image base.
///
uint loadedRVA = 0;
///
/// Size of raw data in the section. Must be multiple of file alignment size.
/// Can be smaller than loadedSize, or larger (as a result of alignment).
///
uint sizeOnDisk = 0;
///
/// Offset to section's page within the PE file. Must be multiple
/// of file alignment constant.
///
uint fileOffset = 0;
// These are all zero mostly.
uint relocRVA, lineRVA, relocOff, numRelocs, numLineNums = 0;
///
/// Flags of section: code = 0x20, init-data = 0x40, un-init-data = 0x80,
/// execute = 0x20000000, read = 0x40000000, write = 0x80000000.
///
uint flags;
uint relocTide = 0;
uint padding = 0;
uint[] relocs;
internal Section(string sName, uint sFlags)
{
nameString = sName;
name = sName.ToCharArray();
flags = sFlags;
}
internal Section(PEReader input)
{
name = new char[8];
for (int i = 0; i < name.Length; i++)
name[i] = (char)input.ReadByte();
nameString = new String(name);
loadedSize = input.ReadUInt32();
loadedRVA = input.ReadUInt32();
sizeOnDisk = input.ReadUInt32();
fileOffset = input.ReadUInt32();
relocRVA = input.ReadUInt32();
lineRVA = input.ReadUInt32();
numRelocs = input.ReadUInt16();
numLineNums = input.ReadUInt16();
flags = input.ReadUInt32();
if (Diag.DiagOn)
{
Console.WriteLine(" " + nameString + " RVA = " + Hex.Int(loadedRVA) + " vSize = " + Hex.Int(loadedSize));
Console.WriteLine(" FileOffset = " + Hex.Int(fileOffset) + " aSize = " + Hex.Int(sizeOnDisk));
}
}
internal bool ContainsRVA(uint rvaPos)
{
return (loadedRVA <= rvaPos) && (rvaPos <= loadedRVA + loadedSize);
}
internal uint GetOffset(uint inRVA)
{
uint offs = 0;
if ((loadedRVA <= inRVA) && (inRVA <= loadedRVA + loadedSize))
offs = fileOffset + (inRVA - loadedRVA);
return offs;
}
internal uint Tide() { return loadedSize; }
internal void IncTide(uint incVal) { loadedSize += incVal; }
internal uint Padding() { return padding; }
internal uint Size() { return sizeOnDisk; }
internal void SetSize(uint pad)
{
padding = pad;
sizeOnDisk = loadedSize + padding;
}
internal uint RVA() { return loadedRVA; }
internal void SetRVA(uint loadedRVA) { this.loadedRVA = loadedRVA; }
internal uint Offset() { return fileOffset; }
internal void SetOffset(uint offs) { fileOffset = offs; }
internal void DoBlock(BinaryWriter reloc, uint page, int start, int end)
{
//Console.WriteLine("rva = " + rva + " page = " + page);
if (Diag.DiagOn) Console.WriteLine("writing reloc block at " + reloc.BaseStream.Position);
reloc.Write(loadedRVA + page);
uint blockSize = (uint)(((end - start + 1) * 2) + 8);
reloc.Write(blockSize);
if (Diag.DiagOn) Console.WriteLine("Block size = " + blockSize);
for (int j = start; j < end; j++)
{
//Console.WriteLine("reloc offset = " + relocs[j]);
reloc.Write((ushort)((0x3 << 12) | (relocs[j] - page)));
}
reloc.Write((ushort)0);
if (Diag.DiagOn) Console.WriteLine("finished reloc block at " + reloc.BaseStream.Position);
}
internal void DoRelocs(BinaryWriter reloc)
{
if (relocTide > 0)
{
// align block to 32 bit boundary
relocOff = (uint)reloc.Seek(0, SeekOrigin.Current);
if ((relocOff % 32) != 0)
{
uint padding = 32 - (relocOff % 32);
for (int i = 0; i < padding; i++)
reloc.Write((byte)0);
relocOff += padding;
}
uint block = (relocs[0] / relocPageSize + 1) * relocPageSize;
int start = 0;
for (int i = 1; i < relocTide; i++)
{
if (relocs[i] >= block)
{
DoBlock(reloc, block - relocPageSize, start, i);
start = i;
block = (relocs[i] / relocPageSize + 1) * relocPageSize;
}
}
DoBlock(reloc, block - relocPageSize, start, (int)relocTide);
}
}
internal void AddReloc(uint offs)
{
if (Diag.DiagOn) Console.WriteLine("Adding a reloc to " + nameString + " section");
int pos = 0;
if (relocs == null)
{
relocs = new uint[5];
}
else
{
if (relocTide >= relocs.Length)
{
uint[] tmp = relocs;
relocs = new uint[tmp.Length + 5];
for (int i = 0; i < relocTide; i++)
{
relocs[i] = tmp[i];
}
}
while ((pos < relocTide) && (relocs[pos] < offs)) pos++;
for (int i = pos; i < relocTide; i++)
{
relocs[i + 1] = relocs[i];
}
}
relocs[pos] = offs;
relocTide++;
if (Diag.DiagOn) Console.WriteLine("relocTide = " + relocTide);
}
internal void WriteHeader(BinaryWriter output, uint relocRVA)
{
if (Diag.DiagOn) Console.WriteLine("relocTide = " + relocTide);
output.Write(name);
output.Write(loadedSize); // Virtual size
output.Write(loadedRVA); // Virtual address
output.Write(sizeOnDisk); // SizeOfRawData
output.Write(fileOffset); // PointerToRawData
if (relocTide > 0)
{
output.Write(relocRVA + relocOff);
}
else
{
if (Diag.DiagOn) Console.WriteLine(nameString + " section has no relocs");
output.Write(0);
} // PointerToRelocations
output.Write(0); // PointerToLineNumbers
output.Write((ushort)relocTide); // NumberOfRelocations
output.Write((ushort)0); // NumberOfLineNumbers
output.Write(flags); // Characteristics
}
}
}