Proguard

Androidを実務で使ってていろいろ知識がたまってきたので吐き出しておく。

 

その1ってことでProguardを解説

 

元々、ProguardはJavaの.classファイルに対するサイズ圧縮(shrinker)/難読化(obfuscator)/最適化(optimizer)のツール。

preverifierっていまいち最適化と違いが判らん。

 

Android標準のSDKにもついていて色々できる。

ただ、注意すべき点も多いのでその辺をまとめてみた。

 

基本的な使い方を解説しつつ、

自分がいろいろやりたかったのを紹介しながらケーススタディしてく。

 

基本的な使い方

以下はADTr21を前提に。

Ant/Eclipse共通で、Androidプロジェクトの

project.propertyの中にこんな記述がある。

# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

 

ここの記述の通り、このコメントアウトを外せば実行できる。で、デフォルトで読み込むのは

SDKの中に入っているファイルと、

ローカルに置いてあるproguard-project.txt

後者は新しいADTで作っていればすでにできてるはず。

 

中身はこんな感じのやつ

# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

 

デフォルトは何もしてくれないけどね。

 

こんな感じで設定をONにしたら

Eclipseでコンパイルする場合は、Exportをしてる場合に

Antではreleaseコンパイルするとき(だよね?)にproguardを実行してくれる。

 

で、mapping.txtってのは必ず取っておかないと

後々困るのでちゃんととっておきましょう。

 

ケース0 そもそも難読化する必要あるのか

まずそもそも、Androidのapkファイルは危ないとかきくけど

何もしてないapkがどれくらい危ないのか調べてみた。

 

使ったのはapktool

http://code.google.com/p/android-apktool/

解析したのはgithubにもあげてるこいつ

https://github.com/mitsuo0114/Calculator

 

解析してみるといくつかわかることがある。

1.XMLの中身は全然なにもいじられない

ちょっとぐらい何かしてくれてもよいじゃんとは思うけど。

とにかくもこの辺りはProguardは何もしてくれないので

OAuthのクライアントトークンとか、社内専用のサーバとかそういうのはXMLに書いちゃダメです。誰も何もしてくれないし検索も簡単。

ちなみにlib-projectのresファイルもそのままコピーされるみたいで

これがコンフリクトの原因みたいね。

 

逆に言えば、解析すればintentfilterとかexported属性もわかってしまうので

めちゃめちゃ脆弱性になりうるので気を付けてください。

 

2.クラス名はそのまま

解析したapkのTreeを出すとこんな感じ

├─res
│  ├─drawable-hdpi
│  ├─drawable-mdpi
│  ├─drawable-xhdpi
│  ├─layout
│  ├─menu
│  └─values
└─smali
    ├─android
    │  ├─annotation
    │  └─support
    │      └─v4
    │          ├─accessibilityservice
    │          ├─app
    │          ├─content
    │          │  └─pm
    │          ├─database
    │          ├─net
    │          ├─os
    │          ├─util
    │          ├─view
    │          │  └─accessibility
    │          └─widget
    └─jp
        └─fedom
            └─android
                └─calculator
                    ├─activity
                    └─logic

これだけでもわかるように、ドメイン周りはバレバレだしクラス構成もよくわかる。

└─logic
        BasicArithmeticOperation$1.smali
        BasicArithmeticOperation$Operator.smali
        BasicArithmeticOperation.smali

中身はこんな感じになってる。

$Operationはinnerクラスのenumで、どこにどんなことが書いてあるか

正しい名前を付ければつけるほど簡単にわかっちゃう。

 

さらにソースコードと合わせて内部を見てみる

BasicArithmeticOperation.java

BasicArithmeticOperation.smali

 

変数名も宣言ももうバレバレもいいところ。

 

というところで難読化を始めようと思う。

 

ケース1 普通に難読化したい。

一番最初に解説したとおりにとりあえずそのまま難読化してみるとこうなる。

D:.
├─res
│  ├─drawable-hdpi
│  ├─drawable-mdpi
│  ├─drawable-xhdpi
│  ├─layout
│  ├─menu
│  └─values
└─smali
    ├─android
    │  └─support
    │      └─v4
    │          ├─a
    │          ├─app
    │          ├─b
    │          ├─c
    │          ├─d
    │          └─view
    └─jp
        └─fedom
            └─android
                └─calculator
                    ├─a
                    └─activity

XML周りはそのまま、パッケージ名のうち、おそらくアプリとして必要なパッケージはのこる。

さらにファイル名はこんな感じ

├─a
│      a.smali
│      b.smali
│      c.smali

└─activity
        a.smali
        b.smali
        c.smali
        d.smali
        e.smali
        f.smali
        g.smali
        h.smali
        i.smali
        j.smali
        k.smali
        l.smali
        m.smali
        MainActivity.smali
        n.smali
        o.smali
        p.smali

Actvityは残るけれど、ほかはきれいに難読化されてる。

さっきのところはこんな感じ

 

 

enumの宣言はどこか別に置かれて、landの型はわかるけどそれ以外はなかなか読み取りにくくなってる。

 

ただし、ソースコードにべた書きしてある文字列は消えない

元のソース

proguard後

 

“0”がそのまま残ってしまってる。まぁそりゃそうだよね。

 

で、動作は当然問題なし。定数を消す必要がなければとりあえずこれでもよいかな。

 

ちなみになんでActivityは残ってるのかというと、Android用にそういう設定がされてるから。

最初の設定のこれ

${sdk.dir}/tools/proguard/proguard-android.txt

 

中身はこんなのが書かれてる。

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

この辺りがいろいろしてくれてるっぽい。

xmlからのonClickとかもクラス名が変わらないようになってるみたいね。

 

 

 

 

ケース2 メソッド/使ってない文字列消したい

 

さて、次。よくある話。Logメソッド消したい。あるいは文字列消したい。

最近のAndroidの脆弱性の話の半分ぐらいは出しちゃいけないLog情報を出しちゃったって話ばっかり。

ご多分に漏れず自分もそういうことをしたくて、初めはAndroidのLogクラスをラップして出さないようにしてたんだけれどもあることに気が付く。

「メソッド呼び出ししてる段階で文字列でてんじゃん」

 

WrappedLogger.v(TAG, “#additionalReading : append more”);

例えばこういうことやるじゃん。この文字列は消えないので呼び出し上はProguardをかけても残ります。

このログの文字は結構重要な場合が多くて変数の中身を見つけられなくてもどういう重要な情報が集まるかがわかっちゃう。

 

また、セキュリティとしてはこの辺は削っておくべき。というのも

残ってるってことはセキュリティに甘いって思われるから。

そしたらほかの部分をあたったりしてしまうんだよね。

 

さて話が長くなった。

この場合、いろいろなところを調べるとproguardの設定の中に以下を追加しろ

って出てくる。

-assumenosideeffects class android.util.Log {

public static *** v(...);

public static *** d(...);

public static *** i(...);

public static *** w(...);

public static *** e(...);

}

ただ、これだけでは動かないので、SDKに含まれてるもう一つのProguardサンプルである

proguard-android-optimize.txt

のほうに切り替えてあげる必要がある。

 

元々のほうには

-dontoptimize
この設定があるのだが、どうもこれがあると削除工程が入らないっぽい。

また、この設定を消すだけだと具合が悪いらしいので先ほどのサンプルをコピーするかちゃんと読み込んであげること。

 

こっちには、具合悪い設定が排除されるようになってる。

-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

 

これは3時間ぐらいはまった。でも実はちゃんと書いてある

# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# “proguard-android-optimize.txt” file instead of this one from your
# project.properties file.

ちゃんと英語は読みましょう。

 

で、これをやると、メソッドそのものと、メソッド呼び出しをしているところがすべて消える。

こええよと思うけど消える。

 

これはメソッドそのままごりっと消せるので

このメソッドの中だけに書いておく文字列はごりっと消える。

呼び出してる行もそのまま消える。だから基本的にはvoidの関数だけにやるのがよい、

 

 

社内だけのプロキシホスト名とかそういうのは消えるところでそのまま書いてあげれば

これによってごりっと消してくれる。

 

動作自体はよくわからんのでちゃんとテストはしましょう。

 

ってことでAndroidのapkセキュリティの話でした。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です