如何使用 R8 混淆 Kotlin 属性?

How to obfuscate Kotlin properties with R8?

我有一个关于 Kotlin 的库我想几乎完全混淆但保持 public 类、属性和方法不变。这是我打算混淆的 public 类 之一的示例:

class SomeClass(val propertyToShow: SomePublicClass, private val propertyToHide: SomeOtherPublicClass) {

    fun methodToShow(someArg: SomeArg) {
        // Some code
    }

    private fun methodToHide(someOtherArg: SomeOtherArg) {
        // Some more code
    }

}

ProGuard 文件基于 typical library ProGuard file,如下所示:

-keepattributes SourceFile,LineNumberTable

-renamesourcefileattribute SourceFile

-dontwarn javax.annotation.**

-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform

-keepattributes Signature

-keepattributes *Annotation*

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

-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

-keep public class * {
    public protected *;
}

-keepclassmembernames class * {
    java.lang.Class class$(java.lang.String);
    java.lang.Class class$(java.lang.String, boolean);
}

-keepclasseswithmembernames class * {
    native <methods>;
}

-keeppackagenames **
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,Synthetic,PermittedSubclasses
-keepparameternames
-renamesourcefileattribute SourceFile
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keep class kotlin.Metadata { *; }

-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-keep class kotlin.reflect.** { *; }
-dontwarn kotlin.reflect.**
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}

-keepclassmembers,allowobfuscation class * {
    @javax.inject.* *;
    @dagger.* *;
    <init>();
}

-keep class javax.inject.** { *; }
-keep class **$$ModuleAdapter
-keep class **$$InjectAdapter
-keep class **$$StaticInjection
-keep class dagger.** { *; }

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

-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    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();
}

运行 ./gradlew assembleRelease 生成 .aar 文件。 SomeClass.class 看起来像这样(如果我们通过 Android Studio 查看它)

public final class SomeClass public constructor(propertyToShow: SomePublicClass, propertyToHide: SomeOtherPublicClass) {
    
    public final val propertyToShow: SomePublicClass /* compiled code */

    private final val propertyToHide: SomeOtherPublicClass /* compiled code */

    public final fun methodToShow(someArg: SomeArg): kotlin.Unit { /* compiled code */ }

    private final fun a(someOtherArg: SomeOtherArg): kotlin.Unit { /* compiled code */ }
    
}

我们可以看到,私有方法的名字已经被混淆了,但是它的参数并不像私有方法那么好属性。不管用ProGuard还是R8混淆,结果都是一样的

是否可以混淆 Kotlin 源代码的私有属性和私有方法的参数?或者它没有意义,因为它不会干扰其他人进行逆向工程?

所以在this article中或多或少地解释了这个问题的答案。基本上,问题是代码确实被正确混淆了,但仍然存在 Kotlin 元数据,Android Studio 正在根据此元数据重建代码。