SlideShare a Scribd company logo
#mceconf #mce-proguard 11 Jan 2014
ProGuard
Optimizer and Obfuscator
in the Android SDK
Eric Lafortune
Developer of ProGuard
Technical director at Saikoa
#mceconf #mce-proguard 11 Jan 2014
ProGuard
Generic
Shrinker
Optimizer
Obfuscator
For Java bytecode
Open source
#mceconf #mce-proguard 11 Jan 2014
ProGuard history
Java applications
Applets
2002
Midlets
2010 2012
Android apps
●
May 2002 First release
●
Sep 2010 Recommended for protecting LVL
●
Dec 2010 Part of Android SDK
●
Jan 2012 Startup Saikoa
#mceconf #mce-proguard 11 Jan 2014
ProGuard
Application
code
Libraries
Runtime
libraries
Shrink Optimize Obfuscate
Processed
code
Runtime
libraries
#mceconf #mce-proguard 11 Jan 2014
ProGuard in Android builds
Application
Java bytecode
ProGuard
Libraries
Java bytecode
Processed
Java bytecode Dex
Dalvik bytecode
Javac
Application
Java source
Libraries
Java bytecode
XML resources
Assets Assets
Compiled
XML resourcesAapt
#mceconf #mce-proguard 11 Jan 2014
Why use ProGuard?
●
Application size
●
Performance
●
Remove logging, debugging, testing code
●
Protection
#mceconf #mce-proguard 11 Jan 2014
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]
#mceconf #mce-proguard 11 Jan 2014
Performance: CaffeineMark
Without ProGuard
Sieve score = 6833
Loop score = 14831
Logic score = 19038
String score = 7694
Float score = 6425
Method score = 4850
Overall score = 8794
With ProGuard
Sieve score = 6666
Loop score = 15473
Logic score = 47840
String score = 7717
Float score = 6488
Method score = 5229
Overall score = 10436
Improvement: 18%
[Acer Iconia Tab A500, nVidia Tegra 2, 1.0 GHz, Android 3.2.1]
#mceconf #mce-proguard 11 Jan 2014
Results
●
Size reduction:
– Code (classes.dex): 20 … 90%
– Application (.apk): 5 ... 70%
●
Performance improvements: 0 … 20%
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
How to enable ProGuard?
Gradle: build.gradle
→ completely flexible
android {
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
some_flavor {
proguardFile 'proguard-project.txt'
}
}
}
android {
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
some_flavor {
proguardFile 'proguard-project.txt'
}
}
}
New
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
Shrinking
Also called treeshaking, minimizing, shrouding
#mceconf #mce-proguard 11 Jan 2014
Shrinking
●
Classes, fields, methods
#mceconf #mce-proguard 11 Jan 2014
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
…
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
Optimization
At the bytecode instruction level:
●
Dead code elimination
●
Constant propagation
●
Method inlining
●
Class merging
●
Remove logging code
●
Peephole optimizations
●
Devirtualization
●
...
#mceconf #mce-proguard 11 Jan 2014
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);
}
}
#mceconf #mce-proguard 11 Jan 2014
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);
}
#mceconf #mce-proguard 11 Jan 2014
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);
}
#mceconf #mce-proguard 11 Jan 2014
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;
}
#mceconf #mce-proguard 11 Jan 2014
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;
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
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
}
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
#mceconf #mce-proguard 11 Jan 2014
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 };
...
}
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;
}
}
New
#mceconf #mce-proguard 11 Jan 2014
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 };
...
}
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;
}
}
New
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;
}
}
#mceconf #mce-proguard 11 Jan 2014
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 };
...
}
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;
}
}
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;
}
}
New
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;
}
}
#mceconf #mce-proguard 11 Jan 2014
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 };
...
}
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;
}
}
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;
New
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;
}
}
#mceconf #mce-proguard 11 Jan 2014
How to enable optimization?
Ant and Eclipse: project.properties
Tip
# 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
#mceconf #mce-proguard 11 Jan 2014
How to enable optimization?
Gradle: build.gradle
android {
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
}
}
productFlavors {
some_flavor {
proguardFile 'proguard-project.txt'
}
}
}
android {
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
}
}
productFlavors {
some_flavor {
proguardFile 'proguard-project.txt'
}
}
}
New
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
Obfuscation
Traditional name obfuscation:
●
Rename identifiers:
class/field/method names
●
Remove debug information:
line numbers, local variable names,...
#mceconf #mce-proguard 11 Jan 2014
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;
}
}
#mceconf #mce-proguard 11 Jan 2014
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;
}
}
#mceconf #mce-proguard 11 Jan 2014
Complementary steps
Optimization Obfuscation
Irreversibly remove information
#mceconf #mce-proguard 11 Jan 2014
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
#mceconf #mce-proguard 11 Jan 2014
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”);
}
}
#mceconf #mce-proguard 11 Jan 2014
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”);
}
}
#mceconf #mce-proguard 11 Jan 2014
More application protection
Nothing is unbreakable, but you can raise the bar:
●
ProGuard
●
String encryption
●
Reflection
●
Dynamic class loading
●
Native code
●
Data encryption
●
… DexGuard
#mceconf #mce-proguard 11 Jan 2014
ProGuard - DexGuard
Open source
Generic
Shrinker
Optimizer
Obfuscator
For Java bytecode
Closed source
Specialized
Shrinker
Optimizer
Obfuscator
Protector
For Android
Compatible
#mceconf #mce-proguard 11 Jan 2014
ProGuard guide – Android SDK
developer.android.comdeveloper.android.com
#mceconf #mce-proguard 11 Jan 2014
ProGuard website
proguard.sourceforge.netproguard.sourceforge.net
#mceconf #mce-proguard 11 Jan 2014
ProGuard manualTip
proguard.sourceforge.netproguard.sourceforge.net
#mceconf #mce-proguard 11 Jan 2014
Saikoa website
www.saikoa.comwww.saikoa.com
#mceconf #mce-proguard 11 Jan 2014
Questions?
Open source
Shrinking
Optimization
Obfuscation
Java bytecode
ProGuard
Saikoa
DexGuard
Dalvik bytecode
Protection

More Related Content

PDF
Eric Lafortune - ProGuard and DexGuard for optimization and protection
PDF
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
PDF
ProGuard / DexGuard Tips and Tricks
PDF
OWF12/PAUG Conf Days Pro guard optimizer and obfuscator for android, eric l...
PDF
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
PDF
Eric Lafortune - Fighting application size with ProGuard and beyond
PPTX
Tools and Techniques for Understanding Threading Behavior in Android*
PPTX
20130530-PEGjs
Eric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
ProGuard / DexGuard Tips and Tricks
OWF12/PAUG Conf Days Pro guard optimizer and obfuscator for android, eric l...
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - Fighting application size with ProGuard and beyond
Tools and Techniques for Understanding Threading Behavior in Android*
20130530-PEGjs

What's hot (19)

PDF
Qe Reference
PDF
Mutation Testing at BzhJUG
PPTX
Navigating the xDD Alphabet Soup
PPTX
Scientific calcultor-Java
PPT
Computer Programming- Lecture 4
DOCX
Simulado java se 7 programmer
PDF
C++ game development with oxygine
PPT
Lecture 12: Classes and Files
PPT
E:\Plp 2009 2\Plp 9
PPTX
Kpi driven-java-development-fn conf
PDF
Functional Programming with Groovy
PDF
The Ring programming language version 1.2 book - Part 59 of 84
PPTX
Applying Compiler Techniques to Iterate At Blazing Speed
PDF
TDD Hands-on
PPTX
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
PDF
Improving Android Performance at Droidcon UK 2014
PDF
Some stuff about C++ and development
PDF
Introduction to Erlang
PDF
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
Qe Reference
Mutation Testing at BzhJUG
Navigating the xDD Alphabet Soup
Scientific calcultor-Java
Computer Programming- Lecture 4
Simulado java se 7 programmer
C++ game development with oxygine
Lecture 12: Classes and Files
E:\Plp 2009 2\Plp 9
Kpi driven-java-development-fn conf
Functional Programming with Groovy
The Ring programming language version 1.2 book - Part 59 of 84
Applying Compiler Techniques to Iterate At Blazing Speed
TDD Hands-on
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
Improving Android Performance at Droidcon UK 2014
Some stuff about C++ and development
Introduction to Erlang
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
Ad

Viewers also liked (20)

PDF
Segurança no Desenvolvimento de App`s
PDF
Linkedin.com DomXss 04-08-2014
PDF
iOS Masque Attack
PDF
Concrete5 Multiple Reflected XSS Advisory
PDF
Concrete5 Sendmail RCE Advisory
PDF
Comparing DOM XSS Tools On Real World Bug
PDF
Mobile application security – effective methodology, efficient testing! hem...
PDF
Mobile Banking Security: Challenges, Solutions
PDF
Advanced JS Deobfuscation
PDF
Security Testing Mobile Applications
PDF
Reverse Engineering iOS apps
PDF
The old is new, again. CVE-2011-2461 is back!
PDF
Cybersecurity - Mobile Application Security
PDF
I Want More Ninja – iOS Security Testing
PPTX
The curious case of mobile app security.pptx
PPTX
AppSec EU 2016: Automated Mobile Application Security Assessment with MobSF
PDF
Mobile Application Security Testing, Testing for Mobility App | www.idexcel.com
PPTX
Pentesting iOS Applications
PDF
Mobile Application Security
PPTX
Automated Security Analysis of Android & iOS Applications with Mobile Securit...
Segurança no Desenvolvimento de App`s
Linkedin.com DomXss 04-08-2014
iOS Masque Attack
Concrete5 Multiple Reflected XSS Advisory
Concrete5 Sendmail RCE Advisory
Comparing DOM XSS Tools On Real World Bug
Mobile application security – effective methodology, efficient testing! hem...
Mobile Banking Security: Challenges, Solutions
Advanced JS Deobfuscation
Security Testing Mobile Applications
Reverse Engineering iOS apps
The old is new, again. CVE-2011-2461 is back!
Cybersecurity - Mobile Application Security
I Want More Ninja – iOS Security Testing
The curious case of mobile app security.pptx
AppSec EU 2016: Automated Mobile Application Security Assessment with MobSF
Mobile Application Security Testing, Testing for Mobility App | www.idexcel.com
Pentesting iOS Applications
Mobile Application Security
Automated Security Analysis of Android & iOS Applications with Mobile Securit...
Ad

Similar to Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK (20)

PDF
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
PDF
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
PDF
Code Generation in PHP - PHPConf 2015
PDF
DeVry GSP 115 All Assignments latest
DOCX
Assignment #2.classpathAssignment #2.project Assig.docx
PDF
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
PDF
C++ Question on References and Function Overloading
PDF
The following is the (incomplete) header file for the class Fracti.pdf
PPTX
Pruning your code
PDF
Kinect v2 Introduction and Tutorial
PPTX
Compiler Optimization Presentation
PDF
"Развитие ветки PHP-7"
PDF
How to practice functional programming in react
PDF
Engineering Problem Solving With C++ 4th Edition Etter Solutions Manual
PDF
Facts about multithreading that'll keep you up at night - Guy Bar on, Vonage
DOCX
2.1 ### uVision Project, (C) Keil Software .docx
PDF
Engineering Problem Solving With C++ 4th Edition Etter Solutions Manual
ODP
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
PDF
Refactoring
PDF
DeVry GSP 115 Week 3 Assignment latest
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Code Generation in PHP - PHPConf 2015
DeVry GSP 115 All Assignments latest
Assignment #2.classpathAssignment #2.project Assig.docx
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
C++ Question on References and Function Overloading
The following is the (incomplete) header file for the class Fracti.pdf
Pruning your code
Kinect v2 Introduction and Tutorial
Compiler Optimization Presentation
"Развитие ветки PHP-7"
How to practice functional programming in react
Engineering Problem Solving With C++ 4th Edition Etter Solutions Manual
Facts about multithreading that'll keep you up at night - Guy Bar on, Vonage
2.1 ### uVision Project, (C) Keil Software .docx
Engineering Problem Solving With C++ 4th Edition Etter Solutions Manual
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Refactoring
DeVry GSP 115 Week 3 Assignment latest

Recently uploaded (20)

PPTX
Introduction to Artificial Intelligence
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
System and Network Administration Chapter 2
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PPTX
ai tools demonstartion for schools and inter college
PPTX
Online Work Permit System for Fast Permit Processing
PDF
System and Network Administraation Chapter 3
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
top salesforce developer skills in 2025.pdf
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
history of c programming in notes for students .pptx
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
AI in Product Development-omnex systems
PPT
Introduction Database Management System for Course Database
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Introduction to Artificial Intelligence
VVF-Customer-Presentation2025-Ver1.9.pptx
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Design an Analysis of Algorithms I-SECS-1021-03
Wondershare Filmora 15 Crack With Activation Key [2025
System and Network Administration Chapter 2
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
ai tools demonstartion for schools and inter college
Online Work Permit System for Fast Permit Processing
System and Network Administraation Chapter 3
ManageIQ - Sprint 268 Review - Slide Deck
How to Migrate SBCGlobal Email to Yahoo Easily
top salesforce developer skills in 2025.pdf
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
history of c programming in notes for students .pptx
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
AI in Product Development-omnex systems
Introduction Database Management System for Course Database
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf

Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK

  • 1. #mceconf #mce-proguard 11 Jan 2014 ProGuard Optimizer and Obfuscator in the Android SDK Eric Lafortune Developer of ProGuard Technical director at Saikoa
  • 2. #mceconf #mce-proguard 11 Jan 2014 ProGuard Generic Shrinker Optimizer Obfuscator For Java bytecode Open source
  • 3. #mceconf #mce-proguard 11 Jan 2014 ProGuard history Java applications Applets 2002 Midlets 2010 2012 Android apps ● May 2002 First release ● Sep 2010 Recommended for protecting LVL ● Dec 2010 Part of Android SDK ● Jan 2012 Startup Saikoa
  • 4. #mceconf #mce-proguard 11 Jan 2014 ProGuard Application code Libraries Runtime libraries Shrink Optimize Obfuscate Processed code Runtime libraries
  • 5. #mceconf #mce-proguard 11 Jan 2014 ProGuard in Android builds Application Java bytecode ProGuard Libraries Java bytecode Processed Java bytecode Dex Dalvik bytecode Javac Application Java source Libraries Java bytecode XML resources Assets Assets Compiled XML resourcesAapt
  • 6. #mceconf #mce-proguard 11 Jan 2014 Why use ProGuard? ● Application size ● Performance ● Remove logging, debugging, testing code ● Protection
  • 7. #mceconf #mce-proguard 11 Jan 2014 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]
  • 8. #mceconf #mce-proguard 11 Jan 2014 Performance: CaffeineMark Without ProGuard Sieve score = 6833 Loop score = 14831 Logic score = 19038 String score = 7694 Float score = 6425 Method score = 4850 Overall score = 8794 With ProGuard Sieve score = 6666 Loop score = 15473 Logic score = 47840 String score = 7717 Float score = 6488 Method score = 5229 Overall score = 10436 Improvement: 18% [Acer Iconia Tab A500, nVidia Tegra 2, 1.0 GHz, Android 3.2.1]
  • 9. #mceconf #mce-proguard 11 Jan 2014 Results ● Size reduction: – Code (classes.dex): 20 … 90% – Application (.apk): 5 ... 70% ● Performance improvements: 0 … 20%
  • 10. #mceconf #mce-proguard 11 Jan 2014 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
  • 11. #mceconf #mce-proguard 11 Jan 2014 How to enable ProGuard? Gradle: build.gradle → completely flexible android { buildTypes { release { runProguard true proguardFile getDefaultProguardFile('proguard-android.txt') } } productFlavors { some_flavor { proguardFile 'proguard-project.txt' } } } android { buildTypes { release { runProguard true proguardFile getDefaultProguardFile('proguard-android.txt') } } productFlavors { some_flavor { proguardFile 'proguard-project.txt' } } } New
  • 12. #mceconf #mce-proguard 11 Jan 2014 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
  • 13. #mceconf #mce-proguard 11 Jan 2014 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
  • 14. #mceconf #mce-proguard 11 Jan 2014 Shrinking Also called treeshaking, minimizing, shrouding
  • 15. #mceconf #mce-proguard 11 Jan 2014 Shrinking ● Classes, fields, methods
  • 16. #mceconf #mce-proguard 11 Jan 2014 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 …
  • 17. #mceconf #mce-proguard 11 Jan 2014 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
  • 18. #mceconf #mce-proguard 11 Jan 2014 Optimization At the bytecode instruction level: ● Dead code elimination ● Constant propagation ● Method inlining ● Class merging ● Remove logging code ● Peephole optimizations ● Devirtualization ● ...
  • 19. #mceconf #mce-proguard 11 Jan 2014 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); } }
  • 20. #mceconf #mce-proguard 11 Jan 2014 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); }
  • 21. #mceconf #mce-proguard 11 Jan 2014 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); }
  • 22. #mceconf #mce-proguard 11 Jan 2014 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; }
  • 23. #mceconf #mce-proguard 11 Jan 2014 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;
  • 24. #mceconf #mce-proguard 11 Jan 2014 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
  • 25. #mceconf #mce-proguard 11 Jan 2014 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 } 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
  • 26. #mceconf #mce-proguard 11 Jan 2014 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 }; ... } 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; } } New
  • 27. #mceconf #mce-proguard 11 Jan 2014 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 }; ... } 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; } } New 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; } }
  • 28. #mceconf #mce-proguard 11 Jan 2014 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 }; ... } 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; } } 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; } } New 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; } }
  • 29. #mceconf #mce-proguard 11 Jan 2014 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 }; ... } 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; } } 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; New 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. #mceconf #mce-proguard 11 Jan 2014 How to enable optimization? Ant and Eclipse: project.properties Tip # 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
  • 31. #mceconf #mce-proguard 11 Jan 2014 How to enable optimization? Gradle: build.gradle android { buildTypes { release { runProguard true proguardFile getDefaultProguardFile('proguard-android-optimize.txt') } } productFlavors { some_flavor { proguardFile 'proguard-project.txt' } } } android { buildTypes { release { runProguard true proguardFile getDefaultProguardFile('proguard-android-optimize.txt') } } productFlavors { some_flavor { proguardFile 'proguard-project.txt' } } } New
  • 32. #mceconf #mce-proguard 11 Jan 2014 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
  • 33. #mceconf #mce-proguard 11 Jan 2014 Obfuscation Traditional name obfuscation: ● Rename identifiers: class/field/method names ● Remove debug information: line numbers, local variable names,...
  • 34. #mceconf #mce-proguard 11 Jan 2014 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; } }
  • 35. #mceconf #mce-proguard 11 Jan 2014 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; } }
  • 36. #mceconf #mce-proguard 11 Jan 2014 Complementary steps Optimization Obfuscation Irreversibly remove information
  • 37. #mceconf #mce-proguard 11 Jan 2014 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
  • 38. #mceconf #mce-proguard 11 Jan 2014 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”); } }
  • 39. #mceconf #mce-proguard 11 Jan 2014 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”); } }
  • 40. #mceconf #mce-proguard 11 Jan 2014 More application protection Nothing is unbreakable, but you can raise the bar: ● ProGuard ● String encryption ● Reflection ● Dynamic class loading ● Native code ● Data encryption ● … DexGuard
  • 41. #mceconf #mce-proguard 11 Jan 2014 ProGuard - DexGuard Open source Generic Shrinker Optimizer Obfuscator For Java bytecode Closed source Specialized Shrinker Optimizer Obfuscator Protector For Android Compatible
  • 42. #mceconf #mce-proguard 11 Jan 2014 ProGuard guide – Android SDK developer.android.comdeveloper.android.com
  • 43. #mceconf #mce-proguard 11 Jan 2014 ProGuard website proguard.sourceforge.netproguard.sourceforge.net
  • 44. #mceconf #mce-proguard 11 Jan 2014 ProGuard manualTip proguard.sourceforge.netproguard.sourceforge.net
  • 45. #mceconf #mce-proguard 11 Jan 2014 Saikoa website www.saikoa.comwww.saikoa.com
  • 46. #mceconf #mce-proguard 11 Jan 2014 Questions? Open source Shrinking Optimization Obfuscation Java bytecode ProGuard Saikoa DexGuard Dalvik bytecode Protection