吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

领取今日签到奖励
查看: 32586|回复: 80

[原创] Qt5 程序初步逆向分析+解析脚本

  [复制链接]
发表于 2016-5-11 11:03 | 显示全部楼层
本帖最后由 coldnight 于 2016-7-24 22:49 编辑

Qt5 程序初步逆向分析+解析脚本
本文参考了以下文章:
Qt Internals & Reversing
【翻译】Qt内部机制及逆向 QT 的信号与槽机制介绍

本文是参考以上文章作出的,但是文章对象是Qt4的,其解析脚本已不适用于Qt5,本人重新分析了Qt5程序的元数据结构,并给出了解析脚本,方便Qt5程序的逆向破解
第一次发贴,有任何疑问请回贴,谢谢。

Qt 的信号/槽机制
Qt 是一个跨平台的C++图形用户界面应用程序框架。它提供给开发者建立图形用户界面所需的功能,广泛用于开发GUI程序,也可用于开发非GUI程序。
Qt使用信号(Signal)和槽(Slot)机制用于对象间的通信。可以将信号和槽通过QObject对象的connet函数关联起来。我们可以使用emit(Qt定义的语句)发出某个信号,与该信号关联的槽就会接受到信号进行处理。
下面是一个简单的Qt5代码:
[C++] 纯文本查看 复制代码
// tsignal.h
#include <QMainWindow>
#include <QObject>
// 必须继承QObject才能使用信号和槽
class TsignalApp:public QMainWindow
 {
public:
    TsignalApp();
    void slotFileNew();
     Q_OBJECT
     // 信号声明区
     signals:
         // 声明信号 mySignal()
         void mySignal();
         // 声明信号 mySignal(int)
         void mySignal(int x);
         // 声明信号 mySignalParam(int,int)
         void mySignalParam(int x,int y);
     // 槽声明区
     public slots:
         // 声明槽函数 mySlot()
         void mySlot();
         // 声明槽函数 mySlot(int)
         void mySlot(int x);
         // 声明槽函数 mySignalParam (int,int)
         void mySlotParam(int x,int y);
         TsignalApp* mySlot2();
 };


tsignal.cpp
#include "tsignal.h"
#include <QMessageBox>
TsignalApp::TsignalApp()
{
    // 将信号 mySignal() 与槽 mySlot() 相关联
    connect(this,SIGNAL(mySignal()),SLOT(mySlot()));
    // 将信号 mySignal(int) 与槽 mySlot(int) 相关联
    connect(this,SIGNAL(mySignal(int)),SLOT(mySlot(int)));
    // 将信号 mySignalParam(int,int) 与槽 mySlotParam(int,int) 相关联
    connect(this,SIGNAL(mySignalParam(int,int)),SLOT(mySlotParam(int,int)));
}
// 定义槽函数 mySlot()
void TsignalApp::mySlot()
{
    QMessageBox::about(this,"Tsignal", "This is a signal/slot sample withoutparameter.");
}
// 定义槽函数 mySlot(int)
void TsignalApp::mySlot(int x)
{
    QMessageBox::about(this,"Tsignal", "This is a signal/slot sample with oneparameter.");
}
// 定义槽函数 mySlotParam(int,int)
void TsignalApp::mySlotParam(int x,int y)
{
    char s[256];
    sprintf(s,"x:%d y:%d",x,y);
    QMessageBox::about(this,"Tsignal", s);
}
void TsignalApp::slotFileNew()
{
    // 发射信号 mySignal()
    emit mySignal();
    // 发射信号 mySignal(int)
    emit mySignal(5);
    // 发射信号 mySignalParam(5,100)
    emit mySignalParam(5,100);
}

# main.cpp
#include "tsignal.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TsignalApp w;
    w.slotFileNew();
    return a.exec();
}
上面的代码编译后运行,会弹出依次弹出,"This is a signal/slot sample withoutparameter."、"This is a signal/slot sample with oneparameter."、"Tsignal x: y:"的窗口。
从上面的代码可以看出,Qt定义signals、slots、emit等关键字用于简化Qt程序的开发。Qt使用元对象编译器moc(meta object compiler)将这些关键字翻译成正常的C++代码,以便gcc或者vc编译。以上面的代码为例,tsignal.cpp中的qt关键字将被编译成如下代码:
[C++] 纯文本查看 复制代码
// moc_tsignal.cpp
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'tsignal.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.4.1. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
struct qt_meta_stringdata_TsignalApp_t {
    QByteArrayData data[10];
    char stringdata[78];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_TsignalApp_t, stringdata) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_TsignalApp_t qt_meta_stringdata_TsignalApp = {
    {
QT_MOC_LITERAL(0, 0, 10), // "TsignalApp"
QT_MOC_LITERAL(1, 11, 8), // "mySignal"
QT_MOC_LITERAL(2, 20, 0), // ""
QT_MOC_LITERAL(3, 21, 1), // "x"
QT_MOC_LITERAL(4, 23, 13), // "mySignalParam"
QT_MOC_LITERAL(5, 37, 1), // "y"
QT_MOC_LITERAL(6, 39, 6), // "mySlot"
QT_MOC_LITERAL(7, 46, 11), // "mySlotParam"
QT_MOC_LITERAL(8, 58, 7), // "mySlot2"
QT_MOC_LITERAL(9, 66, 11) // "TsignalApp*"

    },
    "TsignalApp\0mySignal\0\0x\0mySignalParam\0"
    "y\0mySlot\0mySlotParam\0mySlot2\0TsignalApp*"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_TsignalApp[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       7,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    0,   49,    2, 0x06 /* Public */,
       1,    1,   50,    2, 0x06 /* Public */,
       4,    2,   53,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       6,    0,   58,    2, 0x0a /* Public */,
       6,    1,   59,    2, 0x0a /* Public */,
       7,    2,   62,    2, 0x0a /* Public */,
       8,    0,   67,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    3,
    QMetaType::Void, QMetaType::Int, QMetaType::Int,    3,    5,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    3,
    QMetaType::Void, QMetaType::Int, QMetaType::Int,    3,    5,
    0x80000000 | 9,

       0        // eod
};

void TsignalApp::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        TsignalApp *_t = static_cast<TsignalApp *>(_o);
        switch (_id) {
        case 0: _t->mySignal(); break;
        case 1: _t->mySignal((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 2: _t->mySignalParam((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;
        case 3: _t->mySlot(); break;
        case 4: _t->mySlot((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 5: _t->mySlotParam((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;
        case 6: { TsignalApp* _r = _t->mySlot2();
            if (_a[0]) *reinterpret_cast< TsignalApp**>(_a[0]) = _r; }  break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (TsignalApp::*_t)();
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TsignalApp::mySignal)) {
                *result = 0;
            }
        }
        {
            typedef void (TsignalApp::*_t)(int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TsignalApp::mySignal)) {
                *result = 1;
            }
        }
        {
            typedef void (TsignalApp::*_t)(int , int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TsignalApp::mySignalParam)) {
                *result = 2;
            }
        }
    }
}

const QMetaObject TsignalApp::staticMetaObject = {
    { &QMainWindow::staticMetaObject, qt_meta_stringdata_TsignalApp.data,
      qt_meta_data_TsignalApp,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};


const QMetaObject *TsignalApp::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *TsignalApp::qt_metacast(const char *_clname)
{
    if (!_clname) return Q_NULLPTR;
    if (!strcmp(_clname, qt_meta_stringdata_TsignalApp.stringdata))
        return static_cast<void*>(const_cast< TsignalApp*>(this));
    return QMainWindow::qt_metacast(_clname);
}

int TsignalApp::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QMainWindow::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 7)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 7;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 7)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 7;
    }
    return _id;
}

// SIGNAL 0
void TsignalApp::mySignal()
{
    QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR);
}

// SIGNAL 1
void TsignalApp::mySignal(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

// SIGNAL 2
void TsignalApp::mySignalParam(int _t1, int _t2)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
    QMetaObject::activate(this, &staticMetaObject, 2, _a);
}

QT_END_MOC_NAMESPACE
可以看到,mySignal信号被翻译成如下形式
[C++] 纯文本查看 复制代码
void TsignalApp::mySignal() {
    QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR);
}
如果信号带参数,那么会多一次类型形转换的过程:
[C++] 纯文本查看 复制代码
void TsignalApp::mySignalParam(int _t1, int _t2) {
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
    QMetaObject::activate(this, &staticMetaObject, 2, _a);
}

无论带不带参数,发出信号都会调用的QMetaObject::activate函数。

Qt 元数据结构
QMetaObject::activate使得Qt可以动态调用信号关联的槽,但这给也我们破解Qt程序带来了困难。
假设有这样一个程序,在接收到注册码调用后进行检查,如果正确,则触发触发注册成功的信号,否则触发注册失败信号。注册码破解的直接思路是通过注册失败的信息查找注册函数,但是由上面我们可以知道发送信号的时候是通过QMetaObject::activate触发信号,这不是一个直接的函数调用。即使我们在出错信息上下断点,此时栈上是大量的Qt核心库调用信息,触发信号的函数查找起来十分麻烦。
Qt是通过connect将信号与槽关联。但是connet的时候,我们无法直接知道与信号相关联的槽函数的位置。例如:
[C++] 纯文本查看 复制代码
connect(this,SIGNAL(mySignal()),SLOT(mySlot())); 

在编译后使用IDA反编译如下:


没有出现mySlot的地址,保留的只有mySlot的名称。
但是Qt是可以正确调用相应的函数的,原因是Qt会将信号和槽的元数据保存,并在运行的时候动态查找相应的方法。在moc_tsignal.cpp中,元数据如下:
[C++] 纯文本查看 复制代码
static const qt_meta_stringdata_TsignalApp_t qt_meta_stringdata_TsignalApp = {
    {
QT_MOC_LITERAL(0, 0, 10), // "TsignalApp"
QT_MOC_LITERAL(1, 11, 8), // "mySignal"
QT_MOC_LITERAL(2, 20, 0), // ""
QT_MOC_LITERAL(3, 21, 1), // "x"
QT_MOC_LITERAL(4, 23, 13), // "mySignalParam"
QT_MOC_LITERAL(5, 37, 1), // "y"
QT_MOC_LITERAL(6, 39, 6), // "mySlot"
QT_MOC_LITERAL(7, 46, 11), // "mySlotParam"
QT_MOC_LITERAL(8, 58, 7), // "mySlot2"
QT_MOC_LITERAL(9, 66, 11) // "TsignalApp*"

    },
    "TsignalApp\0mySignal\0\0x\0mySignalParam\0"
    "y\0mySlot\0mySlotParam\0mySlot2\0TsignalApp*"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_TsignalApp[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       7,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    0,   49,    2, 0x06 /* Public */,
       1,    1,   50,    2, 0x06 /* Public */,
       4,    2,   53,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       6,    0,   58,    2, 0x0a /* Public */,
       6,    1,   59,    2, 0x0a /* Public */,
       7,    2,   62,    2, 0x0a /* Public */,
       8,    0,   67,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    3,
    QMetaType::Void, QMetaType::Int, QMetaType::Int,    3,    5,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    3,
    QMetaType::Void, QMetaType::Int, QMetaType::Int,    3,    5,
    0x80000000 | 9,

       0        // eod
};
此外,每个Qt对象都有一个qt_static_metacall方法用于确定调用的函数:
[C++] 纯文本查看 复制代码
void TsignalApp::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        TsignalApp *_t = static_cast<TsignalApp *>(_o);
        switch (_id) {
        case 0: _t->mySignal(); break;
        case 1: _t->mySignal((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 2: _t->mySignalParam((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;
        case 3: _t->mySlot(); break;
        case 4: _t->mySlot((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 5: _t->mySlotParam((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;
        case 6: { TsignalApp* _r = _t->mySlot2();
            if (_a[0]) *reinterpret_cast< TsignalApp**>(_a[0]) = _r; }  break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {.. 
    }
}


其中的_id是相应Qt方法(信号/槽等)的索引,将索引与方法对应十分简单,之后会说。
实际上,在编译后Qt程序中qt_meta_stringdata_TsignalApp和qt_meta_data_TsignalApp会被封装在另一个数据结构中,由Qt的源代码分析得,该结构为QMetaObject内部的结构体d:
[C++] 纯文本查看 复制代码
// qobjectdefs.h
struct QMetaObject {
    struct { // private data
    const QMetaObject *superdata;
    const QByteArrayData *stringdata;
    const uint *data;
    typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
    StaticMetacallFunction static_metacall;
    const QMetaObject * const *relatedMetaObjects;
    void *extradata; //reserved for future use
    } d;
};
在结构体由索引(index)得到对应的方法代码如下:
[C++] 纯文本查看 复制代码
// qmetaobject.cpp
QMetaMethod QMetaObject::method(int index) const
{
    int i = index;
    i -= methodOffset();
    if (i < 0 && d.superdata)
        return d.superdata->method(index);

    QMetaMethod result;
    if (i >= 0 && i < priv(d.data)->methodCount) {
        result.mobj = this;
        result.handle = priv(d.data)->methodData + 5*i;
    }
    return result;
}

其中priv(d.data)的代码如下:
[C++] 纯文本查看 复制代码
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
即d.data指向一个QMetaObjectPrivate对象。
QMetaObjectPrivate的声明如下:
[C++] 纯文本查看 复制代码
// qmetaobject_p 
struct QMetaObjectPrivate {
    enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus

    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData; //since revision 2
    int flags; //since revision 3
    int signalCount; //since revision 4
}
实际上,这就是moc_tsignal.cpp中的相应内容
[C++] 纯文本查看 复制代码
 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       7,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // signalCount
由QMetaObjectPrivate.methodData成员,可以确定QMetaMethod在d.data中的偏移,不考虑父类的情况下,索引为id的QMetaMethod在d.data的偏移为QMetaObjectPrivate.methodData + id * 5

QMetaMethod
QMetaMethod在Qt源代码中只保留QObject的指针及偏移量计算的代码,没有定义真正的结构体,为了方便,将结构体整理如下:
[C++] 纯文本查看 复制代码
struct QMetaMethod {
    int name;
    int parameterCount;  // 参数个数
    int typesDataIndex;
    int tag;
    int flag;
}

获得方法名

QMetaMethod其中的name和typesDataIndex都是其在d.stringdata[]的索引。在moc中,由d.stringdata由如下宏生成:
[C++] 纯文本查看 复制代码
QT_MOC_LITERAL(0, 0, 10), // "TsignalApp"
在宏之后紧跟着C语言的字符串数据:
[C++] 纯文本查看 复制代码
"TsignalApp\0mySignal\0\0x\0mySignalParam\0"
"y\0mySlot\0mySlotParam\0mySlot2\0TsignalApp*"
d.stringdata指向的类型为QByteArrayData,其定义为:
[C++] 纯文本查看 复制代码
typedef QArrayData QByteArrayData;

// qarraydata.h
struct QTypedArrayData
    : QArrayData

struct QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;

    qptrdiff offset; // 相对于QArrayData所在地址的偏移
};
由QArrayData.offset,我们可以计算C字符串的偏移,由QArrayData.size可以确定字符串的长度。
至此,我们可以得到QMetaMethod的方法名。
例如:设mth为QMetaMethod对象,通过d.stringdata[mth.name]获得QArrayData对象arr,则&arr + arr.offset处即为mth的方法名字符串。

获得方法类型数据
QMetaMethod.typesDataIndex是方法的类型数据偏移,其计算方式如下:
偏移=d.data + QMetaMethod.typesDataIndex * 4
知道类型数据偏移后,我们就可以得到QMetaMethod的类型,以上面例子的mySignalParam为例,其原型为:
`void mySlotParam(int x,int y);
其编译后的类型信息为:
QMetaType::Void, QMetaType::Int, QMetaType::Int,    3,    5,
其中QMetaType::Void、QMetaType::Int都是定义在qmetatype.h的枚举。上面对应于:
[C++] 纯文本查看 复制代码
返回值类型, 第一个参数类型,第二个参数类型,第一个参数名称,第二个参数名称

参数名称和QMetaMethod.name一样,是在stringdata数组中的索引。
如果类型没有在QMetaType中定义,如TSingapp*这样的自定义类型,其类型将以
[C++] 纯文本查看 复制代码
0x80000000 | 类型名索引

的形式定义,如例子中的TsignalApp* mySlot2();被编译为:
[C++] 纯文本查看 复制代码
0x80000000 | 9,

9即为
QT_MOC_LITERAL(9, 66, 11) // "TsignalApp*"
QMetaMethod.flag保存相应方法的属性,如Public等,这里略过。
至此,我们已经可以得到完整的QMetaMethod的函数签名信息了。
确定QMetaObject.d位置
以上对方法的解析前提是有QMetaObject.d的信息,下面来确定该位置。 实际上十分简单,Qt的元数据类型的签名为static const,也就是说只要在程序的.rdata段或者.data段查找如下形式的结构体即可:

QMetaObject.d的主要特征是成员都是指什,superdata可以为null,因此找到连续三个的offset,且第4个成员为一函数指针,可以考虑为QMetaObject.d。

破解Qt5程序的思路
  • 在错误信息上下断点,其所在函数err_func通常为槽。
  • 断下后跟踪到对应的static_metacall,确定err_func的索引idx
  • 解析QMetaObject.d,在d的data中查找idx对应的方法,解析方法的名称name
  • 在字符串引用中查找name(可能要加上SLOT、SINGAL前缀)的引用,确定Qt进行connect的信号
  • 查到对信号的引用,逆向完成破解

总结

Qt通过信号/槽机制实现事件通信,可以在运行时动态connet。Qt在生成的可执行文件中保存了Qt对象(QObject)的元数据(QMetaObject.d结构),所有Qt对象均有一个static_metacall函数,根据索引调用相应的方法。我们可以根据QMetaObject.d->data获得方法的名称、返回值、参数、索引等信息。
本人写了一个IDA脚本,功能如下:
  • 自动完成方法的名称、返回值、参数、索引的解析;
  • Qt结构体(QMetaObject.d、QArrayData、QMethod)的添加和标记;
  • static_metacall函数的重命名;
  • 支持32位和64位的Qt5程序

使用方法:
将光标置于QMetaObject.d的起始处,Alt+F7调用本脚本即可。
效果如下:

运行脚本后:

QMetaObject.d.data的效果如下:

方法的名称和参数都还原了。



qtmetaparser.7z

3.72 KB, 下载次数: 324, 下载积分: 吾爱币 -1 CB

脚本

免费评分

参与人数 23吾爱币 +24 热心值 +22 收起 理由
风间仁 + 1 + 1 我很赞同!
naylxsc + 1 + 1 谢谢@Thanks!
___Mario + 1 + 1 谢谢@Thanks!
yanjingtu + 1 + 1 很详细的分析过程,感谢您的分享!学习了
不懂破解 + 1 + 1 我很赞同!
speedboy + 1 + 1 谢谢@Thanks!
丶峰宇 + 1 + 1 热心回复!
Catshark + 1 + 1 我很赞同!
LOVE_TT + 1 + 1 哈哈 原来做嵌入式时用的是QT~
yajiedesign + 1 谢谢@Thanks!
胡子啪啪 + 1 + 1 谢谢@Thanks!
Tortoise + 1 + 1 谢谢@Thanks!
dhs347 + 1 + 1 谢谢@Thanks!
测试中…… + 1 + 1 我很赞同!
回归自然 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 1 + 1 用心讨论,共获提升!
lies2014 + 1 + 1 谢谢@Thanks!
eyesonly + 1 谢谢@Thanks!
myqqq + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
_stdcall + 1 + 1 天天对着Qt
menghun + 1 + 1 用心讨论,共获提升!
Linkasyan + 1 + 1 论坛Qt相关内容较少 感谢楼主分享
renfeng + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-7-7 13:19 | 显示全部楼层
事实上还有动态初始化的。。。。。。。
.data:0094E764 ?staticMetaObject@testDlg@@2UQMetaObject@@B dd 0
.data:0094E764                                         ; DATA XREF: testDlg::metaObject(void):loc_4021B2o
.data:0094E764                                         ; _dynamic_initializer_for__testDlg__staticMetaObject__+8w
.data:0094E768 dword_94E768    dd 0                    ; DATA XREF: _dynamic_initializer_for__testDlg__staticMetaObject__+Dw
.data:0094E76C dword_94E76C    dd 0                    ; DATA XREF: _dynamic_initializer_for__testDlg__staticMetaObject__+17w
.data:0094E770 dword_94E770    dd 0                    ; DATA XREF: _dynamic_initializer_for__testDlg__staticMetaObject__+21w
.data:0094E774 dword_94E774    dd 0                    ; DATA XREF: _dynamic_initializer_for__testDlg__staticMetaObject__+2Bw
.data:0094E778 dword_94E778    dd 0                    ; DATA XREF: _dynamic_initializer_for__testDlg__staticMetaObject__+35w


[Asm] 纯文本查看 复制代码
.text:00417500 _dynamic_initializer_for__testDlg__staticMetaObject__ proc near
.text:00417500                                         ; DATA XREF: .rdata:testDlg__staticMetaObject$initializer$o
.text:00417500                 push    ebp
.text:00417501                 mov     ebp, esp
.text:00417503                 mov     eax, ds:__imp_?staticMetaObject@QDialog@@2UQMetaObject@@B ; QMetaObject const QDialog::staticMetaObject
.text:00417508                 mov     ?staticMetaObject@testDlg@@2UQMetaObject@@B, eax ; QMetaObject const testDlg::staticMetaObject
.text:0041750D                 mov     dword_94E768, offset qt_meta_stringdata_testDlg
.text:00417517                 mov     dword_94E76C, offset qt_meta_data_testDlg
.text:00417521                 mov     dword_94E770, offset j_?qt_static_metacall@testDlg@@CAXPAVQObject@@W4Call@QMetaObject@@HPAPAX@Z ; testDlg::qt_static_metacall(QObject *,QMetaObject::Call,int,void * *)
.text:0041752B                 mov     dword_94E774, 0
.text:00417535                 mov     dword_94E778, 0
.text:0041753F                 pop     ebp
.text:00417540                 retn
.text:00417540 _dynamic_initializer_for__testDlg__staticMetaObject__ endp


以上Qt5.5 +vs2010  debug 版用PDB发现的。debug版上边的方法就不太好用了,不过很少有debug版的程序。

Release 版倒是符合上述规律。

获取可以加一个自动搜索QMetaObject结构体。

感谢楼主的精彩分享。

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 11:09 | 显示全部楼层

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 11:33 | 显示全部楼层

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 11:34 | 显示全部楼层
厉害! Qt有些过于庞大,不然还继续用它

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 11:56 | 显示全部楼层
感谢分享,学习一下

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 12:01 | 显示全部楼层
论坛Qt相关内容较少  感谢楼主分享

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 12:51 | 显示全部楼层
楼主有心了 不错

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 13:13 | 显示全部楼层
牛..这东西应该必须收藏了  谢谢楼主的分享

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 13:24 | 显示全部楼层
好贴,努力学习吧

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

发表于 2016-5-11 13:47 | 显示全部楼层
来学习学习了

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则


免责声明:
吾爱破解所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Service@52PoJie.Cn

快速回复 收藏帖子 返回列表 搜索

RSS订阅|手机版|小黑屋|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2018-12-12 09:11

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表