谁能帮我间preT从WinDbg的这个简单的拆卸? [英] Can anyone help me interpret this simple disassembly from WinDbg?
问题描述
我得到了以下简单的C ++ code:
的#include<&stdio.h中GT;
INT主要(无效)
{
::输出(\\ nHello,调试器\\ n!);
}
和从WinDbg中,我得到了以下拆卸code:
SimpleDemo主!
01111380 55推EBP
01111381 8bec MOV EBP,ESP
01111383 81ecc0000000子ESP,0C0H
01111389 53推EBX
0111138a 56推ESI
0111138b 57推EDI
0111138c 8dbd40ffffff LEA EDI,[EBP-0C0H]
01111392 b930000000 MOV ECX,30H
01111397 b8cccccccc MOV EAX,0CCCCCCCCh
0111139c f3ab代表STOS DWORD PTR ES:[EDI]
0111139e 8bf4 MOV ESI,ESP
011113a0 683c571101推偏移SimpleDemo!'串'(0111573c)
011113a5 ff15b0821101通话DWORD PTR [SimpleDemo!_imp__printf(011182b0)
011113ab 83c404加ESP,4
011113ae 3bf4 CMP ESI,ESP
011113b0 e877fdffff叫SimpleDemo!ILT + 295(__ RTC_CheckEsp)(0111112c)
011113b5 33c0 XOR EAX,EAX
011113b7 5F流行EDI
011113b8 5E流行ESI
011113b9 5B流行EBX
011113ba 81c4c0000000加ESP,0C0H
011113c0 3bec CMP EBP,ESP
011113c2 e865fdffff叫SimpleDemo!ILT + 295(__ RTC_CheckEsp)(0111112c)
011113c7 8be5 MOV ESP,EBP
011113c9 5D流行EBP
011113ca C3 RET
我有一些困难,充分了解它。什么是在的 SimpleDemo!ILT 的的东西在这里做什么?
什么是指令的点比较EBP和ESP的的 011113c0 的
由于我没有在main()函数,任何局部变量,为什么还是有 子ESP,0C0H 的是在的loacation 01111383 的
非常感谢。
更新1
虽然我还是不知道是什么 ILT 的表示,但__RTC_CheckESP是运行时检查。这些code可以通过之前放置以下pragma中elimiated的的的main()的功能。
的#pragma runtime_checks(苏,关闭)
参考:
<一个href=\"http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx\">http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx
<一个href=\"http://msdn.microsoft.com/en-us/library/6kasb93x.aspx\">http://msdn.microsoft.com/en-us/library/6kasb93x.aspx
更新2
在的子ESP,0C0H 的指令分配另一个字节0C0H在堆栈上额外的空间。然后EAX充满0xCCCCCCCC,这是4个字节,因为ECX = 30H,4 * 30H = 0C0H,因此指令的代表STOS DWORD PTR ES:[EDI] 的填写完全多余的空格用的0xCC。但是,什么是堆栈这额外的空间?这是某种安全带吗? 的此外,我注意到,如果我关闭运行时检查为更新1所示,仍有上摞这样的额外的空间,但要小得多。这个空间没有充满的0xCC。的
没有运行时检查装配code是象下面这样:
SimpleDemo主!
00231250 55推EBP
00231251 8bec MOV EBP,ESP
00231253 83ec40子ESP,40H&LT; - 从堆栈中分配尽管如此额外的空间,但规模较小
00231256 53推EBX
00231257 56推ESI
00231258 57推EDI
00231259 683c472300推偏移SimpleDemo!'串'(0023473c)
0023125e ff1538722300通话DWORD PTR [SimpleDemo!_imp__printf(00237238)]
00231264 83c404加ESP,4
00231267 33c0 XOR EAX,EAX
00231269 5F流行EDI
0023126a 5E流行ESI
0023126b 5B流行EBX
0023126c 8be5 MOV ESP,EBP
0023126e 5D流行EBP
0023126f C3 RET
我批注的汇编,希望这将帮助你一点。开始'D'线调试code线,启动R的运行时检查code线线路。我也把我的想法没有运行时检查版本和发行版本的调试会是什么样子。
; EBP寄存器被用来访问存储在栈上局部变量,
;这被称为一个堆栈帧。我们开始做任何事情之前,我们需要保存
;所以它可以当我们完成恢复调用函数的栈帧。
推EBP
;这两个指令创建我们堆栈帧,在这种情况下,192字节
;这个空间,虽然在这种情况下不使用的,是可用于编辑和 - 继续。如果你
;中断程序,并添加code这需要一个局部变量,空间
;可它。这是比试图移居堆栈变量简单得多,
;特别是如果你有指针堆栈变量。
MOV EBP,ESP
d子ESP,0C0H
; C / C ++函数不应该在这个版本的配置改变这三个寄存器中,
;所以拯救他们。这些都是我们的存储栈帧以下(堆栈内存下移)
- [R推EBX
- [R推ESI
- [R推EDI
;这使得栈帧底部的地址(最低地址)EDI ...
ðLEA EDI,[EBP-0C0H]
; ...然后填写与未初始化的数据值(ECX =号的堆栈帧
;双字,EAX =值存储)
ðMOV ECX,30H
ðMOV EAX,0CCCCCCCCh
ð代表STOS DWORD PTR ES:[EDI]
;堆栈检查code:堆栈指针存储在ESI
- [R MOV ESI,ESP
;这是给printf的第一个参数。参数被压入堆栈
;按相反的顺序(即最后一个参数推第一)调用之前。
推偏移SimpleDemo!'串'
;这是对printf调用。注意调用是间接的,目标地址是
;在存储器地址SimpleDemo!_imp__printf,其被填充在时指定
;可执行程序加载到RAM。
通话DWORD PTR [SimpleDemo!_imp__printf]
;在C / C ++,调用者负责清除参数。这是因为
;来电者是唯一的code,知道有多少参数放在堆栈上
; (感谢'...'参数类型)
ADD ESP,4
;更多的堆栈检查code - 设置零标志,如果堆栈指针指向
;在这里,我们希望它是指向。
- [R CMP ESI,ESP
; ILT - 导入查找表?这是一个静态链接的功能,它抛出一个
;如果异常/错误零标志被清除(即堆栈指针指向
;意想不到的地方)
- [R致电SimpleDemo!ILT + 295(__ RTC_CheckEsp))
;返回值按照惯例存储在EAX
XOR EAX,EAX
;恢复我们不应该改变了价值观
ř流行EDI
ř流行ESI
ř流行EBX
;销毁堆栈帧
r添加ESP,0C0H
;更多的堆栈检查code - 设置零标志,如果堆栈指针指向
;在这里,我们希望它是指向。
- [R CMP EBP,ESP
;往上看
- [R致电SimpleDemo!ILT + 295(__ RTC_CheckEsp)
;这是摧毁堆栈帧通常的方式,但在这里它是不是真的有必要
;由于EBP == ESP
MOV ESP,EBP
;还原调用者的堆栈帧
流行EBP
;和EXIT
RET
;只有调试,无运行时检查
推EBP
MOV EBP,ESP
d子ESP,0C0H
ðLEA EDI,[EBP-0C0H]
ðMOV ECX,30H
ðMOV EAX,0CCCCCCCCh
ð代表STOS DWORD PTR ES:[EDI]
推偏移SimpleDemo!'串'
通话DWORD PTR [SimpleDemo!_imp__printf]
ADD ESP,4
XOR EAX,EAX
MOV ESP,EBP
流行EBP
RET
;释放模式(我假设优化器是足够聪明砸堆栈帧的时候有没有局部变量)
推偏移SimpleDemo!'串'
通话DWORD PTR [SimpleDemo!_imp__printf]
ADD 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
这篇关于谁能帮我间preT从WinDbg的这个简单的拆卸?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!