A largely "untouched" area of intellectual property protection is Tamper Proofing. I can only assume that the reason for this is that while tamper proofing methods are easy to add, it is extremely difficult to stop tamper proofing being removed. It all boils down to the simple fact: if your computer can run the program then a hacker can understand the control flow and change your code for their own purposes.
This article takes a look at what tamper proofing is, various methods of tamper proofing, and finally evaluates their real world use.
What is Tamper Proofing?
Imagine this scenario: you've put a number of hours writing an amazing program. To help the sales process you've created a fully functional demo that users can install on their PC's - the only catch is that it expires after 30 days of use. Wouldn't it be great if you could create a system that detected if the demo program's code was tampered with? i.e. If it was tampered with then just crash the program? Well, that's the idea of tamper proofing however it is a little harder than it seems.
Wouldn't it be nice if you could just have some code like so:
if (IsTamperedWith) throw new InvalidProgramException();
As you've probably already determined through this series of articles: if there's a will to get into your code; there is always a way - it just may take a bit of effort! The above code is easily circumvented by reversing the test of the branch statement (i.e. brtrue.s instead of brfalse.s). So what methods are there and what use are they?
Detecting tampering
Detecting tampering can be split into three base methods:
- Hash detection: Detect any changes to the program via a hash algorithm
- Result checking: Examine arbitrary results generated by your program
- Encryption: Encrypt the program so that they need to decrypt it before being able to modify it
Now there are obvious issues with all of the above methods:
- Hash detection: Find the hash inside your program and change it to the new hash of your modified program, or relax the check altogether.
- Result checking: Either change the results to check, remove the checking code, or relax the checking code.
- Encryption: Find the decryption algorithm and decrypt the assembly permanently to change it.
Now, all of these methods I've assumed a purely software implementation. For better protection you could utilise a hardware "drongal" which contains the decryption routine for example so that it is more difficult to bypass - again it is not fool proof however.
So... what to do?
Well, a number of commercial obfuscators tend to utilise a mixture of Hash detection and Encryption, leaving Result checking to that of the licensing code. While it isn't fool proof; it does create a lot more work for a potential hacker. Consider the following scenario:
- A program is started by an obfuscated bootstrapper.
- The bootstrapper decrypts the program using a public key.
- The stored assembly checks the stored hash meets the decrypted assemblies hash.
- Whilst running, the program checks the result of arbitrary calculations the program generates (watermarks?) to ensure that code is in its "original" form.
Now to "hack" this scenario does take a fair bit of work to reverse engineer:
- Decompile the bootstrapper - extract the public key.
- Decrypt the assembly with the public key
- Change the program as desired
- Find the stored hash and where it is checked: relax the check for the hash, or update the hash.
- Identify program checking routines and remove/adjust them as you find them. Most likely, a similar pattern or common method will be used for this so try automated fixing via a pattern search and replace.
- Either encrypt with your own key pair and replace the used key, or change the bootstrapper to avoid the decryption step.
As you can see, it isn't impossible to reverse engineer it, but the work gone into doing so has just increased substantially. The question for the hacker comes down to really whether it is worth his/her time for the potential benefit. If the answer is no, then the technique is working for the better.
The grim truth
I am yet to come across a fool proof way of protecting your code. The reality is that you are really just putting obstacles in the way in the hope that any hacker will just "give up" along the way. I've said it before; but you're best solution is to perform a threat analysis of your target audience first. After you've done that you can then look at how much a breach would cost you, the likelihood of a breach, and how much time and effort should be spent in avoiding this ever happening.
Conclusion
Tamper proofing is another tool in your toolbox for protecting your precious code. The methods of tamper proofing all stem from three base methods:
- Hash checking - making sure your program is not tampered by using a one-way hash
- Result checking - checking arbitrary program results within your code for tampering
- Encryption - encrypting your bytecode to avoid direct tampering
Unfortunately, much like the other tools in your toolbox, its work can be reversed with enough time and effort; however if the time and effort it takes to reverse is greater than that the hacker is willing to put in then you've won the game.
Coming soon
As with all "theory" articles; I will implement some of these techniques into NCloak bit by bit. So in coming weeks we'll likely see a (slow) implementation of each of these features (perhaps with a few "side swipes" here and there). If there is anything you would like to see, then let me know!
Credit where it's due!
This article references information from a paper by Christian S. Collberg and Clark Thomborson: "Watermarking, Tamper-Proofing, and Obfuscation", IEEE Transactions on Software Engineering 28:8, 735-746, August 2002. An old article, but still very much relevant! In fact, if you're interested; Clark has put out a number of very interesting articles on the area of IP protection. Check out his publication list here.
Hi Paul, great article!
I have a question, in future articles will be possible see about some anti-debugging techniques and other little tricks?
Nice!
I 'm looking forward to implementation article!
That's a great idea regarding the anti-debugging techniques. We'll definitely take a look at those and their use in a type-code safe environment :)
I'll do some thinking to see if there are any other little tricks we can throw in there also.
Thanks for your comments!
I just found your blog. I am working on a C# .net project right now that I intend to deploy in the future. I'm not even remotely close to finishing, but it wasn't until today that I found out C# code is so easy to decompile! What a bite!
Anyway, I've been reading your blog and checked out your project on Google Code...nice. I can't wait to see this project unfold, and hey, maybe by the time I'm finished with my project, your project will be finished and I can use it to obfuscate my source!
Thanks for that me!
If you do find anything you'd like the obfuscator to do, or something that doesn't work properly then feel free to let me know.
This was a good read. I've tinkered around with hash checking a bit and released a sample application.
http://johnleitch.blogspot.com/2010/03/net-assembly-tamperproofing.html