Upload
arsch777
View
100
Download
3
Embed Size (px)
Citation preview
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