请问怎么输出QT的QString对象的内容

需求:需要在tweak中输出一个函数的QString类型参数,但是tweak中没有qt头文件,无法调用QString的成员函数 toStdString()。
函数声明为Net::Connect(QString path),参数是QString类型,但是Hopper反编译得到的函数原型是int sub_19248(int arg0, int arg1),参数变成了int,请问这个arg1是对应原来的path参数的指针吗

如果 QString 是值类型,声明为 int 原型会有问题。因为 Caller 会按照 Net::Connect(Net *this, QString path) 准备环境,所以你 Hook 以后的 Callee 也必须遵循这个规则来生成代码。不用按照 Hopper 里的原型,试试看根据原函数构造一个等价的原型。

QString对象是4字节大小。
hopper反编译得到的函数sub_19248(int arg0, int arg1)的参数应该是函数被调时保存在r0~r4寄存器中的值,
如果源代码参数是个按值传递的类对象,寄存器保存的参数就不能是这个对象的值本身,否则如果这个对象的类型大小如果超过寄存器尺寸,参数就不能保存,
那么寄存器保存的参数和源代码按值传入的自定义类型参数是什么关系,寄存器是原参数的指针吗

先说结论,在 armv7 下,值传递时如果值的尺寸大于 r 寄存器尺寸,会优先使用后续的 r 寄存器传参。当前这个例子如果 QString 的大小为4,理论上应该 arg0 = this, arg1 就是它的值(正好容纳 4B)。再深入的需要去看文档里的 ARMv7 Function Calling Conventions

可以自己做个实验,例如下面的代码:

#include <cstdio>

typedef struct Bucket {
    int id;
    int value;
} Bucket;

int handle_bucket(void *thiz, Bucket b) {
    int id = b.id;
    int value = b.value;
    printf("address of this is at %p", thiz);
    return id + value;
}

int main(int argc, char **argv) {
    Bucket buc;
    buc.id = 0;
    buc.value = 1;
    handle_bucket(new Bucket(), buc);
    return 0;
}

显然在 armv7 下,一个 r 寄存器容纳不了一个完整的 Bucket,可以反汇编看下结果:

clang -S -arch armv7 -isysroot `xcrun --sdk iphoneos --show-sdk-path` -fno-asynchronous-unwind-tables value_trans.cpp -o armv7.s

找到 Caller 的关键代码:

	bl	__Znwm
	ldr	r1, [sp, #4]            @ 4-byte Reload
	str	r1, [r0, #4]
	str	r1, [r0]	; r0 = this
	vldr	d16, [sp, #16]
	vstr	d16, [sp, #8]
	ldr	r1, [sp, #8] ; r1 = bucket.id
	ldr	r2, [sp, #12] ; r2 = bucket.value
	bl	__Z13handle_bucketPv6Bucket

可以看到 r0 = this, r1 = bucket.id, r2 = bucket.value。

再看 Callee 的关键代码:

__Z13handle_bucketPv6Bucket:
@ %bb.0:
	push	{r7, lr}
	mov	r7, sp
	sub	sp, #24
	str	r1, [sp, #16] ; sp + 16 = r1 = bucket.id
	str	r2, [sp, #20] ; sp + 20 = r2 = bucket.value
	str	r0, [sp, #12] ; sp + 12 = r0 = this

所以理论上你只要声明一个正确的 Callee 原型,编译器就会生成与 Caller 匹配的代码。

1 个赞