QT初步逆向分析二:QT 从对象找到信号槽

获取qt元数据对象

qt元对象为在虚表的第一个函数中
[[this]]
8c9c799577636312a2f037d8b5e5dc7a
最后返回的这个静态地址就是元数据对象的地址

元数据对象结构

QT_INIT_METAOBJECT const QMetaObject TsignalApp::staticMetaObject = { {
    QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(), //父类数据元对象
    qt_meta_stringdata_TsignalApp.data, //字符串数据
    qt_meta_data_TsignalApp, //该类的元数据
    qt_static_metacall, //信号槽分发函数
} };

解析字符串数据

34336c212d04ef7afd0a6acb79ec18f9
字符串数据是一个数组,

+0    分隔符 固定取值 0xFFFFFFFF
+4     字符串长度 
+0x10    字符串偏移 

以第一个为例
字符串偏移为0xC0,所以数组首地址 +0xC0 处的字符串是TsignalApp,长度刚好是10

上面的数据实际上可用字符串数组表达

	std::string stringdata[] = {
			"TsignalApp",
			"mySignal",
			"",
			"x",
			"mySignalParam",
			"y",
			"mySlot",
			"mySlotParam"
	};

解析元数据

元数据是元数据对象的第3个成员

第一个部分的数据
81366d8e243e7cced5d7b4ec2ce72a6a

+0x10    信号槽数量
+0x34    信号数量

由上可知 信号槽数量为6,信号数量为3

第二部分数据
7589f906fe638334d3d0dc4681c156b1

这是一个函数数组,前面的是信号,后面是槽,每个子项有五个成员,每个成员4字节,下面以第3个函数为例

  • name
    函数名称,这里用序号表示,这里序号用于前面解析字符串数组 ,stringdata[4]mySignalParam

  • argc
    参数数量 2

  • parameters
    参数的偏移 0X30 ,那么参数信息的位置在 元数据起始地址+0x30 * 4 也就是0xC0处
    0aeff87c4ce301941ce1edca4a2b89f6
    第一个成员 2B ,这个是返回值 0x2B表示QMetaType::Void
    中间部分 这里有argc决定数量,argc是2,所以 02 02 表示两个参数的类型都为QMetaType::Int , 03 05 是参数名称 stringdata[3] stringdata[5]表示 x y

    注意 若参数类型为自定义类型,则类型名称的最高字节为0x80 ,取最低字节为stringdata的索引以获取类型名称

  • tag
    不知道什么作用

  • flags
    不知道什么作用

qt类型枚举

 enum Type {
     UnknownType = 0, Bool = 1, Int = 2, UInt = 3, LongLong = 4, ULongLong = 5,
     Double = 6, Long = 32, Short = 33, Char = 34, ULong = 35, UShort = 36,
     UChar = 37, Float = 38,
     VoidStar = 31,
     QChar = 7, QString = 10, QStringList = 11, QByteArray = 12,
     QBitArray = 13, QDate = 14, QTime = 15, QDateTime = 16, QUrl = 17,
     QLocale = 18, QRect = 19, QRectF = 20, QSize = 21, QSizeF = 22,
     QLine = 23, QLineF = 24, QPoint = 25, QPointF = 26, QRegExp = 27,
     QEasingCurve = 29, QUuid = 30, QVariant = 41, QModelIndex = 42,
     QPersistentModelIndex = 50, QRegularExpression = 44,
     QJsonValue = 45, QJsonObject = 46, QJsonArray = 47, QJsonDocument = 48,
     QByteArrayList = 49, QObjectStar = 39, SChar = 40,
     Void = 43,
     Nullptr = 51,
     QVariantMap = 8, QVariantList = 9, QVariantHash = 28,
     QCborSimpleType = 52, QCborValue = 53, QCborArray = 54, QCborMap = 55,

     // Gui types
     QFont = 64, QPixmap = 65, QBrush = 66, QColor = 67, QPalette = 68,
     QIcon = 69, QImage = 70, QPolygon = 71, QRegion = 72, QBitmap = 73,
     QCursor = 74, QKeySequence = 75, QPen = 76, QTextLength = 77, QTextFormat = 78,
     QMatrix = 79, QTransform = 80, QMatrix4x4 = 81, QVector2D = 82,
     QVector3D = 83, QVector4D = 84, QQuaternion = 85, QPolygonF = 86,

     // Widget types
     QSizePolicy = 121,
     LastCoreType = QCborMap,
     LastGuiType = QPolygonF,
     User = 1024
 };

所以第三个函数的签名为

void mySignalParam(int x,int y)

由于信号数量为3,所以第三个函数是信号

所有的函数解析出来为

0 void mySignal();
1 void mySignal(int x);
2 void mySignalParam(int x, int y);
3 void mySlot();
4 void mySlot(int x);
5 void mySlotParam(int x, int y);

在分发函数寻找函数体

元数据对象的第四个成员qt_static_metacall为信号槽分发函数
ec4b6ccca9148fe3323e0089f9d5ed23
这个函数原型为

void qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)

其中最重要的参数为 _id,这个为函数的索引,比如我们之前分析的第三个函数2 void mySignalParam(int x, int y);,它的索引为2,
所以我们要在分发函数中找到当_id为2时,会分发到哪个函数

f90ec435e1cc4eca15175a9ac5049ed4

posted @ 2025-06-30 01:51  乘舟凉  阅读(109)  评论(0)    收藏  举报