2015年2月16日月曜日

IngressのGlyph Hackをハックする


IngressのGlyph Hackをハック中。

フレームバッファのバイナリをBitmapに変換して必要なとこだけキャプチャするのは完了。
こうして人の脳は退化していくわけですね。

ついでに自前の検出器を作成して、それがどの図柄なのかを検出するのも完了。
画面上の表示位置が変わらないし図柄がすごく単純なので、OpenCVなんて使う必要は全くありませんでした。

そしてあとは自動で絵を描くだけだ~と思ったら、後一歩のところで壁にぶつかりました。

そもそも、画面キャプチャ、検出器作成まで出来たら、あとはイージーだと思って何も調べてなかったのですが、結構厄介な問題がありました。

よそ様のアプリを操作するためにタッチイベントを生成するには、android.permission.INJECT_EVENTSが必要なのですが、こいつがいわゆるシステム権限らしくうまく動きません。

AndroidManifest.xmlで宣言しても、SecurityExceptionが発生します。


システム権限ということであれば、/system/app配下に放り込めば良いのかと考え、以下のようにしてADB Shellから無理やり放り込んでシステムアプリ化してみました。

$ adb shell
$ su
# mount -o rw,remount /system
# cp /sdcard/AutoGlyph.apk /system/app
# chmod 644 /system/app/AutoGlyph.apk
# reboot

が、やはり動きません。


INJECT_EVENTSprotectionLevelsignatureで、公式の説明にはthe application that declared the permissionと同じ証明書で署名されていないとダメだとあります。
googleの署名じゃないとダメってことでしょうか?

もしかするとprotectionLevelsignatureOrSystemであれば、システムアプリ化するだけでいけたのかもしれませんが。。

これはお蔵入りの予感。。


ただ、LMT Launcherとかは普通に動いてるわけで、何かやり方はあるんでしょう。
セキュリティ的には出来ちゃダメな気もしますが。

システムアプリを偽装できるのか、Input Subsystemあたりを利用するのか。
LMT Launcherのpackagenamecom.android.lmtなのが気になりますが、それだけではダメな気がします。

う~ん。。


2015年2月15日日曜日

Androidアプリからスクリーンショット /system/bin/screencap


前回に続きスクリーンショットについてです。

/system/bin/screencap -p test.png

前回は上記のように-pオプションを付けて実行し、ファイルの拡張子も.pngにしていたので、pngファイルで保存されていました。

普通の用途ではそれでいいんだと思いますが、今回は訳あって1200ミリ秒ぐらいの中で全部やりたいので、時間的にギリギリです。

そこで、pngへの変換を省いて生のBitmap画像を得る方法について実験します。


/system/bin/screencapコマンドについては、pオプションやファイル名を指定せずに実行すると、いわゆるフレームバッファの生データが標準出力に出力されます。

データのはじめにはwidth、height、formatの各4byteで12byteのヘッダが付いてくるそうです。

とりあえずヘッダの中身を見てみます。

 2684682240
 655360
 16777216

うーん、まったく意味が分からない。。
実際のwidthやheightで割ってみても意味のありそうな数字になりません。

LITTLE_ENDIANで読み直してみます。

 1440
 2560
 1

これだ!

そしてその後には14,745,600byteのデータが飛んできました。

Nexus6で実験しているので、画面サイズは縦×横が2,560×1,440です。

2560 * 1440 = 3686400

14745600 / 3686400 = 4

1ピクセルあたり4byteということは、普通に考えれば32bitということでしょうか。
ただ、ネットで調べると16bitのダブルバッファリングだという説があったので、適当に16bitのヘッダを付加して表示してみます。

はい。ぜんぜんダメでした。

素直に32bitのヘッダを付加してみます。

上下が逆転しています。

Bitmapのデータはなぜか左下から右方向に向かって格納されているそうなのですが、フレームバッファには普通に並んでいるので、byte配列を逆さにしてからBitmapに食わせます。

色情報の順番も変わってしまいました。また、左右は入れ替えなくて良いようです。

行単位で逆にします。

まだちょっと色がおかしい。
どうやらRとBが逆になっているようですね。

このサンプルだと分かりにくいのでもう少しカラフルなサンプルでRとBを入れ替えると。

完成。


正体の良く分からないバイナリをいい感じに加工して読めるデータにする作業って、何か高度なパズルを解いているようで楽しいです。

まぁちゃんと調べれば正体は分かるんでしょうけど。。


2015年2月8日日曜日

Androidアプリからスクリーンショット /system/bin/screencap -p


Androidアプリからスクリーンショットを取るには、JNIとかでフレームバッファを直接見に行くしかないのかと思っていたのですが、Android4.0から/system/bin/screencapでスクリーンショットが可能になったとのことです。

root化は必要なものの、ずいぶん楽になったようですね。

try {
    final String SAVE_DIR = "/test/";
    String fileName = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".png";
    String fullpath = Environment.getExternalStorageDirectory().getPath() + SAVE_DIR + fileName;
    Process p = Runtime.getRuntime().exec("su");
    OutputStream os = p.getOutputStream();
    os.write(("/system/bin/screencap -p " + fullpath).getBytes("ASCII"));
    os.flush();
    os.close();
    p.waitFor();
    p.destroy();
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}

これ終わったらちゃんとdestroy()で開放してあげないと、色々と後で死にそうですね。