1. 程式人生 > >C#實現軟體授權,限定MAC執行(軟體license管理,簡單軟體註冊機制)

C#實現軟體授權,限定MAC執行(軟體license管理,簡單軟體註冊機制)

最近做了一個綠色免安裝軟體,領導臨時要求加個註冊機制,不能讓現場工程師隨意複製。事出突然,只能在現場開發(離開現場軟體就不受我們控了)。花了不到兩個小時實現了簡單的註冊機制,稍作整理。 
基本原理:1.軟體一執行就把計算機的CPU、主機板、BIOS、MAC地址記錄下來,然後加密(key=key1)生成檔案;2.註冊機將該檔案內容MD5加密後再進行一次加密(key=key2)儲存成註冊檔案;3.註冊驗證的邏輯,計算機資訊加密後(key=key1)加密md5==註冊檔案解密(key=key2); 
另外,採用ConfuserEx將可執行檔案加密;這樣別人要破解也就需要點力氣了(沒打算防破解,本意只想防複製的),有能力破解的人也不在乎破解這個軟體了(開發這個軟體前後只花了一週時間而已); 

技術上主要三個模組:1.獲取電腦相關硬體資訊(可參考);2.加密解密;3.讀寫檔案;

1.獲取電腦相關硬體資訊程式碼:

public class ComputerInfo
{
    public static string GetComputerInfo() { string info = string.Empty; string cpu = GetCPUInfo(); string baseBoard = GetBaseBoardInfo(); string bios = GetBIOSInfo(); string mac = GetMACInfo(); info = string.Concat(cpu, baseBoard, bios, 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 = GetHardWareInfo("Win32_BaseBoard", "SerialNumber"); return info; } 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; } private static string GetMacAddressByNetworkInformation() { string key = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\"; string macAddress = string.Empty; try { NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface adapter in nics) { if (adapter.NetworkInterfaceType == NetworkInterfaceType.Ethernet && adapter.GetPhysicalAddress().ToString().Length != 0) { string fRegistryKey = key + adapter.Id + "\\Connection"; RegistryKey rk = Registry.LocalMachine.OpenSubKey(fRegistryKey, false); if (rk != null) { string fPnpInstanceID = rk.GetValue("PnpInstanceID", "").ToString(); int fMediaSubType = Convert.ToInt32(rk.GetValue("MediaSubType", 0)); if (fPnpInstanceID.Length > 3 && fPnpInstanceID.Substring(0, 3) == "PCI") { macAddress = adapter.GetPhysicalAddress().ToString(); for (int i = 1; i < 6; i++) { macAddress = macAddress.Insert(3 * i - 1, ":"); } break; } } } } } catch (Exception ex) { //這裡寫異常的處理 } return macAddress; } }

  2.加密解密程式碼;

public enum EncryptionKeyEnum
{
    KeyA,
    KeyB
}
public class EncryptionHelper
{
    string encryptionKeyA = "pfe_Nova"; string encryptionKeyB = "WorkHard"; string md5Begin = "Hello"; string md5End = "World"; string encryptionKey = string.Empty; public EncryptionHelper() { this.InitKey(); } public EncryptionHelper(EncryptionKeyEnum key) { this.InitKey(key); } private void InitKey(EncryptionKeyEnum key = EncryptionKeyEnum.KeyA) { switch (key) { case EncryptionKeyEnum.KeyA: encryptionKey = encryptionKeyA; break; case EncryptionKeyEnum.KeyB: encryptionKey = encryptionKeyB; break; } } public string EncryptString(string str) { return Encrypt(str, encryptionKey); } public string DecryptString(string str) { return Decrypt(str, encryptionKey); } public string GetMD5String(string str) { str = string.Concat(md5Begin, str, md5End); MD5 md5 = new MD5CryptoServiceProvider(); byte[] fromData = Encoding.Unicode.GetBytes(str); byte[] targetData = md5.ComputeHash(fromData); string md5String = string.Empty; foreach (var b in targetData) md5String += b.ToString("x2"); return md5String; } private string Encrypt(string str, string sKey) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray = Encoding.Default.GetBytes(str); des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); des.IV = ASCIIEncoding.ASCII.GetBytes(sKey); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); foreach (byte b in ms.ToArray()) { ret.AppendFormat("{0:X2}", b); } ret.ToString(); return ret.ToString(); } private string Decrypt(string pToDecrypt, string sKey) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray = new byte[pToDecrypt.Length / 2]; for (int x = 0; x < pToDecrypt.Length / 2; x++) { int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16)); inputByteArray[x] = (byte)i; } des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); des.IV = ASCIIEncoding.ASCII.GetBytes(sKey); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); return System.Text.Encoding.Default.GetString(ms.ToArray()); } }

        注:這邊在MD5時前後各加了一段字元,這樣增加一點破解難度。

 3.讀寫檔案

public class RegistFileHelper
{
    public static string ComputerInfofile = "ComputerInfo.key"; public static string RegistInfofile = "RegistInfo.key"; public static void WriteRegistFile(string info) { WriteFile(info, RegistInfofile); } public static void WriteComputerInfoFile(string info) { WriteFile(info, ComputerInfofile); } public static string ReadRegistFile() { return ReadFile(RegistInfofile); } public static string ReadComputerInfoFile() { return ReadFile(ComputerInfofile); } public static bool ExistComputerInfofile() { return File.Exists(ComputerInfofile); } public static bool ExistRegistInfofile() { return File.Exists(RegistInfofile); } 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; } }

4.其他介面程式碼:

主介面程式碼:

public partial class FormMain : Form
{
    private string encryptComputer = string.Empty; private bool isRegist = false; private const int timeCount = 30; public FormMain() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; } private void FormMain_Load(object sender, EventArgs e) { string computer = ComputerInfo.GetComputerInfo(); encryptComputer = new EncryptionHelper().EncryptString(computer); if (CheckRegist() == true) { lbRegistInfo.Text = "已註冊"; } else { lbRegistInfo.Text = "待註冊,執行十分鐘後自動關閉"; RegistFileHelper.WriteComputerInfoFile(encryptComputer); TryRunForm(); } } /// <summary> /// 試執行視窗 /// </summary> private void TryRunForm() { Thread threadClose = new Thread(CloseForm); threadClose.IsBackground = true; threadClose.Start(); } private bool CheckRegist() { EncryptionHelper helper = new EncryptionHelper(); string md5key = helper.GetMD5String(encryptComputer); return CheckRegistData(md5key); } private bool CheckRegistData(string key) { if (RegistFileHelper.ExistRegistInfofile() == false) { isRegist = false; return false; } else { string info = RegistFileHelper.ReadRegistFile(); var helper = new EncryptionHelper(EncryptionKeyEnum.KeyB); string registData = helper.DecryptString(info); if (key == registData) { isRegist = true; return true; } else { isRegist = false; return false; } } } private void CloseForm() { int count = 0; while (count < timeCount && isRegist == false) { if (isRegist == true) { return; } Thread.Sleep(1 * 1000); count++; } if (isRegist == true) { return; } else { this.Close(); } } private void btnRegist_Click(object sender, EventArgs e) { if (lbRegistInfo.Text == "已註冊") { MessageBox.Show("已經註冊~"); return; } string fileName = string.Empty; OpenFileDialog openFileDialog = new OpenFileDialog(); if (openFileDialog.ShowDialog() == DialogResult.OK) { fileName = openFileDialog.FileName; } else { return; } string localFileName = string.Concat( Environment.CurrentDirectory, Path.DirectorySeparatorChar, RegistFileHelper.RegistInfofile); if (fileName != localFileName) File.Copy(fileName, localFileName, true); if (CheckRegist() == true) { lbRegistInfo.Text = "已註冊"; MessageBox.Show("註冊成功~"); } } }

 註冊機程式碼:

public partial class FormMain : Form
{
    public FormMain() { InitializeComponent(); } private void btnRegist_Click(object sender, EventArgs e) { string fileName = string.Empty; OpenFileDialog openFileDialog = new OpenFileDialog(); if (openFileDialog.ShowDialog() == DialogResult.OK) { fileName = openFileDialog.FileName; } else { return; } string localFileName = string.Concat( Environment.CurrentDirectory, Path.DirectorySeparatorChar, RegistFileHelper.ComputerInfofile); if (fileName != localFileName) File.Copy(fileName, localFileName, true); string computer = RegistFileHelper.ReadComputerInfoFile(); EncryptionHelper help = new EncryptionHelper(EncryptionKeyEnum.KeyB); string md5String = help.GetMD5String(computer); string registInfo = help.EncryptString(md5String); RegistFileHelper.WriteRegistFile(registInfo); MessageBox.Show("註冊碼已生成"); } }

 最後採用ConfuserEx將可執行檔案加密( ConfuserEx介紹),這樣就不能反編譯獲得原始碼。

至此全部完成,只是個人的一些實踐,對自己是一個記錄,同時希望也能對別人有些幫助,如果有什麼錯誤,還望不吝指出,共同進步,轉載請保留原文地址。https://download.csdn.net/download/pfe_nova/7943235