MichaelRocks / paranoid

String obfuscator for Android applications.
Apache License 2.0
670 stars 79 forks source link

[0.3.x] Release app crashes with ClassNotFoundException for DeobfuscatorHelper #38

Open valeriyo opened 3 years ago

valeriyo commented 3 years ago

Thanks @MichaelRocks for an excellent tool!

I have an Android library project (AAR) that's using paranoid. It worked fine with 0.2.5 - but after I upgraded to 0.3.2, I hit this runtime error in the app that's using that library AAR:

java.lang.ClassNotFoundException: Didn't find class "io.michaelrocks.paranoid.DeobfuscatorHelper" on path: DexPathList..

NOTE: this only in the release build (with R8 turned on)

I didn't have time to dig into this problem, but 0.3.0 is the first version with this problem. Are some required R8 rules missing?

MichaelRocks commented 3 years ago

Hi @valeriyo,

Starting from 0.3.0 Paranoid relies on a helper class to perform deobfuscation at runtime. It seems that for some reasons this class doesn't appear in the main dex but is accessed on application launch. I faced the same issue in some of my projects even without Paranoid and currently I'm not aware of a reliable way to fix it.

As a workaround you can try and add 'io.michaelrocks:paranoid-core:0.3.2' to the application module and access DeobfuscatorHelper from the Application subclass.

If you find a way to fix it please don't hesitate to share it with me. I would be very grateful if you could provide me with any information on this issue.

valeriyo commented 3 years ago

@MichaelRocks of course my problem was because my AAR now has a new dependency - and any app using it needs to add:

implementation("io.michaelrocks:paranoid-core:0.3.2")

Here is the thing -- it would be A LOT better, if the gradle plugin dumped DeobfuscatorHelper.java and RandomHelper.java in the current project's build/generated/source, so that those could be mangled/obfuscated with R8. It would make it even harder to reverse-engineer those!

For example, I manually added these two files into my library, and after R8 doing its job they became hardly recognizable:


  public static String a(long var0) {
    String[] var8 = a;
    long var10001 = r.a(((var0 & 4294967295L ^ (var0 & 4294967295L) >>> 33) * 7109453100751455733L ^ (var0 & 4294967295L ^ (var0 & 4294967295L) >>> 33) * 7109453100751455733L >>> 28) * -3808689974395783757L >>> 32);
    long var2 = var10001 >>> 32 & 65535L;
    long var4;
    long var6 = (var4 = r.a(var10001)) >>> 16 & -65536L;
    int var1;
    int var9;
    char[] var5 = new char[var9 = (int)((var2 = r.a(var1 = (int)(var0 >>> 32 ^ var2 ^ var6), var8, var4)) >>> 32 & 65535L)];

    for(int var10 = 0; var10 < var9; ++var10) {
      var5[var10] = (char)((int)((var2 = r.a(var1 + var10 + 1, var8, var2)) >>> 32 & 65535L));
    }

    return new String(var5);
  }

  public static long b(long var0) {
    long var10000 = var0;
    short var3 = (short)((int)(var0 & 65535L));
    short var1;
    short var4 = var1 = (short)((int)(var10000 >>> 16 & 65535L));
    var1 = (short)(a((short)(var3 + var1), 9) + var3);
    short var2 = (short)(var4 ^ var3);
    var4 = var1;
    var3 = (short)((short)(a(var3, 13) ^ var2) ^ var2 << 5);
    var1 = a(var2, 10);
    return ((long)var4 << 16 | (long)var1) << 16 | (long)var3;
  }

  public static short a(short var0, int var1) {
    return (short)(var0 << var1 | var0 >>> 32 - var1);
  }
MichaelRocks commented 3 years ago

Adding classes to an AAR will cause issues with duplicate classes if the plugin is used in multiple AARs. If your goal is to obfuscate the AAR then you'd better do it yourself. When building an APK all classes within it can be obfuscated with R8 if it's configured properly.

AndroidDeveloperLB commented 3 years ago

@MichaelRocks Are you saying that if a developer wishes to create an obfuscated aar file, he should re-consider using this nice library? It could have issues later? What will happen if something gets updated on the app that uses the aar file? Or if the app itself is using this library, and it gets to a newer version ?

MichaelRocks commented 3 years ago

@AndroidDeveloperLB The library doesn't fully support obfuscation of AARs at the moment because of the issue @valeriyo mentioned above. Probably it can be supported but I don't like the idea of adding helper classes to the AAR because it will lead to possible class duplication in the resulting APK, which is a worse and harder to fix problem. So if you need to build an obfuscated AAR right now it would be better to to it manually by applying Paranoid, adding it's dependencies right into the AAR file, and then applying R8.

I would like to fix this issue but I have no idea how to do that in scope of this library.

AndroidDeveloperLB commented 3 years ago

@MichaelRocks I think this library isn't unique in this problem. I think that the moment I've obfuscated the code of an Android library I work on, I will have to tell whoever uses it to write the same dependencies I use. I got similar issues with plenty of dependencies. Examples are Retrofit, Room, okhttp, libphonenumber, ...

But this is weird: How do other libraries do this, avoiding dependencies on the library-users ? Is it because I obfuscated the code? Or because it's aar ? Or what?

MichaelRocks commented 3 years ago

I believe the root of the problem isn't in libraries but in multidex. Gradle Android plugin decides what classes to put into the main dex when building the APK. If some unexpected bytecode modifications occur it may lead to situations when a class that should've been in the main dex remains in one of the secondary dex files, which leads to crashes on application launch. In Paranoid's case adding the dependency to the application module results in adding its classes to the main dex.

So @valeriyo's idea of inlining dependency's classes into the module is correct but it has to be done smartly and carefully with generating a unique set of classes per module.

AndroidDeveloperLB commented 3 years ago

@MichaelRocks Do you know perhaps how to handle it for other dependencies? It's very weird that I've reached this situation (first time I make an obfuscated aar) that means that whoever uses the aar file will have to write all the dependencies it uses. I don't even know what causes it. Never had this problem with any dependency.

What is the possible fix for this exactly?

MichaelRocks commented 3 years ago

@AndroidDeveloperLB What dependencies do you mean? Sorry, it seems I don't quite understand the problem you're describing.

AndroidDeveloperLB commented 3 years ago

@MichaelRocks I mean that I think this is a general issue with AAR files. That whoever uses an AAR file, will need to put all the dependencies that the AAR file uses. I was told that POM file should be configured to avoid this, but not sure how. I was wondering if you could know about such a fix.

MichaelRocks commented 3 years ago

@AndroidDeveloperLB If you add an AAR to the project as a maven dependency and this dependency contains a proper pom file transitive dependencies will be added automatically. And it works fine with Paranoid but in some cases the resulting application may start crashing on launch because of issues with multidex (at least I believe so).

MichaelRocks commented 3 years ago

Actually @AndroidDeveloperLB told a quite an interesting thing. @valeriyo, how do you add the AAR file to the project? Is it a Maven artifact or just a file without pom.xml?

AndroidDeveloperLB commented 3 years ago

@MichaelRocks Where do you put the pom file? I don't see it here on this repository. How does it look like? Can you please share it here? I want to have a look at how it's configured. Might help me with something.

MichaelRocks commented 3 years ago

@AndroidDeveloperLB Usually you don't put the pom file into the repo but generate it instead. If you use the built-in Gradle's maven-publish plugin the pom file can be customized via DSL. Adding AAR dependencies to the pom file is a little bit tricky but there're existing plugins which can do that for you or you can create your own plugin.

AndroidDeveloperLB commented 3 years ago

@MichaelRocks I think I got it using the plugin. Thank you. Hope that Jitpack can handle it fine. Sadly though, I think it missed something that I had in gradle file:

packagingOptions {
  exclude 'META-INF/DEPENDENCIES'
}
valeriyo commented 3 years ago

BTW, if this helps, I ended up dropping paranoid-core-0.3.2.jar (downloaded from https://mvnrepository.com/artifact/io.michaelrocks/paranoid-core/0.3.2) into the libs folder in my AAR library project. That way, it's treated as a local dependency, so it's embedded and obfuscated into the AAR - so that the consumer of my AAR doesn't need to be "paranoid" :)

crackleeeessyp commented 2 years ago

Any ideas about how to make it work for AAR library? I tried @valeriyo suggestions to add local libs, but it show "class duplication" error when build.

shrishkukde commented 2 years ago

@MichaelRocks : I need to obfuscate strings inside my AAR library project. What is the correct way to apply this library:

  1. I manually added gradle dependency and plugin of paraniod library only inside the submodule(i.e. my AAR library project)
  2. Then I added @Obfuscate annotation to required files which needs string obfuscation.
  3. Then I converted my submodule to AAR library project, which will be consumed by many apps.
  4. I have not added paranoid dependency to my consumer app
  5. I ship my .aar file directly on github from where the consumer apps use it

Is this approach correct OR my consumer app will crash as they do not have paranoid. Request you to please guide.

@valeriyo : Would you please advice on above query

shrishkukde commented 2 years ago

@valeriyo Request you to please provide your guidance on above query

AndroidDeveloperLB commented 1 year ago

For all those who use this dependency, there is a new one that's based on it: https://github.com/LSPosed/LSParanoid

The good thing is that it solves an issue with new gradle plugin, and might be faster in build-time: https://github.com/MichaelRocks/paranoid/issues/65

The bad news is that sadly I failed to use it as part of AAR library, and sadly the developers there quickly closed what I wrote about it: https://github.com/LSPosed/LSParanoid/issues/8 I don't even understand what they wrote, and if they know how to solve it or not.

For a normal module (the app's module, at least), though, it works fine.

If anyone succeeds, please let me know.