This article has been published exclusively by the official wechat account “Yugang Saio”.

Hello, everyone. What you’re looking at is the first in a series of articles called “The Things Java Confuses,” in which I want to reintroduce you to what ProGuard can do. Finally understand how to write good obfuscation rules. So the focus of this series of articles will be on writing the keep rule. I’ll write it in plain English as best I can.

First let’s take a look at what ProGuard is and what it does.

ProGuard is a tool for compressing, optimizing, obfuscating, and pre-validating Java class files.

Briefly explain what ProGuard does

  • Shrinker: To remove invalid classes, fields, methods, etc.
  • Optimizer: Optimizes bytecode, merges methods, removes useless fields, etc.
  • Obfuscator: Confusing class, attribute, method, and field names into unreadable letters, such as A, B, c, and so on.
  • Preverifier: pre-verifies class files to ensure that the class files loaded by the VM are secure and executable.

Let’s look at the next question: how does ProGuard work?

  1. In the compression phase, ProGuard starts recursively at the “entry point to the code”, leaving all classes or variables that are used, and removing all that is not.

  2. During the optimization phase, ProGuard optimizes classes left over from the compression phase, such as making private methods or classes that are not called from outside the code entry point, or making some methods final, corresponding fields static or final, or combining several methods into one. Optimization operations such as removing unused parameters.

  3. Classes and methods (non-code entry point methods) to which code entry points are called in the obtrusion phase, giving them names such as short or complex. The renamed dictionary can be customized during this process. After changing the name can also ensure the normal operation of the program logic.

  4. The prevalidation phase is turned on by default when compiled for Java ME or 1.6 and later. However, prevalidation is not required when compiling for Android.

So what exactly are code entry points?

All right, forget all that nonsense for now, and let’s focus on the first point: code entry points. We should have seen that the ProGuard compression phase starts at the code entry point and recursively searches for the code to use.

For example: if you write a handy download class, assume that the only method you need to use is new DowonloadClien(” URL “).start() then this method should be specified as the code entry point.

How does ProGuard know where the code entry point is? Yeah, ProGuard won’t know about this entry point until we tell him about it. So how do I tell him? We can tell ProGuard by using the keep rule. We will explain how to use it in future articles.

For example

Let’s write a simple little example, with the code to understand. Look at compression, optimization, obfuscation.

// Test code, the following code is purely for testing, there is no rationality beyond that.
src
 -> model
    -> ModelA.java
        int testA = 2;
        
        public void modelA(int age) {
            int a = 1 + age;
            int b = testA + age;
            System.out.println("print " + b);
        }
        
        public void modelB(String name) {
            System.out.println("print " + name);
        }

    -> ModelB.java
        public void modelA(String name) {
            System.out.println("print " + name);
        }

        public void modelB(String name) {
            System.out.println("print " + name);
        }

 -> utils
    -> UtilsA.java
        private static final String UtilA = "utila";

        public static void printA(a) {
            System.out.println("print " + UtilA);
        }

        public static void printB(a) {
            System.out.println("print B");
        }

    -> UtilsB.java
        public static void printA(a){
            System.out.println("print A");
        }

        public static void printB(a){
            System.out.println("print B");
        }

 Main.java
        public static Main sMain = null;

        public static void main(String[] args) {
            sMain = new Main();
            sMain.run();
        }

        private void run(a) {
            ModelA modelA = new ModelA();
            modelA.modelA(5);
            UtilsA.printA();
        }

// We do not add any obfuscation parameters, obfuscation results

src
 -> a
    -> a.java
        private int a = 2;
        public final void a(int i) {
            System.out.println("print " + (this.a + 5));
        }

 -> defpackage
    -> Main.java
        private static Main a = null;

        public static void main(String[] strArr) {
            a = new Main();
            new a().a(5);
            System.out.println("print utila");
        }
Copy the code

Compare the contents of the Jar package before and after obfuscation

See a couple of obvious effects

  1. Classes and methods that are not called by code entry points are removed.
  2. Multiple variables defined are merged together or even disappear altogether.
  3. Many methods have also been merged.
  4. Except for the code entry point, the remaining method and variable names have all changed.
  5. Public static Main sMain = null; After the mix-up, it automatically changed to private.
  6. It also automatically optimizes some methods to be final.

Why isn’t the Main class confused with the Main method?

There is a bar matching rule in the default profile generated by ProGuard

-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
}
Copy the code

To clarify: match the main method in each class as the entry point to the code, if no class has a main method. So our example above is an empty file, because all the code has been deleted during the compression phase.

The main method is the entry method to a Java application, the first method executed by the program.

summary

After this small example, the other features are clearly visible, except for prevalidation. I get the idea. Congratulations on your success in beating monsters, go to the next one.