hyoromoのブログ

最近はVRSNS向けに作ったものについて書いています

ProGuardを使っての難読化方法

ProGuardJavaコードを最適化/難読化するためのツールです。apkのサイズが少なくなったり、apk解析された際に解析され難くなります。
今回は難読化のさせ方がよく分からなかったので整理しておきました。
なお、ProGuardのバージョンは4.5を使用しています。

前準備

ProGuardの導入に関しては他ブログを参照ください。
公式のここここのブログの方が綺麗に導入方法をまとめています。

一応ハマりポイントだけまとめておきます。

  • ADTをr7に上げる
  • proguard.dirはProGuardのlibまでのパス
  • androd-sdk内のantファイルでjavacタグ内のencodingをUTF-8にする

プロジェクト内にて以下のコマンドを実行すると正常に処理されたら準備OK。

$ ant release

ここまでで出来ている必要があるもの。

  • obf以下にdump.txtやmapping.txtが作成されているか。
  • bin以下にant実行後に作成されたxxxx-release.apkファイルがあるか。

難読化すべきモノとすべきでないモノ

難読化できるモノとしてクラス・メソッド・変数を難読化させる事ができます。それぞれ元の名前から一文字名となり、名前から機能を推測され難くなります。
名称が変更されるため難読化してはいけないモノが出てきます。
以下のモノは難読化してしまうとエラーになってしまう代表的なモノとなります。

  • AndroidManifest等に記載する必要のあるActivity/Service名
  • リフレクション
  • JNI
  • JavaScriptからコールされるJavaメソッド
  • layout.xmlに定義したonClickメソッド

これら以外にも難読化させてはいけないモノがあると思われますので、意識して難読化させてください。

難読化させない方法

Android公式ブログの手順書を元にProGuardを導入した場合、procfg.txtを追加したかと思います。
難読化させないモノはこちらに追記していく形を取ります。
まずは初期設定を見ていきます。

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
    native <methods>;
}
-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet); 
}
-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet, int); 
}
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

...はい。サッパリ意味が分からないと思いますので、よく使われるオプションを表にまとめました。

名前 意味
optimizationpasses 最適レベル。数値を高くすればするほど最適化される。
dontusemixedcaseclassnames クラス名を大小区別せずに変更する。
dontskipnonpubliclibraryclasses publicでないライブラリクラスを対象から外す
dontpreverify iモードアプリのサイズ削減用ライブラリ?を使用しない
verbose ログを出力
optimizations 最適化対象
keep 該当クラスと該当メソッドを難読化しない
keepclassmembers 該当クラス内の該当メソッドを難読化しない
keepclasseswithmembernames ???*1

基本的に弄るオプションはkeep系くらいだと思います、その他のオプションは私もあまり弄らないので説明が怪しいです...
説明がよく分からなかったり、他のオプションを知りたい方は公式を参照ください。

指定クラス名を難読化させない方法

このように書くと hoge.hyoromo.TestActivity のクラス名は難読化されません。

-keep public class hoge.hyoromo.TestActivity
特定クラス名を難読化させない方法

このように書いても hoge.hyoromo.TestActivity のクラス名は難読化されません。

-keep public class hoge.hyoromo.*

ただし、サブディレクトリ先までは適用されないため hoge.* だと難読化されてしまいます。

メソッドを難読化させない方法

このように書くと hoge.hyoromo.TestAvtivity のクラス名内の該当メソッドが難読化されません。

-keepclassmembers public class hoge.hyoromo.TestActivity {
	<methods>;
}

メソッド名もクラスメイト同様に指定も出来ますし、特徴を設定していくことも可能です。

これはデフォルトで設定すべきkeep設定

-keep public class * extends android.app.Activity{
    public void *(android.view.View);
}
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}
-keep public class **.R

Activity名を入れていますが、これはActivity以外にもXML側にクラス名を記載する場合は必ず設定してください。
layoutでViewを使用した際にViewのインスタンスメソッド名が紐付けるKeyとなるため、難読化させないよう対応が必要です。
また、その他のSurfaceViewや独自Viewも同じように言えるため、Viewを使用する際は追加するのが無難かと思います。

難読化された事の確認方法

obf/mapping.txt に何がどう変わったかがリスト化されています。
mapping.txtのリストとprocfg.txtのkeep内容を確認しながら難読化の作業をしていく事になります。

*1:keepclassmembersとの違いが不明