具有动态加载的.Net程序集的二进制序列化 [英] Binary serialization with dynamically loaded .Net assembly

查看:65
本文介绍了具有动态加载的.Net程序集的二进制序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将类的实例序列化为文件(使用 BinaryFormatter

I serialized an instance of my class into a file (with BinaryFormatter)

之后,在另一个项目中,我想反序列化此文件,但是它没有用,因为我的新项目没有我的旧班级的描述。 .Deserialize()出现异常

After, in another project, I wanted to deserialize this file, but it did not work because my new project does not have the description of my old class. The .Deserialize() gets an exception

Unable to find assembly '*MyAssembly, Version=1.9.0.0, Culture=neutral, PublicKeyToken=null'.*".

但是我有程序集的.DLL,其中包含我想反序列化的旧类的描述。

But I have the .DLL of the assembly containing a description of the old class which I want to deserialize.

我不想在项目中添加一个此DLL的引用(我希望能够反序列化任何类型的程序集的类...)

I don't want to add a reference a this DLL in the project (I want be able to deserialize a class of any kind of assembly...)

如何通知Serializer / Deserializer可以使用我的动态加载的程序集?

How can I inform the Serializer/Deserializer to use my dynamically loaded assembly?

推荐答案

假设您正在通过 Assembly.Load() Assembly.LoadFrom() ,然后按expl 此答案 Chris Shain 的动态加载的Type 的SerializationException,您可以使用< a href = https://msdn.microsoft.com/zh-cn/library/system.appdomain.assemblyresolve.aspx rel = nofollow noreferrer> AppDomain.AssemblyResolve 事件,以在反序列化期间加载动态程序集。但是,出于安全原因,您将要防止加载完全意外的程序集。

Assuming you are loading your assembly via Assembly.Load() or Assembly.LoadFrom(), then as explained in this answer to SerializationException for dynamically loaded Type by Chris Shain, you can use the AppDomain.AssemblyResolve event to load your dynamic assembly during deserialization. However, for security reasons you will want to prevent loading of entirely unexpected assemblies.

一种可能的实现方式是引入以下内容:

One possible implementation would be to introduce the following:

public class AssemblyResolver
{
    readonly string assemblyFullPath;
    readonly AssemblyName assemblyName;

    public AssemblyResolver(string assemblyName, string assemblyFullPath)
    {
        // You might want to validate here that assemblyPath really is an absolute not relative path.
        // See e.g. https://stackoverflow.com/questions/5565029/check-if-full-path-given
        this.assemblyFullPath = assemblyFullPath;
        this.assemblyName = new AssemblyName(assemblyName);
    }

    public ResolveEventHandler AssemblyResolve
    {
        get
        {
            return (o, a) =>
                {
                    var name = new AssemblyName(a.Name);
                    if (name.Name == assemblyName.Name) // Check only the name if you want to ignore version.  Otherwise you can just check string equality.
                        return Assembly.LoadFrom(assemblyFullPath);
                    return null;
                };
        }
    }
}

然后,在创业公司的某个地方,将适当的 ResolveEventHandler 添加到 AppDomain.CurrentDomain.AssemblyResolve 例如如下:

Then, somewhere in startup, add an appropriate ResolveEventHandler to AppDomain.CurrentDomain.AssemblyResolve e.g. as follows:

class Program
{
    const string assemblyFullPath = @"C:\Full-path-to-my-assembly\MyAssembly.dll";
    const string assemblyName = @"MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";

    static Program()
    {
        AppDomain.CurrentDomain.AssemblyResolve += new AssemblyResolver(assemblyName, assemblyFullPath).AssemblyResolve;
    }

ResolveEventHandler 检查所请求的程序集是否具有动态程序集的名称,如果是这样,则从预期的完整路径加载当前版本。

This ResolveEventHandler checks to see whether the requested assembly has your dynamic assembly's name, and if so, loads the current version from the expected full path.

一种替代方法是编写自定义 SerializationBinder 并将其附加到 BinaryFormatter.Binder 。在 BindToType(字符串assemblyName,字符串typeName) 活页夹将需要检查属于您的动态程序集的类型,并适当地绑定到它们。这里的技巧是处理动态加载的类型嵌套在另一个程序集中的泛型中的情况,例如 List< MyClass> 。在这种情况下, assemblyName 将是 List< T> 的程序集的名称,而不是 MyClass 。有关如何执行此操作的详细信息,请参见

An alternative would be to write a custom SerializationBinder and attach it to BinaryFormatter.Binder. In BindToType (string assemblyName, string typeName) the binder would need to check for types belonging to your dynamic assembly, and bind to them appropriately. The trick here is dealing with situations in which your dynamically loaded types are nested in a generic from another assembly, e.g. a List<MyClass>. In that case assemblyName will be the name of the assembly of List<T> not MyClass. For details on how to do this see

评论 @sgnsajgon 问,我想知道为什么不能像在项目中显式引用签名程序集时那样,对流进行反序列化吗? code> formatter.Deserialize(stream)没什么。

In comments @sgnsajgon asked, I wonder why I cannot deserialize stream the same way I would do when signed assembly is explicitly referenced in project - just formatter.Deserialize(stream) and nothing else.

虽然我不知道微软的员工是什么在设计这些类的时候在想( .Net 1.1 ),可能是因为:

While I don't know what Microsoft employees were thinking when they designed these classes (back in .Net 1.1), it might be because:

BinaryFormatter 安全性已经有些垃圾了,但是自动调用 Assembly.Load( ) 放在 BinaryFormatter 流中任何意外的程序集名称上,可能会使情况变得更糟。

BinaryFormatter security is already somewhat of a dumpster fire, but automatically calling Assembly.Load() on any unexpected assembly name in a BinaryFormatter stream might make things even worse.

通过垃圾箱大火,我的意思是说,现成的 BinaryFormatter 将实例化并填充其中指定的类型输入流可能不是您期望的类型。因此,您可以

By "dumpster fire" I mean that, out-of-the-box, BinaryFormatter will instantiate and populate the types specified in the input stream which might not be the types you are expecting. Thus you might do

var instance = (MyClass)new BinaryFormatter().Deserialize(stream);

但是,如果流实际上包含序列化的攻击小工具,例如 TempFileCollection ,然后将创建并填充该小工具,然后进行攻击。

But if the stream actually contains a serialized attack gadget such as TempFileCollection then the gadget will be created and populated and the attack will be effected.

(有关此类攻击的详细信息,请参见 Newtonsoft Json中的TypeNameHandling警告 外部json容易受Json.Net TypeNameHandling自动影响吗? 如何配置Json.NET来创建易受攻击的Web API 和AlvaroMuñoz& ; Oleksandr Mirosh的blackhat论文> https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th -JSON-Attacks-wp.pdf 。这些链接指定如何修改Json.NET的配置以启用此类攻击。 BinaryFormatter 在默认配置下容易受到攻击。)

(For details on this sort of attack, see TypeNameHandling caution in Newtonsoft Json, External json vulnerable because of Json.Net TypeNameHandling auto?, How to configure Json.NET to create a vulnerable web API and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf. Those links specify how to modify Json.NET's configuration to enable such attacks; BinaryFormatter is vulnerable to them in default configuration.)

现在,如果 BinaryFormatter 会在无法识别的程序集名称上自动调用 Assembly.Load(),使用它的应用可能还会容易受到 DLL植入攻击 ,其中攻击DLL的攻击类型将从意外位置而不是安全位置意外加载,进一步加剧了攻击风险。

Now if BinaryFormatter were automatically calling Assembly.Load() on unrecognized assembly names, apps using it might additionally become vulnerable to a DLL planting attack where attack types from attack DLLs would get unexpectedly loaded from some unexpected location rather than a secure location, further exacerbating the attack risk.

(顺便说一句,如果您选择编写自己的 SerializationBinder ,您可以过滤出意外的类型和/或已知的攻击类型,从而降低攻击小工具注入的风险,这也可能比预期的要难,因为 BinaryFormatter 流通常包含您可能不知道不允许的序列化私有或内部类。)

(Incidentally, if you do choose to write your own SerializationBinder you can filter out unexpected types and/or known attack types, thus reducing the risk of attack gadget injection. This also can be harder than expected since BinaryFormatter streams often include serialized private or internal classes that you may not know to allow.)

顺便说一句, 内置BinaryFormatter的不足之处是什么 基于.net的序列化? 提供了使用 BinaryFormatter 可能遇到的其他问题的有用概述。

As an aside, What are the deficiencies of the built-in BinaryFormatter based .Net serialization? gives a useful overview of other problems you might encounter using BinaryFormatter.

这篇关于具有动态加载的.Net程序集的二进制序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆