The entrance

To decide which code to keep and which to discard and obfuscate, entry points must be specified. These entry points are usually main methods, activities, services, and so on.

  • During the compression phase, Proguard recursively determines from these entry points which classes or class members are to be used, and the rest are discarded.

  • During the optimization phase, ProGuard further optimizes the code. Among other optimizations, you can make classes and methods that are not entry points private, static, or final, remove unused arguments, and you can inline some methods.

  • During the obfuscation phase, ProGuard renames classes and class members that are not part of the entry point. Throughout the process, ensure that entry points remain accessible by their original names.

View the Proguard output

It is necessary to check the results to avoid introducing bugs.

Outputs the following files in /build/outputs/mapping/ :

  • Dump.txt describes the internal structure of all classes in APK files

  • TXT Provides mapping tables of classes, methods, and class members before and after the confusion

  • Seeds.txt lists the classes and members that have not been confused

  • Usage.txt lists the code that was removed

We can check the unscrambled classes and members to see if they contain all the desired ones using the seeds.txt file, and then use the usage.txt file to see if any code was mistakenly removed.

The filter

ProGuard provides different filtering options for many configurations: file names, directories, categories, packages, properties, optimizations, etc.

Filters are comma-separated lists of names that can contain wildcards.

Only names that match items in the list will pass through the filter.

The wildcards for each configuration may differ, but the following wildcards are common:

  • ? Matches any single character in the name.

  • * Matches any part of a name that does not contain a package or directory separator

  • ** Matches any part of the name and may contain any number of package or directory separators.

In addition, the name can be preceded by a negative exclamation point! Exclude names and further attempts to match subsequent names.

Therefore, if the name matches an item in the filter, the item is immediately accepted or rejected, depending on whether it has a negation character.

If the name does not match the project, it is tested against the next project, and so on.

If it does not match any item, it is accepted or rejected depending on whether the last item has a negation.

Such as, “!” Foobar, *. Bar “matches all names ending in bar except foobar.

The following uses file filtering as an example.

File filter

Like a generic filter, a file filter is a comma-separated list of file names that can contain wildcards. Only files with matching filenames are read (in the case of input) or written (in the case of output). The following wildcards are supported:

  • ? Matches any single character in the file name

  • * Matches any part of the file name that does not contain the directory separator.

  • ** matches any part of the file name and can contain any number of directory separators.

For example, “Java /**. Class,javax/**. Class” will match all class files in the Java and javax directories.

Also, file names may be preceded by an exclamation point ‘! ‘to exclude file names from matching subsequent file names.

For example, “! **.gif,images/**” matches all files in the images directory except GIF

For more details, check out the official filtering documentation

Keep options

-keep [,modifier,…] class specification

Specifies that classes and class members (fields, methods) are reserved as entry points.

For example, to keep a program, you specify the Main method and the class. To preserve a library, you should specify all publicly accessible elements.

  • Keep the main class and main method
-keep public class com.example.MyMain { 
      public static void main(java.lang.String[]); 
}
Copy the code
  • Preserve all publicly accessible elements
-keep public class * { 

      public protected *; 

} 

Copy the code

Note: If you keep only the class and no class members, your class members will not be retained

Let’s say we have an entity class

public class Product implements Serializable {

    public static final int A = 1;

    public static final int B = 2;

    private String name;

    private String url;



    public String getName(a) {

        return name;

    }



    public void setName(String name) {

        this.name = name;

    }



    public String getUrl(a) {

        return url;

    }



    public void setUrl(String url) {

        this.url = url; }}Copy the code

The rule configuration is as follows

# retain Product class - keep class cn. Sintoon. Camera. The ProductCopy the code

The usage.txt file contains the following, and you can see that all members of the class have been removed

cn.sintoon.camera.Product:

    public static final int A

    public static final int B

    private java.lang.String name

    private java.lang.String url

    16:16:public java.lang.String getName()

    20:21:public void setName(java.lang.String)

    24:24:public java.lang.String getUrl()

    28:29:public void setUrl(java.lang.String)

Copy the code

-keepclassmembers [,modifier,…] class specification

Specify class members to be retained, provided that their classes are also retained.

For example, you want to preserve all Serializable methods and fields in a class that implements the Serializable interface.

-keepclassmembers class * implements java.io.Serializable { 

     private static final java.io.ObjectStreamField[] serialPersistentFields; 

     private void writeObject(java.io.ObjectOutputStream); 

     private void readObject(java.io.ObjectInputStream); 

     java.lang.Object writeReplace(); 

     java.lang.Object readResolve(); 

}

Copy the code

Note: Note that the field type contains the package name; String is of java.lang.String type. In addition, if only the members of the class are retained it is the same as not retaining the class at all

So let’s take the example above and change the rules

-keepclassmembers class * implements java.io.Serializable{

    private String name;

     public String getName();

     public static final int A;

}
Copy the code

The usage. TXT class has been removed, leaving the fields unworded.

cn.sintoon.camera.Product
Copy the code

-keepclasseswithmembers [,modifier,…] class specification

Specifies the class and class members to keep, provided that all specified class members are present.

For example, if you want to keep all the main programs in the program, you don’t have to list them explicitly.

-keepclasseswithmembers public class * { 

    public static void main(java.lang.String[]); 

}
Copy the code

Keep the class and all the members of the class using the example above

-keepclasseswithmembers class cn.sintoon.camera.Product{

 public static final int A;

    public static final int B;

    private java.lang.String name;

    private java.lang.String url;

    public java.lang.String getName();

    public void setName(java.lang.String);

    public java.lang.String getUrl();

    public void setUrl(java.lang.String);

}

Copy the code

Look at seeds.text and you’ll see the class and its members

cn.sintoon.camera.Product

cn.sintoon.camera.Product: int A

cn.sintoon.camera.Product: int B

cn.sintoon.camera.Product: java.lang.String name

cn.sintoon.camera.Product: java.lang.String url

cn.sintoon.camera.Product: java.lang.String getName()

cn.sintoon.camera.Product: void setName(java.lang.String)

cn.sintoon.camera.Product: java.lang.String getUrl()

cn.sintoon.camera.Product: void setUrl(java.lang.String)

Copy the code

Note: It is important to Note that the specified class member must exist. If it does not exist, this rule has no effect at all

-keepnames class specification

– Keep, allowShrinking class Specification

Specify the class members and class members whose names are to be retained if they were not deleted during the compression phase.

For example, you might want to keep all the class names of the classes that implement the Serializable interface so that the processed code remains compatible with any original serialized classes.

Classes that are not used at all can still be deleted. It only works when there is confusion.

-keepnames class * implements java.io.Serializable
Copy the code

Note: This is provided that it was not deleted during the compression phase, which is equivalent to using allowshrinking

-keepclassmembernames class specification

Short – keepclassmembers allowshrinking class specification

Specify class members whose names are to be retained if they are not deleted during the compression phase.

For example, when working with libraries compiled by JDK 1.2 or earlier, you might want to keep the name of the synthesis class $method.

So when working with applications that use the processed library, the obfuscator can detect it again (although ProGuard itself does not require this).

It only works when there is confusion.

-keepclassmembernames class * { 

    java.lang.Class class$(java.lang.String); 

    java.lang.Class class$(java.lang.String, boolean); 
} 

Copy the code

Note: This is provided that it was not deleted during the compression phase, which is equivalent to using allowshrinking

-keepclasseswithmembernames class specification

Short – keepclasseswithmembers allowshrinking class specification

Specifies the class and class members whose names you want to keep, provided that all specified class members exist after the shrink phase.

For example, you might want to keep the names of all the native method names and categories so that the code being processed can still be linked to the native library code. Local methods that are not used at all can still be deleted.

If a class file is used, but none of its native methods are, its name will still be confused. It only works when there is confusion.

-keepclasseswithmembernames,includedescriptorclasses class * { 

    native <methods>; 

} 

Copy the code

Note: This is provided that it was not deleted during the compression phase, which is equivalent to using allowshrinking

-printseeds [filename]

Specifies an exhaustive list of classes and class members matched by the various -keep options. The list is printed to standard output or to a given file. This list can be used to verify that the expected class members are actually found, especially if wildcards are used.

For example, you might want to list all the applications or all the applets you have saved.

Refer to seeds.txt above

-whyareyoukeeping class specification

Specifies to print details about why a given class and class members are in the compression step.

This can be useful if you want to know why a given element appears in the output.

In general, there can be many different reasons.

This option prints the shortest method chain to the specified seed or entry point for each specified class and class member.

In current implementations, printed shortest chains may sometimes contain cyclic deductions – these do not reflect the actual shrinking process.

If the -verbose option is specified, the trace includes the full field and method signatures. Only for compression.

Compression rules

-dontshrink

Specifies a class file that is not compressed.

Compression is turned on by default, and is removed except for classes or class members that are used directly or indirectly with the Keep option.

The compression step usually follows optimizations, because some optimizations may open classes or class members that have been deleted.

-printusage [filename]

Specifies the dead code to list for removal. The list is printed to standard output or to a given file.

Refer to usage.txt above

For example, you can list the unused code of your application. Only for compression.

Optimization rules

-dontoptimize

Specifies that input class files are not optimized. By default, optimization is enabled; All methods are optimized at the bytecode level

-optimizationpasses n

Specifies the number of optimization passes to perform.

By default, one pass is performed. Multiple passes may lead to further improvements. If no improvement is found after optimization, the optimization ends. Only for optimization.

Confusing rules

-dontobfuscate

Specifies a class file that does not obfuscate input.

By default, obfuscation is turned on, and classes and class members are changed to new short random names, in addition to those listed by the various -keep options.

Internal attributes are useful for debugging, such as source file names, variable names, and line numbers being removed.

-printmapping [filename]

Specifies mappings that map old names to renamed classes and new names of class members. Map printing to standard output or to a given file.

For example, it is required for subsequent incremental obfuscation, or if you want to understand the obfuscated stack trace again. It only works when there is confusion.

Refer to mapping.txt above.

-useuniqueclassmembernames

Specifies that the same obfuscated name is assigned to a class member with the same name and that different obfuscated names are assigned to a class member with a different name (signed for each given class member).

Without this option, more class members can be mapped to the same short name, such as ‘a’, ‘b’, and so on.

This option therefore increases the size of the resulting code slightly, but it ensures that the saved obfuscation name map is always respected in subsequent incremental obfuscation steps.

For example, consider two different interfaces that contain methods with the same name and signature. Without this option, these methods might get different obfuscation names in the first obfuscation step.

If a patch containing classes that implement both interfaces is added, ProGuard must enforce the same method name for both methods in the incremental obliquation step.

The original fuzzy code has been changed to keep the resulting code consistent. Using this option in the initial obfuscation step, this renaming will never be necessary.

This option only applies to obfuscation.

In fact, if you plan to perform incremental obfuscation, you may want to avoid compression and optimization altogether, because these steps may remove or modify parts of the code that will be critical for future additions.

-dontusemixedcaseclassnames

Specifies the name of a class that does not generate mixed case when obfuscated.

By default, obfuscated class names can contain a mixture of uppercase and lowercase characters.

The fully acceptable and usable jars created are only available when unzipping jars on platforms with case-insensitive file systems, such as Windows, where the decompression tool may overwrite each other with similarly named class files.

Self-destruct code after decompression! Developers who really want to unzip their JARS on Windows can use this option to turn off this behavior.

The obfuscated jars will become slightly larger as a result.

It only works when there is confusion.

-keeppackagenames [package_filter]

Specifies not to obfuscate the given package name.

The optional filter is a comma-separated list of package names. The package name can contain? , * and ** wildcards, and they can be found in! No. It only works when there is confusion.

-flattenpackagehierarchy [package_name]

Specifies that all renamed packages are repackaged by moving them into a single given parent package. If there are no arguments or an empty string (“), the package is moved to the root package.

This option is an example of further obfuscation of package names. It can make processed code smaller and harder to understand.

It only works when there is confusion.

-repackageclasses [package_name]

Specifies that all renamed class files are repackaged by moving them into a single given package. With no arguments or an empty string (“), the package will be removed completely.

This option overrides the -flattenpackageHierarchy option.

This is another example of further obfuscation of package names.

It can make processed code smaller and harder to understand.

The unrecommended name is -DefaultPackage.

It only works when there is confusion.

Warning: Classes that look for resource files in their package directories will no longer work if they are moved elsewhere. If in doubt, do not use this option to avoid touching the packaging.

-keepattributes [attribute_filter]

Specify any optional attributes to keep. These attributes can be specified with one or more -keepAttributes directives.

The optional filter is a comma-separated list of property names supported by the Java Virtual Machine and ProGuard.

Attribute names can include? , * and ** wildcards, and can be preceded by! No.

For example, when working with libraries, you should at least keep the Exceptions, InnerClasses, and Signature attributes.

You should also keep the SourceFile and LineNumberTable attributes to generate useful obfuscation stack traces.

Finally, if your code depends on them, you may want to keep comments.

It only works when there is confusion.

Keepattributes *Annotation*,InnerClasses # Avoid confusing generics - KeepAttributes Signature # Preserve the line number when throwing an exception -keepattributes SourceFile,LineNumberTableCopy the code

-keepparameternames

Specifies the parameter name and type of the saved method.

This option actually preserves the clipped versions of the debug attributes LocalVariableTable and LocalVariableTypeTable.

It can be useful when working with libraries.

Some ides can use this information to help developers using the library,

Such as tooltips or autocomplete.

It only works when there is confusion.

-renamesourcefileattribute [string]

Specifies a constant string in the SourceFile attribute (and SourceDir attribute) to be placed in the class file. Note that the attribute must be present first, so it must also be explicitly retained using the -keepAttributes directive.

For example, you might want to have processed libraries and applications generate useful obfuscated stack traces.

It only works when there is confusion

Precheck rule

-dontpreverify

Specifies that processed class files are not pre-validated.

By default, class files are pre-validated if they are for Java Micro Edition or Java 6 or later.

With Java Micro Edition, pre-validation is required, so if you specify this option, you need to run an external pre-validation program on the code being processed.

For Java 6, pre-validation is optional, but starting with Java 7, it is required.

It is not required until it is finally available for Android, so you can turn it off to shorten processing time.

-android

Specifies that the class file being processed is for the Android platform. ProGuard then ensures that some features are compatible with Android.

For example, if you are working with an Android application, you should specify this option.

The general rule

-verbose

Specifies that more information is written out during processing. If the program terminates with an exception, this option prints out the entire stack trace, not just the exception message.

-dontnote [class_filter]

Specifies not to print comments about possible errors or omissions in configuration,

Examples include misspellings in class names or missing options that might be useful.

The optional filter is a regular expression;

ProGuard does not print comments about classes with matching names.

-dontwarn [class_filter]

Specify not to warn about unresolved references and other important issues.

The optional filter is a regular expression; ProGuard does not print warnings about classes with matching names. It can be dangerous to ignore warnings.

For example, if the processing does require an unresolved class or class member, the processed code will not work properly.

Use this option only if you know what you’re doing!

-ignorewarnings

Specifies to print any warnings about unresolved references and other important issues, but to continue processing and ignore the warnings in any case.

It can be dangerous to ignore warnings.

For example, if the processing does require an unresolved class or class member, the processed code will not work properly.

Use this option only if you know what you’re doing!

-printconfiguration [filename]

Specifies that the entire parsed configuration is written out using the included files and replacement variables. The structure is printed to standard output or to a given file.

This can sometimes be useful for debugging configurations or converting XML configurations into a more readable format.

-dump [filename]

Specifies the internal structure of the class file to be written out after any processing. The structure is printed to standard output or to a given file.

For example, you might want to write out the contents of a given JAR file without processing it.

Refer to dump.txt above.

-addconfigurationdebugging

Specifies that processed code is handled with debug statements that show suggestions for missing ProGuard configuration.

If the processed code crashes, it might be useful to get a utility hint at run time, because it still lacks some reflection configuration.

For example, the code might be serializing classes using the GSON library, which might require some configuration. It is often possible to copy/paste the console’s recommendations into a configuration file.

Warning: Do not use this option in distributions as it adds obfuscated information to processed code.

Keep option modifier

includedescriptorclasses

Any classes in the type descriptors of methods and fields saved by specifying the -keep option should also be saved.

This is often useful when preserving method names to ensure that method parameter types are not renamed. Their signatures remain exactly the same and are compatible with the local library.

includecode

The code attributes of methods that specify fields saved by the hold-keep option should also be preserved, that is, perhaps not optimized or obfuscated. This is often useful for classes that have been optimized or obfuscated to ensure that their code has not been modified during optimization.

allowshrinking

Entry points specified in the -keep option may be compressed, even if they must be kept separately.

That is, entry points can be removed in the compression step, but they may not be optimized or obtruded if they are required.

allowoptimization

Entry points specified in the -keep option may be optimized, even if they must be saved separately.

That is, entry points may be changed during optimization steps, but they may not be deleted or obtruded.

This modifier is used only to implement unusual requirements.

allowobfuscation

Specifying entry points specified in the -keep option can be confusing, even if they must be saved separately.

That is, entry points may be renamed in the obliquation step, but they may not be deleted or optimized.

This modifier is used only to implement unusual requirements.

The relationship between the keep options

The various -keep options for compression and obfuscation may seem confusing at first, but there’s actually a pattern behind them.

The following table summarizes the relationship between them:

content Deleted or renamed By renaming
Class and class members -keep -keepnames
Only class members -keepclassmembers -keepclassmembernames
Class and class members, reference members exist -keepclasseswithmembers -keepclasseswithmembernames

If you specify a class with no class members, ProGuard keeps only that class and its no-argument constructor as an entry point. It may still remove, optimize or confuse other class members.

If a method is specified, ProGuard saves that method only as an entry point. The code may still be optimized and tweaked.

Type of specification

A class specification is a template for classes and class members (fields and methods). It is used in various -keep and -assumenosideeffects options. The corresponding options only apply to classes and class members that match the template.

The template design looks very Similar to Java, with some extensions for wildcards. To understand the syntax, you should look at these examples, but this is an attempt at a complete formal definition:

[@annotationtype] [[!]public|final|abstract|@ ...] [!] interface|class|enum classname [extends|implements [@annotationtype] classname] [{ [@annotationtype] [[! ]public|private|protected|static|volatile|transient ...] <fields> | (fieldtype fieldname); [@annotationtype] [[! ]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> | <init>(argumenttype,...) | classname(argumenttype,...) |(returntype methodname(argumenttype,...)); [@annotationtype] [[! ]public|private|protected|static ... ] *; ... }]Copy the code

Square brackets “[]” indicate that its content is optional.

Ellipsis point “…” Indicates that any number of the preceding items can be specified.

Vertical bar “|” the two choices.

The non-bold parentheses “()” simply group together the parts that belong to the specification.

Indentation attempts to clarify the intended meaning, but whitespace is irrelevant in the actual configuration file.

The class keyword refers to any interface or class. The interface keyword restricts the matching interface class. Enum Keyword restriction matches enumeration class. Before the interface or enum keyword! Restrict matches to classes that are not interfaces or enumerations.

Each class name must be a fully qualified name, such as java.lang.string.

Inner classes are separated by the dollar sign “$”, such as java.lang.thread $State. The class name can be specified as a regular expression containing the following wildcards:

  • ? Matches any single character in the class name, but not the package delimiter. For example, “com. Example. The Test?” Can match “com.example.Test1” and “com.example.Test2” but not “com.example.Test12”

  • * Matches any part of the class name that does not contain the package separator. Test such as “com. Example. * *” to match. Com. Example MyTest “and” com. Example. MyTestProduct “but can’t match. Com. Example. MXC MyTest” or “Com.example.*” matches “com.example” but not “com.example.mxc”.

  • ** Matches any part of the class name and may contain any number of package separators. For example, “**.testz “matches all Test classes in all packages except the root package. Alternatively, “com.example.**” matches all classes and their subpackages in “com.example”.

  • <n> matches the NTH wildcard in the same option. For example, “com.example.*Foo<1>” matches “com.example.BarFooBar”.

For more flexibility, the class name can actually be a comma-separated list of class names, which can be added! . This notation doesn’t look very Java, so it should be used sparingly.

For convenience and backward compatibility, the class name * refers to any class, regardless of its package.

  • Extends and **implements ** are commonly used to restrict classes that use wildcards. At the moment they are the same. What they mean is that only classes that inherit or implement a given class are eligible. The given class itself is not included in this collection. If necessary, this should be specified in a separate option.

  • @ can be used to restrict classes and class members to classes annotated with the specified annotation type. Annotationtype is specified like the class name.

  • Fields and methods are defined very similarly in Java (as they are in other tools such as Javadoc and Javap), except that method argument lists do not contain parameter names. These specifications can also contain the following wildcard wildcards:

The wildcard meaning
<init> Matches any constructor
<fields> Match any field
<methods> Match any method
* Matches any method and field

Note that there is no return type for the wildcard. Only the <init> wildcard has a parameter list.

Fields and methods can also be specified using regular expressions. The name can contain the following wildcards:

The wildcard meaning
? Matches any single character of the method name
* Matches any part of the method name
<n> Matches the NTH wildcard in the same option

The type can contain the following wildcards

The wildcard meaning
% Matches any primitive type (Boolean,int, etc., void not included)
? Matches a single character in the class name
* Matches any part of the class name but does not contain the package separator
** Matches any part of the class name but does not contain the package separator
* * * Matches any type (primitive or non-primitive, array or non-array)
Matches any number of parameters of any type
<n> Matches the NTH wildcard in the same option.

Attention, please? , * and ** wildcards will never match primitive types. Also, only *** wildcards can match array types of any dimension.

For example, “** get * ()” matches “java.lang.object getObject ()”, but not “float getFloat ()” and “java.lang.object [] getObjects ()”.

  • Constructors can also be specified using short class names (without packages) or using full class names. Like the Java language, the constructor specification has a list of arguments, but no return types.

  • Class access modifiers and class member access modifiers are typically used to restrict wildcard classes and class members. They specify that access flags must be set for members to match. Adding “!” Determines that the corresponding access flag should be unset.

Multiple flags can be combined (for example, public static). This means that two access flags must be set (for example, public static) unless they conflict, in which case at least one of them must be set (for example, at least public or protected).

ProGuard supports other modifiers that may be set by the compiler: synthetic, Bridge, and Varargs.

The resources

  • www.guardsquare.com/en/proguard…

  • www.diycode.cc/topics/380

Scan my wechat and follow my official account