具有动态加载的.Net程序集的二进制序列化 [英] Binary serialization with dynamically loaded .Net assembly
问题描述
我将类的实例序列化为文件(使用 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
- 如何为二进制格式化程序创建一个SerializationBinder,以处理类型从一个程序集和名称空间到另一个程序集和命名空间的移动。
- BinaryFormatter反序列化会带来SerializationException
在评论 @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:
-
用Eric Lippert的话来说,从来没有人设计,指定,实施,测试,记录和
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屋!