https://mp.weixin.qq.com/s/ttqbSjjbJPnW1DMJojONXw

前言
首先,对于.NET/C#生成的托管的代码,没有100%的保护措施,因为它们的程序执行最终会到二进制/汇编这个层面。而二进制/汇编层面可以针对性的对于指令集(Risc-v/Arm64/x64/Loongarch64)进行反汇编逆向重构。所以,对于.NET/C#生成的托管的代码,能做的只能是有限度的保护、增加理解难度。

软件保护
20年前,.Net刚诞生的时候有个笑话。有人嘲讽微软做托管代码,是为了方便别人破解。事实证明,托管确实比非托管更容易破解和篡改。MSIL遵循了一整套的ECMA-335的规范,对于这个规范了如指掌的程序开发人员来说,托管代码就像纸糊的一样,戳哪儿哪儿就破。

但绝大多数99%的软件工程师是在做业务开发、为商业服务,他们往往只需要懂.NET/C#这种高级语言就够了,很少有人会去研究&也没必要关心MSIL以及汇编这类低级语言(往往只有黑客、做编译器、驱动开发的那1%的程序员需要懂低级语言)。

所以,只需要对.NET/C#托管代码进行混淆加密,就能防范住99%的破解。

.Net加密工具
目前市面上流行的.Net加壳软件大致有如下:

Dotfuscator:Visual Stuido自带的,你可以自己去安装使用。混淆后的程序改变了方法名和一些变量名,降低了代码可读性。

dotNET Reactor:主要功能:NecroBit IL(转为非托管代码)、反 ILDASM(反编译器)、混淆代码、合并、压缩源码等。效果比VS自带的Dotfuscator要好。

ILProtector:可保护您的 .NET 代码免受逆向工程、反编译和修改。

Xenocode Fox:反编译工具,混淆工具,只能针对早期.NET Framework代码使用。

SmartAssembly:一款.NET代码加密保护软件,里面内置的选项方面是蛮多的,能够往程序里面注入一些没用的方法和类来混淆代码结构。使用该软件加密混淆后效果比.NET Reactor好一些。

DNGuard HVM:加密功能很强大,混淆后的程序很难被破解,效果更好。

商业授权

我们做的商业软件需要进行售卖,为了收取费用,一般需要一个软件使用许可证,然后输入这个许可到软件里就能够使用软件(比如,一串序列码或者一个许可证文件)。于是有的小伙伴就开始好奇这个许可是怎么实现的。

实现许可证的关键点1. 如何控制只在指定设备上使用
如果不控制指定设备,那么下发了许可证,只要把软件复制多份安装则可到处使用,不利于版权维护。但我们想想,有什么办法唯一标识一台电脑?答案就是:mac地址,ip地址,主板序列号等。在许可证中指定这些唯一标识即可实现指定设备使用。
实现许可证的关键点2. 如何控制软件使用期限
为了版权可持续性收益,对软件使用设置期限,到期续费等,则需要在许可证中配置使用起止日期。

其实,在前述的.Net加密工具中,往往就附带有制作许可证实现商业授权的功能。但我想各位小伙伴可能更想知道具体的实现原理、以及后期自己开发此类功能?下面就把关键的【核心源码】展现给大家参考:
【核心源码】
1.1 电脑信息获取
主要通过ManagementClass进行获取客户端电脑硬件相关配置信息,如下所示:

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace DemoLicence.Common
{
    public class ComputerHelper
    {
        public static Dictionary<string,string> GetComputerInfo()
        {
            var info = new Dictionary<string,string>();
            string cpu = GetCPUInfo();
            string baseBoard = GetBaseBoardInfo();
            string bios = GetBIOSInfo();
            string mac = GetMACInfo();
            info.Add("cpu", cpu);
            info.Add("baseBoard", baseBoard);
            info.Add("bios", bios);
            info.Add("mac", mac);
            return info;
        }
        private static string GetCPUInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_Processor", "ProcessorId");
            return info;
        }
        private static string GetBIOSInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_BIOS", "SerialNumber");
            return info;
        }
        private static string GetBaseBoardInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_BaseBoard", "SerialNumber");
            return info;
        }
        private static string GetMACInfo()
        {
            string info = string.Empty;
            info = GetMacAddress();//GetHardWareInfo("Win32_NetworkAdapterConfiguration", "MACAddress");
            return info;
        }

        private static string GetMacAddress()
        {
            var mac = "";
            var mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            var moc = mc.GetInstances();
            foreach (var o in moc)
            {
                var mo = (ManagementObject)o;
                if (!(bool)mo["IPEnabled"]) continue;
                mac = mo["MacAddress"].ToString();
                break;
            }
            return mac;
        }

        private static string GetHardWareInfo(string typePath, string key)
        {
            try
            {
                ManagementClass managementClass = new ManagementClass(typePath);
                ManagementObjectCollection mn = managementClass.GetInstances();
                PropertyDataCollection properties = managementClass.Properties;
                foreach (PropertyData property in properties)
                {
                    if (property.Name == key)
                    {
                        foreach (ManagementObject m in mn)
                        {
                            return m.Properties[property.Name].Value.ToString();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //这里写异常的处理
            }
            return string.Empty;
        }
    }
}

1.2 RSA非对称加密
主要对客户端提供的电脑信息及有效期等内容,进行非对称加密,如下所示:

public class RSAHelper
{

  private static string keyContainerName = "star";

  private static string m_PriKey = string.Empty;

  private static string m_PubKey = string.Empty;


  public static string PriKey
  {
    get
    {
      return m_PriKey;
    }

    set
    {
      m_PriKey = value;
    }
  }

  public static string PubKey
  {
    get
    {
      return m_PubKey;
    }

    set
    {
      m_PubKey = value;
    }
  }

  public static string Encrypto(string source)
  {
    if (string.IsNullOrEmpty(m_PubKey) && string.IsNullOrEmpty(m_PriKey))
    {
      generateKey();
    }
    return getEncryptoInfoByRSA(source);
  }

  public static string Decrypto(string dest)
  {
    if (string.IsNullOrEmpty(m_PubKey) && string.IsNullOrEmpty(m_PriKey))
    {
      generateKey();
    }
    return getDecryptoInfoByRSA(dest);
  }

  public static void generateKey()
  {
    CspParameters m_CspParameters;
    m_CspParameters = new CspParameters();
    m_CspParameters.KeyContainerName = keyContainerName;
    RSACryptoServiceProvider asym = new RSACryptoServiceProvider(m_CspParameters);
    m_PriKey = asym.ToXmlString(true);
    m_PubKey = asym.ToXmlString(false);
    asym.PersistKeyInCsp = false;
    asym.Clear();
  }

  private static string getEncryptoInfoByRSA(string source)
  {
    byte[] plainByte = Encoding.ASCII.GetBytes(source);
    //初始化参数
    RSACryptoServiceProvider asym = new RSACryptoServiceProvider();
    asym.FromXmlString(m_PubKey);
    int keySize = asym.KeySize / 8;//非对称加密,每次的长度不能太长,否则会报异常
    int bufferSize = keySize - 11;
    if (plainByte.Length > bufferSize)
    {
      throw new Exception("非对称加密最多支持【" + bufferSize + "】字节,实际长度【" + plainByte.Length + "】字节。");
    }
    byte[] cryptoByte = asym.Encrypt(plainByte, false);
    return Convert.ToBase64String(cryptoByte);
  }

  private static string getDecryptoInfoByRSA(string dest)
  {
    byte[] btDest = Convert.FromBase64String(dest);
    //初始化参数
    RSACryptoServiceProvider asym = new RSACryptoServiceProvider();
    asym.FromXmlString(m_PriKey);
    int keySize = asym.KeySize / 8;//非对称加密,每次的长度不能太长,否则会报异常
                     //int bufferSize = keySize - 11;
    if (btDest.Length > keySize)
    {
      throw new Exception("非对称解密最多支持【" + keySize + "】字节,实际长度【" + btDest.Length + "】字节。");
    }
    byte[] cryptoByte = asym.Decrypt(btDest, false);
    return Encoding.ASCII.GetString(cryptoByte);
  }
}

1.3 生成文件
主要是加密后的信息,和解密秘钥等内容,保存到文件中,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DemoLicence.Common
{
    public class RegistFileHelper
    {
        public static string ComputerInfofile = "ComputerInfo.key";
        public static string RegistInfofile = "Licence.key";
        public static void WriteRegistFile(string info,string keyFile)
        {
            string tmp = string.IsNullOrEmpty(keyFile)?RegistInfofile:keyFile;
            WriteFile(info, tmp);
        }
        public static void WriteComputerInfoFile(string info)
        {
            WriteFile(info, ComputerInfofile);
        }
        public static string ReadRegistFile(string keyFile)
        {
            string tmp = string.IsNullOrEmpty(keyFile) ? RegistInfofile : keyFile;
            return ReadFile(tmp);
        }
        public static string ReadComputerInfoFile(string file)
        {
            string tmp = string.IsNullOrEmpty(file) ? ComputerInfofile : file;
            return ReadFile(tmp);
        }

        private static void WriteFile(string info, string fileName)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(fileName, false))
                {
                    sw.Write(info);
                    sw.Close();
                }
            }
            catch (Exception ex)
            {
            }
        }
        private static string ReadFile(string fileName)
        {
            string info = string.Empty;
            try
            {
                using (StreamReader sr = new StreamReader(fileName))
                {
                    info = sr.ReadToEnd();
                    sr.Close();
                }
            }
            catch (Exception ex)
            {
            }
            return info;
        }
    }
}

以上这三部分,各个功能相互独立,通过LicenceHelper相互调用,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace DemoLicence.Common
{
    public class LicenceHelper
    {
        /// <summary>
        /// 获取电脑信息,并生成文件
        /// </summary>
        public static string GetComputerInfoAndGenerateFile()
        {
            string computerKeyFile = string.Empty;
            try
            {
                var info = GetComputerInfo();
                if (info != null && info.Count > 0)
                {
                    //获取到电脑信息
                    var strInfo = new StringBuilder();
                    foreach (var computer in info)
                    {
                        strInfo.AppendLine($"{computer.Key}={computer.Value}");
                    }
                    RegistFileHelper.WriteComputerInfoFile(strInfo.ToString());
                    computerKeyFile = RegistFileHelper.ComputerInfofile;
                }
            }catch(Exception ex)
            {
                throw ex;
            }
            return computerKeyFile;
        }

        public static Dictionary<string,string> GetComputerInfo()
        {
            var info = ComputerHelper.GetComputerInfo();
            return info;
        }

        public static bool CheckLicenceKeyIsExists()
        {
            var keyFile = RegistFileHelper.RegistInfofile;
            if (File.Exists(keyFile))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public static string GetComputerInfo(string computerInfoFile)
        {
            return RegistFileHelper.ReadComputerInfoFile(computerInfoFile);
        }

        public static void GenerateLicenceKey(string info,string keyfile)
        {
            string encrypto = RSAHelper.Encrypto(info);
            string priKey = RSAHelper.PriKey;//公钥加密,私钥解密
            byte[] priKeyBytes = Encoding.ASCII.GetBytes(priKey);
            string priKeyBase64=Convert.ToBase64String(priKeyBytes);
            StringBuilder keyInfo= new StringBuilder();
            keyInfo.AppendLine($"prikey={priKeyBase64}");
            keyInfo.AppendLine($"encrypto={encrypto}");
            RegistFileHelper.WriteRegistFile(keyInfo.ToString(), keyfile);
        }

        public static string ReadLicenceKey(string keyfile)
        {
            var keyInfo = RegistFileHelper.ReadRegistFile(keyfile);
            if (keyInfo == null)
            {
                return string.Empty;
            }
            string[] keyInfoArr = keyInfo.Split("\r\n");
            var priKeyBase64 = keyInfoArr[0].Substring(keyInfoArr[0].IndexOf("=")+1);
            var encrypto = keyInfoArr[1].Substring(keyInfoArr[1].IndexOf("=")+1);
            var priKeyByte= Convert.FromBase64String(priKeyBase64);
            var priKey = Encoding.ASCII.GetString(priKeyByte);
            RSAHelper.PriKey= priKey;
            var info = RSAHelper.Decrypto(encrypto);
            return info;
        }

        public static string GetDefaultRegisterFileName()
        {
            return RegistFileHelper.RegistInfofile;
        }

        public static string GetDefaultComputerFileName()
        {
            return RegistFileHelper.ComputerInfofile;
        }

        public static string GetPublicKey()
        {
            if (string.IsNullOrEmpty(RSAHelper.PubKey))
            {
                RSAHelper.generateKey();
            }
            return RSAHelper.PubKey;
        }

        public static string GetPrivateKey()
        {
            if (string.IsNullOrEmpty(RSAHelper.PriKey))
            {
                RSAHelper.generateKey();
            }
            return RSAHelper.PriKey;
        }
    }
}
文档更新时间: 2023-09-23 06:12   作者:admin