Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践!難読化ガイド

 実践!難読化ガイド

DroidKaigi 2024 登壇資料

みっちゃん

September 13, 2024
Tweet

More Decks by みっちゃん

Other Decks in Programming

Transcript

  1. 自己紹介 • 名前:みっちゃん • 経歴: ◦ 22新卒でSTORES 株式会社に入社 ◦ STORES

    決済 Androidアプリの開発に従事 ◦ Android歴は3年目くらい ◦ DroidKaigiスタッフもしてます X(Twitter): 🔗@mimimi_engineer 2/101
  2. R8の機能を有効にしよう! Android Studio 3.4 または Android Gradle プラグイン 3.4.0 以上を

    使用する場合、デフォルトでR8が難読化ツールとして使われる ですが、先ほど紹介したR8の機能である コードとリソースの圧縮・最適化・難読化が有効になっている訳ではない! 💡R8を有効にするとアプリサイズは小さくできるがビルドに時間がかかってしまうので、本番用 リリースなど適切なタイミングで有効にする必要があるため 26/101
  3. ProGuard設定ファイルの使い分け • proguard-android-optimize.txt • proguard-rules.pro • consumerProguardFiles 'proguard-consumer-rules.pro' ◦ SDK開発の時に使う

    ◦ 開発者が自分でファイルを作成する ◦ SDK利用者がアプリをビルドする際に適用されてほしい難読化ルール がある場合にこのファイルにルールを書く 34/101
  4. Androidアプリ開発の歴史 • 2019年くらいまで:ProGuardというツールで難読化してた • Android Gradle Plugin 3.3.0 :R8が導入された •

    Android Gradle Plugin 3.4.0:R8がデフォルトになる つまり、昔はProGuardだったけど、今はR8で難読化している 36/101
  5. Proguardの時代 Javaバイト コード ソースコード DEXファイル コンパイル ProGuard (難読化) D8 (DEX化)

    Kotlin/Javaファイル をAndroid環境で 実行可能な形式に 変換圧縮したもの 38/101
  6. 新時代:R8の時代 Javaバイト コード ソースコード DEXファイル コンパイル R8 (難読化・DEX化) Kotlin/Javaファイル をAndroid環境で

    実行可能な形式に 変換圧縮したもの R8によって難読化・最適化する機能 とDEX化する機能が統合された! ステップ数が減ることでビルド時間短縮につながった!! 💡詳しくはDroidKaigi 2019 SatoShunさんの発表:🔗R8/ProGuard 徹底比較 を参照してください 39/101
  7. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.TestA * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一部にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 51/101
  8. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一部にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 52/101
  9. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一単語にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 53/101
  10. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一単語にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 54/101
  11. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一単語にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 55/101
  12. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一単語にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 56/101
  13. 記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -keep

    class com.example.Test? com.example.Test1 com.example.Test2 * 任意の一単語にマッチする -keep class com.example.* com.example.HogeClass com.example.FooClass ** 任意の一単語にマッチする かつ、パッケージセパレーターも含む -keep class com.example.** com.example.HogeClass com.example.subpackage.FugaClass … 任意の数の任意の型の引数にマッチ -keep class com.example. * { public void hogeMethod(...); } hogeMethod() hogeMethod(a: Int) hogeMethod(a: String, b: Int) <init> すべてのコンストラクタにマッチ -keep class MyClass { @javax.inject.Inject <init>(...); } MyClassにある全てのコンストラクタ <field> すべてのフィールドにマッチ -keep class MyClass { public <fields>; } MyClassにある全てのpublicなフィール ド <method> すべてのメソッドにマッチ -keep class MyClass { public <method>; } MyClassにある全てのpublicなメソッド 57/10 1
  14. 5. 情報収集できる場所を知ろう! • やはり公式ドキュメント、特にUsageのパートは便利です ◦ 🔗ProGuard manual Configuration_Usage • GUARDSQUARE社のブログ

    ◦ 🔗Keep Rules in the Age of Kotlin • PlaygroundもKeepルールを楽して考える上でとても便利 ◦ 🔗ProGuard Playground 71/101
  15. • PlayGroundも難読化ルールを実践的に勉強する上でとても便利 ◦ 🔗ProGuard Playground 72/101 5. 情報収集できる場所を知ろう! ⚠ 仕事で開発しているアプリを

    アップロードする場合は、自社のセ キュリティポリシーと、サービスの セキュリティポリシーをご確認の上 ご利用お願いします
  16. 3. Firebase Crashlyticsを確認する Firebase Crashlyticsを使っている場合は、 Crashlytics Gradle プラグインがビルドプロセス中に 自動的にmappingファイルをアップロードし、それを元に アプリのスタックトレースの難読化を解除し

    人が読める形式のコードにしてクラッシュレポートを出力してくれる! 💡デフォルトでそうなってるので、回避したい場合はbuild.gradleでmappingFileUploadEnabled=falseにする 🔗Firebase_Crashlytics ダッシュボードで読み取り可能なクラッシュレポートを取得する 84/101
  17. @Keepアノテーションとは Hoge.kt const val HOGE = “hoge” proguard-rules.pro -keep class

    Hoge { public static String HOGE; } 定数HOGEを難読化 回避したい場合 proguard-rules.pro に-keepをする 86/101
  18. @Keepアノテーションとは Hoge.kt const val HOGE = “hoge” proguard-rules.pro -keep class

    Hoge { public static String HOGE; } Hoge.kt const val HOGE = “hoge” @Keep 定数HOGEを難読化 回避したい場合 難読化を回避したいとこ ろに直接@Keepをつける ことでも回避できる 87/101
  19. なぜライブラリコードで@Keepは使わない方が良い? 91/101 ライブラリのコードが難読化されるポイントは2つ 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //

    公開API val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ レジ アプリ ビルド 開発者に使ってもらうために 難読化せずに配布したい 開発者がアプリをビルドする際 にはもう難読化されていい
  20. なぜライブラリコードで@Keepは使わない方が良い? 93/101 ライブラリのコードが難読化されるポイントは2つ 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //

    公開API @Keep val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ レジ アプリ ビルド @Keepを使って 難読化回避しちゃうと・・・ ビルド後のライブラリにも @Keepの影響が残ってしまう!! ❌難読化・最適化
  21. なぜライブラリコードで@Keepは使わない方が良い? 94/101 このケースを回避するため公式はProGuard設定ファイルでのルール指定を推奨してる 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //

    公開API val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ ProGuard設定ファイルを使って 難読化回避していれば・・・ 1のタイミングのProGuard設定ファイル は2のタイミングでは参照されない! proguard -keep … { brand } ⭕難読化・最適化 レジ アプリ ビルド
  22. 参考文献 98/101 • Qiita_Proguard設定のメモ_keepオプション_@boohbah (Jumpei Yamamoto) https://qiita.com/boohbah/items/7372b29637d28e6d671c#keep%E3%82%AA%E3%83%97% E3%82%B7%E3%83%A7%E3%83%B3 • Keep

    Rules in the Age of Kotlin https://www.guardsquare.com/blog/keep-rules-in-the-age-of-kotlin • R8/ProGuard 徹底比較 https://satoshun.github.io/2019/02/droidkaigi-2019/
  23. 参考文献 • Android Gradle プラグイン 3.4.0(2019 年 4 月) https://developer.android.com/build/releases/past-releases/agp-3-4-0-release-notes?hl=ja

    • ProGuard manual_Usage https://www.guardsquare.com/manual/configuration/usage • AndroidDeveloper_D8 https://developer.android.com/tools/d8 99/101
  24. 参考文献 • AndroidDeveloper_Development considerations for library modules https://developer.android.com/studio/projects/android-library#Considerations • Kotlinドキュメント_AnnotationRetention

    https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-annotation-retention/ • AndroidDeveloper_R8_retrace https://developer.android.com/tools/retrace?hl=ja 100/101