42
Protecting your Android content Erik Hemming Unity Technologies Mobile Mechanic & Lead Android Developer

[UniteKorea2013] Protecting your Android content

Embed Size (px)

DESCRIPTION

유나이트 코리아 2013 발표자료: 안드로이드에서의 스크립트 암호화 및 에셋 보안 (에릭 헤밍)

Citation preview

Page 1: [UniteKorea2013] Protecting your Android content

Protecting your Android contentErik HemmingUnity TechnologiesMobile Mechanic & Lead Android Developer

Page 2: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

About me

• Developer at Unity / Sweden

• 3 years of Unity for Android

• Used to make games

• Battlefield series

• Focus on mobiles and consoles

• Android / PSM / VITA & PS4

2

Page 3: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Agenda• Unity on Android - what does it mean?

• Authentication with Google Play Licensing

• Application tampering detection

• Code Obfuscation

• Encryption of

• PlayerPrefs

• Scripts

• Assets

• Conclusion / Q&A

3

Page 4: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Unity on Android

4

Page 5: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Unity on Android (overview)

5

Linux Kernel

Android / Dalvik VM

Unity on Android

Mono VM

User script / “Game”

OS

App

Page 6: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Unity on Android (detail)

6

C# / Scripts

Dalvik (Java)

Page 7: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Unity on Android (detail)

7

AndroidJavaObject

java.lang.Object

Page 8: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

AndroidJavaObject et al

• Script objects wrap Java objects

• AndroidJavaObject → java.lang.Object

• AndroidJavaClass → java.lang.Class

• AndroidJavaRunnable → java.lang.Runnable

• AndroidJavaProxy → java.lang.reflect.Proxy (in Unity 4.2)

• Automatically maps / instantiates Classes by name

• Methods / Fields are handled through reflection lookups

8

Page 9: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page 9

java.lang.String str = new java.lang.String("some string");int hash = str.hashCode();

AndroidJavaObject jo =         new AndroidJavaObject("java.lang.String", "some string");int hash = jo.Call<int>("hashCode");

Java

C#

AndroidJavaObject (example)

Page 10: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Authentication withGoogle Play Licensing

10

Page 11: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Authentication withGoogle Play Licensing

• Provided by Google

• Only available for applications published on Play Store

• Online verification of purchase records

• If the device is o"ine the verification will “fail”

• Example code (LVL) provided by Google

• Don’t use as-is - very easy to find and hack

11

Page 12: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Verification Flow

• Application → {random number} → Google

• Google → {message, signature} → Application

• message = purchase status + random number + timestamp + (..)

• signature = RSA(message, private key)

• Verify that RSA(signature, public key) is a match for ‘message’

12

Page 13: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

How to handle the o!ine case

• “Online check” == “Internet access”

• Don’t require constant internet access

• that would ruin the game experience while flying / roaming / etc.

• Instead do the checks only if network is available

• allow the app be used a week (or so) without being verified

• trust the app/user during that time.

• If your app has game elements that require internet connection, make sure you also do a license check at that point.

13

Page 14: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Server Side Verification

• Application → {some number / data request} → Google

• Google → {message, signature} → Application

• Application → {message, signature} → Server

• Server → {application data} → Application

• Server only fulfill Client requests that have correct ‘signature’

14

Page 15: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Unity Plugin : Google Play License Verification

• Written in C#

• Except for the small Service Binder (Java) - loaded dynamically

• Easy to embed / hide anywhere in your project

• Available on the Unity Asset Store

• Ready to be included into an existing project

• Original project hosted on GitHub

• Feel free to fork and improve

15

Page 16: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Application tampering detection

16

Page 17: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Application tampering detection

• Why?

• A hacker would have to remove and/or alter licensing checks

• and thus change the code in your application

• Also possible to change code to gain in-game advantages

• Like changing the physics so that a car drives faster

• In general a very easy way to determine if you’ve been hacked

17

Page 18: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Application tampering detection

• Make sure the application is signed with your key

• Make sure the Java code (classes.dex) isn’t altered

• Make sure the Mono class library (mscorlib.dll) isn’t altered

• if the License check is done in C# we will rely on it

• Make sure your script code (Assembly-CSharp.dll) isn’t altered

• Needs to be done from Assembly-UnityScript.dll, or v.v.

• Make sure your native code (libunity.so / libmono.so / etc) isn’t altered

18

Page 19: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Check the APK signature (Java)

19

// Retrieve the PackageManager and packageName (i.e. 'com.Company.Product')Activity activity = com.unity3d.player.UnityPlayer.currentActivity;PackageManager manager = activity.getPackageManager();String name = activity.getPackageName();

// Fetch APK signature(s)PackageInfo packageInfo = manager.getPackageInfo(name, PackageManager.GET_SIGNATURES);Signature[] signatures = packageInfo.signatures;

// Process signatures (i.e. check their validity)for (Signature signature : signatures){    Log.i("signature", signature.toCharsString());    Log.i("signature hash", Integer.toHexString(signature.hashCode()));}

Page 20: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Check the APK signature (UnityScript)

20

// Retrieve the PackageManager and packageName (i.e. 'com.Company.Product')var unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");var activity = unity.GetStatic.<AndroidJavaObject>("currentActivity");var manager = activity.Call.<AndroidJavaObject>("getPackageManager");var name = activity.Call.<String>("getPackageName");

// Fetch APK signature(s)var GET_SIGNATURES = 64;    // PackageManager.GET_SIGNATURESvar packageInfo = manager.Call.<AndroidJavaObject>("getPackageInfo", name, GET_SIGNATURES);var signatures = packageInfo.Get.<AndroidJavaObject[]>("signatures");

// Process signatures (i.e. check their validity)for (var i = 0; i < signatures.length; ++i){    Debug.Log("signature = " + signatures[i].Call.<String>("toCharsString"));    Debug.Log("signature hash = " + signatures[i].Call.<int>("hashCode").ToString("X"));}

Page 21: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Detect changes to ‘classes.dex’ (C#)

21

// Unity's WWW class supports reading 'jar:{archive-url}!/{entry}' on Androidstring urlScheme = "jar:file://";string apkPath = Application.dataPath;string separator = "!/";string entry = "classes.dex";string url = urlScheme + apkPath + separator + entry;

// Read classes.dex inside package.apkWWW www = new WWW(url);yield return www;

// Calculate the MD5 sum of classes.dex contentsMD5 md5 = new MD5CryptoServiceProvider();byte[] hash = md5.ComputeHash(www.bytes);

// Print MD5 sumSystem.Text.StringBuilder sb = new System.Text.StringBuilder();for (int i = 0; i < hash.Length; i++)    sb.Append(hash[i].ToString("x2"));Debug.Log("md5sum(classes.dex) = " + sb.ToString());

Page 22: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Native libs check (UnityScript)

22

// Retrieve main Activityvar unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");var activity = unity.GetStatic.<AndroidJavaObject>("currentActivity");

// Retrieve ApplicationInfo and nativeLibraryDir (N.B. API-9 or newer only!)var info = activity.Call.<AndroidJavaObject>("getApplicationInfo");var nativeLibraryDir = info.Get.<String>("nativeLibraryDir");var unityPath = Path.Combine(nativeLibraryDir, "libunity.so");

var file = new FileStream(unityPath, FileMode.Open, FileAccess.Read);var sha1 = new SHA1CryptoServiceProvider();var hash = sha1.ComputeHash(file);file.Close();

// Print SHA1 sumvar sb = new System.Text.StringBuilder();for (var i = 0; i < hash.Length; i++)    sb.Append(hash[i].ToString("x2"));Debug.Log("sha1sum(libunity.so) = " + sb.ToString());

Page 23: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Code Obfuscation

23

Page 24: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Code Obfuscation

• Java Obfuscation

• Proguard

• C# Obfuscation

• Obfuscar

• Hides method/variable names

• but still very much readable code

• False sense of security

24

Page 25: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Java Obfuscation (before)

25

private void playVideo(){    doCleanUp();    try    {        mMediaPlayer = new MediaPlayer();

        if (mIsURL)        {            mMediaPlayer.setDataSource(mContext, Uri.parse(mFileName));        }        else if (mVideoLength != 0)        {            FileInputStream file = new FileInputStream(mFileName);            mMediaPlayer.setDataSource(file.getFD(), mVideoO"set, mVideoLength);            file.close();        }        else        {(...)

Page 26: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Java Obfuscation (after)

26

private void a(){    b();    try    {        this.r = new MediaPlayer();        if (this.h)        {            this.r.setDataSource(this.b, Uri.parse(this.e));        }        else        {            if (this.j != 0L)            {                Object localObject = new FileInputStream(this.e);                this.r.setDataSource(((FileInputStream)localObject).getFD(), this.i, this.j);                ((FileInputStream)localObject).close();            }            else            {(...)

Page 27: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption

27

Page 28: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption of PlayerPrefs

• Why?

• Prevent simple cheating

• Prevent cracking IAB purchases (if you cache anything locally)

• In general good practice for sensitive data (like game progression)

• How?

• Encrypt key/values before inserting them in the PlayerPrefs

• Use a user-specific encryption, so prefs cannot be copied, but still shared in a cloud

28

Page 29: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

SetString(key, value, secret)

29

// Hide 'key' stringstring key_string = MD5(key);

// Convert 'value' into a byte arraybyte[] bytes = UTF8Encoding.UTF8.GetBytes(value);

// Encrypt 'value' with 3DES('secret')TripleDES des = new TripleDESCryptoServiceProvider();des.Key = secret;des.Mode = CipherMode.ECB;ICryptoTransform xform = des.CreateEncryptor();byte[] encrypted = xform.TransformFinalBlock(bytes, 0, bytes.Length);

// Convert encrypted array into a "readable" stringstring encrypted_string = Convert.ToBase64String(encrypted, 0, encrypted.Length);

// Set the { key, encrypted value } pair in regular PlayerPrefsPlayerPrefs.SetString(key_string, encrypted_string);

Page 30: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

value GetString(key, secret)

30

// Hide 'key' stringstring key_string = MD5(key);

// Retrieve encrypted 'value' and Base64 decode itstring value = PlayerPrefs.GetString(key_string);byte[] bytes = Convert.FromBase64String(value);

// Decrypt 'value' with 3DES('secret')TripleDES des = new TripleDESCryptoServiceProvider();des.Key = secret;des.Mode = CipherMode.ECB;ICryptoTransform xform = des.CreateDecryptor();byte[] decrypted = xform.TransformFinalBlock(bytes, 0, bytes.Length);

// Return decrypted value as a proper stringreturn UTF8Encoding.UTF8.GetString(decrypted);

Page 31: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encrypted SetString() / GetString()

31

// Generate a secret based on 'username'string username = "Turrican II";MD5 md5 = new MD5CryptoServiceProvider();byte[] secret = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(username));

// Game progress { key, value } pairstring key = "unlocked levels";string value = "the desert rocks,traps,secret dungeons,the wall,the final challenge,the final fight";

// Insert { key, value } pairSetString(key, value, secret);

// Retrieve { key, value }string ret = GetString(key, secret);

// Output to the logcatDebug.Log("secret = " + username);Debug.Log(key + " = " + ret);

Page 32: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption of Scripts

• Why?

• Scripts are generally insecure

• Gameplay could be altered

• Security checks could be disabled

• Code needs to be “hidden” for some reason (i.e. IAB logic)

32

Page 33: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption of Scripts

• How?

• Compile scripts outside Unity

• Run a symmetric / asymmetric encryption on the Script.dll

• Choose a delivery mechanism

• Embed in the application, or

• Download it from a trusted server

• Decrypt the Script.dll in memory

• Load it through Assembly.Load(byte[])

33

Page 34: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Compile scripts outside Unity

• Download Mono (www.mono-project.com)

• Compile the script (Plugin.cs) with ‘gmcs’

• Reference the UnityEngine.dll assembly to access to Unity

34

$ gmcs -target:library -out:Script.dll -r:AndroidPlayer/Managed/UnityEngine.dll Plugin.cs

Page 35: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encrypt the assembly

• Using OpenSSL

• Converted to ‘text’ using Base64 encoding

• Result can be embedded in Unity as a TextAsset

35

$ openssl rc2 -nosalt -p -in Script.dll -out Encrypted.binkey=...iv =...

$ base64 Encrypted.bin > ~/UnityProject/Assets/Encrypted.txt

Page 36: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Plugin.cs

36

using UnityEngine;

public class SomeImportantGameClass{    static SomeImportantGameClass()    {        const int GET_SIGNATURES = 64;        AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")

.GetStatic<AndroidJavaObject>("currentActivity");        AndroidJavaObject manager = activity.Call<AndroidJavaObject>("getPackageManager");        string packageName = activity.Call<string>("getPackageName");        AndroidJavaObject packageInfo =                manager.Call<AndroidJavaObject>("getPackageInfo", packageName, GET_SIGNATURES);        AndroidJavaObject[] signatures = packageInfo.Get<AndroidJavaObject[]>("signatures");        foreach (AndroidJavaObject signature in signatures)        {            Debug.Log("signature hash = " + signature.Call<int>("hashCode").ToString("X"));        }    }(...)}

Page 37: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Decrypt and run assembly

37

public TextAsset assembly;

void Start () {

    // Load encrypted data and decryption keys    byte[] bytes = Convert.FromBase64String(assembly.text);    byte[] key = new byte[] { <key from encryption step> };    byte[] iv = new byte[] { <iv from encryption step> };

    // Decrypt assembly    RC2 rc2 = new RC2CryptoServiceProvider();    rc2.Mode = CipherMode.CBC;    ICryptoTransform xform = rc2.CreateDecryptor(key, iv);    byte[] decrypted = xform.TransformFinalBlock(bytes, 0, bytes.Length);

    // Load assembly and instantiate 'SomeImportantGameClass' to trigger static constructor    Assembly asm = Assembly.Load(decrypted);    Type SomeClass = asm.GetType("SomeImportantGameClass");    SomeClass.GetConstructor(Type.EmptyTypes).Invoke(null);}

Page 38: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption of Assets

• Why?

• Some assets might need to be protected from tampering.

• “Assets” doesn’t necessarily mean just “textures”; could be

• Game logic

• Dalvik bytecode

• Script code

• Native code

• .. “anything”

38

Page 39: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Encryption of Assets

• How?

• Create an AssetBundle from the “secret” assets.

• Run a symmetric / asymmetric encryption on the AssetBundle.unity3d

• Choose a delivery mechanism

• Embed in the application, or

• Download it from a trusted server

• Decrypt the AssetBundle.unity3d in memory

• Load it through AssetBundle.CreateFromMemory(Byte[])

39

Page 40: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Conclusion

• Be imaginative

• APK integrity checks are so simple everyone should have them.

• Sensitive code must be protected

• Combine the di#erent approaches, and create new ones

• Finally: Don’t spend too much time on this

• Instead update the logic for each new release.

40

Page 41: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Questions?

41

Page 42: [UniteKorea2013] Protecting your Android content

4 Apr 2013 Page

Thanks!

42

Twitter: @_eriq_