QT初步逆向分析一:由訊號傳送的地方找到槽
環境
msvc 2019 64位 Release
QT 5.15.1
原始碼
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); };
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();
}
材料
用於分析的測試程式
https://wwmf.lanzout.com/iYx5G0haftbi
獲取已知資訊
我們使用IDA和x64dbg定位到 emit mySignal();訊號傳送的地方,看看我們已知的資訊有哪些
void __fastcall sub_140001470(struct QObject *a1) { QMetaObject::activate(a1, (const struct QMetaObject *)&qword_140006100, 0, 0i64); }
struct QObject *a1
是傳送訊號的物件
const struct QMetaObject qword_140006100
元資料物件
int local_signal_index
訊號在訊號傳送類的本地索引
void **argv
傳遞的引數
後面我們所有的資訊都是通過這幾個數得到的
獲取傳送訊號在類中的全域性索引
訊號全域性索引(signal_index)是由元資料物件和local_signal_index得到的
元資料物件
struct { // private data
SuperData superdata; // 0x0 父類元資料物件的指標 由此可知這是一個連結串列結構
const QByteArrayData *stringdata;
const uint *data; //0x10 類的元資料 裡面包含訊號數量
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const SuperData *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
const uint *data
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
int signalCount; //0x34 類的訊號數量(不包含基類)
}
獲取所有父類的訊號數量
我們通過SuperData superdata;獲取父類的元資料物件,然後通過data.signalCount獲取父類的訊號數量,不斷重複,直到superdata值為0,說明沒有父類了,把這些訊號數量累積起來就可以得到所有基類的訊號總數了
由上圖可知,元資料物件的地址為0x0000000140021100,在記憶體視窗開啟
由上圖可知,父類的元資料物件的地址為0x00007FFF7C34EE08,在記憶體視窗中開啟
由上圖可知,由於data的偏移為0x10,故data的地址為0x00007FFF7C34EC20,在記憶體視窗中開啟
由於 signalCount偏移為0x34 ,所以這個父類的signalCount為3
回到0x00007FFF7C34EE08
找到下一個父類的元資料物件地址 0x00007FFF7C4DD130
按照相同的辦法找到signalCount 這裡為4
繼續找下一個父類的元資料物件地址 0x00007FFF7BDFB700
發現SuperData superdata;的值為0,說明沒有父類了
這裡的signalCount為3
所有父類的訊號數量為3+4+3 = 10
獲取本地訊號索引(local_signal_index)
由上圖可知,local_signal_index值為0
signal_index = local_signal_index + 所有父類的訊號數量 = 10