Tampering SharePoint assemblies part 1

Arseni Mourzenko
Founder and lead developer
166
articles
August 18, 2015
Tags: testing 6

I've in­her­it­ed a Share­Point pro­ject and I'm at the step when I start adding tests for re­gres­sion test­ing. If the au­thors of the code knew any­thing about ob­ject ori­en­ta­tion and de­sign, there would be no prob­lems at this step; but, as usu­al, they were slight­ly ig­no­rant of the very ba­sics of pro­gram­ming, and so near­ly every method re­lies on Share­Point class­es such as SPSite or SPWeb. If those class­es were based on in­ter­faces, cre­at­ing the cor­re­spond­ing mocks would be rel­a­tive­ly easy, but un­for­tu­nate­ly, Mi­crosoft didn't thought that some­one could ever need to test the code of Share­Point ap­pli­ca­tions.

So came the search for a so­lu­tion. I al­ready knew that Mi­crosoft did a great job with Mi­crosoft Fakes, and, in the con­text of Share­Point, with Share­Point.Em­u­la­tors which are based on Mi­crosoft Fakes and con­tain the mocks of SP class­es. Un­for­tu­nate­ly, it doesn't work with .NET Frame­work 4 or lat­er, and in all cas­es needs Vi­su­al Stu­dio Ul­ti­mate, which would prob­a­bly be prob­lem­at­ic in a con­text of the cur­rent pro­ject:

Sys­tem Re­quire­ments [...] Mi­crosoft Vi­su­al Stu­dio 2012 Ul­ti­mate

Hope­ful­ly, there is a third-par­ty al­ter­na­tive for Share­Point 2013 and .NET Frame­work 4.5: SPEm­u­la­tors. Nice. But it doesn't work well. If all the test­ed code needs is to ac­cess sim­ple stuff, like the name of the site, every­thing works flaw­less­ly. But as soon as you start do­ing chains such as site.WebApplication.AlternateUrls, you end up with null ob­jects that you can't re­place by some­thing else.

Since the so­lu­tions de­signed specif­i­cal­ly to mock Share­Point class­es didn't work, it was time to start us­ing the heavy ar­tillery: prox­ies and AOP frame­works.

.NET Frame­work prox­ies

When I used .NET Frame­work prox­ies in the past, I had the con­trol over the prox­ied ob­jects, and so in­her­it­ing the class­es of those ob­jects from MarshalByRefObject and even­tu­al­ly mak­ing them se­ri­al­iz­able wasn't a prob­lem. This so­lu­tion couldn't be ap­plied in the cur­rent case, since I wouldn't be able to change the in­her­i­tance of the class­es of Share­Point as­sem­blies in the first place.

Cas­tle Wind­sor Dy­nam­icProxy

So I start­ed by the third-par­ty proxy li­brary I [thought I] knew: Cas­tle Wind­sor's Dy­nam­icProxy. I start­ed by set­ting up a sim­ple pro­ject to test the fea­si­bil­i­ty of the thing. The pro­ject was a sim­ple test pro­ject with a sin­gle file con­tain­ing every­thing I need­ed, and a sin­gle test: green means that I found the so­lu­tion; red means I failed.

namespace UnitTests
{
    using Castle.DynamicProxy;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;

    [TestClass]
    public class DemoTests
    {
        [TestMethod]
        public void TestProxy()
        {
            var generator = new ProxyGenerator();
            var proxy = generator.CreateClassProxy<Demo>(new DemoInterceptor());
            var actual = proxy.SayHello();
            var expected = "Something else";
            Assert.AreEqual(expected, actual);
        }
    }

    [Serializable]
    public class DemoInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            invocation.ReturnValue = "Something else";
        }
    }

    public class Demo
    {
        public string SayHello()
        {
            return "Hello, World!";
        }
    }
}

The test failed. Thanks to Sri­ram Sak­thiv­el, I learned that it won't work any­way: I would need ei­ther an in­ter­face or a vir­tu­al method to make this work.

SpringAOP

SpringAOP has the very same prob­lem. The ap­proach is sim­i­lar; the in­ter­cep­tor looks like this:

public class DemoInterceptor : IMethodInterceptor
{
    public object Invoke(IMethodInvocation invocation)
    {
        return "Something else";
    }
}

and the test looks like that:

var factory = new ProxyFactory(new Demo());
factory.AddAdvice(new DemoInterceptor());
var proxy = (Demo)factory.GetProxy();
var actual = proxy.SayHello();
var expected = "Something else";
Assert.AreEqual(expected, actual);

but the func­tion­ing re­mains the same, and the re­sult is the same as well: the test fails, but suc­ceeds if SayHello is vir­tu­al.

As­pect­Sharp

As­pect­Sharp seems to be just a lay­er of ab­strac­tion based on Cas­tle's Dy­nam­icProxy. I'm pret­ty sure it has the same lim­i­ta­tions as Dy­nam­icProxy.

Post­Sharp

Post­Sharp seemed more promis­ing, since not be­ing lim­it­ed to vir­tu­al meth­ods, but once again, re­quired to mod­i­fy the prox­ied code it­self by dec­o­rat­ing ei­ther the class­es or the spe­cif­ic meth­ods with an at­tribute which, by in­her­it­ing from OnMethodBoundaryAspect, sig­nals to Post­Sharp that the class or method is a part of the AOP strat­e­gy.

Mono.Ce­cil

Since AOP frame­works are too lim­it­ed, it was time to push a lit­tle fur­ther: in­stead of ex­tend­ing class­es through AOP, what if we could tam­per them?

Reflection.Emit be­ing quite dif­fi­cult to grasp, I found it more con­ve­nient to use Mono.Ce­cil. Well, maybe con­ve­nience is the least ap­pro­pri­ate term when it comes to Re­flec­tion and es­pe­cial­ly dy­nam­ic al­ter­ation of as­sem­blies, but at least, it's damn ef­fec­tive.

For those who may ask them­selves if it's even pos­si­ble (could you imag­ine the im­pli­ca­tions of that? Some­one could go and change the ef­fec­tive im­ple­men­ta­tion of string.IsNullOrEmpty to re­turn false every time just for fun, or, slight­ly more sub­tle, change SecureString to also send sen­si­tive in­for­ma­tion to a re­mote serv­er. More­over, what about signed as­sem­blies?

In fact, it is per­fect­ly pos­si­ble to change an as­sem­bly. You can even tam­per with an as­sem­bly in GAC us­ing a text ed­i­tor. This is be­cause they are checked dur­ing in­stal­la­tion, not every time they are called.

The ac­tu­al so­lu­tion, how­ev­er, is not par­tic­u­lar­ly straight­for­ward. We can't just put every­thing in the same as­sem­bly and ex­pect it to work mag­i­cal­ly. Let me ex­plain the over­all ar­chi­tec­ture of the so­lu­tion.

I still have my test pro­ject, but it can­not ref­er­ence di­rect­ly the as­sem­bly which should be tam­pered (let's call it Target). If it does, this means that when­ev­er I start the pro­ject, it will read the orig­i­nal, ref­er­enced as­sem­bly, and lock the file, pre­vent­ing me from mod­i­fy­ing it. In­stead, I pass by an in­ter­me­di­ary pro­ject which, in turn, ref­er­ences the one which will be tam­pered. This in­ter­me­di­ary, let's call it Consumer, sim­ply calls a method from the as­sem­bly which is sub­ject to tam­per­ing; in the same way, the as­sem­blies un­der test of an ac­tu­al pro­ject call Share­Point as­sem­blies, with­out the need for unit tests to ref­er­ence those Share­Point as­sem­blies.

When the test starts, it be­gins by tam­per­ing the tar­get as­sem­bly, and only then loads a con­sumer with­in an AppDomain and launch­es the ac­tion which uses the tam­pered tar­get.

The unit test with­in the DemoTests class looks just like the one in the pre­vi­ous AOP ex­am­ple:

[TestMethod]
public void TestProxy()
{
    var originalPath = @"H:\Samples\TamperingWithCecil\Target\bin\Debug\Target.dll";
    var replacement = typeof(DemoTests).GetMethod("SayHelloReplacement");

    var proxy = CreateProxy(originalPath, "Demo", "SayHello", replacement);
    var consumer = this.CreateConsumer<Consumer>();

    var actual = consumer.Talk(proxy);
    var expected = "Something else";
    Assert.AreEqual(expected, actual);
}

public string SayHelloReplacement()
{
    return "Something else";
}

This is a pro­to­type, which means that many things are far from be­ing as I would like them to be. In par­tic­u­lar, many el­e­ments are no­tice­ably string­ly-typed.

The method which cre­ates the con­sumer (cur­rent­ly in the same DemoTests class) is sim­ply load­ing the as­sem­bly in the cur­rent AppDomain and cre­at­ing an in­stance of the class:

private dynamic CreateConsumer<T>()
{
    var consumerAssemblyPath = this.GetAssemblyFilePath(typeof(T));
    AppDomain.CurrentDomain.Load(File.ReadAllBytes(consumerAssemblyPath));
    return AppDomain.CurrentDomain.CreateInstanceAndUnwrap(
        typeof(T).Assembly.FullName, typeof(T).FullName);
}

The method which does the most in­ter­est­ing part—the tam­per­ing of the as­sem­bly—is more com­pli­cat­ed, and I sin­cere­ly apol­o­gize for such ug­li­ness. Both this method, the pre­vi­ous one and the few ones which will fol­low be­long to a sep­a­rate li­brary and are in DemoTests just for the con­ve­nience of this ear­ly pro­to­type.

private dynamic CreateProxy(
    string proxiedAssemblyPath, string proxiedTypeName,
    string proxiedMethodName, MethodInfo replacement)
{
    var originalPath = proxiedAssemblyPath;
    var alternatePath = Path.Combine(
        Path.GetDirectoryName(GetAssemblyFilePath()),
        Path.GetFileName(originalPath));
    File.Copy(originalPath, alternatePath, overwrite: true);

    var module = ModuleDefinition.ReadModule(alternatePath);
    var method = this.FindMethod(module, proxiedTypeName, proxiedMethodName);
    var replacementMethod = this.FindMethod(
        this.GetAssemblyFilePath(replacement.DeclaringType),
        replacement.DeclaringType.Name, replacement.Name);

    var processor = method.Body.GetILProcessor();
    var firstTargetInstruction = method.Body.Instructions.First();
    foreach (var newInstruction in replacementMethod.Body.Instructions)
    {
        processor.InsertBefore(firstTargetInstruction, newInstruction);
    }

    module.Write(alternatePath);

    var assembly = Assembly.LoadFile(alternatePath);
    var type = assembly.GetType(
        Path.GetFileNameWithoutExtension(proxiedAssemblyPath) +
        "." + proxiedTypeName);
    return Activator.CreateInstance(type);
}

Fi­nal­ly come a bunch of small meth­ods used by two pre­vi­ous meth­ods:

private string GetAssemblyFilePath()
{
    return this.GetAssemblyFilePath(Assembly.GetExecutingAssembly());
}

private string GetAssemblyFilePath(Type type)
{
    return this.GetAssemblyFilePath(type.Assembly);
}

private string GetAssemblyFilePath(Assembly assembly)
{
    var uri = new UriBuilder(assembly.CodeBase);
    return Uri.UnescapeDataString(uri.Path);
}

public MethodDefinition FindMethod(
    string modulePath, string typeName, string methodName)
{
    var module = ModuleDefinition.ReadModule(modulePath);
    return this.FindMethod(module, typeName, methodName);
}

public MethodDefinition FindMethod(
    ModuleDefinition module, string typeName, string methodName)
{
    var type = module.Types.Single(c => c.Name == typeName);
    return type.Methods.Single(c => c.Name == methodName);
}

The con­sumer (which is in the sep­a­rate con­sumer as­sem­bly) con­tains the fol­low­ing class:

public class Consumer
{
    public string Talk(Demo demo)
    {
        return demo.SayHello();
    }
}

This ap­proach not only works for any method, would it be vir­tu­al or not, but is also per­fect­ly vi­able for test­ing, es­pe­cial­ly test­ing code which over­ly-re­lies on third-par­ty as­sem­blies, such as Share­Point.

There is still a lot of work un­til this ap­proach could be used to cre­ate mocks. It is also very slow (one sec­ond to run a sin­gle test), and it would be prob­a­bly much, much slow­er when used with an as­sem­bly of the size of Microsoft.SharePoint, but de­spite this, this pro­to­type shows a pos­si­ble so­lu­tion to the prob­lem of unit test­ing of lega­cy code based on Share­Point or any oth­er as­sem­blies which are not de­signed to be eas­i­ly re­place­able.