Despite my ramblings to the contrary, I have decided to make some of my applications “commercial” or at least paid-for.
Not entirely, though; Instead, I’ve, at least for the moment, decided on a “almost everything works except for a few things” method. In this case, for BASeBlock, the Editor will work fine, but will be unable to save to a file.
There are some important considerations I took while developing my current solution:
- I am not trying to create an “industrial strength” type of lock-out anti-piracy thing like, say, SecuROM or something. This is just a basic deterrent I want to be able to pop onto any application I deem “worthy”.
- If there is any doubt- it assumes the user is registered
The second point is something of an opposite stance compared to what you usually find with “DRM” or similar measures. I figure that a user who paid accidentally being marked as unregistered is going to be a lot worse than a unregistered user being able to appear as a registered one to the program, so in a few cases where there is a bit of confusion, the program just proceeds as licensed. The “Protection” I use is more or less obfuscation, and rather weak obfuscation at that. Anybody with a decompiler will be able to figure out how I generate the keys, but if they are willing to do that to use my software than I feel I ‘won’ anyways.
Product Keys
By far the simplest, or, rather, easiest to implement, method of software protection is to make it so that you hash a few unique pieces of data for a user into a “Product Key” using a “secret” method. In my case, I use three pieces of string data; the User name, Organization, and the Product Name in question. The idea is to take these three strings and create a “Product Key” string out of them. My relatively simple (and probably easy to reverse engineer) method, when given values for User Name, Organization, and Product Name, spits out something like P6WI-CKEC-RJ5F as a product key.
The basic idea is that the application, naturally, is initially “unregistered”. When it is unregistered, it will normally prompt for registration information at start-up. In BASeBlock, the code being used for license-checking is fairly short:
1 2 3 4 5 6 7 |
BCUpdateLib.LicensedFeatureData.NetCheck("BASeBlock"); isLicensed=BCUpdateLib.LicensedFeatureData.IsLicensed("BASeBlock"); if (!isLicensed) { BCUpdateLib.LicensedFeatureData.DoRegister("BASeBlock", null); isLicensed = BCUpdateLib.LicensedFeatureData.IsLicensed("BASeBlock"); } |
The call to NetCheck is current dev code that I’m trying out to make it consult my web site to find out if the key in question is locked out or something. (if it discovers that it is, the data in the registry will be deleted). Afterwards, it checks if “BASeBlock” is registered. Each Licensed BASeBlock Application will store it’s registration information in the registry; or rather, the update library will store it. If it isn’t registered, it calls the Update Library and asks it to show the “Register” dialog. This dialog accepts the username, organization, and Product Key- the last one of which ideally I will be providing to each user as purchases are made, or given away when I finally give up on the entire idea. It uses the same code to generate the key from the username, organization and application name, and compares it to what it is given in the dialog. If so, it stores that data in the registry (which will be inspected in future sessions) and returns. Note that immediately after, BASeBlock again calls the “IsLicensed()” routine- this is because if the registration was in fact valid, it will now be true whereas before it was false.
Currently, the implementation of the “lock-out” is trivial- if it’s not licensed, it throws an exception or shows a dialog. However, this is, as I said, trivial to overcome. Something I’ve been considering is to make it so that the unregistered version literally doesn’t have the save function at all- that is, the code simply isn’t there. The best way I can think of to do that would be to actually have that as part of a separate assembly that is loaded at run-time. Ideally, the code to do this would be something like this:
1 2 3 4 5 |
[BCUpdateLib.ProtectedFunction("BBLocked","DoSave")] private void DoSave() { //Code for saving Levels here } |
But needless to say things aren’t that simple. For one thing I don’t think you can simply have an attribute magically create a new assembly. For another, even if I assume it is possible (and really, with enough effort and research almost anything is) the code will no doubt be quite complex, and I prefer to keep things simpler if I can.
In any case, the core concept is that the protected functions will be in separate assemblies, and those assemblies will be encrypted using the product key. This will occur at my end- I will be sending these files to users along with their product key. (which means I should probably try to keep the functionality contained therein as short as possible) The Registration dialog will have Browse… dialog, or allow drag-drop, etc to reference the “key” file, which will be stored in a central location (perhaps %appdata%\BASeCamp\Licenses or something).
When the application is loading, it will call a static “LoadProtectedAssemblies()” Function, or something similar, within the Update Library. This will enumerate the “key” files, which will be decrypted using a Product Key generated from the username, organization, and product name for the product in question. If the result is a valid assembly, load it, otherwise we clearly can’t. Later in the code, the Editor goes to save and calls a static routine in the Update Library, which will attempt to find the appropriate assembly and if none is found throw something like a “unlicensedCodeException” or something similar. This could have an interesting tie-in with the current code that enumerates various assemblies; any decrypted assemblies could be tossed into the array for inspection. This would allow me to stick various other classes that implement certain interfaces or derive from specific classes, like Block, or iLevelSetBuilder, into a assembly, and encrypt them in this fashion, and they will only work for “licensed” users of the program. At the moment though, even though the licensing code has worked well for me so far I’d rather keep it from getting it’s paws on everything.
Have something to say about this post? Comment!