public sealed class ImportDirectoryParser
{
public IReadOnlyList<ImportInfo> Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections, SafetyLimits limits)
{
if (nt.OptionalHeader.DataDirectories.Count <= 1)
{
return Array.Empty<ImportInfo>();
}
var dir = nt.OptionalHeader.DataDirectories[1];
if (dir.VirtualAddress == 0 || dir.Size == 0)
{
return Array.Empty<ImportInfo>();
}
var offset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
if (offset is null)
{
return Array.Empty<ImportInfo>();
}
BoundsChecker.EnsureRange(offset.Value, 20, rawBytes.Length, "import directory");
var imports = new List<ImportInfo>();
using var ms = new MemoryStream(rawBytes, writable: false);
using var reader = new BinaryReader(ms);
ms.Position = offset.Value;
while (ms.Position + 20 <= ms.Length)
{
BoundsChecker.EnsureCount(imports.Count + 1, limits.MaxImportsCount, "Import descriptors");
var originalFirstThunk = reader.ReadUInt32();
var timeDateStamp = reader.ReadUInt32();
var forwarderChain = reader.ReadUInt32();
var nameRva = reader.ReadUInt32();
var firstThunk = reader.ReadUInt32();
if (originalFirstThunk == 0 && timeDateStamp == 0 && forwarderChain == 0 && nameRva == 0 && firstThunk == 0)
{
break;
}
var dllName = DirectoryParserHelpers.ReadAsciiAtRva(rawBytes, nameRva, nt, sections, 512);
var thunkRva = originalFirstThunk != 0 ? originalFirstThunk : firstThunk;
var thunkOffset = RvaMapper.RvaToFileOffset(thunkRva, sections, nt.OptionalHeader.SizeOfHeaders);
var functions = new List<ImportFunctionInfo>();
if (thunkOffset is not null)
{
var thunkEntrySize = nt.OptionalHeader.IsPe32Plus ? 8 : 4;
BoundsChecker.EnsureRange(thunkOffset.Value, thunkEntrySize, rawBytes.Length, "import thunk table");
using var thunkStream = new MemoryStream(rawBytes, writable: false);
using var thunkReader = new BinaryReader(thunkStream);
thunkStream.Position = thunkOffset.Value;
while (thunkStream.Position + thunkEntrySize <= thunkStream.Length)
{
BoundsChecker.EnsureCount(functions.Count + 1, limits.MaxImportsCount, "Import functions");
ulong thunkValue = nt.OptionalHeader.IsPe32Plus
? thunkReader.ReadUInt64()
: thunkReader.ReadUInt32();
if (thunkValue == 0)
{
break;
}
var isOrdinal = nt.OptionalHeader.IsPe32Plus
? (thunkValue & 0x8000000000000000UL) != 0
: (thunkValue & 0x80000000U) != 0;
if (isOrdinal)
{
functions.Add(new ImportFunctionInfo(null, null, thunkValue & 0xFFFF, true));
}
else
{
var ibnOffset = RvaMapper.RvaToFileOffset((uint)thunkValue, sections, nt.OptionalHeader.SizeOfHeaders);
if (ibnOffset is null)
{
break;
}
BoundsChecker.EnsureRange(ibnOffset.Value, 2, rawBytes.Length, "import by name hint");
var hint = BitConverter.ToUInt16(rawBytes, ibnOffset.Value);
var name = BinaryReaderExtensions.ReadAsciiStringAt(rawBytes, ibnOffset.Value + 2, 1024);
functions.Add(new ImportFunctionInfo(name, hint, thunkValue, false));
}
}
}
imports.Add(new ImportInfo(dllName, functions));
}
return imports;
}
}