——————————————————————————————————————————————————————————————————————————

前一篇指出 tail_recursivef_factorial() 会递归调用自身来计算某个正整数的阶乘。当要计算的目标数值过大,经历多次调用后,

就会耗尽可用的内核栈,引发一次页错误异常,而转移控制到错误处理程序前再次向无效的内存地址压入“陷阱帧”则会让原本可

以处理的异常升级为“double fault”,致使系统崩溃。本篇通过试图计算 685! 来触发“double fault”并进行分析。

将编译好的驱动拷贝到被调试机器上,利用 sc.exe 把它加载至内核空间,源码中(参见上一篇)设置的初始断点被激活从而断入

调试机上的 WinDbg.exe,观察驱动入口点“DriverEntry()”内的局部变量,其中“Number”的值 0x2ad 正是要计算阶乘的数

685:

按下“g”键恢复执行,没多久就让系统崩溃了,这在我们的意料之中,如果没有连接宿主机上的调试器,目标系统就会直接

蓝屏,并且显示“bug check”代码——0000007F:

在 MSDN 网站上搜索该错误码,它对应于“UNEXPECTED_KERNEL_MODE_TRAP”,官方给出的解释如下:

The UNEXPECTED_KERNEL_MODE_TRAP bug check has a value of 0x0000007F.
This bug check indicates that the Intel CPU generated a trap and the kernel failed to catch this trap.

This trap could be a bound trap (a trap the kernel is not permitted to catch) or a double fault
(a fault that occurred while processing an earlier fault, which always results in a system failure).

这种错误是由于 Intel CPU 生成了一个陷阱(trap),而内核未能捕获这个陷阱。
此陷阱可能是一个受困陷阱(内核不允许捕获的陷阱),或一个“double fault”(当处理一个早先的错误时又出现一个错误,
这样就总是会导致系统故障)。

原文描述中的后一种情况(处理错误时又发生另一个错误)就是我们此刻的处境。

UNEXPECTED_KERNEL_MODE_TRAP 有四个参数,你可以从上一张图看到,首个参数值为“0x00000008(陷阱编号)”,

官方对该值的解释为:

0x00000008, or Double Fault, indicates that an exception occurs during a call to the handler for a prior exception.
Typically, the two exceptions are handled serially.
However, there are several exceptions that cannot be handled serially,
and in this situation the processor signals a double fault. There are two common causes of a double fault:

A kernel stack overflow. This overflow occurs when a guard page is hit, and the kernel tries to push a trap frame.
Because there is no stack left, a stack overflow results, causing the double fault.
If you think this overview has occurred, use !thread to determine the stack limits, and then use kb
(Display Stack Backtrace) with a large parameter (for example, kb 100) to display the full stack.

A hardware problem.

“Double Fault”,指明在调用前一个异常处理程序期间,又出现了一个异常。一般而言,两个异常是顺序处理的。
然而,有一些异常无法顺序处理,在这种情况下处理器就会发出一个“double fault”信号。有两种常见情况会导致
“double fault”:

1。一次内核栈溢出。当接触到一个保护页时就会发生此类溢出,然后内核试图向其中压入一个陷阱帧。
因为已经没有剩余栈可用了,导致又一次栈溢出,造成“double fault”。如果你认为发生了这种溢出,利用“!thread”调试器
命令确定栈界限,然后使用“kb”(显示栈回溯)命令,并带着较大的参数(比如 kb 100)来显示完整的栈。

2。硬件问题

遵循原文的指示,我们先检查当前线程的栈界限,然后执行栈回溯看是否真的越界了,如下图所示,内核栈边界在 8bf47000 处,

而发生异常前的最后一次递归调用的帧指针(ChildEBP)为 8bf47008 ,已经快要出界了:

“nt!KiTrap08”是实际的陷阱处理程序,有趣的是前面的陷阱编号(0x00000008)就在这个例程的名字中,这绝不是巧合,

实际上“nt!KiTrap08”就是“double fault”专用的异常处理程序!

传递给它的第三个参数“801d8940”同时也就是 UNEXPECTED_KERNEL_MODE_TRAP 的第二个参数,它是一

个“nt!_NT_TIB”结构的“SubSystemTib”字段值:

其实这个字段中包含的信息对于我们此刻的故障排查而言并不那么重要,只是怕有人好奇它的来龙去脉,才略作说明罢了。

上图中的 nt!KiTrap08 栈帧名称后面给出了一个 TSS(任务状态段)的段选择符为 28。这才是关键的信息,通过它可会回到

事故现场,分析异常发生时的上下文。这个段选择符存储在“nt!_KTSS”结构的首个字段(Backlink)内:

看到这里应该能够稍稍体会出内核中相关数据结构设计的多么用心良苦!

放下我们的多愁善感,利用调试器的“.tss”命令,后接段选择符,即可回到事故现场,如下所示,异常发生时,EIP 指向即将执行

的指令地址为 977bd06f,换言之是该地址处的“前一条”指令(push ecx)导致的异常,为啥这条压栈指令会导致异常呢?

你看“那时”的 esp 已经指向了内核栈的边界点(8bf47000),而压栈指令需要先把 esp 值减去 4 字节,然后再把 ecx 的内容

写入 8BF46FFC 地址处,该地址已经位于边界之外。

还记得前一篇我们计算出每次递归调用都会消耗掉 16 字节的内核栈空间吗?这出错前的最后一次调用中,试图消耗的最后 4 字节

就在边界之外!

低于 8bf47000 的虚拟内存没有分配实际的物理页,而且我们模拟对 8BF46FFC 执行物理地址转译也失败,证实是由于访问到

无效地址引发异常的(CR2 寄存器存储导致页错误的访问地址):

如前所述,页错误发生后,在控制权转移到 nt!KiTrap08 之前,再次向这个无效的地址压入一张“陷阱帧”,导致再度出现错误,

而 nt!KiTrap08 通过传递给它的首个参数(0x0000007f)明白了这是一个“double fault”,所以调用 nt!KiBugCheck,后者

探测调试器是否存在,决定是要绘制蓝屏还是断入调试器。这就是前面那张栈回溯输出的由来!

执行“kv 1000”回溯大范围的栈帧,你可以看到 683 次(栈帧编号 0x2b0 - 5)对 tail_recursivef_factorial() 的调用,在

我们的预测点(685 号栈帧)之前就发生了溢出:

最后介绍一个强大的命令“!analyze -v”,它会自动分析内核崩溃的原因,并给出所有对故障排除有帮助的信息,对于本例而言,

有价值的信息截图如下:

—————————————————————————————————————————————————————
小结:本篇以源码+调试+在线文档等综合手段排除了“double fault”蓝屏故障;编写驱动并上机调试是理解内核设计思想

的最佳途经!
—————————————————————————————————————————————————————

------ 解析因内核栈溢出导致的 “double fault” 蓝屏 ------的更多相关文章

  1. ------ 新春第一炮:阶乘算法性能分析与 double fault 蓝屏故障排查 Part I ------

    -------------------------------------------------------------------------- 春节期间闲来无事想研究下算法,上机测试代码却遇到了 ...

  2. win7下自写驱动导致开机蓝屏调试过程

    之前没有接触过驱动调试.这里上手就要解决一个因为某个自定义驱动导致的系统登陆后蓝屏问题,记录下来.   问题: 从客户那边弄来的一个虚拟机,已知是加了我们的驱动之后才会导致蓝屏. 解决过程:   使用 ...

  3. [转载] Win7KB3146706补丁导致蓝屏0x0000006B的修复方案

    进入winpe,将附件的蓝屏6B修复补丁kb3146706.zip的补丁替换windows/system32下面的ci.dll文件,里面有64和32位系统的,替换了文件就可以进入系统了. 启动进入系统 ...

  4. 【原创】驱动开发中Memory read error导致的蓝屏问题

    最近在看着<windows驱动开发技术详解>这本书,模仿着敲了第七章中的模拟文件读写部分.在Debug过程中,蓝屏了好多次并出现了各种奇葩的问题.在调了快两天之后,问题终于解决了!现在在这 ...

  5. C# 串口导致电脑蓝屏一个可能的原因

    在某些win7电脑上, 如果使用SerialPort对象的Read(byte[] buffer, int offset, int count)方法读取端口数据时, 若端口接受缓存区的数据少于count ...

  6. windows10蓝屏page fault in nonpaged area

    Windows系统最让人头疼的问题就是蓝屏了,总是出现得那么莫名其妙,而且造成原因也是千奇百怪的.所以,对于电脑蓝屏,系统迷也无法一次性讲清楚.前天,我的电脑就经历过这样的蓝屏page fault i ...

  7. Android内核栈溢出与ROP(CVE-2013-2597)

    一.准备 由于内核栈不可执行(NX),栈溢出利用需用到ROP.简单回顾一下ARM ROP. 漏洞演示代码如下,网上随便找了个. char *str="/system/bin/sh" ...

  8. 解析 Linux 内核可装载模块的版本检查机制

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可 ...

  9. JAVA网络爬虫WebCollector深度解析——爬虫内核

    WebCollector爬虫官网:https://github.com/CrawlScript/WebCollector 技术讨论群:250108697 怎样将爬虫内核导入自己的项目? 1.进入爬虫官 ...

随机推荐

  1. Java学习笔记22---内部类之成员内部类的继承问题

    成员内部类可以继承其他的类,也可以被其它类继承,本文主要说明其它类继承成员内部类的问题. 本文要点如下: 1).成员内部类的子类可以是内部类,也可以不是内部类: 2).当成员内部类的子类不是内部类或子 ...

  2. 网站地图怎么做?dedecms网站地图制作方法听语音

    网站地图怎么生成?下面分享织梦dedecms系统网站地图的生成方式,怎么制作网站地图,方法很简单.下面介绍一下网站地图优化方法及制作方法. 工具/原料 一个网站 方法/步骤 第一步 登录网站后台 第二 ...

  3. mysql远程连接缓及缺少*.dll文件解决方案

    1.mysql远程连接缓慢 # For advice on how to change settings please see # http://dev.mysql.com/doc/refman/5. ...

  4. 用程序读取CSV文件的方法

    CSV全称 Comma Separated values,是一种用来存储数据的纯文本文件格式,通常用于电子表格或数据库软件.用Excel或者Numbers都可以导出CSV格式的数据. CSV文件的规则 ...

  5. [ios 开发笔记]:一句话笔记

    1.NSString转int int a=[@"123" intValue]; 同样适用于NSDictionary将NSNumber转为int   2.switch(stateme ...

  6. Codeforces 899 F. Letters Removing (二分、树状数组)

    题目链接:Letters Removing 题意: 给你一个长度为n的字符串,给出m次操作.每次操作给出一个l,r和一个字符c,要求删除字符串l到r之间所有的c. 题解: 看样例可以看出,这题最大的难 ...

  7. 5dfda1332b67817b0f2d7839242021ce'Java数据结构和算法

    1.return 一个空的集合,而不是 null 如果一个程序返回一个没有任何值的集合,请确保一个空集合返回,而不是空元素.这样你就不用去写一大堆 "if else" 判断null ...

  8. linux_rsync定时备份

    在linux系统中,需要注意空格使用,有着整体性原则,并且注意大小写问题 Rsync数据同步工具 开源.快速.多功能.可实现全量和增量的本地或远程 具有本地和远程两台主机之间数据快速同步镜像.远程备份 ...

  9. linkin大话数据结构--Collections类

    操作集合的工具类:Collections Collections 是一个操作 Set.List 和 Map 等集合的工具类.Collections 中提供了大量方法对集合元素进行排序.查询和修改等操作 ...

  10. finally中关闭资源

    对finally中关闭资源是否还要使用try...catch老是感到迷惑,现在存个例子,省的忘了 public StringBuilder readTxtFile(File file){ String ...