Having encountered several crash dumps with the code running on heap and the following similar stack traces

1: kd> k

*** Stack trace for last set context - .thread/.cxr resets it

ChildEBP RetAddr Args to Child

WARNING: Frame IP not in any known module. Following frames may be wrong.

02cdfbfc 0056511a 0x634648

02cdfc24 005651a1 ModuleA!ClassA::~ClassA+0x5a

02cdfc30 00562563 ModuleA!ClassA::`scalar deleting destructor'+0x11

[...]

02cdffec 00000000 kernel32!BaseThreadStart+0x37

I decided to model this situation. The idea was to corrupt a class member by overriding its vtable pointer with a heap entry address. Because the virtual destructor address is a first virtual method table entry in our class memory layout I made sure that it points to the same heap address by making vtable pointer a dereference fixpoint. Here is a source code based on how Visual C++ compiler implements objects in memory:

class Member {

public:

virtual ~Member() { data = 1; };

public:

int data;

};

class Compound {

public:

Compound(): pm(NULL) { pm = new Member(); }

virtual ~Compound() { delete pm; }

void Corrupt() {

unsigned int * pbuf = new unsigned int[0x10];

*pbuf = reinterpret_cast<unsigned int>(pbuf); // to ensure that the code would run through pbuf pointer

*reinterpret_cast<unsigned int *>(pm) = reinterpret_cast<unsigned int>(pbuf);

}

Member *pm;

};

int _tmain(int argc, _TCHAR* argv[])

{

Compound *pc = new Compound();

pc->Corrupt();

delete pc;

return 0;

}

In a crash dump we therefore see the similar stack trace:

0:000> .ecxr

eax=001f4c28 ebx=7efde000 ecx=001f4c18 edx=001f4c28 esi=00000000 edi=00000000

eip=001f4c28 esp=003cf7d0 ebp=003cf7e8 iopl=0 nv up ei pl nz na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206

001f4c28 284c1f00 sub byte ptr [edi+ebx],cl ds:002b:7efde000=00

0:000> k

*** Stack trace for last set context - .thread/.cxr resets it

ChildEBP RetAddr Args to Child

WARNING: Frame IP not in any known module. Following frames may be wrong.

003cf7cc 011d10e5 0×1f4c28

003cf7e8 011d114f Destructors!Compound::~Compound+0×35

003cf7f4 011d121e Destructors!Compound::`scalar deleting destructor’+0xf

003cf82c 011d1498 Destructors!wmain+0×8e

003cf874 77043677 Destructors!__tmainCRTStartup+0xfa

003cf880 77719d72 kernel32!BaseThreadInitThunk+0xe

003cf8c0 77719d45 ntdll!__RtlUserThreadStart+0×70

003cf8d8 00000000 ntdll!_RtlUserThreadStart+0×1b

We now check the correctness of the stack trace by examining the return addresses:

0:000> .asm no_code_bytes

Assembly options: no_code_bytes

0:000> ub 011d10e5

Destructors!Compound::~Compound+0×21:

011d10d1 cmp dword ptr [ebp-4],0

011d10d5 je Destructors!Compound::~Compound+0×3a (011d10ea)

011d10d7 push 1

011d10d9 mov ecx,dword ptr [ebp-4]

011d10dc mov edx,dword ptr [ecx]

011d10de mov ecx,dword ptr [ebp-4]

011d10e1 mov eax,dword ptr [edx]

011d10e3 call eax

0:000> ub 011d114f

Destructors!Compound::Corrupt+0×3e:

011d113e int 3

011d113f int 3

Destructors!Compound::`scalar deleting destructor’:

011d1140 push ebp

011d1141 mov ebp,esp

011d1143 push ecx

011d1144 mov dword ptr [ebp-4],ecx

011d1147 mov ecx,dword ptr [ebp-4]

011d114a call Destructors!Compound::~Compound (011d10b0)

We now examine the crash address:

0:000> u 001f4c28

001f4c28 sub byte ptr [edi+ebx],cl

001f4c2c les eax,fword ptr [eax]

001f4c2e pop ds

001f4c2f add byte ptr [eax],al

001f4c31 add byte ptr [eax],al

001f4c33 add byte ptr [eax],al

001f4c35 add byte ptr [eax],al

001f4c37 add byte ptr [eax],al

Then we check that it resides in a heap segment:

0:000> dt _PEB 7efde000

Destructors!_PEB

[...]

+0x088 NumberOfHeaps : 2

+0x08c MaximumNumberOfHeaps : 0x10

+0x090 ProcessHeaps : 0x777e4740 -> 0x004b0000 Void

[...]

0:000> dd 0x777e4740 l2

777e4740 004b0000 001f0000

0:000> !heap 001f0000

Index Address Name Debugging options enabled

2: 001f0000

Segment at 001f0000 to 00200000 (00005000 bytes committed)

Now we check vtable to see that it was normal for Compound object but corrupt for Member object:

0:000> .frame 1

01 003cf7e8 011d114f Destructors!Compound::~Compound+0x35

0:000> dv /i /V

prv local 003cf7dc @ebp-0x0c this = 0x001f4c08

0:000> dt Destructors!Compound 0x001f4c08

+0x000 __VFN_table : 0x011daa0c

+0x004 pm : 0x001f4c18 Member

0:000> dps 0x001f4c08 l1

001f4c08 011daa0c Destructors!Compound::`vftable'

0:000> dps 0x001f4c18 l1

001f4c18 001f4c28

0:000> dps 001f4c28 l1

001f4c28 001f4c28

The application, its source code and PDB file are available for download here.

- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -