利用Mono.Cecil動態修改程序集來破解商業組件(僅用於研究學習)


作者:Leepy

    Mono.Cecil是一個強大的MSIL的注入工具,利用它可以實現動態創建程序集,也可以實現攔截器橫向切入動態方法,甚至還可以修改已有的程序集,並且它支持多個運行時框架上例如:.net2.0/3.5/4.0,以及silverlight程序

官方地址:http://www.mono-project.com/Cecil

     首先,我先假想有一個這樣的商業組件,該組件滿足了以下條件:

1. 該程序集的代碼被混淆過了

2. 該程序集被強命名簽名過了

3. 該程序集的UI界面被加上了版權信息,例如水印等等

     這里我引用的是之前寫的一篇文章中的用戶登錄的組件(http://www.cnblogs.com/liping13599168/archive/2010/05/07/1729357.html),並將其進行改造成一個Silverlight組件,項目結構如圖:

image

其中BusinessComponent類庫作為選定的”商業組件",這里編寫一個CopyRight類,作為水印的打印信息:

復制代碼
internal  sealed  class Copyright 

     internal  void SetRightInfo(TextBlock textBlock) 
    { 
        textBlock.Text =  "This is a watermark."
    } 
}
復制代碼

運行效果為:

image

可以看到組件上面附帶一個版權信息效果

      接着,要對BusinessComponent進行強命名簽名,選擇項目右鍵,如下設置:

image

新建一個snk文件,這個文件利用RSA加密包含着公鑰和私鑰的信息,對該組件編譯會把它編譯進去了。

這個snk到底做什么用的呢?一會兒大家看着就知道了。

      最后,我要利用Dotfuscator混淆工具,對於BusinessComponent.dll進行代碼混淆

image

由於我這里的Dotfuscator使用的老版本的4.2,所以不支持對於Sliverlight程序集的混淆,有興趣的朋友可以自己下載試試,官方地址是:http://www.preemptive.com

 

      好了,到這里,一個BusinessComponent商業組件的雛形設計好了。現在拿起手上的工具——Mono.Cecil去改造它了。

首先官方下載了一個mono.cecil的項目,打開工程主動去編譯一下,在目錄Silverlight_Release就能夠獲取Mono.Cecil.dll

      那么,把它引入到項目中來,在頁面中加入一個按鈕,點擊觸發事件程序:

復制代碼
var entryPointPart = Deployment.Current.Parts.First(asmPart => asmPart.Source ==  "BusinessComponent.dll"); 
var entryPointResourceInfo = Application.GetResourceStream( new Uri(entryPointPart.Source, UriKind.Relative)); 
var module = ModuleDefinition.ReadModule(entryPointResourceInfo.Stream);
var type = module.Types.FirstOrDefault(o => o.Name ==  "Copyright"); 
var method = type.Methods.FirstOrDefault(o => o.Name ==  "SetRightInfo"); 
Instruction instruction = method.Body.Instructions[ 2]; 
instruction.Operand =  ""
module.Write( "C:\\BusinessComponent.dll"); 
復制代碼

      其中module獲取程序集中的Module模塊,module.Types可以得到程序集中的所有類型:

image

選擇Type的類型名稱為Copyright,通過type.Methods可以得到該類型得到的所有方法:

image

選擇Method的名稱為SetRightInfo,method.Body.Instructions可以得到該方法里面的IL堆棧調用信息:

image

我們看到索引2這行的信息,ldstr是定義一個字符串常量的方式,而索引3通過索引2行的值,賦值給TextBlock中,請查看Copyright的代碼就可以知道。於是:

Instruction instruction = method.Body.Instructions[ 2]; 
instruction.Operand =  "";

這樣就可以把該水印信息去除了,是不是很簡單呢:)

最后一行module.Write("C:\\BusinessComponent.dll");就是將修改過后的程序集重新寫入到一個新的程序集中;

執行程序,在C盤就得到一個BusinessComponent的新程序集了。

現在,我重新引入該新的程序集,看是否水印信息會被去掉,重新編譯,發現編譯出錯:

image

      這是為什么?這就是我上面提到的利用snk的強命名簽名了,它的目的是為了防止你的程序集被非法篡改而設置的,而需要破解它就需要它的私鑰了,可是作為一個真正的商業組件,我們是沒有辦法得到它的私鑰,那是不是意味着沒辦法破解了呢。

那么我重新通過sn.exe進行重新簽名:

sn –R BusinessComponent.dll mykey.snk

image

顯示公鑰不配對。這個是顯然的,因為之前通過VS的Sign標簽人工設置business.snk,它采用的是這個文件的公鑰,而mykey.snk就要采用它自己的公鑰了。

既然無法破解,那我就去改掉它的簽名,利用我自定義的公鑰和私鑰去做簽名,而恰恰好,Mono.Cecil就可以滿足你的需要。

     

打開VS2010命令提示窗口,在C盤上輸入:

sn -k mykey.snk

這樣就隨機產生了一條包含公鑰和私鑰的RSA文件,接着要抽取出它的公鑰,輸入:

sn –p mykey.snk mykey.PublicKey

緊接着輸入:

sn –tp mykey.PublicKey

就可以得到公鑰的數據,例如publicKey以及publicKeyToken

image

得到了兩串字符串,把它們都復制出來,繼續在剛才的按鈕代碼中補充:

復制代碼
var entryPointPart = Deployment.Current.Parts.First(asmPart => asmPart.Source ==  "BusinessComponent.dll"); 
var entryPointResourceInfo = Application.GetResourceStream( new Uri(entryPointPart.Source, UriKind.Relative)); 
var module = ModuleDefinition.ReadModule(entryPointResourceInfo.Stream); 
module.Assembly.Name.PublicKey = this.GetPublicKey(); 
module.Assembly.Name.PublicKeyToken = this.GetPubliKeyToken(); 
復制代碼

 

復制代碼
private  byte[] GetPublicKey() 

     string s =  "0024000004800000940000000602000000240000525341310004000001000100c5f2c7d8057257ea3640b93b7a98b1a5501718196589973b09b94cf47fb246c8ad86ae688caa36959d9793702c27ce198a447d69ba0ddac70075fab16748999f066795de472dfb5cd9347a10f967e750ca388aaa9a66619291003345b0176dec0008d3d88986c4605c0d60e3b6563f96984c6d28aeb4bf2d672e8d2d2123c394";
     return  this.StrToHexBytes(s); 

private  byte[] GetPubliKeyToken() 

     string s =  "d2850434514ae5ca"
     return  this.StrToHexBytes(s); 

private  byte[] StrToHexBytes( string hexString) 

    hexString = hexString.Replace( " """); 
     if ((hexString.Length %  2) !=  0
        hexString +=  " "
     byte[] returnBytes =  new  byte[hexString.Length /  2]; 
     for ( int i =  0; i < returnBytes.Length; i++) 
        returnBytes[i] = Convert.ToByte(hexString.Substring(i *  22),  16); 
     return returnBytes; 
復制代碼
其中StrToHexBytes方法,將字符串轉換為十六進制的字節數組

 

重新編譯執行,它的公鑰信息就已經編譯進去了,同樣在C盤生成BusinessComponent.dll新程序集

      這個時候,先不着急引入程序集,先對新程序集通過私密對其進行重新簽名,輸入命令:

sn –R BusinessComponent.dll mykey.snk

image

OK,現在就可以重新引入了,這個時候再重新編譯代碼:

image

編譯通過了。

現在運行下程序(這里要注意,你必須將SilverlightApp程序切換到OOB模式,才可以修改程序集):

image

水印信息已經被去除了:)

 

      Mono.Cecil還可以用來做很多東西,比如說可以在一個方法中增加其他代碼,能夠細到IL中的每一個執行步驟,所以通過它可以做很多有意思的東西,比如說單元測試中的Mock對象以及性能上更佳的反射功能。

 

附上本文源代碼:SilverlightMonoCecilDemo.rar

作者: Leepy
 
郵箱:sunleepy(AT)gmail.com
 
    
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM