Google's Developer Programs Engineer Trevor Johns explains four methods to guard against piracy:
- Customizing the Licensing Library
- Making your application tamper resistant
- Using a secondary server for added protection
Lets look at the first technique: obfuscation. This should be the first line of defense in any application. It won't protect against automated attacks, but it will make the initial attack more difficult by removing symbols that would easily reveal the original structure of the compiled application. There are plenty of open source and commercial obfuscaters available for Java that will work with Android apps.
Modifying the license library is another technique you can use against attackers. This will make it difficult for an attacker to modify the disassembled code and get a positive license check. This technique protects against people trying to crack your application and it also prevents attacks that target your application through other applications. You'll want to modify three main areas in the license library - the core library logic, entry/exit points in the library, and the code that your application uses to invoke the licensing library and handle its response. Your goal its to modify the LicenseChecker and LicenseValidator classes in the core LVL logic. You should be as creative as possible without changing the original function of the application. Here are some things you can do:
- Replace switch statements with if statements.
- Use XOR or hash functions to derive new values for any constants used and check for those instead.
- Remove unused code. For instance, if you’re sure you won’t need swappable policies, remove the Policy interface and implement the policy verification inline with the rest of LicenseValidator.
- Move the entirety of the LVL into your own application’s package.
- Spawn additional threads to handle different parts of license validation.
- Replace functions with inline code where possible.
To make your application tamper resistant, you need to make your code recognize when it's being modified. This can be done with a lightweight hash function, such as CRC32, to build a hash of your application's code. Just compare the checksum with a known good value. You can also check to see if your application is debuggable. It's harder to compromise your application if it stops performing normally when the debug flag is set.
boolean isDebuggable = ( 0 != ( getApplcationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) );
If your application includes an online component, you can also send a copy of the license server response and its signature to a trusted secondary server. As long as the license checks are handled entirely within the server code, even an expert cracker will not be able to gain access to any online content because the server is using a trusted computing environment.
Above all else, be creative and make your LVL implementation unique so that it is difficult to trace when decompiled and resistant to changes.