1

I'm experimenting with tampering of the assemblies in order to be able to mock the code which cannot easily be mocked, such as SharePoint assemblies.

As a prototype, I have a solution where the tested code is tampered through a custom build action with ILRepack and Mono.Cecil, so that the actual code is not the one which was originally written.

When the assembly containing the tested code is not signed, everything works well. When the assembly is signed, I see the well expected error:

The located assembly's manifest definition does not match the assembly reference.

Since the assembly is tampered, obviously, the key is either not the same (if signed after tampering) or doesn't exist.

I thought that passing by AppDomain.CurrentDomain.AssemblyResolve event could allow forcing the tampered assembly to be accepted, but it's not.

How do I ask .NET Framework to ignore the checking of assembly matching during unit tests running in full trust, and accept unsigned assemblies when it expects them to be signed?


As I understand it, the problem is not strong name validation. Not only the error would be different, but also running the corresponding sn –Vr has no effect: the error is still there.

The actual problem seems to be at the assembly binding level. This is also why I'm surprised to see that AppDomain.CurrentDomain.AssemblyResolve event is raised, but its result is still ignored; probably, it doesn't do what I always thought it did.

Community
  • 1
  • 1
Arseni Mourzenko
  • 45,791
  • 28
  • 101
  • 185
  • I'm not sure if I'm understanding correctly but it sounds like you're trying to defeat the secure code you've written. i.e. If you figure out how to outwit your code via a unit test doesn't that mean that a hacker could use the same approach to defeat your defenses in production? – Paul Sasik Aug 09 '15 at 01:21
  • @PaulSasik: exactly. In production, the hacker can indeed use the same technique, but it applies to code running in full-trust anyway. Aside, I hope the specific context of unit tests may also help: for example, isn't the same mechanism used when doing code coverage or profiling? – Arseni Mourzenko Aug 09 '15 at 01:25
  • http://stackoverflow.com/a/3268952 appears to mention something about being able to use `sn -T ` where `fullname` is `Assembly=MyAssembly.dll, Public Key Token=`. Maybe you can set it after modding the assembly? – Eris Aug 09 '15 at 05:46

1 Answers1

0

The solution is much easier that I thought. Since, as I guessed, the problem is not strong name validation, but assembly binding relying on the public key token, one can replace the token while tampering the assembly.

The first step is, before tampering, to get the original public key and public key token. They are both inside the AssemblyName.

var originalAssemblyBytes = File.ReadAllBytes(originalFilePath);
var originalAssemblyName = Assembly.Load(originalAssemblyBytes).GetName();

The next step is the tampering itself: first the merge of two assemblies using ILRepack, then more granular changes with Mono.Cecil. During this second step, Mono.Cecil is used to replace the public key and the public key token:

var assemblyName = mergedModule.Assembly.Name;
assemblyName.HasPublicKey = true;
assemblyName.PublicKey = originalAssemblyName.GetPublicKey();
assemblyName.PublicKeyToken = originalAssemblyName.GetPublicKeyToken();

It is essential to change both: if only the token is changed, the error “Invalid assembly public key” is thrown as soon as the assembly is used.

This works for both assemblies signed with .snk and with .pfx: since tests are running in full trust, the validation is not done, meaning that the fact that the public key doesn't correspond to the private key is irrelevant.

Arseni Mourzenko
  • 45,791
  • 28
  • 101
  • 185