Code obfuscation
techniques
Eric Lafortune
CTO at GuardSquare
Creator of ProGuard and DexGuard
Topics
●
Threats, attacks, and techniques
●
Android applications and build process
●
Obfuscation techniques
Threats
Attackers
Attackers
Time
Choice
Static analysis
●
Disassemblers: dexdump, baksmali
●
Decompilers: dex2jar + jad, JD-GUI, JEB,...
●
Resources: aapt, apktool,...
Dynamic analysis
●
Debuggers: adb,...
●
Subverted runtime
●
Cracking tools
Android application
*.apk
Manifest Asset files
(AndroidManifest.xml) (assets/*)
Resource constants Resource files
(resources.arsc) (res/*/*)
Dalvik bytecode Native libraries
(classes.dex) (lib/*/*.so)
Android build process
Assets Assets Assets Assets
Compiled Jar Compiled Zip Compiled
XML res. Aapt XML res. signer XML res. align XML res.
Application Application
Java source Javac Java bytec. Dalvik Dalvik Dalvik
Processed bytecode bytecode bytecode
ProGuard Java bytec. Dex
Libraries Libraries
Java bytec. Java bytec.
Native Native Native
libraries libraries libraries
Application Gcc
Native src
Signatures Signatures
ProGuard
Application
code
Processed
Shrink Optimize Obfuscate
code
Libraries
Android Android
runtime runtime
Shrinking
●
Classes, fields, methods
Optimization
At the bytecode instruction level:
●
Dead code elimination
●
Constant propagation
●
Method inlining
●
Class merging
●
Remove logging code
●
Peephole optimizations
●
Devirtualization
●
...
Obfuscation
Traditional name obfuscation:
●
Rename identifiers:
class/field/method names
●
Remove debug information:
line numbers, local variable names,...
Name obfuscation
public class MyComputationClass {
private MySettings settings;
private MyAlgorithm algorithm;
private int answer;
public int computeAnswer(int input) {
…
return answer;
}
}
Name obfuscation
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;
}
}
Some limitations
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”,
.checkSignatures(“mypackage1”, “mypackage2”);
“mypackage2”);
}
}
Some limitations
public class MyVerificationClass {
public int checkSignatures() {
…
return activity
.getPackageManager()
.checkSignatures(“mypackage1”,
.checkSignatures(“mypackage1”, “mypackage2”);
“mypackage2”);
}
}
public class a {
public int a() {
…
return a
.getPackageManager()
.checkSignatures(“mypackage1”,
.checkSignatures(“mypackage1”, “mypackage2”);
“mypackage2”);
}
}
Server-side code
?
Server-side
application
Client-side
application
String encryption
String KEY = “Secret key”;
String KEY = new String(Base64.decode(“U2VjcmV0IGtleQo=”));
Reflection
java.lang.reflect
System.out.println(“Hello world!”);
Class clazz =
Class.forName(“java.io.PrintStream”);
Method method =
clazz.getMethod(“println”,
new Class[] { String.class });
method.invoke(nul, new Object[] { “Hello world!” });
Control flow obfuscation
Control flow flattening
Opaque predicates
boolean flag = true;
boolean flag = System.currentTimeMillis() > 0;
Arithmetic obfuscation
int c = a + b;
int c = (a ^ b) + 2 * (a & b);
Dynamic class loading
http://android-developers.blogspot.be/2011/07/custom-class-loading-in-dalvik.html
classes.dex classes.dex
extra.dex
Native code
Java Native Interface
classes.dex classes.dex
libutil.so
libutil.so
libutil.so
Data encryption
●
Standard cryptography APIs
Cipher cipher =
Cipher.getInstance(“AES/CFB/NoPadding”);
cipher.init(Cipher.ENCRYPT_MODE,
key, initializationVector);
byte[] decrypted = cipher.doFinal(encrypted);
●
SQLCipher
White-box cryptography
Research: www.whiteboxcrypto.com
final int KEY = 42;
int decryptValue(int encryptedValue) {
return encryptedValue * KEY;
}
int decryptValue(int encryptedValue) {
encryptedValue += 5;
encryptedValue *= 21;
encryptedValue -= 105;
encryptedValue += encryptedValue;
return encryptedValue;
}
Real examples: DES, AES
Application checks
●
Check application certificate:
byte[] certificateData = activity
.getPackageManager()
.getPackageInfo(activity.getPackageName(),
PackageManager.GET_SIGNATURES)
.signatures[0]
.toByteArray();
●
Check debug flag:
boolean debug =
(activity.getApplicationInfo().flags &
ApplicationInfo.FLAG_DEBUGGABLE) != 0;
Environment checks
●
Emulator detection
●
Root detection
Packing
Files Memory
Dalvik bytecode Dalvik bytecode
(classes.dex)
Summary
Nothing is unbreakable, but you can raise the bar:
●
String encryption
●
Reflection
●
Control flow obfuscation
●
Dynamic class loading
●
Native code
●
Data encryption
●
Application checks
●
Environment checks
●
Packing
Questions?
Android
ProGuard
Shrinking
Optimization Cryptography
Obfuscation
Protection
Java bytecode
DexGuard
Dalvik bytecode