The Ant/Gradle/Eclipse/Maven build processes of DexGuard automatically
invoke DexGuard for you, with settings that provide a good basis for most
projects. You only need to consider this section if you create your own build
process, or if your code requires custom settings.
DexGuard is compatible with ProGuard configuration. You typically add custom
configuration to proguard-project.txt or
dexguard-project.txt. The sections below provide more details,
with the extensions highlighted:
Keep Options@filename-include
filename'.-include
filename-basedirectory
directoryname-injars
class_path-injars options.-outjars
class_path-injars
options will be written to the named jars. This allows you to collect the
contents of groups of input jars into corresponding groups of output jars.
In addition, the output entries can be filtered, as explained in
the filters section. Each processed class file
or resource file is then written to the first output entry with a matching
filter, within the group of output jars.
You must avoid letting the output files overwrite any input files. For
better readability, class path entries can be specified using multiple
-outjars options. Without any -outjars options,
no jars will be written.
-libraryjars
class_path-libraryjars options.
Please note that the boot path and the class path set for running DexGuard are not considered when looking for library classes. This means that you explicitly have to specify the run-time jar that your code will use.
-skipnonpubliclibraryclasses-dontskipnonpubliclibraryclasses-dontskipnonpubliclibraryclassmembers-keepdirectories
[directory_filter]mypackage.MyClass.class.getResource("")". You'll then want
to keep the directory corresponding to the package,
"-keepdirectories mypackage". If the option is specified
without a filter, all directories are kept. With a filter, only matching
directories are kept. For instance,
"-keepdirectories mydirectory" matches the specified
directory, "-keepdirectories mydirectory/*" matches its
immediate subdirectories, and
"-keepdirectories mydirectory/**" matches all of its
subdirectories.-splitdexfile
class_filterdx compiler is "Unable to
execute dex: method ID not in [0, 0xffff]: 65536"). Each instance of this
option results in a separate dex file. DexGuard automatically inserts the
necessary code to tie the dex files together. Only applicable when
targeting Android with the -dalvik
option.
Performance tip: Every reference from the main dex file to a separate dex file carries some overhead, due to reflection. If performance is important for these references, you can reduce the overhead by accessing classes through interfaces that are not in separate dex files.
Counter-indication: it is currently not possible to encrypt classes that have direct references to classes in separate dex files. You may need to consider this when deciding which classes to split.
-dontcompress
[file_filter]-zipalign option below, to
specify the optimal alignment of the data.-zipalign n-dontcompress option above, to
specify which files should not be compressed.-target version1.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). By default, the version numbers of the class files
are left unchanged. For example, you may want to upgrade class files to
Java 6, by changing their version numbers and having them preverified.
This option is not relevant for Android applications.-forceprocessing
-keep
[,modifier,...]
class_specification-keepclassmembers
[,modifier,...]
class_specificationSerializable
interface.-keepclasseswithmembers
[,modifier,...]
class_specification-keepnames
class_specification-keep,allowshrinking
class_specification
Specifies classes and class members whose names are to be preserved, if
they aren't removed in the shrinking phase. For example, you may want to
keep all class names of classes
that implement the Serializable interface, so that the
processed code remains compatible with any originally serialized classes.
Classes that aren't used at all can still be removed. Only applicable when
obfuscating.
-keepclassmembernames
class_specification-keepclassmembers,allowshrinking
class_specification
Specifies class members whose names are to be preserved, if they aren't
removed in the shrinking phase. For example, you may want to preserve the
name of the synthetic class$ methods
when processing a library compiled by
JDK 1.2 or older, so obfuscators can detect it again when processing an
application that uses the processed library (although DexGuard itself
doesn't need this). Only applicable when obfuscating.
-keepclasseswithmembernames
class_specification-keepclasseswithmembers,allowshrinking
class_specification
Specifies classes and class members whose names are to be preserved, on the condition that all of the specified class members are present after the shrinking phase. For example, you may want to keep all native method names and the names of their classes, so that the processed code can still link with the native library code. Native methods that aren't used at all can still be removed. If a class file is used, but none of its native methods are, its name will still be obfuscated. Only applicable when obfuscating.
-printseeds
[filename]-keep options. The list is printed to the standard
output or to the given file. The list can be useful to verify if the
intended class members are really found, especially if you're using
wildcards. For example, you may want to list all the applications or all the applets that you are keeping.
-dontshrink-keep options, and the ones on which
they depend, directly or indirectly. A shrinking step is also applied
after each optimization step, since some optimizations may open the
possibility to remove more classes and class members.-printusage
[filename]-whyareyoukeeping
class_specification-verbose option if specified, the traces
include full field and method signatures. Only applicable when
shrinking.
-dontoptimize-optimizations
optimization_filter-optimizationpasses n-assumenosideeffects
class_specificationSystem.currentTimeMillis() returns a value, but it doesn't
have any side effects. In the optimization step, DexGuard can then remove
calls to such methods, if it can determine that the return values aren't
used. DexGuard will analyze your program code to find such methods
automatically. It will not analyze library code, for which this option can
therefore be useful. For example, you could specify the method
System.currentTimeMillis(), so that any idle calls to it will
be removed. With some care, you can also use the option to
remove logging code. Note that
DexGuard applies the option to the entire hierarchy of the specified
methods. Only applicable when optimizing. In general, making assumptions
can be dangerous; you can easily break the processed code. Only use
this option if you know what you're doing!-assumenoexternalsideeffects
class_specification-assumenosideeffects, because it allows side effects on the
parameters or the heap. For example, the StringBuffer#append
methods have side effects, but no external side effects. Only applicable
when optimizing. Making assumptions can be dangerous; you can easily break
the processed code. Only use this option if you know what you're
doing!-assumenoescapingparameters
class_specificationSystem.arrayCopy does not let its reference parameters
escape, but method System.setSecurityManager does. Only
applicable when optimizing. Making assumptions can be dangerous; you can
easily break the processed code. Only use this option if you know what
you're doing!-assumenoexternalreturnvalues
class_specificationProcessBuilder#start returns a Process reference
value, but it is a new instance that wasn't on the heap yet. Only
applicable when optimizing. Making assumptions can be dangerous; you can
easily break the processed code. Only use this option if you know what
you're doing!-allowaccessmodification-repackageclasses option).
Counter-indication: you probably shouldn't use this option when processing code that is to be used as a library, since classes and class members that weren't designed to be public in the API may become public.
-mergeinterfacesaggressivelyCounter-indication: setting this option can reduce the performance of the processed code on some VMs, since advanced just-in-time compilation tends to favor more interfaces with fewer implementing classes. Worse, some VMs may not be able to handle the resulting code.
-dontobfuscate-keep options.
Internal attributes that are useful for debugging, such as source files
names, variable names, and line numbers are removed.-printmapping
[filename]-applymapping
filename-useuniqueclassmembernames
in both obfuscation runs. Only a single mapping file is allowed. Only
applicable when obfuscating.-obfuscationdictionary
filename# sign are ignored. Note that an
obfuscation dictionary hardly improves the obfuscation. Decent compilers
can automatically replace them, and the effect can fairly simply be undone
by obfuscating again with simpler names. The most useful application is
specifying strings that are typically already present in class files (such
as 'Code'), thus reducing the class file sizes just a little bit more.
Only applicable when obfuscating.-classobfuscationdictionary
filename-obfuscationdictionary.
Only applicable when obfuscating.-packageobfuscationdictionary
filename-obfuscationdictionary.
Only applicable when obfuscating.-overloadaggressivelyCounter-indication: the resulting class files fall within the Java bytecode specification (cfr. The Java Virtual Machine Specification, Second Edition, first paragraphs of Section 4.5 and Section 4.6), even though this kind of overloading is not allowed in the Java language (cfr. The Java Language Specification, Third Edition, Section 8.3 and Section 8.4.5). Still, some tools have problems with it. Notably, the Dalvik VM can't handle overloaded static fields.
-useuniqueclassmembernamesFor instance, consider two distinct interfaces containing methods with the same name and signature. Without this option, these methods may get different obfuscated names in a first obfuscation step. If a patch is then added containing a class that implements both interfaces, DexGuard will have to enforce the same method name for both methods in an incremental obfuscation step. The original obfuscated code is changed, in order to keep the resulting code consistent. With this option in the initial obfuscation step, such renaming will never be necessary.
This option is only applicable when obfuscating. In fact, if you are planning on performing incremental obfuscation, you probably want to avoid shrinking and optimization altogether, since these steps could remove or modify parts of your code that are essential for later additions.
-dontusemixedcaseclassnames-keeppackagenames
[package_filter]-flattenpackagehierarchy
[package_name]-repackageclasses
[package_name]-flattenpackagehierarchy
option. It is another example of further obfuscating package names. It can
make the processed code even smaller and less comprehensible. Its
deprecated name is -defaultpackage. Only applicable when
obfuscating.
Counter-indication: classes that look for resource files in their package directories will no longer work properly if they are moved elsewhere. When in doubt, just leave the packaging untouched by not using this option.
Note: On Android, you should not use the empty string when classes
like activities, views, etc. may be renamed. The Android run-time
automatically prefixes package-less names in XML files with the application
package name or with android.view. This is unavoidable but it
breaks the application in this case.
-keepattributes
[attribute_filter]-keepattributes directives. The
optional filter is a comma-separated list
of attribute names that Java virtual
machines and DexGuard support. Attribute names can
contain ?, *, and ** wildcards, and they can be
preceded by the ! negator. For example, you should at least keep
the Exceptions, InnerClasses, and
Signature attributes when
processing a library. You should also
keep the SourceFile and LineNumberTable
attributes for producing useful
obfuscated stack traces. Finally, you may want
to keep annotations if your code
depends on them. Only applicable when obfuscating.-keepparameternamesLocalVariableTable and LocalVariableTypeTable.
It can be useful when processing a library. Some IDEs can use the
information to assist developers who use the library, for example with
tool tips or autocompletion. Only applicable when obfuscating. This
option is not relevant for Android applications.-renamesourcefileattribute
[string]SourceFile
attributes (and SourceDir attributes) of the class files.
Note that the attribute has to be present to start with, so it also has to
be preserved explicitly using the -keepattributes directive.
For example, you may want to have your processed libraries and
applications produce useful obfuscated
stack traces. Only applicable when obfuscating.-accessthroughreflection
class_specificationClass.forName constructs. Reads and writes of matching
fields are replaced by Class.getField and
Field.get/set constructs. Method invocation are replaced
by Class.getMethod and Method.invoke constructs.
For example, when processing the Android License Verification Library, you
may want to add reflection
for sensitive APIs in the Android run-time. Only applicable when
obfuscating.-encryptclasses
[class_filter]Counter-indications: It is not possible to encrypt classes that are explicitly preserved from obfuscation (in your configuration), extended by non-encrypted classes, or created by reflection (for instance because they are referenced from XML files).
Note: When encrypting classes in library projects, the code must set a temporary directory:
System.setProperty("java.io.tmpdir", getDir("files", Context.MODE_PRIVATE).getPath());
Caveat: When encrypting classes in library projects, the encrypted classes must not contain references to classes or class members that are later on obfuscated in the final application projects. Once encrypted, classes can no longer be changed, so their references would become invalid. If encrypted classes do contain references to other non-encrypted classes in their library projects, these referenced classes and class members must be preserved from obfuscation in the application projects. If your requirements allow it, it is easier to encrypt classes when processing the final application.
Performance tip: Every access from an external class to an encrypted class carries some overhead, due to reflection. If performance is important in this part of your code, you can reduce the overhead by accessing the class through an interface that is not encrypted.
-encryptstrings
[string_filter]
-encryptstrings
class_specificationString encryption makes it more difficult to find them in disassembled or decompiled code. Again, "obfuscated" would be a better word, since the processed code necessarily has to be able to reverse the encryption. It therefore increases the code size and introduces processing overhead whenever the string is accessed. But again, it raises the bar for any reverse engineering attempts. For example, if you have hard-coded some key string, you may want to encrypt it to hide it a bit better. Also, if you are already accessing a class or a class member through introspection, you may want to obfuscate the resulting strings containing their names in the code. Only applicable when obfuscating.
Caveat: The current implementation does not support encrypting strings in static initializers in interfaces. Final string constants in interfaces are not a problem.
Performance tip: Every encrypted string is decrypted on the fly at
run-time, without implicit caching. If performance of encrypted strings is
important in some parts of your code, you can define them as private
static String fields, so they are only decrypted once, when the
class is initialized. Don't declare them as final though,
because the compiler will then inline them again.
-adaptclassstrings
[class_filter]-adaptresourcefilenames
[file_filter]-adaptresourcefilecontents
[file_filter]Caveat: You probably only want to apply this option to text files, since parsing and adapting binary files as text files can cause unexpected problems. Therefore, make sure that you specify a sufficiently narrow filter.
-keepresourcexmlattributenames
[name_filter]res directory and contain XML elements with attributes.
Each attribute is identified by a name, but often also by a numeric
identifier. The obfuscation step can then remove the name. you can
preserve it with this option, if it is still required, for
AttributeSet#getAttributeValue(namespace, name) or
AttributeSet#getAttributeName(id). The filter is applied to
strings of the form "element/element/.../element/attribute", based on the
names of the nested elements and the attributes. For example, the Android
runtime somewhat arbitrarily requires the names of the attributes
"installLocation", "versionCode", and "name", in some elements in the
Android manifest file. The examples for processing
a complete Android
application and for publishing
on the Samsung app market show how they are preserved. Only applicable
when obfuscating Android code.-encryptassetfiles
[file_filter]assets directory and can contain any data.
The obfuscation step can automatically encrypt them and make sure they are
decrypted on the fly at run-time. In order for this to work, the asset
files must be streamed and their names must be specified as string
constants. This means that your code must invoke
the AsssetManager as follows:
open("MyAssetFile")
Your configuration can then specify "-encryptassetfiles
assets/MyAssetFile". Only applicable when obfuscating Android
code.-encryptnativelibraries
[file_filter]lib
directory. The obfuscation step can automatically encrypt them and make
sure they are decrypted on the fly at run-time. In order for this to work,
your application must load the native library with a hard-coded library
name: System.loadLibrary("mycode"). Your configuration can
then specify "-encryptnativelibraries lib/**/libmycode.so",
matching the complete path and name of the library. Note the wildcards: if
a library is encrypted for one platform, it must be encrypted for all
platforms. Only applicable when obfuscating Android code.
Note: When encrypting classes in library projects, the code should set a temporary directory, for maximum portability:
System.setProperty("java.io.tmpdir", getDir("files", Context.MODE_PRIVATE).getPath());
-dontpreverify-microedition-android
-dalvikclasses.dex
file instead of many .class files. For example, you probably
want to use this option if you
are processing Android
applications.
-keystore
filename-keystorepassword
password-keyalias alias-keypassword
password
-verbose-dontnote
[class_filter]-dontwarn
[class_filter]-ignorewarnings-printconfiguration
[filename]-dump
[filename]
Each input entry can be:
The paths of directly specified class files and resource files is ignored, so class files should generally be part of a jar file, an aar file, a war file, an ear file, a zip file, or a directory. In addition, the paths of class files should not have any additional directory prefixes inside the archives or directories.
Each output entry can be:
When writing output entries, DexGuard will generally package the results in a sensible way, reconstructing the input entries as much as required. Writing everything to an output directory is the most straightforward option: the output directory will contain a complete reconstruction of the input entries. The packaging can be almost arbitrarily complex though: you could process an entire application, packaged in a zip file along with its documentation, writing it out as a zip file again. The Examples section shows a few ways to restructure output archives.
Files and directories can be specified as discussed in the section on file names below.
In addition, DexGuard provides the possibility to filter the class path entries and their contents, based on their full relative file names. Each class path entry can be followed by up to 7 types of file filters between parentheses, separated by semi-colons:
If fewer than 7 filters are specified, they are assumed to be the latter filters. Any empty filters are ignored. More formally, a filtered class path entry looks like this:
classpathentry([[[[[[aarfilter;]apkfilter;]zipfilter;]earfilter;]warfilter;]jarfilter;]filefilter)
Square brackets "[]" mean that their contents are optional.
For example, "android.jar(java/**.class,javax/**.class)" matches
all class files in the java and javax directories
inside the android jar.
For example, "input.jar(!**.gif,images/**)" matches all files in
the images directory inside the input jar, except
gif files.
The different filters are applied to all corresponding file types, irrespective of their nesting levels in the input; they are orthogonal.
For example,
"input.war(lib/**.jar,support/**.jar;**.class,**.gif)" only
considers jar files in the lib and support
directories in the input war, not any other jar files. It then
matches all class files and gif files that are encountered.
The filters allow for an almost infinite number of packaging and repackaging possibilities. The Examples section provides a few more examples for filtering input and output.
The names can contain Java system properties (or Ant properties, when using Ant), delimited by angular brackets, '<' and '>'. The properties are automatically replaced by their corresponding values.
For example, <java.home>/lib/rt.jar is automatically
expanded to something like /usr/local/java/jdk/jre/lib/rt.jar.
Similarly, <user.home> is expanded to the user's home
directory, and <user.dir> is expanded to the current
working directory.
Names with special characters like spaces and parentheses must be quoted with single or double quotes. Each file name in a list of names has to be quoted individually. Note that the quotes themselves may need to be escaped when used on the command line, to avoid them being gobbled by the shell.
For example, on the command line, you could use an option like '-injars
"my program.jar":"/your directory/your program.jar"'.
? |
matches any single character in a file name. |
* |
matches any part of a filename not containing the directory separator. |
** |
matches any part of a filename, possibly containing any number of directory separators. |
java/**.class,javax/**.class" matches all
class files in the java and javax.
Furthermore, a file name can be preceded by an exclamation mark '!' to exclude the file name from further attempts to match with subsequent file names.
For example, "!**.gif,images/**" matches all files in the
images directory, except gif files.
The Examples section provides a few more examples for filtering input and output.
A filter is a list of comma-separated names that can contain wildcards. Only names that match an item on the list pass the filter. The supported wildcards depend on the type of names for which the filter is being used, but the following wildcards are typical:
? |
matches any single character in a name. |
* |
matches any part of a name not containing the package separator or directory separator. |
** |
matches any part of a name, possibly containing any number of package separators or directory separators. |
foo,*bar" matches the name foo and
all names ending with bar.
Furthermore, a name can be preceded by a negating exclamation mark '!' to exclude the name from further attempts to match with subsequent names. So, if a name matches an item in the filter, it is accepted or rejected right away, depending on whether the item has a negator. If the name doesn't match the item, it is tested against the next item, and so on. It if doesn't match any items, it is accepted or rejected, depending on the whether the last item has a negator or not.
For example, "!foobar,*bar" matches all names ending with
bar, except foobar.
Keep Options-keep options for shrinking and obfuscation may seem
a bit confusing at first, but there's actually a pattern behind them. The
following table summarizes how they are related:
| Keep | From being removed or renamed | From being renamed |
|---|---|---|
| Classes and class members | -keep |
-keepnames |
| Class members only | -keepclassmembers |
-keepclassmembernames |
| Classes and class members, if class members present | -keepclasseswithmembers |
-keepclasseswithmembernames |
Each of these -keep options is of course followed by a
specification of the classes and class
members (fields and methods) to which it should be applied.
If you're not sure which option you need, you should probably simply use
-keep. It will make sure the specified classes and class members
are not removed in the shrinking step, and not renamed in the obfuscation step.
includedescriptorclassesallowshrinkingallowoptimizationallowobfuscation
-keep options and in the
-assumenosideeffects option. The corresponding option is only
applied to classes and class members that match the template.
The template was designed to look very Java-like, with some extensions for wildcards. To get a feel for the syntax, you should probably look at the 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 ... ] *;
...
}]
Square brackets "[]" mean that their contents are optional. Ellipsis dots "..." mean that any number of the preceding items may be specified. A vertical bar "|" delimits two alternatives. Non-bold parentheses "()" just group parts of the specification that belong together. The indentation tries to clarify the intended meaning, but white-space is irrelevant in actual configuration files.
class keyword refers to any interface or class.
The interface keyword restricts matches to interface
classes. The enum keyword restricts matches to
enumeration classes. Preceding the interface or
enum keywords by a ! restricts
matches to classes that are not interfaces or enumerations,
respectively.java.lang.String. Inner classes are separated by a dollar sign
"$", e.g. java.lang.Thread$State. Class names
may be specified as regular
expressions containing the following wildcards:
? |
matches any single character in a class name, but not the package
separator. For example, "mypackage.Test?" matches
"mypackage.Test1" and "mypackage.Test2", but not
"mypackage.Test12". |
* |
matches any part of a class name not containing the package separator. For
example, "mypackage.*Test*" matches
"mypackage.Test" and
"mypackage.YourTestApplication", but not
"mypackage.mysubpackage.MyTest". Or, more generally,
"mypackage.*" matches all classes in
"mypackage", but not in its subpackages. |
** |
matches any part of a class name, possibly containing any number of
package separators. For example, "**.Test" matches all
Test classes in all packages except the root package. Or,
"mypackage.**" matches all classes in
"mypackage" and in its subpackages. |
! negators, just
like file name filters. This notation doesn't look very Java-like, so it
should be used with moderation.
For convenience and for backward compatibility, the class name
* refers to any class, irrespective of its package.
extends and implements
specifications are typically used to restrict classes with wildcards. They
are currently equivalent, specifying that only classes extending or
implementing the given class qualify. Note that the given class itself is
not included in this set. If required, it should be specified in a
separate option.@ specifications can be used to restrict classes
and class members to the ones that are annotated with the specified
annotation types. An annotationtype is specified just like a
classname.javadoc and javap). The specifications can
also contain the following catch-all wildcards:
<init> |
matches any constructor. |
<fields> |
matches any field. |
<methods> |
matches any method. |
* |
matches any field or method. |
<init> wildcard has an argument list.
Fields and methods may also be specified using regular expressions. Names can contain the following wildcards:
? |
matches any single character in a method name. |
* |
matches any part of a method name. |
% |
matches any primitive type ("boolean", "int",
etc, but not "void"). |
? |
matches any single character in a class name. |
* |
matches any part of a class name not containing the package separator. |
** |
matches any part of a class name, possibly containing any number of package separators. |
*** |
matches any type (primitive or non-primitive, array or non-array). |
... |
matches any number of arguments of any type. |
?, *, and **
wildcards will never match primitive types. Furthermore, only the
*** wildcards will match array types of any dimension. For
example, "** get*()" matches "java.lang.Object
getObject()", but not "float getFloat()", nor
"java.lang.Object[] getObjects()".! specifies that the corresponding access
flag should be unset.
Combining multiple flags is allowed (e.g. public static). It
means that both access flags have to be set (e.g. public
and static), except when they are conflicting, in
which case at least one of them has to be set (e.g. at least
public
or protected).
DexGuard supports the additional modifiers synthetic,
bridge, and varargs, which may be
set by compilers.