使用windbg进行简单的debug c++代码

了解eax, esp, ebp的作用(x86上是exx, x64上是rxx)

1. eax -- 数据寄存器,用来存储/读取数据,做累加的操作

2. esp 指针寄存器- 堆栈指针,里面寸的都是指针,需要调用dd来查看内容

3. ebp 指针寄存器 - 基址寄存器,每段函数进入后都会有如下指令集:

00414330  push        ebp  <-- 存当前函数上层函数的基指针
00414331  mov         ebp,esp <-- 存当前的堆栈指针, 传入的参数都都在这个esp里,由于下面还会有push操作,所以把esp存入ebp中,ebp成为当前函数的基指针,非常重要,此时ebp以上,为传入的参数,以下为临时变量的指针
00414333  sub         esp,0D8h  <--开辟一段内存,用于储存临时/局部变量
00414339  push        ebx  <--几个寄存器值先入站
0041433A  push        esi  
0041433B  push        edi  
0041433C  lea         edi,[ebp-0D8h] <--循环
00414342  mov         ecx,36h 
00414347  mov         eax,0CCCCCCCCh <--debug版本将栈上所有的数据置为该值,用于检查当前变量是否初始化


首先调用kn来查看当前call stack和frame


0:000> kn
  *** Stack trace for last set context - .thread/.cxr resets it
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0012fc3c 102afdd2 kernel32!RaiseException+0x52
01 0012fc7c 00414385 msvcr90d!CxxThrowException+0x52
02 0012fd70 004143f0 ConsoleCpp1!Test22+0x55 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 669] <--准备查看此stack上传入的参数以及临时变量
03 0012fe58 004144ee ConsoleCpp1!Test11+0x30 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 674]
04 0012ff68 00415268 ConsoleCpp1!wmain+0xae [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 684]
05 0012ffb8 004150af ConsoleCpp1!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]
06 0012ffc0 7c81776f ConsoleCpp1!wmainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
07 0012fff0 00000000 kernel32!RegisterWaitForInputIdle+0x49


使用.frame /r #来查看frame上的register的情况

0:000> .frame /r 2
02 0012fd70 004143f0 ConsoleCpp1!Test22+0x55 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 669]
eax=0012fbec ebx=7ffd6000 ecx=00000000 edx=00000003 esi=0012fc7c edi=0012fc7c
eip=00414385 esp=0012fc84 ebp=0012fd70iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ConsoleCpp1!Test22+0x55:
00414385 8b45f8          mov     eax,dword ptr [ebp-8] ss:0023:0012fd68=00008de6


查看当前ebp的值,来查找传入的参数args->arg1和args->arg2,还有results(此处我也不是很确定,传说参数是从ebp+8开始是第一个参数,因为ebp+4存的是返回地址,ebp存的是上层ebp的值),此处查询得之两个参数为0x6554, 0x2892, 注意这两个数都是16进制

0:000> dd 0012fd70 + 8
0012fd78  00006554 00002892 0012ff5c 0012fe64
0012fd88  7ffd6000 cccccccc cccccccc cccccccc
0012fd98  cccccccc cccccccc cccccccc cccccccc
0012fda8  cccccccc cccccccc cccccccc cccccccc
0012fdb8  cccccccc cccccccc cccccccc cccccccc
0012fdc8  cccccccc cccccccc cccccccc cccccccc
0012fdd8  cccccccc cccccccc cccccccc cccccccc
0012fde8  cccccccc cccccccc cccccccc cccccccc


接下来寻找当前frame上的局部变量result,此处不知为何多出一个值为cccccccc的临时变量,查到第二个局部变量的时候才找到results的值0x6554 + 0x2892 = 0x8de6

0:000> dd 0012fd70 -4
0012fd6c  cccccccc 0012fe58 004143f0 00006554
0012fd7c  00002892 0012ff5c 0012fe64 7ffd6000
0012fd8c  cccccccc cccccccc cccccccc cccccccc
0012fd9c  cccccccc cccccccc cccccccc cccccccc
0012fdac  cccccccc cccccccc cccccccc cccccccc
0012fdbc  cccccccc cccccccc cccccccc cccccccc
0012fdcc  cccccccc cccccccc cccccccc cccccccc
0012fddc  cccccccc cccccccc cccccccc cccccccc


0:000> dd 0012fd70  - 8 
0012fd68  00008de6 cccccccc 0012fe58 004143f0
0012fd78  00006554 00002892 0012ff5c 0012fe64
0012fd88  7ffd6000 cccccccc cccccccc cccccccc
0012fd98  cccccccc cccccccc cccccccc cccccccc
0012fda8  cccccccc cccccccc cccccccc cccccccc
0012fdb8  cccccccc cccccccc cccccccc cccccccc
0012fdc8  cccccccc cccccccc cccccccc cccccccc
0012fdd8  cccccccc cccccccc cccccccc cccccccc


从此处dump文件的寄存器使用情况可以得知,vs2008编译器的局部变量存储位置就是ebp-8, ebp - 14, ebp-20以此类推,每次用8 byte的数据,这是dbg版本为了在变量前后都加上前后标志用以检查越界,这就可以解释为什么ebp-4是cccccccc了


CrashCPP!Test:
012b1c80 55              push    ebp
012b1c81 8bec            mov     ebp,esp
012b1c83 81ece4000000    sub     esp,0E4h
012b1c89 53              push    ebx
012b1c8a 56              push    esi
012b1c8b 57              push    edi
012b1c8c 8dbd1cffffff    lea     edi,[ebp-0E4h]
012b1c92 b939000000      mov     ecx,39h
012b1c97 b8cccccccc      mov     eax,0CCCCCCCCh
012b1c9c f3ab            rep stos dword ptr es:[edi]
012b1c9e 8b4508          mov     eax,dword ptr [ebp+8]
012b1ca1 8b08            mov     ecx,dword ptr [eax]
012b1ca3 894df8          mov     dword ptr [ebp-8],ecx
012b1ca6 8b4508          mov     eax,dword ptr [ebp+8]
012b1ca9 8b4804          mov     ecx,dword ptr [eax+4]
012b1cac 894dec          mov     dword ptr [ebp-14h],ecx
012b1caf 8b45f8          mov     eax,dword ptr [ebp-8]
012b1cb2 0345ec          add     eax,dword ptr [ebp-14h]
012b1cb5 8945e0          mov     dword ptr [ebp-20h],eax
012b1cb8 837df800        cmp     dword ptr [ebp-8],0
012b1cbc 752b            jne     CrashCPP!Test+0x69 (012b1ce9)
012b1cbe 8bf4            mov     esi,esp
012b1cc0 ff15d4922b01    call    dword ptr [CrashCPP!_imp__getchar (012b92d4)]
012b1cc6 3bf4            cmp     esi,esp
012b1cc8 e88cf4ffff      call    CrashCPP!ILT+340(__RTC_CheckEsp) (012b1159)
012b1ccd 8bf4            mov     esi,esp
012b1ccf 6a00            push    0
012b1cd1 6a00            push    0
012b1cd3 6a00            push    0
012b1cd5 6803000080      push    80000003h
012b1cda ff15fc912b01    call    dword ptr [CrashCPP!_imp__RaiseException (012b91fc)]
012b1ce0 3bf4            cmp     esi,esp



到此,本轮的debug完成,下面是完整的c++代码和汇编代码,另外附上资料原文和连接



/*
 * main
 */

class MyArgs
{
public:
	int arg1;
	int arg2;
	MyArgs()
	{
		srand(time(NULL));
		arg1 = rand();
		arg2 = rand();
	}
};

int Test22(int a, int b)
{
	int result = a+b;
	if(result%2==0)
	{
		throw 1;
	}
	return result;
}

void Test11(MyArgs* arg)
{
	int result = Test22(arg->arg1, arg->arg2);
	printf("Result: %d", result);
}



int _tmain(int argc, _TCHAR* argv[])
{
	getchar();
	MyArgs* arg = new MyArgs();
	Test11(arg);
    return 0;
}

int Test22(int a, int b)
{
00414330  push        ebp  
00414331  mov         ebp,esp 
00414333  sub         esp,0D8h 
00414339  push        ebx  
0041433A  push        esi  
0041433B  push        edi  
0041433C  lea         edi,[ebp-0D8h] 
00414342  mov         ecx,36h 
00414347  mov         eax,0CCCCCCCCh 
0041434C  rep stos    dword ptr es:[edi] 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WinDBG 是个非常强大的调试器,它设计了极其丰富的功能来支持各种调试任务,包括用户 态调试、 内核态调试、 调试转储文件、 远程调试等等。 WinDBG 具有非常大的灵活性和可扩展性, 用来满足各种各样的调试需求,比如用户可以自由定义调试事件的处理方式,编写调试扩展模块 来定制和补充 WinDBG 的调试功能。 尽管 WinDBG 是个典型的窗口程序, 但是它的大多数调试功能还是以手工输入命令的方式来 工作的。目前版本的 WinDBG 共提供了 20 多条标准命令, 140 多条元命令( Meta-commands), 和难以计数的大量扩展命令。学习和灵活使用这些命令是学习 WinDBG 的关键,也是难点。 上一章我们从设计的角度分析了 WinDBG ,本章将从使用(用户)的角度介绍 WinDBG 。我 们先介绍工作空间的概念和用法(第 1 节),然后介绍命令的分类和不同种类的命令提示符(第 2 节)。 第 3 节介绍不同的调试模式, 也就是如何与不同特征的调试目标建立调试会话。 第 4 节介绍 上下文的概念和在调试时应该如何切换和控制上下文。第 5 节介绍调试事件和如何定制调试事件 的处理方式。 从第 6 节到第 9 节我们将分别介绍如何在 WinDBG 中完成典型的调试操作, 比如控 制调试目标(第 6 节)、设置断点(第 7 节)、观察栈(第 8 节)以及如何观察和修改数据(第 9 节)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值