Search this site
Adding copy protection & using the AquaticPrime licensing framework
This is a guide for programmers (software developers). It gives practical hints about using the AquaticPrime (AQ) framework, which helps commercial software publishers to add license validation to their applications. It is available mainly for Mac OS X, but with partial support for Windows and Linux, too.
The original, now defunct, AquaticPrime website is at http://www.aquaticmac.com/. The current website, with latest code for many system and programming languages (ObjectiveC, C++, PHP, REALbasic), is at https://github.com/bdrister/AquaticPrime.
This guide covers the following points of interest:
On this page... (hide)
You think about protecting your software against unauthorized use. Think twice.
You may not like the idea that people use your work without compensation. However, here are some reasons why you should not even care:
I've employed most of this guide in one of my own apps, Arbed, which sells at a rather high price. Here, the effort was worth it, I think. I also learned a lot, which is a plus. Another app of mine, the quite popular Find Any File, is sold at such a low price that I didn't bother with AQ and instead just have a hidden method that I reveal to my customers for deactivating the occasional shareware nag screen. It's all about making it the least hassle for the customer, adjusted to the value of the product.
Most software that we license nowadays gives us a rather lengthy code that we have to enter into the app's license validation dialog, after which the app decides whether the code is valid and enables all or certain application features.
Internally, it works like this: The code the user sees is hiding some secret information. The code follows some rules that allow a verification that this code is valid. The app decodes the code and checks if the rules are met. Thereby the app decides if the code is valid and not a fake one, and can also extract the hidden information such as the user's name and entitlements.
There is a problem with this approach: The decoding can be reverse engineered by looking at the code in the software, and then a key generator (keygen) can be created that provides new fake licenses. With a keygen made available publicly (just have a look at PirateBay), your apps, even updates, can be easily used by anyone without having to purchase a license from you. Blacklisting such fake licenses become more and more difficult, as you'll have trouble telling your own valid ones from those generated by the keygen, as both appear valid to your current app. Fighting this can become quite tedious.
AquaticPrime goes a different way by using the technique of asymmetric key encryption with both a public and a private key, as it's also used in SSL (https), for instance. In fact, asymmetric keys can be used in two ways: To encrypt data in a way that only the intended recipient can decipher it, and to sign data, allowing everyone to verify that the sender has authored it, and no on else.
AquaticPrime uses only the signing part of this technique. As such, the key differences to the secret code encryption explained above are:
In short, AQ solves mainly one particular problem with licenses: That of preventing others to create licenses without your agreement.
AQ does not prevent your app from being cracked, from removal of the license checking code, and from redistribution of such a cracked app. Therefore, AQ by itself doesn't prevent software piracy. It just makes abuse harder.
Of course, you are free to combine both schemes: Use a AQ license file, and include as one of its items a "secret" code that hides information and is decoded inside your application, using its value directly for some behavior in a way that is not predictable by an attacker, yet uniquely bound to the rest of the visible data in the license.
And even if you're now disillusioned by the potential of AQ, I'll give you some tips below about how to keep your app from being cracked. So read on, even if you're not going to use AQ, after all.
An AQ license is a file that usually contains the following components:
This means that if the user receives a license after a purchase, he won't receive a code to type in but instead will receive a file that he has to pass to the software for processing.
The smart way to accomplish this is to give the license file a unique file name extension, e.g. after the pattern: .appname-license The software registers itself to handle this extension. That way, if the user receives the license file, he only has to double click it. This will launch your software, which can then validate the license and store it in a safe place.
The license generation, delivery, installation and validation works about as follows:
If you use FastSpring, you can set it up so that you upload your private key once, and then instruct it to create this file from the customer data (e.g. the name from the used credit card), and offer it as a downloadable file right away, and also send it separately to the customer via e-mail. If you need help with that, contact me - I'll extend this chapter with more detail and examples upon request.
Now that it's clear how the app determines a valid license file, we must consider the possible attacks against this process.
As I've already stated above, it's practically impossible that someone else can create fake license files without access to the private key. That leaves two other possibilities of attack:
When you use blacklisting, there are two ways: Hard-coded and auto-updating.
With hard-coded blacklisting, the app would have the IDs of the blacklisted licenses inside the app. That means, however, that you can only enable such blacklists in newer versions of your app, after the license has already been compromised. Users trying to use a blacklisted therefore can keep using the older version without difficulty.
To deal with this problem, your app could try to retrieve blacklist updates from your server over the internet. A smart pirate can circumvent such an attempt, however, in two ways:
You might fare better if you do not put your customers into a position where you suggest that you generally do not trust them - this will upset some of them and give you negative reviews, at worst. I suggest that instead of going the auto-updating blacklist path, rather only use the hard-coded blacklist and lure those pirates into wanting new versions because your app keeps getting better.
First of all, similar to protecting your app from an attacker simply patching your blacklist, you need to hide your public key in your app's code: If the attacker can find the key in plain sight in your app using a hex editor, he could simply insert a key of his own, and then provide his own license that's created from his own asymmetric key pair. Therefore, hide the public key as well, to make finding and patching of it not too easy.
Until now, I've explained the simply types of attack, those accomplishable by even the laziest crackers and pirates.
Finally, we come to the more advanced attacks: Against your app's code.
Cracking your code requires an attacker who understand machine language. He'll use OSX tools such as "nm" and "gdb" to analyze your code and patch it.
The first attack will be directly to the AQ function that determines if a license if valid. There is a single point in code where the license signature is compared to the value it should have, and if the attacker tweaks this one instruction, he can make it so that even an invalid signature results in the dictionary being return, so that your code will think that the license is valid.
Protecting yourself against this attack is rather easy: You need to include a false-positive test. That means that you call the same AQ function once more, this time passing in a similar, but invalid license. E.g. you could take the same license you tested, but modify its signature slightly. This should surely result in the function determining an invalid license. But if the function tells you that the license is valid, too, you'll know that someone tweaked your code.
Next, you need to add more checks for the given license, maybe even using entirely separate functions. The more checks the better. Each one should use different code so that the cracker cannot simply find all similar checks by a simple search of the instruction code sequence. And perform the checks at various times, within vital parts of your app's functionality.
The cracker will have to find all code that performs these checks...
The gdb breakpoint attack
This is a dynamic attack on your code.
To find the code that performs license checks, he could set a breakpoint into the affected libcrypto functions and whenever it hits, he could look at the stack trace to find the caller and thereby get to the point where the caller reacts to the positive/negative result, and make it so that it always behaves as if it received the positive result. To counteract this, you could try avoiding the libcrypto calls in some cases and instead hard-code the relevant parts of the public key validation code. That's quite some work, though. (If you've extracted the relevant code, please contact me so I can share the how-to or the code itself here.)
Similarly, if you only use one central function that performs the license check, the cracker will easily find this one (as it's called when opening a new license file), and then can set a breakpoint there, thereby find all calls to it and patch them.
To counter this breakpoint attack, you need to make infrequent, sparse calls, so that the cracker, while testing your app, won't see all invocations and thereby likely overlook a few necessary patches.
The symbol lookup attack
Then there's the static attack on your code.
Especially if your code is written in ObjectiveC, practically all functions, even with their parameter names, appear clear text in your app's code. A tool such as "nm" reveals them easily.
Therefore, you need to hide the names of all relevant functions. I do not know of a simple way to accomplish this. You may have to manually rename the used AquaticPrime class and its methods to turn them into unsuspecting names that won't reveal their purpose. However, remember that the the first AQ invocation, which happens if the user opens the new license file to register it, will be rather easily found using gdb by stepping through the code. Therefore, hiding this one may be rather futile. However, it might be smart to have a second copy of the AQ class, all with renamed identifiers, and call this later for another license check. To find this one, the cracker would have to go the dynamic way of breaking on other known functions that the AQ methods use, such as the functions of the libcrypto lib. So, if you can hide that as well, you'll make it very hard for the attacker to find this code.
Another way is to hide the references to the AQ lib functions. In ObjectiveC, you can use the inclusion of the class and method names to your advantage: Using the functions objc_lookUpClass() and class_getMethod() you can invoke methods without revealing them directly in the code. Instead, the cracker needs to first realize that you're invoking the methods that way and then find the names of the looked-up class and methods in the code, then find their references. And if you encode the names so that they do not appear in clear text to be found by a hex editor, you've successfully foiled this attempt at a static attack. Which means you leave the cracker to go the dynamic route, which is harder, usually.
Another path to get to your license checking code might be to find accesses to the license file. There are debugging tools, e.g. DTrace, which make it easy to trace calls to system functions along with their arguments. That could make it easy to find all the code points that open or reference the license file. Therefore, consider loading the license file once and access it from memory. However, I suspect (not sure) that there are also ways to find all code points that reference a certain variable - so if you store the license data, all refs to it might be found rather easily as well. To counter that, use other ways of accessing that memory, e.g. by using a known memory layout and then doing pointer arithmetics to find the same location from different references. This can be accomplished in C using a set of static variables in succession, for instance. In ObjectiveC, you can also use the class_getClassVariable() function to look up the variable by its name, along with storing the name in an encoded way in your source code, then decoding the name only at runtime.
Most important rule: Do not taunt the cracker! Crackers are usually idealists, and this wouldn't discourage them the least. If you challenge them too well, they'll not give up, no matter how hard you make it for them, just to teach you a lesson in return. It'd be a question of honor and pride for them.
Next, you might be tempted to take immediate "action" if you detect an attack. Resist from this, too, for two reasons:
Instead, let the attack go unnoticed in your app's behavior first.
For instance, modify just a innocent looking variable or pointer that only much later will be accessed again, then leading to an inexplicable crash or other misbehavior of the app.
The goal here is to make it difficult for the cracker to see if he's done with his crack. If the app only misbehaves later, and he won't understand when, he's forced to test the app thoroughly to see if there's more to do. And once he sees the app is misbehaving, he'll need to track back to find what caused this misbehavior. It'll be tedious. It'll tire him. And that's the path to make him give up.
Because that's for sure: Any app can be cracked. It's just a matter of how much effort has to be put in for that. Make this your primary goal: Discouraging the cracker.
Think of this: The cracker can't know in advance how much work it'll be to crack your code. So, the more hard-to-find hurdles you install, the better. Once the cracker has passed the first hurdle, he'll run into the next. And the next. And so on. And he's never sure when this will end. This can take him hours, days. Eventually he might give up, depending on the value that this crack is worth to him. The cracker works usually for his ego. The more popular the software is, the more likely he'll be challenged to go on. Unless you're writing something that the CIA is interested in cracking. Then you'll have no chance :)
Of course, feedback on improving this guide is welcome, too. And let me know if you have questions or don't understand something.