views:

528

answers:

7

Hey, I'm using C# with .Net 3.5. Is it possible to serialize a block of code, transmit it somewhere, deserialize it, then execute it?

An example usage of this would be:

Action<object> pauxPublish = delegate(object o)
{
  if (!(o is string))
  {
    return;
  }
  Console.WriteLine(o.ToString());
};
Transmitter.Send(pauxPublish);

With some remote program doing:

var action = Transmitter.Recieve();
action("hello world");

My end goal is to be able to execute arbitrary code in a different process (which has no prior knowledge of the code).

+1  A: 

Compile it into a separate assembly, send the assembly, have the other process load it.

You might want to consider security implications.

Update: another idea would be to generate an expression tree and use this library to serialize it:

http://www.codeplex.com/metalinq/

Daniel Earwicker
+3  A: 

You could also send it as a string then use the CodeDomProvider to compile it, same result. I have an example bit of code, but it's not the best, I'll post it at request.

Edit

I warned you ! Everyone and anyone feel free to improve on this, but please don't just down vote me for crappyness ^_^

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;

namespace DynamicCodeApplication
{
    class azCodeCompiler
    {
        private List<string> assemblies;

        public azCodeCompiler()
        {
            assemblies = new List<string>();
            scanAndCacheAssemblies();
        }

        public Assembly BuildAssembly(string code)
        {

            CodeDomProvider prov = CodeDomProvider.CreateProvider("CSharp");
            string[] references = new string[] { };   // Intentionally empty, using csc.rsp
            CompilerParameters cp = new CompilerParameters(references)
                                        {
                                            GenerateExecutable = false,
                                            GenerateInMemory = true
                                        };
            string path = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
            cp.CompilerOptions = "@" + path + @"\csc.rsp";
            CompilerResults cr = prov.CompileAssemblyFromSource(cp, code);

            foreach (CompilerError err in cr.Errors)
            {
                Console.WriteLine(err.ToString());
            }
            return cr.CompiledAssembly;
        }

        public object ExecuteCode(string code,
                                  string namespacename, string classname,
                                  string functionname, bool isstatic, params object[] args)
        {
            object returnval = null;
            Assembly asm = BuildAssembly(code);
            object instance = null;
            Type type = null;
            if (isstatic)
            {
                type = asm.GetType(namespacename + "." + classname);
            }
            else
            {
                instance = asm.CreateInstance(namespacename + "." + classname);
                type = instance.GetType();
            }
            MethodInfo method = type.GetMethod(functionname);
            returnval = method.Invoke(instance, args);
            return returnval;
        }

        private void scanAndCacheAssemblies()
        {

            /*
            foreach (string str in Directory.GetFiles(@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727"))
            {
                if (str.Contains(".dll"))
                {
                    foreach (string st in str.Split(new char[] { '\\' }))
                    {
                        if (st.Contains(".dll"))
                        {
                            assemblies.Add(st);
                        }
                    }
                }
            }
             * */

            assemblies.Add("Accessibility.dll");
            assemblies.Add("AspNetMMCExt.dll");
            assemblies.Add("cscompmgd.dll");
            assemblies.Add("CustomMarshalers.dll");
            assemblies.Add("IEExecRemote.dll");
            assemblies.Add("IEHost.dll");
            assemblies.Add("IIEHost.dll");
            assemblies.Add("Microsoft.Build.Conversion.dll");
            assemblies.Add("Microsoft.Build.Engine.dll");
            assemblies.Add("Microsoft.Build.Framework.dll");
            assemblies.Add("Microsoft.Build.Tasks.dll");
            assemblies.Add("Microsoft.Build.Utilities.dll");
            assemblies.Add("Microsoft.Build.VisualJSharp.dll");
            assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
            assemblies.Add("Microsoft.JScript.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
            assemblies.Add("Microsoft.VisualBasic.dll");
            assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
            assemblies.Add("Microsoft_VsaVb.dll");
            assemblies.Add("mscorlib.dll");
            assemblies.Add("sysglobl.dll");
            assemblies.Add("System.configuration.dll");
            assemblies.Add("System.Configuration.Install.dll");
            assemblies.Add("System.Data.dll");
            assemblies.Add("System.Data.OracleClient.dll");
            assemblies.Add("System.Data.SqlXml.dll");
            assemblies.Add("System.Deployment.dll");
            assemblies.Add("System.Design.dll");
            assemblies.Add("System.DirectoryServices.dll");
            assemblies.Add("System.DirectoryServices.Protocols.dll");
            assemblies.Add("System.dll");
            assemblies.Add("System.Drawing.Design.dll");
            assemblies.Add("System.Drawing.dll");
            assemblies.Add("System.EnterpriseServices.dll");
            assemblies.Add("System.Management.dll");
            assemblies.Add("System.Messaging.dll");
            assemblies.Add("System.Runtime.Remoting.dll");
            assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
            assemblies.Add("System.Security.dll");
            assemblies.Add("System.ServiceProcess.dll");
            assemblies.Add("System.Transactions.dll");
            assemblies.Add("System.Web.dll");
            assemblies.Add("System.Web.Mobile.dll");
            assemblies.Add("System.Web.RegularExpressions.dll");
            assemblies.Add("System.Web.Services.dll");
            assemblies.Add("System.Windows.Forms.dll");
            assemblies.Add("System.XML.dll");
            assemblies.Add("vjscor.dll");
            assemblies.Add("vjsjbc.dll");
            assemblies.Add("vjslib.dll");
            assemblies.Add("vjslibcw.dll");
            assemblies.Add("vjssupuilib.dll");
            assemblies.Add("vjsvwaux.dll");
            assemblies.Add("vjswfc.dll");
            assemblies.Add("VJSWfcBrowserStubLib.dll");
            assemblies.Add("vjswfccw.dll");
            assemblies.Add("vjswfchtml.dll");
            assemblies.Add("Accessibility.dll");
            assemblies.Add("AspNetMMCExt.dll");
            assemblies.Add("cscompmgd.dll");
            assemblies.Add("CustomMarshalers.dll");
            assemblies.Add("IEExecRemote.dll");
            assemblies.Add("IEHost.dll");
            assemblies.Add("IIEHost.dll");
            assemblies.Add("Microsoft.Build.Conversion.dll");
            assemblies.Add("Microsoft.Build.Engine.dll");
            assemblies.Add("Microsoft.Build.Framework.dll");
            assemblies.Add("Microsoft.Build.Tasks.dll");
            assemblies.Add("Microsoft.Build.Utilities.dll");
            assemblies.Add("Microsoft.Build.VisualJSharp.dll");
            assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
            assemblies.Add("Microsoft.JScript.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
            assemblies.Add("Microsoft.VisualBasic.dll");
            assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
            assemblies.Add("Microsoft_VsaVb.dll");
            assemblies.Add("mscorlib.dll");
            assemblies.Add("sysglobl.dll");
            assemblies.Add("System.configuration.dll");
            assemblies.Add("System.Configuration.Install.dll");
            assemblies.Add("System.Data.dll");
            assemblies.Add("System.Data.OracleClient.dll");
            assemblies.Add("System.Data.SqlXml.dll");
            assemblies.Add("System.Deployment.dll");
            assemblies.Add("System.Design.dll");
            assemblies.Add("System.DirectoryServices.dll");
            assemblies.Add("System.DirectoryServices.Protocols.dll");
            assemblies.Add("System.dll");
            assemblies.Add("System.Drawing.Design.dll");
            assemblies.Add("System.Drawing.dll");
            assemblies.Add("System.EnterpriseServices.dll");
            assemblies.Add("System.Management.dll");
            assemblies.Add("System.Messaging.dll");
            assemblies.Add("System.Runtime.Remoting.dll");
            assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
            assemblies.Add("System.Security.dll");
            assemblies.Add("System.ServiceProcess.dll");
            assemblies.Add("System.Transactions.dll");
            assemblies.Add("System.Web.dll");
            assemblies.Add("System.Web.Mobile.dll");
            assemblies.Add("System.Web.RegularExpressions.dll");
            assemblies.Add("System.Web.Services.dll");
            assemblies.Add("System.Windows.Forms.dll");
            assemblies.Add("System.XML.dll");
            assemblies.Add("vjscor.dll");
            assemblies.Add("vjsjbc.dll");
            assemblies.Add("vjslib.dll");
            assemblies.Add("vjslibcw.dll");
            assemblies.Add("vjssupuilib.dll");
            assemblies.Add("vjsvwaux.dll");
            assemblies.Add("vjswfc.dll");
            assemblies.Add("VJSWfcBrowserStubLib.dll");
            assemblies.Add("vjswfccw.dll");
            assemblies.Add("vjswfchtml.dll");


            return;
        }
    }
}
Tarks
That would be a great help Tarks, thanks! Continuing along this topic, is it possible to turn a c# block of code into a string?
NoizWaves
A: 

another option is using the DLR, and constraining the code to execute...

Tracker1
+4  A: 

Generally speaking that sounds like a really bad idea and a big security hole.

You don't want another process to execute any code. Understand what you really need another process to do and build a little DSL around it.

VVS
Absolutely, you cannot trust the caller you really aught to come up with a DSL. Take a look at MGrammar and Oslo.
justin.m.chase
Executing arbitrary code and/or storing it in database somewhere... what could possibly go wrong? Doesn't seem like an anti-pattern to me ;-)
Repo Man
+3  A: 

You could try to use IronPython in your project, it's trivial to do what you are asking in python. The python code could call your c# methods. As for security, you could execute the code in a restricted environment of some kind (one example is RestrictedPython).

Toni Ruža
+1  A: 

It is an interesting challenge, but you should probably describe why you want to do this, since there is a lot of different approaches depending on your objective. As humpohl points out, there is also some pretty serious security issues.

"Serialized code" could just be source code or a compiled assembly, depending on your requirements. You probably don't need to use a seperate code serialization format.

If you want to generate code dynamically and pass that on, you could generate code using CodeDOM and compile it. However, you most likely dont need to generate completely arbitrary code.

JacquesB
+5  A: 

YES!!!

We have done this for a very real case of performance. Doing this at runtime or using a DSL was not an option due to performance.

We compile the code into an assembly, and rip the IL out of the method. We then get all the metadata associated with this method and serialize the whole mess via XML, compress it, and put it in our database.

At re-hydration time, we re-constitute the IL with the metadata using the DynamicMethod class, and execute it.

We do this because of speed. We have thousands of little blocks of code. Unfortunately, to compile a block of code and run it on the fly takes at least 250 ms, which is way too slow for us. We took this approach, and it is working REALLY well. At run-time, it takes an unmeasurable amount of time to reconstitute the method and run it.

Only thing to keep an eye on... Signed assemblies and Unsigned assemblies cannot mix the serialized method data.

Brian Genisio
This was the method I came up with and have been working on. The biggest problem seems to be converting the byte array that MethodBody gives into OpCodes to use with Reflection.Emit. I'm looking at http://weblogs.asp.net/rosherove/archive/2006/04/25/methodsvisualizer.aspx which might help.
ICR
o0o this sounds like the approach that I am looking for. If you have any more resources relating to think, links would be very much appreciated :)
NoizWaves
I can't make up my mind if what you have done is amazing or awful. Possibly both.
Anthony
http://blogs.msdn.com/haibo_luo/archive/2006/11/07/turn-methodinfo-to-dynamicmethod.aspx also seems pretty useful, perhaps more so.
ICR
Amazing or awful? I think it is a bit of both. I really can't do anything but think it is a hack of grand proportions... I was amazed when it finally worked... shocked by how evil/complex the solution really was.
Brian Genisio
We based our approach off of this article: http://www.codeproject.com/KB/cs/ExpressionEval.aspx . I should add that since we got it in place (with a TON of unit tests), we haven't had a single problem with the approach.
Brian Genisio
Cheers, this has all been very helpful, thanks :)
NoizWaves