Recommendations for Copy Protection?

Posts   
1  /  2
 
    
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 10:18:35   

Hi what do you guys recommend for copy protection?

The copy protection needs to be implemented into a Desktop App and also into a web app.

Prefered VB.Net but C# also welcome.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 10:51:52   
  • make all methods and classes in your .exe internal or private.
  • obfuscate your .exe. Because of the internal/private methods, almost everything gets obfuscated simple_smile
  • sign your assemblies
  • your .exe's startup classes have to be sealed. Making the classes internal already solves this too. If you don't do that, someone can rename the .exe to .dll, reference it in his own project and create an instance of your startup classes, and attack it with invoke calls to access private members/methods. The mistake I made in the beginning was that I didn't make my main form a sealed class nor an internal one so someone could create a subclass from that form class, or access it from the outside, p/invoke into the private member variables and overwrite the license file object, circumventing the copy protection. This was the reason for us to release a crippled demo so people couldn't use it when they cracked it as some code was simply not compiled into the .exe/assemblies.
  • add a lot of code to check if the signature is still there. You can remove a signature from a signed assembly in 20 seconds using ILASM and ILDASM
  • use named license files. People who payed for a license which have their names in the license are not that willing to give away their license because everyone can see who gave away the license. Use signed XML files for this, as .NET has build in support for reading these and checking the signature

However, in the end, if people really want to, you can't protect your code. I spend a lot of time to make LLBLGen Pro's demo (which is now full featured) as much protected as possible, however I also know that it can be cracked. Though if you make it as tough as possible, the average potential custom who tries to crack the protection to see if he can get away with it will not succeed, only people who crack a lot of software will. That last group will not bother spending a lot of time cracking software which is not wanted by millions, like Halo2 or Halflife2.

Now, before you ask me, no I won't tell you how I protected LLBLGen Pro simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 11:05:39   

Thanks frans, you gave me alot of information and told me about alot of holes that i never ever considered. Never even thought about making my exe methods internal...

What obfuscator do you recommend?

Now, before you ask me, no I won't tell you how I protected LLBLGen Pro

He, he, he, But why? stuck_out_tongue_winking_eye

So I assume you don't use a third party library? I was hoping to find some recommendations on good thridparty copyprotection librarys - i can't spent to much time on this - so no time for custom protection library.

wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 12:35:29   

Hey Frans are you sure you obfuscated your last release?

If i use a reflector tool i can see all the method names public and internal, and all classes - the names have not been mangled up at all - clever trick to keep all public method of an exe as internal - I really liked that one.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 12:45:40   

wayne wrote:

Thanks frans, you gave me alot of information and told me about alot of holes that i never ever considered. Never even thought about making my exe methods internal...

What obfuscator do you recommend?

I used the community edition of dotfuscator which comes with vs.net. As all methods and classes are internal, the obfuscation is very very good simple_smile

Now, before you ask me, no I won't tell you how I protected LLBLGen Pro

He, he, he, But why? stuck_out_tongue_winking_eye So I assume you don't use a third party library? I was hoping to find some recommendations on good thridparty copyprotection librarys - i can't spent to much time on this - so no time for custom protection library.

No, these libs are very lame. It took me 2 minutes to crack the protection of Xheo's demo protection logic, just with reflector and it could have been even less than 2 minutes. I tried to crack it because I looked into it to protect our demo application. Before I decided to go with their solution I thought it would be good to see if I can circumvent the trial time period. That was very easy actually. Also, by setting back the system time worked! The oldest trick in the book. smile

There aren't many solutions for .NET though which is logical: a standard, turn key solution has a big downside: a lot of applications use it, so as soon as the inner workings are known, you can crack any application with that protection.

What disturbed me the most was that Xheo apparently didn't look into the flaw every trial protection has: where to store the starttime of the trial period? They went for the most easiest solution, which can be found with every registry key monitor and file monitor within seconds. So as soon as you have that, you can restart a trial period without problems, the one thing the protection has to prevent.

Also, a fancy protection is useless if you can simply comment out the call to the protection. So if the protection works like:

if(!CheckLicense()) { // invalid, exit HandleInvalidLicense(); } // proceed, valid license.

and CheckLicense() uses a tremendous clever protection scheme, all I have to do is:

if(false) { // invalid, exit HandleInvalidLicense(); } // proceed, valid license.

or return true from CheckLicense always and I'm set. You can easily do that by first using ILDASM to export to a file, then alter the IL, and then use ILASM to get back the assembly. Of course, it's not signed then, so you have to remove the signed signatures of all referenced assemblies as well, as a signed assembly can't reference a non-signed assembly simple_smile

wayne wrote:

Hey Frans are you sure you obfuscated your last release?

If i use a reflector tool i can see all the method names public and internal, and all classes - the names have not been mangled up at all - clever trick to keep all public method of an exe as internal - I really liked that one.

The official version is not obfuscated, the demo is. The official version requires a license to run, you can hack that out of the code of course, but you can only retrieve an official one if you buy it first. To hack out that check, you have to change the signature of the assembly, because they're signed and resigning them with my key is not possible. This will make it easy to track if the assembly is modified: check the signature of the assembly simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 13:01:37   

netclectic wrote:

You might be interested in these new articles on codeproject:

Building Security Awareness in .NET Assemblies : Part 1 - Learn to break a .NET Assembly

Good one, it demonstrates what I explained earlier simple_smile

Part 2 - Learn to protect your .NET assemblies from being tampered

This is misleading. With the same IL, you can remove the signature, or even add your own. Also with sn - Vr assemblyname, you can skip signature checking. It is therefore a good method to check if the assembly is tampered with, but you have to add that check code yourself, you can't rely on .NET's. For example, if I put my license check code in assembly A, a dll, and I sign it. I reference it from my exe E. Now, I tamper with the IL from A, and it doesn't check the license anymore. I can't sign it with the same signature, although I can try sn -Vr. What I can do is to disassembly E as well, and remove the strong name signature of the reference at the top. Re-assemble the IL into E' and my E' can now reference the non-signed A', something which is probably explained in part 3, which won't load now as codeproject is back to its normal speed: 1 byte per second... simple_smile

(edit) Ah it's up again. Indeed, it explains the method to remove the signatures simple_smile .

Also, what's another big mistake, is that you shouldn't display a dialog with "You hacked my app!" or something, when you detect a tampering of the assembly. Simply set something to null so it will crash, later on simple_smile This way there is no relation between the check code and the actual results with the application and the crack is useless.

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 13:05:31   

This will make it easy to track if the assembly is modified: check the signature of the assembly

Call me stupid, but where do i go to check the signature of a file?

add a lot of code to check if the signature is still there. You can remove a signature from a signed assembly in 20 seconds using ILASM and ILDASM

How do i add a signature?

These are things that i have never worked with.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 13:09:08   

wayne wrote:

This will make it easy to track if the assembly is modified: check the signature of the assembly

Call me stupid, but where do i go to check the signature of a file?

A signature of an assembly simple_smile When you sign an assembly, it gets a unique public token. You can verify that in code. Just browse the Assembly class documentation simple_smile

add a lot of code to check if the signature is still there. You can remove a signature from a signed assembly in 20 seconds using ILASM and ILDASM

How do i add a signature? These are things that i have never worked with.

Check the links netclectic pasted. They show you a lot of information about the subject simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 13:18:42   

Thanks, amazing how easy it is to get passed some of the checks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 13:43:30   

wayne wrote:

Thanks, amazing how easy it is to get passed some of the checks.

Yeah tell me about it. I almost got a heartattack when someone mailed me 2 weeks or so after we released llblgen pro back in september 2003, and explained to me how he had circumvented the protection with just 3 minutes of work. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 14:24:02   

Yeah tell me about it. I almost got a heartattack when someone mailed me 2 weeks or so after we released llblgen pro back in september 2003,

I know the feeling, 3 weeks ago some dude got access to my web application files & databases on my webserver through a hole that i never knew about! And he copied all my files - luckily there was no source on the server but the dll's where not obfuscated!!! I was very upset ! and wanted to do something to him - especially after he told me - but all i can do is hope that he does not bother to decompile my source and sell it off. - It was my own fault afterall - but how the hell are you suppose to keep up with all the possible entries.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 14:50:57   

wayne wrote:

Yeah tell me about it. I almost got a heartattack when someone mailed me 2 weeks or so after we released llblgen pro back in september 2003,

I know the feeling, 3 weeks ago some dude got access to my web application files & databases on my webserver through a hole that i never knew about! And he copied all my files - luckily there was no source on the server but the dll's where not obfuscated!!! I was very upset ! and wanted to do something to him - especially after he told me - but all i can do is hope that he does not bother to decompile my source and sell it off. - It was my own fault afterall - but how the hell are you suppose to keep up with all the possible entries.

whoa frowning . Didn't you patch the webserver? It's hard to keep up with holes in software and even then, sometimes it's just a combination of features really which do the trick. The upside of your situation is that the people who try to get access to other people's systems are either payed to do that (and then you're in trouble) but that's often not the case, or are only interested in getting access.

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 14:59:21   

Didn't you patch the webserver?

Yes i did, afterwards wink But the hole was due to a configuration mistake on my part. I left my default websites browsing permissions ON. frowning And all my client domains are in subfolder underneath my default domain. So this guy got access to all my clients files and my application files. frowning - Terrible feeling.

wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 02-Nov-2004 15:18:17   

So how do i check that my assembly is signed programmatically? I was looking in the help - me not find anything.

So i guess the best way to go is:

  1. Secure your assemly with a strong name.
  2. Do the normal saving of key information in a file and in registry and add all the nessecary checks.
  3. Check whether the assembly is signed by using your own checks in the assembly.
    • Do i call the same function in several places? or Do i duplicate the logic in several places? I presume the later. simple_smile
  4. Mark all public methods of an exe assembly as Internal.
  5. Make the main form / class sealed
  6. Obfuscate the assembly
lyndon_h
User
Posts: 79
Joined: 14-Sep-2004
# Posted on: 02-Nov-2004 16:28:35   

This is why i really enjoy this forum. There is always a good information.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 02-Nov-2004 21:02:47   

wayne wrote:

So how do i check that my assembly is signed programmatically? I was looking in the help - me not find anything.

this.GetType().Assembly.GetName().GetPublicKeyToken(); where this is a class in an assembly you want to test the token for. simple_smile

[quote] So i guess the best way to go is: 1. Secure your assemly with a strong name. 2. Do the normal saving of key information in a file and in registry and add all the nessecary checks. 3. Check whether the assembly is signed by using your own checks in the assembly. - Do i call the same function in several places? or Do i duplicate the logic in several places? I presume the later. simple_smile

No routine call of course, you copy the code every time, and you use different code each time. This is about hiding the checks, so the checks should be very compact and look interesting but not 'I do checks here, look!'-kind of code. So therefore you also don't call to a single method as by disabling that method will kill all your checks, plus you don't show message boxes nor do you call to methods which show 'you hacked!' kind of messages as you can trace back the call chain pretty easily then.

  1. Mark all public methods of an exe assembly as Internal.

And all classes too. Then you get all types also obfuscated WITH methods. Very hard to get it figured out, even with a very lame obfuscator simple_smile

  1. Make the main form / class sealed
  2. Obfuscate the assembly

5 is not really necessary if you make the class internal.

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 03-Nov-2004 09:39:30   

Hi Otis

I get the following message "Assembly generation failed -- Referenced assembly 'DAL.magnet.circdata.net' does not have a strong name"

Does this mean that all referenced assemblies must have strong names? What about the 3rd party assemblies.

The code "this.GetType().Assembly.GetName().GetPublicKeyToken();" returns me a part of the signuature - Do i just check that there is a signature or do i check that the signature matches? If it needs to match then i am going to have to save in the exe as const?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 03-Nov-2004 10:00:36   

wayne wrote:

I get the following message "Assembly generation failed -- Referenced assembly 'DAL.magnet.circdata.net' does not have a strong name"

Does this mean that all referenced assemblies must have strong names? What about the 3rd party assemblies.

Yes, a signed assembly can only reference signed assemblies. Normally 3rd party assemblies are signed, just for this purpose. You run into trouble though when you have embedded the COM object for IE, which is not signed and when that's the case, you can't sign your own assembly...

The code "this.GetType().Assembly.GetName().GetPublicKeyToken();" returns me a part of the signuature - Do i just check that there is a signature or do i check that the signature matches? If it needs to match then i am going to have to save in the exe as const?

You get back the public token of the key used to sign the assembly, a unique number. So when the assembly is not signed or signed with another key, that number is either null or different. You have to check the signature's bytes with the value it gets when you sign it with your own key. As hiding the code is key for success, you should do everything you can to make that routine look complex and hard to follow and not important at the same time. Comparing it to a const is not wise.

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 11-Jan-2005 19:42:46   

I used the community edition of dotfuscator which comes with vs.net. As all methods and classes are internal, the obfuscation is very very good Regular Smiley

I am trying to dotfuscate (using community edition) some of my libraries to give to a "friend" - but the dotfuscater does not dotfuscate the code - it only renames All the methods and properties - am i missing some setting somewhere?

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 11-Jan-2005 19:59:47   

What code? The msil code or the code you are compiling into the runtime library?

wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 11-Jan-2005 20:03:06   

If i use Lutz Roeders utility to view my dotfuscated Dll then i can see the actual C# code inside my protected and public methods.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 11-Jan-2005 20:04:46   

wayne wrote:

I used the community edition of dotfuscator which comes with vs.net. As all methods and classes are internal, the obfuscation is very very good Regular Smiley

I am trying to dotfuscate (using community edition) some of my libraries to give to a "friend" - but the dotfuscater does not dotfuscate the code - it only renames All the methods and properties - am i missing some setting somewhere?

Did you make the classes internal and the methods as well? (internal classes only in .exe's) If you make the classes internal, the obfuscation is best.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 11-Jan-2005 20:12:09   

wayne wrote:

If i use Lutz Roeders utility to view my dotfuscated Dll then i can see the actual C# code inside my protected and public methods.

that will always be possible. Obfuscation mangles names, and it makes methods seem to look like overloaded methods while they aren't (all void methods can be made the same name for example simple_smile ).

Frans Bouma | Lead developer LLBLGen Pro
wayne avatar
wayne
User
Posts: 611
Joined: 07-Apr-2004
# Posted on: 11-Jan-2005 20:31:08   

I am dotfuscating libraries. Here is an example of my code copied out of Lutz'es program:

This code should be obfuscated? confused


protected void OpenDBConnection()
{
      try
      {
            if (this.DBConnection.State != ConnectionState.Open)
            {
                  if (StringType.StrCmp(this.DBConnection.ConnectionString, "", false) == 0)
                  {
                        throw new ConnectionException("No connection string supplied.");
                  }
                  this.DBConnection.Open();
                  if (this.b != null)
                  {
                        this.b();
                  }
            }
      }
      catch (Exception exception2)
      {
            ProjectData.SetProjectError(exception2);
            Exception exception1 = exception2;
            throw new ConnectionException("Connection Failure - See Inner Exception", exception1);
            ProjectData.ClearProjectError();
            return;
      }
}


1  /  2