1. 程式人生 > 其它 >QT初步逆向分析一:由訊號傳送的地方找到槽

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