references:
here is a demo source file
here is the compiled binary
it is a x86 exe
the main function of it is sub_402A60
virtual function table of these three class begin from 40AB34
you can see two _purecall
function in father class:
they're corresponding to these two lines in source code:
normally, only virtual function in base class have this function defined, here is the code of _purecall
:
//
// purevirt.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The _purecall handler, called by compiler-generated code when a pure virtual
// call occurs.
//
#include <vcruntime_internal.h>
#include <stdlib.h>
extern "C" extern _purecall_handler __pPurecall;
extern "C" int __cdecl _purecall()
{
_purecall_handler const purecall_handler = _get_purecall_handler();
if (purecall_handler != nullptr)
{
purecall_handler();
// The user-registered purecall handler should not return, but if it does,
// continue with the default termination behavior.
}
abort();
}
extern "C" _purecall_handler __cdecl _set_purecall_handler(
_purecall_handler const new_handler
)
{
return __crt_fast_decode_pointer(
__crt_interlocked_exchange_pointer(
&__pPurecall,
__crt_fast_encode_pointer(new_handler)));
}
extern "C" _purecall_handler __cdecl _get_purecall_handler()
{
return __crt_fast_decode_pointer(__crt_interlocked_read_pointer(&__pPurecall));
}
in the main function, we can see sub_4010E1
is being called for allocate 4h
bytes memory
this is the implementation of this function, it is just a wrapper of malloc
:
void *__cdecl sub_402CB0(size_t Size)
{
void *v2; // [esp+0h] [ebp-4h]
while ( 1 )
{
v2 = malloc(Size);
if ( v2 )
break;
if ( !callnewh(Size) )
{
if ( Size == -1 )
sub_401357();
sub_4010DC();
}
}
return v2;
}
then, the new allocated memory is pass either to sub_40125D
or sub_401316
, depends on the rand()
return value, these two function is actually the Dog
and Cat
class constructor
in IDA, we can see that this memory will finally be assigned with the correspond class virtual function table:
after class constructor call, member function walk
will be called, we can not see the actual function address being called, it is called from a register:
so v5
is now the class object, based on the constructor function code, we know *v5
is virtual function table, and 8h offset of it is the 3rd function in vftable
, which is depends on the which class is this vftable
belongs to, it is decided in runtime, it will either call dog's walk or cat's walk
because neither Dog nor Cat implement move function, so the last entry in the vftable
of all three class is the same
now let's define their structure:
then we need to create a structure of every vfptr
of them
then we set the vfptr
to the correct type for these three class
then we set set for variable in IDA, we can either set v5
to dogs*
or cats*