从最近由 MSVC 编译的“HelloWorld.exe”的反汇编中:
.text:00411279 call ds:__imp__printf
那么,为什么基地址是数据段,而不是代码段(这样做更合乎逻辑?)
项目使用多线程调试 Dll 作为运行时库。
从最近由 MSVC 编译的“HelloWorld.exe”的反汇编中:
.text:00411279 call ds:__imp__printf
那么,为什么基地址是数据段,而不是代码段(这样做更合乎逻辑?)
项目使用多线程调试 Dll 作为运行时库。
__imp__printf
不是函数的代码;它是函数代码的指针。由于它是一个指针(它是数据,而不是代码),因此指针值位于数据段中而不是代码段中是有意义的。更具体地说,这个指针值是由加载器在运行时设置的,所以内存页需要是可写的,这更像是将它放在数据段而不是代码段中的一个原因。该ds:
由IDA加入,而不是由编译器。如果查看原始操作码字节,您将看到指令中没有 DS 覆盖前缀。这样做很愚蠢。
IDA 添加了这个ds:
前缀,否则你不会知道这是一个间接调用——也就是说,它正在读取一个名为地址的 32 位变量,__imp__printf
然后调用存储在该变量中的地址。如果没有ds:
,它将只是直接调用__imp__printf
。
如果 IDA 使用更好的汇编语言语法——即 nasm 语法——该指令看起来很简单,使用方括号表示它是内存读取(并dword
与其他一些奇怪的 类型区分开来call
):
.text:00411279 call dword [__imp__printf]
Windows 与几乎所有其他 32 位操作系统一样,具有扁平地址空间。CS、DS、ES 和 SS 都具有相同的基地址 0,因此您使用哪个段作为基地址并不重要。(除了如果 CS 是你的段,你不能进行内存写入。)FS 和 GS 有不同的基础,因为主要操作系统都将它们用于线程本地存储,但那些在指令中总是有明确的前缀字节。