.NET Software Development Tutorials and Videos: C#, ASP, SQL Server, Linq, Visual Basic, Silverlight, Azure
Dot NET Assemblies and Strong Name Signature

Dot NET Assemblies and Strong Name Signature

This article discusses .NET assemblies and strong name signature. In addition to providing some benefits like versioning and giving name uniqueness to an assembly, strong name provides a strong integrity check, and here is the point of this article! By strongly naming an assembly, you are supposed to ensure that your binary has not been tampered with since it was compiled or built.

Author:   Soufiane Tahiri, InfoSec Institute

General Overview

Before going any deeper on this subject, we need to clarify a huge ambiguity regarding signing for versioning and signing for protecting.

A strong name signature is after all a set of information regarding an assembly. It may contain version number or culture information, but it will definitely contain a public key and a signature.

And regardless of the mathematical details of the public/private key pair, the strong name is intended to guarantee evidence about the origin of a given assembly, so that by loading an assembly, you are sure it’s the one you want to load and not another that looks just like it.

You have to distinguish between digitally signing and strong name signing. The common aim is to determinate if the source of our assembly is trusted or not, but when we talk about digitally signing, we are talking about a digital certificate which can come from different certificate authorities and Public Key Infrastructure source. If an assembly must be strong named (or strongly named), this step should be done before digitally signing it.

In addition to providing some benefits like versioning and giving name uniqueness to an assembly, strong name provides a strong integrity check, and here is the point of this article! By strongly naming an assembly, you are supposed to ensure that your binary has not been tampered with since it was compiled or built.

What interests us, as reverse engineers concerned by software protections, is the similarity between the behaviors of a traditional WIN32 application protected with a Cyclic Redundancy Checksum and a strongly named “dot NET” Assembly. As was said earlier, this will prevent the assembly from being changed, or in other words from being patched. Most software vendors rely on this, which is really not safe at all since strong name is not for security and must not be considered as a protection. We will see why in detail…

Signing an Assembly with a Strong Name

Understanding the two cryptographic concepts of hashing and digital signing is important to understand how strong name works.

Hashing is essentially used to verify data integrity so that we can determine if the message (data) was altered or not. Hashing is an algorithm capable of producing another data with smaller and/or fixed length. In our context, data refers to an assembly, so our assembly is used as an input for an algorithm hash, such as SHA-1 (as it happens on strong naming) or MD5 to produce a “theoretically?” unique output. For a deeper look into hashing and digital signing, check out the reverse engineering training course offered by the InfoSec Institute.

DotNetAssemblies-1

Hashing is irreversible and the hash value produced cannot be decrypted, so if two assemblies produce the same hash value, we can deduce that they are the same. If the value of a previously calculated hash changed, this means that the assembly itself has been changed.

By previously knowing the calculated hash of an assembly, we can determine if this last is tampered or not. And to protect this hash value itself from being tampered too, digital signing is used.

Beyond mathematical details and complexities, the concept of digital signing is quite simple and clear. Every digital signature depends on a public and a private key which are obviously related. If an assembly (data) is encrypted using the public key, it can be decrypted only using the private key.

Basically, by strong naming an assembly, a hash value is calculated, and then it’s encrypted using the private key previously generated by the vendor. Then it’s placed along the public key in the signed assembly itself so the Common Language Runtime (CLR) can validate the assembly at runtime by comparing the decrypted hash stored in the signed assembly, using the private key stored among other information, and a new calculated hash. If the two hashes match, the assembly is loaded; otherwise, it crashes. The figure below describes this process:

Dot NET Assemblies and Strong Name Signature

This is the basics of how strong name works, so every strong name essentially contains the name of an assembly, its version number and its culture information. If provided, the whole provides a unique hash / identity for every signed assembly.

Before strong name signing an assembly, we need to generate a public/private key pair, and obviously the dot NET Framework SDK provides tools for assigning a cryptographic signature to any built assembly. This includes the Strong Name tool “sn.exe” which will help us in building the key pair we need.

The first step in strong name signing is generating a key pair. The “sn.exe” tool is located in “C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\” and is a command line based tool, so start Microsoft CMD and use this command to generate a new key pair:

Sn –k MyKeyPair.snk

Dot NET Assemblies and Strong Name Signature

This way, we obtain a random key pair (MyKeyPair.snk) which contains both private and public key. We can of course extract the public key from the key pair obtained and place it in a separate file, but we will not enter in details on this since our main goal is to demonstrate that strong name must not be considered as a protection because of how weak it is!

Well, now by using MyKeyPair.snk, we will sign an assembly. There are several ways to do it: either by adding the correct custom attribute to your AssemblyInfo.vb or AssemblyInfo.cs source:

C#
[assembly: AssemblyKeyFile(@”..\..\MyKeyPair.snk”)]

VB.NET
<Assembly: AssemblyKeyFile(“..\..\MyKeyPair.snk”)>

Or directly through Microsoft Visual Studio IDE via project’s properties – click on signing tab, check “Sign the assembly” then browse to the .snk file:

Dot NET Assemblies and Strong Name Signature

By building your project, you get a strong name signed assembly.

Determining if an Assembly is Strongly Named or Not

There are several approaches applicable to determine if an assembly has a strong name or not. I’ll show you some of them. The first one uses the same tool that helped us in generating our key pair file: the sn.exe tool.

For this, we run it using the following command: sn –vf MyAssembly.exe (or MyAssembly.dll)

The result when tested on a strongly named assembly is:

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
Assembly ‘CrackMe4-Signed.exe’ is valid

But when tested on a non signed assembly:

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42

Copyright (c) Microsoft Corporation. All rights reserved.
CrackMe4-unSigned.exe does not represent a strongly named assembly

Dot NET Assemblies and Strong Name Signature

Using the same tool, we can also explore the public key of a signed assembly using the following command: sn –tp MyAssembly.exe (or MyAssembly.dll).

ILDASM, the Microsoft Intermediate Language Disassembler, can provide more details about strong name. To know more about handling this tool, please refer to previous articles about Demistyfying Dot NET Reverse Engineering. By loading a strongly named assembly using this utility, which is found in somewhere like C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe, you can double-click the “MANIFEST” section to explore its content:

Dot NET Assemblies and Strong Name Signature

If the assembly we are analyzing was not strongly named, the .publickey directive should be missing. Obviously, any other tool like Reflector and ILSpy will do the same work.

Actually, strong name does not survive to round trip engineering, which means if you modify the Intermediate Language of a strong name protected assembly then recompile it, it’s not supposed to work.

Removing Strong Name from an Assembly

There are several ways to remove a strong name from a protected assembly. I’ll try to demonstrate some “easy” ways to un-protect any strong name protected assembly. In order to achieve this, in addition to ILDASM and ILASM, we will need a hex editor and a tool called CFF Explorer which you can find in the reference section.

Try to run our target to see what it says and let’s practice:

Dot NET Assemblies and Strong Name Signature

Basically, we need to change the string loaded even if this is a strong name protected assembly. Start by disassembling our target using ILDASM to produce an IL file, which is the attached CrackMe#4 –BreakingStrongName, then search for this string:

Dot NET Assemblies and Strong Name Signature

Change it and reassemble the IL file using ILASM:

040813_1256_DotNETAssem18

Reassembling…

C:\Windows\Microsoft.NET\Framework\v4.0.30319>ilasm “C:\Users\Soufiane\Desktop\Sname\dis\sname.il”

…………..

Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully
C:\Windows\Microsoft.NET\Framework\v4.0.30319>

Now our target refuses to run because it’s strong name protected and, as said before, strong name does not survive in round trip engineering, which we did by disassembling, editing IL code, then reassembling it.

Now let’s get back to ILDASM and see the content of the MANIFEST section:

Dot NET Assemblies and Strong Name Signature

Actually this is the value of the public key of our target’s strong name, which can be located on the IL file produced by ILDASM after dumping the loaded assembly as seen here (search for the string “RSA1?):

Dot NET Assemblies and Strong Name Signature

The first way to remove a strong name from an assembly is by simply removing these directives and its content which will lead to something simillar to this:

Dot NET Assemblies and Strong Name Signature

Then change the string loaded by the target with whatever you want, recompile it and test it:

Dot NET Assemblies and Strong Name Signature

And yes, strong name is weak and we changed the string loaded!

This is the first method you can use to remove strong name from an assembly. Now let’s discover how we can remove it using CFF Explorer.

Load our target on CFF Explorer. This tool provides an important section when dealing with dot net assemblies which is .NET Directory:

Dot NET Assemblies and Strong Name Signature

Dot NET Assemblies and Strong Name Signature

According to the structure of a managed executable file, the Common Language Runtime headers are put in the.text section and the header file is defined as a structure:

typedef struct IMAGE_COR20_HEADER
{
ULONG cb;
USHORT MajorRuntimeVersion;
USHORT MinorRuntimeVersion;
// Symbol table and startup information
IMAGE_DATA_DIRECTORY MetaData;
ULONG Flags;
union {
DWORD EntryPointToken;
DWORD EntryPointRVA;
};
// Binding information
IMAGE_DATA_DIRECTORY Resources;
IMAGE_DATA_DIRECTORY StrongNameSignature;
// Regular fixup and binding information
IMAGE_DATA_DIRECTORY CodeManagerTable;
IMAGE_DATA_DIRECTORY VTableFixups;
IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
IMAGE_DATA_DIRECTORY ManagedNativeHeader;
} IMAGE_COR20_HEADER;

And to provide a closer look at the marked fields:

Offset Size Field Description
16 4 Flags Binary flags. In ILASM, you can specify thisvalue explicitly by the directive .corflags<integer value> and/or the command lineoption /FLAGS=<integer value>. Thecommand line option takes precedenceover the directive.
32 8 StrongNameSignature RVA and size of the hash data for this PEfile, used by the loader for binding andversioning.

Flags = 0000000B means COMIMAGE_FLAGS_ILONLY (0×00000001) |COMIMAGE_FLAGS_32BITREQUIRED ((0×00000002)| COMIMAGE_FLAGS_STRONGNAMESIGNED (0×00000008). This means that the image file contains IL code only with no embedded native unmanaged code. This image file can only be loaded on a 32-bit processor and is protected with a strong name signature. So 0×00000001 + 0×00000002 + 0×00000008 = 0×00000011, which is B in hexadecimal.

The trick is to change the value of the Flags field by changing COMIMAGE_FLAGS_STRONGNAMESIGNED from 0000000B to 00000003, and changing the StrongNameSignature Size and StrongNameSignature RVA to 0. But this is not everything. Keep on analyzing the target using CFF Explorer by clicking on Metadata Streams -> Tables -> Assembly:

Dot NET Assemblies and Strong Name Signature

The Strong Name signature field of the CLR contains the RVA and size of the strong name hash value. After creating and hashing the image file to stronglly name it, the resulting hash blob is written into the space allocated to it inside the image file.

To make it easy and clear, we have to set Flags’ and PublicKey’s values to zero.
So to completely remove any strong name authenticity related checks, we have to make these changes:

Offset Original Value Changed Value
00000418 0000000B 00000003
00000428 0001C4C0 00000000
0000042C 00000080 00000000
0001BCEE 00000001 00000000
0001BCF2 04F7 0000

You can perfom these changes directly using CFF Explorer or by looking for the value using its given offset using a hexadecimal editor. To change a value using CFF Explorer, just double click on it and type in the new value, then File -> Save as.

Let’s try the new saved file. Now disassemble it, change the string and reassemble it as we previously did:

Dot NET Assemblies and Strong Name Signature

This is how easy you can get rid of strong name protection ! You can find in the references section below a generic tool that can remove strong name from any dot NET assembly.

References:

Soufiane Tahiri is is an InfoSec Institute contributor and computer security researcher, specializing in reverse code engineering and software security. Dynamic and very involved, Soufiane is ready to catch any serious opportunity to be part of a workgroup.

2 Comments

  1. Philip

    You completely misunderstood the point of a strongname on an assembly. It is by *verifying* the strongname is what you expect that the validity of the assembly is confirmed. If you remove the strongname, then it will no longer pass this verification.

    Removing a strong name is fairly easy. The issue is getting that modified assembly to be used by anything that does verification. This is like saying that id cards are weak because you can just throw yours away! The actual use of an id card is when someone asks you for it – if you threw yours (or someone elses) away, then it no longer works.. as designed.

    If you want any respect, please research your subjects before posting uninformed articles.

Comments are closed.