Load WPF from memory

There are two steps.

A launcher

[STAThread]
static void Main(string[] args)
{

    string pathMain =

that is loading the WPF in memory

byte[] bytes = File.ReadAllBytes(pathMain);
Assembly assembly = Assembly.Load(bytes);

var app = typeof(Application);

var field = app.GetField("_resourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, assembly);

var helper = typeof(BaseUriHelper);
var property = helper.GetProperty("ResourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
property.SetValue(null, assembly, null);


try
{
    assembly.EntryPoint.Invoke(null, new object[0]);
} catch(Exception ex)
{
    Debug.WriteLine(ex);
}

and a modified WPF with the following App.xaml.cs in order to resolve the dependencies

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        base.OnStartup(e);
    }
    private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        string dir = @"C:\your\path\to\WPF\exe\folder\";
        string fileExtension = "*.dll";
        string needed = args.Name.Split(',')[0];
        if (needed.EndsWith(".resources"))
        {
            return null;
        }
        foreach (String file in Directory.GetFiles(dir, fileExtension, SearchOption.TopDirectoryOnly))
        {
            string name = System.IO.Path.GetFileNameWithoutExtension(file);
            if (args.Name.StartsWith(name))
            {
                byte[] bytes = File.ReadAllBytes(file);
                Assembly assembly = Assembly.Load(bytes);
                return assembly;
            }
        }
        Debug.WriteLine(args.Name);

        return null;
    }
}

Signing your assemblies

All this goes very well with an encrypted signature for your assemblies!

Remember: to verify your signature they only need the public key.

Both in the launcher for the exe

using (var crypt = RSACryptoServiceProvider.Create())
{
    using (StreamReader sr = new StreamReader(@"C:\path\to\publickey.xml"))
    {
        string publickkey = sr.ReadToEnd();
        crypt.FromXmlString(publickkey);
    }
    string outdir = @"C:\path\to\Signatures";
    byte[] signed = File.ReadAllBytes(Path.Combine(outdir, "YourExe.signed"));
    bool verified = crypt.VerifyData(bytes, signed, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
    if (!verified)
    {
        return;
    }
}

As well as for the dll files, with a simple helper:

private bool VerifySigned(byte[] bytes, string name)
{
    using (var crypt = RSACryptoServiceProvider.Create())
    {
        using (StreamReader sr = new StreamReader(@"C:\path\to\publickey.xml"))
        {
            string publickkey = sr.ReadToEnd();
            crypt.FromXmlString(publickkey);
        }
        string outdir = @"C:\path\to\Signatures";
        byte[] signed = File.ReadAllBytes(Path.Combine(outdir, name + ".signed"));
        bool verified = crypt.VerifyData(bytes, signed, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
        return verified;
    }
}

your code is very neat

byte[] bytes = File.ReadAllBytes(file);
bool verify = VerifySigned(bytes, name);
Debug.WriteLine(name + " verified: " + (verify ? "yes" : "no"));
if (!verify)
{
    return null;
}
Assembly assembly = Assembly.Load(bytes);
return assembly;

Perfect! But how do you save your private and public keys, first of all? Here it is:

void ProduceXML()
{
    using (var crypt = RSACryptoServiceProvider.Create())
    {
        string secretkey = crypt.ToXmlString(true);
        Console.WriteLine("secretkey");
        Console.WriteLine(secretkey);
        using (StreamWriter sw = new StreamWriter(@"C:\path\to\secretkey.xml"))
        {
            sw.WriteLine(secretkey);
        }
        string publickey = crypt.ToXmlString(false);
        Console.WriteLine("publickey");
        Console.WriteLine(publickey);
        using (StreamWriter sw = new StreamWriter(@"C:\path\to\publickey.xml"))
        {
            sw.WriteLine(publickey);
        }
    }
}

So now you’re able to sign your code so that it won’t be tampered with: enjoy it!

using (var crypt = RSACryptoServiceProvider.Create())
{
    using (StreamReader sr = new StreamReader(@"C:\path\to\secretkey.xml"))
    {
        string secret = sr.ReadToEnd();
        crypt.FromXmlString(secret);
    }
    string dir = @"C:\path\to\bin\Release\";
    string fileExtension = "*.dll";
    byte[] exe = File.ReadAllBytes(Path.Combine(dir, "YourApp.exe"));
    byte[] crypto_exe = crypt.SignData(exe, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
    string outdir = @"C:\path\to\Signatures";
    File.WriteAllBytes(Path.Combine(outdir, "YourApp.signed"), crypto_exe);
    foreach (String file in Directory.GetFiles(dir, fileExtension, SearchOption.TopDirectoryOnly))
    {
        string name = Path.GetFileNameWithoutExtension(file);
        byte[] bytes = File.ReadAllBytes(file);
        byte[] crypto_dll = crypt.SignData(bytes, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
        File.WriteAllBytes(Path.Combine(outdir, name + ".signed"), crypto_dll);
    }
}

2 thoughts on “Load WPF from memory

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s