46
#mceconf #mce-proguard 11 Jan 2014 ProGuard Optimizer and Obfuscator in the Android SDK Eric Lafortune Developer of ProGuard Technical director at Saikoa

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

Embed Size (px)

Citation preview

#mceconf #mce-proguard 11 Jan 2014

ProGuard

Optimizer and Obfuscatorin the Android SDK

Eric LafortuneDeveloper of ProGuard

Technical director at Saikoa

#mceconf #mce-proguard 11 Jan 2014

ProGuard

Generic

ShrinkerOptimizerObfuscator

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

Applicationcode

Libraries

Runtimelibraries

Shrink Optimize ObfuscateProcessed

code

Runtimelibraries

#mceconf #mce-proguard 11 Jan 2014

ProGuard in Android builds

ApplicationJava bytecode

ProGuard

LibrariesJava bytecode

ProcessedJava bytecode Dex

Dalvik bytecode

JavacApplicationJava source

LibrariesJava bytecode

XML resources

Assets Assets

CompiledXML 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

WithoutProGuard

WithProGuard

Reduction

ApiDemos 716 K 482 K 33 % 2.6 M 2.5 M 4 %

GoogleIOApp

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.JSONArrayWarning: 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.LoggerWarning: 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.LoggerWarning: 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 exampleint 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 exampleint 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 exampleint 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 exampleint 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 exampleint 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 thisproguard.config= ${sdk.dir}/tools/proguard/proguard-android-optimize.txt: proguard-project.txt

# To enable ProGuard to shrink and obfuscate your code, uncomment thisproguard.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 examplepublic 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 examplepublic 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

ApplicationJava bytecode

ProGuard

LibrariesJava bytecode

ProcessedJava 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

ShrinkerOptimizerObfuscator

For Java bytecode

Closed source

Specialized

ShrinkerOptimizerObfuscatorProtector

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

ShrinkingOptimizationObfuscation

Java bytecode

ProGuard

Saikoa

DexGuardDalvik bytecode

Protection