下面的 x86 汇编指令有什么作用?
call dword ptr ds:[00923030h]
我怀疑这是一个间接调用,但它究竟是如何计算调用地址的呢?
下面的 x86 汇编指令有什么作用?
call dword ptr ds:[00923030h]
我怀疑这是一个间接调用,但它究竟是如何计算调用地址的呢?
[编辑] 更新
每当您看到类似于 的内存操作数时ds:0x00923030
,这就是一种相对于段的寻址模式。ds
被引用的实际地址 tp 位于相对于段寄存器的基地址的线性地址 0x00923030 。
x86 架构中的内存分割有点令人困惑,我认为Wikipedia很好地解释了它。
基本上,x86 有许多特殊的段寄存器:(cs
代码段)、(ds
数据段)es
、、、、和(堆栈段)。每个内存访问都与某个段寄存器相关联。通常,您不指定段寄存器,并且根据访问内存的方式,使用默认的段寄存器。例如,寄存器用于读取指令。fs
gs
ss
cs
每个段寄存器都有一定的基地址和限制。基地址决定了线性地址 0x00000000 对应的物理地址,限制决定了该段允许的最大线性地址。例如,如果基地址为 0x00040000,限制为 0x0000FFFF,则唯一有效的线性地址将是 0x00000000 到 0x0000FFFF,对应的物理地址将是 0x00040000 到 0x0004FFFF。
因此,被调用的子程序所在的物理地址由存储在ds
段寄存器中的基地址加上 0x00923030 给出。但我们还没有完成——指令中有这个词ptr
。这增加了一个额外的间接级别,因此子例程的实际目标是存储在 location的地址ds:0x00923030
。
在 AT&T 语法(被 GNU 汇编器接受)中,该指令将编写如下:
lcall *ds:0x00923030
有关该指令作用的完整详细信息,请参阅80386 参考手册。该指令的特定变体是"CALL r/m16"
(调用近寄存器间接/内存间接)。
此特定操作码通过位于逻辑地址指向的位置的虚拟地址(此处为 32 位)进行调用ds:[00923030h]
。
逻辑地址由两部分组成:
请注意,上面的计算表示线性地址,而不是物理地址(参见英特尔手册第 3a 卷,图 2.2),然后通过 4KB 分页的标准机制进行转换,即地址由页面目录的索引组成,page表和所选页面的偏移量。但请记住,所有主流操作系统都使用所谓的平面内存模型,这意味着所有段选择器都指向地址 0x00000000,限制设置为 0xFFFFFFFF,这就是您可以在所有段之间进行转换并最终导致的原因(容易)利用缓冲区溢出。
您给出的汇编指令很可能是通过可执行文件的导入地址表(有关更多详细信息,请参阅这篇精彩的文章)的调用,即这不太可能是序数子程序调用。
像这样的代码是由编译器发出的,因为从外部 dll 导入的函数的最终虚拟地址在编译时通常是未知的(由于 dll 的变基)。通过使用这样的调用构造,操作系统加载程序可以在逻辑地址指向的地址指针处插入正确的虚拟地址,并且编译器不需要关心最终函数具有哪个地址。
call dword ptr ds:[00923030h]
, 表示call
指针00923939h
中的某些值data segment
IIRC,它获取 DS 寄存器的值(并将其左移 4 位),将给定的立即值相加,从生成的内存位置获取一个 dword 值,该值成为要调用的地址。(编辑:这适用于 16 位实模式,对于保护模式,请参阅其他答案。)