Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
有人能帮我解释一下WinDbg的这个简单的反汇编吗? 我得到以下简单的C++代码: #include <stdio.h> int main(void) { ::printf("\nHello,debugger!\n"); }_C++_C_Debugging_Assembly - Fatal编程技术网

有人能帮我解释一下WinDbg的这个简单的反汇编吗? 我得到以下简单的C++代码: #include <stdio.h> int main(void) { ::printf("\nHello,debugger!\n"); }

有人能帮我解释一下WinDbg的这个简单的反汇编吗? 我得到以下简单的C++代码: #include <stdio.h> int main(void) { ::printf("\nHello,debugger!\n"); },c++,c,debugging,assembly,C++,C,Debugging,Assembly,我很难完全理解它。什么是SimpleDemo!ILT这里有什么事吗 在011113c0上比较ebp和esp的说明有什么意义 既然main()函数中没有任何局部变量,为什么在01111383的位置仍然有一个子esp,0C0h 非常感谢 更新1 虽然我仍然不知道ILT的意思,但是RTC检查ESP用于运行时检查。可以通过在main()函数之前放置以下pragma来消除这些代码 #pragma runtime_checks( "su", off ) 参考: 更新2 子esp,0C0h指令在堆栈上分

我很难完全理解它。什么是SimpleDemo!ILT这里有什么事吗

011113c0上比较ebp和esp的说明有什么意义

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

非常感谢

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

#pragma runtime_checks( "su", off )
参考:

更新2 子esp,0C0h指令在堆栈上分配另一个0C0h字节的额外空间。然后EAX用0xCCCC填充,这是4个字节,因为ECX=30h,4*30h=0C0h,所以指令代表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
SimpleDemo!主要内容:
00231250 55推式ebp
00231251 8bec mov ebp,esp

00231253 83ec40子esp,40h这是编译器在使用构建时插入的代码。禁用这些选项,应该会更清晰。这也可能是由VS版本引起的。

第一,代码的main()格式不正确。它不会返回你承诺会返回的int。纠正此缺陷,我们得到:

#include int main(int argc, char *argv[]) { ::printf("\nHello,debugger!\n"); return 0; } #包括 int main(int argc,char*argv[]) { ::printf(“\n调试程序!\n”); 返回0; } 另外,在C++程序中,看到<代码> >包含< /代码>是非常奇怪的。我相信你想要
#包括

在所有情况下,必须在堆栈上为参数和返回值留出空间。main()的返回值需要堆栈空间。调用printf()期间要保存的main()上下文需要堆栈空间。printf()的参数需要堆栈空间。printf()的返回值需要堆栈空间。这就是0c0h字节堆栈帧所做的


发生的第一件事是将传入的bas指针复制到堆栈的顶部。然后将新堆栈指针复制到基指针中。我们将在稍后进行检查,以确保堆栈回到其起始位置(因为您已启用运行时检查)。然后,我们构建(0C0h字节长)堆栈框架,以在调用printf()期间保存上下文和printf()的参数。我们跳到printf()。当我们返回时,我们跳过您在代码中没有签入的返回值(帧上唯一剩下的东西),并确保调用后的堆栈与调用前的堆栈位于相同的位置。我们将上下文从堆栈中弹出。然后检查最后一个堆栈指针是否与前面保存的值匹配。然后我们将基本指针的前一个值从堆栈顶部弹出并返回。

我已经对汇编程序进行了注释,希望这会对您有所帮助。以“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 this build configuration,
  ; 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 (I'm assuming the optimiser is clever enough to drop the stack frame when there's no local variables)
  push    offset SimpleDemo!`string' 
  call    dword ptr [SimpleDemo!_imp__printf] 
  add     esp,4                 
  xor     eax,eax               
  ret                               

对于任何被调用或随后被调用的函数,40字节是最糟糕的堆栈分配。这一点解释得非常详细

堆栈顶部保留的空间是用来做什么的?首先,为任何局部变量创建空间。在本例中,FunctionWith6Params()有两个参数。但是,这两个局部变量只占0x10字节。在堆栈顶部创建的剩余空间是怎么处理的? 在x64平台上,当代码为调用另一个函数准备堆栈时,它不会像x86代码中常见的情况那样使用推送指令将参数放入堆栈中。相反,对于特定函数,堆栈指针通常保持固定。编译器在当前函数调用中查看代码中的所有函数,找到参数最多的函数,然后在堆栈上创建足够的空间来容纳这些参数。在本例中,FunctionWith6Params()调用printf()并向其传递8个参数。由于这是具有最大参数数的被调用函数,编译器将在堆栈上创建8个插槽。堆栈上的前四个插槽将成为任何函数调用6Params()时使用的主空间。
为了记录在案,我怀疑ILT的意思是“增量链接Thunk”


增量链接(以及编辑和继续)的工作方式如下:链接器通过Thunk为每个调用添加一层间接寻址,Thunk在可执行文件的开头分组,并在它们之后添加一个巨大的保留空间。这样,当您重新链接更新的可执行文件时,它可以将任何新的/更改的代码放入保留区域,只修补受影响的Thunk,而不更改其余代码。

谢谢您的回复。但我不同意0C0H空间用于保存上下文和返回值。见我的更新2。返回值通过EAX寄存器返回。自从你提出它后,在C++程序中登记< <代码>返回<代码> >从<代码>(代码)>到->代码>返回0是没有问题的;代码>结尾。@Michael Burr:同意除main()之外的所有函数。我在太多的系统上工作过,如果main()没有显式返回某些内容,加载程序会将随机堆栈返回到调用环境。只是C++11中的一个注释,在
main
中省略return语句与返回0是一样的
  ; 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 this build configuration,
  ; 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 (I'm assuming the optimiser is clever enough to drop the stack frame when there's no local variables)
  push    offset SimpleDemo!`string' 
  call    dword ptr [SimpleDemo!_imp__printf] 
  add     esp,4                 
  xor     eax,eax               
  ret