public sealed class RelocationDirectoryParser
{
public IReadOnlyList<RelocationBlockInfo> Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections)
{
if (nt.OptionalHeader.DataDirectories.Count <= 5)
{
return Array.Empty<RelocationBlockInfo>();
}
var dir = nt.OptionalHeader.DataDirectories[5];
if (dir.VirtualAddress == 0 || dir.Size == 0)
{
return Array.Empty<RelocationBlockInfo>();
}
var offset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
if (offset is null)
{
return Array.Empty<RelocationBlockInfo>();
}
var blocks = new List<RelocationBlockInfo>();
var pos = offset.Value;
var end = Math.Min(rawBytes.Length, offset.Value + (int)dir.Size);
while (pos + 8 <= end)
{
var pageRva = BitConverter.ToUInt32(rawBytes, pos);
var blockSize = BitConverter.ToUInt32(rawBytes, pos + 4);
if (blockSize < 8 || pos + blockSize > end)
{
break;
}
var count = ((int)blockSize - 8) / 2;
var entries = new List<RelocationEntryInfo>(count);
for (var i = 0; i < count; i++)
{
var raw = BitConverter.ToUInt16(rawBytes, pos + 8 + i * 2);
entries.Add(new RelocationEntryInfo((ushort)(raw >> 12), (ushort)(raw & 0x0FFF)));
}
blocks.Add(new RelocationBlockInfo(pageRva, blockSize, entries));
pos += (int)blockSize;
}
return blocks;
}
}