【windbg】调用栈分析

一些心得和说明

  • windbg 还不熟,如有错误,请指正。
  • 调用栈鼓捣了好久,一直没整明白。每次都打算弄明白,结果还是...(泪目啊)。今天,不慌不忙的,把windbg的打印信息都copy出来,再把 ebp、esp、eip都挑出来,看它们的地址变化,然后再,整个表格,看他们是怎么跳转的... 再参考下 对于ESP、EBP寄存器的理解,慢慢的就有头绪了,所以,一定要有耐心,心细梳理。
图 01

创建MFC程序

创建MFC程序,使用以下代码。(参考:对于ESP、EBP寄存器的理解

int FunAdd(int iPara1, int iPara2)
{
	int iAdd = 7;
	int iResult = iPara1 + iPara2 + iAdd;
	return iResult;
}

// button 响应函数
void CMFCApp2015Dlg::OnBnClickedBtnDo()
{
	int iVal1 = 5;
	int iVal2 = 6;
	int iRes = FunAdd(iVal1, iVal2);
}

Windbg调试

  • Step1:Windbg -> Open Executable...,打开待调试的exe。
  • Step2:加入断点。
# 格式:bp module!symbol_name
bp MFCApp2015!FunAdd

bp MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo
  • 点击button,开始调试。下面是调试过程中的寄存器等信息。

0:000> r (查看寄存器信息)

eax=00000000 ebx=00000000 ecx=002afc18 edx=00000000 esi=002aef34 edi=002aef34

eip=00ee5d90 esp=002aef30 ebp=002aefd0 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246

// 命中断点,进入到 OnBnClickedBtnDo

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo:

00ee5d90 55              push    ebp

 

0:000> p(F10单步执行)

eax=cccccccc ebx=00000000 ecx=002afc18 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee5db3 esp=002aee30 ebp=002aef2c iopl=0         nv up ei pl nz na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206

 

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo+0x23:

// 执行 int iVal1 = 5; 5 放入 [ebp-14h] 这个地址中

00ee5db3 c745ec05000000  mov     dword ptr [ebp-14h],5 ss:002b:002aef18=cccccccc

 

0:000> dw ebp-14h(查看地址所对应的内存)

// 当前ebp为 0x002aef2c,ebp - 14h 就是 0x002aef18,该地址的内容就是 5,说明int iVal1 = 5;执行成功
002aef18  0005 0000 cccc cccc cccc cccc fc18 002a

 

0:000> p(F10单步执行)

eax=cccccccc ebx=00000000 ecx=002afc18 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee5dba esp=002aee30 ebp=002aef2c iopl=0         nv up ei pl nz na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo+0x2a:

// 分析同上,iVal1 、iVal2 两个变量地址未连续的原因尚不清楚

00ee5dba c745e006000000  mov     dword ptr [ebp-20h],6 ss:002b:002aef0c=cccccccc

 

0:000> p(F10单步执行)

eax=cccccccc ebx=00000000 ecx=002afc18 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee5dc1 esp=002aee30 ebp=002aef2c iopl=0         nv up ei pl nz na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo+0x31:

// 不知道为什么要move 到 eax 寄存器,等我回家找到转汇编的网址再来分析

00ee5dc1 8b45e0          mov     eax,dword ptr [ebp-20h] ss:002b:002aef0c=00000006

 

0:000> p(F10单步执行)

Breakpoint 0 hit

eax=00000006 ebx=00000000 ecx=00000005 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee7cc0 esp=002aee24 ebp=002aef2c iopl=0         nv up ei pl nz na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206

MFCApp2015!FunAdd:

// 准备进入FunAdd,此时ebp的值是 ebp=002aef2c,push入栈。存 002aef2c 的地址是 ebp=002aee20,下一条指令就能反应出来了。

00ee7cc0 55              push    ebp

 

0:000> p(F10单步执行)

eax=cccccccc ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aee20

eip=00ee7cde esp=002aed3c ebp=002aee20 iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

MFCApp2015!FunAdd+0x1e:

// 执行 int iAdd = 7; 7 放入 [ebp-8] 这个地址中

00ee7cde c745f807000000  mov     dword ptr [ebp-8],7  ss:002b:002aee18=cccccccc

 

0:000> kb(产看调用栈)

 # ChildEBP RetAddr  Args to Child

// 002aee20 FunAdd的ebp, 002aef2c d则是 OnBnClickedBtnDo  的ebp。

// 00ee5dce 是返回地址,后面会讲到。   

// 00000005 00000006 FunAdd 的入参。

00 002aee20 00ee5dce 00000005 00000006 002aef34 MFCApp2015!FunAdd+0x1e

01 002aef2c 0ff859d1 002aeff4 002aeff4 cccccccc CMFCApp2015Dlg::OnBnClickedBtnDo+0x3e

02 002aefd0 0ff85318 002afc18 000003ec 00000000 mfc140ud!_AfxDispatchCmdMsg+0xe1

 

0:000> p(F10单步执行)

eax=cccccccc ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aee20

eip=00ee7ce5 esp=002aed3c ebp=002aee20 iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

MFCApp2015!FunAdd+0x25:

// 我也不知道说啥,大概就是执行个加法

00ee7ce5 8b4508          mov     eax,dword ptr [ebp+8] ss:002b:002aee28=00000005

 

0:000> r(查看寄存器信息)

// eax=00000012 是累加器已经是0x12,即 18 = 5 + 6 + 7。

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aee20

eip=00ee7cf1 esp=002aed3c ebp=002aee20 iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

MFCApp2015!FunAdd+0x31:

00ee7cf1 8b45ec          mov     eax,dword ptr [ebp-14h] ss:002b:002aee0c=00000012

 

0:000> dt iResult(查看iResult的值)

// 0x2aee0c 是iResult的地址,下面的18则是它的值。用 (dw 0x2aee0c )也可以看它的值。 

Local var @ 0x2aee0c Type int

0n18

 

0:000> p(F10单步执行)

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aee20

eip=00ee7cf4 esp=002aed3c ebp=002aee20 iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

// 准备退出 FunAdd

MFCApp2015!FunAdd+0x34:

00ee7cf4 5f              pop     edi

 

0:000> p(F10单步执行)

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee5dce esp=002aee28 ebp=002aef2c iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

// 退回到 OnBnClickedBtnDo了,ebp 的值重回 002aef2c ,eip 指向了 FunAdd 的RetAddr,即 eip=00ee5dce

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo+0x3e:

00ee5dce 83c408          add     esp,8

 

0:000> kb(产看调用栈)

 # ChildEBP RetAddr  Args to Child             

00 002aef2c 0ff859d1 002aeff4 002aeff4 cccccccc MFCApp2015!OnBnClickedBtnDo+0x3e

01 002aefd0 0ff85318 002afc18 000003ec 00000000 mfc140ud!_AfxDispatchCmdMsg+0xe1

 

0:000> p(F10单步执行)

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aef2c

eip=00ee5dd4 esp=002aee30 ebp=002aef2c iopl=0         nv up ei pl nz ac pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200216

MFCApp2015!CMFCApp2015Dlg::OnBnClickedBtnDo+0x44:

00ee5dd4 5f              pop     edi

 

0:000> p(F10单步执行)

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aef34

eip=0ff859d1 esp=002aef34 ebp=002aefd0 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246

// 已退出 OnBnClickedBtnDo

mfc140ud!_AfxDispatchCmdMsg+0xe1:

0ff859d1 3bf4            cmp     esi,esp

 

0:000> r(查看寄存器信息)

eax=00000012 ebx=00000000 ecx=00000000 edx=00000000 esi=002aef34 edi=002aef34

eip=0ff859d1 esp=002aef34 ebp=002aefd0 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246

mfc140ud!_AfxDispatchCmdMsg+0xe1:

0ff859d1 3bf4            cmp     esi,esp

windbg调试のESP EIP EBP

图02

上图是整个调试过程中 eip、esp、ebp的变化过程。

EIP寄存器:存储当前执行指令的内存位置。

EBP寄存器:表明当前栈帧的栈底。

ESP寄存器:表明当前栈帧的栈顶。

从图中可以看出,EBP寄存器,在进入一个栈帧后,就维持不变了,因此一些变量的值也是根据它的偏移量来算的(这句话没有依据,自己总结的,谨慎参考)。

因为指令一直在跑,所以EIP一直在变动。

ESP ? : 指的是栈顶,那就看有没有数据出入栈,但上图中ESP的数据变化还没弄明白

windbg调试の栈布局

图03
图04

 

上图是整个调试过程中的栈布局,其中的几个要点:

  • 用图03去验证图04,能对应上。(第1张图的准确性很高,是我从书上抄过来的。大家可以拿这张图作为参考。)
  • 图04 中的esp*、ebp* 和 图02中的一一对应。

windbg调试の调用栈

图05
图06
  • 关注图05 ChildEBP,可以发现 0x002aee20中保存着0x002aef2c;0x002aef2c则保存着 0x002aefd0。(参照图04)
  • 关注图05 RetAddr,再对比图06,可以发现 RetAddr 所指地址,是代码所在地址,而不是栈所在地址。

windbg调试@变量查看

图07
图08

调试过程中的变量查看,图中有变量的地址与值。可以与图04一一对应上。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值