无源码,定位qt程序的函数响应地址

水平有限,错误在所难免,求指点。




qt是跨平台的C++ ui库,不少的商业公司都采用qt开发,有的时候我们需要去分析一下商业软件的实现, 但是qt的程序有和普通的windows程序有所区别,其界面上的控件都是没有句柄的,所以我们需要用特殊的办法去定位相关响应函数。

编译一份qt官方自带的例子先, C:\Qt\Qt5.4.1\Examples\Qt-5.4\widgets\widgets\calculator

qt里面有2个比较重要的宏,一个是SLOT, 一个是SIGNAL。


c:\Qt\Qt5.4.1\5.4\msvc2013\include\QtCore\qobjectdefs.h


# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a

其实这2个宏只是个字符串的链接而已。例如 SLOT(HelloFunction), 其实变成 "1HelloFunction", SIGNAL(HelloFunction) 其实变成 "2HelloFunction"

其次要想使用这2个宏还需要在类的定义中加上 Q_OBJECT


class Calculator : public QWidget
{
    Q_OBJECT

public:
    Calculator(QWidget *parent = 0);

private slots:
    void digitClicked();
    void unaryOperatorClicked();
    void additiveOperatorClicked();
    void multiplicativeOperatorClicked();
    void equalClicked();
    void pointClicked();
    void changeSignClicked();
    void backspaceClicked();
    void clear();
    void clearAll();
    void clearMemory();
    void readMemory();
    void setMemory();
    void addToMemory();
    
    //省略部分代码,
    .............
}

Q_OBJECT 是个宏,其实帮你加上了以下代码。


 /* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
    Q_OBJECT_CHECK \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    QT_TR_FUNCTIONS \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    struct QPrivateSignal {};
    

在 void Calculator::digitClicked() 函数下个断点,点个数字按钮,栈回溯大概如下。

2e949bf1461114c3.png

Calculator::qt_static_metacall 在moc_calculator.cpp 文件里面,这个文件是用Moc程序 自动生成的,此文件其实是 Q_OBJECT 宏所加上的那几个函数的实现。

搜索Calculator::staticMetaObject, 发现被 Calculator::staticMetaObject 引用。

Calculator::staticMetaObject 类型是 QMetaObject, QMetaObject中包含的结构体变量如下,


struct Q_CORE_EXPORT 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;
 }


    const QMetaObject Calculator::staticMetaObject = {
    { &QWidget::staticMetaObject, qt_meta_stringdata_Calculator.data,
      qt_meta_data_Calculator,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};

这里主要看下 staticMetaObject里面的 stringdata, data, StaticMetacallFunction, 也就是
qt_meta_stringdata_Calculator.data, qt_meta_data_Calculator, qt_static_metacall 3个。



struct qt_meta_stringdata_Calculator_t {
    QByteArrayData data[16];
    char stringdata[221];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_Calculator_t, stringdata) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
    

static const qt_meta_stringdata_Calculator_t qt_meta_stringdata_Calculator = {
    {
QT_MOC_LITERAL(0, 0, 10), // "Calculator"
QT_MOC_LITERAL(1, 11, 12), // "digitClicked"
QT_MOC_LITERAL(2, 24, 0), // ""
QT_MOC_LITERAL(3, 25, 20), // "unaryOperatorClicked"
QT_MOC_LITERAL(4, 46, 23), // "additiveOperatorClicked"
QT_MOC_LITERAL(5, 70, 29), // "multiplicativeOperatorClicked"
QT_MOC_LITERAL(6, 100, 12), // "equalClicked"
QT_MOC_LITERAL(7, 113, 12), // "pointClicked"
QT_MOC_LITERAL(8, 126, 17), // "changeSignClicked"
QT_MOC_LITERAL(9, 144, 16), // "backspaceClicked"
QT_MOC_LITERAL(10, 161, 5), // "clear"
QT_MOC_LITERAL(11, 167, 8), // "clearAll"
QT_MOC_LITERAL(12, 176, 11), // "clearMemory"
QT_MOC_LITERAL(13, 188, 10), // "readMemory"
QT_MOC_LITERAL(14, 199, 9), // "setMemory"
QT_MOC_LITERAL(15, 209, 11) // "addToMemory"

    },
    "Calculator\0digitClicked\0\0unaryOperatorClicked\0"
    "additiveOperatorClicked\0"
    "multiplicativeOperatorClicked\0"
    "equalClicked\0pointClicked\0changeSignClicked\0"
    "backspaceClicked\0clear\0clearAll\0"
    "clearMemory\0readMemory\0setMemory\0"
    "addToMemory"
};

static const uint qt_meta_data_Calculator[] = {

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

 // slots: name, argc, parameters, tag, flags
       1,    0,   84,    2, 0x08 /* Private */,
       3,    0,   85,    2, 0x08 /* Private */,
       4,    0,   86,    2, 0x08 /* Private */,
       5,    0,   87,    2, 0x08 /* Private */,
       6,    0,   88,    2, 0x08 /* Private */,
       7,    0,   89,    2, 0x08 /* Private */,
       8,    0,   90,    2, 0x08 /* Private */,
       9,    0,   91,    2, 0x08 /* Private */,
      10,    0,   92,    2, 0x08 /* Private */,
      11,    0,   93,    2, 0x08 /* Private */,
      12,    0,   94,    2, 0x08 /* Private */,
      13,    0,   95,    2, 0x08 /* Private */,
      14,    0,   96,    2, 0x08 /* Private */,
      15,    0,   97,    2, 0x08 /* Private */,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,

       0        // eod
};

void Calculator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Calculator *_t = static_cast(_o);
        switch (_id) {
        case 0: _t->digitClicked(); break;
        case 1: _t->unaryOperatorClicked(); break;
        case 2: _t->additiveOperatorClicked(); break;
        case 3: _t->multiplicativeOperatorClicked(); break;
        case 4: _t->equalClicked(); break;
        case 5: _t->pointClicked(); break;
        case 6: _t->changeSignClicked(); break;
        case 7: _t->backspaceClicked(); break;
        case 8: _t->clear(); break;
        case 9: _t->clearAll(); break;
        case 10: _t->clearMemory(); break;
        case 11: _t->readMemory(); break;
        case 12: _t->setMemory(); break;
        case 13: _t->addToMemory(); break;
        default: ;
        }
    }
    Q_UNUSED(_a);
}

qt_meta_data_Calculator 实际对应的类是 QMetaObjectPrivate


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
    // revision 5 introduces changes in normalized signatures, no new members
    // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself
    // revision 7 is Qt 5
    
}
    

可以看到 qt_static_metacall函数的_id次序 和 qt_meta_stringdata_Calculator 的 stringdata的字符串信息是一一对应的。 所有我们就可以通过qt_meta_stringdata_Calculator的字符串,定位到具体的实现。

5f4a481ae44d46dd.png
205eb4a6395796e7.png





以genymotion 2.4.0 版本。 在注册对话框,如果没有输入符合格式的注册码,注册按钮是灰色的,不可用
a64856234e66fbf8.png

以正向编码猜测 注册对话框的某个 slot函数 绑定了 textview 的 textchanged singal,

hooper查找2textchanged,发现 多次字符串引用,
03096b3bffb653bf.png

一一排除,得到 1serialChanged(), 去掉1, 查找serialChanged(), 来到0x00000001000cbee0


0x00000001000cbee0 db "AboutDialog", 0 ; XREF=sub_1000bc430+29
0x00000001000cbeec db 0x00 ; '.'
0x00000001000cbeed db "registerLicense()", 0
0x00000001000cbeff db "serialChanged()", 0
0x00000001000cbf0f db "hideRegistrationStatus()", 0

726491aac67097e4.png

查找对0x00000001000cbee0引用,来到sub_1000bc430,伪代码如下

6ac4ff70eb885891.png

对比Calculator类,其对应的是 Calculator::qt_metacast。

在sub_1000bc430函数,上下3个函数去找,如何有间断的 数字判断跳转语句,得到sub_1000bc4a0 ,也就是对应的 qt_static_metacall函数了。 (标准的是继续通过字符串的引用去找,这边是用了取巧的办法)。
qt_static_metacall函数伪代码如下
e138caac80713a5d.png



判断注册按钮是否可用的函数伪代码如下(也就是sub_10005bb20 AboutDialog::serialChanged函数)
c916d54da79022fc.png


点击注册按钮的函数伪代码如下,(也就是 sub_10005ed40 AboutDialog::registerLicense)
07444414a9fa8cef.png

简单patch了一下player (注意不是genymotion),从字符串入手(例如Network),免费版本和付费版本的按钮在初始化时候传递的状态参数不同,定位以下2处代码,patch一下强制所有按钮可用,

修改0x00000001000c8a00 指令为 xor edx , edx 补上需要的nop
eba1175914e55296.png

修改 0x00000001000c9a64 指令为 mov esi, 1 , 补上需要的nop
97dd7462fa214450.png

开启付费功能后
f2dbb361718a47ca.png

本例的例子是很简单的,实际中qt中还有有包含properties, enums等情况, 包含slot 或者 singal 函数也可以带参数的,需要再进行分析,可以自己写个包含各种属性的qt测试程序来辅助练习。

转载于:https://www.cnblogs.com/tieyan/p/4485676.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值