在軟件的破解及源碼獲取及重新編譯的道路上會遇到一些問題,書此備查。
大名鼎鼎的Reflector以及開源的ILSPY都是.NET程序集的反編譯利器,但是它們不能為你做全部的工作。
0x01:
遇到反編譯所得源碼裡面調用類的屬性時多出set_Name或者get_Name之類的說明程序集引用沒有加載完全,
因為反編譯工具在沒有元數據的情況下無法判斷這丫的到底是個方法還是個屬性。
有時候反編譯得到的源碼裡的-1實際上可能是某個數的最大值,因為反編譯時沒有加載依賴的程序集,工具將類似int.MaxValue等值變成了-1。
所以將依賴項加載完是一個好的習慣,Reflector在反編譯時找不到依賴會提示你手動選擇,你可以手動選擇或者忽略(點取消就是忽略),而ILSPY不會提示。
0x02:
Reflector新版支持C#6的特性,會把一些屬性賦值反編譯為Lumbda表達式,而實際該項目可能不是C#6的項目,造成無法通過編譯器檢查。
解決辦法是生成程序集的源碼之前從主界面工具欄那個下拉列表選擇合適的.NET框架版本。
0x03:
一些WPF強名稱應用程序會由於資源文件也是強名稱引用導致反編譯為項目後跑不起來。
或許重新簽名然後替換資源引用的公鑰可以解決此問題但我沒有試過。
0x04:
在反編譯MVC Web項目時,控制器類和預編譯視圖裡(如果發布時啟用的了話)會出現很多類似這樣的類
[CompilerGenerated] private static class <Index>o__SiteContainer19 { public static CallSite<Func<CallSite, object, string, object>> <>p__Site1a; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1b; public static CallSite<Func<CallSite, object, DateTime, object>> <>p__Site1c; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1d; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1e; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1f; public static CallSite<Func<CallSite, object, string, object>> <>p__Site20; public static CallSite<Func<CallSite, object, string, object>> <>p__Site21; }
這些代碼是編譯器為優化性能生成的靜態類及靜態屬性。參考源碼後你會發現,這些代碼簡單的還是比較容易還原的,復雜的你就留著吧。
我的做法是干掉[CompilerGenerated]特性,批量替換類名<Index>o__SiteContainer19為o__SiteContainer19,然後替換屬性中的<>為空字符串。
0x05:
有時候我們並不需要源碼,只是需要修改某個值或一處代碼。所以我們需要的是Reflexil,當然還有更厲害的mono.cecil和dnlib,這是我知道的最牛逼的三個項目。
個人喜歡用ILSPY看源碼,然後用mono.cecil改程序。
可是修改後發現並不能運行,因為開發商使用了程序集強名稱。所以我一般會使用mono.cecil做如下操作來干掉干掉公鑰簽名
var asmdef = AssemblyDefinition.ReadAssembly(dll.FullName); if (asmdef.Name.PublicKey != null && asmdef.Name.PublicKey.Any()) { asmdef.Name.PublicKey = new byte[0]; asmdef.Name.PublicKeyToken = new byte[0]; asmdef.Name.Attributes = AssemblyAttributes.SideBySideCompatible; asmdef.MainModule.Attributes &= ~ModuleAttributes.StrongNameSigned;
}
可是修改後發現並不能運行,因為開發商使用了強名稱的InternalsVisibleTo特性,所以我一般會使用mono.cecil做如下操作來干掉InternalsVisibleToAttribute值裡面的公鑰
#region//0x02 干掉InternalsVisibleToAttribute值裡面的公鑰 var internalsVisibleToAttrs = asmdef.CustomAttributes.Where(x => x.AttributeType.Name == "InternalsVisibleToAttribute").ToList();foreach (CustomAttribute item in internalsVisibleToAttrs) { var argsctor = item.ConstructorArguments.Single(); var commaIndex = argsctor.Value.ToString().IndexOf(","); if (argsctor.Value != null && commaIndex != -1) //format is "AsmName,PublicKey=..." { var newValue = item.ConstructorArguments[0].Value.ToString().Substring(0, commaIndex);//format is "AsmName" item.ConstructorArguments[0] = new CustomAttributeArgument(argsctor.Type, newValue); var index = asmdef.CustomAttributes.IndexOf(item); asmdef.CustomAttributes.RemoveAt(index); asmdef.CustomAttributes.Insert(index, item); } }
可是修改後發現並不能運行,因為開發商使用了程序集強名稱導致編譯引用的也是強名稱的程序集,所以我一般會使用如下操作來干掉引用定義裡面的公鑰
//這裡假設引用的該產品的程序集都是SupperApp.*.dll格式的
var asmRefs = asmdef.MainModule.AssemblyReferences .Where(x => x.FullName.StartsWith("SupperApp", StringComparison.OrdinalIgnoreCase) && x.PublicKeyToken != null && x.PublicKeyToken.Any()) .ToList();foreach (var item in asmRefs) { item.PublicKeyToken = new byte[0]; }
以上操作後把變更寫入該軟件就可以順利運行了。
0x06:
當然有些加了殼的,一般情況需要找到對應的脫殼工具。如果只是修改關鍵邏輯通過關鍵詞查找應該能找到蛛絲馬跡,但前提是要能看到源碼。
對於既沒有工具能脫殼也沒有工具能看源碼的可以求助C++反匯編高手。