Flier Lu <flier_lu@sina.com.cn>
注意:本系列文章在水木清華BBS(smth.org)之.Net版首發, 轉載請保留以上信息,發表請與作者聯系
概述
本系列文章,將從系統層角度,通過對MS.Net CLR架構對PE映像結構的擴展的分析 , 解析MS.Net CLR架構的底層部分運行機制,幫助讀者從更深層次理解CLR中某些重要概念 本文讀者應具備基本的Win32編程經驗,了解.Net中常見概念意義,并對Win32之PE映像 結構有一定了解,具體結構請參看Matt Pietrek于1994.3發表在MSJ的經典文章 《Peering Inside the PE: A Tour of the Win32 Portable Executable File Format》,與之重復的部分我一概跳過。 本系列文章,將分為幾個大部分,首先是最重要的MetaData,其次是IL代碼結構, 然后……我還沒想好,呵呵。此外會根據需要穿插一下CLR核心概念、思想、技術的介紹 。 至于CLR幾個核心部件之間的關系與交互等問題,我熱切期待TBSoft的大作, 我這里就不去搶他的話題了,呵呵。
前言
對一個優秀Win32程序員來說,對PE結構的了解是對Win32架構了解的必經之路, 而從Chicago(Win95的開發代號,Win95正式發布以前的文檔對Win95的稱呼)以來, PE結構就相對穩定,直到MS.Net的出現,才發生了一些不大不小的變化。 之所以說是不大不小,是因為CLR基本上沒有對PE結構進行改變,只是利用現有PE 結構的優良可擴展性,將其所需的信息擴展到PE映像中。具體一點說,就是利用了PE結 構 中的IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] 來保存服務于CLR的IMAGE_COR20_HEADER結構。此外的PE結構一律不變。 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR此節,原本是設計用于COM,但不知為何 一直沒有被使用,現在用于保存.Net信息的最高級信息結構。 我們的分析也將集中在此結構以及相關信息的分析上。 IMAGE_COR20_HEADER結構的定義,可以在FrameworkSDK\include\CorHdr.h 文件中找到,如下:
// CLR 2.0 header structure. typedef struct IMAGE_COR20_HEADER { // Header versioning ULONG cb; USHORTMajorRuntimeVersion; USHORTMinorRuntimeVersion;
// Symbol table and startup information IMAGE_DATA_DIRECTORYMetaData; ULONG Flags; ULONG EntryPointToken;
// Binding information IMAGE_DATA_DIRECTORYResources; IMAGE_DATA_DIRECTORYStrongNameSignature;
// Regular fixup and binding information IMAGE_DATA_DIRECTORYCodeManagerTable; IMAGE_DATA_DIRECTORYVTableFixups; IMAGE_DATA_DIRECTORYExportAddressTableJumps;
// Precompiled image info (internal use only - set to zero) IMAGE_DATA_DIRECTORYManagedNativeHeader;
} IMAGE_COR20_HEADER;
而詳細的說明,則可以在FrameworkSDK\Tool Developers Guide\docs 目錄中找到。因為要將CLR變為標準,MS這次一反常態,公開大量有價值的文檔, 避免我等浪費時間去逆向過程,呵呵 此結構雖然字段較多,但實際上其核心在于MetaData,其他信息都是圍繞著 MetaData服務。之間的關系,等會再慢慢道來。 cb是結構大小,MajorRuntimeVersion.MinorRuntimeVersion是版本號 指執行此程序所需的最低CLR版本號,目前一般設置為1.1。而現在發布的.Net Framework的CLR版本一般為2.0。 Flags是Runtime Image描述標志,描述此映像的執行屬性。如設置位 COMIMAGE_FLAGS_32BITREQUIRED=0x02,則此映像只能在32位系統上執行 對以后的64位CLR無效(MS.Net很大的一個功能就是為以后平滑過渡到64位 平臺做準備,想想以前16位平臺到32位平臺過渡時的混亂,以及現在比以前翻了 n倍的代碼量就恐怖,MS真是未雨綢繆啊,呵呵)。如果設置 COMIMAGE_FLAGS_STRONGNAMESIGNED=0x04,則此映像有strong name signature(這個東東不知道怎么翻譯好)。這個strong name signature 在CLR架構里起到了非常重要的作用。為什么這么說呢?因為這個strong name signature起到Assembly的身份證的作用,它關系到CLR中一大堆概念的實現, 以后我會專門用一章篇幅來介紹他,這里暫且放下。 EntryPointToken則是指向IL程序的入口,類似于以前的 IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint的作用,只是以前的 AddressOfEntryPoint是一個RVA直接指向程序入口代碼所在地址, (不要告訴我你不知道RVA是什么啊,呵呵,趕快去看Peering Inside the PE) 而現在EntryPointToken指向一個Token。注意,是Token,因為IL代碼是 JIT編譯的,存在于映像中的都是IL形式的P-code(pseudo code),在需要時 才由CLR動態讀取,在內存中編譯展開為本機代碼(Native Code),進而執行。 因此這里的程序入口執行的只是一個MethodDef或File表的入口,一個Token而已。 這里的MethodDef是一個MetaData表,每行定義一個方法;而File表則是 每行有一個File定義的表,每行包含一個外部文件的信息。也就是說,在執行程序時 可以直接編譯執行此映像中的一個方法的IL代碼,也可能是重定向到另一個文件, 這就是Assembly作為一個邏輯代碼單元,與傳統DLL之類相比一個很大的不同。 Assembly的概念也非常重要,我不想這里一下說完,以后專門拿一章出來講好了。 剩下幾個字段都是IMAGE_DATA_DIRECTORY類型,這個類型是一個數據塊 定義結構,在Winnt.h中有定義 typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 呵呵,知道RVA的意思了吧 RVA = Relative Virtual Address Resources定義CLI資源;StrongNameSignature定義剛剛提到的 strong name signature;此外CodeManagerTable, ExportAddressTableJumps,MangedNativeHeader都沒用到。 VTableFixups暫且略過,以后用上時再詳細解釋。 這樣一來,就剩下一個MetaData字段沒有介紹了,不過這個重中之重的東東, 這次只言片語是無法介紹了,因為下面會有專門的一整篇——MetaData篇, 用n章的篇幅來詳細剖析,呵呵
btw:因為自己以前不是搞Win32底層開發的,實在不知這種文章怎么寫, 希望讀者多多提意見,有沒有解釋清楚或者錯誤的地方盡管提出來。
分析.Net的CLR PE映像其實并不是什么困難的事情,有現成的 代碼(mono)現成的文檔(Tool Developers Guide)可以看, 只是代碼比較難看(不習慣unix代碼風格),文檔比較長 (一共20幾M,poor)而已。我是實在耐不住好奇心才動手分析的, 希望能夠把自己分析的一些收獲和體會寫出來,節省其他朋友的時間。
希望能夠有充足的時間、精力和耐心完成這個系列
|