Strong Naming Notes

I noticed earlier in the week that someone pointed out that you can use ILDASM to pull out the strong-name from a strong-named assembly, and recompile using a different key. I remembered an article I came across on MSDN that talked about this, so I thought I'd quote it here.

Most of the wizard-generated projects in Microsoft Visual Studio® .NET place this attribute in a file called AssemblyInfo, but you can put it in any source file you like. When the compiler sees this attribute, it copies the entire public key into the assembly's metadata, and uses the private key to form a digital signature. This is done by hashing the files in the assembly, incorporating those hash values into the manifest for the assembly, hashing the manifest, and, finally, encrypting this final hash value using the corresponding private key and tucking it away as yet another block of metadata in the assembly.

(Note that using this simplistic approach, the compiler needs to have not only the public key, but the private key as well, from the file c:\temp\mykeyfile. You'll see a safer way to approach code signing later in this article.)

If you look at an assembly's manifest using ILDASM, you'll be able to see its public key quite plainly, as shown in Figure 1.

Figure 1. A public key assigned to an assembly

What you won't see is the signature or the intermediate hash values. This sometimes confuses people who are learning about strong names. ILDASM doesn't show these things because ILDASM is a disassembler—it is supposed to produce IL that can be compiled into an assembly, and remember that the signature and hash values are output by the compiler (which would be ILASM in this case).

So there you have it. Just because you can use ILDASM doesn't mean you'll be able to access everything in an assembly. You can change the public key, but you won't be able to change the signature or the hash. By changing the public key, you'll render the signature invalid, and the assembly will fail to load.

Now, what they can do is delay-sign malicious code using your public key, and spoof any publicly available delay-signed assemblies... and very easily, I might add. This is why, you should NEVER let unsigned or delay-signed assemblies outside whatever environment you develop in. If you're a third party component developer, and you're releasing unsigned assemblies.... SHAME ON YOU.

I might note that the point (at least from Microsoft's perspective) was to not be able to substitute assemblies in a componentized environment. If you built the program with a strong named assembly system, the name, version number, culture code, and public key hash are stored in the manifest. If the runtime comes across an assembly with the same weak name but missing or incorrect strong name, it will fail to execute.

This prevents someone from building an assembly with the same names and methods but does something bad, like deleting everything out of the "Program Files" directory or something. Even if they could recompile an assembly without signatures, and had physical access to your server to replace it, it still would not work.

What really needs to happen is twofold.... there needs to be an Identity Authority to be able to have certificates specific to the publisher that can be authenticated, revoked, etc (kind of like Authenticode, but I don't belive that works for .NET), and you need to be able to encrypt assemblies using a PKI, and have the assemblies decrypted in protected memory (NGSCB anyone?) and then executed. That would stop people from disassembling production assemblies, and compormising security.

7 Comments

  • Robert, my application, LLBLGen Pro, is completely signed, all assemblies are signed. Still it took a cracker 15 seconds to remove all the hashes and make it completely unsigned so he could tamper the assemblies. He could also add new signatures if he liked, the assemblies were completely unsigned. Trust me on this, it can be done and rather easily. You have to use ILASM and ILDASM, not only ILDASM. I had to cripple the actual code (i.e. create specific compilation paths for the demo app) to make changing the signatures not important.



    If you like I can ask him how he did it precisely, but you can be sure it can be done and without a lot of effort. I didn't believe it either until he showed me the app with altered assemblies and no signatures. This is also how they can remove XHeo protection without a lot of efforts, they remove the signatures of all assemblies, change the references of the assemblies to not strong names, alter the code which accesses XHeo licensing and they're done. Just a FYI.



    (note: this is not to critize you, just a warning about what can happen as it bit me already)

  • Yeah, you would have to decompile and recompile the assemblies, because decompilation doesn't remove that information, just produces IL that could be recompiled. I don't think talking about how to do that here would be a particularly good idea though.



    I think that we ISVs need to be coming up with ways to combat this. My thought is that if your component is priced low enough, it won't be worth the effort. Whether it is true or not remains to be seen.



    I would like to hear more (offline, of course) about your "compilation paths" dealie. It may be of use to other ISVs.

  • Blogging about it is indeed not a good idea, don't give people ideas indeed :)



    Conditional compilations is very simple (don't have to do that offline, it's a general techique). First create another Build configuration in the build configuration manager (Build menu). Call this 'Release Demo'. Select this as the active.



    Then (C#) open properties of project. You're now editing 'Release Demo'. Open 'Build' under 'Configuration Properties' at the left. At the right you'll see 'Conditional Compilation Constants.'. In debug builds there are for example 'DEBUG' and 'TRACE'. Now add 'DEMO'.



    In your code, you can now mark sections which you do not want to be included in the DEMO build like:



    #ifndef DEMO

    // .. some code

    #endif



    this code is NOT included in the build result of Release Demo. This way, you can exclude stuff like 'Create a project' or your save routine's body. Even by removing the protection, the cracker doesn't win anything, because essential code isn't there.



    You can also use this for other purposes of course, for example testcode in DEBUG builds which you want to exclude from the Release build :)



    Combatting removal of protection is a lost battle, unfortunately. The only thing that helps (a bit) is making it uninteresting for the cracker, for example by low pricing and a lot of hurdles to take. Also, if crackers don't get any attention when they crack the protection, they'll spend more time on something that will get their attention.

  • oh darn... I'm coding C++ for to days now and I make a mistake immediately. :)



    it should be:

    #if !DEMO

    // some code

    #endif



    Sorry about that.

  • Sorry folks but:

    "Blogging about it is indeed not a good idea, don't give people ideas indeed :)"



    WRONG! if we can't / don't talk about the issues then many folks who need to know what the issues are will have to go and spend time finding the problems before they can even start to find a solution. by way of example:

    DVD copy and region codes were lame ideas and it took a brave soul to tell the world.

    the companie that was going to sue some guy be cause he told folks they could hold down the shift key to by pass an audio cd protection system on WIndows pc's.



    Expose the flaws so that EVERYONE knows about them then we don't have a false feeling of security and become victims of the flaw.



    even MS has said that security via obscurity is NOT security ...

  • As an ISV, it's probably not a great idea for me to post an entery in my blog that tells you how to circumvent my unrestricted free trials and steal my code. I know that you can use Reflector to get it out, I don't need to tell the whole world how to do it.

  • Robert: I was not saying you should.



    what I am and do say is that if there is a problem with how the .net framework functions we should all know about where the problem is and how it can be exploited. that is the first step in developing a sound "counter measure" or an alternate means of protection.



    for example: is the above still a problem if an Obfuscator tool is used? would that be of any help?



    and fwiw, IMHO any fool can copy someones code binary or source and distribute it.... thats not hard. but only you, me and others who work to master the art can create a valuable new work of art/craft in code.



    look at how Id Software publishes the soruce to their game engines, how many folks are using that code legaly when they could try and steal it?? not many, it has a very uniqe "fingerprint" of John and the others who wrote it.

Comments have been disabled for this content.