Search code examples
c++reverse-engineering

How to write a c++ code according to this reversed code?


I used IDA7 to decompile some binary and got something like this.

Pseudocode:

int A::start(){
    int (__fastcall *v3)(B *const); // x2
    unsigned int fd; // w0
    int (__fastcall *v5)(A *const, int); // x3
    int v6; // w20

    v3 = *(this->_vptr.B + 6);    //pointing to getFD (the sixth function of ref_B)
    if ( v3 != B::getFD )
    {
        fd = v3(&this->B);
        v5 = *(this->_vptr.B + 7);
        if ( v5 == B::listen )
            goto LABEL_4;
    LABEL_9:
        v6 = v5(this, fb);
        if ( v6 )
            goto LABEL_10;
        goto LABEL_4;

    }
    fd = this->mFd;
    v5 = *(this->_vptr.B + 7);
    if ( v5 != B::listen )    //major problem here, A doesn't overwrite listen, 
                              //so "v5 != B::listen" never true, 
                              //so we can't just place this->listen(fb) here
        goto LABEL_9;

    LABEL_4:
    if ( evutil_socketpair(1, 1, 0, fd) ) // this one comes from event2
    {
        fd = -1LL;
        return -1;
    }
    LABEL_10:
    return 0;
}

And Constructor of A looks like:

Pseudocode:

void A::A(A *const this)
{
    B::B(&this->B);
    this->_vptr.B = ref_B;
}

ref_B looks like:

.data.rel.ro:000000555644D8D8                 DCQ _ZTI1A ; `typeinfo for'A
.data.rel.ro:000000555644D8E0 ref_B           DCQ _ZThn8_N1AD1Ev ; `non-virtual thunk to'A::~A()
.data.rel.ro:000000555644D8E8                 DCQ _ZThn8_N1AD0Ev ; `non-virtual thunk to'A::~A()
.data.rel.ro:000000555644D8F0                 DCQ _ZThn8_N1A4openE8TYPE ; `non-virtual thunk to'A::open(TYPE)
.data.rel.ro:000000555644D8F8                 DCQ _ZThn8_N1A5closeEv ; `non-virtual thunk to'A::close(void)
.data.rel.ro:000000555644D900                 DCQ _ZN1B9get******Ev ; B::get******(void)
.data.rel.ro:000000555644D908                 DCQ _ZN1B9write****EPKcm ; B::write****(char const*,ulong)
.data.rel.ro:000000555644D910                 DCQ _ZN1B5getFDEv ; B::getFD(void)
.data.rel.ro:000000555644D918                 DCQ _ZN1B6listenEi ; `non-virtual thunk to'B::listen(int)

struct(class) A looks like:

struct __cppobj A : B
{
    pthread_mutex_t mMutex;
};

struct(class) B looks like:

struct B
{
    int (**_vptr.B)(...);//this is generated by IDA7, and I think
                         // it's pretty much equal to _vptr_B (so dose all '_vptr.B' above)
    int mFd;
};

My problem is what kind of source code yields code like A::start. I double checked the assemble code of A::start, and I'm quite sure IDA did the right Pseudocode translation.

I tried to understand the code's behaviour. It looks like A::start wants to judge whether getFD from it's vtable is from ref_B.(if Some class X derive from A and overwrite getFD, but not overwrite start, then calling x.start will calling its getFD instead of using mFd from class B). But I just don't know how to achieve the source code. Will some one so kind to help me out?


Solution

  • I think the code you highlighted is just the compiler optimistically inlining a virtual function with a slow path to fall back to.

    The original source code probably is just

    fd = getFD();
    

    where getFD is declared in B as:

    virtual int getFD() { return this->mFd; }
    

    The compiler cannot know whether A in turn is not subclassed with an override for getFD, so it must keep the slower virtual call as a backup.