[轉]強命名的延遲與關聯在.net程序集保護中的作用及其逆向方法

強命名的延遲與關聯在.net程序集保護中的作用及其逆向方法
[email protected]
2006-9-15

一、老調重談強命名
    強命名的定義這裏就不重複了,不妨就把他看作一個文件的hash,而如果文件被修改的話,計算出的hash將與最被程序設計者給定的強命名不一致,程序將拒絕加載。這可憐的一點點安全特性被人用多種方法證實原來靠強命名保護程序集只是紙老虎。
至少有三種方法可以去除單獨的可執行文件的強命名:
1、  ildasm反編譯,在il源代碼中刪除該assembly對強命名的引用,再編譯回去。在.net初期時,這種方法還是很好用的,codeproject上也介紹過。但是現在的程序對於ildasm的anti越來越強,想完整的反編譯再完整地編譯回去,有時還不太容易。
 

2、  利用工具,原理是將CLI Header的標誌置0
CLI Header的結構如下:

RVA  Field                       Contents
0x2008  Cb(結構的大小)            0x48
0x200C  MajorRuntimeVersion            2
0x200E  MinorRuntimeVersion            0
0x2010  MetaData                       0x2060
0x2014  Size of the Metadata            0x148 =(RVA of Import Table) – (RVA of MetaData)
0x2018  Flags                       1
0x201C  EntryPointToken             0x06000001 (Method #1 in TypeDef table)
0x2020  Resources                        0
0x2028  StrongNameSignature             0
0x2030  CodeManagerTable             0
0x2038  VTableFixups             0
0x2040  ExportAddressTableJumps  0
0x2048  ManagedNativeHeader             0

包括flags標誌要減去COMIMAGE_FLAGS_STRONGNAMESIGNED (0x00000008) ,以及StrongNameSignature處表示的強命名的偏移位置和大小要置0。這還沒完,還要將Metadata表中AssemblyDef處的強命名標誌刪除。
來看一下AssemblyDef的定義:

• HashAlgId (a 4-byte constant of type AssemblyHashAlgorithm).
• MajorVersion, MinorVersion, BuildNumber, RevisionNumber
(2-byte constants).
• Flags (a 4-byte bit mask of type AssemblyFlags).
• PublicKey (index into Blob heap).
• Name (index into String heap).
• Culture (index into String heap).

其中Flags項要減去afPublicKey = 0x0001。

3、就是利用Patch系統文件的形式。這種形式不推薦,畢竟強命名還有判別版本差異及文件完整性的作用,整個Patch了不大方便。


二、延遲強命名
    現在的.net程序都被混淆過了。比如你原來寫了個計算註冊碼的程序叫CalRegCode(),但是混淆過的程序名可能是x0ab23ff10(),或者乾脆是不可打印的ASCII字符。但如果你在程序中使用了強命名,混淆不會出錯嗎?這裏其實就要用到延遲強命名。
    在VisualStudio中,點擊項目的屬性的sign選項,選擇延遲強命名:
 

如果不想在窗口中設置,可以直接在AssemblyInfo.cs中設置如下:
   [assembly:AssemblyKeyFileAttribute("key.snk")]
    [assembly:AssemblyDelaySignAttribute(true)]
    延遲強命名就是在編譯程序時設置好標誌,並將存儲空間留出,在編譯完成後人工加上。比如,我們可以將strings流中的一些敏感名稱,比如crypt()、crypcal()改爲不可見:



    保存後在命令行用:sn –Ra xxxx.exe key.snk重新給文件籤屬強命名便OK了。
    修改名稱有個注意事項,如果你修改的方法名稱,在別的程序集中對此方法有調用的話,必須在調用的文件中也把相應的方法改爲相同的名字,應爲這種調用是按名稱來的,否則會報找不到method的錯誤。但總的來說,如果僅僅對一個文件使用這種延遲強命名的方法,仍然是非常容易修改的。但如果一個主程序a.exe要調用b.dll和c.dll時(甚至更多,或者b與c間存在調用),而b.dll和c.dll都被加上強命名的話,這樣如果修改b.dll(比如網絡驗證在b.dll中,我們要對其進行patch),整個程序則會報錯。下面一節就講講這種整個程序中存在多個文件,且每個文件都有強命名的情況,看看他的保護強度和破解。我暫時給它起名叫關聯強命名。


三、關聯強命名
    我們來建一個C#工程,名叫example,其中存在兩個dll(a.dll和b.dll),主程序對這兩個程序分別存在調用,dll的代碼只是彈出窗口顯示被調用了,且每個dll都被加上強命名。並在主項目中添加對a和b的引用。


    a與b中的代碼如圖所示。主程序中建兩個button控件,分別調用這兩個Class Library中的方法。

代碼如下:
        
private void button1_Click(object sender, EventArgs e)        {            a.Class1 newa=new a.Class1();            newa.showMsg();        }        private void button2_Click(object sender, EventArgs e)        {            b.Class1 newb=new b.Class1();            newb.showMsg();        }


單擊不同的button會顯示不同的窗口,提示當前是哪個lib被調用。如,單擊checkLib1的窗口如下:
  

    由於a.dll與b.dll中已被加上強命名,我們試着修改一下,看看能否運行。比如將a.dll中窗口的顯示字符改爲“ClassLibrary A is called”(UserString的存儲格式爲Unicode):
00001590h: 53 68 6F 77 00 00 00 00 00 31 43 00 6C 00 61 00 ; Show.....1C.l.a.
000015a0h: 73 00 73 00 4C 00 69 00 62 00 72 00 61 00 72 00 ; s.s.L.i.b.r.a.r.
000015b0h: 79 00 20 00 41 00 20 00 69 00 73 00 20 00 63 00 ; y. .A. .i.s. .c.
000015c0h: 61 00 6C 00 6C 00 65 00 64 00 00 00 E5 79 88 40 ; a.l.l.e.d...鍄園

運行後單擊checkLib1,會報錯無法載入a.dll:
 

    這樣看來,如果我們將關鍵代碼分佈在多個dll中,且每個dll加上強命名,並且存在一定的相互調用,就可以保護我們的程序集了。是不是這樣呢?我們來看看example.exe中對a.dll的引用,看爲什麼它會認出我們修改了文件。
    Example.exe中的AssemblyRef表中存儲了關於a.dll和b.dll的信息,AssemblyRef表的結構如下:
The AssemblyRef table has the following columns:
•  MajorVersion, MinorVersion, BuildNumber, RevisionNumber (2-byte constants)
•  Flags (a 4-byte bitmask of type AssemblyFlags; see Partition II, section 22.1.2)
•  PublicKeyOrToken (index into Blob heap—the public key or token that identifies the author of this assembly)
•  Name (index into String heap)
•  Culture (index into String heap)
•  HashValue (index into Blob heap)

    其中第三項就規定了是否有PublicKeyOrToken,也就是是否有強命名。看看example.exe中的數據:



其中PublicKeyOrToken項清清楚楚地寫的0x00be,這就是有強命名的標誌,強命名存儲在#Blog表中第0x00be項。再來看一下#Blog表,第0x00be項如下:

 

到這一步,我們就應該知道怎麼修改了。首先,將example.exe中對a的引用表中的PublicKeyOrToken項的值改爲0,然後第二步是去除a.dll中的強命名。修改完成後,我們再運行,果然,系統已經不再報錯,並正確顯示我們所修改的字符串了:

 

四、小結
    到這裏,強命名的幾種應用形式基本介紹完。試想,如果一個有數十個文件的程序每個文件都被加個強命名,其保護強度也是很一般的。我們只須編寫程序自動去除每個文件的本身強命名定義與AssemblyRef中的強命名定義(注意,不要把系統文件包含在內),就可以很方便地進行修改。因此,用StrongName做爲程序的保護手段實在是不可取,還應另尋別路。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章