.windbg-k*实例分析(查看调用栈分析)

[cpp]  view plain  copy
  1. #include "stdafx.h"  
  2.   
  3. int fun0(int i)  
  4. {  
  5.     return i;  
  6. };  
  7.   
  8. int fun1(int i)  
  9. {  
  10.     return fun0(i);  
  11. }  
  12.   
  13. int _tmain(int argc, _TCHAR* argv[])  
  14. {  
  15.   
  16.     fun1(10);  
  17.   
  18.     return 0;  
  19. }  


代码如上

我们在test!fun1下个断点,g运行,断下来后:


我们来观注下蓝色小框的地址是RetAddr,具体指什么,跳转到此处汇编:


很明显了,就是运行函数后的下一条要执行的指令

我们再看看ChildEBP的含义:

现在打印下ebp值:

[cpp]  view plain  copy
  1. 0:000> r ebp  
  2. ebp=002ef930  
很诡异,怎么会是前一个栈的ebp呢,其实我们可以注意到当前汇编是push ebp

而这个函数用到的ebp应该是到mov ebp, esp之后,单步调过此处,再看其值:


也就是ChildEbp就是当前函数需要使用的EBP,不要被ChildEbp的child字面意思搞晕了~~~~


有函数调用的栈中的情况:


上图中上面是低地址,下面是高地址,保存的EBP就是前一个EBP,它的地址就是当前的EBP

在内存中的一系列的值是可以被识别出来的, 这些值表示当前栈中的某个地址, 并且在这些值之后是一个可执行的地址.

[cpp]  view plain  copy
  1. 0:000> lm  
  2. start    end        module name  
  3. 011e0000 011fb000   test     C (private pdb symbols)  E:\test\Debug\test.pdb  
  4. 53ca0000 53dc3000   MSVCR90D   (deferred)               
  5. 756f0000 7573b000   KERNELBASE   (deferred)     

再看下转存的栈


可以看到,蓝色的地址和最右排地址相差无几,极可能是当前栈的某个地址,后面接着红色部分是在test模块内的,所以应该是返回地址

以后为重启了次程序运行结果,我们可以打印出kb看到:

[cpp]  view plain  copy
  1. 0:000> kb  
  2. ChildEBP RetAddr  Args to Child                
  3. 0029fbb4 011f1417 0000000a 0029fd64 00000000 test!fun0+0xb [e:\test\test\test.cpp @ 7]  
  4. 0029fc8c 011f1465 0000000a 00000000 00000000 test!fun1+0x27 [e:\test\test\test.cpp @ 13]  
  5. 0029fd64 011f19f8 00000001 00331b90 00334800 test!wmain+0x25 [e:\test\test\test.cpp @ 19]  
  6. 0029fdb4 011f183f 0029fdc8 75aaed6c 7ffdd000 test!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]  
  7. 0029fdbc 75aaed6c 7ffdd000 0029fe08 7757377b test!wmainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]  
  8. 0029fdc8 7757377b 7ffdd000 764a1a3e 00000000 kernel32!BaseThreadInitThunk+0xe  
  9. 0029fe08 7757374e 011f107d 7ffdd000 00000000 ntdll!__RtlUserThreadStart+0x70  
  10. 0029fe20 00000000 011f107d 7ffdd000 00000000 ntdll!_RtlUserThreadStart+0x1b  

这是fun1用到的ebp,按图就知道,保存的EBP和返回地址是连续的.

后面的test!fun1+0x27其实是通过寻找上一行的返回地址(011f1417)得来的,可以使用ln 011f1417查看.


 特别注意是栈是向低地址增加,dd 后一个EBP的值就是前一个EBP

[cpp]  view plain  copy
  1. 0:000> p  
  2. eax=00000002 ebx=7efde000 ecx=00000001 edx=00000001 esi=00000000 edi=0034fed8  
  3. eip=00c13613 esp=0034fde4 ebp=0034fed8 iopl=0         nv up ei pl nz na po nc  
  4. cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202  
  5. test1!wmain+0x33:  
  6. 00c13613 51              push    ecx  
  7.   
  8. 0:000> r esp  
  9. esp=0034fde0  

push ecx后,esp减了4

另外经常用到的指令如kn 100 

kn用于显示帧号码,100表示层级,一般不会到100,所以就是显示实际的层级

[cpp]  view plain  copy
  1. 0:000> kn 100  
  2.  # ChildEBP RetAddr    
  3. 00 010ff860 776fb2e4 ntdll!LdrpDoDebuggerBreak+0x2b  
  4. 01 010ffa98 776f7744 ntdll!LdrpInitializeProcess+0x14dc  
  5. 02 010ffaec 776f7590 ntdll!_LdrpInitialize+0x178  
  6. 03 010ffaf4 00000000 ntdll!LdrInitializeThunk+0x10  
   

又如:

~*kv 1

让每个线程只显示前一帧

[cpp]  view plain  copy
  1. :000> ~*kv 1  
  2.   
  3. .  0  Id: ae14.5c8 Suspend: 1 Teb: 00ff5000 Unfrozen  
  4.  # ChildEBP RetAddr  Args to Child                
  5. 00 010ff860 776fb2e4 a7e95f8e ffffffff 00000000 ntdll!LdrpDoDebuggerBreak+0x2b (FPO: [Non-Fpo])  
  6.   
  7.    1  Id: ae14.b410 Suspend: 1 Teb: 00ff8000 Unfrozen  
  8.  # ChildEBP RetAddr  Args to Child                
  9. 00 013cf88c 776c6a61 000000b8 013fc388 00000010 ntdll!NtWaitForWorkViaWorkerFactory+0xc (FPO: [5,0,0])  
  10.   
  11.    2  Id: ae14.5ed0 Suspend: 1 Teb: 00ffb000 Unfrozen  
  12.  # ChildEBP RetAddr  Args to Child                
  13. 00 015ef96c 776c6a61 000000b8 013fc638 00000010 ntdll!NtWaitForWorkViaWorkerFactory+0xc (FPO: [5,0,0])  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值