【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
我们平时在开发程序的时候,如果仅仅是自己使用,那么代码和使用场景都是自己控制的,一般来说问题不大。但是程序如果是给别人使用的,总是会担心,这个程序会不会被客户用作其他的用途。所以这个时候,我们就要想办法保护自己的权益,保证这个程序只能在特别类型的板子、甚至是某个单一板子上面运行。
当然,在实施的过程当中,还不能特别复杂。最起码说,不能一个板子一个编译版本。但是一个版本可以给一个license文件,这是可以的。
1、特定的板子有自己的cpuid和mac地址
不同的板子,特别是linux板子,上面的cpuid和mac地址,一般都是唯一的。如果需要获取cpuid,输入cat /proc/cpuinfo即可,
feixiaoxing@raspberrypi:~/Desktop/module $ cat /proc/cpuinfo
processor : 0
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 1
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 2
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 3
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
Revision : c03115
Serial : 1000000082145aa2
Model : Raspberry Pi 4 Model B Rev 1.5
如果需要获取mac地址的话,则可以通过ifconfig命令获取,
feixiaoxing@raspberrypi:~/Desktop/module $ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.97 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::60b9:1158:5415:a5 prefixlen 64 scopeid 0x20<link>
ether d8:3a:dd:ab:00:ba txqueuelen 1000 (Ethernet)
RX packets 2535 bytes 231312 (225.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3190 bytes 254851 (248.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 1758 bytes 177511 (173.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1758 bytes 177511 (173.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.163 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::dbcb:56e0:b11e:4202 prefixlen 64 scopeid 0x20<link>
ether d8:3a:dd:ab:00:bb txqueuelen 1000 (Ethernet)
RX packets 444 bytes 70922 (69.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 95 bytes 54553 (53.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2、直接检验法
所谓的直接检验法,就是判断程序的cpuid和mac是不是和期望的数值是一致的。如果是一致的,就继续运行;反之则停止运行。这种方法比较简单,也是直觉上比较容易想到的方法。但是缺点也是比较明显,那就是有多少个板子,就要做多少个版本。要是板子不多,这种方法问题不大,但是一旦遇到量产的情况,就复杂了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void get_cpu_info() {
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
if (!cpuinfo) {
perror("fopen");
return;
}
char line[256];
while (fgets(line, sizeof(line), cpuinfo)) {
if (strncmp(line, "Serial", 6) == 0) {
printf("%s", line);
break;
}
}
fclose(cpuinfo);
}
int main() {
get_cpu_info();
return 0;
}
如果是获取eth0 mac地址,那么可以这么来处理,
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char mac_addr[18]; // MAC 地址的格式是 "XX:XX:XX:XX:XX:XX",共 17 个字符,加上 '\0' 是 18 个字符。
// 打开文件 /sys/class/net/eth0/address 以读取 MAC 地址
fp = fopen("/sys/class/net/eth0/address", "r");
if (fp == NULL) {
perror("Failed to open file");
return EXIT_FAILURE;
}
// 读取文件内容到 mac_addr
if (fgets(mac_addr, sizeof(mac_addr), fp) != NULL) {
// 打印读取到的 MAC 地址
printf("MAC Address: %s\n", mac_addr);
} else {
perror("Failed to read MAC address");
}
// 关闭文件
fclose(fp);
return EXIT_SUCCESS;
}
3、用非对称rsa加密代替单一加密
所谓的非对称加密,就是说加密的密码、解密的密码是不一样的。假设我们给客户发一个可执行程序,那么程序需要运行的话,那么需要一个license文件。这个license文件其实就是对cpuid和mac的加密。这种情况下,我们就需要把解密的密钥写在代码里面。所以就会存在一种情况,有些客户会饶过你直接和开发人员联系。这样的话就存在很大的泄密风险。
用非对称加密的话,开发人员就算把解密的密钥写在代码里面,但是只要加密的密钥自己牢牢掌握,就不存在泄密的风险。这对于保护自己的利益,是非常重要的。
4、利用驱动解决板子类型的问题
如果觉得单一加密的方法还是不足以解决问题,可以通过驱动加载的方法来解决开发板类型匹配的问题。我们都知道,每一个linux都有特定的kernel类型与之匹配,那么可以完全通过这一点来达到我们的目的。终端程序如果需要起来,需要先通过设备节点,获取license信息。如果没有看到设备节点,拒绝启动。同样的,看到设备节点,但没法获取license信息,也拒绝启动。
相关参数的方法可以通过module_param的方法来处理。假设驱动代码是这样的,
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("feixiaoxing");
MODULE_DESCRIPTION("This is just a hello module!\n");
static int my_param = 0;
static char* my_str_param = "default_string";
module_param(my_param, int, S_IRUGO);
module_param(my_str_param, charp, S_IRUGO);
static int __init hello_init(void)
{
printk(KERN_EMERG "%d\n", my_param);
printk(KERN_EMERG "%s\n", my_str_param);
printk(KERN_EMERG "hello, init\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_EMERG "hello, exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
接下来就是准备好makefile文件,
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions hello.mod modules.order Module.symvers
endif
make之后,就可以看到hello.ko文件。接下来,就可以通过sudo insmod hello.ko my_param=2 my_str_param="hello"传进去。确认参数有没有真的传进去,可以通过sudo dmesg看下。
5、时间限制法
前面说的方法没有时间限制,这是不太合适的。特别是前期试用的时候,客户有可能试用之后还没有付费的意愿。这个时候只能通过时间来进行限制了。比如这个时间可以是一个星期、一个月等等,具体多长时间可以灵活进行处理。