SD2.0 大会分享(二)

 

张银奎

简介上写的是“资深 Windows 系统内核和调试技术专家,英特尔亚太研发中心高级工程师,国内第一部软件调试技术专著,畅销书《软件调试》的作者”,有鉴于 Session 的主讲人的来头都不小,所以这个简介大概并不能提高先前不认识他、或没看过他写的书的人对于其 Session 的期望值。不过在听过他的 Session 之后,回味之余不能不竖起大拇指称赞“真高人也”。值得一提的是,在后半段实例演示的时候,讲到精彩之处,全场自发为之鼓掌,这是其他所有 Session 主讲人都不曾有过的待遇,足见其所讲内容之精彩。只是可惜其讲述内容大多以实际演示为主,且涉及很多 Windows 相关内容,我记录速度跟不上他的演示过程,只能尽力回忆希望能大概重现所讲述内容和方法给大家分享。

 

这个 Session 的名称为《调试应用程序的崩溃和挂死》,分别就 Winodws 下程序崩溃和挂死两方面进行了讲解以及对如何利用 windbg 进行调试作了精彩的演示。但调试技巧并非凭空而来,需要对系统有着深入的了解和丰富的经验,遵循一定的法则和规律才能事半功倍。例如对于进程 / 系统的崩溃,我们至少要了解其发生的一般原因,即 OS 检测到违反系统规则的行为或违反系统安全的情况(进程倍 OS 杀掉),抑或是内核代码有问题 OS 主动自杀( Windows 蓝屏)。其次就是要善于使用调试工具,俗话说工欲善其事必先利其器嘛,调试程序的话一个好用的调试器是必不可少的,张先生用的是 windows 平台的即时调试工具 windbg ,其他平台大家可以自己找到适合的工具就好。

对于调试动作本身而言,大体可以分为用户态调试和内核态调试。用户态调试相对比较简单,因为只需要面对自己的那一个进程。而内核态调试需要面对的对象是系统中所有进程,要想精准定位问题需要了解都有哪些内核服务程序、他们之间的关系以及自己的程序与这些服务进程之间会产生怎样的关联,调试起来相对要复杂的多,对于调试工具的使用及掌握程度也有相当程度的要求。

1. 用户态调试

这种调试我们作为程序员应该视之为空气和水一样的存在了,具体操作规则也很简单,就是利用调试器找到程序发生异常的地点,查看调用栈的内容,具体程序集体分析即可。张先生举了一个栈溢出 [ ] 的例子,具体过程就不再赘述了,主要是在程序启动之前向系统注册调试器 windbg ,当发生异常时系统自动调用调试器并打出当时的程序调用栈,找出异常的地址和产生地点从而解决问题。

2. 内核态调试

由于应用程序一旦进入内核态之后其行为就不再像用户态那样可控,甚至有时候是不可观察的( Win7 的内核 Session 就是不可见的),一旦用户程序由于某种原因粘在内核态出不来,所谓应用程序的挂死大多数都是这个原因。在 Windows 平台上的表现就是,一个窗口突然在某个动作之后就失去响应,怎么点都白搭,甚至有时候用任务管理器都杀不掉(例如驱动程序不返回)。由于内核调试既是重点又是难点,所以张先生在这里给出了两个精彩的实例演示。

(1) 起机就挂的 Win7 Win7 溢出漏洞)

首先用 VirtualBox 启动一个虚拟的 Win7 ,发现启动之后立刻就死掉,必须重起,但重起之后仍然是老样子。这时候只有一个办法,对,就是上调试器! windbg 开启内核调试模式,进行一些配置注册进系统。在系统出问题的时候调试器被激活,这时可以看到程序陷入内核的地点,在陷入点分析程序进入了哪个地址、哪个模块,最后分析出是调用了一个自定义的电源驱动,该驱动在内核态势中不应答(后来看源码就是一个 while(true); )而造成了应用程序的挂死。

(2) 解除 Word 的挂死状态

我们都遇到过这样一种情况,就是用 word 写东西的时候想要插入一个图片或者其他资源文件之后突然发现 word 死在那里了,怎么点都不动,又没保存不舍得强行关闭,但是最终一般都得接受失掉部分资料的痛苦经历。这个解决 Word 挂死在内核态的演示引起了大家的高度兴趣,堪称本 Session 中最精彩的部分。张先生首先打开一个 Word 文档,然后把一个事先准备好的有问题的资源文件拖拽到 Word 中,然后 Word 就不响应了。这种情况自然还是上调试器,找陷入地址,分析 API 和部分汇编代码,找到进程僵死的调用栈。分析后(需要很多经验且非常熟悉 Windows 内核才能这么分析)发现是 Word 调用 OLE 僵死了,原因是 OLE 的老 API 会发广播消息到所有系统注册的顶层窗口,等待所有窗口的应答之后才能进行下一步处理。而偏偏有一个注册了顶层窗口但又不显示任何窗体的进程(事先准备好的)对于这种广播的处理函数中没有返回应答的代码,导致 Word 一直僵死在等待应答的状态。我上面这段描述漏过了对于 WinAPI 、地址、模块、汇编代码的分析,虽然听得时候觉得很酣畅淋漓,但是要达到他那种分析的境界丰富的实践经验和内核熟悉程度二者缺一不可,像我这种 Windows 编程一窍不通的只能高山仰止了。不过好在可以学习一下分析问题和解决问题的方法,工具的使用技巧和分析结果反倒不那么重要。

小结一下本 Session 涉及的软件调试的经验:

1 )栈回溯。找到程序的调用栈就离解决问题靠近了一半。

2 )一个趁手的调试器, Windows 平台必备 windbg ,其它平台肯定也有类似的替代品,就看各人喜好了

3 )了解应用程序挂死通常都是陷入内核态出不来造成的

4 )多去了解内核,多思考多分析

注:栈溢出,在写地址的时候覆盖了栈中返回值的地址,导致函数返回时跑到错误的地点引发的错误。主要识别方法:观察栈的长度,观察是否有异常的函数返回地址(该地址不属于任何模块)

你可能感兴趣的:(SD2.0 大会分享(二))