1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| /// <summary>
/// A single entry in the section table.
/// This is what allows to map a Relative Virtual Address (RVA) to a file offset.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct IMAGE_SECTION_HEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] Name;
/// <summary>Size of the section after loading.
/// It's the exact size, which means it can be smaller than <c>SizeOfRawData</c> because it does not include padding.
/// It can also be bigger, if the section contains "uninitialized data" that will occupy space in memory but not in the file.</summary>
public UInt32 VirtualSize;
/// <summary>RVA of the section's first byte.</summary>
public UInt32 VirtualAddress;
/// <summary>Size of the section within the PE file.</summary>
public UInt32 SizeOfRawData;
/// <summary>File offset of the section's first byte.</summary>
public UInt32 PointerToRawData;
public UInt32 PointerToRelocations;
public UInt32 PointerToLineNumbers;
public UInt16 NumberOfRelocations;
public UInt16 NumberOfLineNumbers;
public UInt32 Characteristics;
public string NameAsString { get { return Name==null ? null : Encoding.UTF8.GetString(Name).TrimEnd('\0'); } }
public void Dump(TextWriter output)
{
if(output==null) { throw new ArgumentNullException("output"); }
output.WriteLine("Name: {0}", NameAsString);
output.WriteLine("VirtualSize : {0:X8}", VirtualSize);
output.WriteLine("VirtualAddress : {0:X8}", VirtualAddress);
output.WriteLine("SizeOfRawData : {0:X8}", SizeOfRawData);
output.WriteLine("PointerToRawData : {0:X8}", PointerToRawData);
output.WriteLine("PointerToRelocations: {0:X8}", PointerToRelocations);
output.WriteLine("PointerToLineNumbers: {0:X8}", PointerToLineNumbers);
output.WriteLine("NumberOfRelocations : {0:X4}", NumberOfRelocations);
output.WriteLine("NumberOfLineNumbers : {0:X4}", NumberOfLineNumbers);
output.WriteLine("Characteristics : {0:X8}", Characteristics);
}
public static int CompareVirtualAddresses(IMAGE_SECTION_HEADER x, IMAGE_SECTION_HEADER y)
{
int res = x.VirtualAddress.CompareTo(y.VirtualAddress);
if(res != 0) { return res; }
return 0;
}
public static int CompareVirtualAddressesAndOffsets(IMAGE_SECTION_HEADER x, IMAGE_SECTION_HEADER y)
{
int res = x.VirtualAddress.CompareTo(y.VirtualAddress);
if(res != 0) { return res; }
res = x.PointerToRawData.CompareTo(y.PointerToRawData);
if(res != 0) { return res; }
return 0;
}
public override string ToString()
{
return string.Format("{0} at RVA {1:X8}, file offset {2:X8}", NameAsString, VirtualAddress, PointerToRawData);
}
}
/// <summary>
/// Handles the PE file's section table after it's loaded.
/// This class is immutable.
/// </summary>
class SectionTable : IComparer<IMAGE_SECTION_HEADER>
{
readonly List<IMAGE_SECTION_HEADER> unsortedSections;
readonly List<IMAGE_SECTION_HEADER> sortedSections;
public readonly uint sectionTableOffset;
public SectionTable(IEnumerable<IMAGE_SECTION_HEADER> sections, uint sectionTableOffset)
{
this.unsortedSections = new List<IMAGE_SECTION_HEADER>(sections ?? new IMAGE_SECTION_HEADER[0]);
this.sortedSections = new List<IMAGE_SECTION_HEADER>(unsortedSections);
this.sortedSections.Sort(IMAGE_SECTION_HEADER.CompareVirtualAddressesAndOffsets);
this.sectionTableOffset = sectionTableOffset;
}
public bool MapRVAToSection(uint rva, out IMAGE_SECTION_HEADER foundSection, out uint offsetInSection)
{
if(sortedSections==null) { throw new ArgumentNullException("sortedSections"); }
int ixFoundForReal;
IMAGE_SECTION_HEADER dummy = new IMAGE_SECTION_HEADER();
dummy.VirtualAddress = rva;
int ixBinarySearch = sortedSections.BinarySearch(dummy, this);
if(ixBinarySearch >= 0)
ixFoundForReal = ixBinarySearch;
else
{
int ixNext = ~ixBinarySearch;
if(ixNext==0) { foundSection=new IMAGE_SECTION_HEADER(); offsetInSection=0; return false; } //Before the first section? I don't think it's a valid RVA.
ixFoundForReal = ixNext-1;
}
Debug.Assert(ixFoundForReal >= 0, "ixFoundForReal can't be negative at this point.");
Debug.Assert(ixFoundForReal < sortedSections.Count, "ixFoundForReal can't exceed section table length.");
Debug.Assert(sortedSections[ixFoundForReal].VirtualAddress <= rva, "RVA can't be below the found section.");
foundSection = sortedSections[ixFoundForReal];
offsetInSection = rva - foundSection.VirtualAddress;
if(offsetInSection >= foundSection.VirtualSize)
return false; //RVA is outside the section's virtual data
if(offsetInSection >= foundSection.SizeOfRawData)
return false; //RVA can't be mapped into the file because it's in the part of the section that's not in the file.
return true;
}
public bool MapRVAToFileOffset(uint rva, out uint fileOffset)
{
if(sortedSections==null) { throw new ArgumentNullException("sortedSections"); }
IMAGE_SECTION_HEADER foundSection;
uint offsetInSection;
if(!MapRVAToSection(rva, out foundSection, out offsetInSection))
{ fileOffset=0; return false; }
fileOffset = foundSection.PointerToRawData + offsetInSection;
return true;
}
int IComparer<IMAGE_SECTION_HEADER>.Compare(IMAGE_SECTION_HEADER x, IMAGE_SECTION_HEADER y)
{
return IMAGE_SECTION_HEADER.CompareVirtualAddresses(x, y);
}
} |