Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieving the stack trace from the stored exception context in a minidump (similar to .ecxr; k)

Tags:

Dumps obtained from Windows Error Reporting typically have a useless current context set on the faulting thread, with a stack deep in WerpReportFault. The actual context at the time of the exception can be retrieved with .ecxr -- which also sets the context in such a way that subsequent commands on the same thread (such as k) return the "correct" information.

I am building a tool for automatic dump analysis, which uses IDebugControl::GetStackTrace to obtain the faulting thread's stack. I can retrieve the stored exception context using IDebugControl4::GetStoredEventInformation. If I use the EBP/RBP, ESP/RSP, EIP/RIP values from the stored context with GetStackTrace, I get the correct stack. However, I would much rather replicate what the .ecxr command does, setting the "correct" state until the thread is switched. I tried using IDebugAdvanced::SetThreadContext, but it seems to be an illegal operation for dump targets and fails with 0x8000FFFF.

I tried to figure out what .ecxr does by debugging a WinDbg instance, and it looks like .ecxr is implemented in dbgeng!DotEcxr. However, from tracing it (with wt) I wasn't able to understand how it resets the current thread's context. It doesn't seem to call any of the COM debug-client interface methods, anyway, and doesn't use IDebugAdvanced::SetThreadContext.

Any suggestions on how to set the thread context in a dump file would be much appreciated. As a last resort, I can always use IDebugControl::Execute and simply invoke the .ecxr command, but I'd prefer a more programmatic approach.

like image 670
Sasha Goldshtein Avatar asked Aug 30 '16 14:08

Sasha Goldshtein


2 Answers

.ecxr memcopies the context record

to set the scope you can use this

EXT_COMMAND( setscope, "setscope", "{;e,d=@$ip;!setscope;}" )
{
    m_Symbols3->SetScopeFromStoredEvent();
}

after this call if you do k etc it will be for the last set context

:\>cdb -z oktest.dmp
Microsoft (R) Windows Debugger Version 10.0.10586.567 X86

This dump file has a breakpoint exception stored in it.
The stored exception information can be accessed via .ecxr.

0:000> k
ChildEBP RetAddr
0007fb1c 7c940442 ntdll!DbgBreakPoint
0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
00000000 00000000 ntdll!KiUserApcDispatcher+0x7


0:000> .load setscope
0:000> !setscope
0:000> k


  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr
0007fb1c 7c940442 ntdll!DbgBreakPoint
0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
00000000 00000000 ntdll!KiUserApcDispatcher+0x7
0:000>

the full extension code including getstacktrace and outputstacktrace

#include <codeanalysis\warnings.h>
#pragma warning( push )
#pragma warning ( disable : ALL_CODE_ANALYSIS_WARNINGS )
#include <engextcpp.cpp>
#pragma warning( pop )
class EXT_CLASS : public ExtExtension 
{
public:
    EXT_COMMAND_METHOD(setscope);
};
EXT_DECLARE_GLOBALS();
EXT_COMMAND( setscope, "setscope", "{;e,d=@$ip;!setscope;}" )
{
    m_Symbols3->SetScopeFromStoredEvent();
    DEBUG_STACK_FRAME Frames[0x20] = {0};
    ULONG FramesFilled = NULL;
    m_Control->GetStackTrace(0,0,0,Frames,0x20,&FramesFilled);
    m_Control->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT,Frames,FramesFilled,0x1fff);
}

executed kvf and setscope

0:000> kVf
  *** Stack trace for last set context - .thread/.cxr resets it
 #   Memory  ChildEBP RetAddr  Args to Child              
00           0007fb1c 7c940442 00000000 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
01       178 0007fc94 7c9210af 0007fd30 7c900000 0007fce0 ntdll!LdrpInitializeProcess+0xffa (FPO: [Non-Fpo])
02        88 0007fd1c 7c90e457 0007fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183 (FPO: [Non-Fpo])
03           00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7
0:000> !setscope
 #   Memory  ChildEBP RetAddr  Args to Child              
00           0007fb1c 7c940442 00000000 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
01       178 0007fc94 7c9210af 0007fd30 7c900000 0007fce0 ntdll!LdrpInitializeProcess+0xffa (FPO: [Non-Fpo])
02        88 0007fd1c 7c90e457 0007fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183 (FPO: [Non-Fpo])
03           00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7
like image 71
blabb Avatar answered Sep 23 '22 16:09

blabb


I think there is no magic way in dbgeng to retain a register context and use it in every further API call, but if you get an exception context from IDebugControl4::GetStoredEventInformation() I believe you should prefer IDebugControl4::GetContextStackTrace() instead of tricking GetStackTrace().

like image 40
Nicolas Dietrich Avatar answered Sep 22 '22 16:09

Nicolas Dietrich