相關閱讀 |
>>> 技術話題—商業文明的嶄新時代 >>> | 簡體 傳統 |
經過之前那個道德指責風波過后也有一段時間沒寫博客了,當然不是我心懷內疚才這么久不寫,純粹是程序員的通病。。怎一個懶字了得,本來想寫一些長篇大論反諷一下那些道德高人的。想想還是算了,那樣估計會引來新一波攻勢,沒什么實際意義,影響風氣,大家看了也不爽,這次寫點有實際意義的,說說我是怎么保護我的代碼的,個人心得,如有不妥的地方請見諒。
我們都知道.NET的代碼容易被反編譯出來,站在破解者的角度來看,破解一個軟件情況可分為幾種:
1.修改注冊驗證的代碼,達到繞過注冊的效果;
2.破解出注冊核心算法,寫出注冊機;
3.完全復制代碼,做成一個可編譯修改的項目;
從危害是上來看,第三種是最嚴重的,如果你的軟件被實力更強的競爭對手復制了,那簡直是毀滅性的打擊,理論上.NET沒有什么軟件是不能破解復制的,只要有高手死了心要破你的軟件,保護再好也是無意義的,那時只能認命,當然一般高手也不屑于破解一些沒意義的東西。我們雖然做不到完全保護代碼,但是我們可以增加他們的破解難度,防不了高手,至少我們要防住像我這種普通的程序員,不能隨便什么阿貓阿狗都能染指我們的代碼,這簡直是對程序員智商的侮辱嘛。
一般我們保護軟件的幾個重要過程:
1.設計機器碼;
2.根據機器碼生成注冊碼;
3.驗證注冊碼(本地+遠程驗證);
4.給程序集加強命名;
5.對程序集進行代碼混淆;
當然這么多步驟一次性也說不完,當前我主要就說說怎么隱藏自己的核心程序集,也就是.DLL動態庫。我們平時寫項目的時候,里面肯定會有各種各樣的動態庫出現,當我們編譯的時候都會有一個*.DLL的動態庫出現,DLL里包含了許多元數據,這就很容易被人用反編譯軟件完全看到里面的代碼,所以隱藏程序集就很重要了,思路是:把程序集存入內存中,到該用的時候才會去用它,這樣反編譯軟件就不能輕易的看到我們的代碼了,但是一個比較大的項目,把全部程序集存入內存是不現實的,我們只能把幾個比較核心的程序集,如注冊驗證過程,登陸過程,核心算法過程等隱藏,由于你隱藏了一些重要的程序集,一些人就算得到了你的其他代碼,想要軟件正確運行還是需要很大的工夫的。by Zengg
花了一點時間寫了一個隱藏程序集的的小DEMO,如下圖:
這個DEMO的主要功能是:
1.從外部打開某個DLL,并列出該DLL里一共有多少個類;
2.列出某個類里有多少個可執行的公開方法;
3.執行某個方法,會顯示執行的返回結果,前提是該方法具有返回值;
4.把從外部打開的DLL轉換成C#可用的代碼,方便大家存入內存;
5.從內存里讀取已經存入內存的程序集,并且執行它的某個類的方法;
下面我們看看項目的VS結構:
我們假設AssemblyTest這個類庫是存放我們注冊驗證的地方,AssemblyWPFDemo就是我們的主程序,一般來說,我們如果要用到AssemblyTest類庫的功能時,肯定要由主程序先引用該類庫,才能使用這個類庫的功能,這里面就存在一個問題,只要引用了類庫,在本地就會生成該類庫對應的DLL,這個就暴露了我們的代碼,下面的過程就是解釋,如何在不引用的情況下使用該類庫的功能。by Zengg
AssemblyTest類庫情況:
有兩個測試類,和一個WPF窗體。
namespace AssemblyTest { public class AssemblyTest1 { public string AssemblyTest1GetString() { return "這是Assembly1"; } } }
namespace AssemblyTest { public class AssemblyTest2 { public string AssemblyTest2GetString() { return "這是Assembly2"; } } }
WPF窗體預覽
這個窗體就是上面DEMO圖里彈出的那個窗口。
下面我們看看主要代碼是怎么實現的,在這之前你們可能得去了解C# Assembly的應用,里面我只寫一些簡單的注釋,大家自行去看:
核心代碼:
// *********************************************************************** // Assembly : AssemblyWPFDemo // Author : 曾意恒 // Created : 09-25-2013 // // Last Modified By : 曾意恒 // Last Modified On : 09-25-2013 // *********************************************************************** // <copyright file="AssemblyControl.cs" company=""> // Copyright 2013(c) . All rights reserved. // </copyright> // <summary>程序集操作類</summary> // *********************************************************************** using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; namespace AssemblyWPFDemo { /// <summary> /// Class AssemblyControl /// </summary> public class AssemblyControl { /// <summary> /// 把指定程序集轉化為二進制流 /// </summary> /// <param name="assemblyFileName">路徑程序集</param> /// <returns>二進制源</returns> public byte[] GetAssemblyToByte(string assemblyFileName) { byte[] assemblySource=null; if (File.Exists(assemblyFileName)) { FileStream fStream = new FileStream(assemblyFileName, FileMode.Open); BinaryReader bReader = new BinaryReader(fStream); assemblySource = bReader.ReadBytes(Convert.ToInt32(fStream.Length)); fStream.Close(); bReader.Close(); } return assemblySource; } /// <summary> /// 二進制源轉換為C#代碼,方便我們新建一個靜態類存入數據 /// </summary> /// <param name="memberNmae">字段名</param> /// <param name="assemblySource">二進制源</param> /// <returns>System.String.</returns> public string ByteConverToString(string memberNmae, byte[] assemblySource) { string strAssemblySource = "public byte[] " + memberNmae + "={"; foreach (byte b in assemblySource) { strAssemblySource += b.ToString() + ","; } strAssemblySource = strAssemblySource.Substring(0, strAssemblySource.Length - 1); strAssemblySource += "};\n"; return strAssemblySource; } /// <summary> /// 通過二進制數據源轉換為程序集 /// </summary> /// <param name="assemblySource">二進制源</param> /// <returns>程序集</returns> /// <exception cref="System.NullReferenceException">assembly為空,請檢查二進制數據源</exception> public Assembly GetAssemblyBySource(byte[] assemblySource) { Assembly assembly = Assembly.Load(assemblySource); if (assembly == null) throw new NullReferenceException("assembly為空,請檢查二進制數據源"); return assembly; } /// <summary> /// 獲取該程序集里有多少個類 /// </summary> /// <param name="assembly">程序集</param> /// <returns>類列表</returns> public Type[] GetTypesByAssembly(Assembly assembly) { if (assembly == null) return null; Type[] types = assembly.GetTypes(); return types; } /// <summary> /// 獲取類名 /// </summary> /// <param name="type">類</param> /// <returns>類名</returns> public string GetTypeName(Type type) { if (type == null) return ""; return type.Name; } /// <summary> /// 獲取類里的所有公開方法的信息 /// </summary> /// <param name="type">The type.</param> /// <returns>MemberInfo[][].</returns> public MemberInfo[] GetMemberInfosByType(Type type) { if (type == null) return null; return type.GetMethods(); } /// <summary> /// 獲取當前方法的名稱 /// </summary> /// <param name="menberInfo">The menber info.</param> /// <returns>System.String.</returns> public string GetMemberInfoName(MemberInfo menberInfo) { return menberInfo.Name; } /// <summary> /// 執行無返回值的方法 /// </summary> /// <param name="menberInfo">方法體</param> /// <param name="obj">類實體</param> public void InvokeMember(MemberInfo menberInfo,object obj) { obj.GetType().InvokeMember(menberInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, null); } /// <summary> /// 執行有返回值的方法 /// </summary> /// <param name="menberInfo">方法體</param> /// <param name="obj">類實體</param> /// <returns>返回結果</returns> public object InvokeMemberHaveResult(MemberInfo menberInfo, object obj) { return obj.GetType().InvokeMember(menberInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, null); } } }
AssemblyWPFDemo里的AssemblyTestSource類里存放的就是我們AssemblyTest類庫的二進制流,這個二進制流是通過我寫的這個DEMO轉換來的,可看上圖,里面有個“轉換代碼”的按鈕,當你打開了一個DLL后就可以把它轉換為C#代碼,通過加載這個數據流就能獲得AssemblyTest的程序集,有了程序集你就能使用程序集里的功能。由于AssemblyTestSource的代碼比較多,貼上來會影響排版,大家自行下載DEMO來看。
其他的就是一些簡單的邏輯處理了,不一一貼上來,我這里只是起著拋磚引玉的作用,寫得比較粗糙,大家要注意的是,程序集的二進制流要存放的隱蔽,要有藝術,不然別人還是能把二進制流取出來存入文件,這就又變成DLL了,有心研究的可以去研究一下把這些二進制數據進行個加密,盡量增加反編譯的難度。
這里我又要嘮叨一下破解者和軟件開發者的關系,我們寫軟件保護就是和破解者斗智斗勇的過程,這兩個是良性的競爭關系,如果你輸了只能說明你技不如人,回去回爐一下卷土再來,一味的指責并不能使你的技術提高,你得了解破解人的思路是怎么想的才能設計出更好的保護手段,我相信一個高明的破解者,也一定是一個高明的軟件開發者,這兩個關系并沒有什么分明的界限。。。另提前祝大家國慶快樂。。。下篇博客不知道什么時候才能出來了。。。以后的幾篇我可能會圍繞著保護這個主題來寫..
源碼:http://pan.baidu.com/s/1xc9aW
如果您看了本篇博客,覺得對您有所收獲,請點擊右下角的 [推薦]
如果您想轉載本博客,請注明出處
如果您對本文有意見或者建議,歡迎留言
感謝您的閱讀,請關注我的后續博客 Zengg
作者:Zengg 出處:http://www.cnblogs.com/01codeworld/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
Zengg 2014-07-04 22:20:51
稱謂:
内容: