Android
gradle
Proguard

ProGuardで-dontoptimizeを使いたくないから作った何か

More than 1 year has passed since last update.

はじめに

結論

https://github.com/nao20010128nao/ProguardTransformFix

はじめに

こんなエラー見たことありませんか?

Unexpected error while computing stack sizes:
  Class       = [com/google/android/gms/internal/zzgr]
  Method      = [run()V]
  Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [166] pop in [com/google/android/gms/internal/zzgr.run()V])
Warning: Exception while processing task java.io.IOException: java.lang.IllegalArgumentException: Stack size becomes negative after instruction [166] pop in [com/google/android/gms/internal/zzgr.run()V]

Support Libraryとかgms更新すると-dontoptimize無しには難読化はできなくなってしまいます。
でも使いたい! 1

もちろん、

-keep class com.google.android.gms.internal.zzgr { *; }

は効きません。
ProGuard改造なんてしてもまともに動かない。

でも結局諦めるしか...と思いつつ考えていたら、ある結論にたどり着きました。

ProGuardを2回やればいいんだ

Transform API

AndroidのGradleプラグインには、Transform APIなるものがあります。2
詳細については省きますが、これにはProGuardで難読化する部分があります。3

優先順位は選べない

GradleにはdependsOnみたいにTaskの順番を左右する指定をすることができますが、Transform APIが崩壊するのは間違いないので使わないことにしました。
また、android.registerTransformはTransformの順番を変えられないので、途中に挿入することは諦めました。

改造

じゃあTransform自体を改造すればいいじゃない!
Transformをなんとか改造すれば、ProGuardを起動するまでの部分に手を加えることができます。
TransformTaskがガバガバだったこともあり4これだ!と一瞬閃きました。
ですがJVMの仕様上、一度読み込まれたクラスは書き換えられません。
書き換えるのではなく、継承をしつつ、TransformTaskにはフィールドの読み書きで対応することにしました。

意外とコピペでもエラーは多く出ないんですね。びっくりしました。

2段階ProGuardって一体何してるの?

1段階目

ユーザーの指定したProGuard設定を少し変更し、入力ファイルの組成を変更して難読化をします。

ProGuard設定の変更内容

-keep class **.R { *; } # 追加
-keep class **.R$** { *; } # 追加

libraryJars
- android.jarと愉快な仲間たち
- jarファイル(大体ライブラリです)
inJars
- ディレクトリのクラスファイル群(大体プロジェクトに属するコードです)

inJarsからライブラリを排除することが重要で、これをすれば難読化ができます。

2段階目

ユーザーの指定したProGuard設定を少し変更し、入力ファイルの組成を再変更して難読化をします。

ProGuard設定の変更内容

-dontoptimize # 追加

libraryJars
- android.jarと愉快な仲間たち
inJars
- 1段階目でのjarファイル(1段階目の時点では変わっていません)
- 1段階目で吐き出したjarファイル

ここまでの時点ですべての入力クラスファイルが全てProGuardによって処理されます。
エラー? 出ませんよ5

最後に

ここまでご覧いただきありがとうございました。
ソースコードはここに置いておいたので、改変するなりPRするなりIssueするなりご自由に。
そしてJitPack.ioって便利ですね



  1. -dontoptimizeの使用が推奨されている模様。仕方ないね 

  2. transformで始まるTaskがTransform APIによるものです。 

  3. https://github.com/nao20010128nao/android-gradle-plugin-source-codes/blob/master/gradle-core-2.4.0-alpha7-sources/com/android/build/gradle/internal/transforms/ProGuardTransform.java 

  4. 面白いことにTransformTasktransformフィールドはfinalではないのでリフレクションで触れるんです。 

  5. 少なくとも投稿者の環境では、ですが。