在per method的dotNet加密中,首要解決的方法體對應關系,即在運行時加密殼如何確定當前要解密的方法體所對應的加密信息。
目前大部分加密殼都直接利用了dotNet的元數據來保存這種對應關系,我們知道在元數據中每個方法都會對應一個RVA值,加密殼可以直接把這個關系記錄在RVA的地址處。在框架運行中RVA處的數據會被作為“方法體”在處理流程中直接傳遞,加密殼通過攔截框架處理流程中的函數,來對“方法體”進行分流處理。即先判斷RVA處的數據是否“方法體加密對應信息”,如果是進入加密殼運行庫的內部處理,不是則按原框架流程處理。
對於這個“方法體加密對應信息”,最簡單的方式是指記錄一個指針信息,指向另一處數據塊,四字節空間就夠了。但是為了和普通沒有加密的方法體進行區分,除了這個之外還需要增加一些唯一標識以便能被運行庫在運行時安全無誤的區分出來。
大家可以用UE打開,加密後的程序集,看看一個方法體RVA處的數據,應該能很容易分別出來哪些是記錄的“方法體加密對應信息”。
正是這個原因,所以DNGuard v1.0和同類處理方式的加密殼,對方法體小於某個指定字節數的,就不能進行加密。
因為“方法體加密對應信息”的大小超過的方法體的空間大小,寫入的話會覆蓋到後面方法體的信息。這實際上也是因為偷懶造成的。可以通過對方法體進行重排來解決這個問題,當然要麻煩很多了。
這種模式實際上就是在元數據保存了一個虛擬表實現了MethodToken => “方法體加密對應信息”的對應記錄。這個表可以看著是公開的。
在DNGuard 2007中我沒有選擇使用對方法體重排的方式來解決這個問題,而是選擇了另一個方法,自己記錄一個 虛擬表實現:MethodToken => “方法體加密對應信息” 的對應記錄。
因為這樣有一個好處,就是這個虛擬表也可以進行加密後保存。另外,就是“方法體加密對應信息”中不需要添加標識符和普通沒有加密的方法體進行區分。
在DNGuard 2007試用版中沒有使用真正的加密算法來對程序集加密,只是采用了“代碼直接挪位”的方式,運行庫的“解密”操作只是從另一個位置直接讀取的操作。
有個朋友分析到DNGuard試用版裡面有一個虛擬表記錄了:MethodRid => ILCode。這個就是虛擬表:MethodToken => “方法體加密對應信息” 在試用版中退化的模式。
另外,因為方法體只是挪位,所以它實際上還是在程序集文件內,加載到內存中後也在程序集模塊的內存空間中。而不是那位朋友說的運行庫在解密後將IL代碼填回到內存裡面去了。
試用版只是提供給用戶驗證DNGuard是否適合自己的軟件項目以及系統發布環境,請不要用試用版加密程序集後直接分發。