.NET体系中的源程序安全问题
在.NET平臺上,代碼以中間語言的形式運行,它是.NET眾多優勢的基礎。但在獨立桌面應用中,它給源代碼的安全帶來了威脅。本文探討產生這個問題的原因,分析可能的解決辦法。
在Visual Studio.NET(VS.NET)體系中,VB、Visual C++以及C#之類的編譯器把源程序編譯成MSIL。MSIL即Microsoft Intermediate Language,或Microsoft中間語言,它在執行之前被即時(Just-In-Time Compile,JIT)編譯成為機器語言。但是,你可能還沒有深入了解當你在VS中點擊Build按鈕時發生了什么事情,或者你私有的源代碼和信息是否在偷窺的眼光面前安然無恙、當你把IL代碼發布給客戶時是否能夠保證代碼不被篡改。下面我們將深入.NET的內部工作過程,探討VB.NET體系MSIL的特點和一些你必須關心的問題。
你必須搞清楚以下幾個問題。首先,.NET是為客戶機/服務器系統以及Web應用而設計的。軟件開發正在逐漸向Internet以及基于客戶機/服務器的應用發展,許多應用不再有傳統風格的界面,而是提供類似瀏覽器的界面。.NET也同樣追隨著這個趨勢。
第二,在桌面應用中,我們無法保護以受管理的MSIL形式存在的代碼,如果你覺得保護知識產權(即源代碼)非常重要,那么.NET不適合桌面應用開發。雖然MSIL的承諾令人心動,雖然.NET平臺和CLR(Common Language Runtime)很穩定,但從安全的角度來看,對于一個獨立的桌面應用來說,這一切缺乏實際意義。在.NET中,作為一個VB程序員甚至是C#程序員,你只能編寫受管理、不受保護的代碼。
由于存在這種限制,如果你要在桌面應用中保護代碼,你必須使用非受管理的C++。保護知識產權唯一真正有效的方法是:用非受管理的C++組件封裝代碼,然后從.NET受管理代碼中通過COM協作接口調用它。
另外你還必須清楚的是,由于Active Server Pages.NET(ASP.NET)完全在服務器端運行,因此ASP.NET應用是安全的。實際上,這正是.NET最理想的境界——在受保護的服務器上運行代碼,讓代碼遠離任何想要研究它的人。ASP.NET把Web開發簡化到了難以置信的程度,而Visual Basic.NET正是編寫ASP.NET應用的優秀工具。
掌握VB.NET需要經過艱苦的學習,而全面接受.NET更是一個緩慢的過程。從VB6遷移到VB.NET并非輕而易舉,在把VB6應用移植到VB.NET之前的時間里,你仍舊需要提供對VB6應用的支持。在未來很長的時間內,許多開發者仍將使用VB6。
二、中間語言
為了了解在用VB.NET構造工程的過程中發生了什么事情,我們需要創建一個生成代碼和程序集時使用的示例工程:打開VS.NET,新建一個Visual Basic工程,在窗體中加入一個文本標簽(Label),然后把文本標簽的Text屬性改成“Good Bye Visual Basic 6.0”(如圖1),把這個應用命名為GoodByeVB6。
圖1
在深入.NET體系之前,我們需要了解一些有關.NET的邊緣知識和術語。首先,IL(中間語言)并不是什么新概念,VB、C++編譯器生成和利用IL已經有數年的歷史,只不過很少有人公開談論它或為它編寫文檔。與過去編譯應用的方法相比,.NET最大的改變之一就在于編譯器所生成的代碼不同。除了名字之外,新的MSIL與VB6編譯器的IL很少有類同之處。因此,如果你以前曾經接觸過IL,現在還得從頭開始學習。請參見圖2,它是GoodByeVB6應用的MSIL代碼片段:
圖2
這個代碼片段設置了一個8字節的棧,然后把this指針壓棧,調用get_Label1方法。接下來,代碼把要設置的標簽文本壓入堆棧,然后調用setText方法。
傳統的CPU利用寄存器和棧完成所有工作。CLR所提供的執行引擎只有一個棧,它的操作過程非常類似于一個逆波蘭表示法計算器。如果某個過程調用具有多個參數,執行引擎將在發出調用之前把參數壓棧。函數調用的返回值也通過棧傳遞。
MSIL中的局部變量很容易識別,它們用.locals關鍵詞聲明。如果符號存在的話,你將看到變量名字;否則,你看到的將是V_1、V_2之類的變量:
.locals init ([0] int32 x,
[1] int32 y,
[2] float64 z,
[3] class System.String Vb_t_string_0)
ldarg指令把參數裝入棧,ldc指令把數字常量裝入棧,stloc指令把值保存到合適的局部變量:
//000064: Dim x As Integer = 100
IL_0001: ldc.i4.s 100
IL_0003: stloc.0
在這個例子中,常量100被作為4字節整數壓入棧,隨后這個值被保存到第1個局部變量。關于MSIL指令的完整說明,請參見IL程序員參考的ILinstrset.doc文件。
本文的所有MSIL輸出都以GoodByeVB6應用的調試版本為基礎。非調試版本雖然不帶代碼行和變量名字,但仍能夠提供大量有用的信息。在查看MSIL代碼的時候,調試符號雖然重要,但不是必不可少的。
當我們運行編譯器時,它生成的不是我們今天熟悉的執行文件,而是一個程序集(Assembly)。程序集是一個文件的集合,程序集中的文件可以作為單一整體進行部署。在當前的Windows體系中,我們可以把單個執行文件看成一個程序集。但從更嚴格的意義上來說,程序集聚合了執行文件和它的所有支持文件,包括DLL、圖形、資源以及幫助文件。
一般地,一個程序集至少由兩個文件構成:執行部分,manifest(英文單詞原意:載貨清單,乘客名單)。manifest是程序集內所有文件的清單。程序集內的可執行部分又分開稱為模塊(Module)。從概念上說,模塊對應著DLL或者EXE文件;除了父程序集所包含的元數據(Metadata)之外,每一個模塊都包含元數據。程序集是當前可移植執行文件格式(Portable Executable,PE)的一個增強版本。
如圖3所示,文件的開頭是標準的PE頭。文件內部包含了CLR頭,CLR頭的后面是把代碼裝入進程空間所必需的描述數據——即元數據。元數據為執行引擎提供了大量信息,其中包括:如何裝載模塊,需要哪些支持文件,如何裝載支持文件,如何與COM以及.NET運行時環境交互。另外,元數據還描述了模塊或者程序集所包含的方法、接口以及類。元數據所提供的信息使得JIT編譯器能夠編譯并運行模塊。同時,元數據暴露了有關應用的大量內部信息,使得從反匯編IL獲取有價值的代碼更加方便。
圖3
使用.NET代碼的核心問題在于受管理代碼。受管理代碼是專門為在CLR控制之下運行而編寫的代碼,它可以用VB.NET、C#以及C++等語言創建,但C++是唯一能夠創建.NET平臺非受管理代碼的語言。我們無法用VB6為.NET平臺創建非受管理代碼,這是因為在VB6中我們把代碼編譯成i386指令而不是IL代碼。正如使用VB.NET,如果你要使用受管理代碼,你只能把代碼編譯成IL。
現在我們來看看使用這種新的MSIL代碼有哪些優點。如果代碼編譯成了MSIL,我們可以在任何支持CLR的平臺上安裝和運行這些代碼。就目前來說,這一點可能不是很吸引人,因為當前支持.NET的平臺還很少:只有32位的Windows。但不久之后,64位平臺和.NET for Windows CE都將提供這方面的支持。把代碼編譯成MSIL格式使得我們能夠無縫地把應用移植到所有這些平臺和未來的新平臺。
MSIL的另外一個優點是:JIT編譯器在安裝應用的目標機器上把MSIL代碼編譯成機器指令,它能夠利用目標機器的硬件特點,根據平臺的具體情況對代碼進行優化。這一點很有用,例如,它能夠為目標機器的特殊寄存器優化代碼,或為目標機器上帶有特殊處理器的硬件設備優化操作代碼。請點擊VB6工程屬性窗口Compile選項卡中Advanced Optimizations按鈕了解更多信息。由于有了程序集中的元數據,JIT編譯器知道代碼做些什么以及它支持哪些平臺,從而能夠迅速地作出優化決定、提高代碼的性能表現。
還有一個優點涉及到.NET的兩個V:Validation(檢驗),Verification(核查)。檢驗是對模塊進行的一系列檢查,確保元數據、MSIL代碼以及文件格式的一致性。不能通過這些檢查的代碼可能導致執行引擎或者JIT編譯器崩潰。一旦模塊通過了檢驗,則代碼是正確的且可以開始運行。
JIT編譯器把MSIL代碼轉換成機器代碼時對代碼進行核查,它是對元數據進行復查,保證程序不會訪問它不具有相應許可的內存或其他資源。經過核查的代碼是類型安全的(Type-Safe)代碼。這種核查即使是在程序被直接編譯成機器代碼的時候也要進行,但除非由JIT編譯器進行核查,否則這種核查不是100%精確無誤,因為核查結果依賴于來自其他程序集的元數據。如果把源程序直接編譯成機器代碼,我們面臨著這樣一種危險:在目標機器上的其他程序集發生了變化,從而導致程序不再類型安全。
使用JIT編譯器保證了檢驗和核查是對所有相關程序集的當前版本進行。這些操作確保執行程序總是類型安全,程序總是以合適的安全許可運行。你可以用.NET SDK的PEVerify工具自己對代碼進行檢驗和核查。
三、反向工程
當程序集以MSIL而不是機器代碼的形式發布時,最令人關心的問題應該就是安全。正如前面所介紹的,程序集包含了關于包里面所有模塊的manifest以及詳細描述各個模塊的元數據。.NET SDK 提供了一個名為ILDASM的工具,它是一個IL反匯編程序,能夠從模塊反匯編出IL代碼以及應用程序中各個模塊的元數據說明。從Listing 1可以看出,利用ILDASM對代碼實施反向工程是極為方便的。
【Listing 1】下面是IL反匯編程序ILDASM輸出的部分結果。
它顯示的是應用中一個名為LeavingMessage的私有方法,后
面部分是調用LeavingMessage方法的代碼。CLR把參數壓入
棧,執行調用,然后恢復棧為下一個操作做好準備。
.method private instance void LeavingMessage(class System.String& strText) il managed
{
// Code size 10 (0xa)
.maxstack 8
//000059: Private Sub LeavingMessage(ByRef strText As String)
IL_0000: nop
.line 60
//000060: debug.Write(strText)
IL_0001: ldarg.1
IL_0002: ldind.ref
IL_0003: call void [System]System.Diagnostics.Debug::
Write(class System.String)
.line 61
//000061: End Sub
IL_0008: nop
IL_0009: ret
} // end of method Form1::LeavingMessage
Code to call LeavingMessage Sub
finally
{
IL_002d: nop
.line 73
//000073: LeavingMessage("Goodbye Dear Friend")
IL_002e: ldarg.0
IL_002f: ldstr "Goodbye Dear Friend"
IL_0034: stloc.3
IL_0035: ldloca.s _Vb_t_string_0
IL_0037: callvirt instance void
GoodbyeVB6.Form1::LeavingMessage(class System.String&)
IL_003c: endfinally
.line 74
//000074: End Try
} // end handler
人們已經認識到了這個問題,一個常見的反駁意見是:在現實中,應用的規模很大,IL反匯編輸出結果的規模將超過可以忍受的限度。但是,它可能使一個業余愛好者望而卻步,卻不能阻止一個真正對代碼感興趣的人。實際情況是:與機器代碼的反匯編結果相比,ILDASM的反匯編結果要容易閱讀得多,任何對此感興趣的組織都能夠從IL反匯編結果了解到大量有關應用的信息。
按照Microsoft的意見,要保證企業機密安全,我們應該把所有包含企業機密的模塊放到受保護的服務器上。對于ASP.NET客戶機/服務器應用來說這沒問題,但對于標準的桌面應用來說它行不通。那么,如何才能對知識產權進行保護呢?MSIL匯編程序文檔提到了一個命令行參數/owner:
ilasm ... /owner
ilasm ... /owner=fergus
這個選項用密碼加密代碼,防止代碼被反匯編。問題在于Microsoft準備取消這個選項,因為它看起來不是一種好方法。這樣,對于用受管理的C++、C#或VB為.NET Beta 1編寫的桌面應用來說,要保護知識產權將非常困難。
但希望仍舊存在。在.NET最終發布之前,Microsoft可能提供一個模糊器(Obfuscator)程序,它能夠修改MSIL的私有方法,使得除CLR JIT編譯器之外沒有人能夠閱讀這些私有方法。但是,它不會隱藏應用的公用(全局)方法以及對外部庫的調用,這是因為:如果修改全局調用的名字或者隱藏這些調用,CLR將不能再鏈接到外部函數。因此,黑客們仍舊能夠通過查看IL代碼,找出應用調用系統DLL的各種信息。這樣,現在我們只能用一種方法對桌面應用的知識產權進行保護,即用非受管理的C++編寫關鍵性代碼,然后從VB.NET通過為訪問非受管理代碼提供的交互機制訪問它。當然,對于VB開發者來說,這可能比較困難。
由于所有受管理代碼必須以MSIL形式發布,所以在發布之前代碼不能進行JIT編譯。但是,在目標機器上安裝應用的時候,我們可以把代碼編譯成匯編形式。從表面上看來這很不錯,但代碼在安裝盤上仍舊是IL形式,我們可以手工從安裝盤提取出代碼,然而分別對它們進行反匯編。由于應用安裝完成后以編譯代碼而不是IL的形式存在,除了安全之外,它還能夠少量地提高應用運行的速度,因為此時我們不再需要JIT編譯器編譯IL代碼。
四、結束語
如果你是一個桌面應用的供應商,你清楚自己應該怎么做。你可以用非受管理的C++編寫代碼,然后從受管理的VB調用它。用這種方法設計應用,你能夠確信代碼的安全。然而,如果你是一個第三方供應商,而且準備在組件中用非受管理的代碼替代受管理的代碼,那么,你是在強迫用戶放棄.NET的優勢,重新讓他們面對他們今天所面臨的問題。受管理代碼能夠防止對應用本身或者其他應用所使用的內存空間進行破壞性操作,對受管理代碼的支持正是.NET吸引人的原因之一。某些用戶可能會查看受管理代碼的IL程序,甚至還有可能分析應用的算法實現,如果不能正確地認識.NET的優勢所在,第三方供應商可能會為了防止用戶分析代碼而拒絕用受管理代碼編寫各種軟件部件。
VB.NET/VS.NET有著許多優點,僅僅是對IDE(集成開發環境)的改進就足以成為我們升級到VB.NET的理由;語言方面的增強為我們帶來許多新的編程支持,對底層OS訪問的簡化使得我們聲明變量、對象以及調用低層功能更加方便。VB.NET是一個創建安全ASP.NET應用的優秀工具;但是,如果你的主要目標集中在客戶端或者是桌面應用,你應該慎重考慮可能出現的問題。Microsoft準備為桌面應用開發者提供哪些幫助?我們將拭目以待。
轉載于:https://www.cnblogs.com/it201108/archive/2005/02/20/2148490.html
總結
以上是生活随笔為你收集整理的.NET体系中的源程序安全问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vb的GUID生成算法
- 下一篇: 自由鸟书评排行网开始上线试运行,欢迎前往