Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

crash with NoSuchMethodError after proguard with method references

the source code before compile&proguard :

public class IntentSession extends BaseIntentSession {
    @Override
    public void onResume() {
        super.onResume();
        mExecutor.exec(getIntent(), this::finish);
    }
}

the decompiled code after compile&proguard : (Decompiled with CFR 0_118)

public class a extends superA {

    public void e() {
        super.e();
        this.c.a(this.j(), b.a((a)this)); // the problematic code here
    }
}

now is the key code after compile&proguard, the b class's decompiled code :

final class b implements c.a {
    private a a;

    b (a a1) {
        this.a = a1;
    }

    static /* synthetic */ b a(final a a) {
        return new b(a);
    }

    @LambdaForm.Hidden
    public void a() {
        this.a.finish();
    }
}

it still referenced the finish() method which was already obfuscated as m() by proguard.

I expect the reference finish() method to be obfuscated as m(), but this is not what is happening, and that's my question.

Proguard didn't warn me, it only crashes with NoSuchMethodError on runtime once it hits the wrong code. So don't tell me to add a proguard configuration like -dontwarn java.lang.invoke.* which i tried but it did not work.

Maybe the handling sequence of the involved classes were wrong during obfuscation, who know?

I don't want to add the @Keep annotation on the finish() method, it's a bad solution and I would have to worry about it and carefully use method references in the future, so I'm looking for the best solution.

below are my gradle configurations :

dependencies {
    classpath 'com.android.tools.build:gradle:2.2.3'
    classpath 'me.tatarka:gradle-retrolambda:3.4.0'
    classpath "com.fernandocejas.frodo:frodo-plugin:0.8.3"
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

Below is my proguard-rules.pro:

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

-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 * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

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

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-dontwarn java.util.**
-keep class java.util.** {*; }

-dontwarn com.android.**
-keep class com.android.** { *; }

-dontwarn android.support.**
-keep class android.support.** { *; }

-keepattributes SourceFile, LineNumberTable

# end common config

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

-dontwarn com.google.gson.**
-keep class com.google.gson.** { *; }

-dontwarn com.baidu.util.audiocore.**
-keep class com.baidu.util.audiocore.** { *; }

# Application classes that will be serialized/deserialized over Gson
##---------------End: proguard configuration for Gson  ----------

# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

-keep public class * implements java.io.Serializable {*;}
# end Serializable

#  ----------------------------
-dontnote
-dontwarn com.xiaomi.push.service.XMPushService

#for speech sdk
-keep class com.orion.speech.** {*;}
-keep class com.orion.speech.audio.** {*;}
#end for speech sdk

#for xiaomi
-keep class PushReceiver {*;}
-keep class com.xiaomi.push.**{*;}
#end for xiaomi

#for retrofit
-dontwarn sun.misc.Unsafe
-dontwarn okio.**
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on iOS. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
#end for retrofit

#for lambda
-dontwarn java.lang.invoke.*
#end for lambda

#for okhttp
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
#end for okhttp

#for RxJava
-keep class rx.schedulers.Schedulers {
    public static <methods>;
}
-keep class rx.schedulers.ImmediateScheduler {
    public <methods>;
}
-keep class rx.schedulers.TestScheduler {
    public <methods>;
}
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
   long producerIndex;
   long consumerIndex;
}

-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}

-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
# end for RxJava

#for bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
#end for bugly

#----------------android

# this indicate the case of using APIs higher than minSDK (API 8)
-dontwarn android.**

# ---------------------------------------

# TODO: can be reduce if we have more understanding about Service and AIDL
-keep public class android.service.notification.** {*;}

-keepattributes *Annotation*,EnclosingMethod
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}


-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepattributes *Annotation*,EnclosingMethod,Signature

-keep interface android.content.pm.**{*;}
-keep class android.content.pm.**{*;}
-keep class android.os.Process{*;}

-dontwarn com.android.internal.os.*

-keep class android.support.v4.os.**{*;}

-keepclassmembers class * {
    @android.support.v4 *;
}

# cmcm support
-keep class com.cmcm.support.jni.** { *; }
like image 243
VinceStyling Avatar asked May 11 '17 13:05

VinceStyling


1 Answers

Working through these bugs that -keep doesn't fix is a REAL pain and the only way I've ever made headway on them is by following this strategy:

  1. Figure out at what phase of the proguard cycle the bug is being introduced (shrinking, optimizing or obfuscating)
  2. Add/remove exceptions for that step, working your way from the broadest scope of exclusion to the narrowest until the problem resurfaces

E.G.

Verify that this is an optimization issue or not

  1. Add -dontoptimize instead of the -optimizations string rebuild and test
  2. If the crash is mitigated, work backwards through the classes of optimization exclusions at the highest level !method/*, !code/*, !class/*, !field/* until you determine which exclusion makes the issue go away
  3. Determine what the minimum exclusion is in that class of exclusion (assuming it was !method/*, go from it to !method/marking/* and then if that works too try !method/marking/final. If that works, then you've found the minimum exclusion)

This could very well be a bug in the Proguard rules of one of the libs you're using or in the version of Proguard you're using itself (I've seen both) so try to update both as well.

like image 140
Reuben Tanner Avatar answered Oct 26 '22 04:10

Reuben Tanner