Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
実践!難読化ガイド
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
みっちゃん
September 13, 2024
Programming
4.1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
実践!難読化ガイド
DroidKaigi 2024 登壇資料
みっちゃん
September 13, 2024
More Decks by みっちゃん
See All by みっちゃん
Do you use git worktree?
mitchan
0
120
Android値受け渡し大全 〜 設計を制する者が「渡す」を制す 〜
mitchan
1
7.1k
2024年にチャレンジしたことを振り返るぞ
mitchan
0
300
DroidKaigi初めて登壇したレポ
mitchan
1
260
「実践!難読化ガイド」事前予告編
mitchan
0
320
画面遷移 〜iOSとAndroid〜
mitchan
0
420
パソコン音痴な私がモバイル開発界隈でぬくぬく成長している理由
mitchan
0
600
ドキュメントから adbコマンドの仕組みを読み解く
mitchan
1
400
2024年は難読化と仲良くなりたい
mitchan
0
430
Other Decks in Programming
See All in Programming
AIチームを指揮するOSS「TAKT」活用術 / How to Use “TAKT,” an OSS Tool for Orchestrating AI Teams
nrslib
6
860
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
5
3.5k
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
20
6.4k
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
190
TAKTでAI駆動開発の品質を設計する
j5ik2o
6
1.1k
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.6k
JavaDoc 再入門
nagise
0
310
Contextとはなにか
chiroruxx
0
280
Composerを使ったサプライチェーン攻撃の様子を眺めてみる #phpstudy
o0h
PRO
2
240
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
250
Webフレームワークの ベンチマークについて
yusukebe
0
150
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.4k
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
220
A Modern Web Designer's Workflow
chriscoyier
698
190k
How STYLIGHT went responsive
nonsquared
100
6.2k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
230
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
160
Marketing to machines
jonoalderson
1
5.4k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
960
Leo the Paperboy
mayatellez
7
1.8k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.7k
Transcript
実践!難読化ガイド 登壇者:STORES 決済 Androidエンジニア みっちゃん
自己紹介 • 名前:みっちゃん • 経歴: ◦ 22新卒でSTORES 株式会社に入社 ◦ STORES
決済 Androidアプリの開発に従事 ◦ Android歴は3年目くらい ◦ DroidKaigiスタッフもしてます X(Twitter): 🔗@mimimi_engineer 2/101
本日のゴール • 難読化ナニモワカラナイな人が、なんとなく難読化の雰囲気を 分かった気持ちになれる • 業務で難読化関連のエラーが起きた時に、全メンバーがある程度自力 で立ち向かえる 3/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールを自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 4/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 5/101
難読化ってなに? wikipediaさん 🔗Wikipedia_難読化 6/101
難読化ってなに? wikipediaさん 変数や関数名を意味のない文字列に変換するなどして コードを読みにくくするよ! wikipediaさん 🔗Wikipedia_難読化 7/101
難読化の目的 一般的な回答 ソースコードを読みにくくすることで 悪意のある第三者や攻撃者からプロダクトを守るため 8/101
難読化の目的 逆コンパイル! ハッキング! 機密情報を盗むぜ! システムを破壊するぜ! 読めん!やめる! ぴえん 難読化されたコード 9/101
Androidアプリ開発においてもそうなの? 10/101
公式ドキュメントが説明する難読化の目的 Android Developerを確認してみましょう コードを読みにくくして、悪意のある第三者からコードを守ると いうより、単に文字数を減らしてアプリサイズを小さくするとい うのが目的として大きいのかも 🔗AndroidDeveloper_コードの難読化 11/101
実際に難読化されたソースコードを見てみよう 確かに。めっちゃ文字数 少なくなりそう。 個人的には十分読みにくいのでソース コードを守る役割にもなってそうとも 思う。 🔗AndroidDeveloper_コードの難読化 12/101
どれくらいアプリサイズを小さくできるの 🔗Android Developer_Impact on runtime performance 難読化関連の設定を全て有効にすることで 実行時パフォーマンスが最大30%改善される くらいには小さくできるらしい 13/101
本当に難読化しないといけないのか アプリサイズが小さくなるのってそんなありがたいの? → インストール成功率への影響, Google Playにはサイズ上限がある 🔗 Play Console ヘルプ
14/101
結論:商用アプリ・大規模アプリは難読化しよう • 難読化によってコードが読みにくくなり、ソースコードが攻撃者から 守られる • AndroidDeveloperでは上記のことに言及していないが、難読化によって アプリサイズが小さくなる • アプリサイズはAndroid開発者にとってパフォーマンスや インストール成功率の観点などから重要
15/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 16/101
難読化を行うためのツール R8! Androidでは、R8(あーるえいと)というツールが難読化を実行してくれる ここまで難読化にだけ言及してきたが、R8は難読化以外のこともやってくれる 17/101
R8がやってくれる、4つのコト。 • コードの圧縮 • リソースの圧縮 • 最適化 • 難読化 18/101
1. コードの圧縮 ツリーシェイキングとも言われる エントリポイントからアプリのコードを検査し、到達不可能と判断した ソースコードを削除する つまり、 これ絶対実行されないし! ていうソースコードを削除してくれる! 削除 19/101
2. リソースの圧縮 • 使用されていない画像やstringリソース・レイアウトxmlなどを削除する • リソースの圧縮はコードの圧縮と連動して実行される 削除 削除 コードの圧縮によって削除されたコードの 先で参照されていたはずの不要になった
リソース達も削除される 削除 20/101
3. 最適化 深いレベルでコードを検査して必要のないコードを削除 さらにはコードを書き直すまでして冗長なコードをとことん圧縮してくれる コ ー ド を 書 き
直 す ? 21/101
最適化:いらないelseを削除してくれる 22/101
最適化:使用箇所が限定的なメソッドをインラインで配置 calculate()からしか呼ばれてない 23/101
最適化:使用箇所が限定的なメソッドをインラインで配置 処理を直接配置 24/101
4. 難読化 先に述べた通り、クラス名やメソッド名を適当な文字に変換してくれる 25/101
R8の機能を有効にしよう! Android Studio 3.4 または Android Gradle プラグイン 3.4.0 以上を
使用する場合、デフォルトでR8が難読化ツールとして使われる ですが、先ほど紹介したR8の機能である コードとリソースの圧縮・最適化・難読化が有効になっている訳ではない! 💡R8を有効にするとアプリサイズは小さくできるがビルドに時間がかかってしまうので、本番用 リリースなど適切なタイミングで有効にする必要があるため 26/101
R8の機能を有効にしよう! Groovy build.gradle 27/101
難読化の設定に関わるファイル さっきのサンプルコードに次のような記述があったかと思います → 難読化の設定に関わるファイル ってなんだ?? 28/101
難読化の設定に関わるファイル 難読化の設定を有効化すると全てのファイルのクラス名・メソッド名が めちゃくちゃな名前に書き換えられてしまう そうすると参照先をうまく見つけられなくなって、ビルドが通らなくなる! e: Unresolved reference 29/101
ProGuard設定ファイル そこで、これは難読化しないでおくれ...という設定を 難読化の設定に関わるファイル(ProGurad設定ファイル)に記述する! 💡Android Studioで新しいモジュールを作成すると IDEがそのモジュールのルートディレクトリに proguard-rules.pro を自動的に作成してくれる! proguard-android-optimize.txtに関しても コンパイル時にAndroid
Gradle Pluginによって自動生成される! proguard-rules.pro -keep class HogeDataClass 訳:HogeDataClassは難読化 しないでね! 30/101
ProGuard設定ファイルの使い分け • proguard-android-optimize.txt • proguard-rules.pro • consumerProguardFiles 'proguard-consumer-rules.pro' 31/101
ProGuard設定ファイルの使い分け • proguard-android-optimize.txt ◦ コンパイル時にAGPによって自動生成される ◦ 単にR8の機能でコードの圧縮や最適化を適用したい場合は このファイルだけを設定していればOK • proguard-rules.pro
• consumerProguardFiles 'proguard-consumer-rules.pro' 32/101
ProGuard設定ファイルの使い分け • proguard-android-optimize.txt • proguard-rules.pro ◦ モジュール作成時Android Studioがルートディレクトリ下に自動生成 ◦ 特定のクラスを難読化対象から除外したいなど、難読化ルールを
カスタムしたい場合にこのファイルにルールを書く • consumerProguardFiles 'proguard-consumer-rules.pro' 33/101
ProGuard設定ファイルの使い分け • proguard-android-optimize.txt • proguard-rules.pro • consumerProguardFiles 'proguard-consumer-rules.pro' ◦ SDK開発の時に使う
◦ 開発者が自分でファイルを作成する ◦ SDK利用者がアプリをビルドする際に適用されてほしい難読化ルール がある場合にこのファイルにルールを書く 34/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 35/101
Androidアプリ開発の歴史 • 2019年くらいまで:ProGuardというツールで難読化してた • Android Gradle Plugin 3.3.0 :R8が導入された •
Android Gradle Plugin 3.4.0:R8がデフォルトになる つまり、昔はProGuardだったけど、今はR8で難読化している 36/101
何が変わったの? 37/101
Proguardの時代 Javaバイト コード ソースコード DEXファイル コンパイル ProGuard (難読化) D8 (DEX化)
Kotlin/Javaファイル をAndroid環境で 実行可能な形式に 変換圧縮したもの 38/101
新時代:R8の時代 Javaバイト コード ソースコード DEXファイル コンパイル R8 (難読化・DEX化) Kotlin/Javaファイル をAndroid環境で
実行可能な形式に 変換圧縮したもの R8によって難読化・最適化する機能 とDEX化する機能が統合された! ステップ数が減ることでビルド時間短縮につながった!! 💡詳しくはDroidKaigi 2019 SatoShunさんの発表:🔗R8/ProGuard 徹底比較 を参照してください 39/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 40/101
Keepルールの設定を自由自在に操る! ProGuard設定ファイルを使って難読化を回避するルールをカスタムできますが、 ProGuard特有の文法や構文があり、慣れるまでは読むのも書くのもむずかしい。 41/101 proguard-rules.pro -keep class HogeDataClass 訳:HogeDataClassは難読化 しないでね!
Keepルールの設定を自由自在に操る! Keepルールの設定を自由自在に操るための5つのノウハウ • テンプレートを知ろう! • 基本的な-keepの種類を知ろう! • よく出てくる記号の意味を知ろう! • 原理原則に基づいて立ち向かおう!
• 情報収集できる場所を知ろう! 42/101
1. テンプレートを知ろう! 例えばGreetクラスのhelloメソッドを難読化対象から外したいとする 43/101
1. テンプレートを知ろう! そのような場合は次のようなKeepルールを書けば良い 🔗ProGuard Playground 44/101
これをもうちょっと抽象化すると・・・ めっちゃ大体こういう↑感じの文法です。 45/101 1. テンプレートを知ろう!
見比べてみる 46/101 1. テンプレートを知ろう!
関数以外にもクラスやインタフェース、Companion objectなど 場合によって難読化対象から外したいものがたくさんあると思います。 全部説明すると時間がないので、公式が提示している網羅的なテンプレートを置いときます↓ 🔗ProGuardドキュメント _Configuration_Usage_ClassSpecifications 文法がわからなくなった時は テンプレートを確認しよう! 47/101 1.
テンプレートを知ろう!
2. 基本的な-keepの種類を知ろう! -keepで難読化を回避するが、-keepにも種類が色々ある 🔗ProGuardドキュメント_Overview of Keep Options 48/101
もっと分かりやすいのを見ましょう 🔗Qiita_Proguard設定のメモ_keepオプション_@boohbah (Jumpei Yamamoto) 49/101 2. 基本的な-keepの種類を知ろう!
3. よく出てくる記号(ワイルドカード)の意味を知ろう! ここでは、なんとなく読める&書けるようになるための大体の意味を紹介します 💡クラスに付与するかフィールドやメソッドに付与するかで若干意味合いや守備範囲が変わったりするので 詳細は🔗ProGuardドキュメント_Class Specificationsのwild cardのパートを見て下さい 50/101
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
記号 (ワイルドカー ド) 説明 使い方の例 ←の結果 難読化回避されるものの例 ? 任意の一文字にマッチする -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
4. 原理原則に基づいて立ち向かおう! R8はJavaバイトコード上で動作することを覚えておきましょう その原理原則を忘れてしまうと、どうやってKeepルールを書いていいのかわからなくなることがあります Javaバイト コード ソースコード DEXファイル コンパイル R8
(難読化・DEX化) Kotlin/Javaファイル をAndroid環境で 実行可能な形式に 変換圧縮したもの 58/101
59/101 4. 原理原則に基づいて立ち向かおう! 例えばクラス外で宣言された showMessage()を 難読化から回避したい場合
60/101 全てのKeepルールにクラス名は必須だけど クラス内にないんだが?
Javaバイトコードをみる! このような場合は内部的に ファイル名 + Kt(接尾語) という名前のクラスファイル が生成される 61/101
JavaバイトコードからKeepルールを考える 難読化が回避されている!! 62/101
JavaバイトコードからKeepルールを考える 63/101 わからなくなったら原理原則に立ち戻り Javaバイトコードから考えよう!!
コラム!Android Studioでデコンパイルしよっ そうはいっても、 Javaバイトコードを確認するのだるいよ...と思った方もいるでしょう Android Studioの機能で簡単にKotlinコードをバイトコードに変換し、 バイトコードをデコンパイルすることができます 64/101
コラム!Android Studioでデコンパイルしよっ 65/10 1
コラム!Android Studioでデコンパイルしよっ 66/10 1
コラム!Android Studioでデコンパイルしよっ 67/10 1
コラム!Android Studioでデコンパイルしよっ 68/10 1
コラム!Android Studioでデコンパイルしよっ 69/10 1
コラム!Android Studioでデコンパイルしよっ デコンパイル後の Javaコードが簡単に 生成される! 70/101
5. 情報収集できる場所を知ろう! • やはり公式ドキュメント、特にUsageのパートは便利です ◦ 🔗ProGuard manual Configuration_Usage • GUARDSQUARE社のブログ
◦ 🔗Keep Rules in the Age of Kotlin • PlaygroundもKeepルールを楽して考える上でとても便利 ◦ 🔗ProGuard Playground 71/101
• PlayGroundも難読化ルールを実践的に勉強する上でとても便利 ◦ 🔗ProGuard Playground 72/101 5. 情報収集できる場所を知ろう! ⚠ 仕事で開発しているアプリを
アップロードする場合は、自社のセ キュリティポリシーと、サービスの セキュリティポリシーをご確認の上 ご利用お願いします
ProGuard Playgroundがいかに便利か 難読化を回避したい メソッドやクラスを選択 Keepルールを提案してくれる \ 便利すぎてヤバイ / ボタンぽちでルールを 適用してくれる
73/101
ProGuard Playgroundがいかに便利か 書き込んだKeepルールによって どこが難読化回避されるのか アイコンで教えてくれる \ さすがに神 / 74/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化されたクラッシュレポートとの戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 75/101
難読化されたクラッシュレポートとの戦い方 難読化でビルドが通らなくなってしまった原因の場所さえ分かれば あとは先の章で話した通りProGuardのドキュメントを見ながらKeepルールを 追加し、難読化対象から外してあげればいいだけ! 逆にいうと、原因に辿り着くまでの探索が難しい・・・ 76/101
どうして難しい? R8で処理された後のソースコードは 圧縮やコードの最適化を経て行番号が変更されていたり、 難読化によってクラスやメソッドの名前が変更されていたりする →スタックトレースがソースコードと正確に一致せず、原因が追いづらい!! e: /~~/HogeActivity.kt: Unresolved reference: a
どれ?! 77/101
難読化されたクラッシュレポートとの戦い方 この章では難読化が原因でビルドが通らなくなってしまった時の 原因箇所の特定方法について解説します! 78/101
原因を調べる3つの方法 • mappingファイルを活用する • retraceコマンドを活用する • Firebase Crashlyticsを確認する 79/101
1. mappingファイルを活用する R8を有効にしてアプリをビルドすると <module-name>/build/outputs/mapping/<build-type>/ 配下に mapping.txt が生成される ただの対応表 80/101
2. retraceコマンドを活用する mapping.txtはただの対応表なのでこれを頼りに スタックトレース読み解くのはちょっとしんどい mappingファイルを使って 難読化前の状態のスタックトレースを表示する方法もあります! 81/101
retraceコマンドとは? 難読化される前の情報を復元することをリトレースと言う アプリケーションのスタックトレースをリトレースし、原因の場所を特定する! 82/101
retraceコマンドを活用する リトレースに必要な情報を保持しておくために、 以下のルールをproguard-rules.proファイルに追加 💡LineNumberTable: メソッド内の位置情報を保持する, スタックトレースを出力する際に行番号として表示する 💡SourceFile: 全てのjava実行環境(ランタイム)で一貫して行番号情報を出力するために必要 retraceコマンドを叩く 難読化前の命名や行番号でスタックトレースがコマンドラインに表示される
🔗AndroidDeveloper_R8_retrace 83/101
3. Firebase Crashlyticsを確認する Firebase Crashlyticsを使っている場合は、 Crashlytics Gradle プラグインがビルドプロセス中に 自動的にmappingファイルをアップロードし、それを元に アプリのスタックトレースの難読化を解除し
人が読める形式のコードにしてクラッシュレポートを出力してくれる! 💡デフォルトでそうなってるので、回避したい場合はbuild.gradleでmappingFileUploadEnabled=falseにする 🔗Firebase_Crashlytics ダッシュボードで読み取り可能なクラッシュレポートを取得する 84/101
• 難読化とは? • 難読化のツール • コラム!難読化の歴史:ProGuard、そしてR8 • Keepルールの設定を自由自在に操る! • 難読化でビルドが通らなくなった時の戦い方
• よくある質問:@Keepと-keepの違い 本日のお品書き 85/101
@Keepアノテーションとは Hoge.kt const val HOGE = “hoge” proguard-rules.pro -keep class
Hoge { public static String HOGE; } 定数HOGEを難読化 回避したい場合 proguard-rules.pro に-keepをする 86/101
@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
じゃあ全部@Keepすればよくない? 88/101
結論:ライブラリを配布する場合に注意が必要! 🔗Android Developer_Keep 89/101
なぜライブラリコードで@Keepは使わない方が良い? 90/101 ライブラリのコードが難読化されるポイントに着眼してみる 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //
公開API val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ レジ アプリ ビルド
なぜライブラリコードで@Keepは使わない方が良い? 91/101 ライブラリのコードが難読化されるポイントは2つ 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //
公開API val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ レジ アプリ ビルド 開発者に使ってもらうために 難読化せずに配布したい 開発者がアプリをビルドする際 にはもう難読化されていい
@Keepの定義を確認してみる 🔗Android Developer_Keep 🔗Kotlinドキュメント_AnnotationRetention バイナリ(ビルド後のライブラリ)でも アノテーションの効力が保持されるよという意味 92/101
なぜライブラリコードで@Keepは使わない方が良い? 93/101 ライブラリのコードが難読化されるポイントは2つ 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //
公開API @Keep val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ レジ アプリ ビルド @Keepを使って 難読化回避しちゃうと・・・ ビルド後のライブラリにも @Keepの影響が残ってしまう!! ❌難読化・最適化
なぜライブラリコードで@Keepは使わない方が良い? 94/101 このケースを回避するため公式はProGuard設定ファイルでのルール指定を推奨してる 1. ライブラリ開発者がリリースの ためにビルドするとき 2. ライブラリを含んだアプリが ビルドされるとき //
公開API val brand : EmoneyBrand 決済ライブラリ ビルド 決済ライブラリ ProGuard設定ファイルを使って 難読化回避していれば・・・ 1のタイミングのProGuard設定ファイル は2のタイミングでは参照されない! proguard -keep … { brand } ⭕難読化・最適化 レジ アプリ ビルド
まとめ • 難読化とは、クラス名やメソッド名を短くして アプリサイズを小さくすること • R8というツールで、難読化以外にも最適化やリソースの圧縮を実行する 95/101
まとめ • 難読化によってビルドが通らなくなった場合は、 ProGuard設定ファイルにKeepルールを書いて難読化回避しよう • R8は、Kotlinコードではなく、Javaバイトコード上で実行されることを 意識してKeepルールを考えよう • @Keepでも難読化回避は可能であるが、ライブラリ開発の場合はやめよう 96/101
参考文献 • AndroidDeveloper_アプリの圧縮、難読化、最適化 https://developer.android.com/build/shrink-code?hl=ja • AndroidDeveloper_コードの難読化 https://developer.android.com/build/shrink-code?hl=ja#obfuscate • AndroidDeveloper_Keep https://developer.android.com/reference/androidx/annotation/Keep
• Qiita_Androidアプリの圧縮、難読化、最適化【R8】 https://qiita.com/toroncho-good/items/cce0e660caba6a5d4191 97/101
参考文献 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/
参考文献 • 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
参考文献 • 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
おしまい 101/101