OJ’s rants What would OJ do?

9Jul/0825

.NET-fu: Signing an Unsigned Assembly (without Delay Signing)

This article is also available in: Italian


The code-base that I am currently working with consists of a large set of binaries that are all signed. The savvy .NET devs out there will know that any assembly that's used/referenced by a signed assembly must also be signed.

This is an issue when dealing with third-party libraries that are not signed. Sometimes you'll be lucky enough to be dealing with vendor that is happy to provide a set of signed assemblies, other times you won't. If your scenario fits the latter (as a recent one did for my colleagues and I), you need to sign the assemblies yourself. Here's how.

Note: delay signing is not covered in this article.

Scenario 1 - Foo and Bar

Foo is the component that you're building which has to be signed.
Bar is the third-party component that you're forced to use that isn't.

Relationship between Foo and Bar
Grab Bar.dll and project along with Foo.dll and project to see a source sample.

You'll notice Foo has a .snk which is used to sign Foo.dll. When you attempt to compile Foo you get the following error message:

Assembly generation failed -- Referenced assembly 'Bar' does not have a strong name

We need to sign Bar in order for Foo to compile.

Disassemble Bar

Step 1 - Disassemble Bar

We need to open a command prompt which has the .NET framework binaries in the PATH environment variable. The easiest way to do this is to open a Visual Studio command prompt (which is usually under the "Visual Studio Tools" subfolder of "Visual Studio 200X" in your programs menu). Change directory so that you're in the folder which contains Bar.dll.

Use ildasm.exe to disassemble the file using the /all and /out, like so:

C:\Foo\bin> ildasm /all /out=Bar.il Bar.dll

The result of the command is a new file, Bar.il, which contains a dissassembled listing of Bar.dll.

Rebuild and Sign Bar

Step 2 - Rebuild and Sign Bar

We can now use ilasm to reassemble Bar.il back into Bar.dll, but at the same time specify a strong-name key to use to sign the resulting assembly. We pass in the value Foo.snk to the /key switch on the command line, like so:

C:\Foo\bin> ilasm /dll /key=Foo.snk Bar.il

Microsoft (R) .NET Framework IL Assembler.  Version 2.0.50727.1434
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'Bar.il'  to DLL --> 'Bar.dll'
Source file is ANSI

Assembled method Bar.Bar::get_SecretMessage
Assembled method Bar.Bar::.ctor
Creating PE file

Emitting classes:
Class 1:        Bar.Bar

Emitting fields and methods:
Global
Class 1 Methods: 2;
Resolving local member refs: 1 -> 1 defs, 0 refs, 0 unresolved

Emitting events and properties:
Global
Class 1 Props: 1;
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Signing file with strong name
Operation completed successfully

Bar.dll is now signed! All we have to do is reopen Foo's project, remove the reference to Bar.dll, re-add the reference to the new signed assembly and rebuild. Sorted!

Scenario 2 - Foo, Bar and Baz

Foo is the component that you're building which has to be signed.
Bar is the third-party component that you're forced to use that isn't.
Baz is another third-party component that is required in order for you to use Bar.

Relationship between Foo, Bar and Baz

Grab Baz.dll and project, Bar.dll and project along with Foo.dll and project for a sample source.

When you attempt to build Foo you get the same error as you do in the previous scenario. Bear in mind that this time, both Bar.dll and Baz.dll need to be signed. So first of all, follow the steps in Scenario 1 for both Bar.dll and Baz.dll.

Done? OK. When you attempt to build Foo.dll after pointing the project at the new Bar.dll no compiler errors will be shown. Don't get too excited :)

When you attempt to use Foo.dll your world will come crashing down. The reason is because Bar.dll was originally built with a reference to an unsigned version of Baz.dll. Now that Baz.dll is signed we need to force Bar.dll to reference the signed version of Baz.dll.

Hack the Disassembled IL

Step 1 - Hack the Disassembled IL

Just like we did in the previous steps we need to disassemble the binary that we need to fix. This time, make sure you disassemble the new binary that you created in the previous step (this binary has been signed, and will contain the signature block for the strong name). Once Bar.il has been created using ildasm, open it up in a text editor.

Search for the reference to Baz -- this should be located a fair way down the file, somewhere near the top of the actual code listing, just after the comments. Here's what it looks like on my machine:

.assembly extern /*23000002*/ Baz
{
  .ver 1:0:0:0
}

This external assembly reference is missing the all-important public key token reference. Before we can add it, we need to know what the public key token is for Bar.dll. To determine this, we can use the sn.exe utility, like so:

C:\Foo\bin> sn -Tp Baz.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 3.5.21022.8
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key is
0024000004800000940000000602000000240000525341310004000001000100a59cd85e10658d
9229d54de16c69d0b53b31f60bb4404b86eb3b8804203aca9d65412a249dfb8e7b9869d09ce80b
0d9bdccd4943c0004c4e76b95fdcdbc6043765f51a1ee331fdd55ad25400d496808b792723fc76
dee74d3db67403572cddd530cadfa7fbdd974cef7700be93c00c81121d978a3398b07a9dc1077f
b331ca9c

Public key token is 2ed7bbec811020ec

Now we return to Bar.il and modify the assembly reference so that the public key token is specified. This is what it should look like after modification:

.assembly extern /*23000002*/ Baz
{
  .publickeytoken = (2E D7 BB EC 81 10 20 EC )
  .ver 1:0:0:0
}

Save your changes.

Reassemble Bar

Step 2 - Reassemble Bar

This step is just a repeat of previous steps. We are again using ilasm to reassemble Bar.dll, but this time from the new "hacked" Bar.il file. We must use the exact same command line as we did previously, and we still need to specify the Foo.snk for signing the assembly. To save you having to scroll up, here it is again:

C:\Foo\bin> ilasm /dll /key=Foo.snk Bar.il

Microsoft (R) .NET Framework IL Assembler.  Version 2.0.50727.1434
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'Bar.il'  to DLL --> 'Bar.dll'
Source file is ANSI

Assembled method Bar.Bar::get_SecretMessage
Assembled method Bar.Bar::.ctor
Creating PE file

Emitting classes:
Class 1:        Bar.Bar

Emitting fields and methods:
Global
Class 1 Fields: 1;      Methods: 2;
Resolving local member refs: 3 -> 3 defs, 0 refs, 0 unresolved

Emitting events and properties:
Global
Class 1 Props: 1;
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Signing file with strong name
Operation completed successfully

Open up Foo's project, remove and re-add the reference to Bar.dll, making sure you point to the new version that you just created. Foo.dll will not only build, but this time it will run!

Disclaimer

"Hacking" third-party binaries in this manner may breach the license agreement of those binaries. Please make sure that you are not breaking the license agreement before adopting this technique.

I hope this helps!

  • OJ
    Glad to hear it mate!

    I've been around, just very busy :)
  • Moffmo
    This has helped me out massively again today! Thanks again.

    P.S. Where have you been?
  • OJ
    When you disassemble you new dll, does it look like it has been signed properly?
  • OJ
    Are you getting an error message when you run the command or is it unable to locate ilasm.exe? Which version of .NET are you using? Which versions do you have installed?
  • Kesav7902
    unable to execute the below step 2 :

    C:\Foo\bin> ilasm /dll /key=Foo.snk Bar.il


    any help would add a great value in saving my time !!!


    Thanks,
    keshav.
  • Nice! I've used this to do the exact opposite so I could mix DLLs compiled locally and those compiled on the build server, which are versioned and signed.

    Basically I stripped the public keys and the public key tokens and changed the version numbers to match (local builds all ended up 2.0 instead of 3.7 which is the team build version)....

    I was able to do my debugging tests without checking-in code and doing a server build, or changing all references to project references... perfect! :)

    Now I need a tool that does this automagically... "UnsignReversion"
  • Andrew
    This worked for me, allowing me to sign a 3rd party DLL. However, I can't 'Import' my new, signed MyDLL.dll. Heres what i did:
    1. Dissambled Old.dll
    2. Rebuild and signed as New.dll
    3. Removed all references in my VS2005 proj to Old.dll and added references to New.dll
    4. Replaced 'Imports Old.dll' with 'Imports New.dll'

    this final step fails. VS2005 won't recognise my New.dll

    Any clues?
  • OJ
    You're most welcome Andry. Thanks for the feedback.
  • Andry
    This was a great help for me. Thanks!
  • Thomas James
    Good one. Thanks
  • OJ
    @Matt: I'm glad to hear you managed to find your way back here :-) Also chuffed to hear that it helped solve your problem!

    Cheers bud :)
  • Matt (Moffmo)
    No Way!

    This was linked from another website on my google trip looking for a how to, and this worked perfectly! Also a lot easier than I thought it would be.

    Thanks for your help OJ! Legend.
  • OJ
    @Luis: Glad it helped! I wrote the article so that people can understand how easy it is to do when you only have a .NET framework install handy. There are tools out there that you can use to do this for you as well. Sometimes those tools aren't accessible though, so knowing how it's done manually is a good thing :) Cheers!
  • Luis
    A much more cool solution than the one that I was using, executing the methods of my unsigned DLL using Reflection!
    Thanks!!
  • OJ
    For those that are interested, Cosimo has translated this article into Italian! Thanks very much Cosimo :)
  • OJ
    @Reza: That's great to hear :) I had a feeling that would be the case, and I'm happy to hear that your problem is solved.
    <div>
    </div>
    <div>Cheers!</div>
  • Reza
    thank you for you reply,
    in this period, I tested and it worked just fine :D
    ILDASM, beside IL code of assembly, extracts all resources which are required to rebuild the assembly. There for you simply can rebuild it using ILASM.
    one point about hacking references, I do it using CECIL from MONO.
    You can access my utility from here:
    http://sites.google.com/site/asyncresult/Home/h...
  • OJ
    @Reza: I apologise for the slow reply. I've been pretty busy lately!

    The short answer is: I don't know :) Perhaps you could give it a try? If I get time over the weekend I'll do a test or two and post my results. If you happen to try it before me, please let us know how it goes.

    My gut feeling is that it should work as expected.

    Good luck!
  • Reza
    just one thing, what will happen if my unsigned assembly contains some compiled resources? will they be lost?
  • OJ
    @Samantha: I'm happy to hear that this has helped you solve your problem. Cheers :)
  • Samantha
    Can I just say thanks? This is exactly what I needed to do and this solution is perfect! :)
  • OJ
    Ah yes, I've corrected it. It now links to VIM. :)
  • slight typo there:
    when you've said 'text editor' you've linked to vim.
  • OJ
    Thanks mate. I was worried about the possible confusion between Bar and Baz, but I thought that most readers would be able to handle it.
  • nicely explained OJ. Well done.
blog comments powered by Disqus