Qt 5 の Qt Quick アプリのバイナリには qml のソースコードが含まれている
はじめに
みなさん Qt Quick でアプリ書いていますか?便利でいいですよね!
最近こんな相談を受けました。
Qt Quick でアプリを書いているのですが、アプリのバイナリの中身に qml や js のソースコードがそのまま含まれているのですがなんとかなりませんか?
状況と原因を調査
以下の Qt Quick の簡単なプロジェクトで状況を確認してみましょう。
app.pro
SOURCES = main.cpp
RESOURCES = qml.qrc
main.cpp
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
qml.qrc
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
main.qml
これをコンパイルして中身を見てみましょう。
バイナリエディタで確認
Qt に詳しい方は分かるかと思いますが、このプロジェクトでは qml のソースコードを Qt のリソースシステム に格納し、それをテキストファイルとして読み込んで実行しています。
いい対策は無いか
上記のリソースのドキュメントの Compression に記載があるとおり、リソースシステムには圧縮機能があり、これが機能すれば、少なくとも生のファイルが見えることはなくなります。
実際の rcc の実行コマンドは以下の通りになっているので(Qt Creator のビルドのログなどを参考にしてください)
$ /path/to/rcc -name qml ../app/qml.qrc -o qrc_qml.cpp
以下のように内部情報の表示を増やしてみましょう。
$ /path/to/rcc -name qml ../app/qml.qrc -o qrc_qml.cpp –verbose
Qt resource compiler
Processing 1 files [listMode=0]
Interpreting ../app/qml.qrc
Outputting code
main.qml: note: not compressed
理由は不明ですが、”not compressed” ということで、生データがそのままリソースに組み込まれていることがわかります。
ドキュメントには、「デフォルトではしきい値は 70% にしてあるよ」と書いてあるので、これを下げて圧縮が効くか試してみましょう。
$ /path/to/rcc -threshold 25 -name qml ../app/qml.qrc -o qrc_qml.cpp –verbose
Qt resource compiler
Processing 1 files [listMode=0]
Interpreting ../app/qml.qrc
Outputting code
main.qml: note: not compressed
$ /path/to/rcc -threshold 15 -name qml ../app/qml.qrc -o qrc_qml.cpp –verbose
Qt resource compiler
Processing 1 files [listMode=0]
Interpreting ../app/qml.qrc
Outputting code
main.qml: note: compressed using zlib (139 -> 116)
ということで、しきい値を下げることで(ここではzlib)圧縮が効くことがわかりました。
rcc コマンドを手動で毎回実行するわけにもいかないので、app.pro に以下の行を追加しましょう。
だいぶましになりました。
が、Qt のリソースシステムに詳しい人であれば、この内容からソースコードの復元(解凍)が可能ではあります。
QML をコンパイル時にコンパイルするのはどうか
Qt には QML をコンパイルする機能 があり、以前は商用版でのみ利用可能でしたが、Qt 5.11 以降はオープンソース版でも使えるようになっています。
app.pro を以下のように変更してみましょう。
CONFIG += qtquickcompiler
コンパイルした QML がバイナリに含まれるのでソースコードは含まれない…という想定に反し、なんとソースコードが見える状態になっています。
[QTBUG-88147] qtquickcompiler requirements have changed というバグレポートを見ると、
「Qt 5.14 までは元のファイルをアプリのバイナリに含めないようにしていたが、[QTBUG-73669] Adding *.js to resources of project without QML leads to linker error のバグを直すために Qt 5 ではソースファイルもバイナリに含めるようにしたよ」
というようなことが書いてあります。
というわけで、Qt 5 ではもうあきらめるしかないということになりますので、以下のようにして祈りましょう。
CONFIG += qtquickcompiler
納得がいかないなぁ
大多数のケースでは QTBUG-73669 の条件に当てはまらないと思うので、qmlcachegen という .qml や .js のコンパイルをしているツールのソースコードを局所的に元に戻すことでソースコードをバイナリに含めなくすることも可能だと思います。
ソースコードの秘匿化や、バイナリサイズの削減などでこういったことが必要であれば、調査もしますし対応も考えますので一声かけてください!