x86 代码 - 通过 ds 段选择器调用?

x86 code - Call through ds segment selector?

我正在使用 windbg 在 windows 上调试 32 位 x86 代码级别的东西。有些事情让我感到困惑,我将其缩减为似乎通过 ds 段选择器的 call,这显然我不明白。

下面这只是重现我的困惑的最小示例,这不是我正在处理的实际问题。

我在 DLL 中有一个函数

__declspec(dllexport) void __cdecl MyDllTest_Cdecl(int i);

void __cdecl MyDllTest_Cdecl(int i)
{
    printf("In MyDll MyDllTest_Cdecl(%d)\n", i);
}

在可执行文件中,我 link 静态地访问 DLL 并调用函数

void __cdecl MyDllTest_Cdecl(int i);

MyDllTest_Cdecl(1);

在 windbg 中调试进程,在函数调用的位置执行,我在 windbg 中找到了以下机器码

call    dword ptr [UseDll_Static!_imp__MyDllTest_Cdecl (00062118)] ds:002b:00062118={MyDll!MyDllTest_Cdecl (68391100)}

我在 windbg 中关闭了源模式,(l-t) 所以跟踪命令 (t) 应该总是单步执行机器码,不隐藏任何东西。

当我使用 trace (t) 命令时,执行突然移动到 0x68391100(如调用命令的反汇编所示)。

这是如何工作的?地址0x68391100从哪里来? x86 反汇编器显示机器代码 ff1518219e00 是

call   DWORD PTR ds:0x00062118

正如 windbg 显示的那样。

我知道 compiler/linker/loader 通过 calljmp 指令使用间接寻址,特别是对于在动态链接库。我看到在其他情况下,但我能够单步执行构成 C 级函数调用的每个 calljmp。在这种情况下,它是一个单一的步骤。所以这不是由 compiler/linker/loader.

创建的 calljmp 指令的间接寻址

在地址 0x00062118 我看到以下内容:

0:000> u 00062118
UseDll_Static!_imp__MyDllTest_Cdecl:
00062118 0011            add     byte ptr [ecx],dl
0006211a 396820          cmp     dword ptr [eax+20h],ebp
0006211d 1139            adc     dword ptr [ecx],edi

所以没有 jmpcall 可能是间接寻址的一部分。它是符号的地址 UseDll_Static!_imp__MyDllTest_Cdecl 但那里的代码看起来像垃圾。

符号可以忽略,我就想知道怎么办

call   DWORD PTR ds:00062118

将执行移动到 0x68391100。 ds 寄存器为 0x002b.

我想我基本上理解了实模式下的x86分段。但我认为在保护模式下的现代 Windows 虚拟内存基本上是一个平坦的 2^32 地址 space。无论如何,如果有一些分段偏移,在这种情况下它将是 0x68391100 - 0x000621180x6832efe8 .我希望偏移量以一个或多个零结尾。

这可能是完全不相关的,但是当我尝试使用dg windbg 命令显示

0:000> dg 0x002b
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
002B 00000000 ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3

感谢您的帮助...这是一个简单的问题,但我试图避免关注 DLL 中的函数是如何 linked 和加载的,或者关注 x86 实模式分段的历史。我只想在机器码层面上理解。

0x00062118是存放实际函数地址的段偏移量。 如果您查看您的反汇编,位于 0x00062118 的前四个字节是 0x00、0x11、0x39 和 0x68。它们会以相反的顺序加载到 EIP 中,得到 0x68391100,这就是你跳转到的地址!