在没有源代码的情况下调试/绕过BSOD

时间:2011-09-29 03:06:45

标签: c++ windows debugging directx bsod

你好,祝你好运。

这里需要一些帮助:

场合
我有一个模糊的DirectX 9应用程序(名称和应用程序的详细信息与问题无关),导致所有nvidia卡(GeForce 8400GS及以上)的蓝屏死机,因为某些驱动程序版本。我认为问题是由DirectX 9调用或触发驱动程序错误的标志间接引起的。

目标
我想通过编写代理dll来追踪有问题的标记/函数调用(为了好玩,这不是我的工作/作业)并绕过错误条件。我已经有一个完成的代理dll,它为IDirect3D9,IDirect3DDevice9,IDirect3DVertexBuffer9和IDirect3DIndexBuffer9提供包装器,并提供Direct3D调用的基本日志记录/跟踪。但是,我无法确定导致崩溃的功能。

问题

  1. 没有可用的源代码或技术支持。没有任何帮助,没有其他人可以解决这个问题。
  2. 内核产生的内存转储没有帮助 - 显然在nv4_disp.dll中发生了访问冲突,但我无法使用stacktrace转到IDirect3DDevice9方法调用,而且异步发生错误的可能性。
  3. (主要问题)由于大量的Direct3D9Device方法调用,我无法将它们可靠地记录到文件或网络中:
    1. 即使没有刷新,登录文件也会导致显着减速,因此系统BSOD会丢失日志的所有最后内容。
    2. 通过网络登录(使用UDP和WINSOck的sendto)也会导致显着的减速,并且不能异步完成(异步数据包在BSOD上丢失),加上数据包(崩溃周围的数据包)有时会丢失,即使同步发送。
    3. 当应用程序通过记录例程“减慢”时,BSOD不太可能发生,这使得跟踪它变得更难。
  4. 问题
    我通常不会编写驱动程序,也不会进行这种级别的调试,所以我觉得我缺少一些重要的东西,比使用自定义日志记录机制编写IDirect3DDevice9代理dll有更简单的方法来追踪问题。它是什么?诊断/处理/修复问题的标准方法是什么(没有源代码,COM接口方法触发BSOD)?

    Minidump分析(WinDBG)

    Loading User Symbols
    Loading unloaded module list
    ...........
    Unable to load image nv4_disp.dll, Win32 error 0n2
    *** WARNING: Unable to verify timestamp for nv4_disp.dll
    *** ERROR: Module load completed but symbols could not be loaded for nv4_disp.dll
    *******************************************************************************
    *                                                                             *
    *                        Bugcheck Analysis                                    *
    *                                                                             *
    *******************************************************************************
    
    Use !analyze -v to get detailed debugging information.
    
    BugCheck 1000008E, {c0000005, bd0a2fd0, b0562b40, 0}
    
    Probably caused by : nv4_disp.dll ( nv4_disp+90fd0 )
    
    Followup: MachineOwner
    ---------
    
    0: kd> !analyze -v
    *******************************************************************************
    *                                                                             *
    *                        Bugcheck Analysis                                    *
    *                                                                             *
    *******************************************************************************
    
    KERNEL_MODE_EXCEPTION_NOT_HANDLED_M (1000008e)
    This is a very common bugcheck.  Usually the exception address pinpoints
    the driver/function that caused the problem.  Always note this address
    as well as the link date of the driver/image that contains this address.
    Some common problems are exception code 0x80000003.  This means a hard
    coded breakpoint or assertion was hit, but this system was booted
    /NODEBUG.  This is not supposed to happen as developers should never have
    hardcoded breakpoints in retail code, but ...
    If this happens, make sure a debugger gets connected, and the
    system is booted /DEBUG.  This will let us see why this breakpoint is
    happening.
    Arguments:
    Arg1: c0000005, The exception code that was not handled
    Arg2: bd0a2fd0, The address that the exception occurred at
    Arg3: b0562b40, Trap Frame
    Arg4: 00000000
    
    Debugging Details:
    ------------------
    
    
    EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".
    
    FAULTING_IP: 
    nv4_disp+90fd0
    bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi
    
    TRAP_FRAME:  b0562b40 -- (.trap 0xffffffffb0562b40)
    ErrCode = 00000000
    eax=00000808 ebx=e37f8200 ecx=e4ae1c68 edx=e37f8328 esi=e37f8400 edi=00000000
    eip=bd0a2fd0 esp=b0562bb4 ebp=e37e09c0 iopl=0         nv up ei pl nz na po nc
    cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202
    nv4_disp+0x90fd0:
    bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi ds:0023:00000900=????????
    Resetting default scope
    
    CUSTOMER_CRASH_COUNT:  3
    
    DEFAULT_BUCKET_ID:  DRIVER_FAULT
    
    BUGCHECK_STR:  0x8E
    
    LAST_CONTROL_TRANSFER:  from bd0a2e33 to bd0a2fd0
    
    STACK_TEXT:  
    WARNING: Stack unwind information not available. Following frames may be wrong.
    b0562bc4 bd0a2e33 e37f8200 e37f8200 e4ae1c68 nv4_disp+0x90fd0
    b0562c3c bf8edd6b b0562cfc e2601714 e4ae1c58 nv4_disp+0x90e33
    b0562c74 bd009530 b0562cfc bf8ede06 e2601714 win32k!WatchdogDdDestroySurface+0x38
    b0562d30 bd00b3a4 e2601008 e4ae1c58 b0562d50 dxg!vDdDisableSurfaceObject+0x294
    b0562d54 8054161c e2601008 00000001 0012c518 dxg!DxDdDestroySurface+0x42
    b0562d54 7c90e4f4 e2601008 00000001 0012c518 nt!KiFastCallEntry+0xfc
    0012c518 00000000 00000000 00000000 00000000 0x7c90e4f4
    
    
    STACK_COMMAND:  kb
    
    FOLLOWUP_IP: 
    nv4_disp+90fd0
    bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi
    
    SYMBOL_STACK_INDEX:  0
    
    SYMBOL_NAME:  nv4_disp+90fd0
    
    FOLLOWUP_NAME:  MachineOwner
    
    MODULE_NAME: nv4_disp
    
    IMAGE_NAME:  nv4_disp.dll
    
    DEBUG_FLR_IMAGE_TIMESTAMP:  4e390d56
    
    FAILURE_BUCKET_ID:  0x8E_nv4_disp+90fd0
    
    BUCKET_ID:  0x8E_nv4_disp+90fd0
    
    Followup: MachineOwner
    

3 个答案:

答案 0 :(得分:6)

nv4_disp+90fd0
bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi

这是重要的部分。看看这个,最有可能的是eax无效,因此试图访问无效的内存地址。

您需要做的是将nv4_disp.dll加载到IDA(您可以获得免费版本),检查IDA加载nv4_disp的图像库并点击“g”到转到地址,尝试将90fd0添加到图像库IDA正在使用,它应该直接带你到违规指令(取决于部分结构)。

从这里您可以分析控制流程,以及如何设置和使用eax。如果你有一个好的内核级调试器,你可以在这个地址上设置一个断点并试着让它命中。

分析这个函数,你应该试着找出函数的作用,那个eax指向的那个点,实际指向的是什么,以及为什么。这是困难的部分,是逆向工程的难点和技巧的重要组成部分。

答案 1 :(得分:6)

找到解决方案。

<强>问题
记录不可靠,因为消息(转储到文件时)在bsod期间消失,数据包有时在网络登录时丢失,并且由于记录而减慢。

<强>解决方案
而不是记录到文件或通过网络,配置系统以在BSOD上生成完整的物理内存转储并将所有消息记录到任何内存缓冲区中。它会更快。一旦系统崩溃,它将把整个内存转储到文件中,并且可以使用WinDBG的dt(如果你有调试符号)命令查看日志文件缓冲区的内容,或者你将能够使用“内存”视图搜索和定位存储在内存中的日志文件。

我使用了std :: strings的循环缓冲区来存储消息和单独的const char *数组,以便在WinDBG中更容易阅读,但你可以简单地创建大量的char并以明文形式存储其中的所有消息。 / p>

<强>详情:
winxp上的整个过程:

  1. 确保最小页面文件大小等于或大于RAM总量+ 1兆字节。 (右键单击“我的电脑” - &gt;属性 - >高级 - >性能 - >高级 - >更改)
  2. 配置系统以在BSOD上生成完整的内存转储(点击“我的电脑” - >>属性 - >高级 - >启动和恢复 - >设置 - >写入调试信息。选择“完全内存转储”并指定您想要的路径。
  3. 确保磁盘(将写入文件的位置)具有所需的可用空间量(系统上的RAM总量。
  4. 使用调试符号和触发器BSOD构建app / dll(进行日志记录的那个)。
  5. 等到内存转储完成,重新启动。在系统写入内存转储并重新启动时,请随意向驱动程序开发人员发誓。
  6. 将MEMORY.DMP系统复制到安全的地方,这样如果系统再次崩溃,您将不会丢失所有内容。
  7. 发布windbg。
  8. 打开内存转储(文件 - &gt;打开崩溃转储)。
  9. 如果您想查看发生的情况,请使用!analyze -v命令。
  10. 访问使用以下方法之一存储已记录消息的内存缓冲区:
    1. 要查看全局变量的内容,请使用dt module!variable,其中“module”是您的库的名称(不含* .dll),“variable”是变量的名称。您可以使用通配符。您可以使用不包含module!variable
    2. 的地址
    3. 要查看全局变量的一个字段的内容(如果全局变量是结构),请使用dt module!variable field其中“field”是变量成员。
    4. 要查看有关可变数据(数组和子结构的内容)的更多详细信息,请使用dt -b module!variable fielddt -b module!variable
    5. 如果您没有符号,则需要使用内存窗口搜索“logfile”。
  11. 此时,您将能够看到存储在内存中的日志内容,此外,您还可以在崩溃时获得整个系统的快照。

    也...

    1. 要查看有关崩溃系统的流程的信息,请使用!process
    2. 要查看已加载的模块,请使用lm
    3. 有关线程的信息,!thread id其中id是您在!process输出中看到的十六进制ID。

答案 2 :(得分:1)

看起来崩溃可能是由错误的指针或堆损坏引起的。您可以这样说,因为崩溃发生在一个释放内存的函数(DxDdDestroySurface)中。破坏表面是你绝对需要做的事情 - 你不能只是将它存根,当程序退出时表面仍会被释放,如果你在内核中禁用它,你将耗尽卡上的内存很快就崩溃了。

您可以尝试找出导致此堆损坏的事件序列,但这里没有灵丹妙药 - 正如文件偏移建议的那样,您需要对驱动程序进行实际反向工程以了解为什么会发生这种情况(这可能有所帮助)在违规驱动程序版本之前和之后比较驱动程序!)