受限 AppDomain 中的 C# Class 继承自位于主 AppDomain 中的另一个 class
C# Class in a restricted AppDomain inherit from an other class located in the main AppDomain
我尝试在 C# 中创建一个简单的控制台改装项目,其中我的程序包含一个 抽象列表 class,名为 ElementInGame 。我希望能够创建其他 class 从 .txt 文件 继承 ElementInGame。 class ElementInGame 将包含一些基本方法(虚拟的和非虚拟的)。但我不希望这些其他修改的 class 执行恶意代码,我希望他们只能从继承的 class 访问 methods/properties。这是我的 ElementInGame 代码:
(我的 C# 程序 #1)
using System;
namespace Modding
{
//The class itself inherit from MarshalByRefObject to be available in 2 differents Domains
public abstract class ElementInGame : MarshalByRefObject
{
public ElementInGame()
{
Console.WriteLine("ElementInGame class created");
}
public virtual int GetNumber()
{
return 10;
}
public void CountToTen()
{
for (int i = 0; i <= 10; i++)
{
Console.WriteLine(i);
}
}
}
}
然后我将我的 .txt 文件存储在 "C:\program.txt"
(我原来的 .txt 文件)
using System;
namespace Test
{
public class HelloWorld
{
public HelloWorld()
{
Console.WriteLine("Called Constructor() !");
}
public static int TestMethod()
{
Console.WriteLine("Called TestMethod() !");
return 11;
}
}
}
所以我编写主程序来读取.txt文件,限制编译,然后执行:
(我的 C# 程序 #2 在第二个 .cs 文件中,长代码警告)
using System;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using System.Reflection;
using System.Security.Permissions;
using System.Security;
using System.Security.Policy;
using System.Runtime.Remoting;
using System.Collections.Generic;
namespace Modding
{
public class Program : MarshalByRefObject
{
public static void Main(string[] args)
{
string assemblyPath = @"C:\program.txt"; // Where the .txt file is stored
string code = File.ReadAllText(assemblyPath); //The code to compile
CompilerResults compile = CompileFromCode(code); //Compile the code in the temporary files
string fullPath = compile.PathToAssembly; //sample : C:\Users\MY_USER_NAME\AppData\Local\Tempv2p3qki.dll
string pathWithoutFile = Path.GetDirectoryName(fullPath); //sample : C:\Users\MY_USER_NAME\AppData\Local\Temp
string pathNameOnly = Path.GetFileNameWithoutExtension(fullPath); //sample : 5v2p3qki
Program newDomainInstance = GetOtherProtectedDomainInstance(pathWithoutFile);
newDomainInstance.CallMethod(pathNameOnly, "Test.HelloWorld", "TestMethod", null, null);
newDomainInstance.CreateObject(pathNameOnly,"Test.HelloWorld");
List<ElementInGame> allElement = new List<ElementInGame>();
//allElement.Add ***?***
Console.ReadKey();
}
public static Program GetOtherProtectedDomainInstance(string pathWithoutFile)
{
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = pathWithoutFile;
//Set some permissions to avoid malicious code
PermissionSet permSet = new PermissionSet(PermissionState.None);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
StrongName fullTrustAssembly = new StrongName(
new StrongNamePublicKeyBlob(typeof(Program).Assembly.GetName().GetPublicKey()),
typeof(Program).Assembly.GetName().Name,
typeof(Program).Assembly.GetName().Version);
AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
ObjectHandle handle = Activator.CreateInstanceFrom(
newDomain, typeof(Program).Assembly.ManifestModule.FullyQualifiedName,
typeof(Program).FullName
);
Program newDomainInstance = (Program)handle.Unwrap();
return newDomainInstance;
}
public static CompilerResults CompileFromCode(string code)
{
//Compile the code in a .dll locate in the temporary files
//The following code is based on
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = false;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
//Adding a reference to the current project to allow the .txt file to inherit the class "ElementInGame" later
string[] references = { "System.dll", Assembly.GetEntryAssembly().Location };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
if (compile.Errors.HasErrors)
{
string text = "Compile error: ";
foreach (CompilerError ce in compile.Errors)
{
text += "rn" + ce.ToString();
}
throw new Exception(text);
}
return compile;
}
public static void DisplaySomething()//Useful for later
{
Console.WriteLine("This isn't supposed to be display");
}
//Calling a method from the restricted Domain
public void CallMethod(string assemblyName, string typeName, string entryPoint, object objectToExecute = null, object[] parameters = null)
{
MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
try
{
target.Invoke(objectToExecute, parameters);
}
catch
{
Console.WriteLine("Security Error with Method " + assemblyName + " namespace : " + typeName + " method : " + entryPoint);
}
}
//Create an instance from the restricted Domain
public void CreateObject(string assemblyName, string typeName)
{
try
{
object o = Assembly.Load(assemblyName).CreateInstance(typeName);
}
catch
{
Console.WriteLine("Security Error with Constructor " + assemblyName + " namespace : " + typeName);
}
}
}
}
目前 .txt 文件根本没有 link 我的 C# 程序。 代码正常工作,我得到以下输出:
Called TestMethod() !
Called Constructor() !
然后我在我的 .txt 文件中编辑我的代码以继承自 Modding.ElementInGame :
(我的 编辑的 .txt 文件)
using System;
namespace Test
{
public class HelloWorld : Modding.ElementInGame
{
public HelloWorld() : base()
{
Console.WriteLine("Called Constructor() !");
}
public static int TestMethod()
{
Console.WriteLine("Called TestMethod() !");
return 11;
}
}
}
所以我希望得到这样的输出:
Called TestMethod() !
ElementInGame class created
Called Constructor() !
但在进行此更改后,程序在调用 TestMethod 时崩溃,并带有 System.NullReferenceException
:newDomainInstance.CallMethod(pathNameOnly, "Test.HelloWorld", "TestMethod", null, null);
但是创建 HelloWorld 实例(.txt 文件):newDomainInstance.CreateObject(pathNameOnly,"Test.HelloWorld");
似乎有效(没有崩溃,代码在执行时留在 try 部分try/catch),但是我的 我的控制台中没有打印任何内容,所以我猜它不起作用?
更改 AppDomain 的权限没有任何改变。
PermissionSet permSet = new PermissionSet(PermissionState.Unrestricted);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags));
所以我的问题是:我怎样才能创建和存储我的程序中的 .txt 文件 从 ElementInGame 继承(并将其添加到 ElementInGame 列表中)?
这样我就可以在我的程序中使用虚拟方法 GetNumber()。我不希望 .txt 文件访问程序本身(比如调用 DisplaySomething() 方法),只需与 ElementInGame 通信。
您正在从不同位置生成和加载引用程序集。您确实为输出设置了当前目录,但忘记将其分配给编译器参数。
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.OutputAssembly = Path.Combine(outputDirectory, "Test.dll");
这应该可以解决问题。
我尝试在 C# 中创建一个简单的控制台改装项目,其中我的程序包含一个 抽象列表 class,名为 ElementInGame 。我希望能够创建其他 class 从 .txt 文件 继承 ElementInGame。 class ElementInGame 将包含一些基本方法(虚拟的和非虚拟的)。但我不希望这些其他修改的 class 执行恶意代码,我希望他们只能从继承的 class 访问 methods/properties。这是我的 ElementInGame 代码:
(我的 C# 程序 #1)
using System;
namespace Modding
{
//The class itself inherit from MarshalByRefObject to be available in 2 differents Domains
public abstract class ElementInGame : MarshalByRefObject
{
public ElementInGame()
{
Console.WriteLine("ElementInGame class created");
}
public virtual int GetNumber()
{
return 10;
}
public void CountToTen()
{
for (int i = 0; i <= 10; i++)
{
Console.WriteLine(i);
}
}
}
}
然后我将我的 .txt 文件存储在 "C:\program.txt"
(我原来的 .txt 文件)
using System;
namespace Test
{
public class HelloWorld
{
public HelloWorld()
{
Console.WriteLine("Called Constructor() !");
}
public static int TestMethod()
{
Console.WriteLine("Called TestMethod() !");
return 11;
}
}
}
所以我编写主程序来读取.txt文件,限制编译,然后执行:
(我的 C# 程序 #2 在第二个 .cs 文件中,长代码警告)
using System;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using System.Reflection;
using System.Security.Permissions;
using System.Security;
using System.Security.Policy;
using System.Runtime.Remoting;
using System.Collections.Generic;
namespace Modding
{
public class Program : MarshalByRefObject
{
public static void Main(string[] args)
{
string assemblyPath = @"C:\program.txt"; // Where the .txt file is stored
string code = File.ReadAllText(assemblyPath); //The code to compile
CompilerResults compile = CompileFromCode(code); //Compile the code in the temporary files
string fullPath = compile.PathToAssembly; //sample : C:\Users\MY_USER_NAME\AppData\Local\Tempv2p3qki.dll
string pathWithoutFile = Path.GetDirectoryName(fullPath); //sample : C:\Users\MY_USER_NAME\AppData\Local\Temp
string pathNameOnly = Path.GetFileNameWithoutExtension(fullPath); //sample : 5v2p3qki
Program newDomainInstance = GetOtherProtectedDomainInstance(pathWithoutFile);
newDomainInstance.CallMethod(pathNameOnly, "Test.HelloWorld", "TestMethod", null, null);
newDomainInstance.CreateObject(pathNameOnly,"Test.HelloWorld");
List<ElementInGame> allElement = new List<ElementInGame>();
//allElement.Add ***?***
Console.ReadKey();
}
public static Program GetOtherProtectedDomainInstance(string pathWithoutFile)
{
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = pathWithoutFile;
//Set some permissions to avoid malicious code
PermissionSet permSet = new PermissionSet(PermissionState.None);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
StrongName fullTrustAssembly = new StrongName(
new StrongNamePublicKeyBlob(typeof(Program).Assembly.GetName().GetPublicKey()),
typeof(Program).Assembly.GetName().Name,
typeof(Program).Assembly.GetName().Version);
AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
ObjectHandle handle = Activator.CreateInstanceFrom(
newDomain, typeof(Program).Assembly.ManifestModule.FullyQualifiedName,
typeof(Program).FullName
);
Program newDomainInstance = (Program)handle.Unwrap();
return newDomainInstance;
}
public static CompilerResults CompileFromCode(string code)
{
//Compile the code in a .dll locate in the temporary files
//The following code is based on
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = false;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
//Adding a reference to the current project to allow the .txt file to inherit the class "ElementInGame" later
string[] references = { "System.dll", Assembly.GetEntryAssembly().Location };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
if (compile.Errors.HasErrors)
{
string text = "Compile error: ";
foreach (CompilerError ce in compile.Errors)
{
text += "rn" + ce.ToString();
}
throw new Exception(text);
}
return compile;
}
public static void DisplaySomething()//Useful for later
{
Console.WriteLine("This isn't supposed to be display");
}
//Calling a method from the restricted Domain
public void CallMethod(string assemblyName, string typeName, string entryPoint, object objectToExecute = null, object[] parameters = null)
{
MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
try
{
target.Invoke(objectToExecute, parameters);
}
catch
{
Console.WriteLine("Security Error with Method " + assemblyName + " namespace : " + typeName + " method : " + entryPoint);
}
}
//Create an instance from the restricted Domain
public void CreateObject(string assemblyName, string typeName)
{
try
{
object o = Assembly.Load(assemblyName).CreateInstance(typeName);
}
catch
{
Console.WriteLine("Security Error with Constructor " + assemblyName + " namespace : " + typeName);
}
}
}
}
目前 .txt 文件根本没有 link 我的 C# 程序。 代码正常工作,我得到以下输出:
Called TestMethod() !
Called Constructor() !
然后我在我的 .txt 文件中编辑我的代码以继承自 Modding.ElementInGame :
(我的 编辑的 .txt 文件)
using System;
namespace Test
{
public class HelloWorld : Modding.ElementInGame
{
public HelloWorld() : base()
{
Console.WriteLine("Called Constructor() !");
}
public static int TestMethod()
{
Console.WriteLine("Called TestMethod() !");
return 11;
}
}
}
所以我希望得到这样的输出:
Called TestMethod() !
ElementInGame class created
Called Constructor() !
但在进行此更改后,程序在调用 TestMethod 时崩溃,并带有 System.NullReferenceException
:newDomainInstance.CallMethod(pathNameOnly, "Test.HelloWorld", "TestMethod", null, null);
但是创建 HelloWorld 实例(.txt 文件):newDomainInstance.CreateObject(pathNameOnly,"Test.HelloWorld");
似乎有效(没有崩溃,代码在执行时留在 try 部分try/catch),但是我的 我的控制台中没有打印任何内容,所以我猜它不起作用?
更改 AppDomain 的权限没有任何改变。
PermissionSet permSet = new PermissionSet(PermissionState.Unrestricted);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags));
所以我的问题是:我怎样才能创建和存储我的程序中的 .txt 文件 从 ElementInGame 继承(并将其添加到 ElementInGame 列表中)?
这样我就可以在我的程序中使用虚拟方法 GetNumber()。我不希望 .txt 文件访问程序本身(比如调用 DisplaySomething() 方法),只需与 ElementInGame 通信。
您正在从不同位置生成和加载引用程序集。您确实为输出设置了当前目录,但忘记将其分配给编译器参数。
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.OutputAssembly = Path.Combine(outputDirectory, "Test.dll");
这应该可以解决问题。