minifyEnabled true
のとき、実機にインストールして起動した瞬間にクラッシュします。
2022-04-02 23:21:04.099 5125-5125/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.benigumo.example, PID: 5125
java.lang.NullPointerException
at w5.c$c.b(:112)
at w5.c$c.f(:154)
at v5.f.d(:233)
at e7.x$a.c(:46)
at v5.b$a.a(:38)
at e7.x$a.b(:43)
at w5.b$b.a(:25)
at w5.b$b.add(:23)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
難読化されてるので原因を特定するのがツラいです !!
難読化を元に戻してみましょう。
手順
1. R8 ルールを追加する。
proguard-rules.pro に以下を追加することで再構成がより確実になります。
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
2. 再度クラッシュさせる。
ルール追加したので再度クラッシュさせてスタックトレースを出力します。
2022-04-04 02:44:01.054 2001-2001/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.benigumo.example, PID: 2001
java.lang.NullPointerException
at w5.c$c.b(SourceFile:112)
at w5.c$c.f(SourceFile:154)
at v5.f.d(SourceFile:233)
at e7.x$a.c(SourceFile:46)
at v5.b$a.a(SourceFile:38)
at e7.x$a.b(SourceFile:43)
at w5.b$b.a(SourceFile:25)
at w5.b$b.add(SourceFile:23)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
少しスタックトレースが変化しました。
これは、クリップボードにコピーして使います。
3. 難読化を解除して元に戻す。 - retrace
「スタックトレース」 ( クリップボード内 ) と
「マッピングファイル」
( <module>/build/outputs/mapping/<build>/mapping.txt )
をデータとして retrace ツールを実行します。
~/Example
❯ pbpaste | java -jar ~/Library/Android/sdk/tools/proguard/lib/retrace.jar -verbose \
app/build/outputs/mapping/debug/mapping.txt
2022-04-04 02:44:01.054 2001-2001/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.benigumo.example, PID: 2001
java.lang.NullPointerException
at curtains.internal.WindowCallbackWrapper$Companion.android.view.Window$Callback getJetpackWrapped(android.view.Window$Callback)(SourceFile:112)
at curtains.internal.WindowCallbackWrapper$Companion.android.view.Window$Callback unwrap(android.view.Window$Callback)(SourceFile:154)
at curtains.WindowsKt.android.view.Window$Callback getWrappedCallback(android.view.Window$Callback)(SourceFile:233)
at leakcanary.RootViewWatcher$listener$1.void onRootViewAdded(android.view.View)(SourceFile:46)
at curtains.OnRootViewAddedListener$DefaultImpls.void onRootViewsChanged(curtains.OnRootViewAddedListener,android.view.View,boolean)(SourceFile:38)
at leakcanary.RootViewWatcher$listener$1.void onRootViewsChanged(android.view.View,boolean)(SourceFile:43)
at curtains.internal.RootViewsSpy$delegatingViewList$1.boolean add(android.view.View)(SourceFile:25)
at curtains.internal.RootViewsSpy$delegatingViewList$1.boolean add(java.lang.Object)(SourceFile:23)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
全クラス名が丸出しになりました !!
👉 R8 retrace | Android デベロッパー | Android Developers
4. Android Studio でスタックトレースを分析します。
[Analyze] - [Stack Trace or Thread Dump ...]
ソースファイルへのクリック可能なリンクも表示されています。
👉 スタック トレースを分析する | Android デベロッパー | Android Developers
ちなみに、今回のクラッシュの原因は、Leakcanary まわりでした !
LeakCanary 2.8.1 アプリ起動時に NullPointerException
👉 https://t.co/L3ywACrBfB #Android #プログラミング— chanzmao (@maochanz) April 3, 2022
Proguard GUI
同様に、同梱の Proguard GUI を使っても retrace はできます。
java -jar ~/Library/Android/sdk/tools/proguard/lib/proguardgui.jar
GUI上でマッピングファイルのパスを入力して、スタックトレースは貼り付けます。
日本語
さて、「難読化 (obfuscation)」の反対語は何なのでしょうか。
英語だと
「deobfuscation」
ですが、公式リファレンスの日本語を見ているといろいろな単語が使われています。
「デコード」
「難読化の解除」
「解読」
日本語は難しいですね !
まとめ
proguard-rules.pro に追記して、コピーしたスタックトレースを2つのツールどちらかで retrace する。
# To de-obfuscate stacktrace
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
pbpaste | java -jar ~/Library/Android/sdk/tools/proguard/lib/retrace.jar -verbose \
app/build/outputs/mapping/debug/mapping.txt | tee >(pbcopy)
java -jar ~/Library/Android/sdk/tools/proguard/lib/proguardgui.jar
alias にしとくと良いでしょうか。
👉 アプリの圧縮、難読化、最適化 | Android デベロッパー | Android Developers
👉 【Android Studio】プラグイン「Proguard Retrace Unscrambler」で難読化されたスタックトレースを解除してみた