在崩溃转储中查找所有可能的上下文记录

如果您调试了一段时间的崩溃转储,那么您可能遇到了这样的情况:调试器提供的初始转储上下文对应于在处理初始异常时发生的第二个异常,该异常可能更接近您正在调查的问题中的原始基础问题。
这可能很烦人,因为“.ecxr”命令将指向次要故障异常的位置,而不是原始异常上下文本身。然而,在大多数情况下,原始的、主要的异常上下文仍然在堆栈上;人们只需要知道如何找到它。

有两种方法可以解决这个问题:

  1. 对于硬件生成的异常(access violations),可以查找堆栈上的ntdll!KiUserExceptionDispatcher,它以PCONTEXT和PEXCEPTION_RECORD作为参数。
  2. 对于软件生成的异常(如C++异常),情况会变得更糟。你可以找在堆栈上调用ntdll!RtlDispatchException,然后从那里获取PCONTEXT参数。

如果堆栈展开失败,或者您正在处理其中一个转储,其中多个线程同时出现异常,这可能会有点乏味,这通常是由于崩溃转储写入失控。如果调试器能稍微自动化一下这个过程就好了。
幸运的是,用一点蛮力的方法来做到这一点其实并不难。具体地说,只是一个普通的“哑”内存扫描,以查找大多数上下文记录所共有的内容。这并不完全是一种巧妙的方法,但通常比手动在堆栈中低头要快得多,尤其是在涉及多个线程或多个嵌套异常的情况下。虽然可能会有误报,但通常很明显的一点是,涉及到一个活动异常有什么意义。然而,有时,快速而肮脏的暴力类型解决方案最终真的做到了这一点。

但是,为了基于内存搜索查找上下文记录,我们需要一些公共数据点,这些数据点通常对于所有上下文结构都是相同的,并且最好是连续的(为了便于使用“s”命令,调试器的内存搜索支持)。幸运的是,它以上下文结构的段寄存器的形式存在:

0:000> dt ntdll!_CONTEXT
+0x000 ContextFlags : Uint4B
[…]

+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B

[…]

现在,事实证明,对于给定进程中的所有线程,几乎总是具有相同的段选择器值,不包括异常的和非常不寻常的情况,如VDM进程。(x64上的段选择器值也是如此。)四个非零的32位值(实际上,零填充到32位的16位值)足以在不被误报的情况下合理地完成搜索。下面介绍如何使用臭名昭著的WinDbg调试器脚本(也适用于其他启用DbgEng的程序,如kd):

.foreach ( CxrPtr { s -[w1]d 0 l?ffffffff @gs @fs @es @ds } ) { .cxr CxrPtr – 8c }

这是一个有点冗长的命令,所以让我们把它分解成各个组件。首先,我们有一个“.foreach”构造,根据调试器文档,它遵循以下约定:

.foreach [Options] ( Variable { InCommands } ) { OutCommands }

foreach命令(实际上是多个版本调试器脚本命令之一,一旦人们习惯使用它)基本上接受由输入命令(in commands)生成的一系列输入字符串,并调用一个命令来处理该输出(OutCommands),输入命令的结果作为变量参数。它很难看,并且基于文本解析进行操作(除其他外,还支持跳过每个X输入;请参阅调试器文档),但它完成了任务。

此操作的下一部分是s命令,它指示调试器在目标内存中搜索模式。这里提供的参数指示调试器只搜索可写内存(w),只输出每个匹配项(1)的地址,在地址空间(0L?)的较低4GB中扫描DWORD(4字节)大小的量化项(d)?在本例中,我们假设目标是一个32位进程(它可能托管在Wow64上,因此使用的是4GB而不是3GB)。命令的其余部分指定要查找的搜索模式;当前线程的段寄存器值。“s”命令有很多其他选项(不幸的是,有一个相当笨拙和复杂的语法);调试器文档运行在其他功能的范围内。

这个命令字符串的最后一部分是output命令,它简单地指示调试器将当前上下文设置为输入命令输出替换宏的值,偏移量为0x8c。(如果调用,0x8c是从结构_CONTEXT的开始到SegGs成员的偏移量,这是我们搜索的第一个值;因此“s”命令返回的地址将是SegGs成员的地址。)请记住,我们将“s”命令的输出限制为仅作为地址本身,这样我们就可以轻松地将该地址传递给不同的命令(这可能会让人认为“s”和“.foreach”命令是一起工作的)。

将命令字符串放在一起,它指示调试器在连续内存中搜索由四个32位值(当前线程的gs、fs、es和ds段选择器值)组成的序列,并显示每个匹配项的包含CONTEXT结构。

在执行这个comand时,除了异常相关的上下文记录之外,您还可以找到其他一些上下文记录(特别是,初始线程上下文是常见的),但是与错误相关的上下文记录通常是非常明显和不言而喻的。当然,这个方法并不是万无一失的,但是它让调试器为您做了一些艰苦的工作(这比在多个线程中手动地在损坏的堆栈中卑躬屈膝只为了提取一些上下文记录要好得多)。

当然,“.foreach”和“s”命令还有许多其他用途;不要害怕尝试使用它们。还有其他的助手可以自动执行某些任务(!for_each_frame,!for_each_local, !for_each_module, !for_each_process,!for_each_thread)。调试器脚本支持可能不是看起来最漂亮的,但是它可以非常方便地加速常见的、重复的任务。

一个带有“.foreach”的分隔提示(实际上是两个分隔提示):变量替换宏只有在用空格将其与其他符号分隔时才起作用。但是,在某些情况下,这可能是一个问题(在这种情况下,您需要对生成的展开宏执行某些运算,例如在这种情况下减去0x8c),因为宏符号展开时仍保留空格。有些命令,比如“dt”,不遵循标准的表达式解析规则(这让我非常恼火),如果它们用空格给出了参数,就会窒息。

但是,这些命令不会丢失所有内容;解决此问题的一种方法是将宏替换存储到伪寄存器中(例如,“[email protected]$t0=ReplacementVariableMacro–0x8c”),并在实际输出命令中使用该伪寄存器,因为您可以在“输出命令”部分发出多个分号分隔的命令。

原文地址:https://www.cnblogs.com/yilang/p/12001140.html

时间: 2024-08-05 08:47:04

在崩溃转储中查找所有可能的上下文记录的相关文章

超大文件中查找关键字

一个有10亿条记录的文本文件,已按照关键字排好序存储.请设计算法,可以快速的从文件中查找指字关键字的记录. 因为已经排好序了,可以使用 二分查找方法检索. 又因为文件过于庞大,可以将文件File分成1000份子记录集即读入10000次,每次读入10亿/10000条记录: 每次 读入一次 10亿/10000 条记录时,记作,将其关键字保存于Records数组中 , 当读入一个子记录集时,说明关键字key>=Records[0],故只需将关键字key与Records[i]最后一条记录关键字key'比

通过崩溃trace来查找问题原因

从友盟中, 我们可能会得到如下信息: Application received signal SIGSEGV (null) ( 0 CoreFoundation 0x359348a7 __exceptionPreprocess + 186 1 libobjc.A.dylib 0x37cdb259 objc_exception_throw + 32 2 CoreFoundation 0x35934789 +[NSException raise:format:] + 0 3 CoreFoundati

Visual Studio 2013 无法使用:在文件中查找(Ctrl+Shift+F)

最近遇到一个问题就是在win7(32位.64位都试过)上安装Visual Studio 2013(专业版.旗舰版都试过)之后,打开项目,使用:在文件中查找(Ctrl+Shift+F)功能时,会突然奔溃. 后来,突然想到在安装的时候,右键,以管理员身份安装. 于是,尝试了一下,搞定!

从list中查找子list

member函数用法很奇怪,用一个元素到list中查找,如果找到,则将找到的元素和之后所有元素变成一个新的list返回,如果找不到则返回nil. (set 'aList '(a b c d e f g h)) → (a b c d e f g h) (member 'd aList) → (d e f g h) (member 55 aList) → nil

杨氏矩阵中查找元素

在杨氏矩阵中查找一个元素是否存在 杨氏矩阵即每一行均以递增顺序排列,每列从上到下也为递增顺序 方法一:数组 #include<stdio.h> #include<stdlib.h> #define COLS 3 #define ROWS 3 //要查找只要在找到右上角的元素和输入元素进行比较.如果右上角元素大,即可排除其他行,若小 //,则可排除本行,继续循环,用输入元素和右上角的元素进行比较 int find(int arr[][COLS], int rows, int cols

[转]grep 在文本中查找内容

转自: http://www.lampweb.org/linux/3/27.html 功能:grep系列是Linux中使用频率最高的文本查找命令.主要功能在一个或者多个文件中查找特定模式的字符串.如果该行有匹配的字符串,则输出整个行的内容.如果没有匹配的内容,则不输出任何内容.grep命令不改动源文件.Linux的grep家族包括grep.egrep.fgrep.rgrep.grep可以通过-G.-E.-F命令行选项来使用egrep和fgrep的功能. 语法:grep [选项] PATTERN

在雇员表中查找第二高的工资SQL语句助记

        "在雇员表中查找第二高的工资的员工记录"SQL语句怎么写         这个查询首先查找最高工资,然后将它从列表中排除,再查找最高工资.很明显,第二次返回的是第二高工资. select top 1 * from employee where salary not int (select max(salary) from emplyee) order by salary desc 或者 select top 1 * from(select top 2 * from em

*字符串-01. 在字符串中查找指定字符

1 /* 2 * Main.c 3 * D1-字符串-01. 在字符串中查找指定字符 4 * Created on: 2014年8月18日 5 * Author: Boomkeeper 6 *****部分通过****** 7 */ 8 9 #include <stdio.h> 10 11 int mysearch(char ch, const char str[], int length) { 12 13 int j, ret = -1; 14 15 for (j = 0; j < le

从OTF字体文件中查找字体名称

for in ? 使用神器vim就好了... vim ./AKZIDENZGROTESK-COND.OTF 从OTF字体文件中查找字体名称,布布扣,bubuko.com