一篇文章通透理解序列号实现原理

C/C++
508
0
0
2023-09-21

序列号等价于注册码,是软件发行商的一种维权手段,也就是正版软件的一个身份证。本质: 防止盗版、按功能收费 等。

目前,商用软件和共享软件绝大部份都是采用注册码授权的方式来保证软件本身不被盗用,以保证自身的利益。尽管很多常用的许多软件系统的某些版本已经被别人破解,但对于软件特殊行业而言,注册码授权的方式还是一种保护软件系统本身的一种有效的手段。

序列号一般会和产品的名称(具体到硬件产品的型号、软件产品的版本号)、产品的使用期限(具体到截止日期)一起发布。

以下是一款我曾经试用过的美国软件产品Silver-peak注册码/序列号申请回复邮件的全文:

2.序列号/注册码的原理

一般是以计算机硬件(如主板, 网卡 ,硬盘)的唯一序列号作为注册源,通过一定的算法(即注册机)形成所谓的注册码。一般的程序通过在程序中设置一个全局变量来进行注册码的记录。

Windows下次数可以在注册表里设置一个键值来判断,若有此键,则说明已经到期。(网上说法,个人感觉不可靠,因为 注册表 也可以修改。)

一般情况下软件的注册是通过比较来实现的,也就是说软件本身就是注册机,因为他本身就有个真正的注册码,用他来和你所输入的注册码做比较。不过有的是明码比较有的是加密比较,以明码比较来说:我们已经知道了软件自己可能会产生注册码的有的在内存中有的在软件中有的在注册表中,更有甚者是有个通用的万能注册码。

输入注册码成功是因为软件中用了跳转,也就是说。当你输入的号码和真正的注册码比较完的时候有个跳转。

大部分注册算法的原理是:软件或者用户自己触发了软件的注册模块软件跳转到注册认证模块。弹出注册窗口用户输入注册名+注册码A,把它们存放在内存中。然后软件的注册码验证部分根据用户输入的注册名生成正确的注册码B并和用户输入的注册码A做对比。如果结果为相同则注册通过,并运行“注册成功”后面的程序分支,比如解除各种软件限制。如果不相同则提示“无效的注册码,请重新输入!”或者“Invalid key!”等等警告。如下图所示:

一篇文章通透理解序列号实现原理

图 1 注册截图

原理摘自:

3.注册码/序列号授权分类

通常而言,注册码授权方式有以下几种方式:

(1)安装序列号方式

这是最为常用的方式,Mircosoft提供的产品(例如:Windows系列产品、Office系列产品等等)都是采用这种方式。通过一种复杂的算法生成安装序列号,在安装过程中,安装程序对用户输入的安装序列号进行校验来验证该系统是否被合法,从而完成授权。

一篇文章通透理解序列号实现原理

图 2 Windows安装序列号方式

(2)用户名+序列号方式

即软件系统的供应商给用户提供有效的用户名和序列号,用户在安装过程或启动过程中输入有效的用户名和序列号,系统通过算法校验通过后完成软件授权。

一篇文章通透理解序列号实现原理

图 3 用户名+序列号方式

(3)在线注册方式

用户安装系统后,通过网络进行注册授权。软件系统的供应商事先已经登记了用户的信息,用户在线注册时,供应商的注册系统对用户的信息进行验证。用户身份有效时,注册系统生成一个凭证信息,软件系统根据凭证信息完成授权。

一篇文章通透理解序列号实现原理

图 4 在线注册方式

(4)激活码方式

用户安装系统后,软件系统会根据用户机器的关键信息(例如:MAC地址、CPU序列号、硬盘序列号等等)生成一个注册凭证(也可称为注册码),用户将这个注册凭证发送给软件供应商,供应商通过注册凭证生成一个激活码。用户输入激活码,软件系统完成授权。

一篇文章通透理解序列号实现原理

图 5 激活码方式

4.注册码/序列号组成

(1)产品版本:限定具体到哪个版本、同产品的其他版本不能用。

(2)到期时间:限定截止服务运行时间,到给定截止前一周会有界面提示和 邮件提醒

(3)唯一标识:限定一个软件产品和MAC地址或者磁盘UUID( Linux 系统)/GUID(Windows系统) 绑定

(4)预留字段:用于分模块限定核心功能、分模块收费等。

以下是Silver-peak产品序列号使用期限截止前一周前邮件提示全文:

Hello,
This is to notify you that the Proof of Concept currently in process at XXX@163.com will expire on Wed Jan 21 2015.
This affects the following appliances, shipped on Mon Dec 22 2014:
VX-2000, S/N 001BBC03A8E9
VX-2000, S/N 001BBC03A8EA
If you have questions, please contact your account executive (Tricia Png, tpng@silver-peak.com).
Thank you,
Silver Peak Systems

5.获取机器的唯一标识

上面的分析可知,我们的软件肯定要安装到硬件设备上,为了管控乱拷贝现象或盗版使用现象,必须获取硬件设备的唯一标识。通过该唯一标识生成注册码/序列号。

有了设备的唯一编号,我们就可以实现更好的软件的授权机制,还可以利用它来限制客户端软件访问后台服务的权限,从而提高系统的安全性。

通用唯一识别码(英语:Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为 自由软件基金会 组织在分散式计算环境领域的一部份。

UUID的目的,是让分散式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。 目前最广泛应用的UUID,是 微软公司 的全局唯一标识符(GUID) ,而其他重要的应用,则有Linux ext2/ext3文件系统、LUKS加密分区、 GNOME 、KDE、 Mac OS X等等。另外我们也可以在e2fsprogs包中的UUID库找到实现。

UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照 开放软件基金会 (OSF)制定的标准计算,用到了全局唯一的IEEE机器识别号、纳秒级时间、芯片ID码和许多可能的数字。

如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同( 实际测试结果每次都不同 )。 即每次生成的UUID都是不同的

UUID由以下几部分的组合:

  • 1 全局唯一的IEEE机器识别号。(如果有网卡,从网卡MAC地址获得;没有网卡,以其他方式获得)
  • 2 纳秒级时间
  • 3 芯片ID码
  • 4 许多可能的数字

UUID的 唯一缺陷在于生成的结果串会比较长 。关于UUID这个标准使用最普遍的是 微软 的GUID(Globals Unique Identifiers)。在ColdFusion中可以用CreateUUID()函数很简单地生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个x是0-9 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)

5.1不同操作系统设备的唯一标识

(1)Windows系统GUID

全局唯一标识符,简称GUID(发音为/ˈɡuːɪd/或/ˈɡwɪd/),是一种由算法生成的唯一标识,通常表示成32个16进制数字(0-9,A-F)组成的字符串,如:{21EC2020-3AEA-1069-A2DD-08002B30309D},它实质上是一个128位长的 二进制 整数。GUID一词有时也专指微软对UUID标准的实现。

GUID的主要目的是产生完全唯一的数字。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。GUID的总数也足够大,达到了2128(3.4×1038)个,所以随机生成两个相同GUID的可能性是非常小的,但并不为0。所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的情况不会发生。

[ 维基 百科]


(2)Linux系统UUID

Linux UUID的作用及意义

原因1: 它是真正的唯一标志符

UUID为系统中的存储设备提供唯一的标识字符串,不管这个设备是什么类型的。如果你在系统中添加了新的存储设备如硬盘,很可能会造成一些麻烦,比如说启动的时候因为找不到设备而失败,而使用UUID则不会有这样的问题。

原因2:设备名并非总是不变的

自动分配的设备名称并非总是一致的,它们依赖于启动时内核加载模块的顺序。如果你在插入了USB盘时启动了系统,而下次启动时又把它拔掉了,就有可能导致设备名分配不一致。

使用UUID对于挂载移动设备也非常有好处──例如我有一个24合一的读卡器,它支持各种各样的卡,而使用UUID总可以使同一块卡挂载在同一个地方。

原因3: ubuntu中的许多关键功能现在开始依赖于UUID

【长度缩减可以Base64编码】


5.2获取唯一标识方法

(1)Windows系统机器或者Linux系统机器获取方法

图 6 Windows机器的Guid

图 7 Linux机器的uuid

Windows系统&Linux系统获取uuid统一通用代码(已经测试过,测试结果见上面截图):

 #include <stdio.h>
#include <string>
#include <iostream>

#ifdef WIN
#include <objbase.h>
#else
#include <uuid/uuid.h>
#endif

using namespace std;

#define MAX_LEN


/*
**@brief: get windows guid or linux uuid
**@return: string type windows guid or linux uuid
*/
string GetGuid()
{
char szuuid[MAX_LEN] = {};
    #ifdef WIN
    GUID guid;
    CoCreateGuid(&guid);
    _snprintf_s(
    szuuid,
    sizeof(szuuid),
"{%X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
    guid.Data, guid.Data2, guid.Data3,
    guid.Data[0], guid.Data4[1],
    guid.Data[2], guid.Data4[3],
    guid.Data[4], guid.Data4[5],
    guid.Data[6], guid.Data4[7]);
    #else
    uuid_t uuid;
    uuid_generate(uuid);
    uuid_unparse(uuid, szuuid);
    #endif

    return std::string(szuuid);
}

int main()
{
    string strGuid = GetGuid();
    cout << strGuid.c_str() << endl;
    return;
} 

(2)安卓手机设备(没有验证)

DEVICE_ID

这是Android系统为开发者提供的用于标识手机设备的串号,也是各种方法中普适性较高的,可以说几乎所有的设备都可以返回这个串号,并且唯一性良好。

这个DEVICE_ID可以同通过下面的方法获取:

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String DEVICE_ID = tm.getDeviceId();

它会根据不同的手机设备返回 IMEI ,MEID或者ESN码,但在使用的过程中有以下问题:

非手机设备:最开始搭载Android系统都是手机设备,而现在也出现了非手机设备:如平板电脑、电子书、电视、音乐播放器等。这些设备没有通话的硬件功能,系统中也就没有TELEPHONY_SERVICE,自然也就无法通过上面的方法获得DEVICE_ID。

权限问题:获取DEVICE_ID需要READ_PHONE_STATE权限,如果只是为了获取DEVICE_ID而没有用到其他的通话功能,申请这个权限一来大才小用,二来部分用户会怀疑软件的安全性。

厂商定制系统中的Bug:少数手机设备上,由于该实现有漏洞,会返回垃圾,如:zeros或者asterisks

【参考】

5.3不使用MAC 地址作为唯一标识的原因

(1)有的Windows、Linux设备或者虚拟机 可能没有网卡

(2)对于安卓设备

可以使用手机Wifi或蓝牙的 Mac地址 作为设备标识,但是并不推荐这么做,原因有以下两点:

硬件限制:并不是所有的设备都有Wifi和蓝牙硬件,硬件不存在自然也就得不到这一信息。

获取的限制:如果Wifi没有打开过,是无法获取其Mac地址的;而 蓝牙 是只有在打开的时候才能获取到其Mac地址。

6.关于破解&安全性

不论是采用哪种方式来进行授权,理论上都是可以被破解的。 只要破解者发现了软件授权机制和原理则任何保护机制都将化为乌有。 因此,只能够通过选择复杂的算法和机制来增加破解者的破解难度,从而在在一定的时间内保证软件不被盗用。

RSA算法 (非对称加密算法)是一个广泛用于加密和数字签名的算法,可以适用用户名+序列号、在线注册、激活码等软件保护方式

RSA 算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。RSA也是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。

【参考RSA实现软件注册码原理】

设想的用户使用序列号的流程及可能存在的问题:

一篇文章通透理解序列号实现原理

图 8 用户使用序列号流程

(1)联机验证是一种很好的防止破解的方法

优点:简单、高效、直观。

缺点:针对软件的使用客户情况不同,部分客户是内网机器,无法连接外网,也就无法联网验证。

(2)关于序列号拷贝问题

之前采用过一种方法,如果涉及两台设备(服务端、客户端)之间通信的,可以通过如下判定禁用复制序列号、修改Mac地址的情况。

一篇文章通透理解序列号实现原理

图 9 通信的角度中断序列号复制问题

(3)UUID的弊端

如果将一台机器的UUID拷贝到另一台机器,就类似修改Mac地址的方法达到欺骗的目的。

由于目前没有找到通过UUID反推Mac地址的方法。设想的解密那里只能通过解密到UUID,不能继续解析到Mac地址或者磁盘UUID。

复杂一劳永逸的解决方案 :UUID不通过系统生成,自己写算法生成。这样生成和解析都可控。

简单的解决方案 :假定设备都装了网卡,通过实际网卡(非虚拟网卡)的Mac地址充当设备UUID。

安全性问题的以上3点需要花时间研究。

7.构想的序列号生成方案

下图是生成序列号与反查序列号界面截图(构想)。

一篇文章通透理解序列号实现原理

图 10 序列号生成软件

下图是序列号生成流程图(构想)。

第一步:通过5.2(1)方法获取安装设备的UUID,如:

9d669361-7f8a-4f97-b08a-488e4a92ee52;该UUID应该存储在设备的软件安装路径一份,以备对比验证。

第二步:填写对应安装的软件版本号,如1.0.0.1;

第三步:填写使用或授权限定使用的期限,如3年。

第四步:点击生成序列号生成授权序列号(后台会调用 RSA加密算法 ,对输入内容进行加密)。

一篇文章通透理解序列号实现原理

图 11 序列号生成流程图

下图2是序列号验证逻辑流程图(构想)。

第一步:输入获取的序列号。

第二步:后台执行RSA解密序列号。

第三步:判定各个属性值和安装设备是否一致。

第四步:全部相同确定为有效序列号,可以放行软件功能权限。

一篇文章通透理解序列号实现原理

图 12序列号验证逻辑流程图