元数据与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節
總結
- 上一篇: 评估创业项目的十大标准
- 下一篇: 设计模式之Adapter