软件激活功能的实现方法
常用激活方式
完全离线激活
通过某种算法规则生成激活码,软件通过该规则对激活码进行验证。用户只要将激活码填入软件即可。
规则示例:
- 激活码由四个两数组成
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 上获取硬盘序列号
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <errno.h>
5
6#include <linux/limits.h>
7#include <libmount/libmount.h>
8#include <libudev.h>
9
10#define SERIAL_MAX_LENGTH 1024
11
12// 通过 libmount 获取挂载点的硬盘设备路径
13const char* device_path_of_mount_point(const char* mount_point)
14{
15 static char device_path[PATH_MAX];
16 device_path[0] = 0;
17
18 struct libmnt_context* ctx = mnt_new_context();
19 if (ctx == NULL)
20 return NULL;
21
22 struct libmnt_table* table = NULL;
23
24 if (mnt_context_get_fstab(ctx, &table) < 0)
25 {
26 mnt_free_context(ctx);
27 return NULL;
28 }
29
30 struct libmnt_fs* fs = mnt_table_find_target(table, mount_point, MNT_ITER_BACKWARD);
31 if (fs == NULL)
32 {
33 mnt_free_context(ctx);
34 return NULL;
35 }
36
37 const char* path = mnt_fs_get_srcpath(fs);
38 strncpy(device_path, path, PATH_MAX);
39 mnt_free_context(ctx);
40 return device_path;
41}
42
43
44// 通过 libudev 获取设备路径的序列号
45const char* serial_of_device(const char* device_path)
46{
47 static char device_serial[SERIAL_MAX_LENGTH];
48 device_serial[0] = 0;
49
50 struct udev* udev = udev_new();
51 if (udev == NULL)
52 return NULL;
53
54 struct udev_enumerate* enumerate = udev_enumerate_new(udev);
55 if (enumerate == NULL)
56 {
57 udev_unref(udev);
58 return NULL;
59 }
60
61 if (udev_enumerate_scan_devices(enumerate) < 0)
62 {
63 udev_enumerate_unref(enumerate);
64 udev_unref(udev);
65 return NULL;
66 }
67
68 struct udev_list_entry* device_entry = udev_enumerate_get_list_entry(enumerate);
69 udev_list_entry_foreach(device_entry, device_entry)
70 {
71 const char* syspath = udev_list_entry_get_name(device_entry);
72 struct udev_device* device = udev_device_new_from_syspath(udev, syspath);
73 struct udev_list_entry* link_entry = udev_device_get_devlinks_list_entry(device);
74
75 udev_list_entry_foreach(link_entry, link_entry)
76 {
77 const char* link = udev_list_entry_get_name(link_entry);
78 if (strcmp(link, device_path) == 0)
79 {
80 const char* serial = udev_device_get_property_value(device, "ID_SERIAL");
81 strncpy(device_serial, serial, SERIAL_MAX_LENGTH);
82 udev_device_unref(device);
83 goto SUCCESS;
84 }
85 }
86
87 udev_device_unref(device);
88 }
89
90SUCCESS:
91 udev_enumerate_unref(enumerate);
92 udev_unref(udev);
93 return device_serial;
94}
95
96int main (int argc, char *argv[])
97{
98 const char* device_path = device_path_of_mount_point("/");
99 if (device_path == NULL)
100 return EXIT_FAILURE;
101
102 const char* device_serial = serial_of_device(device_path);
103 printf("Path: %s \nSerial: %s\n", device_path, device_serial);
104 return EXIT_SUCCESS;
105}Windows 上获取硬盘序列号
1#include <Windows.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5#define SERIAL_MAX_LENGTH 1024
6
7const char* serial_of_device(const char* device_path)
8{
9 static char device_serial[SERIAL_MAX_LENGTH];
10 device_serial[0] = 0;
11
12 HANDLE device = CreateFileA(device_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
13 if (device == INVALID_HANDLE_VALUE)
14 return NULL;
15
16 STORAGE_PROPERTY_QUERY query;
17 query.PropertyId = StorageDeviceProperty;
18 query.QueryType = PropertyStandardQuery;
19
20 // 查询存储器头
21 STORAGE_DESCRIPTOR_HEADER header;
22 BOOL success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &header, sizeof(header), NULL, NULL);
23 if (!success || header.Size == 0)
24 {
25 CloseHandle(device);
26 return NULL;
27 }
28
29 // 查询存储器全部属性
30 void* buffer = malloc(header.Size);
31 if (buffer == NULL)
32 {
33 CloseHandle(device);
34 return NULL;
35 }
36 success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), buffer, header.Size, NULL, NULL);
37 if (!success)
38 {
39 free(buffer);
40 CloseHandle(device);
41 return NULL;
42 }
43
44 // 获取序列号的偏移并读取序列号
45 const STORAGE_DEVICE_DESCRIPTOR* descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buffer;
46 strncpy(device_serial, (char*)buffer + descriptor->SerialNumberOffset, SERIAL_MAX_LENGTH);
47
48 free(buffer);
49 CloseHandle(device);
50 return device_serial;
51}
52
53int main()
54{
55 const char* device_serial = serial_of_device("\\\\.\\C:");
56 if (device_serial == NULL)
57 return EXIT_FAILURE;
58
59 printf("%s\n", device_serial);
60 return EXIT_SUCCESS;
61}