Search code examples
androidc++gradlejava-native-interfaceproguard

How to apply Android R8 obfuscation to native code?


I'm developing an Android app that has to be obfuscated for security reasons. Some classes and fields that are obfuscated by R8 are used in my C++ code, so of course the obfuscation step breaks this part of the code.

My question is, since I don't want to just keep those classes and fields unobfuscated, how can I script an automatic modification of the C++ code based on the mapping.txt that is output by R8?

More specifically, the issues I'm facing while trying to tackle this problem are the following:

  • How should I go about editing the source code just for the compilation without permanently modifying it in my project
  • I believe the native part gets compiled first, prior to the compilation of the Kotlin part and then prior to the generation of the mapping.txt file, so how can I reorder the tasks or work around this problem

Any help would be appreciated.


Solution

  • Here is the spark of an idea, up to you to see if it is workable:

    1. Take the mapping.txt of a build and remove everything except the parts needed by your JNI code.
    2. Pass that file to R8 with the -applymapping option in all subsequent builds. This will keep the JNI symbols obfuscated but stable. Other symbols will still be randomly obfuscated.
    3. Use mapping.txt as source for the m table in the following function:
    inline constexpr const char * mapping(const char* in) {
        std::pair<const char *, const char *> m[] = {
            {"important/class","a"},
            {"important/other/class", "a.b"},
        };
        for (const auto& [normal, obfuscated]: m) {
            if (!strcmp(normal, in)) return obfuscated;
        }
        return "ERROR";
    }
    
    1. Now use mapping("foo") anywhere you need to reference a class, field or type name.

    In a brief experiment the compiler completely inlined and eliminated the mapping function, leaving only the obfuscated type in place.