ExportCount
,下表中的第二行)。我读起来就像出口名称的计数与序数索引的计数相同。至少他们的文档说的是这样:我试图解析Win8.1.1 x64 Shell32.dll,与Dependency Walker相比,我得到了不同的结果。我有933作为
NameCount
和354作为ExportCount
。因此,总共应该有933个出口,只有354个具有序数和/或名称。不要问我如何导入剩余的579个导出,这是我不了解的。如果我在Dependency Walker中打开Shell32,它将首先列出
NameCount
导出的名称和顺序,但随后它显示了剩余的NameCount
出口数量,这些出口出奇的是确实具有普通货币(从蓝线开始):根据文档理解。我尝试依次阅读ExportCount - NameCount
普通字符而不是ExportCount
,但是只有垃圾出现。所以我的问题是:不完整吗?
我是否理解文档中的某些错误?我的大脑有毛病。实际情况就是这样:
每个导出地址的序数就是导出地址数组中的索引(不过加上导出目录表中指定的序数基,这可能会使它不同于简单索引。通常是这样,因为根据文档,序数通常从1开始。
一些导出具有名称,并且这些名称使用序数映射到导出地址数组,而序数又是到导出地址数组的索引。序数数组,因为将“无名”映射到导出是没有意义的。
private void ReadExportTables(PEFile peFile, BinaryReader reader, DataDirectoryHeader header)
{
// Read the export address table which contains the RVAs to the 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 the ordinal table containing indices (with base) to named entries in the 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 the name for the 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.ReadString(BinaryStringFormat.ZeroTerminated, Encoding.ASCII);
}
// Check if it's a forwarder export (the RVA points within the export directory to a 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.ReadString(BinaryStringFormat.ZeroTerminated,
Encoding.ASCII);
}
}
}
如果您想知道:仍然可以存在完全空的出口,其代码地址为0,没有名称,因此也没有转发。只需在显示您的出口时将它们分类即可。
#1 楼
如果我正确理解http://win32assembly.programminghorizon.com/pe-tut7.html,则顺序表仅列出实际具有名称的导出数量。因此,您的程序可能具有以下导出:n name address
0 funca 12345678
1 -- no name -- 9abcdef0
2 funcb 76543210
3 -- no name -- fedcba98
这将导致
address table entries = 4
number of name pointers = 2
export address table = [ 12345678, 9abcdef0, 76543210, fedcba98 ]
name pointer table = [ funca, funcb ]
ordinal table = [ 0, 2 ]
名称指针条目和顺序表条目之间的1:1匹配,这就是为什么名称指针的数量等于两个数组大小的原因。但是两个表都只列出了按名称导出的功能。仅按顺序的导出不会出现在其中任何一个中。但是,它们仍会出现在导出地址表中。东西)。
#2 楼
该文档是正确的。可以按名称或顺序导出项目。要按名称导出,顺序表用于在导出表中找到地址。 “名称”表中名称的索引是“序号”表中序数的索引。 Ordinal表中ordinal的值是地址表中的索引。
对于按序导出,该ordinal的值用于直接索引到Address表中(因此完全可以完全没有名字,但出口很多)。序表中没有任何条目。出于明显的原因,除非有协议规定此类出口永远都不能移动,否则不建议按序进行出口。
作为一种特殊情况,在直到您减去序数基数。例如,如果我创建一个包含两个导出的DLL,其OrdinalBase为-1,则我的ExportCount将显示为4。
评论
我对此也有类似的想法,但这意味着我没有机会阅读那些未命名出口的序号,例如(无名称)在您的示例中以1和3作为序数进行导出(因为可能无法保证它们的顺序递增)。
–雷
2015年3月4日13:58
嗯,我敢打赌你是对的。我只是一无所获(请参见顶部的附加假设),即序数只是导出地址表的索引。
–雷
15年3月4日在14:11
一如既往,Guntram是一个很好的答案。 @DebugErr,正如我确定您现在已经注意到的那样,Microsoft的PE规范文档中的措辞可能会产生误导;我尝试改用结构字段的名称(例如,用AddressOfNameOrdinals代替“ Ordinal Table RVA”)。正如Guntram指出的那样,Iczelion的教程在解释这些PE结构中做得非常出色。
–詹森·格夫纳(Jason Geffner)
15年3月4日在16:49