ProGuard is an open source tool that optimizes and obfuscates Android bytecode. It shrinks code size by removing unused code and resources, optimizes code performance through techniques like constant propagation and method inlining, and obfuscates code to make it harder to reverse engineer by renaming classes and fields. Using ProGuard can reduce APK size by 5-70% and improve performance by up to 20%. It is enabled in Android builds by adding configuration files that specify how to process the code.
8. Application size
classes.dex size .apk size
Without
ProGuard
With
ProGuard
Reduction
ApiDemos 716 K 482 K 33 % 2.6 M 2.5 M 4 %
GoogleIO
App
3.4 M 905 K 75 % 1.9 M 906 K 53 %
ApiDemos
in Scala*
~6 M 542 K ~90 % ~8 M 2.5 M ~70 %
* [Stéphane Micheloud, http://lampwww.epfl.ch/~michelou/android/library-code-shrinking.html]
12. How to enable ProGuard?
Ant and Eclipse: project.properties
→ only applied when building release versions
# To enable ProGuard to shrink and obfuscate
your code, uncomment this
#proguard.config=
${sdk.dir}/tools/proguard/proguard-android.txt:
proguard-project.txt
# To enable ProGuard to shrink and obfuscate
your code, uncomment this
#proguard.config=
${sdk.dir}/tools/proguard/proguard-android.txt:
proguard-project.txt
Tip
14. Notes and warnings
“Closed-world assumption”
→ if debug build works fine,
then ok to ignore in proguard-project.txt:
Warning: com.dropbox.client2.DropboxAPI:
can't find referenced class org.json.simple.JSONArray
Warning: com.dropbox.client2.DropboxAPI:
can't find referenced class org.json.simple.JSONArray
-dontwarn twitter4j.internal.logging.**
-dontwarn com.dropbox.client2.**
-dontwarn twitter4j.internal.logging.**
-dontwarn com.dropbox.client2.**
Warning: twitter4j.internal.logging.Log4JLoggerFactory:
can't find referenced class org.apache.log4j.Logger
Warning: twitter4j.internal.logging.SLF4JLoggerFactory:
can't find referenced class org.slf4j.LoggerFactory
...
Warning: twitter4j.internal.logging.Log4JLoggerFactory:
can't find referenced class org.apache.log4j.Logger
Warning: twitter4j.internal.logging.SLF4JLoggerFactory:
can't find referenced class org.slf4j.LoggerFactory
...
Tip
15. Notes and warnings
Straight to the Troubleshooting page:
Warning: there were 12 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)
Warning: there were 12 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)
New
18. Entry points
1) Activities, applications, services, fragments,...
→ provided automatically by Android build process
-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.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
…
19. Entry points
2) Introspection, e.g. Guice, RoboGuice
→ must be specified in proguard-project.txt:
-keepclassmembers class * {
@javax.inject.** <fields>;
@com.google.inject.** <fields>;
@roboguice.** <fields>;
@roboguice.event.Observes <methods>;
}
-keepclassmembers class * {
@javax.inject.** <fields>;
@com.google.inject.** <fields>;
@roboguice.** <fields>;
@roboguice.event.Observes <methods>;
}
Tip
20. Optimization
At the bytecode instruction level:
●
Dead code elimination
●
Constant propagation
●
Method inlining
●
Class merging
●
Remove logging code
●
Peephole optimizations
●
Devirtualization
●
...
21. Optimization example
int answer = computeAnswer(1, 2, 3, 7);int answer = computeAnswer(1, 2, 3, 7);
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
22. Optimization example
int answer = computeAnswer(1, 2, 3, 7);int answer = computeAnswer(1, 2, 3, 7);
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
23. Optimization example
int answer = computeAnswer(1, 2, 3, 7);int answer = computeAnswer(1, 2, 3, 7);
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
1 2 3 7
24. Optimization example
int answer = computeAnswer(1, 2, 3, 7);int answer = computeAnswer(1, 2, 3, 7);
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
} int computeAnswer() {
return 42;
}
int computeAnswer() {
return 42;
}
25. Optimization example
int answer = computeAnswer(1, 2, 3, 7);int answer = computeAnswer(1, 2, 3, 7);
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
return computeAnswer(f1 * f2, f3, f4, 1);
}
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
}
int computeAnswer(int f1, int f2, int f3, int f4) {
do {
if (f2 == 1 && f3 == 1 && f4 == 1) {
return f1;
} else {
f1 = f1 * f2; f2 = f3, f3 = f4, f4 = 1;
}
} while (true);
} int computeAnswer() {
return 42;
}
int computeAnswer() {
return 42;
}
int answer = 42;int answer = 42;
26. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
New
27. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
New
28. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
New
class Internal {
static final int[] $SwitchMap =
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.RED.ordinal()] = 1;
$SwitchMap[MyEnum.GREEN.ordinal()] = 2;
}
}
class Internal {
static final int[] $SwitchMap =
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.RED.ordinal()] = 1;
$SwitchMap[MyEnum.GREEN.ordinal()] = 2;
}
}
29. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
New
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
int answer = getAnswer(2);int answer = getAnswer(2);
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
30. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
New
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
int answer = getAnswer(2);int answer = getAnswer(2);
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
int getAnswer(int e) {
switch (e) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(int e) {
switch (e) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
31. Optimization: enum
int answer = getAnswer(MyEnum.GREEN);int answer = getAnswer(MyEnum.GREEN);
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (e) {
case RED: return 1;
case GREEN: return 42;
default: return 0;
}
}
enum MyEnum {
RED, GREEN, BLUE
}
enum MyEnum {
RED, GREEN, BLUE
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(MyEnum e) {
switch (Internal.$SwitchMap[e.ordinal()]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
class MyEnum {
static final MyEnum RED = new MyEnum(“RED”, 0);
static final MyEnum GREEN = new MyEnum(“GREEN”, 0);
static final MyEnum BLUE = new MyEnum(“BLUE”, 0);
static final MyEnum[] $VALUES =
new MyEnum[] { RED, GREEN, BLUE };
...
}
New
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
class Internal {
static final int[] $SwitchMap
new int[MyEnum.values().length];
static {
$SwitchMap[MyEnum.GREEN.ordinal()] = 1;
$SwitchMap[MyEnum.RED.ordinal()] = 2;
}
}
int answer = getAnswer(2);int answer = getAnswer(2);
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(int e) {
switch (Internal.$SwitchMap[e]) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
class Internal {
static final int[] $SwitchMap =
new int[3];
static {
$SwitchMap[1] = 1;
$SwitchMap[2] = 2;
}
}
int getAnswer(int e) {
switch (e) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int getAnswer(int e) {
switch (e) {
case 1: return 1;
case 2: return 42;
default: return 0;
}
}
int answer = 42;int answer = 42;
32. How to enable optimization?
Ant and Eclipse: project.properties
# To enable ProGuard to shrink and obfuscate
your code, uncomment this
proguard.config=
${sdk.dir}/tools/proguard/proguard-android-optimize.txt:
proguard-project.txt
# To enable ProGuard to shrink and obfuscate
your code, uncomment this
proguard.config=
${sdk.dir}/tools/proguard/proguard-android-optimize.txt:
proguard-project.txt
Tip
34. Remove logging code
Specify assumptions in proguard-project.txt:
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
public static java.lang.String getStackTraceString
(java.lang.Throwable);
}
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
public static java.lang.String getStackTraceString
(java.lang.Throwable);
}
Tip
36. Obfuscation example
public class MyComputationClass {
private MySettings settings;
private MyAlgorithm algorithm;
private int answer;
public int computeAnswer(int input) {
…
return answer;
}
}
public class MyComputationClass {
private MySettings settings;
private MyAlgorithm algorithm;
private int answer;
public int computeAnswer(int input) {
…
return answer;
}
}
37. Obfuscation example
public class MyComputationClass {
private MySettings settings;
private MyAlgorithm algorithm;
private int answer;
public int computeAnswer(int input) {
…
return answer;
}
}
public class MyComputationClass {
private MySettings settings;
private MyAlgorithm algorithm;
private int answer;
public int computeAnswer(int input) {
…
return answer;
}
}
public class a {
private b a;
private c b;
private int c;
public int a(int a) {
…
return c;
}
}
public class a {
private b a;
private c b;
private int c;
public int a(int a) {
…
return c;
}
}
39. What about ART?
The new (experimental) Android RunTime
Application
Java bytecode
ProGuard
Libraries
Java bytecode
Processed
Java bytecode Dex
Dalvik bytecode
Dex2oat
Native code
Development
Device
40. More application protection?
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
41. More application protection?
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
public class a {
public int a() {
…
return a
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
public class a {
public int a() {
…
return a
.getPackageManager()
.checkSignatures(“mypackage1”, “mypackage2”);
}
}
61. Conclusion
Nothing is unbreakable, but you can raise the bar:
●
ProGuard
●
String encryption
●
Reflection
●
Dynamic class loading
●
Native code
●
Data encryption
●
Application checks
●
Environment checks
●
… DexGuard
62. DexGuard
●
Specialized for Android
●
Code and data
●
Layers of protection
●
Recommended techniques
●
Low-level techniques
●
Transparent
Tip
Code
Resources
Assets
Signatures
63. ProGuard - DexGuard
Open source
Generic
Shrinker
Optimizer
Obfuscator
For Java bytecode
Closed source
Specialized
Shrinker
Optimizer
Obfuscator
Protector
For Android
Compatible