具有动态加载的.Net程序集的二进制序列化

问题描述 投票:4回答:2

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

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

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

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

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

如何通知序列化器/解串器使用动态加载的程序集?

c# .net serialization .net-assembly
2个回答
2
投票
二进制序列化对DLL Hell毫无废话。它记录了序列化数据时包含该类型的确切程序集。然后

insists查找反序列化数据时的确切程序集。确保序列化数据与类型匹配的唯一方法是采用任何快捷方式只能确保在不幸运的情况下获得垃圾数据,而在其他情况下则获得异常。早晚发生这种情况的几率是100%。

因此,您需要完全放弃可以使用“动态加载的程序集”并使它“反序列化任何类型的类”的想法,这是一种幻想。您可以旋转命运之轮,然后在app.exe.config文件中放置<bindingRedirect>以强制CLR使用其他程序集版本。处理事故现在是您的责任。许多程序员抓住了机会,很少有经验教训而又没有学习新的课程。必须意识到后果。因此,继续。

1
投票
假设您正在通过Assembly.Load()Assembly.Load()加载程序集,然后如Assembly.LoadFrom()Assembly.LoadFrom()

this answer中所述,可以使用SerializationException for dynamically loaded Type事件加载动态程序集在反序列化期间。但是,出于安全原因,您将要防止加载完全意外的程序集。

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

Chris Shain

然后在启动时的某个位置,将适当的AppDomain.AssemblyResolve添加到AppDomain.AssemblyResolve,例如如下:

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并将其附加到class Program { const string assemblyFullPath = @"H:\Users\dbc\Documents\Visual Studio 2008\Projects\Question18881659\Question18881659\bin\Debug\Question18881659.dll"; const string assemblyName = @"Question18881659, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; static Program() { AppDomain.CurrentDomain.AssemblyResolve += new AssemblyResolver(assemblyName, assemblyFullPath).AssemblyResolve; } 。在ResolveEventHandler中,绑定器需要检查属于动态程序集的类型,并适当地绑定到它们。这里的技巧是处理动态加载的类型嵌套在另一个程序集中的泛型中的情况,例如一个ResolveEventHandler。在这种情况下,SerializationBinder将是SerializationBinder的程序集的名称,而不是BinaryFormatter.Binder。有关如何执行此操作的详细信息,请参见

  • BinaryFormatter.Binder
  • BindToType (string assemblyName, string typeName)
  • BindToType (string assemblyName, string typeName)中,List<MyClass>问道,

    我想知道为什么不能像在项目中显式引用带符号的程序集那样,仅以assemblyName而不是其他方式来反序列化流。

    虽然我不知道Microsoft员工在设计这些类时的想法(返回List<T>,可能是因为:

    formatter.Deserialize(stream)
    但是如果流实际上包含序列化的攻击小工具,例如.Net 1.1,则将创建并填充该小工具,并且攻击将受到影响。

    (有关此类攻击的详细信息,请参见

    [no one ever designed, specified, implemented, tested, documented and shipped that feature.

    BinaryFormatterAssembly.Load()和AlvaroMuñoz&Oleksandr Mirosh的黑帽论文Assembly.Load()。这些链接指定了如何修改Json.NET的配置以启用此类攻击; BinaryFormatter在默认配置下容易受到攻击。)
    现在,如果BinaryFormatter在无法识别的程序集名称上自动调用var instance = (MyClass)BinaryFormatter().Deserialize(stream); ,则使用该应用程序的应用可能另外容易受到

    TempFileCollection

    的攻击,其中来自攻击DLL的攻击类型会意外地从某个意外位置加载而不是安全的位置,进一步加剧了攻击风险。(顺便说一下,如果您确实选择编写自己的TempFileCollection,则可以过滤出意外类型和/或已知的攻击类型,从而降低了攻击小工具注入的风险。由于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可能遇到的其他问题。

    © www.soinside.com 2019 - 2024. All rights reserved.