Recently, I wanted to understand what a Windows program built with Qt 4.7 is doing under the hood, in particular I investigated the use of the QString class. For that I used Frida to hook some of the classes methods.
To get started, I created a simple program that makes use of the two methods fromAscii
and append
:
#include <QString>
#include <stdio.h>
#include <Windows.h>
int main() {
int i = 1;
while(1){
const char* str = "awesome";
QString qstr = QString::fromAscii(str);
QString qstr2 = QString("string");
qstr.append(qstr2);
printf("%d %s\n", i, qstr.toUtf8().data());
i++;
Sleep(2000);
}
return 0;
}
When executed, the program repeatedly appends one QString to the other and prints the result:
With the following Frida script both functions get hooked to log relevant arguments resp. return values to the console, and to modify one of the QStrings. I got inspired by this and this issue on Frida’s Github repo, however both functions didn’t work for me.
//hook.js
var module = "QtCore4.dll";
// get string from QString
function convertQStringToString(qStringInput){
var toascii = new NativeFunction(Module.findExportByName(module, '_ZNK7QString7toAsciiEv'), 'void', ['pointer', 'pointer']);
var allocSpace = Memory.alloc(Process.pointerSize);
toascii(allocSpace,qStringInput);
var qByteArray = Memory.readPointer(allocSpace);
var todata = new NativeFunction(Module.findExportByName(module, '_ZN10QByteArray4dataEv'), 'pointer' , ['pointer']);
var str = todata(allocSpace).readCString();
return str;
}
// create QString
function convertStringToQString(stringInput){
var fromutf8 = new NativeFunction(Module.findExportByName(module, '_ZN7QString8fromUtf8EPKci'), 'void', ['pointer', 'pointer', 'int']);
var cStrPointer = Memory.allocUtf8String(stringInput);
var retQString = Memory.alloc(Process.pointerSize);
fromutf8(retQString,cStrPointer, -1);
return retQString;
}
// hook QString::fromAscii( const char * str, int size = -1 )
var fromAsciiFunc = "_ZN7QString9fromAsciiEPKci";
Interceptor.attach(Module.findExportByName(module, fromAsciiFunc), {
onEnter: function (args) {
// print "str" argument to console
var str = args[1].readCString();
console.log(fromAsciiFunc + ": \t '" + str + "'");
}
});
// hook QString & QString::append ( const QString & str )
var appendFunc = "_ZN7QString6appendERKS_";
var newQString = convertStringToQString("frida");
Interceptor.attach(Module.findExportByName(module, appendFunc), {
onEnter: function (args) {
// replace the appended QString with our own
args[1] = newQString;
},
onLeave: function (retval) {
// print returned QString to console
var str = convertQStringToString(retval);
console.log(appendFunc + ": \t '" + str + "'");
}
});
Run the script with frida.exe -l .\hook.js hello.exe
while hello.exe
is executed, and string will change: