You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据IDA反编译的伪代码还原生成该代码的C++源码?

我通过IDA7反编译某二进制文件,得到以下内容,已确认伪代码翻译准确。分析发现A::start()的逻辑是判断getFD是否来自ref_B虚表,用于处理子类重写getFD但未重写start的场景,以下是反编译内容及源码还原分析:

反编译内容

1. A::start() 伪代码

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);    // 指向getFD(ref_B的第6个函数)
    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 )    // 核心点:A未重写listen,所以此判断永远不成立
                              // 因此不能直接替换为this->listen(fb)
        goto LABEL_9;

    LABEL_4:
    if ( evutil_socketpair(1, 1, 0, fd) ) // 来自libevent2
    {
        fd = -1LL;
        return -1;
    }
    LABEL_10:
    return 0;
}

2. A的构造函数伪代码

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

3. ref_B虚表内容

.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)

4. 类A结构

struct __cppobj A : B
{
    pthread_mutex_t mMutex;
};

5. 类B结构

struct B
{
    int (**_vptr.B)(...); // IDA生成,等价于_vptr_B
    int mFd;
};

源码还原分析

这种代码是GCC等编译器对非虚成员函数中调用虚函数的优化结果,对应的C++源码结构如下:

#include <event2/util.h>

// 基类B
struct B {
    int mFd;
    // 虚函数列表(对应ref_B中的顺序)
    virtual ~B() = default;
    virtual void open(int type) {}
    virtual void close() {}
    virtual int getXXX() { return 0; }
    virtual size_t writeXXX(const char*, size_t) { return 0; }
    virtual int getFD() { return mFd; }
    virtual int listen(int fb) { return 0; }

    // 非虚成员函数start
    int start(int fb) {
        int fd;

        // 编译器优化:直接检查虚表中的getFD是否为B的原生版本
        if (this->__vptr[6] != reinterpret_cast<void*>(&B::getFD)) {
            fd = getFD(); // 子类重写了getFD,需通过虚调用获取
            // 检查listen是否为B的原生版本
            if (this->__vptr[7] == reinterpret_cast<void*>(&B::listen)) {
                goto do_socketpair;
            }
            int ret = listen(fb);
            if (ret) {
                return 0;
            }
            goto do_socketpair;
        } else {
            fd = mFd; // 未重写getFD,直接访问成员变量
            if (this->__vptr[7] != reinterpret_cast<void*>(&B::listen)) {
                int ret = listen(fb);
                if (ret) {
                    return 0;
                }
                goto do_socketpair;
            }
        }

    do_socketpair:
        int fds[2];
        if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
            return -1;
        }
        // 原逻辑中fd被用于socketpair,此处省略具体赋值逻辑
        return 0;
    }
};

// 子类A,继承自B
struct A : B {
    pthread_mutex_t mMutex;

    // 重写部分虚函数,未重写getFD、listen
    ~A() override {}
    void open(int type) override {}
    void close() override {}
};

关键细节说明

  1. 虚表thunk函数:ref_B中的_ZThn8_N1AD1Ev等是编译器生成的非虚thunk,用于调整this指针——因为A比B多了8字节的mMutex成员,调用A的虚函数时需要将this指针偏移8字节,才能正确访问A的成员。
  2. 编译器优化逻辑:start是B的非虚成员函数,编译器会判断当前对象的虚表项是否为B的原生函数:
    • 若getFD未被重写,直接访问mFd,避免虚调用的开销;
    • 若getFD被重写,则通过虚表调用;
    • 对listen的判断同理,决定是否需要虚调用。
  3. 场景适配:这种代码用于处理「子类重写了基类的部分虚函数,但未重写包含这些虚函数调用的非虚成员函数」的场景,既保证多态正确性,又尽可能减少虚调用的性能损耗。

内容的提问来源于stack exchange,提问作者yed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:14:59