【问题标题】:Why does "push ebp" change the value of ebp?为什么“push ebp”会改变ebp的值?
【发布时间】:2012-04-03 20:55:07
【问题描述】:

我有一个简单的代码:

void func()
{
    func2();
}

我省略了 func2 和 main 的实现,因为它们无关紧要。然后我用windbg跟踪汇编,下面是执行“func2()”时汇编代码的输出:

eax=cccccccc ebx=7ffd6000 ecx=00000000 edx=00000001 esi=01faf760 edi=0012ff68
eip=004113f0 esp=0012fe98 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func:
004113f0 55              push    ebp
0:000> t
eax=cccccccc ebx=7ffd6000 ecx=00000000 edx=00000001 esi=01faf760 edi=0012fe94
eip=0041140e esp=0012fdc8 ebp=0012fe94 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
SimpleStack!func+0x1e:
0041140e e83dfcffff      call    SimpleStack!ILT+75(_func2) (00411050)

我们可以看到执行push ebp之前esp = 0012fe98, and ebp=0012ff68,但是执行push ebp之后esp=0012fdc8, ebp=0012fe94?,所以我有两个问题: 1. 推测push code应该只影响esp,为什么ebp也变了? 2. 推测push ebp会push 4字节入栈,所以esp的值应该减4,但是我们看到这里esp的值减了208字节?为什么?

【问题讨论】:

  • 它没有。 push ebp 完全按照它在锡上说的做。请注意,如果只执行了推送,eip 增加了 1E,它只会增加 1。显然,执行的不仅仅是推送。
  • 这里发生了其他事情。 EIP 之间的差异 (0041140e - 004113f0) = 1E 字节。太大而不能成为一条指令。你有多个线程在运行吗?
  • @adelphus:你是对的。只需在004113f0 .. 0041140e 之间反汇编,你会发现一个序列(在此处使用 Intel 语法):PUSH EBP; MOV EBP, ESP; PUSH <other reg>; ...; SUB ESP, ...;这是编译器生成的函数入口代码(称为函数序言),它使代码实现通常的C调用约定/ABI。在函数的/return 末尾,您会找到一个相应的块ADD ESP, ...; POP ...; LEAVE; RET,它撤消了这一切。
  • @wangshuaijie: 变化 EBP 不是PUSH EBP 而是遵循 的指令 - 这将是MOV EBP, ESP .这就是为什么EBP 中的新值与func() 入口处ESP 的初始值相差四个字节。如前所述,检查你的反汇编。
  • @all,恩,你们说得有道理,也许在执行push ebp的过程中,编译器在代码序列中插入了一些windbg没有显示的汇编代码,我会尝试更多看看。

标签: assembly x86 windbg stack-pointer


【解决方案1】:

如果您想查看每条指令的执行情况,您需要禁用源代码级调试。打开调试菜单并取消选中“源模式”。当您进入源代码模式时,映射到当前源代码行的所有指令都会在调试器中断之前执行。

【讨论】:

  • 好点,我会试试,也许是windbg优化了代码序列,有些东西被隐藏起来了。
【解决方案2】:

按照史蒂夫约翰逊的指示,我取消选中“源模式”以跟踪程序集,实际上在推送 ebp 之后,有很多(真的很多!)汇编代码,这些代码之前在选中“源模式”时被隐藏了。所以这个案例解决了,但是我发现了其他问题,先看我更完整的代码:

void func();

int main(int argc, char* argv[])
{
    func();
    return 0;
}
void func2();

void func()
{
    func2();
}

在func中,我没有声明局部变量,下面是我在main中执行func()时windbg的输出:

004113c3 50              push    eax
0:000> 
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113c4 esp=0012fe98 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!main+0x24:
004113c4 e8e0fdffff      call    SimpleStack!ILT+420(_func) (004111a9)
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004111a9 esp=0012fe94 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!ILT+420(_func):
004111a9 e942020000      jmp     SimpleStack!func (004113f0)
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113f0 esp=0012fe94 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func:
004113f0 55              push    ebp
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113f1 esp=0012fe90 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func+0x1:
004113f1 8bec            mov     ebp,esp
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113f3 esp=0012fe90 ebp=0012fe90 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func+0x3:
004113f3 81ecc0000000    sub     esp,0C0h
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113f9 esp=0012fdd0 ebp=0012fe90 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func+0x9:
004113f9 53              push    ebx
0:000> t
eax=0000000a ebx=7ffda000 ecx=00000000 edx=00000001 esi=01f1f760 edi=0012ff68
eip=004113fa esp=0012fdcc ebp=0012fe90 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SimpleStack!func+0xa:
004113fa 56              push    esi

确实我们看到push ebp之后是mov ebp, esp, 真正改变了ebp, 但是"mov ebp, esp"后面有一个代码"sub esp,0C0h", 我知道"sub esp, num "是为栈帧上的局部变量分配内存空间,但是我没有在func中声明局部变量,所以我有这样的疑问: “sub esp, 0C0h” 在这里做什么? 盯着函数 prolog,uf 的输出是:

0:000> uf func
SimpleStack!func [d:\code\simplestack\func.c @ 4]:
    4 004113f0 55              push    ebp
    4 004113f1 8bec            mov     ebp,esp
    4 004113f3 81ecc0000000    sub     esp,0C0h
    4 004113f9 53              push    ebx
    4 004113fa 56              push    esi
    4 004113fb 57              push    edi
    4 004113fc 8dbd40ffffff    lea     edi,[ebp-0C0h]
    4 00411402 b930000000      mov     ecx,30h
    4 00411407 b8cccccccc      mov     eax,0CCCCCCCCh
    4 0041140c f3ab            rep stos dword ptr es:[edi]
    5 0041140e e83dfcffff      call    SimpleStack!ILT+75(_func2) (00411050)
    6 00411413 5f              pop     edi
    6 00411414 5e              pop     esi
    6 00411415 5b              pop     ebx
    6 00411416 81c4c0000000    add     esp,0C0h
    6 0041141c 3bec            cmp     ebp,esp
    6 0041141e e818fdffff      call    SimpleStack!ILT+310(__RTC_CheckEsp) (0041113b)
    6 00411423 8be5            mov     esp,ebp
    6 00411425 5d              pop     ebp
    6 00411426 c3

【讨论】:

  • 请贴出整个函数的反汇编。使用uf命令反汇编整个函数。
  • 发布了“uf func”的输出,其中包含一段代码“sub esp, 0c0h”
  • 您看到的是编译器在堆栈上提供了一些空间,以便安全地检测堆栈未对齐错误和缓冲区溢出。如果你在发布模式下编译你的代码,这一切都会消失。
猜你喜欢
  • 2011-01-31
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
相关资源
最近更新 更多