Introduction to ProGuard and workflow

ProGuard detects and removes unused classes, fields, methods, and properties, analyzes and optimizes bytecodes, and renames classes, fields, and methods with short, meaningless names by compressing, optimizing, obfuscating, and prechecking. This makes code smaller, more efficient, and harder to reverse engineer.

Here is the ProGuard workflow, which goes through four phases:

  1. Shrink: In the compression step, used to detect and remove unused classes, fields, methods, and properties
  2. OptimizeIn the optimization step, the bytecode is optimized and useless instructions are removed
  3. ObfuscateIn the obfuscation step, rename classes, fields, and methods using meaningless names such as A, B, and C
  4. PreveirfyIn the precheck step, the precheck is performed mainly on the Java platform after processing the code

The above four steps are optional, and you can configure the script to determine which of them you want to do. For example, we can configure only compression and obfuscation, no optimization, and no precheck. ProGuard’s website use guidance: proguard.sourceforge.net/

PrgGuard environment configuration and use

Running PrgGuard requires the following dependencies:

  • Jar or proGuardgui.jar. Proguardgui provides a simple configuration interface (shown below) from which you can configure, while Progua.jar is handled using configuration files
  • Java runtime environment

How do I run ProGuard

ProGuard can be invoked from the command line, as in:

  • Java-jar proGuardgui. jar: starts the graphical configuration interface
  • Java -jar proguard.jar @config.file -options… : Performs ProGuard processing through the configuration file

After successful execution, open the processed JAR file with JD-GUI:

You can see that the class has been obfuscated.

PrgGuard configuration file is used

Concept of Entry points

Here, we introduce the concept of Entry points. Entry points are classes or methods that are not handled during ProGuard. In the Shrink step, ProGuard iterates recursively, searching for which classes and members are used, and discarding unused classes and members during the compression phase. Next in the Optimize phase, non-entry points classes and methods are made private, static, or final, unused parameters are removed, and some methods are marked inline. In the Obfuscate step, ProGuard renames classes and methods that are not Entry points.

Instruction parameter description will be used

Modifier

  • Includedescriptorclasses: Is used to ensure that the native method name, method parameter type will not be renamed, and method signature will not be changed so that the method can be matched with the Native libraries.
  • Allowshrinking: Allows compression
  • Allowoptimization: Allows optimization
  • Allowobfuscation: Name confusion is allowed

Class Specifications

Class Specifications are templates that describe classes and methods. Here is the format of this template:

Among them, the contents of the [] is optional, name, can use the wildcard matching the constructor, member, matching method, details please refer to: proguard.sourceforge.net/manual/usag…

Basic instructions

– basedirectory directoryname:

All the relative paths in the configuration file are relative to this path, as shown in the figure:

-injars class_path

Specify the jar package to be processed (or aars, Wars, EARS, Zips, APks, directories), and the classes in the JAR will be processed by ProGuard and written to the output JAR. Injars can be used multiple times to introduce different files that need to be processed. Note that this option can specify a directory in which all files will be treated as input files.

-outjars class_path

Set the path to the output file after the processing is complete

-libraryjars class_path

Specify the JARS (or Aars, Wars, EARS, Zips, APks, directories) for the application to be processed, which will not be included in the output file. Jar packages that the file being processed depends on and that do not need to be processed or written to the output file. Such as:

-skipnonpubliclibraryclasses

Ignore classes in the Library that are not public qualified. This speeds up ProGuard processing and reduces ProGuard memory usage. In general, library classes that are private cannot be used by programs, and ignoring them can speed up obfuscations. Note, however, that this option cannot be used in special cases where someone writes code in the same package as the classes in the class library and uses non-public classes in that package.

– dontskipnonpubliclibraryclasses

Do not ignore library classes that are not public qualified

-dontskipnonpubliclibraryclassmembers

Specifies that members and methods in non-public classes are not ignored. ProGuard ignores library members and methods that are not public classes by default, but may be used in applications for reasons described in 3.2.5. This option is required to specify that they are not ignored.

-keepdirectories [directory_filter]

Specifies the directory to keep in the output file. By default, directories are removed. This will reduce the size of the output file, but if you might cause the program to crash when the code references to them (such as mypackage. MyCalss. Class. GetResource (” “)). Keepdirectories Mypackage need to be specified. -keepdirectories myDirectory matches the myDirectory directory. -keepDirectories myDirectory /* Match directories directly to myDirectory; -keepdirectorie mydirectory/** Matches all subdirectories. If no filter is specified, all directories are retained.

-target version

Specifies the version of Java used to process the class file. The options are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 (or just 5), 1.6 (or just 6), 1.7 (or just 7), or 1.8 (or just 8).

-forceprocessing

Force output, even if the output file is up to date

-keep [,modifier,…] class_specification

Specifies the class and its members and methods as entry points, not to be confused by ProGuard

-keepclassmembers [,modifier,…] class_specification

Specify that certain members of the class are not obfuscated. Note that the class name will still be obfuscated, as in:

-keepclasseswithmembers [,modifier,…] class_specification

Members specify which classes are not obfuscated. For example, we can use it to preserve classes that contain main methods:

If multiple rules are specified, as follows, the class that must contain both sayHello and test methods is retained

-keepnames class_specification

– keepclassmembers allowshrinking class_specification alias, retain name don’t be confused, but they can be compressed

-keepclassmembernames class_specification

– keepclasseswithmembers allowshrinking class_specification alias, retain name don’t be confused, but they can be compressed

-keepclasseswithmembernames class_specification

– keepclasseswithmembers allowshrinking class_specification alias

-printseeds [filename]

Output the classes and methods matched by KEEP to the file, which can be used to verify whether the rules you set are effective.

-dontshrink

Specifies no compression.

-printusage [filename]

Output unused code to a file to see which code is compressed and discarded. .

-dontoptimize

Specifies that input code is not optimized. Optimization options are turned on by default.

-optimizations

The obfuscation is the algorithm used, and the parameter behind it is a filter. This filter is the algorithm recommended by Google and generally does not change

-optimizationpasses n

Specifies the level of optimization between 0 and 7. The default is 5.

-assumenosideeffects class_specification

You can specify which methods to remove without side effects. For example, in Android development, if you want to remove all log output in the release version, you can configure:

Then all log code will be removed during the optimization phase.

– dontobfuscate

Specifies no obfuscation

-printmapping [filename]

Generate a map file to record the names before and after the obfuscations. Note that this is important, because the names run after the obfuscations will become unreadable, only rely on the map file to restore.

-applymapping filename

It is used to maintain a mapping for two obfuscations, ensuring that the same code has the same name after two obfuscations

-obfuscationdictionary filename

Specifies an external fuzzy dictionary

-classobfuscationdictionary filename

Specifies the class fuzzy dictionary.

-packageobfuscationdictionary filename

Specify the Package fuzzy dictionary

– useuniqueclassmembernames

When a class is confused with a member, use a unique name

-dontusemixedcaseclassnames

Do not use mixed case class names. Note that Windows users must specify this option for ProGuard because Windows is case-insensitive to files, i.e. a.java and A.java will be considered the same file. If you don’t do this and your project has more than 26 classes, ProGuard will default to mixing case and case filenames, causing class files to overwrite each other.

-keeppackagenames [package_filter]

Keep Packagename unconfused

-flattenpackagehierarchy [package_name]

Specify repackage and rename all packages. This option further obfuscates the package name, confusing the classes in the package into n and repackaging them into packages

-repackageclasses [package_name]

A flattening the packet into n classes and repackaging them into a uniform package overrides the packageHierarchy option

-keepattributes [attribute_filter]

The following things can be removed when confused. If you want to keep them, you need to use this option for general Annotation handling such as -keepAttributes Annotation.

attribute_filter :

  • Exceptions,
  • Signature,
  • Deprecated,
  • SourceFile,
  • SourceDir,
  • LineNumberTable,
  • LocalVariableTable,
  • LocalVariableTypeTable,
  • Synthetic,
  • #EnclosingMethod,
  • RuntimeVisibleAnnotations,
  • RuntimeInvisibleAnnotations,
  • RuntimeVisibleParameterAnnotations,
  • RuntimeInvisibleParameterAnnotations,
  • AnnotationDefault.

-dontpreverify

Specify not to perform precheck

-verbose

Output all information, not just error messages

-dontnote [class_filter]

Does not output error information for the specified class.

-dontwarn [class_filter]

Does not print a warning for the specified class

-ignorewarnings

If a warning is encountered, continue to execute ProGuard regardless of the warning. It is not recommended to add this item.

-printconfiguration [filename]

Output the current configuration used by ProGuard

-dump [filename]

Specifies the structure of the class to be processed by the output

Reflection processing

In code, if reflection is used, obfuscation will change the name of the class and member, and reflection will not find the corresponding class or method, so the developer must leave the class that reflection is used in the obfuscation. In general, reflection can be used in the following ways: search the code, find the relevant class, and then preserve it in the obfuscation configuration:

  • Class.forName(“SomeClass”)
  • SomeClass.class
  • SomeClass.class.getField(“someField”)
  • SomeClass.class.getDeclaredField(“someField”)
  • SomeClass.class.getMethod(“someMethod”, new Class[] {})
  • SomeClass.class.getMethod(“someMethod”, new Class[] { A.class })
  • SomeClass.class.getMethod(“someMethod”, new Class[] { A.class, B.class })
  • SomeClass.class.getDeclaredMethod(“someMethod”, new Class[] {})
  • SomeClass.class.getDeclaredMethod(“someMethod”, new Class[] { A.class })
  • SomeClass.class.getDeclaredMethod(“someMethod”, new Class[] { A.class, B.class })
  • AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, “someField”)
  • AtomicLongFieldUpdater.newUpdater(SomeClass.class, “someField”)
  • AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, “someField”)
The # reflectClass class uses reflection, leaving it as it is
-keep class package.reflectClass { *; }
Copy the code

Basic use of PrgGuard demo

# Code obfuscation compression ratio, between 0 and 7, default is 5, generally do not change
-optimizationpasses 5
# specify obfuscation is the algorithm used, the following parameter is a filter, this filter is Google recommended algorithm, generally not changed-optimizations ! code/simplification/cast,! field/*,! class/merging/*# Do not use mixed case when mixing, the mixed class name is lowercase, Windows must use this option
-dontusemixedcaseclassnames

# specify that classes and members that are not public libraries are not ignored
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers

Print details
-verbose
# Output class name -> obturate the mapping of the class name
-printmapping map.txt

Preverify is one of proGuard's four steps. Android does not require preVerify, and removing this step can speed up obfuscation.
-dontpreverify

# leave annotations unconfused
-keepattributes *Annotation*,InnerClasses

# Avoid confusing generics
-keepattributes Signature

Keep the line number when throwing an exception
-keepattributes SourceFile,LineNumberTable

Keep native methods unconfused
-keepclasseswithmembernames class * {
    native <methods>;
}

Keep enumeration classes from being confused
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

Leave Serializable classes unconfused
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    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

In addition, due to the Android platform when using confusion, it is important to add the following configuration:

Keep the four major components we use, custom applications, etc. These classes are not confused
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService

Keep all classes under support and their inner classes-keep class android.support.** {*; }Save resources under R-keep class **.R$* {*; }The method parameters retained in the Activity are the view methods,
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}

Keep our custom controls (inherited from View) unconfused
-keep public class * extends android.view.View{
    *** get*();
    void set* * * * (); public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); }Leave the Parcelable serialized class unconfused
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

# Do not confuse onXXEvent with a callback function
-keepclassmembers class * {
    void *(**On*Event);
}

# Use webView in our app requires special treatment
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.webView, jav.lang.String);
}

# Special handling of interactions with HTML5 JavaScript in the app, such as
# package com.ljd.example;
#
# public class JSInterface {
# @JavascriptInterface
# public void callAndroidMethod(){
# // do something
#}
#}
# We need to make sure that the native methods that these JS are calling cannot be confused, so we need to do the following
-keepclassmembers class com.ljd.example.JSInterface {
    <methods>;
}

If you need to preserve an inline class, use the following method to preserve the inline class, such as in MyClass. $is used to separate the inline class from its parent-keep class com.test.MyClass$* {*; }#----------- Handle the reflection class --------------- below

Copy the code