How can I obfuscate my sdk coded with kotlin (and

2019-03-10 03:24发布

问题:

I'm developing a SDK (Android library), and I have to obfuscate a large part of my code so the customer may not try and play with internal code. My lib is coded in kotlin, and I used proguard to obfuscate the code. Problem is that there are still @kotlin.Metadata (runtime) annotations inside the code after compile and obfuscation. With those annotations, it's really easy to retrieve the java code that originated this "(not-so-)obfuscated" bytecode.

I first thought it was my fault, and my project had too many entropy sources that might have induced this behaviour, so I made a sample project to prove that the problem does not come from my sdk implementation. I created a new project with AS, then a lib module with 2 files :

  • facade.kt is my facade class, the one that I do not wish to obfuscate, so the customer may use it :

    package com.example.mylibrary
    
    class MyFacade(val internalClass:InternalClass) {
    
       fun doSomething() {
          internalClass.doSomething(
                 firstArgument=1,
                 secondArgument=2
          )
        }
     }
    
  • and in this sample, internal.kt holds the classes that I want to obfuscate :

    package com.example.mylibrary
    
    class InternalClass {
        fun doSomething(firstArgument: Int, secondArgument: Int) {
            System.out.println("Arguments are : $firstArgument, $secondArgument")
        }
    }
    

The proguard rules are injected into gradle project with this release closure :

buildTypes {
    release {
        minifyEnabled true
        proguardFiles 'proguard-rules.pro'
    }
}

And here is proguard-rules.pro (only one line, nothing more) :

-keep class com.example.mylibrary.MyFacade {*;}

The result : when I ./gradlew clean myLib:assembleRelease, I do obtain an aar in which my facade is kept, and my internal class has been renamed in 'a', with one method 'a', except that the class is still annotated with kotlin @Metadata, which holds every information that helps the decompiler retrieve the original class name, the method, attribute and argument names, etc... So my code is not so obfuscated at all...

@Metadata(
   mv = {1, 1, 7},
   bv = {1, 0, 2},
   k = 1,
   d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0016\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0006\u0010\u0007\u001a\u00020\u0006¨\u0006\b"},
   d2 = {"Lcom/example/mylibrary/InternalClass;", "", "()V", "doSomething", "", "firstArgument", "", "secondArgument", "mylibrary_release"}
)
public final class a {
    ...
}

So my question : is it possible to get rid of those annotations, am I the only one facing this problem, or have I missed something ?

回答1:

If you are making a library project intended to be used by others, don't obfuscate and shrink the project, but rather provide a consumer ProGuard file for your users by implementing the following lines:

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
}

Source

In this consumer-proguard-rules.pro that you ship with your library you should remember to update accordingly, but always remember to declare at least the enums and the annotations to be kept, otherwise let the consumers's getDefaultProguardFile('proguard-android.txt') to take care of the rest.

It goes the same for shrinking - you should only really care about ensuring that all required (at runtime) resources are kept by implementing keep rules for them.