Decoding the parameters of a thrown C++ exception (0xE06D7363)


Special preview content for my TechReady talk later today. I'd like to claim it was planned this way, but actually it was just a coincidence.

The Visual C++ compiler uses exception code 0xE06D7363 for C++ exceptions. Here's how you can decode the other parameters. (Handy if you're debugging a crash dump.)

Note that this information falls under the category of implementation detail. There is no guarantee that this method will continue to work in the future, so don't write code that relies on it. It's just a debugging tip.

When the C++ exception is raised, the exception code is 0xE06D7363 and there are three (possibly four) parameters.

  • Parameter 0 is some internal value not important to the discussion.
  • Parameter 1 is a pointer to the object being thrown (sort of).
  • Parameter 2 is a pointer to information that describes the object being thrown.
  • Parameter 3 is the HINSTANCE of the DLL that raised the exception. (Present only on 64-bit Windows.)

The object being thrown is pretty much the object being thrown, except that sometimes there is some junk in front that you have to skip over. Once you figure out what it is, you can dump it. (I haven't bothered trying to figure out exactly how much; I just dump bytes and figure out the correct start of the object by inspection.) But what is it? That's what Parameter 2 tells you, but in a very roundabout way.

Take Parameter 2 and go to the fourth DWORD and treat it as a pointer. (On 64-bit systems, you have to add this value to the HINSTANCE passed as Parameter 3 to convert it to a pointer.)

Next, go to the second DWORD and treat it as a pointer. (Again, on 64-bit systems, it's really an offset from the HINSTANCE.)

Next, go to the second DWORD and treat it as a pointer. (64-bit systems: you know the drill.)

Finally, skip over the first two void*s and the rest is the class name.

Here's a picture, rendered in high-tech ASCII line drawing. Pointer-sized fields are marked with an asterisk, and fields whose value are unknown or not important are marked with tildes.

EXCEPTION_RECORD
+----------+
| E06D7363 |
+----------+
|  ~~~     |
+----------+
|* ~~~     |
+----------+
|* ~~~     |
+----------+
| 3 or 4   |
+----------+
|* ~~~     |
+----------+
|*Object   |
+----------+     +---+
|*       ------> |~~~|
+----------+     +---+
|*HINSTANCE|     |~~~|
+----------+     +---+
                 |~~~|
                 +---+    +---+
                 | -----> |~~~|
                 +---+    +---+    +---+
                          | -----> |~~~|
                          +---+    +---+    +----------+
                                   | -----> |*   ~~~   |
                                   +---+    +----------+
                                            |*   ~~~   |
                                            +----------+
                                            |Class name|
                                            +----------+

"When in doubt, add another level of indirection" appears to be the mantra here.

Here's a real-world example I had to debug. This came from a crash dump in a third-party application reported via Windows Error Reporting, so all debugging has to be done without source code or symbols.

0:008> .exr 00000000`015dede0
ExceptionAddress: 000007fefd23bb5d (KERNEL32!RaiseException+0x39)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 4 // this is running on 64-bit Windows
   Parameter[0]: 0000000019930520
   Parameter[1]: 00000000015def30 // object being thrown
   Parameter[2]: 00000000100cefa8 // magic Parameter 2
   Parameter[3]: 0000000010000000 // HINSTANCE

According to the cookbook, we follow Parameter 2:

0:008> dd 00000000100cefa8 l4
00000000`100cefa8  00000000 00000000 00000000 000cefc8
                                              ^^^^^^^^

and take the fourth DWORD. Since this is a 64-bit machine, we add it to the HINSTANCE before dumping. (If this were a 32-bit machine, we would just dump it directly.)

0:008> dd 100cefc8 l2
00000000`100cefc8  00000005 000ceff8
                            ^^^^^^^^

Now we take the second DWORD (add the HINSTANCE since this is a 64-bit machine) and then dump it again:

0:008> dd 100ceff8 l2
00000000`100ceff8  00000001 000d6670
                            ^^^^^^^^

Okay, we're within striking distance now. Since this is a 64-bit machine, we add the HINSTANCE to the offset. And on all platforms, we add two pointers (which is 0x10 on a 64-bit machine and 8 on a 32-bit machine). The result should be an ASCII string representing the class name:

0:008> da 100d6670+10
00000000`100d6680  ".PEAVCResourceException@@"

If you ignore the decorations, you see that this is telling you that the object thrown was a CResource­Exception.

And for old time's sake, here's a 32-bit version I just made up now.

0:000> .exr 0008f2e4
ExceptionAddress: 7671b046 (kernel32!RaiseException)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3 // 32-bit platform
   Parameter[0]: 19930520
   Parameter[1]: 0008f384 // object being thrown
   Parameter[2]: 10cfed60 // magic Parameter 2
0:000> dd 10cfed60 l4
10cfed60  00000000 00000000 00000000 10db297c
0:000> dd 10db297c l2
10db297c  00000004 10db2990
0:000> dd 10db2990 l2
10db2990  00000001 10dbccac
0:000> da 10dbccac+8
10dbccb4  ".PAVCFileException@@"

Anyway, back to the original problem: Knowing that the object being thrown was a CResource­Exception was a big help, because that's a class used by MFC, so I have additional information as to what it does and how it's used. This turns out to have been the necessary foothold to identify the source of the problem, which will be the subject of a future write-up.

Comments (10)
  1. Rich says:

    Hey, great post. The exact type of thing that gets me started, and I can dive deeper if needed. This will be really handy in sept. when I take my final semester in Comp. Sci. It's going to be a rough semester, these timbits of info really help.

  2. Pierre B. says:

    Thank you so much for getting back to in-depth, hard-to-get, advance debugging technique!

  3. Todd A. says:

    Is there any documentation on the structure that we are traversing in this article? Even if the EXACT structure is generated by the VC 8.23.3737 compiler ONLY, I'd be curious to see what information kicks around after an exception.

    Alternatively, is the traversing you have shown in this article the only traversing that can be safely done with the application in this exceptional state?

    And finally… How do you learn about these techniques or develop them? Is it because you have internal Microsoft documentation for the exception structures generated or are there books that will show you how to derive this information?

  4. Ben C. says:

    If you have a pointer to the object, and that object has virtual functions, there is a strong probability that the object will have a vtable pointer.  If you have symbols loaded for that object, then the debugger will usually tell you the type of the object based off of that vtable information.

  5. AC says:

    Very Interesting.

    Is there a similar way to do this in the Visual Studio debugger when you're in a catch(…) block? (Or maybe there's a build in way to display the exception type that I've overlooked)

  6. W says:

    Note that this information falls under the category of implementation detail. There is no guarantee that this method will continue to work in the future, so don't write code that relies on it. It's just a debugging tip.

    You should format that as big red and ugly. And still everybody will ignore it.

  7. Felix Wyss says:

    Filling in some blanks based on information I've collected and reverse engineered.

    Labeling the structure pointed to by the ExceptionInformation[2] member of EXCEPTION_RECORD as "A", we have the following:

    *A*

    DWORD  bitmask;              // Probably: 1=Const, 2=Volatile

    DWORD  destructor;           // RVA (Relative Virtual Address) of destructor for that exception object

    DWORD  unknown;

    DWORD  catchableTypesPtr;    // RVA of instance of type "B"

    *B*

    // Array of RVAs of descriptors (type "C") describing types as which the thrown object may be caught.  

    // The most specific/derived type is listed first.

    DWORD  count;

    DWORD  catchableTypes[count];

    *C*

    DWORD  someBitmask;

    DWORD  typeInfo;             // RVA of std::type_info for that type

    DWORD  memberDisplacement;   // Add to ExceptionInformation[1] in EXCEPTION_RECORD to obtain 'this' pointer.

    DWORD  virtBaseRelated1;     // -1 if no virtual base

    DWORD  virtBaseRelated2;     // ?

    DWORD  objectSize;           // Size of the object in bytes

    DWORD  probablyCopyCtr;      // RVA of copy constructor (?)

  8. Morten says:

    It strikes me that given that this is something people in the real world use as part of their job, it should probably be locked down or have a tool for somehow helping one get at the info. I mean, when I debug $SYSTEM I want as much information about what went on at the time of the incident as possible, up to and including implementation details like which class was most intimately involved in the crash and where the offending object was living at the time. Sure it might change in the next incarnation, but RIGHT NOW real world problems are harder to solve than they should be because the support information is not quite available. YMMV.

  9. JamesNT says:

    Mr. Chen,

    Very helpful information.  You are my programming god.

    JamesNT

  10. CPDaniel says:

    @Todd A:  I'd recommend writing a small bit of C++ code that catches and throws exceptions and compiling it with /FAsc to get an assembler listing.  You can usually deduce a lot from the structure declarations and the names of the symbols generated by the compiler.  Sometimes feeding the symbol names to undname.exe will reveal details about the purpose of the symbol beyond the simple unmangling of a C++ object name.

Comments are closed.