以下是提出此问题的原因:www.devplusplus.com/Tests/CSharp/Hello_World。
虽然之前提出了类似的问题,但网上的许多答案都有几个问题:
这样的事情:
var evidence = new Evidence();
evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
var permissionSet = SecurityManager.GetStandardSandbox(evidence);
到目前为止,我找不到一种方法来创建AppDomain并加载程序集不是在文件系统,而是在RAM中。
同样,上面列出了其他解决方案不起作用的原因:1。许多是4.0之前的版本,2。许多人依赖指向文件系统的“.Load”方法。
答案2:我有一个汇编引用,因为它是由CSharpCodeProvider
类生成的,所以如果你知道一种方法将那个转换成一个字节数组,那就完美了!
var provider = new CSharpCodeProvider(new Dictionary<String, String>
{ { "CompilerVersion", "v4.0" } });
var compilerparams = new CompilerParameters
{ GenerateExecutable = false, GenerateInMemory = true, };
var compilerResults = provider.CompileAssemblyFromSource(compilerparams,
string_Of_Code_From_A_User);
var instanceOfSomeClass = compilerResults.CompiledAssembly
.CreateInstance(className);
// The 'DoSomething' method can write to the file system and I don't like that!
instanceOfSomeClass.GetType().GetMethod("DoSomething")
.Invoke(instanceOfSomeClass, null);
有两个原因:
答案 0 :(得分:42)
好的,首先要做的事情是:没有实际的方法可以使用CSharpCodeProvider完全在内存中动态编译C#源代码。有些方法似乎支持该功能,但由于C#编译器是无法在进程中运行的本机可执行文件,因此源字符串将保存到临时文件中,在该文件上调用编译器,然后生成的程序集为保存到磁盘,然后使用Assembly.Load为您加载。
其次,正如您所发现的,您应该能够在AppDomain中使用Compile方法来加载程序集并为其提供所需的权限。我遇到了同样不寻常的行为,经过大量挖掘后发现它是框架中的一个错误。我在MS Connect上提交了一份问题报告。
由于框架已经写入文件系统,因此解决方法是将程序集写入临时文件,然后根据需要加载。但是,当您加载它时,您需要在AppDomain中临时断言权限,因为您不允许访问文件系统。这是一个示例片段:
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
从那里你可以使用程序集和反射来调用你的方法。请注意,此方法允许您在沙盒AppDomain之外提升编译过程,这在我看来是个加分。
作为参考,这里是我的Sandbox类,它是为了便于在一个干净利落的独立AppDomain中启动脚本程序集,该AppDomain具有有限的权限,并且可以在必要时轻松卸载:
class Sandbox : MarshalByRefObject
{
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 PermissionSet(PermissionState.None);
permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
typeof(Sandbox).Assembly.Evidence.GetHostEvidence<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 FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Type type = assembly.GetType(scriptType);
if (type == null)
return null;
var instance = Activator.CreateInstance(type);
return string.Format("{0}", type.GetMethod(method).Invoke(instance, parameters));
}
}
快速注意:如果您使用此方法为新AppDomain提供安全证据,则需要对程序集进行签名以赋予其强名称。
请注意,这在运行过程中运行正常,但如果您真的需要防弹脚本环境,则需要更进一步,在单独的进程中隔离脚本,以确保执行恶意脚本(或者只是堆栈溢出,fork炸弹和内存不足等情况并不会导致整个应用程序进程崩溃。如果您需要,我可以为您提供更多相关信息。