Androidアプリ開発 ProGuardによる難読化と最適化 [Androidアプリ開発]
昨日のネタ、難読化の続きである。「ProGuardによるログの削除」にも使えることがわかった。通常、android.util.Log.v()で出力するログって、デバッグ時にしか使わなかったりする。Javaは条件コンパイルができないので、C/C++のようにassertで書いておいて、リリース時はデバッグ用のコードをなくすといったことができない。
ProGuardは難読化のために、メソッド名を異なる名前にしてしまうことができる。もちろん、呼び出す側と、呼び出される側の両方で変換が行われる。変更ができないAPIとかライブラリについては、変換されない。
ここは、変換して欲しくないな、ということをproguard-project.txtで指定できる。オプションの書き方で特定のメソッド呼び出しを「無かったこと」にもできるというのである。
どうするかというと、proguard-project.txtに以下のような設定を行うと、Log.vが削除される。
これは、やってみなくては。
APKをエクスポートする際に、Log.vが残っていても自動で消してくれる。
しかし、本来の用途はそういうことじゃないらしい。そんなトリッキーなことしていいのか?
assume no side effectsなんだろうな。ちょっと検索。
http://d.hatena.ne.jp/m12i/20110410/1302406778
から抜粋
-assumenosideeffects class_specification
(値を返す以外の)副作用のないメソッドを指定します。最適化のステップでは、ProGuardは返値が利用されていないと判断できるメソッドの呼び出しを削除しれます。ProGuardは入力クラスファイルのコードを解析し、そのようなメソッドを自動で発見します。しかしこの解析はライブラリコードに対しては実施されません。このため-assumenosideeffects オプションが活きていきます。例えば、無駄なメソッド呼び出しをすこしでも減らすため、System.currentTimeMillis() メソッドをこのオプションを使用して指定するといったことができます。ProGuardはこのオプションを、指定されたメソッドの呼び出し階層のすべてにわたって適用することに注意してください。最適化処理においてのみ有効なオプションです。一般的に、憶測でことをなすのは危険なことです。(訳者:このオプションの使い方を誤ると)容易にコードを破壊できてしまうのです。自身が何をしているかを理解している場合にのみ、このオプションを使用してください。
なるほど。ProGuardは最適化も行ってくれる。最適化で「意味の無いメソッド呼び出しを削除する」というのが本来の目的なのか。
Log.v()は戻り値で何かを返すのか?
intを戻すようである。voidじゃなかったのか。
だとすると、やらないとは思うが、戻り値を受けるような呼び出しのLog.vは削除されないっていうことだよね。
本当にそうか。ちょっと試してみよう。
上記のコードを書いて、APKを署名付きでエクスポート。
proguard/mapping.txtとかを見ても削除されたかどうかわからないなぁ...
APKの逆コンパイル
そこで、リバースエンジニアリングしてみることに。まず、APKファイルは、ZIPで圧縮されているので、拡張子をzipに変えて、展開してしまう。なんかエラーになるが、classes.dexはできた。
classes.dexをdex2jarにかけて、jarファイルに変換。
jarファイルをjar xvfで展開。
jadでclassファイルを逆コンパイル。
ん、a.classとかb.classになっているのは難読化の結果か。a.javaを見ると、フィールドa,b,cが並んでいる。そうか、こんな感じで難読化されるのね。でも、読みにくいけれど、全く読めないわけでもない。
えーと、Log.vはどうなったかな?
あれ、あるじゃん。
最適化オプションが無効になっているのか。
ProGuardの最適化は、Dalvik VMじゃあ通用しないっていうこと?
全体に対して適用するのは危険そうなのでやめた。
できるかどうかのテストのために、proguard-project.txtの方に元のproguard-android.txtの内容をコピーしてきて、-dontoptimizeのところをコメントアウト。
project.propertiesのproguard.configはproguard-project.txtのみに変更。
APKファイルをエクスポートして、zipで展開して、jarに変換して、展開して、jadにかけて、とけっこうやることがいっぱい。で、どうなったかなぁ...
おお、なくなった。けど、引数の部分は残っている。
戻り値を見ている方は削除できない。これは予想通り。2行目は、引数のところは何かしらの演算が行われているので、削除したらだめ、という判断で残されているのか。
他の部分にあった、Log.vは消えている。単純に文字列しか渡していないから。
でも、普通のデバッグ用のログには変数の値をくっつけて出すよなぁ。これじゃあ、中途半端。
最適化のパスを増やせばいいのかも?
よくわからないが、ProGuardでログを消すのは危険っぽいので止めておこう。
関連記事
サイト内を検索
ProGuardは難読化のために、メソッド名を異なる名前にしてしまうことができる。もちろん、呼び出す側と、呼び出される側の両方で変換が行われる。変更ができないAPIとかライブラリについては、変換されない。
ここは、変換して欲しくないな、ということをproguard-project.txtで指定できる。オプションの書き方で特定のメソッド呼び出しを「無かったこと」にもできるというのである。
どうするかというと、proguard-project.txtに以下のような設定を行うと、Log.vが削除される。
-assumenosideeffects class android.util.Log { public static int v(...); }
これは、やってみなくては。
APKをエクスポートする際に、Log.vが残っていても自動で消してくれる。
しかし、本来の用途はそういうことじゃないらしい。そんなトリッキーなことしていいのか?
assume no side effectsなんだろうな。ちょっと検索。
http://d.hatena.ne.jp/m12i/20110410/1302406778
から抜粋
-assumenosideeffects class_specification
(値を返す以外の)副作用のないメソッドを指定します。最適化のステップでは、ProGuardは返値が利用されていないと判断できるメソッドの呼び出しを削除しれます。ProGuardは入力クラスファイルのコードを解析し、そのようなメソッドを自動で発見します。しかしこの解析はライブラリコードに対しては実施されません。このため-assumenosideeffects オプションが活きていきます。例えば、無駄なメソッド呼び出しをすこしでも減らすため、System.currentTimeMillis() メソッドをこのオプションを使用して指定するといったことができます。ProGuardはこのオプションを、指定されたメソッドの呼び出し階層のすべてにわたって適用することに注意してください。最適化処理においてのみ有効なオプションです。一般的に、憶測でことをなすのは危険なことです。(訳者:このオプションの使い方を誤ると)容易にコードを破壊できてしまうのです。自身が何をしているかを理解している場合にのみ、このオプションを使用してください。
なるほど。ProGuardは最適化も行ってくれる。最適化で「意味の無いメソッド呼び出しを削除する」というのが本来の目的なのか。
Log.v()は戻り値で何かを返すのか?
intを戻すようである。voidじゃなかったのか。
だとすると、やらないとは思うが、戻り値を受けるような呼び出しのLog.vは削除されないっていうことだよね。
int rc = android.util.Log.v("tag", "ProGuardで削除されない"); android.util.Log.v("tag", "ProGuardで削除される" + rc);
本当にそうか。ちょっと試してみよう。
上記のコードを書いて、APKを署名付きでエクスポート。
proguard/mapping.txtとかを見ても削除されたかどうかわからないなぁ...
APKの逆コンパイル
そこで、リバースエンジニアリングしてみることに。まず、APKファイルは、ZIPで圧縮されているので、拡張子をzipに変えて、展開してしまう。なんかエラーになるが、classes.dexはできた。
classes.dexをdex2jarにかけて、jarファイルに変換。
jarファイルをjar xvfで展開。
jadでclassファイルを逆コンパイル。
ん、a.classとかb.classになっているのは難読化の結果か。a.javaを見ると、フィールドa,b,cが並んでいる。そうか、こんな感じで難読化されるのね。でも、読みにくいけれど、全く読めないわけでもない。
えーと、Log.vはどうなったかな?
int i = android.util.Log.v("tag", "ProGuardで削除されない"); android.util.Log.v("tag", (new StringBuilder("ProGuardで削除される")).append(i).toString());
あれ、あるじゃん。
最適化オプションが無効になっているのか。
# Optimization is turned off by default. Dex does not like code run # through the ProGuard optimize and preverify steps (and performs some # of these optimizations on its own). -dontoptimize -dontpreverify
ProGuardの最適化は、Dalvik VMじゃあ通用しないっていうこと?
全体に対して適用するのは危険そうなのでやめた。
できるかどうかのテストのために、proguard-project.txtの方に元のproguard-android.txtの内容をコピーしてきて、-dontoptimizeのところをコメントアウト。
# This is a configuration file for ProGuard. # http://proguard.sourceforge.net/index.html#manual/usage.html -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -verbose # Optimization is turned off by default. Dex does not like code run # through the ProGuard optimize and preverify steps (and performs some # of these optimizations on its own). #-dontoptimize -dontpreverify # If you want to enable optimization, you should include the # following: # -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* # -optimizationpasses 5 # -allowaccessmodification # # Note that you cannot just include these flags in your own # configuration file; if you are including this file, optimization # will be turned off. You'll need to either edit this file, or # duplicate the contents of this file and remove the include of this # file from your project's proguard.config path property. 途中省略 # The support library contains references to newer platform versions. # Don't warn about those in case this app is linking against an older # platform version. We know about them, and they are safe. -dontwarn android.support.** -assumenosideeffects class android.util.Log { public static int v(...); }
project.propertiesのproguard.configはproguard-project.txtのみに変更。
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): proguard.config=proguard-project.txt
APKファイルをエクスポートして、zipで展開して、jarに変換して、展開して、jadにかけて、とけっこうやることがいっぱい。で、どうなったかなぁ...
int i = android.util.Log.v("tag", "ProGuardで削除されない"); (new StringBuilder("ProGuardで削除される")).append(i).toString();
おお、なくなった。けど、引数の部分は残っている。
戻り値を見ている方は削除できない。これは予想通り。2行目は、引数のところは何かしらの演算が行われているので、削除したらだめ、という判断で残されているのか。
他の部分にあった、Log.vは消えている。単純に文字列しか渡していないから。
でも、普通のデバッグ用のログには変数の値をくっつけて出すよなぁ。これじゃあ、中途半端。
最適化のパスを増やせばいいのかも?
よくわからないが、ProGuardでログを消すのは危険っぽいので止めておこう。
関連記事
サイト内を検索
2012-05-09 15:02
nice!(0)
コメント(0)
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本
コメント 0