一些心得和说明
- windbg 还不熟,如有错误,请指正。
- 调用栈鼓捣了好久,一直没整明白。每次都打算弄明白,结果还是...(泪目啊)。今天,不慌不忙的,把windbg的打印信息都copy出来,再把 ebp、esp、eip都挑出来,看它们的地址变化,然后再,整个表格,看他们是怎么跳转的... 再参考下 对于ESP、EBP寄存器的理解,慢慢的就有头绪了,所以,一定要有耐心,心细梳理。
创建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
上图是整个调试过程中 eip、esp、ebp的变化过程。
EIP寄存器:存储当前执行指令的内存位置。
EBP寄存器:表明当前栈帧的栈底。
ESP寄存器:表明当前栈帧的栈顶。
从图中可以看出,EBP寄存器,在进入一个栈帧后,就维持不变了,因此一些变量的值也是根据它的偏移量来算的(这句话没有依据,自己总结的,谨慎参考)。
因为指令一直在跑,所以EIP一直在变动。
ESP ? : 指的是栈顶,那就看有没有数据出入栈,但上图中ESP的数据变化还没弄明白。
windbg调试の栈布局
上图是整个调试过程中的栈布局,其中的几个要点:
- 用图03去验证图04,能对应上。(第1张图的准确性很高,是我从书上抄过来的。大家可以拿这张图作为参考。)
- 图04 中的esp*、ebp* 和 图02中的一一对应。
windbg调试の调用栈
- 关注图05 ChildEBP,可以发现 0x002aee20中保存着0x002aef2c;0x002aef2c则保存着 0x002aefd0。(参照图04)
- 关注图05 RetAddr,再对比图06,可以发现 RetAddr 所指地址,是代码所在地址,而不是栈所在地址。
windbg调试@变量查看
调试过程中的变量查看,图中有变量的地址与值。可以与图04一一对应上。