如何从AccessViolationException中检索Register Context?

6

我有一个x64崩溃转储文件,它是一个托管的(C#)应用程序,其中包含对本地代码的P/Invoke调用。 转储文件是在本地代码尝试取消引用错误内存位置后拍摄的,并且在.NET marshaler将其转换为AccessViolationException之后。 因此,发生错误的堆栈帧不再可用,并且异常发生的线程现在被CLR异常处理程序劫持:

0:017> kb
 # RetAddr           : Args to Child                                                           : Call Site
00 000007fe`fd3b10dc : 00000000`0402958b 00000000`20000002 00000000`00000e54 00000000`00000e4c : ntdll!NtWaitForSingleObject+0xa
01 000007fe`ea9291eb : 00000000`00000000 00000000`00000cdc 00000000`00000000 00000000`00000cdc : KERNELBASE!WaitForSingleObjectEx+0x79
02 000007fe`ea929197 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!CLREventWaitHelper2+0x38
03 000007fe`ea929120 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!CLREventWaitHelper+0x1f
04 000007fe`ead8cae5 : 00000000`29cbc7c0 00000000`3213ce40 00000000`00000000 00000000`ffffffff : clr!CLREventBase::WaitEx+0x70
05 000007fe`ead8c9d0 : 00000000`29cbc7c0 00000000`00000000 00000000`0002b228 00000000`0002b228 : clr!Thread::WaitSuspendEventsHelper+0xf5
06 000007fe`eacf2145 : 00000000`007ea060 000007fe`ea924676 00000000`00000000 000007fe`fd3b18da : clr!Thread::WaitSuspendEvents+0x11
07 000007fe`eaccc00c : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!Thread::RareEnablePreemptiveGC+0x33a905
08 000007fe`eae2c762 : 00000000`00000000 00000000`007cbce0 00000000`29cbc7c0 00000000`00000001 : clr!Thread::RareDisablePreemptiveGC+0x31b40c
09 000007fe`eaf662d4 : 00000000`00000000 00000000`007cbce0 00000000`29cbc7c0 00000000`00000000 : clr!EEDbgInterfaceImpl::DisablePreemptiveGC+0x22
0a 000007fe`eaf66103 : 00000000`29cb0100 00000000`00000000 00000000`3213cf80 00000000`29cbca20 : clr!Debugger::SendExceptionHelperAndBlock+0x174
0b 000007fe`eaf65d0d : ffffffff`ffffffff 00000000`29cbca20 00000000`29cbc700 000007fe`eaf62100 : clr!Debugger::SendExceptionEventsWorker+0x343
0c 000007fe`eaf61bd8 : 00000000`00000100 00000000`00000000 00000000`00000019 00000000`3213dd01 : clr!Debugger::SendException+0x15d
0d 000007fe`eadac75d : 00000000`007cbce0 00000000`3213d258 00000000`3213d1e8 00000000`00000001 : clr!Debugger::LastChanceManagedException+0x1f8
0e 000007fe`eaf698c7 : 000075ce`2b30e018 00000000`00000000 00000000`00000001 00000000`00000000 : clr!NotifyDebuggerLastChance+0x6d
0f 000007fe`eaf6af20 : 00000000`00000000 000007fe`8cf40020 000007fe`8cfa200c 4328fffe`43e0fffe : clr!Debugger::UnhandledHijackWorker+0x1a7
10 000007fe`eaaacbf0 : 00000000`0000000a 00000000`2ab23e30 00000000`00000001 00000000`00000000 : clr!ExceptionHijackWorker+0xc0
11 00000000`3213d8c0 : 00000000`3213ddb0 00000000`00000001 00000000`00000000 00000000`0000000b : clr!ExceptionHijack+0x30
12 00000000`3213ddb0 : 00000000`00000001 00000000`00000000 00000000`0000000b 00000000`0035578c : 0x3213d8c0
13 00000000`00000001 : 00000000`00000000 00000000`0000000b 00000000`0035578c ffffffff`00000002 : 0x3213ddb0
14 00000000`00000000 : 00000000`0000000b 00000000`0035578c ffffffff`00000002 00000000`00350268 : 0x1

.exr -1(显示最近的异常)返回:

0:017> .exr -1
ExceptionAddress: 00000000771d685a (user32!ZwUserMessageCall+0x000000000000000a)
   ExceptionCode: 80000004 (Single step exception)
  ExceptionFlags: 00000000
NumberParameters: 0

在线程0的堆栈顶部调用user32!ZwUserMessageCall,而不是发生本机异常的第17个线程,因此我只能假设它没有指向我的异常。

我可以转储访问冲突异常以获取有关本机错误的一些信息:

0:017> !DumpObj /d 0000000012175640
Name:        System.AccessViolationException
MethodTable: 000007fee9a61fe8
EEClass:     000007fee9528300
Size:        176(0xb0) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fee9a50e08  4000002        8        System.String  0 instance 000000001217b538 _className
000007fee9a5b218  4000003       10 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
000007fee9a50e08  4000004       18        System.String  0 instance 0000000000000000 _exceptionMethodString
000007fee9a50e08  4000005       20        System.String  0 instance 0000000012179818 _message
000007fee9a61f18  4000006       28 ...tions.IDictionary  0 instance 0000000000000000 _data
000007fee9a51038  4000007       30     System.Exception  0 instance 0000000000000000 _innerException
000007fee9a50e08  4000008       38        System.String  0 instance 0000000000000000 _helpURL
000007fee9a513e8  4000009       40        System.Object  0 instance 0000000012179ad0 _stackTrace
000007fee9a513e8  400000a       48        System.Object  0 instance 0000000012179c68 _watsonBuckets
000007fee9a50e08  400000b       50        System.String  0 instance 0000000000000000 _stackTraceString
000007fee9a50e08  400000c       58        System.String  0 instance 0000000000000000 _remoteStackTraceString
000007fee9a53980  400000d       88         System.Int32  1 instance                0 _remoteStackIndex
000007fee9a513e8  400000e       60        System.Object  0 instance 0000000000000000 _dynamicMethods
000007fee9a53980  400000f       8c         System.Int32  1 instance      -2147467261 _HResult
000007fee9a50e08  4000010       68        System.String  0 instance 0000000000000000 _source
000007fee9a54a00  4000011       78        System.IntPtr  1 instance                0 _xptrs
000007fee9a53980  4000012       90         System.Int32  1 instance       -532462766 _xcode
000007fee9a02d50  4000013       80       System.UIntPtr  1 instance                0 _ipForWatsonBuckets
000007fee9a3d210  4000014       70 ...ializationManager  0 instance 0000000012179900 _safeSerializationManager
000007fee9a513e8  4000001        0        System.Object  0   shared           static s_EDILock
                                 >> Domain:Value  00000000007e09b0:NotInit  <<
000007fee9a54a00  400018a       98        System.IntPtr  1 instance      7fedad179f4 _ip
000007fee9a54a00  400018b       a0        System.IntPtr  1 instance fffffffc2ab22078 _target
000007fee9a53980  400018c       94         System.Int32  1 instance                0 _accessType

通过这个我看到了失败的指令地址(7fedad179f4)和代码尝试去解引用的地址(fffffffc2ab22078)。似乎是一个符号扩展或溢出错误,但在代码中并不明显这是怎么发生的。被引用的指令为:

0:017> u 7fedad179f4
MYDLL!_interpolate+0x174 [c:\my\source\file.c @ 85]:
000007fe`dad179f4 f3450f59548404  mulss   xmm10,dword ptr [r12+rax*4+4]

为了进一步调试,我需要从本地代码崩溃时的寄存器上下文中查看 r12rax 中的内容。是否可以检索到这些信息?
编辑: 我尝试获取关于 ExceptionHijackWorker 参数的信息,但这些值对我来说没有意义。根据@S.T.的链接,函数签名如下:
void STDCALL ExceptionHijackWorker(T_CONTEXT * pContext,
                                   EXCEPTION_RECORD * pRecord,
                                   EHijackReason::EHijackReason reason,
                                   void * pData);

因此,作为指针的第一个参数0000000a是没有意义的。而且,转储第二个参数000000002ab23e30会产生EXCEPTION_RECORD的无意义数据:

0:017> dd 000000002ab23e30
00000000`2ab23e30  00000019 00000019 2ab23e40 00000000
00000000`2ab23e40  42b8f800 42b8de00 42b89b00 42b85000
00000000`2ab23e50  42b81b00 42b7a000 42b72600 42b6fa00
00000000`2ab23e60  42b6a000 42b67a00 42b63600 42b59c00
00000000`2ab23e70  42b4fc00 42b4da00 42b49e00 42b46a00
00000000`2ab23e80  42b38e00 42b31c00 42b2d600 42b29000
00000000`2ab23e90  42b2ec00 42b2fa00 42b2a000 42b27a00
00000000`2ab23ea0  42b23e00 42b6e800 42b6ab00 42b66c80
ExceptionCodeExceptionFlags 的值为 0x19 没有意义;该值没有对应的代码,并且标志被记录为零或 EXCEPTION_NONCONTINUABLE,其定义为1。

我是否有任何误解?


1
你有没有找到 http://blogs.msdn.com/b/ntdebugging/archive/2010/05/12/x64-manual-stack-reconstruction-and-stack-walking.aspx? - Thomas Weller
1
“单步执行”听起来不太对。它似乎是在调试会话期间进行的转储,但在崩溃时进行。很抱歉,我无法提供更多帮助。我很想看到一个答案。 - Thomas Weller
请检查您线程中帧#0x10的参数。函数ExceptionHijackWorker的第一个参数是指向上下文记录的指针(请参阅.Net Core运行时的此代码)。 - seva titov
cmkd试图自动从转储中检索函数参数。我怀疑它是否适用于您的情况,但值得一试。 - Lieven Keersmaekers
3
在x64中,“kb”永远不起作用。前4个参数在寄存器中,除非您有私有符号,否则必须反汇编函数以查看它是否以及在何处保留参数。对于上下文和异常记录,我曾经多次通过简单地探测调用帧周围来恢复它们--只需从帧的“rbp”开始并使用“.exr <address>”或“.cxr <address>”探测所有指针。通过查看“rip”和“rsp”,很容易识别上下文记录是否合理。 - seva titov
显示剩余5条评论
1个回答

3

@S.T.的建议下,我开始探查函数调用栈,以寻找异常记录或上下文记录。我从堆栈底部的异常情况开始查找,即:

0:017> k
 # Child-SP          RetAddr           Call Site
...
0f 00000000`3213d210 000007fe`eaf6af20 clr!Debugger::UnhandledHijackWorker+0x1a7
10 00000000`3213d850 000007fe`eaaacbf0 clr!ExceptionHijackWorker+0xc0
11 00000000`3213d880 00000000`3213d8c0 clr!ExceptionHijack+0x30
12 00000000`3213d8a8 00000000`3213ddb0 0x3213d8c0
13 00000000`3213d8b0 00000000`00000001 0x3213ddb0
14 00000000`3213d8b8 00000000`00000000 0x1

我恰好找到了异常记录:

0:017> .exr 00000000`3213ddb0 
ExceptionAddress: 000007fedad179f4 (SMTCV!_interpolate+0x0000000000000174)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: fffffffc2ab22078
Attempt to read from address fffffffc2ab22078

然后我偶然找到了上下文记录(我正在寻找的内容):

0:017> .cxr 00000000`3213d8c0 
rax=0000000000000019 rbx=000000000000000a rcx=00000000709c7c88
rdx=0000000000000002 rsi=000000002ab23e30 rdi=0000000080000000
rip=000007fedad179f4 rsp=000000003213dff0 rbp=0000000000000019
 r8=000007ffffe22000  r9=0000000070910000 r10=0000000000000000
r11=000000003213e0a0 r12=fffffffc2ab22010 r13=000000002b50ae40
r14=000000002ab241ec r15=0000000000000003
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=0000  es=0000  fs=0000  gs=0000             efl=00010200
MYDLL!_interpolate+0x174:
000007fe`dad179f4 f3450f59548404  mulss   xmm10,dword ptr [r12+rax*4+4] ds:fffffffc`2ab22078=????????

我现在可以看到我的错误指针在r12中了!

我不理解这些堆栈帧是什么,或者为什么异常和上下文记录被存储为它们的返回地址。对此的任何评论对于我和未来的读者都是非常有用的。


1
有没有办法访问这个转储文件?我很想进一步探索,或者有一个可复现的代码片段吗? - Addy
1
很不幸,我不能发布转储文件,并且没有符号的话,分析起来几乎没有任何价值。创建可重现的代码片段相当容易。你只需要一个C方法来解引用错误的内存,然后从C#中调用该方法即可。 - Patrick Quirk
1
你使用的框架版本是4.0还是4.5?我不指望有太大的区别,但以防万一。 - Addy
1
编译针对.NET 4.0和VS2010的C++工具集。 - Patrick Quirk
1
很高兴听到这个消息。现在,您可以使用k命令的版本来获取调用堆栈,因为您已经从异常记录中获得了riprsprbpk[b|p|P|v] [c] [n] [f] [L] [M] = StackPtr InstructionPtr FrameCount(请查阅WinDbg帮助)。 - seva titov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接