以下x86汇编指令的作用是什么?
call dword ptr ds:[00923030h]
我猜这是一次间接调用,但它是如何计算调用地址的呢?
以下x86汇编指令的作用是什么?
call dword ptr ds:[00923030h]
我猜这是一次间接调用,但它是如何计算调用地址的呢?
[编辑] 更新
每当您看到类似 ds:0x00923030
的内存操作数时,那就是一种 段相对 寻址方式。实际的地址是相对于 ds
段寄存器基地址的线性地址 0x00923030。
x86 架构中的内存分段有些令人困惑,我认为 维基百科 解释得很好。
基本上,x86 有一些特殊的 段 寄存器:cs
(代码段)、ds
(数据段)、es
、fs
、gs
和 ss
(栈段)。每个内存访问都与某个段寄存器关联。通常情况下,您不需要指定段寄存器,取决于内存访问的方式,会使用默认的段寄存器。例如,cs
寄存器用于读取指令。
每个段寄存器都有一个特定的 基地址 和一个 限制。基地址确定与线性地址 0x00000000 对应的物理地址,限制确定该段的最大允许线性地址。例如,如果基地址为 0x00040000,限制为 0x0000FFFF,则仅有效的线性地址为 0x00000000 到 0x0000FFFF,并且相应的物理地址为 0x00040000 到 0x0004FFFF。
因此,调用子程序所在的物理地址由存储在 ds
段寄存器中的基地址加上 0x00923030 给出。但是我们还没结束——指令中有一个单词 ptr
。这增加了一层间接寻址,因此子程序的实际目标是存储在位置 ds:0x00923030
上的地址。
在 AT&T 语法(被 GNU 汇编器接受)中,该指令将如下编写:
lcall *ds:0x00923030
如果想了解指令的全部细节,请参考80386参考手册。这个指令的特定变体是"CALL r/m16"
(调用近距离寄存器间接/内存间接)。
这个特定的操作码通过逻辑地址ds:[00923030h]
指向的虚拟地址(32位)进行调用。
逻辑地址由两个组件组成:
请注意,上述计算表示线性地址,而不是物理地址(参见英特尔手册第3a卷,图2.2),然后通过4KB分页的标准机制进行转换,即地址包括页目录、页表的索引以及所选页内的偏移量。但请记住,所有主流操作系统都使用所谓的平坦内存模型,这意味着所有段选择器都指向地址0x00000000,限制设置为0xFFFFFFFF,这就是可以在所有段之间进行转换并最终导致(容易)利用缓冲区溢出的原因。
给出的汇编指令很可能是通过可执行文件的导入地址表进行调用(有关详细信息,请参见这篇精彩文章),即这很不可能是序数子程序调用。
编译器会生成这样的代码,是因为外部dll导入函数的最终虚拟地址通常在编译时无法确定(由于dll的重新定位)。使用这种调用结构,操作系统加载程序可以将正确的虚拟地址插入到逻辑地址指向的地址指针中,编译器无需关心最终函数的地址是什么。
call dword ptr ds:[00923030h]
的意思是,在数据段
中调用指针00923939h
处的某个值。
据我所知,它获取DS寄存器的值(并将其左移4位),加上给定的立即值,从生成的内存位置获取一个双字值,该值成为要调用的地址。(编辑:这适用于16位实模式,对于保护模式,请参见其他答案。)
ds:
是数据寻址模式的默认值,除了以ebp
或esp
为基址寄存器的情况。这个答案似乎也暗示着call ds:1234
会有更少的间接级别,即它可能会执行EIP=1234,而不是从内存地址ds:1234
加载新的EIP值。但这毫无意义;在ds:1234
中的ds:
已经意味着它是一个(数据)内存源操作数。 - Peter Cordescall r/m32
,而不是16
。dword是32位,word是16位。大多数这些错误都很小,而且它们之间有一个相当好的答案,但链接的重复答案已经非常好了。 - Peter Cordes