Home > Back-end >  Storing (partial) Proguard obfuscation mapping in code
Storing (partial) Proguard obfuscation mapping in code

Time:02-04

In an android Java code like this:

String methodName = "myMethod"

public void myMethod() {
}

Is it possible to figure out the obfuscated name of the method at runtime?
I know I can ask Proguard to not obfuscate that method, but I'm looking for a way to avoid that and still be able to use it through reflection.

I can't find a Proguard configuration for that, but I'm wondering if there could be a gradle task for merging the generated mapping?

CodePudding user response:

Code obfuscation usually just renames classes and variables to short and random text, keeping proper dependencies at the same time. There is no way to get the obfuscated name of the function as it is generated randomly and always different per each run.

Here are some options:

  1. Instead, exclude the class from obfuscation and name your function with random text if you want to keep it unreadable.

    -keep class <com.package.DontObfuscate>

  2. After the first obfuscation, keep mapping.txt file and add a line to your proguard-rules.pro

    -applymapping mapping.txt

That's will keep from generating new names for classes/variables.

-applymapping - Specifies to reuse the given name mapping that was printed out in a previous obfuscation run of ProGuard. Classes and class members that are listed in the mapping file receive the names specified along with them. Classes and class members that are not mentioned receive new names.

  1. Look for a way to not use reflection in your code with class/interface extensions.

CodePudding user response:

If you need the method name from inside the method, you can use

String methodName;

public void myMethod() {
    methodName = new Object() {}.getClass().getEnclosingMethod().getName();
}

Or, in newer Java versions

String methodName;

public void myMethod() {
    methodName = StackWalker.getInstance(Set.of(), 1)
        .walk(s -> s.map(StackWalker.StackFrame::getMethodName).findFirst())
        .orElseThrow();
}

When you need the name from the outside, i.e. with calling it, you can mark the method with an annotation and search for it.

@Retention(RetentionPolicy.RUNTIME) @interface Marker {}

@Marker
public void myMethod() {
}
String name = Arrays.stream(ContainingClass.class.getDeclaredMethods())
    .filter(m -> m.isAnnotationPresent(Marker.class))
    .map(Method::getName)
    .findFirst().orElseThrow();

As said by others, not to use Reflection might be the better option. E.g., referring to the method using a matching functional interface, is not a reflective access:

Runnable method = this::myMethod;

public void myMethod() {
}

This will keep working after obfuscation.

  • Related