首页 > 其他分享 >软件授权码设计方案

软件授权码设计方案

时间:2022-11-05 14:25:48浏览次数:75  
标签:UUID BIOS 硬件 标识 设计方案 软件 授权 ID

 

一、    应用场景

  对于部分软件,不想被别人白嫖,只有在获取到授权后才能使用,常用的方法无非两种,其一是软件认证,其二是硬件绑定。

  软件认证:顾名思义就是在软件层面的一种认证手段,常用的方法就是注册账号设置密码。只要账号密码正确,在任何设备上都可使用。

  硬件绑定:就是将软件和硬件设备进行捆绑,也就是说一旦完成捆绑后,该软件就只能在该硬件设备上使用了。

  两种授权方法各有优劣,因应用场景的不同而选择不同的方案,在此就不多做讨论了,本文主要探讨的是硬件绑定的方法,以在PC机的软件授权为背景进行授权码的设计。

 

二、    唯一标识确定

  硬件绑定,那么首要做的就是确定该硬件的唯一标识,然后获取到其标识,而唯一的标识由正好是设备的基本功能,下面列举一下常见的PC机的唯一标识各种方法及其优缺点。

1、网卡MAC地址

  MAC地址可能是最常用的标识方法,但是现在这种方法基本不可靠:一个电脑可能存在多个网卡,多个MAC地址,如典型的笔记本可能存在有线、无线、蓝牙等多个MAC地址,随着不同连接方式的改变,每次MAC地址也会改变。而且,当安装有虚拟机时,MAC地址会更多。MAC地址另外一个更加致命的弱点是,MAC地址很容易手动更改。因此,MAC地址基本不推荐用作设备唯一ID。

2、CPU ID

  在Windows系统中通过命令行运行“wmic cpu get processorid”就可以查看CPU ID。目前CPU ID也无法唯一标识设备,Intel现在可能同一批次的CPU ID都一样,不再提供唯一的ID。而且经过实际测试,新购买的同一批次PC的CPU ID很可能一样。这样作为设备的唯一标识就会存在问题。

3、硬盘序列号

  在Windows系统中通过命令行运行“wmic diskdrive get serialnumber”可以查看。硬盘序列号作为设备唯一ID存在的问题是,很多机器可能存在多块硬盘,特别是服务器,而且机器更换硬盘是很可能发生的事情,更换硬盘后设备ID也必须随之改变,不然也会影响授权等应用。因此,很多授权软件没有考虑使用硬盘序列号。而且,不一定所有的电脑都能获取到硬盘序列号。

4、自定义算法生成唯一ID

  可以使用自制的一个特定算法(如GUID、或者一定位数的随机数)生成唯一的ID,然后写入到注册表或者设备上,作为其唯一ID。这种方法不依赖任何硬件特征,唯一性也可以自己完全控制,不过纯软件的实现缺点是这个ID很容易伪造,也很容易擦除;而且很可能还需要在线验证,后台存储所有ID的服务器必须保持在线。

5、Windows的产品ID(ProductId)

  在“控制面板\系统和安全\系统”的最下面就可以看到激活的Windows产品ID信息,另外通过注册表“HKEY_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion”也可以看到看到“ProductId”字段。不过这个产品ID并不唯一,不同系统或者机器重复的概率也比较大。虚拟机中克隆的系统,使用同一个镜像安装激活的系统,其产品ID就可能一模一样。经过实测,笔者在两台Thinkpad笔记本上发现其ProductId完全一样。

6、MachineGUID

  Windows安装时会唯一生成一个GUID,可以在注册表“HKEY_MACHINE\SOFTWARE\Microsoft\Cryptography”中查看其“MachineGuid”字段。这个ID作为Windows系统设备的唯一标识不错,不过值得注意的一点是,与硬件ID不一样,这个ID在重装Windows系统后应该不一样了。这样授权软件在重装系统后,可能就需要用户重新购买授权。

7、主板smBIOS UUID

  在Windows系统中通过命令行运行“wmic csproduct get UUID”Linux下用“dmidecode -s system-uuid”命令可以获取UUID。主板UUID是很多授权方法和微软官方都比较推崇的方法,本文用的也是该方法。即便重装系统UUID应该也不会变,双系统一个windows一个Linux,。但是这个方法也有缺陷,因为不是所有的厂商都提供一个UUID,当这种情况发生时,wmic会返回“FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF”,即一个无效的UUID。

8、外置密码设备提供唯一ID

  这种方法很多,比如U盾里面可以提供唯一的密钥标识,可信计算密码芯片里面的背书密钥EK等都是唯一固定在安全硬件里面的,而且通过良好的密码算法生成,唯一性和差异性都可以保证,安全性也更高。这种方法需要在计算设备连接外置密码芯片,增加经济负担和开发成本。而且,即便这种方法也存在欺骗攻击和代理攻击等破解方法。其实设备唯一标识其实也是指纹的一种,想要使用标识或者指纹时,首先必须明确自己的真实意图,是要标识一个用户(这样可以使用身份证、指纹、手机验证等方式),还是要标识一个设备(本文列举的各种设备ID)。根据自己的真实意图才能进一步思考具体使用的方式,不忘初衷。

9、BIOS序列号

  有些bios里面看不到序列号,获取BIOS序列号指令wmic bios get serialnumber. 用户在使用计算机的过程中,都会接触到BIOS,它在计算机系统中起着非常重要的作用。一块主板性能优越与否,很大程度上取决于主板上的BIOS管理功能是否先进。 BIOS(Basic Input/Output System,基本输入输出系统)全称是ROM-BIOS,是只读存储器基本输入/输出系统的简写,它实际是一组被固化到电脑中,为电脑提供最低级最直接的硬件控制的程序,它是连通软件程序和硬件设备之间的枢纽,通俗地说,BIOS是硬件与软件程序之间的一个“转换器”或者说是接口(虽然它本身也只是一个程序),负责解决硬件的即时要求,并按软件对硬件的操作要求具体执行。BIOS芯片是主板上一块长方型或正方型芯片,BIOS中主要存放:自诊断程序:通过读取CMOS RAM中的内容识别硬件配置,并对其进行自检和初始化; CMOS设置程序:引导过程中,用特殊热键启动,进行设置后,存入CMOS RAM中; 系统自举装载程序:在自检成功后将磁盘相对0道0扇区上的引导程序装入内存,让其运行以装入DOS系统; 主要I/O设备的驱动程序和中断服务;由于BIOS直接和系统硬件资源打交道,因此总是针对某一类型的硬件系统,而各种硬件系统又各有不同,所以存在各种不同种类的BIOS,随着硬件技术的发展,同一种BIOS也先后出现了不同的版本,新版本的BIOS比起老版本来说,功能更强。重装系统BIOS序列后并不会改变。

 

三、    生成授权码

  本文的设计思路其实也相对比较简单,第一步就是获取主板smBIOS UUID,第二步对UUID进行AES加密(AES算法逻辑及介绍欢迎查看笔者之前的文章)生成授权码,发送给用户。

  验证的过程,可以分两种方法实现,一是软件启动后获取设备的UUID加密后与授权码进行比较,而是对授权码进行解密,解密后与UUID进行比较。两种方法均可。

 

 

四、    代码实现

1、获取uuid 代码实现

#include "GetUUID.h"
# pragma comment(lib, "wbemuuid.lib")
#include <iostream>
using namespace std;

/*************************************
函数名称:AsciiStrToHexArray
输入参数:
      src - ASCII 数据串地址
      len - ASCII 数据串长度
      des - 输出的16进行地址
输出参数:
      无
返回值:
    false - 转换失败
    true  - 转换成功

功能:将ASCII码的字符串转换成16进制数据

作者:csk
日期:2022/11/02
备注:例如   src="4131423243334434"对应的字符为 "A1B2C3D4"
      转换后 dec= 0xA1 0xB2 0xC3 0xC4
*************************************/

bool AsciiStrToHexArray(unsigned char* src, int len, unsigned char* des)
{
    unsigned char ch;
     
    for (int index = 0x00; index < len; index++)
    {
        if ((src[index] >= '0') && (src[index] <= '9')) //数字0-9范围
        {
            ch = src[index] - 0x30;
        }
        else if ((src[index] >= 'A') && (src[index] <= 'F')) //字母A-F范围
        {
            ch = src[index] - 0x37;
        }
        else if ((src[index] >= 'a') && (src[index] <= 'f')) //字母a-f范围
        {
            ch = src[index] - 0x57;
        }
        else
        {
            return false;
        }
        if (index % 2 != 0x00)
        {
            des[index / 2] |= ch;
        }
        else
        {
            des[index / 2] = ch<<0x04;
        }
    }
    return true;
}

/*************************************
函数名称:getuuID
输入参数:
      uuid - 输出获取到的UUID存储地址 32byte
输出参数:
      无
返回值:
    false - 获取失败
    true  - 获取成功

功能:获取主板smBIOS UUID

作者:csk
日期:2022/11/02
备注:获取到的UUID是去除了"-"的
      UUID 格式:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*************************************/
bool getuuID(unsigned char* uuid)
{
    unsigned char temp[0x20] = {0x00};
    int offset = 0x00;
    HRESULT hres;
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        //cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
        return false;                  // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you need to specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------
    hres = CoInitializeSecurity(
        NULL,
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation 
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities
        NULL                         // Reserved
    );

    if (FAILED(hres))
    {
        //cout << "Failed to initialize security. Error code = 0x"<< hex << hres << endl;
        CoUninitialize();
        return false;                    // Program has failed.
    }
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator* pLoc = NULL;

    hres = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator, (LPVOID*)&pLoc);

    if (FAILED(hres))
    {
        //cout << "Failed to create IWbemLocator object."<< " Err code = 0x"<< hex << hres << endl;
        CoUninitialize();
        return false;                 // Program has failed.
    }

    // Step 4: -----------------------------------------------------

    // Connect to WMI through the IWbemLocator::ConnectServer method
    IWbemServices* pSvc = NULL;
    // Connect to the root\cimv2 namespace with
    // the current user and obtain pointer pSvc
    // to make IWbemServices calls.

    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
        NULL,                    // User name. NULL = current user
        NULL,                    // User password. NULL = current
        0,                       // Locale. NULL indicates current
        NULL,                    // Security flags.
        0,                       // Authority (e.g. Kerberos)
        0,                       // Context object
        &pSvc                    // pointer to IWbemServices proxy
    );

    if (FAILED(hres))
    {
        //cout << "Could not connect. Error code = 0x"<< hex << hres << endl;
        pLoc->Release();
        CoUninitialize();
        return false;                // Program has failed.
    }
    //cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;

    // Step 5: --------------------------------------------------
    // Set security levels on the proxy -------------------------
    hres = CoSetProxyBlanket(
        pSvc,                        // Indicates the proxy to se
        RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
        RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
        NULL,                        // Server principal name
        RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
        RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
        NULL,                        // client identity
        EOAC_NONE                    // proxy capabilities
    );
    if (FAILED(hres))
    {
        //cout << "Could not set proxy blanket. Error code = 0x"<< hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return false;               // Program has failed.
    }
    // Step 6: --------------------------------------------------
    // Use the IWbemServices pointer to make requests of WMI ----
    // For example, get the name of the operating system
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t("SELECT * FROM Win32_ComputerSystemProduct"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator);
    if (FAILED(hres))
    {
        //cout << "Query for operating system name failed."<< " Error code = 0x"<< hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return false;               // Program has failed.
    }

    IWbemClassObject* pclsObj;

    ULONG uReturn = 0;
    //string test;
    while (pEnumerator)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn)
        {
            break;
        }
        VARIANT vtProp;
        // Get the value of the Name property
        hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0);
        wcout << " OS Name : " << vtProp.bstrVal << endl;

        offset = 0x00;
        for (int i = 0; i < 32; i++)
        {
            if ((0x08 == i) || (0x0C == i) || (0x10 == i) || (0x14 == i))
            {
                offset++;
            }
            temp[i] = vtProp.bstrVal[i + offset];
        }

        if (!AsciiStrToHexArray(temp,0x20,uuid))
        {
            return false;
        }

        VariantClear(&vtProp);
        //pclsObj->Release();
    }
    return true;

}
getUUID

2、授权码生成及验证代码实现

// Register.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。


#define _WIN32_DCOM
#include <iostream>

#include "GetUUID.h"
#include "AES.h"
using namespace std;


 
 
int main(int argc, char** argv)
{
    //
    
    unsigned char uuid[0x10] = { 0x00 };
    unsigned char key[0x10] = { 0x4D,0x59,0x48,0x45,0x41,0x52,0x54,0x57,0x49,0x4C,0x4C,0x47,0x4F,0x4F,0x4E,0x21 }; // 加密密钥 MY HEART WILL GO ON !

    //1、获取UUID
    if (getuuID(uuid))
    {//UUID获取成功

        //2、进行AES加密生成授权码
        AES aes(key);
        printf("Input:\n");
        for (int i = 0; i < 0x10; i++)
        { //输出获取到的uuid(设备的唯一标识),作为加密的原始数据
            printf("%02x ", uuid[i]);
        }


        //加密 UUID->授权码
        aes.Cipher(uuid);
        printf("\nAfter Cipher:\n");
        for (int i = 0; i < 0x10; i++)
        {//将uuid进行加密生成授权码
            printf("%02x ", uuid[i]);
        }

        //解密 授权码->UUID
        aes.InvCipher(uuid);
        printf("\nAfter InvCipher:\n");
        for (int i = 0; i < 0x10; i++)
        {//将授权码解密还原成uuid
            printf("%02x ", uuid[i]);
        }
    }

}
 

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
View Code

五、    总 结

当然还有很多其它方法,如可以获取声卡、CPU模式和频率、IDE控制器、内存等其他信息。甚至,可以收集设备的软硬件配置,通过统计方法和机器学习方法进行分类识别设备。学术上,还有各种密码算法,硬件不可克隆函数PUF等唯一标识的方法可以使用。

从软件授权这个简单的应用来看,购买外置密码设备硬件太过昂贵,可以采用简单的组合方法,推荐使用主板UUID作为主标识,当UUID返回无效的值时,可以进一步采用CPU ID、BIOS序列号、MachineGUID等方式作为次标识,这基本可以解决问题。不过,不管使用怎样的硬件信息或者牛气的算法来进行用户或者设备的标识,还是一句老话“道高一尺,魔高一丈”,都是可以被攻破的,即便你的标识伪造不了、克隆不了,攻击者也可以使用其它攻击方式,如逆向你的验证check代码,然后将其修改掉,使其check失灵。因此,无论设备标识或者用户标识,很多情况下可能只防君子、不防小人,甚至悲观者认为这些手段都是防止合法用户的,影响用户使用的方便性,大可以取消掉。笔者认为,没有必要这么悲观,知识产权等信息是尊敬人的价值和劳动的表现,即便不能完全防止小人,我们也要通过这些方法将一般的小人排除在技术门槛之外,并尽量增加高级小人破解时的代价。

 

工程路径:https://files.cnblogs.com/files/chenshikun/Register.rar?t=1667628033

标签:UUID,BIOS,硬件,标识,设计方案,软件,授权,ID
From: https://www.cnblogs.com/chenshikun/p/16860067.html

相关文章

  • 非常好用的5款电脑软件
    1.元气壁纸元气壁纸是一款专注动静态壁纸及桌面美化的电脑应用软件。内置游戏渲染引擎,真实还原雨雪光特效和各种粒子效果,让动态壁纸效果栩栩如真,每张壁纸内置专属背景音和鼠......
  • Capture One Pro 22 (RAW图像处理软件)中文直装版mac/win
    CaptureOnePro22forMac是一款专业的RAW文件转换器和图像编辑软件,拥有更新的处理引擎、市场领先的性能和强大的新功能,可为500多台高端相机提供具有美丽色彩和令人难......
  • 软件技术基础(二)
    这个作业属于哪个课程https://edu.cnblogs.com/campus/zjlg/22rjjc/homework/12841这个作业的目标实现一个命令行文本计数统计程序姓名-学号韩志鹏-20203303......
  • 最完美WIN11_Pro_22H2.22622.755软件选装纯净版VIP36.9
    【系统简介】===============================================================1.本次更新母盘来自WIN11_Pro_22H2.22622.755。2.不支持更新,更新后有些东西又会回来,玩过的......
  • 软件设计__ 软件体系结构设计
    难度:★★★概述定义程序或计算机系统的软件体系结构(SoftwareArchitecture)是指:系统的一个或者多个结构,它包括软件构件、构件的外部可见属性以及它们之间的相互关系。--......
  • js goole 授权 新版本
    js goole授权 新版本警告:适用于网页的Google登录JavaScript平台库已弃用,2023年3月31日后便无法下载。本指南中的解决方案基于此库,因此也已弃用。请改为使用......
  • Nearth===UI/Axure RP 8软件/Axure-UI-知乎登陆框
    效果:说明:(文件放在我的下载包里,想看看的就自己去下载。)不开心是一天,开心也是一天,那就不开心吧·························哈哈哈·········......
  • 软件需求与分析课堂测试 –结构化建模分析1
    今天在系主任课上的测试,题目是:住户到一门市办理业务,若进户及更户要求,可在一门市直接办理,以更新物业管理基本信息,其他业务需求要制作管理签报,连同各种相关材料送管理处处理......
  • 软件需求与分析课堂测试 –结构化建模分析2
    3、绘制系统功能架构图;   4、绘制系统一层数据流图。  ......
  • 数据库设计心得-软件2005-讨口子队
    数据库设计心得撰写人:赵春生、王思涵一、数据库设计的重要性数据库设计软件开发的过程中起着很大的作用,如若不进行数据库设计就进行开发,很可能会导致诸如设计与需求不符......