我将类的一个实例序列化为一个文件(使用BinaryFormatter)
之后,在另一个项目中,我想反序列化这个文件,但是它没有工作,因为我的新项目没有我以前的类的描述。.Deserialize()得到一个异常
Unable to find assembly '*MyAssembly, Version=1.9.0.0, Culture=neutral, PublicKeyToken=null'.*".但是,我有程序集的.DLL,其中包含了我想反序列化的旧类的描述。
我不想在项目中添加引用这个DLL (我希望能够反序列化任何类型的程序集.)
如何通知序列化程序/反序列化程序使用动态加载的程序集?
发布于 2019-12-10 21:19:24
假设您是通过Assembly.Load()或Assembly.LoadFrom()加载程序集,然后按照这个答案 to https://stackoverflow.com/q/9162279/3744182 by 克里斯·肖恩中的解释,您可以在反序列化期间使用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;
};
}
}
}然后,在启动的某个地方,向AppDomain.CurrentDomain.AssemblyResolve添加一个适当的AppDomain.CurrentDomain.AssemblyResolve,例如:
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检查请求的程序集是否具有动态程序集的名称,如果有,则从预期的完整路径加载当前版本。
另一种选择是编写自定义SerializationBinder并将其附加到BinaryFormatter.Binder。在BindToType (string assemblyName, string typeName)中,绑定程序需要检查属于动态程序集的类型,并将它们适当地绑定到它们。这里的诀窍是处理动态加载的类型嵌套在来自另一个程序集的泛型中的情况,例如List<MyClass>。在这种情况下,assemblyName将是List<T>而不是MyClass的程序集的名称。有关如何执行此操作的详细信息,请参阅
在评论 @sgnsajgon问询中,我想知道为什么我不能像在项目中显式引用签名程序集时那样反序列化流--只有formatter.Deserialize(stream)而没有其他东西。
虽然我不知道微软员工在设计这些类(回到.Net 1.1中)时在想什么,但这可能是因为:
BinaryFormatter安全性已经有点像垃圾箱火灾了,但是对BinaryFormatter流中任何意外的程序集名称自动调用Assembly.Load()可能会使事情变得更糟。
所谓“垃圾箱火”,我的意思是,开箱即用,BinaryFormatter将实例化并填充输入流中指定的类型,这些类型可能不是您所期望的类型。所以你可以这样做
var实例=(MyClass)新的BinaryFormatter().Deserialize(流);
但是,如果流实际上包含一个序列化的攻击小工具(如TempFileCollection ),那么将创建并填充该小工具,并进行攻击。
(有关这类攻击的详细信息,请参阅https://stackoverflow.com/q/39565954、https://stackoverflow.com/q/49038055、https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-vulnerable-web-API.html和Alvaro Mu oz& Oleksandr Mirosh的黑帽纸https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf。这些链接指定如何修改Json.NET的配置以启用此类攻击;BinaryFormatter在默认配置中易受攻击。)
现在,如果BinaryFormatter以无法识别的程序集名称自动调用Assembly.Load(),那么使用它的应用程序可能还会受到DLL植入攻击的攻击,来自攻击DLL的攻击类型会意外地从某个意外位置加载,而不是从安全位置加载,从而进一步加剧攻击风险。
(顺便说一句,如果您选择编写自己的SerializationBinder,您可以筛选出意外的类型和/或已知的攻击类型,从而降低攻击小工具注入的风险。这也可能比预期的更难,因为BinaryFormatter流通常包括您可能不知道允许的序列化的私有或内部类。)顺便提一下,https://stackoverflow.com/q/703073/3744182对使用BinaryFormatter可能遇到的其他问题提供了有用的概述。
发布于 2013-09-18 20:54:22
二进制序列化对于DLL来说不是胡说八道.它记录数据序列化时包含类型的确切程序集。并坚持在反序列化数据时找到确切的程序集。确保序列化数据与类型匹配的唯一方法,采用任何快捷方式只会确保在幸运时获得异常,而在不符合条件时则会得到垃圾数据。这种情况迟早会发生的几率是100%。
因此,您需要彻底放弃可以使用“动态加载程序集”并将其“反序列化任何类型的类”的想法,这是一种幻想。您可以旋转命运之轮,并将一个<bindingRedirect>放在app.exe.config文件中,以强制CLR使用不同的程序集版本。处理这些事故现在是你的责任了。许多程序员抓住了机会,很少有人在没有吸取新教训的情况下从经验中回来。我们必须这样做,以了解其后果。那就去吧。
发布于 2019-12-15 16:48:25
首先,有关二进制序列化的一些事实(如果您只对解决方案感兴趣,请跳过它们):
BinaryFormatter。虽然它只在.NET 5中被删除/标记为过时(虽然可以作为一个包使用),但在.NET Core2/3中也有许多类型以前在.NET框架中是可序列化的,但在.NET核心中不再可以序列化(例如)。( Type、Encoding、MemoryStream、ResourceSet、代表等)。如果您仍然确定要通过使用BinaryFormatter来解决这个问题,那么您有以下选项:
1.最简单的情况:只有程序集版本更改了
您可以向assemblyBinding文件中添加一个简单的app.config。只需将实际版本放在newVersion属性中即可。
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyAssembly" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>2.程序集名称和/或类型名称也已更改(或者如果您更喜欢编程解决方案)。
IFormatter实现(因此也是BinaryFormatter)具有Binder属性。您可以使用它来控制程序集/类型名称解析:
internal class MyBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
// mapping the known old type to the new one
if (assemblyName.StartsWith("MyAssembly, ") && typeName == "MyNamespace.MyOldType")
return typeof(MyNewType);
// for any other type returning null to apply the default resolving logic:
return null;
}
}用法:
var formatter = new BinaryFormatter { Binder = new MyBinder() };
return (MyNewType)formatter.Deserialize(myStream);如果您只需要一个程序集版本不敏感的解析器,您可以使用WeakAssemblySerializationBinder。
3.新类型的内部结构也改变了
由于“任择议定书”没有涉及这一案件,我将不会对细节进行太深入的探讨。TL;DR:在这种情况下,您需要设置IFormatter.SurrogateSelector属性。如果类型名称和内部布局都发生了更改,则可以将其与Binder属性一起使用。如果您感兴趣,在备注部分的CustomSerializerSurrogateSelector类中有一些可能的子案例。
的最终想法:
https://stackoverflow.com/questions/18881659
复制相似问题