谁能帮我从一个简单的 Hello World 中解释这个 MSVC 调试模式反汇编? [英] Can anyone help me interpret this MSVC debug-mode disassembly from a simple Hello World?

查看:56
本文介绍了谁能帮我从一个简单的 Hello World 中解释这个 MSVC 调试模式反汇编?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我得到了以下简单的 C++ 代码:

I got the following simple C++ code:

#include <stdio.h>
int main(void)
{
    ::printf("\nHello,debugger!\n");
}

从 WinDbg 中,我得到了以下反汇编代码:

And from WinDbg, I got the following disassembly code:

SimpleDemo!main:
01111380 55              push    ebp
01111381 8bec            mov     ebp,esp
01111383 81ecc0000000    sub     esp,0C0h
01111389 53              push    ebx
0111138a 56              push    esi
0111138b 57              push    edi
0111138c 8dbd40ffffff    lea     edi,[ebp-0C0h]
01111392 b930000000      mov     ecx,30h
01111397 b8cccccccc      mov     eax,0CCCCCCCCh
0111139c f3ab            rep stos dword ptr es:[edi]
0111139e 8bf4            mov     esi,esp
011113a0 683c571101      push    offset SimpleDemo!`string' (0111573c)
011113a5 ff15b0821101    call    dword ptr [SimpleDemo!_imp__printf (011182b0)]
011113ab 83c404          add     esp,4
011113ae 3bf4            cmp     esi,esp
011113b0 e877fdffff      call    SimpleDemo!ILT+295(__RTC_CheckEsp) (0111112c)
011113b5 33c0            xor     eax,eax
011113b7 5f              pop     edi
011113b8 5e              pop     esi
011113b9 5b              pop     ebx
011113ba 81c4c0000000    add     esp,0C0h
011113c0 3bec            cmp     ebp,esp
011113c2 e865fdffff      call    SimpleDemo!ILT+295(__RTC_CheckEsp) (0111112c)
011113c7 8be5            mov     esp,ebp
011113c9 5d              pop     ebp
011113ca c3              ret

我很难完全理解它.SimpleDemo!ILT 在这里做什么?

I have some difficulties to fully understand it. What is the SimpleDemo!ILT things doing here?

011113c0 中比较 ebp 和 esp 的指令有何意义?

What's the point of the instruction comparing ebp and esp at 011113c0?

既然我在main()函数中没有任何局部变量,为什么在的位置还有一个sub esp,0C0h01111383?

Since I don't have any local variables in main() function, why there's still a sub esp,0C0h at the loacation of 01111383?

非常感谢.

虽然我仍然不知道 ILT 是什么意思,但 __RTC_CheckESP 用于运行时检查.这些代码可以通过在 main() 函数之前放置以下编译指示来消除.

Though I still don't know what ILT means, but the __RTC_CheckESP is for runtime checks. These code can be elimiated by placing the following pragma before the main() function.

#pragma runtime_checks( "su", off )

参考:

http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx

http://msdn.microsoft.com/en-us/library/6kasb93x.aspx

sub esp,0C0h 指令在堆栈上分配另一个 0C0h 字节的额外空间.然后EAX填充0xCCCCCCCC,这是4个字节,由于ECX=30h,4*30h=0C0h,所以指令rep stos dword ptr es:[edi]填充正是带有 0xCC 的额外空格.但是堆栈上的这个额外空间是做什么用的呢?这是某种安全带吗?我还注意到,如果我像更新 1 所示那样关闭运行时检查,堆栈上仍然有额外的空间,尽管要小得多.并且这个空间没有填充0xCC.

没有运行时检查的汇编代码如下:

The assembly code without runtime check is like below:

SimpleDemo!main:
00231250 55              push    ebp
00231251 8bec            mov     ebp,esp
00231253 83ec40          sub     esp,40h <-- Still extra space allocated from stack, but smaller
00231256 53              push    ebx
00231257 56              push    esi
00231258 57              push    edi
00231259 683c472300      push    offset SimpleDemo!`string' (0023473c)
0023125e ff1538722300    call    dword ptr [SimpleDemo!_imp__printf (00237238)]
00231264 83c404          add     esp,4
00231267 33c0            xor     eax,eax
00231269 5f              pop     edi
0023126a 5e              pop     esi
0023126b 5b              pop     ebx
0023126c 8be5            mov     esp,ebp
0023126e 5d              pop     ebp
0023126f c3              ret

推荐答案

大多数指令是 MSVC 运行时检查的一部分,默认情况下为调试版本启用.在优化的构建中调用 printf 并返回 0 所需的代码很多.(Godbolt 编译器浏览器).其他编译器(如 GCC 和 clang)不会做太多的事情,比如调用后的堆栈指针比较,或者使用可识别的 0xCC 模式毒化堆栈内存以检测未初始化的使用,因此它们的调试版本是就像 MSVC 调试模式一样,没有额外的运行时检查.

Most of the instructions are part of MSVC runtime checking, enabled by default for debug builds. Just calling printf and returning 0 in an optimized build takes much less code. (Godbolt compiler explorer). Other compilers (like GCC and clang) don't do as much stuff like stack-pointer comparison after calls, or poisoning stack memory with a recognizable 0xCC pattern to detect use-uninitialized, so their debug builds are like MSVC debug mode without its extra runtime checks.

我已经对汇编程序进行了注释,希望对您有所帮助.以 'd' 开头的行是调试代码行,以 'r' 开头的行是运行时检查代码行.我还加入了我认为没有运行时检查版本和发布版本的调试的样子.

I've annotated the assembler, hopefully that will help you a bit. Lines starting 'd' are debug code lines, lines starting 'r' are run time check code lines. I've also put in what I think a debug with no runtime checks version and release version would look like.

  ; The ebp register is used to access local variables that are stored on the stack, 
  ; this is known as a stack frame. Before we start doing anything, we need to save 
  ; the stack frame of the calling function so it can be restored when we finish.
  push    ebp                   
  ; These two instructions create our stack frame, in this case, 192 bytes
  ; This space, although not used in this case, is useful for edit-and-continue. If you
  ; break the program and add code which requires a local variable, the space is 
  ; available for it. This is much simpler than trying to relocate stack variables, 
  ; especially if you have pointers to stack variables.
  mov     ebp,esp             
d sub     esp,0C0h              
  ; C/C++ functions shouldn't alter these three registers in 32-bit calling conventions,
  ; so save them. These are stored below our stack frame (the stack moves down in memory)
r push    ebx
r push    esi
r push    edi                   
  ; This puts the address of the stack frame bottom (lowest address) into edi...
d lea     edi,[ebp-0C0h]        
  ; ...and then fill the stack frame with the uninitialised data value (ecx = number of
  ; dwords, eax = value to store)
d mov     ecx,30h
d mov     eax,0CCCCCCCCh     
d rep stos dword ptr es:[edi]   
  ; Stack checking code: the stack pointer is stored in esi
r mov     esi,esp               
  ; This is the first parameter to printf. Parameters are pushed onto the stack 
  ; in reverse order (i.e. last parameter pushed first) before calling the function.
  push    offset SimpleDemo!`string' 
  ; This is the call to printf. Note the call is indirect, the target address is
  ; specified in the memory address SimpleDemo!_imp__printf, which is filled in when
  ; the executable is loaded into RAM.
  call    dword ptr [SimpleDemo!_imp__printf] 
  ; In C/C++, the caller is responsible for removing the parameters. This is because
  ; the caller is the only code that knows how many parameters were put on the stack
  ; (thanks to the '...' parameter type)
  add     esp,4                 
  ; More stack checking code - this sets the zero flag if the stack pointer is pointing
  ; where we expect it to be pointing. 
r cmp     esi,esp               
  ; ILT - Import Lookup Table? This is a statically linked function which throws an
  ; exception/error if the zero flag is cleared (i.e. the stack pointer is pointing
  ; somewhere unexpected)
r call    SimpleDemo!ILT+295(__RTC_CheckEsp)) 
  ; The return value is stored in eax by convention
  xor     eax,eax               
  ; Restore the values we shouldn't have altered
r pop     edi
r pop     esi
r pop     ebx                   
  ; Destroy the stack frame
r add     esp,0C0h              
  ; More stack checking code - this sets the zero flag if the stack pointer is pointing
  ; where we expect it to be pointing. 
r cmp     ebp,esp               
  ; see above
r call    SimpleDemo!ILT+295(__RTC_CheckEsp) 
  ; This is the usual way to destroy the stack frame, but here it's not really necessary
  ; since ebp==esp
  mov     esp,ebp               
  ; Restore the caller's stack frame
  pop     ebp                   
  ; And exit
  ret                           
  

      ; Debug only, no runtime checks  
      push    ebp                   
      mov     ebp,esp             
    d sub     esp,0C0h              
    d lea     edi,[ebp-0C0h]        
    d mov     ecx,30h
    d mov     eax,0CCCCCCCCh     
    d rep stos dword ptr es:[edi]   
      push    offset SimpleDemo!`string' 
      call    dword ptr [SimpleDemo!_imp__printf] 
      add     esp,4                 
      xor     eax,eax               
      mov     esp,ebp               
      pop     ebp                   
      ret                             

      ; Release mode (The optimiser is clever enough to drop the frame pointer setup with no VLAs or other complications)
      push    offset SimpleDemo!`string' 
      call    dword ptr [SimpleDemo!_imp__printf] 
      add     esp,4                 
      xor     eax,eax               
      ret

这篇关于谁能帮我从一个简单的 Hello World 中解释这个 MSVC 调试模式反汇编?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆