2017-12-29 31 views
1

.NETアプリケーションは、アセンブリというファイルに配布されます。このファイルには、メタデータと共通中間言語(CIL)コードが含まれています。それは、標準に準拠している場合検証不能な.NETアセンブリが有効であることを確認するにはどうすればよいですか?

  • アセンブリは有効です:.NETは、標準に準拠し、 ECMA-335、II.3は、2つの同様な響きの用語の区別を指摘しています。

    検証とは、ファイルのフォーマット、メタデータ、およびCILが自己整合的であることを確認するために、任意のファイルに対して一連のテストを適用することです。これらのテストは、ファイルが本仕様書の規範的な要件を確実に満たすようにするためのものです。アセンブリが有効であり、アセンブリは、型安全であること、標準によって記述静的解析アルゴリズムを介して、証明できる場合

  • アセンブリは検証あります。

    検証とは、CILコードシーケンスがプログラムの論理アドレス空間外のメモリへのアクセスを許可しないようにするための、CILとその関連メタデータの両方のチェックを指します。検証テストと併せて、検証により、アクセスが許可されていないメモリまたは その他のリソースにプログラムがアクセスできないことが保証されます。

すべての検証アセンブリは有効ですが、すべてではありませんが、有効なアセンブリが検証可能です。さらに、実際には有効なアセンブリの中には型セーフであるものもありますが、検証アルゴリズムではそれを証明できないため、検証できません。標準のダイアグラムを使用するには:PEVerify

ECMA-335, II.3, Figure 1: Relationship between correct and verifiable 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 
    } 
} 

私は、これはMyAsm.exeを生産、ilasmを使用して組み立てます。

アセンブリを実行することができますが、.NETランタイムはアセンブリがロードされたときではなく、invalid()メソッドが呼び出されるとエラーになります。コールを削除すると、エラーなしでプログラムが完了して実行されるため、アセンブリをロードして実行しても、完全に有効であるとは限りません。

アセンブリでPEVerifyを実行すると、3つのエラーが発生します。人間の目には、この場合、最初の2つのエラーは検証エラーであり、最後のエラーは検証エラーであることは合理的には分かりやすいですが、その差別化を簡単に自動化する方法(たとえば、の各行をチェックするのはあまりにも広すぎます)。 @Damien_The_Unbelieversに基づいて

Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0 
Copyright (c) Microsoft Corporation. All rights reserved. 

[IL]: Error: [C:\...\MyAsm.exe : Program::unverifiable_jmp][offset 0x0000000A] Instruction cannot be verified. 
[IL]: Error: [C:\...\MyAsm.exe : Program::unverifiable_init][offset 0x00000005] initlocals must be set for verifiable methods with one or more local variables. 
[IL]: Error: [C:\...\MyAsm.exe : Program::invalid][offset 0x0000000A] fall through end of the method without returning 
3 Error(s) Verifying MyAsm.exe 
+0

悪い考え番号1 - あなたが*ここで 'ngen'を乱用する可能性があると思います。すべてのメソッドをコンパイルする必要があるため、無効なメソッドに対して何らかのエラーが生成されるはずです。私は他に何か考えられることができません。アセンブリのすべてのメソッドを強制的にJITできます。 –

+0

@Damien_The_Unbeliever私はこれを試して、それは動作するようです。 'ngen install MyAsm.exe'は、妥当性のためだけにエラーを報告します。検証可能ではありません(' Program.invalid'メソッドをコンパイル中に無効なプログラムが検出されたCommon Language Runtime)。不思議なことに、これはまだコード0で終了し、後で 'ngen display MyAsm.exe'を使うことができますが(この呼び出しは-1で終了します)、' install'を再度呼び出す前に 'ngen uninstall MyAsm.exe'を使用しなければなりません。だから私はAOTコンパイルエラーにもかかわらずインストールされていると思いますか?とにかく、これを答えとして提出できると思います。 –

+0

私はそれを理由で答えとして提出しませんでした。結果を得るためにインフラストラクチャを真剣に乱用しています。 thehennyyは、使用するのが「汚染される」ことが少ないような、ある種の答えを提供したようです。一般的には、正当なエラーを得るためにどこかでコンパイルを誘発する必要があり、あなたが探しているツールはありません(確認のみ、検証しません) –

答えて

3

は、私は、各メソッドをコンパイルするRuntimeHelpers.PrepareMethod Methodを使用して、この小さなスニペットを書いたコメント。それはすべてのケース(ネストされた型、ジェネリック、参照解決を、...)を処理しませんが、出発点として、それが動作します:

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}"); 
      } 

     } 
    } 
} 

意志出力:

プログラム::無効 - 共通言語Runtimeが無効なプログラムを検出しました。

+0

ありがとうございました。 (1) '(BindingFlags)62'は' DeclaredOnly'、 'Instance'、' Static'、 'Public'、' NonPublic'フラグの組み合わせです。 (2) 'PrepareMethod'呼び出しでは、アセンブリはこのプログラムのアセンブリと同じディレクトリに存在する必要があります。 –

+0

あまりにも一般的な方法を扱うのはどれほど難しいでしょうか? – IllidanS4

+0

@ IllidanS4 2つの選択肢があります:ILReaderを使用して、発生しているすべてのインスタンス化を取得し、後で処理するか、制約がある場合は少なくともそれを満たす「ランダム」型を使用します。ジェネリックメソッドの妥当性はジェネリックパラメータに依存してはならないため、後のアプローチで十分である可能性があります。 – thehennyy

関連する問題