Modeling C++ Object Corruption
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 -