这个答案最初是我添加的,作为对我的问题的编辑,我现在将它与那里分开:
感谢 Guntrams 接受的答案,我发现了我的大脑有问题的地方。这是它的真实情况:
- 每个导出地址的序数只是导出地址数组中的索引(加上导出目录表中指定的序数基数,这可能使其与简单索引不同。这通常是这种情况,因为序数通常从 1 开始,根据到文档)。
- 有些导出有名字,这些名字用序号映射到导出地址数组,依次是导出地址数组的索引。
- 由于某些导出没有名称,因此在序数数组中未明确指定序数,因为将“无名称”映射到导出是没有意义的。
我的 C# 代码是这样做的,供参考 - 输出与 Dependency Walker 中的输出相同:
private void ReadExportTables(
PEFile peFile,
BinaryReader reader,
DataDirectoryHeader header)
{
// Read export address table which contains RVAs to exported code or forwarder names.
ExportEntry[] exportEntries = new ExportEntry[EntryCount];
reader.BaseStream.Seek(peFile.GetFileOffset(CodeAddressTableRva), SeekOrigin.Begin);
for (int i = 0; i < exportEntries.Length; i++)
{
exportEntries[i].Ordinal = (ushort)(i + OrdinalStartNumber);
exportEntries[i].CodeOrForwarderRva = reader.ReadUInt32();
}
// Read ordinal table containing indices (with base) to named entries in export entry table.
reader.BaseStream.Seek(peFile.GetFileOffset(OrdinalTableRva), SeekOrigin.Begin);
uint[] ordinals = new uint[NameEntryCount];
for (int i = 0; i < ordinals.Length; i++)
{
// Get name for ordinal, which has the same index as the ordinal array element.
ordinals[i] = reader.ReadUInt16();
}
// Read the export name pointer table which contains pointers to names of exports.
reader.BaseStream.Seek(peFile.GetFileOffset(NameAddressTableRva), SeekOrigin.Begin);
for (int i = 0; i < ordinals.Length; i++)
{
exportEntries[ordinals[i]].Hint = i;
exportEntries[ordinals[i]].NameRva = reader.ReadUInt32();
}
// Read the names of the exports or forwarders.
for (int i = 0; i < exportEntries.Length; i++)
{
if (exportEntries[i].NameRva > 0)
{
reader.BaseStream.Seek(peFile.GetFileOffset(
exportEntries[i].NameRva),
SeekOrigin.Begin);
exportEntries[i].Name = reader.Read0AsciiString();
}
// Check if forwarder export (RVA points within export directory to forwarder name).
if (exportEntries[i].CodeOrForwarderRva >= header.Rva
&& exportEntries[i].CodeOrForwarderRva < header.Rva + header.Size)
{
reader.BaseStream.Seek(
peFile.GetFileOffset(exportEntries[i].CodeOrForwarderRva),
SeekOrigin.Begin);
exportEntries[i].ForwarderName = reader.Read0AsciiString();
}
}
}
如果您想知道:仍然可能存在完全空的导出,代码地址为 0,没有名称,因此不会被转发。只需在显示导出时对这些进行排序即可。