gpt4 book ai didi

c++ - 谁能帮我从一个简单的 Hello World 中解释这个 MSVC Debug模式反汇编?

转载 作者:可可西里 更新时间:2023-11-01 14:52:18 25 4
gpt4 key购买 nike

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

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

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

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 在这里做什么?

011113c0 比较 ebp 和 esp 的指令有什么意义?

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

非常感谢。

更新1

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

#pragma runtime_checks( "su", off )

引用:

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

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

更新2

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

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

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 compiler explorer)。其他编译器(如 GCC 和 clang)不会做太多的事情,比如调用后的堆栈指针比较,或者使用可识别的 0xCC 模式来检测未初始化的堆栈内存,因此它们的调试版本是就像没有额外运行时检查的 MSVC Debug模式。

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

  ; 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

关于c++ - 谁能帮我从一个简单的 Hello World 中解释这个 MSVC Debug模式反汇编?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4024492/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com