public sealed class ComDescriptorParser
{
    public ComDescriptorInfo? Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections)
    {
        if (nt.OptionalHeader.DataDirectories.Count <= 14)
        {
            return null;
        }

        var dir = nt.OptionalHeader.DataDirectories[14];
        if (dir.VirtualAddress == 0 || dir.Size == 0)
        {
            return null;
        }

        var offset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
        if (offset is null || offset.Value + 24 > rawBytes.Length)
        {
            return null;
        }

        var pos = offset.Value;
        var cb = BitConverter.ToUInt32(rawBytes, pos);
        var major = BitConverter.ToUInt16(rawBytes, pos + 4);
        var minor = BitConverter.ToUInt16(rawBytes, pos + 6);
        var metadataRva = BitConverter.ToUInt32(rawBytes, pos + 8);
        var metadataSize = BitConverter.ToUInt32(rawBytes, pos + 12);
        var flags = BitConverter.ToUInt32(rawBytes, pos + 16);
        var entryPoint = BitConverter.ToUInt32(rawBytes, pos + 20);

        return new ComDescriptorInfo(cb, major, minor, new DataDirectory(metadataRva, metadataSize), flags, entryPoint);
    }
}