任何人都可以帮我解释这个简单的反汇编从WinDbg? [英] Can anyone help me interpret this simple disassembly from WinDbg?

查看:104
本文介绍了任何人都可以帮我解释这个简单的反汇编从WinDbg?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

从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)
0 11113b5 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调用SimpleDemo!ILT + 295(__ RTC_CheckEsp)(0111112c)
011113c7 8be5 mov esp,ebp
011113c9 5d pop ebp
011113ca c3 ret

我有一些困难要充分了解它。什么是SimpleDemo!ILT 在这里做的事情?



说明比较ebp和esp的指示是什么因为我在main()函数中没有任何局部变量,为什么还有一个 sub esp,0C0h 01111383



非常感谢。



更新1



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

  #pragma run_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,所以指令 表示dword ptr es:[edi] 填充恰好是0xCC的额外空格。但是这个堆栈的额外空间是多少?这是一种安全带吗?另外,我注意到,如果我关闭运行时检查,如Update 1所示,堆栈上仍然有这么多的空间,尽管要小得多。这个空格没有填写0xCC。



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

  SimpleDemo!main:
00231250 55 push ebp
00231251 8bec mov ebp,esp
00231253 83ec40 sub esp,40h < - 从堆栈中分配更多的空间,但更小的
00231256 53 push ebx
00231257 56 push esi
00231258 57 push edi
00231259 683c472300推偏移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


解决方案

我已经注释了汇编程序,希望这将有助于您。开始'd'的行是调试代码行,行'r'的行是运行时检查代码行。我也把我认为没有运行时检查版本和版本的调试看起来像。

 ; ebp寄存器用于访问存储在堆栈上的局部变量
;这被称为堆栈框架。在我们开始做任何事情之前,我们需要节省
;调用函数的堆栈框架,以便在完成时可以恢复。
push ebp
;这两个指令创建我们的堆栈框架,在这种情况下,192个字节
;这个空间虽然在这种情况下不被使用,但对编辑和继续是有用的。如果你
;打破程序并添加需要局部变量的代码,空格为
;可用这比尝试重新定位堆栈变量
要简单得多特别是如果您有指向堆栈变量的指针。
mov ebp,esp
d sub esp,0C0h
; C / C ++函数不应该在这个构建配置中更改这三个寄存器
;所以拯救他们这些存储在我们的堆栈框架下方(堆栈在内存中向下移动)
r push ebx
r push esi
r push edi
;这将堆栈框架的底部(最低地址)放入到
d lea edi,[ebp-0C0h]
; ...然后用未初始化的数据值填充堆栈框架(ecx =
的数量; dwords,eax =要存储的值)
d mov ecx,30h
d mov eax,0CCCCCCCCh
d rep stos dword ptr es:[edi]
;堆栈检查代码:堆栈指针存储在esi
r mov esi,esp
;这是printf的第一个参数。参数被推到堆栈
;在调用该功能之前,以相反的顺序(即最后一个参数被首先推出)。
push offset SimpleDemo!`string'
;这是对printf的调用。注意呼叫是间接的,目标地址是
;指定在内存地址SimpleDemo!_imp__printf中,当
时填写;可执行文件被加载到RAM中。
调用dword ptr [SimpleDemo!_imp__printf]
;在C / C ++中,调用者负责删除参数。这是因为
;调用者是唯一的代码,它知道有多少个参数放在栈
上; (感谢'...'参数类型)
add esp,4
;更多堆栈检查代码 - 如果堆栈指针指向
,则设置零标志;我们期望它指向。
r cmp esi,esp
; ILT - 导入查找表?这是一个静态链接的函数,它抛出
;如果零标志被清除(即堆栈指针指向
;某处意外),则异常/错误
r调用SimpleDemo!ILT + 295(__ RTC_CheckEsp))
;返回值通过约定存储在eax中
xor eax,eax
;恢复我们不应该改变的值
r pop edi
r pop esi
r pop ebx
;销毁堆栈框架
r add esp,0C0h
;更多堆栈检查代码 - 如果堆栈指针指向
,则设置零标志;我们期望它指向。
r cmp ebp,esp
;见上面
r调用SimpleDemo!ILT + 295(__ RTC_CheckEsp)
;这是破坏堆栈框架的通常方法,但这里并不是真的必要的
;因为ebp == esp
mov esp,ebp
;恢复调用者的堆栈帧
pop ebp
;并退出
ret


;调试只,没有运行时检查
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


;释放模式(我假设优化器是足够聪明的,当没有局部变量时放弃堆栈框架)
推偏移SimpleDemo!`string'
调用dword ptr [SimpleDemo!_imp__printf]
添加esp,4
xor eax,eax
ret


I got the following simple C++ code:

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

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

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

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

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

Many thanks.

Update 1

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 )

Reference:

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

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

Update 2

The sub esp,0C0h instruction allocate another 0C0h bytes extra space on the stack. Then EAX is filled with 0xCCCCCCCC, this is 4 bytes, since ECX=30h, 4*30h=0C0h, so the instruction rep stos dword ptr es:[edi] fill exactly the extra spaces with 0xCC. But what is this extra space on stack for? Is this some kind of safe belt? Also I notice that if I turn off the runtime check as Update 1 shows, there's still such extra space on stack, though much smaller. And this space is not filled with 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

解决方案

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 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                               

这篇关于任何人都可以帮我解释这个简单的反汇编从WinDbg?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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