For the paid version of my app I'm opting for the unlocker app route because it's easy to implement, allows for individual stats in the Developer Console but mostly because I wouldn't need to maintain 2 code bases (one for the free version and another for the paid version). Even If I used a CVS (which I do) it would still be a pain in the neck to keep merging features and bug fixes. The unlocker app is much easier to implement overall...
But this comes with a serious disadvantage, it's really easy to overrun the security check; unless I'm missing something here.
No matter what I do, such implementation will always lead to a simple if
, like this:
if(Program.isPremiumVersion()) {
// Remove ads...
}
The isPremiumVersion()
method is the one responsible for all the work in checking for the paid unlocker app installation, if the certificates match and all that stuff. Yes, the unlocker app is protected by the LVL (although I've read a few articles mentioning how insecure LVL is, but that's not the point right now). But in the end, no matter how complex the code inside isPremiumVersion()
gets, it always results in returning a true
or false
value.
Overriding such security feature is just a matter of reverse engineering the code and get it to always return true
. Is it not? How can we protect our Android apps against this? And yes, the code is obfuscated with ProGuard. Still, shouldn't be too hard for someone skilled enough.
Please note that I'm not trying to fight the crackers, we simply cannot win. I'm not going to lose sleep over this, wasting countless hours on the "perfect solution". I'm just looking for a way to make it a little more secure. This just seems so simple to crack, in theory at least. Am I wrong though?
Any ideas to improve the security of such feature?
There is no easy way around this.
You have to try to mask it. Here are a few tips:
Tip 1: Returning boolean is too obvious. Try returning a value (int, for example). Then, use a comparison to see if that is a valid known return value.
For example: get the md5 of a string that contains something from which you can tell if it's premium or not. Say that you've got a static final string on each app. Maybe the md5 of one starts with a 9 and the other starts with a 1. In this case, calculate the md5 and see if it's greater than a "random" number that you know it's in between the other two numbers. Say that the md5 of "premium" is 987 and the md5 of "free" is 123. You can calculate the md5 and compare it to 456.
Tip 2 - Even better: duplicate some code and use different values every time (instead of 456)! Hopefully this will make it more difficult to decode the obfuscated code.
I know that all these checks will eventually be mapped to a boolean (if(1 > 2)
will be evaluated to if(true)
) but it should be more difficult to reverse engineer your app.
Tip 3: don't run the checks for "isPremium" in the most obvious places. For example, don't do the check when you start your app as this is the most obvious place to do it. It might be difficult to avoid certain obvious spots if you want to have conditional logic depending on the version of the app, but do your best here!
Tip 4: build and obfuscate your app. Run reverse engineering tools against your apk. Read it and see how it looks.
Finally, watch this Google IO presentation everyday at breakfast: Evading Pirates and Stopping Vampires using License Verification Library, In-App Billing, and App Engine
[EDIT - a few more tips]
Tip 6: try to use the code you use to check in perfectly valid places. This might disguise what you are really doing in there. This might include calling the code to check which version of the app this is, but doing nothing meaningful with it. Or, in my previous example, comparing the md5 with 012 or 999, just for the sake of diluting the real use of these variables.
Tip 7: instead of relying in a single string, you might considerer constructing the string at run time. Finals and statics might draw too much attention too, so avoiding those could be a good thing.
Tip 8: don't ever use the LVL code as provided in google tutorials. Modify it. A lot!
Note: I'm not sure if any of these tips will actually make a big difference, but you should have good chances of at least making crackers' life a bit harder.
You may have already seen this, but here is some code for implementing what you are talking about:
http://groups.google.com/group/android-developers/browse_thread/thread/4ad3d67f735f16d7/948b4f9eee2490a3?pli=1
It checks that the signatures on the free and unlocker app are the same. So it is not possible for someone to artifically create an app with the correct name as the signitures will be different. However, it is still possible for people to rip the apk off the phone and distribute that. The only way to combat that would be to use some sort of server authentication but this adds cost and complexity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With