当 `OrdinalBase` 字段设置为 1 时,`kernel32.dll` 如何导出 0 的序数?

Posted

技术标签:

【中文标题】当 `OrdinalBase` 字段设置为 1 时,`kernel32.dll` 如何导出 0 的序数?【英文标题】:How can `kernel32.dll` export an ordinal of 0, when its `OrdinalBase` field is set to 1? 【发布时间】:2017-02-21 03:29:52 【问题描述】:

查看加载到内存中的 kernel32.dll,我看到以下导出序数表:

(gdb) x /400hd $eax

0x776334b0 <Wow64Transition+71576>:     3       4       5       6       7       8       9       10
0x776334c0 <Wow64Transition+71592>:     11      12      13      14      15      16      17      18
0x776334d0 <Wow64Transition+71608>:     19      20      21      22      23      24      25      26
0x776334e0 <Wow64Transition+71624>:     27      28      29      30      31      32      33      34
0x776334f0 <Wow64Transition+71640>:     35      36      37      38      39      40      41      42
0x77633500 <Wow64Transition+71656>:     43      44      45      46      47      48      49      50
0x77633510 <Wow64Transition+71672>:     51      52      53      54      55      56      57      58
0x77633520 <Wow64Transition+71688>:     59      60      61      62      63      64      65      66
0x77633530 <Wow64Transition+71704>:     67      68      69      70      0       71      72      73
0x77633540 <Wow64Transition+71720>:     74      75      76      77      78      79      80      81
0x77633550 <Wow64Transition+71736>:     82      83      84      85      86      87      88      89
0x77633560 <Wow64Transition+71752>:     90      91      92      93      94      95      96      97

可以验证,导出的序数为 0。

但是既然导出目录表的OrdinalBase字段设置为1,那么序数怎么会小于1呢?:

Ordinal Base:此中导出的起始序号 图片。此字段指定导出的起始序号 地址表。它通常设置为 1。

文档说序数是有偏差的,即:

导出序号表是导出的 16 位索引数组 地址表。序数受到 Ordinal Base 字段的影响 导出目录表。换句话说,序数基数必须是 从序数中减去以获得导出的真实索引 地址表。

现在,这意味着 0 的序数会在导出地址表中产生 -1 的索引?

在我看来,序数似乎是预先调整的(即从每个中减去 1),但是“官方”算法(也在 PE 文档中说明)失败了:

因此,当搜索导出名称指针表并匹配 字符串在位置 i 找到,用于查找符号的算法 地址是:

i = Search_ExportNamePointerTable (ExportName); 
ordinal =
ExportOrdinalTable [i]; 
SymbolRVA = ExportAddressTable [ordinal - OrdinalBase];

想到的唯一想法如下:加载器在将 DLL 加载到内存时调整了导出序号表中的序号。

谁能解释一下?

【问题讨论】:

但是PE文档中查找符号RVA的算法呢?那么这个算法不正确呢?它使用序数作为 RVA 查找的一部分。另外,如果不是正确的序数(对应于使用 dumpbin 看到的那些),序数表中的数字是多少? 还有,你说entry 0是序数4,那为什么会显示3呢? @HansPassant :另外,为什么从每个序数中减去 1(OrdinalBase)?如果你能回答这些问题,你就能让今年的圣诞节提前到来:) 【参考方案1】:

这是 PE/COFF 规范中的一个已知错误。指定的算法完全错误,应该是

ordinal = ExportOrdinalTable [i] + OrdinalBase;

不是

ordinal = ExportOrdinalTable [i];

因为序数表实际上包含无偏序数。

【讨论】:

谢谢,你有来源还是个人经验? 前一个项目写PE解析器和加载器的经验:) 我开始考虑以下问题:该算法适用于静态检查 DLL?似乎将 DLL 加载到内存中会改变序数,例如从 348(使用 DUMPBIN 验证)更改为 347(在运行时验证)。你的身份似乎很荒谬:ordinal = ExportOrdinalTable [i] + OrdinalBase; - 为什么添加 OrdinalBase 只是为了在之后立即减去它? 实际上,DUMPBIN 输出有偏序数,而不是存储在序数表中的无偏序数(我不得不重新访问我的一些 PE 解析代码来验证这一点)。换句话说,加载器改变序数。在 ExitProcess 的情况下,存储在表中的值为 347,“实际”(有偏差)序数为 248。 W.r.t.序数身份,这是一个语义问题。当然,你可以只做SymbolRVA = ExportAddressTable[ExportOrdinalTable[i]](我同意添加 OrdinalBase,然后减去它看起来有点愚蠢) - 我只是想指出文档中实际错误的位置,因为 real ordinal value 是 biased 之一(在添加 OrdinalBase 之后)。也就是说,分配变量ordinal = ExportOrdinalTable[i] 是没有意义的,因为这个“无偏序数”实际上只是一个索引。

以上是关于当 `OrdinalBase` 字段设置为 1 时,`kernel32.dll` 如何导出 0 的序数?的主要内容,如果未能解决你的问题,请参考以下文章

当我们单击选择 2 下拉菜单时,将焦点设置为搜索文本字段

工作流字段公式模式(业务)

当 API 没有该字段时的 setState

使用实体框架插入时设置 Id 字段

将学说实体布尔字段设置为 0 而不是 null

在Django Admin中将预填充的段塞字段设置为只读