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 -

Leave a Reply

You must be logged in to post a comment.