Using ProGuard to remove logs

A couple of days ago the following two tweets appeared on my twitter feed:

These tweets reminded me of something: If you’re an Android developer and you want to use ProGuard’s code removal feature to remove your logs and use method renaming at the same time, be careful.

ProGuard is NOT a security product and shouldn’t be used as one - especially if you want to take advantage of obfuscation and log removal.

Here’s the problem: It is designed to remove method calls - nothing beyond that. It doesn’t always work as expected and this has helped me to ‘bypass obfuscation’ several times in the past.

Let’s explain why with an example.

Say you have the following class in your Android code.

public class SecurityUtils {
    static String doSecurityThing(){
        int rnd = new Random().nextInt();
        Log.d("ProguardTest", "SecurityUtils.doSecurityThing() running method. Secret random is "+Integer.toString(rnd));
        return "test";
    }
}

Obviously, the Log.d() call exposes the random number AND the name of the class and the function to whoever has access to logs and any reverse engineer.

After enabling ProGuard without custom rules, here’s what happens:

public final class a {
    static String a() {
        int a = new Random().nextInt();
        Log.d("ProguardTest", "SecurityUtils.doSecurityThing() running method. Secret random is "+Integer.toString(a));
        return "test";
    }
}

You can see the class name and the function name got renamed to something that doesn’t make sense. Good. However the logging call remains.. You decide to use ProGuard’s Log removal hack

-assumenosideeffects class android.util.Log {
 public static int d(...);
 public static int v(...);
}

After a new compilation, you’d expect the logging call not to be there, and your class to be ‘safe’.

However, here’s what happened:

public final class a {
    static String a() {
        new StringBuilder("SecurityUtils.doSecurityThing() running method. Secret random is ").append(Integer.toString(new Random().nextInt()));
        return "test";
    }
}

As you can see, what happened here is that the string concatenation operation inside the logging call was really converted to code that builds a string using the StringBuilder class.

ProGuard’s log removal trick just removed the call to Log.d() but not the constructor of the StringBuilder object, which remained in the code. Even though nothing will get logged, anyone looking at the code can figure out the initial method and class name, and thus understand what that piece of code does.

It’s absolutely right to remove logging in release builds, but ProGuard is not a safe way to do it.

mobile security, static & dynamic analysis, automation, payments

London, UK