Sandboxing untrusted code in c#, Security Permissions seem not working - c#

This is my code:
System.Security.PermissionSet PS = new System.Security.PermissionSet(PermissionState.None);
PS.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess,Path));
PS.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
AppDomainSetup ADS = new AppDomainSetup();
ADS.ApplicationBase= Path;
AppDomain domain = AppDomain.CreateDomain("Pluging", null, ADS, PS, null);
Assembly asm = Assembly.LoadFrom(Path + "MacroBase.dll");
domain.Load(asm.FullName);
MacroBase.MacroBase em = (MacroBase.MacroBase)domain.CreateInstanceAndUnwrap(asm.FullName, "MacroBase.MacroBase");
em.Application(1);
parameter Path has the address of floder that contains dll. Right now it is
"D:\Programming Projects\Server3\Macros\c7b465b2-8314-4c7e-be3c-10c0185b4ac6"
a copy of macrobase.dll is inside that Guid folder. Appdomain loads this dll and runs the method Application.
I expected the last line not to be able to access c:\ due to FileIOPermissionAccess that was applied at the beginning, but the mentioned method:
MacroBase.Application(int i)
{
System.IO.File.ReadAllBytes("c:\\test1_V.103.xls");
}
runs as if it was fully unrestricted.
based on this article from Microsoft:
How to: Run Partially Trusted Code in a Sandbox
I have also tried the following format with no better results(It can access c:):
System.Security.PermissionSet PS = new System.Security.PermissionSet(PermissionState.None);
PS.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess,Path));
PS.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
AppDomainSetup ADS = new AppDomainSetup();
ADS.ApplicationBase= Path;
AppDomain domain = AppDomain.CreateDomain("Pluging", null, ADS, PS, null);
Assembly asm = Assembly.LoadFrom(Path + "MacroBase.dll");
domain.Load(asm.FullName);
System.Runtime.Remoting.ObjectHandle handle = Activator.CreateInstanceFrom(domain, Path + "MacroBase.dll", "MacroBase.MacroBase");
MacroBase.MacroBase m = (MacroBase.MacroBase)handle.Unwrap();
m.Application(1);
MacroBase.Macrobase is a placeholder for future macros. It is placed inside a dll called macrobase.dll . Right now it only contains some dummy code:
namespace MacroBase
{
[Serializable]
public class MacroBase
{
public void Application(int i)
{
List<int> i1 = new System.Collections.Generic.List<int>() { 1,2,3,4};
System.IO.File.ReadAllBytes("c:\\test1_V.103.xls");
switch(i)
{
case 0:
break;
case 1:
break;
default:
break;
}
}
}
}

Your class marked as [Serializable] and does not derive from MarshalByRefObject, that means when instance get thru Application domain boundaries, it get serialized and than deserialized in target domain. So your code get executed in your current domain rather then in separate domain. You should derive your MacroBase.Macrobase class from MarshalByRefObject, to make code to be executed in separate domain.

Related

Assembly reflection loading and app domain conflict

I am trying to make a small C# tool to compare two svn revision builds and track properties changes in any classes. My goal is to use reflection to compare the properties of each class of my dll without using Momo.Cecil.
From experimenting, then reading this article Assembly Loading and a few threads found on Google, I learnt that two DLLs with same identities would get resolved as the same if we dont use Assembly.ReflectionOnlyLoadFrom.
Trying to use their code to create an AppDomain for each loading, and also try many variants from searches, I get this exception and I can't find any thread explaining how to solve this issue:
API restriction: The assembly 'file:///D:\somepath\82\MyLib.dll' has already loaded from a different location. It cannot be loaded from a new location within the same appdomain.
This error happen on the 2nd call (the path 82) on the following line:
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Maybe I din't understand something very basic which make me unable to create a new AppDomain correctly?
Here is all the code used to reproduce this problem.
Code from my entry point
//Let say one of the property has been renamed between both commits
var svnRev81Assembly = ReflectionOnlyLoadFrom(#"D:\somepath\81\MyLib.dll");
var svnRev82Assembly = ReflectionOnlyLoadFrom(#"D:\somepath\82\MyLib.dll");
Implementation of the loader
private string _CurrentAssemblyKey;
private Assembly ReflectionOnlyLoadFrom(string assemblyPath)
{
var path = Path.GetDirectoryName(assemblyPath);
// Create application domain setup information.
AppDomainSetup domaininfo = new AppDomainSetup();
domaininfo.ApplicationBase = path;
domaininfo.PrivateBinPath = path;
_CurrentAssemblyKey = Guid.NewGuid().ToString();
AppDomain currentAd = AppDomain.CreateDomain(_CurrentAssemblyKey, null, domaininfo); //Everytime we create a new domain with a new name
//currentAd.ReflectionOnlyAssemblyResolve += CustomReflectionOnlyResolver; This doesnt work anymore since I added AppDomainSetup
currentAd.SetData(_CurrentAssemblyKey, path);
//Loading to specific location - folder 81 or 82
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
//Preloading the
foreach (var assemblyName in assembly.GetReferencedAssemblies())
{
Assembly.ReflectionOnlyLoadFrom(Path.Combine(path, assemblyName.Name + ".dll"));
}
Type[] types = assembly.GetTypes();
// Lastly, reset the ALS entry and remove our handler
currentAd.SetData(_CurrentAssemblyKey, null);
//currentAd.ReflectionOnlyAssemblyResolve -= CustomReflectionOnlyResolver; This doesnt work anymore since I added AppDomainSetup
return assembly;
}
This can solved by loading dlls in seperate appdomain.
private static void ReflectionOnlyLoadFrom()
{
var appDomain = AppDomain.CreateDomain("Temporary");
var loader1 = (Loader)appDomain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
loader1.Execute(#"D:\somepath\81\MyLib.dll");
var loader2 = (Loader)appDomain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
loader2.Execute(#"D:\somepath\82\MyLib.dll");
loader1.CompareTwoDLLs(loader2);
AppDomain.Unload(appDomain);
}
Loader.cs
public class Loader : MarshalByRefObject
{
public Assembly TempAssembly { get; set; }
public string Execute(string dllPath)
{
TempAssembly = Assembly.LoadFile(dllPath);
return TempAssembly.FullName;
}
public void GetRefAssemblyTypes()
{
foreach (var refAssembly in TempAssembly.GetReferencedAssemblies())
{
var asm = Assembly.Load(refAssembly);
var asmTypes = asm.GetTypes();
}
}
public void CompareTwoDLLs(Loader l2)
{
var types1 = TempAssembly.GetTypes();
var types2= l2.TempAssembly.GetTypes();
GetRefAssemblyTypes();
//logic to return comparison result
}
}

C# Unload temp Appdomain while main Appdomain uses MethodInfo from temp

I want to make a reloadable assembly function for scripting.(So i can debug scripts quicker)
The dll generation works and loading too. The main problem is, that I`m using the functions of my temporary AppDomain in my main AppDomain. The dll seems to be linked to my main AppDomain too, because I cant delete it while the program is running.
If I remove all MethodInfo references from my main AppDomain "context" then I have no problems deleting it.
Here you can see how the program works:
Generate DLL from external process
Load DLL by (temp AppDomain).DoCallBack(...)
Get Type & MethodInfo and call it.
AppDomain.Unload(temp AppDomain)
So if I skip step 3, I have no problems deleten the dll. But I cant check if the return of the Function really shows the updated value (which i edit inside the script).
I have posted the source code for each step here:
1.
//This actually isn`t as important for you
Process assemblyGeneration = new Process();
assemblyGeneration.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + #"lib\AssemblyCompiler.exe";
assemblyGeneration.StartInfo.Arguments = "\"" + AppDomain.CurrentDomain.BaseDirectory + "script.dll\" \"" + source + "\" \"System.dll\"";
assemblyGeneration.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
assemblyGeneration.StartInfo.UseShellExecute = true;
assemblyGeneration.Start();
assemblyGeneration.WaitForExit();
assemblyGeneration.Dispose();
2.
_appDomain.DoCallBack(() =>
{
byte[] fileContent = File.ReadAllBytes(AppDomain.CurrentDomain.BaseDirectory + "script.dll");
AppDomain.CurrentDomain.Load(fileContent);
});
3.
MethodInfo info = _compiledScript.GetTypeFrom("LSCT.ScriptClass").GetMethod("DefineX");
var func = (Func<double>) Delegate.CreateDelegate(typeof(Func<double>), info);
double x = func();
Console.WriteLine("Output was : " + x);
4.
AppDomain.Unload(_appDomain);
So is there a way to work around the issue, that I cant reload the DLL?
I think I have solved the problem. Maybe there is a performance issue now but I have not checked it yet.
Anyways, I created a new class which inherit MarshalByRefObject and let it load from the temp AppDomain.
Here is the code for the new class:
public class ProcessRunner : MarshalByRefObject
{
public void Run()
{
Type typ = null;
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
Type t = asm.GetType("LSCT.ScriptClass");
if (t != null)
{
typ = t;
break;
}
}
MethodInfo info = typ.GetMethod("DefineX");
var func = (Func<double>)Delegate.CreateDelegate(typeof(Func<double>), info);
double x = func();
Console.WriteLine("Output was : " + x);
}
}
And here is how to invoke it from the temp AppDomain:
ProcessRunner pr = (ProcessRunner)_appDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, "Animation_Engine.UserInterface.Code.ProcessRunner");
pr.Run();
Note: As soon as you pass any references from your main AppDomain (by
parameters for example) to the executing class, the dll will be linked to your main AppDomain
and is not able to unload until the main AppDomain ends.

Shadow copy with AppDomain to overwrite exe at runtime

In the following sample app I create a new AppDomain and I execute it with shadow copy enabled. From the new AppDomain I then try to delete (replace) the original main exe. However I get an "access is denied error". Interestingly, after launching the program, from Windows Explorer it is possible to rename the main exe (but not to delete it).
Can shadow copy work for runtime overwriting of the main exe?
static void Main(string[] args)
{
// enable comments if you wanna try to overwrite the original exe (with a
// copy of itself made in the default AppDomain) instead of deleting it
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
Console.WriteLine("I'm the default domain");
System.Reflection.Assembly currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
string startupPath = currentAssembly.Location;
//if (!File.Exists(startupPath + ".copy"))
// File.Copy(startupPath, startupPath + ".copy");
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = Path.GetFileName(startupPath);
setup.ShadowCopyFiles = "true";
AppDomain domain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup);
domain.SetData("APPPATH", startupPath);
domain.ExecuteAssembly(setup.ApplicationName, args);
return;
}
Console.WriteLine("I'm the created domain");
Console.WriteLine("Replacing main exe. Press any key to continue");
Console.ReadLine();
string mainExePath = (string)AppDomain.CurrentDomain.GetData("APPPATH");
//string copyPath = mainExePath + ".copy";
try
{
File.Delete(mainExePath );
//File.Copy(copyPath, mainExePath );
}
catch (Exception ex)
{
Console.WriteLine("Error! " + ex.Message);
Console.ReadLine();
return;
}
Console.WriteLine("Succesfull!");
Console.ReadLine();
}
You can achive self updating application within a single application with multiple AppDomains. The trick is move application executable to a temporary directory and copy back to your directory, then load the copied executable in a new AppDomain.
static class Program
{
private const string DELETED_FILES_SUBFOLDER = "__delete";
/// <summary>
/// The main entry point for the application.
/// </summary>
[LoaderOptimization(LoaderOptimization.MultiDomainHost)]
[STAThread]
static int Main()
{
// Check if shadow copying is already enabled
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
// Get the startup path.
string assemblyPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
string assemblyFile = Path.GetFileName(assemblyPath);
// Check deleted files folders existance
string deletionDirectory = Path.Combine(assemblyDirectory, DELETED_FILES_SUBFOLDER);
if (Directory.Exists(deletionDirectory))
{
// Delete old files from this folder
foreach (var oldFile in Directory.EnumerateFiles(deletionDirectory, String.Format("{0}_*{1}", Path.GetFileNameWithoutExtension(assemblyFile), Path.GetExtension(assemblyFile))))
{
File.Delete(Path.Combine(deletionDirectory, oldFile));
}
}
else
{
Directory.CreateDirectory(deletionDirectory);
}
// Move the current assembly to the deletion folder.
string movedFileName = String.Format("{0}_{1:yyyyMMddHHmmss}{2}", Path.GetFileNameWithoutExtension(assemblyFile), DateTime.Now, Path.GetExtension(assemblyFile));
string movedFilePath = Path.Combine(assemblyDirectory, DELETED_FILES_SUBFOLDER, movedFileName);
File.Move(assemblyPath, movedFilePath);
// Copy the file back
File.Copy(movedFilePath, assemblyPath);
bool reload = true;
while (reload)
{
// Create the setup for the new domain
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = assemblyFile;
setup.ShadowCopyFiles = true.ToString().ToLowerInvariant();
// Create an application domain. Run
AppDomain domain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup);
// Start application by executing the assembly.
int exitCode = domain.ExecuteAssembly(setup.ApplicationName);
reload = !(exitCode == 0);
AppDomain.Unload(domain);
}
return 2;
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm mainForm = new MainForm();
Application.Run(mainForm);
return mainForm.ExitCode;
}
}
}
As it's an interesting use case of MEF, I've bashed out a quick demo of how to hot-swap running code in C#. This is very simple and leaves out a lot of edge cases.
https://github.com/i-e-b/MefExperiments
Notable classes:
src/PluginWatcher/PluginWatcher.cs -- watches a folder for new implementations of a contract
src/HotSwap.Contracts/IHotSwap.cs -- minimal base contract for a hot-swap
src/HotSwapDemo.App/Program.cs -- Does the live code swap
This doesn't lock the task .dlls in the Plugins folder, so you can delete old versions once new ones are deployed.
Hope that helps.
You asked specifically to use ShadowCopy for the update process. If that (and why would it be?) not a fixed requirement, these ones were real eye openers for me:
https://visualstudiomagazine.com/articles/2017/12/15/replace-running-app.aspx
https://www.codeproject.com/Articles/731954/Simple-Auto-Update-Let-your-application-update-i
It comes down to you renaming the target file (which is allowed, even when it is locked since it is running) and then moving/copying the desired file to the now freed destination.
The vs-magazine article is very detailed, including some nifty tricks like finding out if a file is in use by current application (though only for exe, for .dlls and others one has to come up with a solution).

MEF load plugin from directory

I work with MEF and I am looking how to change the url of the location of plugins by another means that MEF find the plugins, I want to change this line
Assembly.LoadFrom(#"C:\julia\project\project.Plugin.Nav\bin\Debug\NavPlugin.dll")));
I want to delete this url because I need to deploy my application in another machine
This is my function :
public void AssembleCalculatorComponents()
{
try
{
//var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
//var container = new CompositionContainer(catalog);
//container.ComposeParts(this);
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFrom(#"C:\yosra\project\project.Plugin.Nav\bin\Debug\NavPlugin.dll")));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
catch (Exception ex)
{
throw ex;
}
}
Can you please help me?
Thanks
You can use the DirectoryCatalog class to have MEF scan a particular directory for assemblies that satisfy your imports.
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog("."));
var container = new CompositionContainer(catalog);
The above will add the base directory the AppDomain uses for locating assemblies (usually the directory holding the executable unless changed with configuration) to the aggregate catalog. You will also probably want to add the current executing assembly, though it is not required.
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog("."));
var container = new CompositionContainer(catalog);
More information on MSDN for DirectoryCatalog: http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.hosting.directorycatalog.aspx
Hello again and thanks for your response, so my problem was to load the plugin directly, so i create a directory and i place my plugins in this folder, so i find this solution
public void AssembleCalculatorComponents()
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
Console.WriteLine(path);
//Check the directory exists
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
Console.WriteLine(path);
string assemblyName = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(assemblyName);
//Create an assembly catalog of the assemblies with exports
var catalog = new AggregateCatalog(
new AssemblyCatalog(Assembly.GetExecutingAssembly().Location),
new AssemblyCatalog(Assembly.Load(assemblyName)),
new DirectoryCatalog(path, "*.dll"));
//Create a composition container
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
this is my solution , thinks for all
Two options.
Use the current directory as the root for your plugins. Environment.CurrentDirectory should point you in the right direction.
Use an app.config to specify a directory for your plugins to be stored in.
If your method knows of the type to be instantiated, you can use the Assembly property of the Type which is typical for my *Domain* libraries; otherwise, you can use Assembly.GetExecutingAssembly(). I am not particularly fond of GetExecutingAssembly() or GetCallingAssembly() ...:
public void AssembleCalculatorComponents() {
try {
var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
// OR
asmCatalog = new AssemblyCatalog(typeof(TObject).Assembly); // replace TObject with object's actual type
var aggregateCatalog = new AggregateCatalog(asmCatalog);
//
AddDirectoryCatalogs(aggregateCatalog.Catalogs));
var container = new CompositionContainer(catalog);
// assuming this class has the member(s) to be composed.
container.ComposeParts(this);
} catch (Exception ex) {
throw ex;
}
}
Aside from that, you can add DirectoryCatalogs - either by the app.config file, serialized list of directories, etc. Starting off, though, you can designate a default directory in your app.config - which is what I recommend. Then, assuming you are using Settings:
private readonly Settings settings = Settings.Default;
void AddDirectoryCatalogs(ICollection<ComposablePartCatalog> Catalogs agrCatalogs ) {
agrCatalogs.Add(new DirectoryCatalog(settings.DefaultPath, settings.DefaultPattern));
// add more directory catalogs
}
While using "." as the search path is a legitimate shortcut to the executing assembly's directory, keep in mind that all assemblies will be searched for fulfilling parts - i.e., matching Import/Export objects. Use of specific patterns is my recommendation. Microsoft's examples are not known for being the best practices. If you expect any plugin to be suffixed with Plugin.dll, make that part of your search pattern.

AppDomain shadow copying not working (original assemblies locked)

Here's a small class I'm using to probe for a list of available plugins:
internal static class PluginDirectoryLoader
{
public static PluginInfo[] ListPlugins(string path)
{
var name = Path.GetFileName(path);
var setup = new AppDomainSetup
{
ApplicationBase = path,
ShadowCopyFiles = "true"
};
var appdomain = AppDomain.CreateDomain("PluginDirectoryLoader." + name, null, setup);
var exts = (IServerExtensionDiscovery)appdomain.CreateInstanceAndUnwrap("ServerX.Common", "ServerX.Common.ServerExtensionDiscovery");
PluginInfo[] plugins = null;
try
{
plugins = exts.ListPlugins(); // <-- BREAK HERE
}
catch
{
// to do
}
finally
{
AppDomain.Unload(appdomain);
}
return plugins ?? new PluginInfo[0];
}
}
The path parameter points to a subdirectory containing the plugin assemblies to load. The idea is to load them using a separate AppDomain with shadow copying enabled.
In this case, shadow copying isn't really necessary seeing as the AppDomain is unloaded quickly, but when I actually load the plugins in the next block of code I intend to write, I want to use shadow copying so the binaries can be updated on the fly. I have enabled shadow copying in this class as a test to make sure I'm doing it right.
Apparently I'm not doing it right because when I break in the debugger on the commented line in the code sample (i.e. plugins = exts.ListPlugins()), the original plugin assemblies are locked by the application!
Seeing as I'm specifying that assemblies loaded by the AppDomain should be shadow copied, why are they being locked by the application?
I figured it out. There was one property I missed in AppDomainSetup. The property was ShadowCopyDirectories.
var setup = new AppDomainSetup
{
ApplicationBase = path,
ShadowCopyFiles = "true",
ShadowCopyDirectories = path
};
When breaking on the line mentioned in my question, I can now delete the plugin assemblies even without unloading the AppDomain.

Categories