无源码,定位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() 函数下个断点,点个数字按钮,栈回溯大概如下。
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的字符串,定位到具体的实现。
以genymotion 2.4.0 版本。 在注册对话框,如果没有输入符合格式的注册码,注册按钮是灰色的,不可用 ![](http://i2.tietuku.com/a64856234e66fbf8.png)
以正向编码猜测 注册对话框的某个 slot函数 绑定了 textview 的 textchanged singal,
hooper查找2textchanged,发现 多次字符串引用,
一一排除,得到 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
查找对0x00000001000cbee0引用,来到sub_1000bc430,伪代码如下
对比Calculator类,其对应的是 Calculator::qt_metacast。
在sub_1000bc430函数,上下3个函数去找,如何有间断的 数字判断跳转语句,得到sub_1000bc4a0 ,也就是对应的 qt_static_metacall函数了。 (标准的是继续通过字符串的引用去找,这边是用了取巧的办法)。
qt_static_metacall函数伪代码如下
判断注册按钮是否可用的函数伪代码如下(也就是sub_10005bb20 AboutDialog::serialChanged函数) ![](http://i2.tietuku.com/c916d54da79022fc.png)
点击注册按钮的函数伪代码如下,(也就是 sub_10005ed40 AboutDialog::registerLicense) ![](http://i2.tietuku.com/07444414a9fa8cef.png)
简单patch了一下player (注意不是genymotion),从字符串入手(例如Network),免费版本和付费版本的按钮在初始化时候传递的状态参数不同,定位以下2处代码,patch一下强制所有按钮可用,
修改0x00000001000c8a00 指令为 xor edx , edx 补上需要的nop
修改 0x00000001000c9a64 指令为 mov esi, 1 , 补上需要的nop
开启付费功能后
本例的例子是很简单的,实际中qt中还有有包含properties, enums等情况, 包含slot 或者 singal 函数也可以带参数的,需要再进行分析,可以自己写个包含各种属性的qt测试程序来辅助练习。