Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何确保无法验证的.NET程序集有效?_.net_Cil_Peverify - Fatal编程技术网

如何确保无法验证的.NET程序集有效?

如何确保无法验证的.NET程序集有效?,.net,cil,peverify,.net,Cil,Peverify,.NET应用程序分布在称为程序集的文件中,其中包含元数据和公共中间语言(CIL)代码。NET所遵循的标准, ,II.3,注意到两个相似的发音术语之间的区别: 如果总成符合标准,则该总成是有效的 验证是指对任何文件应用一组测试,以检查文件的格式、元数据和CIL是否自一致。这些测试旨在确保文件符合本规范的规范要求 如果组件有效,并且可以通过标准描述的静态分析算法证明组件是类型安全的,则组件是可验证的 验证是指检查CIL及其相关元数据,以确保CIL代码序列不允许对程序逻辑地址空间之外的内存进行任何

.NET应用程序分布在称为程序集的文件中,其中包含元数据和公共中间语言(CIL)代码。NET所遵循的标准, ,II.3,注意到两个相似的发音术语之间的区别:

  • 如果总成符合标准,则该总成是有效的

    验证是指对任何文件应用一组测试,以检查文件的格式、元数据和CIL是否自一致。这些测试旨在确保文件符合本规范的规范要求

  • 如果组件有效,并且可以通过标准描述的静态分析算法证明组件是类型安全的,则组件是可验证的

    验证是指检查CIL及其相关元数据,以确保CIL代码序列不允许对程序逻辑地址空间之外的内存进行任何访问。与验证测试一起,验证确保程序无法访问内存或内存 未被授予访问权限的其他资源

所有可验证程序集都是有效的,但并非所有有效程序集都是可验证的。此外,某些有效程序集实际上可能是类型安全的,但验证算法无法证明它们是类型安全的,因此它们是不可验证的。要使用标准中的图表,请执行以下操作:

.NET SDK提供了一个静态确定程序集是否可验证的工具:。由于可验证程序集也必须有效,因此如果程序集无效,此工具还会报告错误

然而,似乎并没有一个等效的工具或过程来确定程序集是否有效。例如,如果我已经知道程序集是不可验证的,并且我对此没有意见,那么如何确保运行时不会因为无效程序而出错

我的测试用例:

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
  .ver 4:0:0:0
}

.assembly MyAsm { }
.module MyAsm.exe
.corflags 0x00020003    //  ILONLY 32BITPREFERRED

.class public Program
{ 
  .method public static int32 EntryPoint(string[] args) cil managed
  {
    .maxstack 2
    .entrypoint

    call string [MyAsm]Program::normal()
    call void [mscorlib]System.Console::WriteLine(string)

    call string [MyAsm]Program::unverifiable_init()
    call void [mscorlib]System.Console::WriteLine(string)

    call string [MyAsm]Program::unverifiable_jmp()
    call void [mscorlib]System.Console::WriteLine(string)

    call string [MyAsm]Program::invalid()
    call void [mscorlib]System.Console::WriteLine(string)

    ldc.i4.0
    ret
  }

  .method public static string normal() cil managed
  {
    .maxstack 2
    .locals init ([0] int32 initialized)

    ldstr  "From normal: "
    ldloca initialized
    call instance string [mscorlib]System.Int32::ToString()
    call string [mscorlib]System.String::Concat(string, string)

    ret
  }

  .method public static string unverifiable_jmp() cil managed
  {
    .maxstack 1

    ldstr "Printing from unverifiable_jmp!"
    call void [mscorlib]System.Console::WriteLine(string)

    jmp string [MyAsm]Program::normal() // jmp is always unverifiable
  }

  .method public static string unverifiable_init() cil managed
  {
    .maxstack 2
    .locals ([0] int32 hasGarbage) // uninitialized locals are unverifiable

    ldstr  "From unverifiable_init: "
    ldloca hasGarbage
    call instance string [mscorlib]System.Int32::ToString()
    call string [mscorlib]System.String::Concat(string, string)

    ret
  }

  .method public static string invalid() cil managed
  {
    .maxstack 1

    ldstr "Printing from invalid!"
    call void [mscorlib]System.Console::WriteLine(string)

    ldstr "From invalid"
    // method fall-through (no ret) is invalid
  }
}
我使用
ilasm
来组装它,生成
MyAsm.exe

虽然我可以运行程序集,但.NET运行时只会在调用
invalid()
方法时出错,而不会在加载程序集时出错。如果我删除了调用,那么程序运行到完成时没有错误,因此仅仅加载和运行程序集并不能保证它完全有效

在程序集上运行PEVerify会产生三个错误。在这种情况下,人眼很容易看出前两个错误是验证错误,最后一个是验证错误,但似乎没有一种简单的方法可以自动进行区分(例如,检查每一行的
verify
似乎太宽)


基于@Damien_的注释,我编写了这个小片段,它使用来编译每个方法。它不会处理所有情况(嵌套类型、泛型、引用解析等),但作为一个起点,它可以工作:

var b = File.ReadAllBytes("MyAsm.exe");
var asm = Assembly.Load(b);

foreach(var m in asm.GetModules())
{
    foreach(var t in m.GetTypes())
    {
        foreach(var mb in t.GetMethods((BindingFlags)62).Cast<MethodBase>().Union(t.GetConstructors((BindingFlags)62)))
        {
            try
            {
                RuntimeHelpers.PrepareMethod(mb.MethodHandle);
            }
            catch (InvalidProgramException ex)
            {
                Console.WriteLine($"{mb.DeclaringType}::{mb.Name} - {ex.Message}");
            }

        }
    }
}
var b=File.ReadAllBytes(“MyAsm.exe”);
var asm=组件负载(b);
foreach(asm.GetModules()中的var m)
{
foreach(m.GetTypes()中的var t)
{
foreach(t.GetMethods((BindingFlags)62.Cast().Union(t.GetConstructors((BindingFlags)62))中的var mb)
{
尝试
{
RuntimeHelpers.PrepareMethod(mb.MethodHandle);
}
捕获(InvalidProgrameException ex)
{
WriteLine($“{mb.DeclaringType}::{mb.Name}-{ex.Message}”);
}
}
}
}
将输出:

Program::invalid-公共语言运行库检测到无效程序


坏思想第一——我想你可以在这里滥用
ngen
。因为它必须编译所有的方法,所以它应该为无效的方法生成某种错误。“我想不出任何其他内置的东西,例如,强制程序集的所有方法都进行JIT。”“Damien_,不相信我的人,我尝试过这个,它似乎奏效了。”
ngen install MyAsm.exe
仅为有效性而非可验证性报告错误(
公共语言运行库在编译方法程序时检测到无效程序。无效
)。奇怪的是,代码0仍然存在,我可以稍后使用
ngen display MyAsm.exe
(尽管此调用随后使用-1退出),并且在再次调用
install
之前,我必须使用
ngen uninstall MyAsm.exe
,所以我猜它是在AOT编译错误的情况下安装的?无论如何,我想你可以把它作为一个答案来提交。我没有把它作为一个答案来提交是有原因的——它严重滥用了基础设施来获得结果。亨尼似乎提供了某种答案,看起来使用起来“污染”更少。一般来说,似乎您需要在某个地方激发编译以获得有效性错误,并且您所寻找的工具(只验证,不验证)不可能重复感谢。如果有人试图重现这一点,请注意两点:(1)
(BindingFlags)62
DeclaredOnly
实例
静态
公共
非公共
标志的组合。(2)
PrepareMethod
调用要求程序集与此程序集位于同一目录中。我想知道,让它也处理泛型方法有多难?@illdans4我想到两个选项:使用ILReader获取并稍后处理所有发生的实例化,或者只使用一些“随机”至少满足约束(如果有)的类型。由于泛型方法的有效性不应取决于泛型参数,因此后面的方法就足够了。
var b = File.ReadAllBytes("MyAsm.exe");
var asm = Assembly.Load(b);

foreach(var m in asm.GetModules())
{
    foreach(var t in m.GetTypes())
    {
        foreach(var mb in t.GetMethods((BindingFlags)62).Cast<MethodBase>().Union(t.GetConstructors((BindingFlags)62)))
        {
            try
            {
                RuntimeHelpers.PrepareMethod(mb.MethodHandle);
            }
            catch (InvalidProgramException ex)
            {
                Console.WriteLine($"{mb.DeclaringType}::{mb.Name} - {ex.Message}");
            }

        }
    }
}