大家都知道,對於用.NET開發的應用程序而言,是很容易被反編譯的。如果我們的應用程序中有一些比較隱秘的東西(如注冊算法),我們是很不希望被其它人知道的,所以我們需要保護自己寫的.NET程序。
目前保護.NET應用程序主要還是靠混淆,並且也不乏一些很強大的混淆軟件,本文主要是從另外一個方向下手,基於文件的保護,而不是代碼層面的。
話不多說,我們從實際操作開始,邊操作我會邊解釋為什么是基於文件的保護。
1、首先我們打開Visual Studio,建立一個解決方案 slnNProtect 。
2、在解決方案中添加一個 Windows窗體應用程序項目 Client,這個Client項目便是我們要保護的.NET應用程序。
3、為了驗證保護后的應用程序是否能夠正確的讀取配置文件,我們給Client添加一個App.config文件。
最后項目目錄結構如下:
FrmMain 是我將默認的 Form1 重新命名了一下,該窗體的設計界面如下:
為了方便讀寫配置文件(App.config),我們增加對 System.Configuration.dll 的引用,FrmMain 的后台代碼如下:
using System; using System.Configuration; using System.Windows.Forms; namespace Client { public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } private void btnHelloWorld_Click(object sender, EventArgs e) { MessageBox.Show("Hello World!!!"); } private void FrmMain_Load(object sender, EventArgs e) { txtFromConfig.Text = ConfigurationManager.AppSettings["KEY"].ToString(); } } }
App.config 內容如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="KEY" value="Hello World!!"/> </appSettings> </configuration>
程序相當的簡單,我們只是在窗體加載的時候,將配置文件中的內容顯示到文本框里,並且在點擊按鈕的時候彈出一個經典的 Hello World,除此之外,我們沒有做任何事情。
介於這個程序過於簡單,我們就不做太多深入的討論,下面我們來談談如何保護它。
如果對 PE 結構了解的朋友應該都知道,將一個 原生的Windows應用程序 通過內存釋放運行是相當困難的,很多加殼軟件都是針對 PE 內容進行壓縮,然后在運行時解壓釋放,如 UPX。相比於 原生的Windows應用程序 ,DotNet 的釋放運行就要簡單的多,因為框架已經幫我們實現了。
下面就來給我們的應用程序做一個簡單的包裹。
1、在解決方案中添加一個新的 Windows窗體應用程序項目 Host。
2、將自動生成的 Form1 刪除。
3、將 Client 項目輸出的 Client.exe 用資源的方式添加到 Host 項目中。
4、添加App.config文件。
最后項目目錄如下:
Program.cs 文件中,我們寫下如下代碼:
using System; using System.Collections.Generic; using System.Windows.Forms; using System.Reflection; namespace Host { static class Program { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { //以字節的方式加載 Assembly asm = Assembly.Load(Properties.Resources.Client); MethodInfo mi = asm.EntryPoint; mi.Invoke(null, null); } } }
代碼還是相當簡單,這里最重要的便是: Assembly.Load(Properties.Resources.Client); 通過這句,我們便將一個字節數組轉換成了一個 DotNet 程序集,通過反射找到該程序集的入口函數(必須是 DotNet 下的可執行程序),然后調用,便啟動了我們包裹的這個應用程序。
通過測試,我們發現配置文件依然是可以有效使用的。
現在我們需要保護的應用程序已經得到了比較簡單的保護,這種保護並不安全,我們可以很容易的將 Host 反編譯,並提取其中的資源。為了更好的保護我們的應用程序,我們需要采取更加強硬的保護措施。
1、添加一個類庫項目到解決方案 NLibraryCom。
2、將 Client 項目輸出的 Client.exe 用資源的方式添加到 NLibraryCom 項目中。
3、添加一個類文件 ClassMain.cs。
最后項目目錄如下:
從項目的名稱上來看就知道,我們要將這個類庫以 COM 的方式對外暴露,所以,我們要讓這個項目對 COM 可見,Properties 目錄下的 AssemblyInfo.cs 文件中,添加這樣一句(或修改為)[assembly: ComVisible(true)] ,即可。
ClassMain.cs 文件中的內容如下:
using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Runtime.InteropServices; namespace NLibraryCom { [Guid("CBBF9F0A-373D-4827-A1C8-83C16853868E")] public interface INApplication { void Run(); } [Guid("5622CA35-F02D-47cc-A228-AE372C5959F6")] [ClassInterface( ClassInterfaceType.None)] public class NApplication : INApplication { #region INApplication 成員 public void Run() { Assembly asm = Assembly.Load(Properties.Resources.Client); MethodInfo mi = asm.EntryPoint; mi.Invoke(null, null); } #endregion } }
關於 COM 的理論和編程,我這里不作太多贅述,這里大家只要清除,我們對外暴露了一個 INApplication 的接口,可以給其它任何支持 COM 的編程語言來調用,這個接口中只有一個 Run 的方法,而這個 Run 便是調用我們包裹的程序。
我們生成這個 NLibraryCom.dll,然后注冊它,通過 VS 的命令行定位到該文件的目錄下,然后執行下面的命令:
regasm NLibraryCom.dll tlb:NLibraryCom.tlb
注意:如果 UAC 打開了,則必須用管理員的身份來運行 VS 的命令行。
到這里,我們 DotNet 平台上要做的事情就結束了,下面我以 Delphi 2010 為例,來調用我們寫的這個 COM 組件。
1、打開 Delphi 2010,如果 UAC 打開了,請以管理員身份來打開 Delphi 2010(這點很重要)。
2、新建一個 VCL Forms Application。
3、刪除自動創建出來的 Form,打開 Project 的源碼,將begin 和 end 之間的代碼清空。
4、載入DotNet 開發的 Com 組件。
在彈出的向導中,選擇第一個 Import a Type Library,點擊Next
在 Registered Type Libraries 頁中,我們選擇添加,然后選擇我們注冊 NLibraryCom.dll 時生成的 NLibraryCom.tlb。
然后一直 Next 即可。
經過上面的操作,我們便將一個 DotNet 的 COM 組件添加到了 Delphi 中,最后,我們調用即可:
運行程序,一切如期而至,並且 App.config 依然可以正確讀取使用,至此,我們的 Client 變成了一個類似 原生的Windows應用程序,但是並不完美。
思考:
1、我們的應用程序 Client 目前依然是在一個 DotNet 應用程序中(NLibraryCom.dll),如果反編譯該類庫,依然可以很輕松的得到它,如何解決這個問題呢?
2、如此保護,維護應用程序會很困難,除了發布時要包裹,部署時還要注冊 COM,如何避免這么多麻煩?
解釋:
1、我們可以將 Client 放到任何地方(比如 Delphi 程序的資源文件中),只要修改暴露出的 COM 接口,讓它接受一個字節數組即可(Run(byte[] rawDatas);)。
2、部署時的 COM 注冊,可以讓宿主程序自動完成(本例中的 Delphi 程序),至於包裹程序,要知道,目前大多數混淆軟件,也是需要做額外的混淆操作的,為了程序的安全,我們完全可以接受這一點點的小麻煩。並且,我們可以讓宿主程序做更多的事情,比如檢測有沒有 DotNet 的運行環境,或則直接在宿主程序寫入注冊邏輯,畢竟目前大多數加殼軟件針對的都是 原生的Windows應用程序。
最后,本篇文章就到這里了,只能算是拋磚引玉,若有不對的地方,歡迎指正,大家一起交流。