以下x86汇编程序指令有何功能?
call dword ptr ds:[00923030h]
这是我怀疑的间接电话,但它究竟是如何计算通话的地址的呢?
[编辑]更新
每当你看到类似的内存操作数时ds:0x00923030
,这就是一个段相对寻址模式.被参考的实际地址tp是相对于ds
段寄存器的基地址的线性地址0x00923030 .
x86架构中的内存分段有点令人困惑,我认为Wikipedia在解释它方面做得很好.
基本上,86具有许多特殊段寄存器: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"
(调用接近寄存器间接/存储器间接).
此特定操作码通过位于逻辑地址指向的位置的虚拟地址(此处为32位)进行调用ds:[00923030h]
.
逻辑地址由两个组件组成:
在这种情况下,16位段选择器ds,它基本上是操作系统管理的(全局/本地)描述符表的索引.这样的选择器还保存给定段的访问权限信息,该访问权限信息在访问时被检查(当前权限级别,CPL)
32位偏移
然后计算最终地址如下:从选择器+偏移量取出的基地址
请注意,上面的计算表示线性地址,而不是物理地址(参见intel手册第3a卷,图2.2),然后通过4KB分页的标准机制进行转换,即地址由页面目录的索引,页面组成.表和所选页面的偏移量.但请记住,所有主流操作系统都使用所谓的平坦内存模式,这意味着所有段选择器都指向地址0x00000000,限制设置为0xFFFFFFFF,这就是为什么你可以在所有段之间进行转换并最终导致(容易)利用缓冲区溢出.
您给出的汇编程序指令很可能是通过可执行文件的导入地址表(请参阅这篇伟大的文章以获取更多详细信息)进行调用,即这不太可能是一个序数子例程调用.
像这样的代码是由编译器发出的,因为来自外部dll的导入函数的最终虚拟地址通常在编译时是不可知的(由于dll的重新定义).通过使用这样的调用构造,OS加载器可以通过逻辑地址在地址指针处插入正确的虚拟地址,并且编译器无需关心最终函数具有哪个地址.