Prerequisite:
Understanding what __purecall
means.
I was asked to help diagnose an issue in which a program managed to stumble
into the __purecall
function.
XYZ!_purecall: 00a14509 a100000000 mov eax,dword ptr ds:[00000000h] ds:0023:00000000=????????
The stack at the point of failure looked like this:
XYZ!_purecall XYZ!CViewFrame::SetFrame+0x14d XYZ!CViewFrame::SetPresentation+0x355 XYZ!CViewFrame::BeginView+0x1fe
The line at XYZ!CViewFrame::SetFrame
that
called the mystic __purecall
was a simple AddRef
:
pSomething->AddRef(); // crashes in __purecall
From what we know of __purecall
,
this means that somebody called into a virtual method on a derived
class after the derived class’s destructor has run.
Okay, well, let’s see if we can find the object in question.
Since the method being called is a COM method,
the __stdcall
calling convention applies,
which means that the this
pointer is on the stack.
0:023> dd esp+4 l1 0529f76c 06a88d58
Using our knowledge of the layout of a COM object, we can navigate through memory to find the vtable.
0:023> dps 06a88d58 06a88d58 009b2eac XYZ!CRegistrationSink::`vftable' 06a88d5c 06b20058 06a88d60 00000002 06a88d64 00998930 XYZ!CObjectWithBrush::`vftable' 06a88d68 00000000 06a88d6c 009c9c80 XYZ!CBrowseSite::`vftable' 06a88d70 009c9c70 XYZ!CBrowseSite::`vftable' 06a88d74 00000000 .... 0:023> dps 009b2eac 009b2eac 00a14509 XYZ!_purecall // virtual QueryInterface() = 0 009b2eb0 00a14509 XYZ!_purecall // virtual AddRef() = 0 009b2eb4 00a14509 XYZ!_purecall // virtual Release() = 0 009b2eb8 009cb1e4 XYZ!CRegistrationSink::Register 009b2ebc 009b3d2d XYZ!CRegistrationSink::Unregister
We see that the object has been destructed down to the
CRegistrationSink
base class,
and the attempt to increment its reference count has led us
into the abyss of __purecall
.
But what was this object before it descended into madness?
Well, we know that the object was something derived from
CRegistrationSink
.
And the other values in memory tell us that the object most
likely also derived from
CObjectWithBrush
and CBrowseSite
.
Just for fun, here’s the CObjectWithBrush
vtable,
to confirm that we destructed down to that point:
00998930 00a14509 XYZ!_purecall // virtual QueryInterface() = 0 00998934 00a14509 XYZ!_purecall // virtual AddRef() = 0 00998938 00a14509 XYZ!_purecall // virtual Release() = 0 0099893c 0099880d XYZ!CObjectWithBrush::SetBrush 00998940 00a319ee XYZ!CObjectWithBrush::GetBrush 00998944 00a13fd9 XYZ!CObjectWithBrush::`scalar deleting destructor'
Ooh, it looks like CObjectWithBrush
has a
virtual destructor.
Probably to destroy the brush.
A check of the source code tells us that nobody derives from
CBrowseSite
, so that is almost certainly the
original object type.
As a cross-check, we check whether what we have matches
the memory layout of a CBrowseSite
:
0:023> dt XYZ!CBrowseSite 06a88d58 +0x000 __VFN_table : 0x009b2eac +0x004 m_prgreg : 0x06a88d58 Registration +0x008 m_creg : 2 +0x00c __VFN_table : 0x00998930 +0x010 m_hbr : (null) +0x014 __VFN_table : 0x009c9c80 +0x018 __VFN_table : 0x009c9c70 +0x01c m_cRef : 0
Looks not unreasonable.
(Well, aside from the fact that we have a bug…)
The object has most likely begun its destruction because its
reference count (_cRef
) went to zero.
At this point, there was enough information to ask the developers
responsible for
CViewFrame
and CBrowseSite
to work out
how the CViewFrame
ended up running around with a pointer
to an object that has already been destructed.
0 comments