.net預防反編譯

Dotnet是一種建立在虛擬機上執行的語言,它直接生成 MSIL 的中間語言,再由DotNet編譯器 JIT 解釋映象爲本機代碼並交付CPU執行。它和Java是一種機制的語言。這種語言的優點就是您不需要去考慮您的程序在那裏運行,您只需要把功能做出來,虛擬機會在任何地方實現您的功能。這是一個很好的趨勢和想法,但虛擬機的中間語言由於帶了大量的“元數據metadada”信息,所以也極容易被反編譯。
Dotnet的保護分爲三大類
ms 提供的非第三方保護方案
a)            強名稱
強名稱是MS提供的保護機制。
它需要使用 sn 這個命令。
強名稱是什麼意思呢?在這裏稍作解釋。強名稱的作用就是防止程序集被非法修改,當對程序集修改後,必須重新用您的私鑰再對程序集加一次強名稱,這也是如果含有強名稱的程序集在混淆或加密後必須要重新加強名稱的原因。
Sn / ? 可以看到它的使用方法,如果你安裝的 Framework是中文的,那麼參數的解釋也是中文的,我就不多講了。
那麼強名稱有用嗎?網上輕鬆破解強名稱的方法很多,Ildasm反編譯加過強名稱的程序集後,在IL文件中將強名稱的相關信息去掉,再利用Ilasm編譯,就可以解除強名稱的限制了。強名稱的PublcKey不管是加在程序集中,還是加在Class中,都可以被去掉,所以強名稱不是一個完善的保護方式。
使用StrongNameIdentityPermissionAttribute
這個類允許你將組件(或類、方法)與某一強名稱(通常就是你發佈程序時所用的強名稱)綁定,這樣,只有在客戶端程序具有該強名稱簽名的情況下才能使用你的組件。也就是說,除了你自己編寫的客戶端代碼因爲擁有同樣的簽名而被允許使用組件以外,任何第三方代碼都無法通過StrongNameIdentityPermissionAttribute的防護,因此也就無法惡意調用你的組件了。
爲了簡便起見,先創建一個很簡單的Class Library項目,代碼如下:
    // SecureComp.dll
using System;
namespace musicland
{
   public class SecureComp
   {
   public string Confidential()
   {
   return "This is confidential!";
   }
   }
}
首先引入System.Security.Permissions命名空間:
using System.Security.Permissions;
然後,在組件級加上StrongNameIdentityPermissionAttribute屬性:
[assembly: StrongNameIdentityPermissionAttribute(SecurityAction.RequestMinimum,
PublicKey="0024000004800000940000000602000000240000525341310004000001000100c11c8497d”+“283259f23d645358d65812b69136846b03a7d15124545fc3ed27d89d1330cceda4232c7bc6e8a0e7ecd857f8”+“126d0859e2300237b3cab6f7737a92f585cbf2afb4b475c537703efb96e17e5921ff00c6e022b22f3d772f14”+“6a3a5c7f6ccad3131b8d0465e6709e5a28cc3ca1c8b610af4162c1a18c0feb8e6993ab1")]
namespace musicland
注意,這裏使用了SecurityAction.RequestMinimum,這申明除非獲得StrongNameIdentityPermissionAttribute所表明的資源訪問權限(即對SecureComp.dll組件的訪問權限,可以把SecureComp.dll看作一樣資源),否則CLR不會准許調用方(即客戶端代碼)訪問所請求的資源;此外,在PublicKey屬性中加入了你所允許的公匙(Public Key)的十六進制表示(轉化成字符串類型)CRL在運行期間將依照這一段公匙來判斷調用方是否合法,除非調用方擁有相應的私匙(Private Key),否則將無法訪問。看來,平時一定要倍加保護你的密鑰文件,因爲密鑰文件(特別是private key)的泄露將會成爲你無盡惡夢的根源,而延遲簽名(delay signing)在這裏也就顯得格外重要了
通過Sn.exe工具就可以把PublicKey給提取出來。打開命令行,定位到密鑰文件所在目錄並輸入以下內容:
  
sn –p Key.snk PublicKey.snk
這樣,提取出來的公匙信息就被存儲在PublicKey.snk文件中。你現在只需使用Secutil.exe把公匙信息讀取出來並轉化成適當的格式就可以了。
b)        編譯MSIL爲本機代碼(誤區?)
關於這一點,我經常能在MS上的社區看到有MVP這樣面對問題:
問:C#寫的程序能編譯成本機代碼嗎?
答:可以,使用 Ngen.exe 即可以 MSIL 代碼編譯爲 本機代碼。
MVP這樣回答錯了嗎?其實,嚴格的說,MVP的回答是沒錯的,Ngen.exe的確是可以將 MSIL 編譯爲本機代碼,並可以使JIT不需要進行再次編譯MSIL。這樣能加快程序的執行效率。
但用戶這樣的問題其實,並不是對執行效率不滿意,而是對中間語言不滿意,可惜 Ngen 並不能解決用戶的問題。
讓我們來淺淺的分析一下 Ngen的工作吧。
NgenMS提供的 本機映象生成器,它可以將中間語言程序集編譯爲本機代碼存放在緩存中。這裏請大家注意,是存放在緩存中,Dotnet在內存中建立了一個緩存,這個緩存中存放了許多常用的程序集編譯後的本機代碼,它們是常駐的,由此來加快Dotnet的執行速度。
所謂一個本機代碼,因爲本機映射時,會映射出一些 Framework 裏需要的Method,編譯爲彙編就是 Call 0x0200000這樣的樣子,而這些東西必須是事件編譯好的。那麼理論上說 Ngen 必須要在當前執行的機器上運行,而直接編譯成本機代碼的程序copy到另一個地方不一定可以用。Ngen.exe 只是一個提速的工具,因爲要執行編譯爲本機代碼必須還是要原程序集,而原程序集中存在MSIL,所以讓程序無法脫離被反編譯的目地。
以上是ms提供的工具,下在講講,自己在編程的過程中,如何使用技巧來防止破解或反編譯。
編程技巧保護方案
在這裏,我會給大家介紹兩種三種方式
1.       人爲混淆
混淆顧名思意,就是混亂,不明確的意思。MetaData中都有一個Rid,程序集運行時就已經和名稱沒什麼關係了,都使用Rid來調用的,所以可以將名稱省去。
什麼叫人爲混淆呢,就是人爲的製造混淆。
曾經看過一個程序集,手工的將一個Method折成幾十個或上百個,從而達到讓你看不懂的目的。不過可惜的說一句:現在的Dotnet程序集的分析工具都很強大,正引用,反調用都可以用程序來實現,所以即實這麼做,了沒多大用處。著名的Reflector就有這些功能。
2.       隱藏程序集
剛剛談到了Reflector,它就是使用這種方式來隱藏自己的核心程序集的。相信我,Reflector並不是您看到的那一個可執行程序,它的可執行程序只是一個殼而以,裏面是一個定義和接口,沒有實例的方法。如果你想得到他是怎樣反編譯的核心,恐怕你會在它這個迷宮中迷失方向。
它的核心程序集事實上就是它的一個資源。而這個資源是一個加密的資源。他應該是在雙擊第一個需要反編譯的Method的時候開始釋放這個資源,並對資源解密然後動態的加載。這樣做的優點核心程序集是不會在硬盤上留下任何痕跡的,它只解在內存中解密並被加載,你基本上無法得到這個程序集。而且Dotnet是不允許內存 Dump的。
大家是不是覺得這種保護方法不錯呢?你可以把你的核心代碼加密後做成資源包在程序裏,在使用的時候再解密出來,這只需要你自己去實現就可以了。
不過我還得說句負責任的話,如果你有精力,並且很有耐心和技術,相信你還是可以在幾天時間內找出它的核心程序集解密算法的位置。併成功的解出它的資源程序集。
如果是高手又非常有經驗,這種方式的加密手段應該是秒殺。
3.       將程序集中的相關Method(方法)編譯成Unmanaged(非託管代碼)
下面介紹的內容無法得到核心代碼
它可稱之爲終極的保護手段,因爲它就是“非託管代碼”。
什麼是託管代碼,什麼是非託管代碼。
託管代碼就是基於.net元數據格式的代碼,運行於.net平臺之上,所有的與操作系統的交換有.net來完成,就像是把這些功能委託給.net,所以稱之爲託管代碼。非託管代碼則反之。
下面要介紹的方式就是如何在自己的程序集中即擁有託管代碼,又擁有非託管代碼。注意,非託管代碼是無法被現在的反編譯工具反編譯的。
    Dotnet程序集中,允許託管代碼和非託管代碼共存,怎樣實現呢?這並不是無償的,這是需要條件的。它的條件就是必須使用VC++.NET非託管方式來寫dll,再用VC++託管方式建立工程引入這個本機代碼的dll。最終生成一個Dotnet程序集的dll。那麼這個程序集裏面即有託管代碼,又有非託管代碼。託管代碼是可以反編譯的,而非託管代碼不可能被反編譯。
    有人可能要問了,這和自己用VC++寫個dll有什麼區別?區別就是這樣的結合更緊密一些,而且也不能用常規的分析Asm的工具去分析這個dll
寫註冊算法,並生成dll供給Dotnet程序集調用,防止破解。其實這句話只說對了一半,這隻能增加破解註冊機的難度,並防止不了破解。爲什麼呢?因爲註冊對不對還是要在Dotnet程序集中進行判斷,所以,只要改掉這個判斷,一樣達到了破解效果。但是如果要分析註冊算法,那可就是困難了一些了。
第三方保護工具
第三方保護工具較好的廠商有:
1.       Aiasted.SOFT       
a)         產品 :MaxtoCode ,種類 :加密、混淆
2.       PerEmptive Solutions     
a)         產品 :Dotfuscator Community ,種類 :混淆
3.       Remotesoft
a)         產品 :Remotesoft Protect ,種類 :加密
b)        產品 :Remotesoft Dotfuscator ,種類 :混淆
4.       XenoCode
a)         產品 :XenoCode ,種類:混淆
第三方工具的保護方式分類
1.混淆
這是目前最流行的方式吧。今天我們就來做個剖析。讓大家去衡量一下混淆的強度如何。
混淆軟件一般都有三個功能
1.字符串加密
字符串加密可以分爲兩類,第一類是混淆保護中的字符串加密技術。主要特徵是修改代碼執行路徑。大部分混淆保護工具的字符串加密都是這一類。
加密前: MessageBox.Show("Hellow World!");
加密後: MessageBox.Show(Helper.Decode("A34579dfbbeyu346563345/=="));
第二類就是加密殼中的字符串加密技術。這種不用修改IL代碼,直接對元數據中的字符串加密。這一類以remotesoft,maxtocode爲代表。
實現就是直接對元數據中的String流進行加密,這類保護有一個缺陷,程序運行後 元數據中的String流會解密後在內存中完整還原。
2.名稱混淆
最簡單的混淆是名稱混淆,即將命名空間名、類名、方法名、字段名等統統換成特殊符號或其它符號,目的就是讓你不能與以前的名稱建立關聯。達到把你弄糊塗的目地。
比方如下代碼所示:
public class Register // 一個註冊類
{
public bool IsRegistered() // 判斷是否已註冊的方法
{
return true ;
}
}
這樣的代碼在程序編譯後,名稱完全被保留,但如果經過名稱反混淆以後,它將變成這樣:
public class a // 一個註冊類
{ public bool a() // 判斷是否已註冊的方法
{ return true ; }
}
我們現在深入的談談它的優點:
名稱混淆,如果使用短名稱及不可見字符,將會縮小程序集的大小
名稱混淆,因爲名稱混淆也只能騙騙門外漢和小孩
那麼它有什麼缺點呢?
名稱混淆的缺點並不多,只有一個,而且非常致命,這就是有時候,當修改了類名之後不能執行的問題。
一般來說,這種情況在 DLL 身上發生的更多,但在 EXE 身上也經常發生。
因爲 DLL 的某些 Public 方法是對外的結口,在程序開發和調試的時候使用的源名稱,當混淆以後,天知道把這些方法改成了什麼名稱,所以調用肯定報錯。處理辦法:不混淆對外提供的 Public 方法
EXE DLL 還有一個共同的容易出錯的地方就是資源,混淆器也可以混淆資源名稱,這樣,就存在的同樣的問題――“無法找到資源而報錯(動態 Load 資源的時候)”,處理方法:不混淆程序內部調用的東西。
3.流程混淆
原理基本上是一樣,即把方法中的代碼分爲幾段,並把每一段都錯開,然後利用“跳轉”語句連接原來的流程邏輯,並達到執行正確的目地。原理圖如下表所示:
塊編號 塊代碼
1 第一個功能
2 第二個功能
3 第三個功能
4 第四個功能
塊編號 塊代碼跳轉
1 第一個功能 Jmp 2
4 第四個功能
3 第三個功能 Jmp 4
2 第二個功能 Jmp 3
基本流程混淆原理即是上表所示,總結就以下這麼幾個字:破壞原有程序結構,並利用Jmp語句接連原有流程。
如果你要手工混淆你的代碼,你需要做以下幾件事:
把源代碼分成幾塊
把這麼幾塊的順序打亂
br.s 對這幾塊的順序進行連接,並保護執行達到原來的邏輯
重新計算行號
這樣,你就能擁有自己的流程混淆了。如果你加入真真假假的邏輯跳轉來混淆,強度將會更大。
反混淆
名稱混淆 - 反混淆
名稱混淆反混淆,基本上是不太可能的事,因爲以前的名稱已經換掉了,也沒有第二個名稱備份表,所以根本無法還換。
不過,可以把不可見字符轉換爲可見字符,長字符串換成短字符串。
有兩種方法可以做處理:
MetaData 中有一個區域叫做 _STRING 它存放了所有名稱字符串,只要修改這裏的內容,即可,此方法需要對元數據結構特別熟悉
如果你對元數據不瞭解,沒關係,你可以用 ILDasm 把混淆後的程序集反編譯,然後一個一個的對應改過來,再用 ilAsm 編譯,一樣可以達到反混淆的作用
流程混淆 - 反混淆
流程混淆,在上面已經給出例子。它纔是有用的一種混淆方式。它改變流程的存放序順,從而達到靜態反編譯的功能。(名稱混淆還是可以反編譯)
不過,不管怎樣,他沒有辦法去阻止讀取 IL ,這就是流程混淆的天生不足。反流程混淆也相當的容易,只要按照執行流程加入特定的條件即可以得到代碼的順序.
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章