12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 567|回复: 0

学会使用windbg定位程序bug

[复制链接]

315

主题

315

帖子

640

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
640
发表于 2019-8-20 10:27:42 | 显示全部楼层 |阅读模式
0x1工作环境
系统:win7 32bit sp1
Windbg: 6.12.0002.633 x86
测试程序:通过com接口获得系统计划任务

0x2被调试程序说明
被调试程序是一个通过com接口获取windows的计划任务列表的程序。
崩溃时的提示信息


0x3启动winDbg开始调试
个人喜欢使用bat脚本文件来打开windbg调试程序。
我的脚本文件内容如下
start """C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe""C:\Users\ky1-2\Desktop\MyTest\Bin\Debug\GetTaskScheduler.exe"
C:\Program Files\Debugging Tools forWindows (x86)\windbg.exe 是windbg的目录
C:\Users\ky1-2\Desktop\MyTest\Bin\Debug\GetTaskScheduler.exe是被调试的程序。
双击bat脚本,启动windbg调试GetTaskScheduler.exe

F5运行。运行到崩溃。

错误信息提示为code c0000005地址访问失败。这是第一次抛出的异常。
此时该此异常还未任何的处理。改异常出现的位置在73700cc3处。
该指令是将一个内容赋值给73700c98。也就是说,在执行73700cc3指令时,向73700c98地址写入数据时失败了。
接下来使用!address 命令查看该地址所在内存页的属性信息

Allocation Base:  动态分配内存单元的始地址。
Base Address:  是基地址,例如,装入一个模块,从某地址开始存放,而这个始地址就是BaseAddress。
Allocation Base 和Base Address的区别主要在于前者用于模块,可以看成 程序块始地址;后者用于数据,可以看成数据块始地址。
Protect: 内存页的属性信息。PAGE_EXECUTE_READ是可读可执行。
通过上面的信息可以看到73700c98的属性是可执行、可读。没有写入的属性。
这时就要通过堆栈查看调用信息,找到最近的一次非系统模块的调用。
这样做的原因是,系统模块崩溃的可能性比较小(除非系统模块存在漏洞),而且如果系统模块崩溃大多数原因在于用户调用时出现了问题。
所以这里选择查看最近的一次非系统模块的调用函数的信息。
补充:与内存相关的指令
.dvalloc申请虚拟内存
.dvfree释放虚拟内存
.writemem写内存到文件
.readmem读文件到内存
!address 内存信息查看
!vprot   看虚拟内存保护属性
接下来,通过kn命令查看调用堆栈信息

先来介绍一下kn命令显示信息的含义。
kn命令显示四列:
#           :代表调用堆栈中函数的编号;
ChildEBP:代表当前函数的栈底指针,这里的Child意思是相对上一个调用函数而言的;
RetAddr :代表该函数返回到父函数时的地址;
第四列   :当前函数下一次要执行的地址对应的模块信息。
例如上图第6行。
03表示在调用栈的编号是03;
001df2a4 是编号为03的函数的栈底即EnumTaskFolder函数的EBP指针(之前写错了)
013f481e 是编号为04的函数的地址即编号为03的函数执行完后返回到父函数的013f481e地址;
GetTaskScheduler!EnumTaskFolder+0x4d7意思是当前函数下一步要执行的指令地址013f4b07(就是上一行中的RetAddr的地址013f4b07)位于GetTaskScheduler模块的EnumTaskFolder函数偏移0x4d7个字节处。
好了,了解了这些,下面开始分析调用栈。
通过上图的调用堆栈定位到最近的一次非系统模块是GetTaskScheduler!FillTaskSchedulerInfoHigh函数。
FillTaskSchedulerInfoHigh函数在0x013f41a4地址指令的上一条指令调用了IComHandlerAction的虚函数。
所以下一步就要进入 FillTaskSchedulerInfoHigh函数查看0x013f41a4之前的反汇编代码以及FillTaskSchedulerInfoHigh函数里用到的变量信息。
补充:
kP命令可以在调用堆栈中显示函数的参数传递情况。且参数自动换行对齐。如下图:

使用.frame 2命令切换到FillTaskSchedulerInfoHigh函数。
使用ub 013f41a4查看调用taskschd!ATL::CComObject::`scalar deleting destructor'函数的代码信息
使用dv /i /V命令,查看当前函数用到的变量信息。

通过反汇编代码可以看出在Call ecx指令之前访问了ebp-0x50处的地址,而ebp-0x50正式是pExecAction的地址。
所以Call ecx指令与pExecAction有莫大关系。
接下来分析pExecAction这个局部变量。
使用dt 命令查看变量详细信息

pExecAction的类型是IExecAction*指针。该对象只有一个虚函数表。下面来分析该对象的虚函数表

013f419f 8b4a38          mov     ecx,dword ptr [edx+38h]
[edx+38h]是0x736f2b30。即虚函数表的第15(38h/4h+1)个函数。
现在虚拟表的第15个函数是IComHandlerAction类的成员函数。
而IExecAction的第15个虚函数是什么?
还有IExecAction与IComHandlerAction到底有什么关系哪?
补充1:

如果dt命令使用不了怎么判断pExecAction是一个类对象哪?
013f4196 8b4db0   mov     ecx,dword ptr[ebp-50h] 把pExecAction赋值给ecx
013f4199 8b11      mov     edx,dword ptr[ecx] 把pExecAction指针的内容赋值给edx
....//没有对edx的赋值操作
013f419f8b4a38     mov     ecx,dword ptr [edx+38h]通过edx+38h取址,可以推测pExecAction指向的地址可能是一个类对象或结构体对象。


Msdn一下:





通过msdn可知 IComHandlerAction和IExecAction都是IAction的子类
通过虚拟表可知第15个函数是put_Data后面的函数,所以推断出IExecAction的第15个虚函数是get_WorkingDirectory即IExecAction的第5个函数.
补充:虚函数表的继承关系

1.   一般继承(无虚函数覆盖)我们可以看到下面几点:
1)  虚函数按照其声明顺序放于表中。
2)  父类的虚函数在子类的虚函数前面。
2.   一般继承(有虚函数覆盖)
覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

我们从表中可以看到下面几点,

1)  覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)  没有被覆盖的函数依旧。
3.   多重继承(无虚函数覆盖)下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。

对于子类实例中的虚函数表,是下面这个样子:

我们可以看到:

1)  每个父类都有自己的虚表。
2)  子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
4.   多重继承(有虚函数覆盖)下面我们再来看看,如果发生虚函数覆盖的情况。下图中,我们在子类中覆盖了父类的f()函数。
  
下面是对于子类实例中的虚函数表的图:


再进一步分析虚拟表的函数:

通过红线标注的位置尤其是put_id, put_ClassId, put_Data几个函数基本可以断定,ebp-0x50处的地址的指针应该是IComHandlerAction类型的指针而不是IExecAction类型的指针。

现在找到了问题的真相,下面就要确定问题的原因了。猜测问题是程序将ebp-0x50处的指针强制转成了IExecAction指针并访问了IExecAction的成员函数get_WorkingDirectory
接下来要验证我们的想法。分析到这里了应该可以看源码了。
0x4源码分析

可以查出Line192pExecAction强制转化成了父指针,且没有判断pExecAction的类型是不是IExecAction.所以,当pActions->get_Item()获得到的IAction不是IExecAction时就会出问题或者崩溃了。

改进方法







来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-5-6 13:30 , Processed in 0.140625 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表