Microsoft 如何从 DLL 的元数据中隐藏 C# Internal 类?
How Microsoft Hides C# Internal Classes from a DLL's Metadata?
这一切都是从我想分析 CVE-2017-8759 周围的代码开始的。我知道 CVE 的修复程序位于 System.Runtime.Remoting.dll 中名为 WsdlParser.cs 的 class,它是 .Net Framework 的一部分。您的计算机上可能有此 dll,其位置类似于:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.7\System.Runtime.Remoting.dll
我使用 ilspycmd 将代码重新assemble 返回到 C#,并注意到输出目录中缺少 WsdlParser.cs:
后来用了CFF Explorer,发现TypeDefs里面的元数据确实少了这个类型:
但是,我知道 class 就在那里:
- 它在微软的文档中:https://referencesource.microsoft.com/System.Runtime.Remoting/metadata/wsdlparser.cs.html
当使用反射和 LoadAssembly() 时,我能够找到 class:
Assembly assembly = Assembly.LoadFile(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7\System.Runtime.Remoting.dll");
foreach (var type in assembly.GetTypes())
{
if (!type.FullName.EndsWith("WsdlParser"))
{
continue;
}
Console.WriteLine("Great Success");
}
我注意到此行为与此 dll 中的所有内部 classes 一致,但我不明白这是怎么回事。我的猜测是可能有一个 post-build 过程来删除内部类型的数据,但如果是这样,我如何通过加载程序集找到 class?我认为 CIL 使用 TypeDef 元数据加载类型,但是否有额外的 space 存储此数据?
为了更好地理解它,我创建了一个带有内部 class 的 C# 测试项目,并使用 CFF Explorer 检查了元数据。内部 class 在调试和发布版本中应该存在。
那么这个巫术是什么?
谢谢大家。
您找到的是 Reference Assembly。在你找到它的路径中有一个很大的线索。
Reference assemblies are a special type of assembly that contain only the minimum amount of metadata required to represent the library's public API surface. They include declarations for all members that are significant when referencing an assembly in build tools, but exclude all member implementations and declarations of private members that have no observable impact on their API contract.
(我的重点)
并且:
Generating reference assemblies for your libraries can be useful when your library consumers need to build their programs against many different versions of the library. Distributing implementation assemblies for all these versions might be impractical because of their large size. Reference assemblies are smaller in size, and distributing them as a part of your library's SDK reduces download size and saves disk space.
没什么神奇的,只是一种公开记录的在不需要完整文件时分发较小文件的方法。
这些程序集在编译时使用,但不在运行时使用。为此,您需要一个实现程序集,它将通过其他方式提供,例如已放置在 GAC 中。
这一切都是从我想分析 CVE-2017-8759 周围的代码开始的。我知道 CVE 的修复程序位于 System.Runtime.Remoting.dll 中名为 WsdlParser.cs 的 class,它是 .Net Framework 的一部分。您的计算机上可能有此 dll,其位置类似于:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.7\System.Runtime.Remoting.dll
我使用 ilspycmd 将代码重新assemble 返回到 C#,并注意到输出目录中缺少 WsdlParser.cs:
后来用了CFF Explorer,发现TypeDefs里面的元数据确实少了这个类型:
但是,我知道 class 就在那里:
- 它在微软的文档中:https://referencesource.microsoft.com/System.Runtime.Remoting/metadata/wsdlparser.cs.html
当使用反射和 LoadAssembly() 时,我能够找到 class:
Assembly assembly = Assembly.LoadFile(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7\System.Runtime.Remoting.dll"); foreach (var type in assembly.GetTypes()) { if (!type.FullName.EndsWith("WsdlParser")) { continue; } Console.WriteLine("Great Success"); }
我注意到此行为与此 dll 中的所有内部 classes 一致,但我不明白这是怎么回事。我的猜测是可能有一个 post-build 过程来删除内部类型的数据,但如果是这样,我如何通过加载程序集找到 class?我认为 CIL 使用 TypeDef 元数据加载类型,但是否有额外的 space 存储此数据?
为了更好地理解它,我创建了一个带有内部 class 的 C# 测试项目,并使用 CFF Explorer 检查了元数据。内部 class 在调试和发布版本中应该存在。
那么这个巫术是什么?
谢谢大家。
您找到的是 Reference Assembly。在你找到它的路径中有一个很大的线索。
Reference assemblies are a special type of assembly that contain only the minimum amount of metadata required to represent the library's public API surface. They include declarations for all members that are significant when referencing an assembly in build tools, but exclude all member implementations and declarations of private members that have no observable impact on their API contract.
(我的重点)
并且:
Generating reference assemblies for your libraries can be useful when your library consumers need to build their programs against many different versions of the library. Distributing implementation assemblies for all these versions might be impractical because of their large size. Reference assemblies are smaller in size, and distributing them as a part of your library's SDK reduces download size and saves disk space.
没什么神奇的,只是一种公开记录的在不需要完整文件时分发较小文件的方法。
这些程序集在编译时使用,但不在运行时使用。为此,您需要一个实现程序集,它将通过其他方式提供,例如已放置在 GAC 中。