Local Links

External Links

Contact

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:

  • Advantages of AQ over the commonly used license key codes.
  • Providing a good user experience for the customer.
  • Using the FastSpring payment processor for generating licenses.
  • Protecting your app against cracking and other ways of circumventing the license validation.

This guide is a result of a lecture I've given at two Real Studio conferences in 2011 in Atlanta, USA, and Essen, Germany. You can find the original slides here: REALbasic - Selling your work.pdf



1.  Introduction to AquaticPrime

1.1  Do you need copy protection, really?

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:

  • The easier the app can be copied by "friends", the more popular it'll get. And unless you already know you'll write that app that surely will make you a millionaire, it'll be hard to make your software known, and the more people know about it, the more likely even more will buy it.
  • Those who appreciate your software are likely to pay for it even if they got it for free initially. Often it only requires a friendly reminder from time to time.
  • And those kids that (illegally) copy mainly games for free, won't even buy them anyway, no matter how much you try to force them. If your copy protection is too strong, they'll just ignore yours and find another game. There's more than plenty of competitors, you know :)
  • If you focus too much on protecting your work, you'll not enjoy the whole business of selling software in the long run. Try to enjoy what you can get, instead of being unhappy about what you can't.

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.

1.2  Comparison of license schemes

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:

  • An AQ license doesn't hide anything, by default.
  • While the software can verify if a license is valid, no one can write a keygen to create fake licenses.

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.

2.  AquaticPrime in action

2.1  The license data

An AQ license is a file that usually contains the following components:

  • Information about the customer, such as his name, e-mail address etc. This information is usually used to be displayed in the App to show who this license belongs to.
  • License entitlements that the app uses to unlock features based on the license, or tying it to a certain version of the app (so that the license won't work on later upgrades). This could be a product key in case you use the same licensing scheme for multiple products. Volume licenses can be recorded in this, too.
  • A purchase date may be handy to have in the license.
  • Finally, the so-called signature, i.e. a long code that is used to validate all the above components in the license file.

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.

2.2  The path of the license

The license generation, delivery, installation and validation works about as follows:

  1. First of all, an asymmetric key pair needs to be created. It's sufficient to create just one pair for all the software products you ever want to deliver, but it's not unreasonable to have one per product. A pair consists of two keys, i.e. files, one of which, the private one, you need to keep to yourself - anyone having access to this private key can create valid licenses. Therefore, make sure no one unauthorized can ever get to it. To create these key pairs, you can use the AquaticPrime Developer.app inside the AQ source's License Utility folder (OS X only, so far).
  2. Every time the customer has made a purchase, a license file needs to be created. This can be done again with the AquaticPrime Developer.app or with some other solutions from the Source folder, e.g. via PHP. Or have your payment processor, e.g. FastSpring, do this automatically upon purchase (see below). The license generator takes the user identification, the fixed-per-product entitlements, and the private key as input, and outputs a file that contains the same user identification and entitlements, added with a signature code.
  3. This file then gets delivered to the customer, either by download right after the purchase, or is sent to him via e-mail.
  4. If the license uses a unique extension as suggested above, then the user only has to open (double click) the license file in order to launch the related application. Otherwise, the user would launch the app manually and open the license file, or the app could even retrieve it from a server if it provides the entire purchase user interface directly from within the app. Note: If you go the path of providing your own purchase dialog in your app, resist from also generating the license directly in your app, because that would require the inclusion of your private key, which a cracker could find and then create a keygen. Remember: The secrecy of the private key is key to AQ's protection. Don't let anyone get near it.
  5. Once the app gets the license file, it has to check if it's valid, i.e. if the name and entitlements in it are really a valid license and not a homemade fake by a "pirate". To do that, your app includes the license validation code from this AQ framework. It's basically a single function that takes the file and the public key and returns a Boolean state whether the license is valid or not. You'd first instantiate an object of the AquaticPrime class, providing the public code which you include in your app code, then invoke the dictionaryForLicenseFile method, passing a reference to the license file. The method returns nil if the license is invalid, or a dictionary with the license contents otherwise. The app can then extract the entitlements and user info from the dictionary.
  6. Once the app determines that the newly opened license is valid, it should store this file in a safe place (in case of Mac OS X, the Application Support folder is a good place, as per Apple's rules you should create your own folder inside this App Support folder, naming it after your product or after your software's bundle id, and inside that folder of yours goes the license file). Next time your app launches, it looks in said place and re-validates the license that way each time.

2.3  Using FastSpring

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.

3.  The tricky part - foiling attempts at cracking the license validation

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:

  1. A valid license can be distributed and thereby used by many others. If you see such a license out in the open, it doesn't even have to mean that the purchaser of the license was the culprit, with the intent to harm your business. It might as well be that the person got digitally robbed. Therefore, do not immediately punish the person (in case you know his name or e-mail address), but give him the benefit of the doubt by contacting him first. Maybe he's just a victim just like you. To counteract such "lost" licenses, you'd blacklist the affected license. The AQ library provides functionality for this, but you could do this any other way, too, as all you have to do is to check if the license contents match the blacklisted one(s) - and you can do that by comparing their signature, or their contents, or some other way that lets you identify them reliably.
  2. Your application code could be hacked, i.e. directly modified or otherwise influenced (by injected code or by patching libs that it uses, e.g. the libcrypto.dylib) to make your code believe that a missing or fake license is valid.

3.1  Blacklisting

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:

  • Patch your app by removing the blacklist. Therefore, you must make sure to hide the blacklist data well. Consider this: If you use the default blacklisting method of the AQ framework, you'd pass it the license signatures as blacklist IDs. Now, if you stored these signatures as individual string contents, the attacker could simply use a hex editor such as HexEdit and use its Find function to find the signature of any compromised license, and edit it out. He could then release a tiny app along with the compromised license file that would find the key in your app, erase the key, and boom, you updated app would still see the compromised license as valid because it's not appearing in the blacklist, apparently. Therefore, you need to hide your blacklist data, e.g. store it encoded, and then decode it only when passing to the AQ library - anything that makes it harder for an attacker to simply find and patch a byte in your app to make the blacklist dysfunctional.
  • Use a network blocker such as Little Snitch. That would allow him to prevent your app from "calling home". To counter this, your app could, however, stop working after a certain time of attempted contacts to your server, by assuming that the user actively tries to keep your app from learning about new blacklists. If you go this path, you need to consider cases where your assumption could be wrong and upset your honest customers, though:
    • They might work behind a company firewall where they're never allowed this kind of outside access.
    • They might be on a trip where they have only rare access to the internet for days or weeks.
    • They might simply not like apps that call home and thereby suggest that the user is dishonest.

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.

3.2  Protection against influencing your license checking code

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.

Further ideas

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.

3.3  What to do when you detect an attack

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:

  1. If the cracker sees your app react to the detection of his intrusion, he'll look for that code, and disable it. Rather easily. Don't make it easy.
  2. A friend of mine once thought to be smart by immediately starting to delete all files on the attacker's computer. His reasoned that by doing this, he'd likely even destroy the tools the cracker used, and thereby stopping him dead in his process. Sadly, though, the only one trying to patch his app was someone who found a bug in the code and wanted to improve the app, then send the fix back to the author. Go figure. (And back then, this had severe consequences, as it happening in the early 90s, when many still worked on floppy disks, hardly anyone could afford a hard disk, let alone a backup drive.)

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 :)

4.  Conclusion

I hope this was helpful information to you. If you agree, don't hesitate to send me a few bucks or buy one of my apps. Thank you.

Of course, feedback on improving this guide is welcome, too. And let me know if you have questions or don't understand something.

Page last modified on 2013-05-02, 19:28 UTC (do)
Powered by PmWiki