我试图在运行时编译一些代码以允许我的应用程序中的一些脚本功能,但是我想在沙箱中运行脚本,以防有人试图做一些顽皮的事情。
问题是 - 毕竟完成并且我的输出内存没有被释放,并且为运行代码而生成的“c.dll”文件仍然被我的进程锁定,因此任何删除它的尝试也会失败- 这意味着如果我尝试第二次运行代码,它无法再次编译它,因为“c.dll”已经在“另一个进程”中打开。这是所有代码:
一切开始的地方:
using (Microsoft.CSharp.CSharpCodeProvider provider = new Microsoft.CSharp.CSharpCodeProvider())
{
string DLLpath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\\c.dll";
try { File.Delete(DLLpath); } catch { /*best attempt*/ }
System.CodeDom.Compiler.CompilerParameters param = new System.CodeDom.Compiler.CompilerParameters();
param.ReferencedAssemblies.Add("System.dll");
param.ReferencedAssemblies.Add("System.Data.dll");
param.ReferencedAssemblies.Add("Newtonsoft.Json.dll");
param.ReferencedAssemblies.Add("mscorlib.dll");
param.ReferencedAssemblies.Add("System.Core.dll");
param.ReferencedAssemblies.Add("System.Net.Http.dll");
param.ReferencedAssemblies.Add("System.Net.dll");
param.GenerateExecutable = false;
param.TreatWarningsAsErrors = false;
string code = Data.Remove(0, 8);
code = "using System;" + Environment.NewLine +
"using System.Data;" + Environment.NewLine +
"using Newtonsoft.Json;" + Environment.NewLine +
"using System.IO;" + Environment.NewLine +
"using System.Text;" + Environment.NewLine +
"using System.Net;" + Environment.NewLine +
"using System.Net.Http;" + Environment.NewLine +
@"namespace Custom{public class Program{public static object Main(){" +
code + "}}}";
param.OutputAssembly = DLLpath;
System.CodeDom.Compiler.CompilerResults res = provider.CompileAssemblyFromSource(param, code);
if (res.Errors.HasErrors)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError error in res.Errors)
{
if (error.IsWarning) sb.AppendLine(String.Format("Warning ({0}): {1}", error.ErrorNumber, error.ErrorText));
else sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
Sandbox sandbox = Sandbox.Create();
string result = sandbox.Execute(res.PathToAssembly, "Custom.Program", "Main", null);
sandbox.Dispose();
sandbox = null;
res = null;
code = null;
param = null;
GC.Collect();
GC.WaitForPendingFinalizers();
try { File.Delete(DLLpath); } catch { /*best attempt*/ }
return "OK" + Environment.NewLine + result;
}
这是沙盒:
class Sandbox : CrossAppDomainObject
{
const string BaseDirectory = "Untrusted";
const string DomainName = "Sandbox";
public Sandbox()
{
}
public static Sandbox Create()
{
var setup = new AppDomainSetup()
{
ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
ApplicationName = DomainName,
DisallowBindingRedirects = true,
DisallowCodeDownload = true,
DisallowPublisherPolicy = true
};
var permissions = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
permissions.AddPermission(new System.Security.Permissions.ReflectionPermission(System.Security.Permissions.ReflectionPermissionFlag.RestrictedMemberAccess));
permissions.AddPermission(new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.Execution));
var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
typeof(Sandbox).Assembly.Evidence.GetHostEvidence<System.Security.Policy.StrongName>());
return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
}
public string Execute(string assemblyPath, string scriptType, string method, params object[] parameters)
{
new System.Security.Permissions.FileIOPermission(System.Security.Permissions.FileIOPermissionAccess.Read | System.Security.Permissions.FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = System.Reflection.Assembly.LoadFile(assemblyPath);
System.Security.CodeAccessPermission.RevertAssert();
Type type = assembly.GetType(scriptType);
if (type == null)
return null;
var instance = Activator.CreateInstance(type);
string result = string.Format("{0}", type.GetMethod(method).Invoke(instance, parameters));
assembly = null;
type = null;
instance = null;
return result;
}
}
这是 CrossAppDomainObject:
public abstract class CrossAppDomainObject : MarshalByRefObject, IDisposable
{
private bool _disposed;
/// <summary>
/// Gets an enumeration of nested <see cref="MarshalByRefObject"/> objects.
/// </summary>
protected virtual System.Collections.Generic.IEnumerable<MarshalByRefObject> NestedMarshalByRefObjects
{
get { yield break; }
}
~CrossAppDomainObject()
{
Dispose(false);
}
/// <summary>
/// Disconnects the remoting channel(s) of this object and all nested objects.
/// </summary>
private void Disconnect()
{
System.Runtime.Remoting.RemotingServices.Disconnect(this);
foreach (var tmp in NestedMarshalByRefObjects)
System.Runtime.Remoting.RemotingServices.Disconnect(tmp);
}
public sealed override object InitializeLifetimeService()
{
//
// Returning null designates an infinite non-expiring lease.
// We must therefore ensure that RemotingServices.Disconnect() is called when
// it's no longer needed otherwise there will be a memory leak.
//
return null;
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
Disconnect();
_disposed = true;
}
}
请注意,我从 Nathan Evans 的博客https://nbevans.wordpress.com/2011/04/17/memory-leaks-with-an-infinite-lifetime-instance-of-marshalbyrefobject/获得了 CrossAppDomainObject 抽象类
我会很感激任何指导,因为我在这里完全一无所知。
编辑:
在汉斯告诉我回到绘图板之后,我确实这样做了,并使用了这篇 MSDN 文章作为参考:https ://msdn.microsoft.com/en-us/library/bb763046(v=vs.110).aspx
在玩了一会儿之后,我一起破解了这个:
class Sandboxer : CrossAppDomainObject
{
public static string Start(string pathToUntrusted, string untrustedAssembly, string untrustedClass, string entryPoint, Object[] parameters)
{
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);
System.Security.PermissionSet permSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
permSet.AddPermission(new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.Execution));
System.Security.Policy.StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<System.Security.Policy.StrongName>();
AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
System.Runtime.Remoting.ObjectHandle handle = Activator.CreateInstanceFrom(
newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
typeof(Sandboxer).FullName
);
Sandboxer newDomainInstance = (Sandboxer)handle.Unwrap();
System.Reflection.AssemblyName untrustedAssemblyName = System.Reflection.AssemblyName.GetAssemblyName(untrustedAssembly);
string result = newDomainInstance.ExecuteUntrustedCode(untrustedAssemblyName, untrustedAssembly, untrustedClass, entryPoint, parameters);
AppDomain.Unload(newDomain);
System.Reflection.Assembly[] list = AppDomain.CurrentDomain.GetAssemblies();
return result;
}
public string ExecuteUntrustedCode(System.Reflection.AssemblyName assemblyName, string assemblyPath, string typeName, string entryPoint, Object[] parameters)
{
new System.Security.Permissions.FileIOPermission(System.Security.Permissions.FileIOPermissionAccess.Read | System.Security.Permissions.FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
System.Reflection.MethodInfo target = System.Reflection.Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
System.Security.CodeAccessPermission.RevertAssert();
try
{
return (string)target.Invoke(null, parameters);
}
catch (Exception ex)
{
Exception e;
(new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted)).Assert();
e = ex;
System.Security.CodeAccessPermission.RevertAssert();
return e.Message;
}
}
}