.NET核心代碼保護策略-隱藏核心程序集

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統

  經過之前那個道德指責風波過后也有一段時間沒寫博客了,當然不是我心懷內疚才這么久不寫,純粹是程序員的通病。。怎一個懶字了得,本來想寫一些長篇大論反諷一下那些道德高人的。想想還是算了,那樣估計會引來新一波攻勢,沒什么實際意義,影響風氣,大家看了也不爽,這次寫點有實際意義的,說說我是怎么保護我的代碼的,個人心得,如有不妥的地方請見諒。

  我們都知道.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";
        }
    }
}
AssemblyTest1
namespace AssemblyTest
{
    public class AssemblyTest2
    {
        public string AssemblyTest2GetString()
        {
            return "這是Assembly2";
        }
    }
}
AssemblyTest2

  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);
        }
    }
}
AssemblyControl

  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

[新一篇] App Store上推廣App的實戰經驗

[舊一篇] 【個人開發者】我的應用開發心得
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表