元數據與IL簡介


 

文/玄魂

1.3.2       元數據

元數據是描述數據的數據。在CLR的上下文中,元數據表示由描述符組成的一套體系,這些操作符包括了在一個模塊中被聲明或引用的所有項。由於CLR模型是面向對象的,因此在元數據中描述的項是類和它們的成員,以及它們伴隨着的特性、屬性和關聯。本節簡單地介紹元數據,與原數據安全相關的內容會在后續章節中繼續講解,元數據的詳細內容不在本書的論述范圍之內。

元數據實際上是一塊二進制數據,包含了三種表:定義表、引用表和清單表。

元數據定義表主要是模塊定義、類型定義、方法定義、字段定義、事件定義、參數定義、屬性定義等一系列定義表的集合。當編譯器編譯代碼時,所有定義的內容都會生成對應的定義表。

元數據引用表用於記錄編譯器中源代碼引用的類型、方法、字段、事件。常用的引用表如:AssemblyRef(程序集引用表)、ModuleRef(模塊引用表)、TypeRef(類型引用表)等。

元數據清單表包含了組成程序集所需要的所有信息,同時包含了對其他程序集的引用信息。它明確地指出了哪些條目可以對外開放,哪些條目只可以在程序集內部進行訪問。

下面通過經典的HelloWorld程序簡要分析其中的元數據信息,如代碼清單1-7所示。

代碼清單1-7 HelloWorld程序代碼

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace HelloWorld

{

    class Program

    {

        static void Main(string[] args)

        {

            Console.WriteLine("Hello World!");

            Console.Read();

 

        }

    }

}

 

下面使用反匯編工具ILDasm打開HelloWorld.exe,雙擊MANIFEST,圖1-7為查看清單信息的截圖。ILDasm的使用方法和參數說明請讀者參考MSDN文檔。

 

圖1-7  查看程序清單信息

詳細的清單信息如代碼清單1-8所示。

代碼清單1-8 HelloWorld.EXE的清單信息

// Metadata version: v4.0.21006

.assembly extern mscorlib

{

  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..

  .ver 4:0:0:0

}

.assembly HelloWorld

{

......

}

  .hash algorithm 0x00008004

  .ver 1:0:0:0

.module HelloWorld.exe

// MVID: {B8EB35DD-5AD2-402C-B422-AA63B0AACCFA}

.imagebase 0x00400000

.file alignment 0x00000200


.stackreserve 0x00100000

.subsystem 0x0003       // WINDOWS_CUI

.corflags 0x00000003    //  ILONLY 32BITREQUIRED

// Image base: 0x05C40000

 

程序比較簡單,代碼中包含了版本、外部引用等簡單的信息。表1-2描述了示例中所使用的 HelloWord.exe 程序集的程序集清單中的各項指令。

表1-2 HelloWorld.exe指令說明

指令

說明

.assembly extern < assembly name >

指定包含當前模塊所引用項目的另一程序集(在此示例中為 mscorlib)

.publickeytoken < token >

指定所引用程序集的實際密鑰的標記

.ver < version number >

指定引用程序集的版本號

.assembly < assembly name >

指定程序集名稱

.hash algorithm < int32 value >

指定使用的哈希算法

.module < file name >

指定組成程序集的模塊名稱,在此示例中,程序集只包含一個文件

.subsystem < value >

指定程序要求的應用程序環境。在此示例中,值 3 表示該可執行文件從控制台運行

.corflags

當前是元數據中的一個保留字段

根據程序集的內容,程序集清單可包含許多不同的指令。有關程序集清單中指令的完整列表請讀者參考相關文檔,本例旨在拋磚引玉。若要查看完整的元數據信息,可以使用快捷鍵“Ctlr+M”,如圖1-8所示。

 

圖1-8 查看元數據信息

1.3.3       IL常用指令

為方便起見,還是以HelloWorld.exe為例講解IL的相關內容。由於篇幅所限,關於IL的詳細內容還請各位讀者參考相關資料。圖1-9為Main方法的IL代碼。

 

圖1-9 HelloWorld.exe Main方法的IL代碼

在一個中間語言程序中,如果某一行以“.”開始,代表這是一個傳輸給匯編工具的指令;而沒有以“.”開始的行是中間語言的代碼。上圖中.method是方法定義指令,定義了Main方法,參數在“()”中,IL代碼在“{}”中。.entrypoint是入口指令,表明該方法是入口方法。.maxstack指定了最大棧的深度為8。下面的IL_n是代碼標簽,后面是IL代碼。nop是空指令;ldstr指令向棧中壓入字符串“Hello World!”;call指令調用靜態方法Console.WriteLine(string)和Console.read();pop彈出棧頂的值;ret指令表示方法體的結束。IL支持“//”和“/* */”的注釋方法。

提示  在中間語言中,如果需要調用一個方法,需要指定方法的全名,包括它的名稱域(namespace)、類名、返回值類型和參數的數據類型。

表1-3列舉了IL的其他一些常用指令,更多的指令可以查看IL指令表。

表1-3 IL常用指令

指令

描述

.assembly <程序集名稱> {}

設置程序集

ldc.i4.n

把一個 32位的常量(n從0到8)裝入堆棧

stloc.n

把一個從堆棧中返回的值存入第n(n取0~8)個局部變量

add

2個值相加。命令的參數必須在調用前裝入堆棧,該函數從堆棧中移除參數並把運算后的結果壓入堆棧

sub

2個值相減

mul

2個值相乘

newarr type

生成一個元素類型為type 的數組。數組的大小必須在調用該命令前裝入堆棧。該命令會把一個數組的引用裝入堆棧

stelem.i4

給一個數組成員賦值。數組的引用、下標和值必須在調用該命令前裝入堆棧

ldelema type

把數組元素的地址裝入堆棧。數組的引用和下標必須在調用該命令前裝入堆棧。地址用來調用非靜態函數

ldlen

把數組的長度裝入堆棧。數組的引用必須在調用該命令前裝入堆棧

ldloca.s variable

把變量的地址裝入堆棧

ldc.i4.s value

把一個Int32的常量裝入堆棧(用於大於8位的數)

conv.i4

把堆棧中值轉換成Int32類型

call instance function(arguments)

調用類的非靜態函數

bge.s label

跳轉至label 如果value1≥value 2. Values 1和 2 必須在調用本命令前裝入堆棧

br.s label

跳轉至label

box value type

把一個值類型轉成一個Object,並把該Object的引用裝入堆棧

blt.s label

跳轉至label 。如果value 1小於 value 2. Values 1 和 2 必須在調用本命令之前裝入堆棧

ldelem.i4

把一個數組元素裝入堆棧。數組引用和下標必須在調用本命令之前裝入堆棧

ldarga.s argument

把函數參數的地址裝入堆棧

dup

在堆棧上復制一個值

stind.i4

存儲值的地址。地址和值必須在調用本命令之前裝入堆棧

.field

定義類成員。和關鍵字public、private、static等一起使用

stsfld static field

用堆棧中的值替換靜態字段的值

ldfld field

把一個非靜態字段裝入堆棧。類實例的地址必須在調用本命令之前裝入堆棧

ldarg.n

把第n個參數裝入堆棧。在非靜態函數中,第0個參數是一個隱含的參數,代表this

newobj constructor

用構造函數constructor生成一個類的實例。構造函數的參數必須在調用本函數之前先裝入堆棧。一個類的實例會被生成並裝入堆棧

callvirt instance function

調用一個對象的后期綁定方法

 

1.3.4       IL與代碼驗證

在將MSIL編譯為本機代碼的過程中,MSIL代碼必須通過驗證過程,除非管理員已經建立了允許代碼跳過驗證的安全策略。驗證過程檢查MSIL和元數據以確定代碼是否是類型安全的,這意味着它僅訪問已被授權訪問的內存位置。類型安全幫助將對象彼此隔離,因而可以保護它們免遭無意或惡意的破壞。它還提供了對代碼可以可靠地強制安全限制的保證。

運行庫使用下列條件來驗證代碼是否為類型安全:

q  對類型的引用與被引用的類型嚴格兼容。

q  在對象上只調用正確定義的操作。

q  標識與聲稱的要求一致。

驗證過程中檢查 MSIL 代碼,嘗試確認該代碼只能通過正確定義的類型訪問內存位置和調用方法。例如,代碼不允許以超出內存范圍的方式來訪問對象。另外,驗證過程檢查代碼以確定 MSIL 是否已正確生成,這是因為不正確的 MSIL 會導致違反類型安全規則。驗證過程通過正確定義的類型安全代碼集,並且它只通過類型安全的代碼。然而,由於驗證過程存在一些限制,某些類型安全代碼可能無法通過驗證,而某些語言在設計上並不產生可驗證的類型安全代碼。如果安全策略要求提供類型安全代碼,而該代碼不能通過驗證,則在運行該代碼時將引發異常。

-------------------------注:本文摘抄自《.NET 安全揭秘》1.3節


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM