了解 Visual C++ 创建的 x86 C 主函数序言

逆向工程 视窗 反编译 x86 C
2021-06-13 10:26:16

我正在 Visual Studio 2010 中调试一个简单的 x86-64 程序,我注意到main函数前导与同一 C 程序的 GNU GCC 编译版本不同。

为了说明我的意思,这里是main函数的 C 代码

int main() {
  int a,b,c;
  a=1;
  b=2;
  c=proc(a,b);
  return c;
}

Visual Studio 2010 反汇编VC++ 版本main函数序言是:

01211410  push        ebp  
01211411  mov         ebp,esp  
01211413  sub         esp,0E4h  
01211419  push        ebx  
0121141A  push        esi  
0121141B  push        edi  
0121141C  lea         edi,[ebp-0E4h]  
01211422  mov         ecx,39h  
01211427  mov         eax,0CCCCCCCCh  
0121142C  rep stos    dword ptr es:[edi]

VC++版本的其余功能代码为:

0081141E  mov         dword ptr [a],1  
00811425  mov         dword ptr [b],2  
0081142C  mov         eax,dword ptr [b]  
0081142F  push        eax  
00811430  mov         ecx,dword ptr [a]  
00811433  push        ecx  
00811434  call        proc (81114Fh)  
00811439  add         esp,8  
0081143C  mov         dword ptr [c],eax  
0081143F  mov         eax,dword ptr [c]  
00811442  pop         edi  
00811443  pop         esi  
00811444  pop         ebx  
00811445  add         esp,0E4h  
0081144B  cmp         ebp,esp  
0081144D  call        @ILT+300(__RTC_CheckEsp) (811131h)  
00811452  mov         esp,ebp  
00811454  pop         ebp  
00811455  ret 

GCC编译版本main函数序言汇编为:

00400502  push   rbp
00400503  mov    rbp,rsp
00400506  sub    rsp,0x10

mainGCC版本的其余功能代码为:

004004e8  mov    DWORD PTR [rbp-0xc],0x1
004004ef  mov    DWORD PTR [rbp-0x8],0x2
004004f6  mov    edx,DWORD PTR [rbp-0x8]
004004f9  mov    eax,DWORD PTR [rbp-0xc]
004004fc  mov    esi,edx
004004fe  mov    edi,eax
00400500  call   0x4004cc <proc>
00400505  mov    DWORD PTR [rbp-0x4],eax
00400508  mov    eax,DWORD PTR [rbp-0x4]
0040050b  leave
0040050c  ret

objdump版本 2.22.90.20120924给出了相同的反汇编

我意识到两个序言的前 3 条指令执行以下操作:

  1. 保存旧的 EBP(稍后需要删除堆栈帧)
  2. 旧堆栈帧的顶部成为新帧的 EBP
  3. 为局部变量保留空间。该函数有 3 个整型局部变量。

问题1: VC++版本中第4条指令的目的是什么?我看到它节省了 EBX,但为什么呢?之后它再也不会使用它了。

对于 VC++ 版本序言的其余指令,我认为它39h使用值初始化双字 0CCCCCCCCh这是有道理的,因为39h * 4h = 0E4h.

问题2:为什么这个空间用值初始化0CCCCCCCCh这个值比00000000h在某些方面更好吗?

问题三:为什么VC++版本0E4h要为3个局部变量分配字节?这个数字是随机的吗?如果不是,它是如何计算的?

问题 4:除了局部变量之外,这个空间还用于其他用途吗?如果是,是为了什么?

2个回答

堆栈上的额外空间用于支持“编辑并继续”功能,可以通过将 /Zl 更改为 /Zi 来消除。保存的 ebx 和堆栈初始化为 0xcc 由/RTC 运行时检查选项完成

SO上有一个类似的问题

顺便说一下,windows 示例显然是一个 32 位二进制文​​件。x64 窗口调用约定使用 RCX、RDX、R8 和 R9 作为前 4 个整数/指针参数(而不是堆栈)。

  1. 不确定,也许它是“标准的”,因此编译器可以理所当然地使用 ebx(无需检查它是否可以)。
  2. 您应该知道,0x0CC 是 int 3,一个调试断点。可能是如果你突然跳转到未初始化的内存,你会得到一个 int 3 而不是 A/V 或类似的异常。不过只是猜测。没有提到的一件事是编译器标志。是调试版本吗?
  3. 不确定
  4. 不确定,尝试关闭堆栈守卫,看看是否仍然获得大的 0x0E4 分配