软件激活功能的实现方法
常用激活方式
完全离线激活
通过某种算法规则生成激活码,软件通过该规则对激活码进行验证。用户只要将激活码填入软件即可。
规则示例:
- 激活码由四个两数组成
AA-BB-CC-DD
- 规则为
DD = (AA + BB + CC) % 100
这种方法使用起来最方便,但安全性极差,一个激活码即可无限激活。
部分离线激活
设备本身离线,但激活时需要其他联网设备复制进行,激活后使用软件不需要联网。
步骤:
- 软件获取设备 ID
- 用户通过其他联网环境再注册网页上提交设备 ID
- 注册服务器根据设备ID,以某种算法规则生成注册码返回给用户
- 用户在软件上填入该激活码激活
- 软件以该规则进行验证
规则示例:
- 激活码 = SHA256(设备ID)
这种方法使用起来也较为方便,且一个激活码只能用于一台设备。但也有算法被破解的风险。
在线激活
设备全程联网验证。这种方式最安全,但对用户而言十分麻烦。
获取唯一设备 ID
X86 汇编中有一条
cpuid
指令,可以获取 CPU 的 ID,原本包含 CPU 的序列号,但出于隐私保护的原因被取消了,现在 CPU ID 仅含 CPU 型号,而不包含序列号。
可以采用 硬盘序列号 作为设备ID,获取方法如下:
Linux 上获取硬盘序列号
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <linux/limits.h>
#include <libmount/libmount.h>
#include <libudev.h>
#define SERIAL_MAX_LENGTH 1024
// 通过 libmount 获取挂载点的硬盘设备路径
const char* device_path_of_mount_point(const char* mount_point)
{
static char device_path[PATH_MAX];
device_path[0] = 0;
struct libmnt_context* ctx = mnt_new_context();
if (ctx == NULL)
return NULL;
struct libmnt_table* table = NULL;
if (mnt_context_get_fstab(ctx, &table) < 0)
{
mnt_free_context(ctx);
return NULL;
}
struct libmnt_fs* fs = mnt_table_find_target(table, mount_point, MNT_ITER_BACKWARD);
if (fs == NULL)
{
mnt_free_context(ctx);
return NULL;
}
const char* path = mnt_fs_get_srcpath(fs);
strncpy(device_path, path, PATH_MAX);
mnt_free_context(ctx);
return device_path;
}
// 通过 libudev 获取设备路径的序列号
const char* serial_of_device(const char* device_path)
{
static char device_serial[SERIAL_MAX_LENGTH];
device_serial[0] = 0;
struct udev* udev = udev_new();
if (udev == NULL)
return NULL;
struct udev_enumerate* enumerate = udev_enumerate_new(udev);
if (enumerate == NULL)
{
udev_unref(udev);
return NULL;
}
if (udev_enumerate_scan_devices(enumerate) < 0)
{
udev_enumerate_unref(enumerate);
udev_unref(udev);
return NULL;
}
struct udev_list_entry* device_entry = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(device_entry, device_entry)
{
const char* syspath = udev_list_entry_get_name(device_entry);
struct udev_device* device = udev_device_new_from_syspath(udev, syspath);
struct udev_list_entry* link_entry = udev_device_get_devlinks_list_entry(device);
udev_list_entry_foreach(link_entry, link_entry)
{
const char* link = udev_list_entry_get_name(link_entry);
if (strcmp(link, device_path) == 0)
{
const char* serial = udev_device_get_property_value(device, "ID_SERIAL");
strncpy(device_serial, serial, SERIAL_MAX_LENGTH);
udev_device_unref(device);
goto SUCCESS;
}
}
udev_device_unref(device);
}
SUCCESS:
udev_enumerate_unref(enumerate);
udev_unref(udev);
return device_serial;
}
int main (int argc, char *argv[])
{
const char* device_path = device_path_of_mount_point("/");
if (device_path == NULL)
return EXIT_FAILURE;
const char* device_serial = serial_of_device(device_path);
printf("Path: %s \nSerial: %s\n", device_path, device_serial);
return EXIT_SUCCESS;
}
Windows 上获取硬盘序列号
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SERIAL_MAX_LENGTH 1024
const char* serial_of_device(const char* device_path)
{
static char device_serial[SERIAL_MAX_LENGTH];
device_serial[0] = 0;
HANDLE device = CreateFileA(device_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if (device == INVALID_HANDLE_VALUE)
return NULL;
STORAGE_PROPERTY_QUERY query;
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
// 查询存储器头
STORAGE_DESCRIPTOR_HEADER header;
BOOL success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &header, sizeof(header), NULL, NULL);
if (!success || header.Size == 0)
{
CloseHandle(device);
return NULL;
}
// 查询存储器全部属性
void* buffer = malloc(header.Size);
if (buffer == NULL)
{
CloseHandle(device);
return NULL;
}
success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), buffer, header.Size, NULL, NULL);
if (!success)
{
free(buffer);
CloseHandle(device);
return NULL;
}
// 获取序列号的偏移并读取序列号
const STORAGE_DEVICE_DESCRIPTOR* descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buffer;
strncpy(device_serial, (char*)buffer + descriptor->SerialNumberOffset, SERIAL_MAX_LENGTH);
free(buffer);
CloseHandle(device);
return device_serial;
}
int main()
{
const char* device_serial = serial_of_device("\\\\.\\C:");
if (device_serial == NULL)
return EXIT_FAILURE;
printf("%s\n", device_serial);
return EXIT_SUCCESS;
}