dgalpin android pirates and vampires

Post on 29-Nov-2014

92 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Evading Pirates and Stopping Vampires Using License Server, In App Billing, and App Engine

Dan Galpin and Trevor JohnsMay 11, 2011

http://goo.gl/Q8SR7#Android

Pirates

• Piracy is a reality on all platforms

• Dedicated pirates cannot be stopped

4

Casual Pirates

Casual Pirates are potential customers.

• Make piracy inconvenient or challenging

• Use it as a marketing tool

• Allow for limited unlicensed gameplay

• Leverage new monetization models around IAPand Ads

5

Casual Pirates

Casual Pirates are potential customers.

• Make piracy inconvenient or challenging

• Use it as a marketing tool

• Allow for limited unlicensed gameplay

• Leverage new monetization models around IAPand Ads

5

Casual Pirates

Casual Pirates are potential customers.

• Make piracy inconvenient or challenging

• Use it as a marketing tool

• Allow for limited unlicensed gameplay

• Leverage new monetization models around IAPand Ads

5

• Google provides the License Verification Library

• Application determines how to enforce the policy

• Frequency of checks is managed by the client application policy

• Private key is stored with License Server, public key is in application to verify signature

• Supported on Android 1.5 devices or abovethat have Android Market

License Verification: Your First Defense

6

Android Market Licensing - Client Verification

Your Application

Market Licensing Server

Android Market Client

Licensing Service

License Verification Library

Android Market Licensing - Client Verification

Your Application

Market Licensing Server Private Key

Android Market Client

Licensing Service

Public Key

License Verification Library

Android Market Licensing - Client Verification

Your Application

Check License Request

Market Licensing Server Private Key

Android Market Client

Licensing Service

Public Key

License Verification Library

Android Market Licensing - Client Verification

Your Application

Check License Request

Checks Purchase Information

Market Licensing Server Private Key

Android Market Client

Licensing Service

Public Key

License Verification Library

Android Market Licensing - Client Verification

Your Application

Check License Request

Checks Purchase Information

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Public Key

License Verification Library

Android Market Licensing - Client Verification

Your Application

Check License Request

Checks Purchase Information

Signed Response

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Public Key

License Verification Library

Android Market Licensing - Client Attack

Your Application

Public Key

License Verification Library

• Use tools to disassemble Dalvik/native code

• Alter the response from the library to ignore the server result and always return “licensed”

• Reassemble the application package

• Sign the package with an alternate signature

LVL - Strengthening our Defenses

• Use an obfuscatorhttp://proguard.sourceforge.nethttp://code.google.com/p/android-proguard-commandline/

• Modify the LVL code

– Invocation of License Check and response-handling

– Core LVL logic

– Entry/Exit Points

• Make the application tamper-resistant

LVL - Invocation/Response Handling

• Don’t invoke in a non-obfuscated function such as onCreate

– Consider in a background thread

• Allow for a limited amount of gameplay when unlicensed

– Consider delaying the result of a license failure

• Implement a policy that allows for multiple background retries if the network or server are unavailable before failing

– Networks don’t always work even when the system thinks they should

• Invoke another activity rather than a dialog to inform the user of a validation failure

LVL - Core Logic

Original Sample Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity ...

switch (responseCode) {

// In bytecode, LICENSED will be converted to the

// constant 0x0

case LICENSED:

// NOT_LICENSED will be converted to the constant 0x1

case NOT_LICENSED:

handleResponse(LicenseResponse.NOT_LICENSED, data);

break;

// ... Extra response codes also removed for brevity ...

}

}

LVL - Core Logic

Original Sample Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity ...

switch (responseCode) {

// In bytecode, LICENSED will be converted to the

// constant 0x0

case LICENSED:

// NOT_LICENSED will be converted to the constant 0x1

case NOT_LICENSED:

handleResponse(LicenseResponse.NOT_LICENSED, data);

break;

// ... Extra response codes also removed for brevity ...

}

}

Example Updated Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity …

java.util.zip.CRC32 crc32 = new java.util.zip.CRC32();

crc32.update(responseCode);

int transformedResponseCode = crc32.getValue();

// crc32(LICENSED) == 3523407757

if (transformedResponseCode == 3523407757) {

LicenseResponse limiterResponse =

mDeviceLimiter.isDeviceAllowed(userId);

handleResponse(limiterResponse, data);

}

// ... put unrelated application code here ...

// crc32(NOT_LICENSED) == 2768625435

if (transformedResponseCode == 2768625435) {

userIsntLicensed();

}

}

LVL - Core Logic

Original Sample Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity ...

switch (responseCode) {

// In bytecode, LICENSED will be converted to the

// constant 0x0

case LICENSED:

// NOT_LICENSED will be converted to the constant 0x1

case NOT_LICENSED:

handleResponse(LicenseResponse.NOT_LICENSED, data);

break;

// ... Extra response codes also removed for brevity ...

}

}

Example Updated Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity …

java.util.zip.CRC32 crc32 = new java.util.zip.CRC32();

crc32.update(responseCode);

int transformedResponseCode = crc32.getValue();

// crc32(LICENSED) == 3523407757

if (transformedResponseCode == 3523407757) {

LicenseResponse limiterResponse =

mDeviceLimiter.isDeviceAllowed(userId);

handleResponse(limiterResponse, data);

}

// ... put unrelated application code here ...

// crc32(NOT_LICENSED) == 2768625435

if (transformedResponseCode == 2768625435) {

userIsntLicensed();

}

}

Compute response code value using a hash function

LVL - Core Logic

Original Sample Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity ...

switch (responseCode) {

// In bytecode, LICENSED will be converted to the

// constant 0x0

case LICENSED:

// NOT_LICENSED will be converted to the constant 0x1

case NOT_LICENSED:

handleResponse(LicenseResponse.NOT_LICENSED, data);

break;

// ... Extra response codes also removed for brevity ...

}

}

Example Updated Codepublic void verify(PublicKey publicKey, int responseCode, String signedData, String

signature) {

// ... Response validation code omitted for brevity …

java.util.zip.CRC32 crc32 = new java.util.zip.CRC32();

crc32.update(responseCode);

int transformedResponseCode = crc32.getValue();

// crc32(LICENSED) == 3523407757

if (transformedResponseCode == 3523407757) {

LicenseResponse limiterResponse =

mDeviceLimiter.isDeviceAllowed(userId);

handleResponse(limiterResponse, data);

}

// ... put unrelated application code here ...

// crc32(NOT_LICENSED) == 2768625435

if (transformedResponseCode == 2768625435) {

userIsntLicensed();

}

}

Use separate if statements separated by unrelated application code

Compute response code value using a hash function

LVL - Tamper Resistance

• Check that your application signature matches

!"#$%&'()*+"#+,-*.*#)&/%01%#)2%$%#)(345#)&/%01%#)6$783#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!45+"#$%&'()+C"7*3*+"#+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E*4*

LVL - Tamper Resistance

• Check that your application signature matches

• Make sure your application is not debuggable

!"#$%&'()*+"#+,-*.*#)&/%01%#)2%$%#)(345#)&/%01%#)6$783#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!45+"#$%&'()+C"7*3*+"#+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E*4*

boolean isDebuggable = !( 0 != ( getApplicationInfo().flags &=

ApplicationInfo.FLAG_DEBUGGABLE ) );

LVL - Tamper Resistance

• Check that your application signature matches

• Make sure your application is not debuggable

• CRC code files and compare

!"#$%&'()*+"#+,-*.*#)&/%01%#)2%$%#)(345#)&/%01%#)6$783#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!45+"#$%&'()+C"7*3*+"#+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E*4*

boolean isDebuggable = !( 0 != ( getApplicationInfo().flags &=

ApplicationInfo.FLAG_DEBUGGABLE ) );

zf = new ZipFile(getApplicationInfo().sourceDir);

" " " ZipEntry ze = zf.getEntry("classes.dex");

" " " if ( null != ze ) {

" " " " ze.getCrc();

" " " }

LVL - Tamper Resistance

• Check that your application certificate matches

public Certificate[] GetApplicationCertificates() {

" InputStream is = null;

" try {

" " JarFile jf = new JarFile(getApplicationInfo().sourceDir);

" " JarEntry je = jf.getJarEntry("classes.dex");

" " is = jf.getInputStream(je);

" " System.out.println("Got InputStream");

" " while ((is.read()) != -1) {} // whole stream is read

" " return je.getCertificates();

" } catch (Exception e) {" "

" } finally {

" " if ( null != is) {

" " " try { "is.close(); }

catch (IOException e) { e.printStackTrace(); }

" " }

" }

" return null;

}"

LVL - Tamper Resistance - Adding Reflection

• Our previous example, which gets the hash of the first signature

!"#$%&'()*+"#+,-*.*#)&/%01%#)2%$%#)(345#)&/%01%#)6$783#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!45+"#$%&'()+C"7*3*+"#+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E*4*

LVL - Tamper Resistance - Adding Reflection

• Our previous example, which gets the hash of the first signature

• Using reflection

!"#$%&'()*+"#+,-*.*#)&/%01%#)2%$%#)(345#)&/%01%#)6$783#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!45+"#$%&'()+C"7*3*+"#+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E*4*

!&("$#*#)&/2*.*$)J*!&("$#3K%+)LM5G)08G)3NOPQDA<RS%PR$OATEU:R$OV6.W$N;*D44C!&("$#*#)&/6*.*$)J*!&("$#3K%+)LM5G)08G)3NOPQDA<RS%PR$OAX'O:Y.W$N;*D44C2)&E8G*#Z:2)&E8G*.*#)&FX%++345#)&2)&E8G3#)&/24C[US)0&*#Z:[US)0&*.*#Z:2)&E8G5"$\81)3&E"+4C2)&E8G*#Z"2)&E8G*.*#Z:[US)0&5#)&FX%++345#)&2)&E8G3#)&/6;*!&("$#50X%++;*"$&50X%++4C[US)0&*Z"*.*#Z"2)&E8G5"$\81)3#Z:[US)0&;*#)&/%01%#)9%:)34;*/%01%#)2%$%#)(5<=>?!6<9@>AB=!4C"7*333/%01%#)6$784Z"45+"#$%&'()+,D-5E%+EF8G)34*..*:H!"#$%&'()I%+E4

LVL - Tamper Resistance - Adding JNI

S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C

S:)&E8G6a*#)&/%01%#)2%$%#)(2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)2%$%#)(N;*N34b%$G(8"Gc

08$&)$&cZ:c/%01%#)2%$%#)(CN4C

S:)&E8G6a*#)&/%01%#)9%:)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)9%:)N;*N34bS%\%cX%$#c

!&("$#CN4C

S8US)0&*Z%01%#)2%$%#)([US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)2%$%#)(2"G4C

S0X%++*Z%01%#)2%$%#)(FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(N4C

S:)&E8G6a*#)&/%01%#)6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N#)&/%01%#)6$78N;*N3bS%\%c

X%$#c!&("$#C64b%$G(8"Gc08$&)$&cZ:c/%01%#)6$78CN4C

S+&("$#*Z%01%#)9%:)!&("$#*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)9%:)2"G4C

S7")XG6a*#)&!"#$%&'()+R"G*.*3]Z^96=$\4_`<)&!&%&"0R")XG6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N<=>?!6<9@>AB=!N;*N6N4C

S"$&*<=>?!6<9@>AB=!*.*3]Z^96=$\4_`<)&!&%&"06$&R")XG3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*#)&!"#$%&'()+R"G4C

S8US)0&*Z%01%#)6$78[US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*Z%01%#)2%$%#)([US)0&;*#)&/%01%#)6$782"G;*

Z%01%#)9%:)!&("$#;*<=>?!6<9@>AB=!4C

S0X%++*Z%01%#)6$78FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*Z%01%#)6$78[US)0&4C

S7")XG6a*+"#$%&'()R"G*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*Z%01%#)6$78FX%++;

* * * N+"#$%&'()+N;*N,b%$G(8"Gc08$&)$&cZ:c!"#$%&'()CN4C

S8US)0&*+"#$%&'()@((%H[US)0&*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*Z%01%#)6$78[US)0&;*+"#$%&'()R"G4C

S"$&*X)$*.*3]Z^96=$\4_`<)&@((%Hb)$#&E3Z^96=$\;*+"#$%&'()@((%H[US)0&4C

S8US)0&*+"#$%&'()[US)0&*.*3]Z^96=$\4_`<)&[US)0&@((%H=X):)$&3Z^96=$\;*+"#$%&'()@((%H[US)0&;*D4C

S0X%++*+"#$%&'()FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*+"#$%&'()[US)0&4C

S:)&E8G6a*E%+EF8G)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*+"#$%&'()FX%++;*NE%+EF8G)N;*N346N4C

"7*3*3]Z^96=$\4_`F%XX6$&2)&E8G3Z^96=$\;*+"#$%&'()[US)0&;*E%+EF8G)2"G4*..*:H!"#$%&'()I%+E*4

LVL - Tamper Resistance - Adding JNI

S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C

S:)&E8G6a*#)&/%01%#)2%$%#)(2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)2%$%#)(N;*N34b%$G(8"Gc

08$&)$&cZ:c/%01%#)2%$%#)(CN4C

S:)&E8G6a*#)&/%01%#)9%:)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)9%:)N;*N34bS%\%cX%$#c

!&("$#CN4C

S8US)0&*Z%01%#)2%$%#)([US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)2%$%#)(2"G4C

S0X%++*Z%01%#)2%$%#)(FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(N4C

S:)&E8G6a*#)&/%01%#)6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N#)&/%01%#)6$78N;*N3bS%\%c

X%$#c!&("$#C64b%$G(8"Gc08$&)$&cZ:c/%01%#)6$78CN4C

S+&("$#*Z%01%#)9%:)!&("$#*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)9%:)2"G4C

S7")XG6a*#)&!"#$%&'()+R"G*.*3]Z^96=$\4_`<)&!&%&"0R")XG6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N<=>?!6<9@>AB=!N;*N6N4C

S"$&*<=>?!6<9@>AB=!*.*3]Z^96=$\4_`<)&!&%&"06$&R")XG3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*#)&!"#$%&'()+R"G4C

S8US)0&*Z%01%#)6$78[US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*Z%01%#)2%$%#)([US)0&;*#)&/%01%#)6$782"G;*

Z%01%#)9%:)!&("$#;*<=>?!6<9@>AB=!4C

S0X%++*Z%01%#)6$78FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*Z%01%#)6$78[US)0&4C

S7")XG6a*+"#$%&'()R"G*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*Z%01%#)6$78FX%++;

* * * N+"#$%&'()+N;*N,b%$G(8"Gc08$&)$&cZ:c!"#$%&'()CN4C

S8US)0&*+"#$%&'()@((%H[US)0&*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*Z%01%#)6$78[US)0&;*+"#$%&'()R"G4C

S"$&*X)$*.*3]Z^96=$\4_`<)&@((%Hb)$#&E3Z^96=$\;*+"#$%&'()@((%H[US)0&4C

S8US)0&*+"#$%&'()[US)0&*.*3]Z^96=$\4_`<)&[US)0&@((%H=X):)$&3Z^96=$\;*+"#$%&'()@((%H[US)0&;*D4C

S0X%++*+"#$%&'()FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*+"#$%&'()[US)0&4C

S:)&E8G6a*E%+EF8G)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*+"#$%&'()FX%++;*NE%+EF8G)N;*N346N4C

"7*3*3]Z^96=$\4_`F%XX6$&2)&E8G3Z^96=$\;*+"#$%&'()[US)0&;*E%+EF8G)2"G4*..*:H!"#$%&'()I%+E*4

S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc%ZZc@0&"\"&HN4CS:)&E8G6a*#)&/%01%#)2%$%#)(2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)2%$%#)(N;*N34b%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(CN4CS:)&E8G6a*#)&/%01%#)9%:)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)9%:)N;*N34bS%\%cX%$#c!&("$#CN4CS0X%++*Z%01%#)2%$%#)(FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(N4CS:)&E8G6a*#)&/%01%#)6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N#)&/%01%#)6$78N;*N3bS%\%cX%$#c!&("$#C64b%$G(8"Gc08$&)$&cZ:c/%01%#)6$78CN4CS7")XG6a*#)&!"#$%&'()+R"G*.*3]Z^96=$\4_`<)&!&%&"0R")XG6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N<=>?!6<9@>AB=!N;*N6N4CS"$&*<=>?!6<9@>AB=!*.*3]Z^96=$\4_`<)&!&%&"06$&R")XG3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*#)&!"#$%&'()+R"G4CS0X%++*Z%01%#)6$78FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)6$78N4CS7")XG6a*+"#$%&'()R"G*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*Z%01%#)6$78FX%++;* * * N+"#$%&'()+N;*N,b%$G(8"Gc08$&)$&cZ:c!"#$%&'()CN4CS0X%++*+"#$%&'()FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c!"#$%&'()N4CS:)&E8G6a*E%+EF8G)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*+"#$%&'()FX%++;*NE%+EF8G)N;*N346N4C

LVL - Tamper Resistance - Adding JNI

S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C

S:)&E8G6a*#)&/%01%#)2%$%#)(2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)2%$%#)(N;*N34b%$G(8"Gc

08$&)$&cZ:c/%01%#)2%$%#)(CN4C

S:)&E8G6a*#)&/%01%#)9%:)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)9%:)N;*N34bS%\%cX%$#c

!&("$#CN4C

S8US)0&*Z%01%#)2%$%#)([US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)2%$%#)(2"G4C

S0X%++*Z%01%#)2%$%#)(FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(N4C

S:)&E8G6a*#)&/%01%#)6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N#)&/%01%#)6$78N;*N3bS%\%c

X%$#c!&("$#C64b%$G(8"Gc08$&)$&cZ:c/%01%#)6$78CN4C

S+&("$#*Z%01%#)9%:)!&("$#*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)9%:)2"G4C

S7")XG6a*#)&!"#$%&'()+R"G*.*3]Z^96=$\4_`<)&!&%&"0R")XG6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N<=>?!6<9@>AB=!N;*N6N4C

S"$&*<=>?!6<9@>AB=!*.*3]Z^96=$\4_`<)&!&%&"06$&R")XG3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*#)&!"#$%&'()+R"G4C

S8US)0&*Z%01%#)6$78[US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*Z%01%#)2%$%#)([US)0&;*#)&/%01%#)6$782"G;*

Z%01%#)9%:)!&("$#;*<=>?!6<9@>AB=!4C

S0X%++*Z%01%#)6$78FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*Z%01%#)6$78[US)0&4C

S7")XG6a*+"#$%&'()R"G*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*Z%01%#)6$78FX%++;

* * * N+"#$%&'()+N;*N,b%$G(8"Gc08$&)$&cZ:c!"#$%&'()CN4C

S8US)0&*+"#$%&'()@((%H[US)0&*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*Z%01%#)6$78[US)0&;*+"#$%&'()R"G4C

S"$&*X)$*.*3]Z^96=$\4_`<)&@((%Hb)$#&E3Z^96=$\;*+"#$%&'()@((%H[US)0&4C

S8US)0&*+"#$%&'()[US)0&*.*3]Z^96=$\4_`<)&[US)0&@((%H=X):)$&3Z^96=$\;*+"#$%&'()@((%H[US)0&;*D4C

S0X%++*+"#$%&'()FX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*+"#$%&'()[US)0&4C

S:)&E8G6a*E%+EF8G)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*+"#$%&'()FX%++;*NE%+EF8G)N;*N346N4C

"7*3*3]Z^96=$\4_`F%XX6$&2)&E8G3Z^96=$\;*+"#$%&'()[US)0&;*E%+EF8G)2"G4*..*:H!"#$%&'()I%+E*4

S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc%ZZc@0&"\"&HN4CS:)&E8G6a*#)&/%01%#)2%$%#)(2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)2%$%#)(N;*N34b%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(CN4CS:)&E8G6a*#)&/%01%#)9%:)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*N#)&/%01%#)9%:)N;*N34bS%\%cX%$#c!&("$#CN4CS0X%++*Z%01%#)2%$%#)(FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)2%$%#)(N4CS:)&E8G6a*#)&/%01%#)6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N#)&/%01%#)6$78N;*N3bS%\%cX%$#c!&("$#C64b%$G(8"Gc08$&)$&cZ:c/%01%#)6$78CN4CS7")XG6a*#)&!"#$%&'()+R"G*.*3]Z^96=$\4_`<)&!&%&"0R")XG6a3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*N<=>?!6<9@>AB=!N;*N6N4CS"$&*<=>?!6<9@>AB=!*.*3]Z^96=$\4_`<)&!&%&"06$&R")XG3Z^96=$\;*Z%01%#)2%$%#)(FX%++;*#)&!"#$%&'()+R"G4CS0X%++*Z%01%#)6$78FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c/%01%#)6$78N4CS7")XG6a*+"#$%&'()R"G*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*Z%01%#)6$78FX%++;* * * N+"#$%&'()+N;*N,b%$G(8"Gc08$&)$&cZ:c!"#$%&'()CN4CS0X%++*+"#$%&'()FX%++*.*3]Z^96=$\4_`R"$GFX%++3Z^96=$\;*N%$G(8"Gc08$&)$&cZ:c!"#$%&'()N4CS:)&E8G6a*E%+EF8G)2"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*+"#$%&'()FX%++;*NE%+EF8G)N;*N346N4CS8US)0&*Z%01%#)2%$%#)([US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)2%$%#)(2"G4C

S+&("$#*Z%01%#)9%:)!&("$#*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&/%01%#)9%:)2"G4CS8US)0&*Z%01%#)6$78[US)0&*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*Z%01%#)2%$%#)([US)0&;*#)&/%01%#)6$782"G;*Z%01%#)9%:)!&("$#;*<=>?!6<9@>AB=!4CS8US)0&*+"#$%&'()@((%H[US)0&*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*Z%01%#)6$78[US)0&;*+"#$%&'()R"G4CS"$&*X)$*.*3]Z^96=$\4_`<)&@((%Hb)$#&E3Z^96=$\;*+"#$%&'()@((%H[US)0&4CS8US)0&*+"#$%&'()[US)0&*.*3]Z^96=$\4_`<)&[US)0&@((%H=X):)$&3Z^96=$\;*+"#$%&'()@((%H[US)0&;*D4C"7*3*3]Z^96=$\4_`F%XX6$&2)&E8G3Z^96=$\;*+"#$%&'()[US)0&;*E%+EF8G)2"G4*..*:H!"#$%&'()I%+E*4

LVL - Tamper Resistance - Obfuscating

• The latest version of the Android tools has built-in support for Proguard and the LVLhttp://developer.android.com/guide/developing/tools/proguard.html

• With one minor change to the LVL, you can obfuscate much better (remove ILicensingService from the Proguard cfg)

LVL - Tamper Resistance - Obfuscating

• The latest version of the Android tools has built-in support for Proguard and the LVLhttp://developer.android.com/guide/developing/tools/proguard.html

• With one minor change to the LVL, you can obfuscate much better (remove ILicensingService from the Proguard cfg)

Change:

boolean!bindResult =!mContext.bindService(

!new!Intent(ILicensingService.class.getName()),

To:

boolean bindResult = mContext.bindService(

new Intent("com.android.vending.licensing.ILicensingService")

LVL - Tamper Resistance - Obfuscating

• The latest version of the Android tools has built-in support for Proguard and the LVLhttp://developer.android.com/guide/developing/tools/proguard.html

• With one minor change to the LVL, you can obfuscate much better (remove ILicensingService from the Proguard cfg)

Change:

boolean!bindResult =!mContext.bindService(

!new!Intent(ILicensingService.class.getName()),

To:

boolean bindResult = mContext.bindService(

new Intent("com.android.vending.licensing.ILicensingService")

Consider performing a transform on “com.android.vending.licensing.ILicensingService” to make the code less obvious.

LVL - Tamper Resistance - NDK

• Put checks in C/C++ code^96=V/[B>*SX8$#*^96F@bb*#)&FX%++)+FBF*3^96=$\*]*Z^96=$\;*S8US)0&*%0&"\"&H[US)0&4*d**SX8$#*()&\%X')*.*_TC**S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C**S:)&E8G6a*#)&@ZZX"0%&"8$6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;******N#)&@ZZX"0%&"8$6$78N;*N34b%$G(8"Gc08$&)$&cZ:c@ZZX"0%&"8$6$78CN4C**S8US)0&*%ZZX"0%&"8$6$78*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&@ZZX"0%&"8$6$782"G4C**S7")XG6a*Z%&ER")XG*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%ZZX"0%&"8$6$784;* *N+8'(0)a"(N;*NbS%\%cX%$#c!&("$#CN4C**S8US)0&*+&(@ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*%ZZX"0%&"8$6$78;*Z%&ER")XG4C**08$+&*SUH&)*]*%ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*9Abb4C**'$eR"X)*'7*.**'$e[Z)$3%ZZX"0%&"8$/%&E4C**"7*3'$eb80%&)R"X)3'7;N0X%++)+5G)fN;F@!=!=9!6>6Q6>g4..A9O?[h4*d****'$e?7"X)?"$78*]*7"X)6$78*.*:%XX803+"e)873'$e?7"X)?"$7844C**"7*3*A9O?[h*..*'$e<)&F'(()$&R"X)6$78*3'7;*7"X)6$78;*9Abb;*D;*9Abb;*D;*9Abb;*D4*4*d* *()&\%X')*.*7"X)6$78_`0(0C*****i***i**'$eFX8+)3'74C**3]Z^96=$\4_`B)X)%+)!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*%ZZX"0%&"8$/%&E4C**()&'($*()&\%X')Ci

LVL - Tamper Resistance - NDK

• Put checks in C/C++ code^96=V/[B>*SX8$#*^96F@bb*#)&FX%++)+FBF*3^96=$\*]*Z^96=$\;*S8US)0&*%0&"\"&H[US)0&4*d**SX8$#*()&\%X')*.*_TC**S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C**S:)&E8G6a*#)&@ZZX"0%&"8$6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;******N#)&@ZZX"0%&"8$6$78N;*N34b%$G(8"Gc08$&)$&cZ:c@ZZX"0%&"8$6$78CN4C**S8US)0&*%ZZX"0%&"8$6$78*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&@ZZX"0%&"8$6$782"G4C**S7")XG6a*Z%&ER")XG*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%ZZX"0%&"8$6$784;* *N+8'(0)a"(N;*NbS%\%cX%$#c!&("$#CN4C**S8US)0&*+&(@ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*%ZZX"0%&"8$6$78;*Z%&ER")XG4C**08$+&*SUH&)*]*%ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*9Abb4C**'$eR"X)*'7*.**'$e[Z)$3%ZZX"0%&"8$/%&E4C**"7*3'$eb80%&)R"X)3'7;N0X%++)+5G)fN;F@!=!=9!6>6Q6>g4..A9O?[h4*d****'$e?7"X)?"$78*]*7"X)6$78*.*:%XX803+"e)873'$e?7"X)?"$7844C**"7*3*A9O?[h*..*'$e<)&F'(()$&R"X)6$78*3'7;*7"X)6$78;*9Abb;*D;*9Abb;*D;*9Abb;*D4*4*d* *()&\%X')*.*7"X)6$78_`0(0C*****i***i**'$eFX8+)3'74C**3]Z^96=$\4_`B)X)%+)!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*%ZZX"0%&"8$/%&E4C**()&'($*()&\%X')Ci

^96=V/[B>*SX8$#*^96F@bb*#)&FX%++)+FBF*3^96=$\*]*Z^96=$\;*S8US)0&*%0&"\"&H[US)0&4*d

**SX8$#*()&\%X')*.*_TC

**S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C

**S:)&E8G6a*#)&@ZZX"0%&"8$6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*

*****N#)&@ZZX"0%&"8$6$78N;*N34b%$G(8"Gc08$&)$&cZ:c@ZZX"0%&"8$6$78CN4C

**S8US)0&*%ZZX"0%&"8$6$78*.*

****3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&@ZZX"0%&"8$6$782"G4C

**S7")XG6a*Z%&ER")XG*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*

****3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%ZZX"0%&"8$6$784;

* **N+8'(0)a"(N;*NbS%\%cX%$#c!&("$#CN4C

**S8US)0&*+&(@ZZX"0%&"8$/%&E*.*

*****3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*%ZZX"0%&"8$6$78;*Z%&ER")XG4C

**08$+&*SUH&)*]*%ZZX"0%&"8$/%&E*.*

*****3]Z^96=$\4_`<)&!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*9Abb4C

LVL - Tamper Resistance - NDK

• Put checks in C/C++ code^96=V/[B>*SX8$#*^96F@bb*#)&FX%++)+FBF*3^96=$\*]*Z^96=$\;*S8US)0&*%0&"\"&H[US)0&4*d**SX8$#*()&\%X')*.*_TC**S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C**S:)&E8G6a*#)&@ZZX"0%&"8$6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;******N#)&@ZZX"0%&"8$6$78N;*N34b%$G(8"Gc08$&)$&cZ:c@ZZX"0%&"8$6$78CN4C**S8US)0&*%ZZX"0%&"8$6$78*.*3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&@ZZX"0%&"8$6$782"G4C**S7")XG6a*Z%&ER")XG*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%ZZX"0%&"8$6$784;* *N+8'(0)a"(N;*NbS%\%cX%$#c!&("$#CN4C**S8US)0&*+&(@ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*%ZZX"0%&"8$6$78;*Z%&ER")XG4C**08$+&*SUH&)*]*%ZZX"0%&"8$/%&E*.*3]Z^96=$\4_`<)&!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*9Abb4C**'$eR"X)*'7*.**'$e[Z)$3%ZZX"0%&"8$/%&E4C**"7*3'$eb80%&)R"X)3'7;N0X%++)+5G)fN;F@!=!=9!6>6Q6>g4..A9O?[h4*d****'$e?7"X)?"$78*]*7"X)6$78*.*:%XX803+"e)873'$e?7"X)?"$7844C**"7*3*A9O?[h*..*'$e<)&F'(()$&R"X)6$78*3'7;*7"X)6$78;*9Abb;*D;*9Abb;*D;*9Abb;*D4*4*d* *()&\%X')*.*7"X)6$78_`0(0C*****i***i**'$eFX8+)3'74C**3]Z^96=$\4_`B)X)%+)!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*%ZZX"0%&"8$/%&E4C**()&'($*()&\%X')Ci

^96=V/[B>*SX8$#*^96F@bb*#)&FX%++)+FBF*3^96=$\*]*Z^96=$\;*S8US)0&*%0&"\"&H[US)0&4*d

**SX8$#*()&\%X')*.*_TC

**S0X%++*%0&"\"&HFX%++*.*3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%0&"\"&H[US)0&4C

**S:)&E8G6a*#)&@ZZX"0%&"8$6$782"G*.*3]Z^96=$\4_`<)&2)&E8G6a3Z^96=$\;*%0&"\"&HFX%++;*

*****N#)&@ZZX"0%&"8$6$78N;*N34b%$G(8"Gc08$&)$&cZ:c@ZZX"0%&"8$6$78CN4C

**S8US)0&*%ZZX"0%&"8$6$78*.*

****3]Z^96=$\4_`F%XX[US)0&2)&E8G3Z^96=$\;*%0&"\"&H[US)0&;*#)&@ZZX"0%&"8$6$782"G4C

**S7")XG6a*Z%&ER")XG*.*3]Z^96=$\4_`<)&R")XG6a3Z^96=$\;*

****3]Z^96=$\4_`<)&[US)0&FX%++3Z^96=$\;*%ZZX"0%&"8$6$784;

* **N+8'(0)a"(N;*NbS%\%cX%$#c!&("$#CN4C

**S8US)0&*+&(@ZZX"0%&"8$/%&E*.*

*****3]Z^96=$\4_`<)&[US)0&R")XG3Z^96=$\;*%ZZX"0%&"8$6$78;*Z%&ER")XG4C

**08$+&*SUH&)*]*%ZZX"0%&"8$/%&E*.*

*****3]Z^96=$\4_`<)&!&("$#A>RFE%(+3Z^96=$\;*+&(@ZZX"0%&"8$/%&E;*9Abb4C

**'$eR"X)*'7*.**'$e[Z)$3%ZZX"0%&"8$/%&E4C

**"7*3'$eb80%&)R"X)3'7;N0X%++)+5G)fN;F@!=!=9!6>6Q6>g4..A9O?[h4*d

****'$e?7"X)?"$78*]*7"X)6$78*.*:%XX803+"e)873'$e?7"X)?"$7844C

**"7*3*A9O?[h*..*'$e<)&F'(()$&R"X)6$78*

****3'7;*7"X)6$78;*9Abb;*D;*9Abb;*D;*9Abb;*D4*4*d

* ****()&\%X')*.*7"X)6$78_`0(0C*

****i*

**i

**'$eFX8+)3'74C

**3]Z^96=$\4_`B)X)%+)!&("$#A>RFE%(+3Z^96=$\;*

*****+&(@ZZX"0%&"8$/%&E;*%ZZX"0%&"8$/%&E4C

**()&'($*()&\%X')C

i

LVL - Tamper Resistance - NDK

• Get native signatures from class file with javap -s

– S%\%Z*_+*_Z("\%&)*08:5%$G(8"G5#XPS$"5<bP^96b"U

+&%&"0*^969%&"\)2)&E8G*:)&E8G+,-*.*d**d*N"$"&N;*N3664QN;*3\8"G]4*$%&"\)<%:)6$"&*i;***d*N+&)ZN;*N34QN;*3\8"G]4*$%&"\)!&)Z*iiC

**0E%(]*0X%++9%:)*.*N08:c%$G(8"Gc#XPS$"c<bP^96b"UNC**S0X%++*0X%ee*.*)$\_`R"$GFX%++30X%++9%:)4C**"7*30X%ee*..*9Abb4*d****??%$G(8"G?X8#?Z("$&3@9aB[6a?b[<?=BB[B;*N@J)+8:)<%:)N;******N9%&"\)*()#"+&(%&"8$*'$%UX)*&8*7"$G*0X%++*jk+jW$N;*0X%++9%:)4C**()&'($*^96?R@b!=C**i**"7*3)$\_`B)#"+&)(9%&"\)+30X%ee;*#2)&E8G+;*$':2)&E8G+4*l*D4*d* ??%$G(8"G?X8#?Z("$&3@9aB[6a?b[<?=BB[B;*N@J)+8:)<%:)N;* **NB)#"+&)(9%&"\)+*7%"X)G*78(*jk+jW$N;*0X%++9%:)4C**()&'($*^96?R@b!=C**i

LVL - Tamper Resistance - Advanced Ideas

• Store LVL and other application binaries as encrypted resources. Decrypt to the filesystem and use the class loader to call the LVL

• Make direct calls to the Binder interface, eliminating the entire RPC shell class

When Pirates Become Vampires

When Pirates Become Vampires

When Pirates Become Vampires

• Games often have server components

• Android games often have asset downloads

• Running an unlicensed title can turn a pirate into a vampire that feeds off your bandwidth

• Since you have a server component, you canstop vampires with server-side validation

Android Market Licensing - Server Validation

Market Licensing Server

Android Market Client

Licensing Service

Your Server

Your Application License Verification Library

Android Market Licensing - Server Validation

Market Licensing Server Private Key

Android Market Client

Licensing Service

Your Server Public Key

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Market Licensing Server Private Key

Android Market Client

Licensing Service

Your Server Public Key

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Checks Purchase Information

Market Licensing Server Private Key

Android Market Client

Licensing Service

Your Server Public Key

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Checks Purchase Information

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Your Server Public Key

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Checks Purchase Information

Signed Response

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Your Server Public Key

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Checks Purchase Information

Signed Response

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Your Server Public Key

Delivered to Serverfor Validation

Your Application License Verification Library

Android Market Licensing - Server Validation

Check License Request

Checks Purchase Information

Signed Response

Market Licensing Server Private Key

Android Market Client

Licensing Service

Response Signed with Private Key

Your Server Public Key

Delivered to Serverfor Validation

Your Application License Verification LibrarySession

Android Market Licensing - Server Validation

Your Application License Verification Library

SessionToken

Your Server

Android Market Licensing - Server Validation

Request Contentwith Session

Your Application License Verification Library

SessionToken

Your Server

Android Market Licensing - Server Validation

Request Contentwith Session

Your Application License Verification Library

SessionToken Return Content

Your Server

Server-Side LVL - Replay Attacks

Replay

Server-Side LVL - Replay Attacks

Rely on the Nonce

• Generating the nonce on your server simplifies the server, as only nonces “in progress” must be tracked - but this adds an extra round trip to your server

• Generating the nonce on the client means the server must store all nonces

Replay

Change the Monetization Model

• Consider a free version of the game that can be upgraded

• Use game mechanics that trade money for time

• Provide add-on content that extends play

Android Market In-App Billing

• Purchase Virtual Goods (Managed Items)

• Purchase Consumables (Unmanaged Items)

• Client-side or Server-side Validation

Android Market In-App Billing

• Purchase Virtual Goods (Managed Items)

• Purchase Consumables (Unmanaged Items)

• Client-side or Server-side Validation

In-App Billing - Managed Items

• SKU’s in Market - like your application

• Can only be purchased once

• Applications can ask to replay all managed item purchases

In-App Billing - Unmanaged Items

• SKU’s in Market - like your application

• Can be purchased multiple times

• Applications cannot ask for a replay

In-App Billing - Client Version

Your Application

Market Server

Android Market Client

BillingReceiver Security

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Market Server Private Key

Android Market Client

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Billing Request

Market Server Private Key

Android Market Client

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Billing Request

Billing Request

Market Server Private Key

Android Market Client

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Billing Request

Billing Request

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Billing Request

Billing Request

Signed Response (onReceive())

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Client Version

Your Application

Billing Request

Billing Request

Signed Response (onReceive())

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver Security Public Key

Market Billing Service

BillingService

In-App Billing - Server Version

Your Application

Market Server

Android Market Client

BillingReceiver

Market Billing Service

BillingService

Your Server Security

In-App Billing - Server Version

Your Application

Market Server Private Key

Android Market Client

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

In-App Billing - Server Version

Your Application

Billing Request

Market Server Private Key

Android Market Client

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

In-App Billing - Server Version

Your Application

Billing Request

Billing Request

Market Server Private Key

Android Market Client

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

In-App Billing - Server Version

Your Application

Billing Request

Billing Request

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

In-App Billing - Server Version

Your Application

Billing Request

Billing Request

Signed Response (onReceive())

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

In-App Billing - Server Version

Your Application

Billing Request

Billing Request

Signed Response (onReceive())

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver

Market Billing Service

BillingService

Your Server Security Public Key

AddCredentials

In-App Billing - Server Version

Your Application

Billing Request

Billing Request

Signed Response (onReceive())

Market Server Private Key

Android Market Client

Response Signed with Private Key

BillingReceiver

Market Billing Service

BillingServiceSessionToken

Your Server Security Public Key

AddCredentials

In-App Billing - Server Version

Your Application

BillingReceiver

BillingService

Your Server Security Public Key

In-App Billing - Server Version

SessionToken

Your Application

BillingReceiver

BillingService

Your Server Security Public Key

In-App Billing - Server Version

SessionToken

Request Contentwith Session

Your Application

BillingReceiver

BillingService

Your Server Security Public Key

In-App Billing - Server Version

SessionToken

Request Contentwith Session

Return Content

Your Application

BillingReceiver

BillingService

Your Server Security Public Key

In-App Billing - Request Purchase

Android Market

Application Activity

In-App Billing - Request Purchase

Request Purchase

Android Market

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

Android Market

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Android Market

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

Android Market

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

Android Market

Activity onPause()

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

Android Market

ActivityonResume()

Activity onPause()

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

BroadcastPurchase State

Changed

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

Confirm Notifications

BroadcastPurchase State

Changed

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

Confirm Notifications

BroadcastPurchase State

Changed

BroadcastResponse Code

Request ID

Android Market

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

In-App Billing - Request Purchase with Server

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

Server Android Market

In-App Billing - Request Purchase with Server

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

Request Nonce

Server Android Market

In-App Billing - Request Purchase with Server

BundleResponse Code Purchase Intent

Request ID

Request Purchase

InvokeStartIntentSender

Display Checkout UI

BroadcastResponse Code

Request ID

ActivityonResume()

Activity onPause()

BroadcastResult Code

Application Activity

Get Purchase Information

Request Nonce

Server

Return Session &

Nonce

Android Market

In-App Billing - Request Purchase with Server

BroadcastResponse Code

Request ID

Confirm Notifications

BroadcastPurchase State

Changed

BroadcastResponse Code

Request ID

ActivityonResume()

BroadcastResult Code

Get Purchase Information

Android MarketServer

Application Activity

In-App Billing - Request Purchase with Server

BroadcastResponse Code

Request ID

Confirm Notifications

BroadcastPurchase State

Changed

BroadcastResponse Code

Request ID

ActivityonResume()

BroadcastResult Code

Get Purchase Information

Android Market

Perform Server Billing Auth

Server

Application Activity

In-App Billing - Request Purchase with Server

BroadcastResponse Code

Request ID

Confirm Notifications

BroadcastPurchase State

Changed

BroadcastResponse Code

Request ID

ActivityonResume()

BroadcastResult Code

Get Purchase Information

Android Market

Perform Server Billing Auth

Server

Validate Billing Response

Application Activity

App Engine can be Your Server

Runs your Java servlet or Python code on our infrastructure

• Validate license responses

• Secure In-App-Billing Transactions

• Serve files from BlobStore

• App Engine is useful as an example. This can be run easily on any Java-capable server.

Creating an AppEngine Project

Creating an AppEngine Project

Creating an AppEngine Project

Creating an AppEngine Project

Manage your AppEngine Server

Server-Side License Validation

Z'UX"0*\8"G*G8/8+&3I&&Z!)(\X)&B)m')+&*()m;*I&&Z!)(\X)&B)+Z8$+)*()+Z4

* &E(8J+*6[=f0)Z&"8$*d

* * cc*[U&%"$*+"#$)G*X"0)$+)*+)(\)(*G%&%*%+*%*/[!>*Z%(%:)&)(

* * !&("$#*X"0)$+)*.*()m5#)&/%(%:)&)(3NX"0)$+)N4C

* * !&("$#*+"#$%&'()*.*()m5#)&/%(%:)&)(3N+"#$%&'()N4C

* * !&("$#*\)(+"8$F8G)*.*()m5#)&/%(%:)&)(3N\)(+"8$_08G)N4C

* * * *

* * cc*=f&(%0&*$8$0)*7(8:*+)++"8$

* * I&&Z!)++"8$*+)++"8$*.*()m5#)&!)++"8$3&(')4C

* * [US)0&*$8$0)[US)0&*.*+)++"8$5#)&@&&("U'&)3N$8$0)N4C

* * +)++"8$5+)&@&&("U'&)3N$8$0)N;*$'XX4C*cc*FX)%(*$8$0)*+8*"&j+*8$XH*\%X"G*8$0)

* *

* * "$&*$8$0)*.*336$&)#)(4*$8$0)[US)0&45"$&Q%X')34C

* * cc*Q)("7H*+"#$%&'()*78(*X"0)$+)

* * b"0)$+)Q%X"G%&8(*X\*.*$)J*b"0)$+)Q%X"G%&8(3$)J*9'XXa)\"0)b":"&)(34;*$8$0);*:/%01%#)9%:);*\)(+"8$F8G)4C

* * b"0)$+)!&%&'+*+&%&'+*.*X\5\)("7H3:/'UX"0h)H;*X"0)$+);*+"#$%&'()4C

* *

* * +)++"8$5+)&@&&("U'&)3N+&%&'+N;*+&%&'+4C

* * "7*3+&%&'+*..*b"0)$+)!&%&'+5b6F=9!=a4*d*()+Z5#)&['&Z'&!&()%:345Z("$&3N81N4C*i*

)X+)*"7*3+&%&'+*..*b"0)$+)!&%&'+59[>?b6F=9!=a4*d*()+Z5+)$G=((8(3MDn;*NA+)(*$8&*X"0)$+)GN4C*i*

)X+)*"7*3+&%&'+*..*b"0)$+)!&%&'+569Q@b6a?!6<9@>AB=4*d*()+Z5+)$G=((8(3MDD;*N6$\%X"G*+"#$%&'()N4C*i*

)X+)*d*()+Z5+)$G=((8(3oDD;*N=((8(*G)08G"$#*X"0)$+)*G%&%N4C*i

* i

Android Cloud Services

Your Application

Android Cloud Services

Cloud to Device Messaging

Your Application

Android Cloud Services

Cloud to Device Messaging

Google Talk

GMail

Your Application

Android Cloud Services

Cloud to Device Messaging

Google Talk

Backup

GMail

Your Application

Android Cloud Services

Cloud to Device Messaging

Google Talk

Android Market

Backup

GMail

Your Application

Billing

Licensing

Cloud to Device Messaging

• Sends lightweight messages to Android applications

• Queues and delivers messages to the target device

• Launches the application if it is not running

• Leverages the network connection being used for Google services

Cloud to Device Messaging Basic Flows

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

Creates and Stores Session

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

Creates and Stores Session

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

Creates and Stores Session

Sends C2DM

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

Creates and Stores Session

Sends C2DM

Sends Message to Service

C2DM Server

C2DM Service

Your Server

Cloud to Device Messaging Basic Flows

Sends REGISTER Intent

Sends REGISTER Intent Responds with REGISTRATION

REGISTRATION intent contains ID

Forwards Registration ID to Server

Your Application

Creates and Stores Session

Sends C2DM

Sends Message to Service

C2DM Server

Sends C2DM Intent to Application

C2DM Service

Your Server

Application Data Backup

• Copies application data to cloud

• Backup is requested by application

• Automatically restored when application is installed

• Application Registers itself with Backup Service (server) and Backup Manager (client)

Android Cloud Backup - Backing Up

Android Backup Server

Your Application

Android Backup Manager

Backup Agent

Android Cloud Backup - Backing Up

Android Backup Server

DataChanged()

Your Application

Android Backup Manager

Backup Agent

Android Cloud Backup - Backing Up

Android Backup Server

DataChanged()

Your Application

Android Backup Manager

onBackup()

Backup Agent

Android Cloud Backup - Backing Up

Android Backup Server

DataChanged()

Your Application

Android Backup Manager

onBackup()

Backup Agent

Data Backed up to Cloud

Android Cloud Backup - Restoring

Application Installed

onBackup()

Backup Agent

Android Backup Server

Android Backup Manager

Android Cloud Backup - Restoring

Application Installed

onBackup()

Backup Agent

Is Data Backed Up?

Android Backup Server

Android Backup Manager

Android Cloud Backup - Restoring

Application Installed

onBackup()

Backup Agent

Is Data Backed Up?

Android Backup Server

Android Backup Manager

OnRestore()

Like the Androids in this Presentation? Androidify Yourself

http://www.androidify.com

Questions?

http://goo.gl/Q8SR7#Android

top related