Unity开发授权系统
Unity开发授权系统
引子
因为有些客户尾款到账不及时,因此研究了一套授权系统,当授权到期后,系统就提示软件授权已到期,不能继续使用云云,这样方便尾款的收回。
大体需求就是
- 时间相关性,可以自由设置授权到期日期,到达时间后提示过期。
- 机器相关性,有时候是按照机器来授权的,需要识别不同的机器,分别给客户授权。
思路和需要的技术点
- 提取机器特征码,可以参考的思路是:CPU序列号、硬盘序列号、主板序列号、网卡MAC地址等。其中,获取MAC地址最简单,但是也最不稳定,客户网络环境如果比较复杂,比如多张网卡,比如拔插网线可能就会导致网卡状态变化,读取时就会有问题。其余的,相对好一点,推荐几个序列号都读取一下,然后拼接使用。但也不是百分百稳定,毕竟各种厂商设备型号繁杂。问题的关键在于如何获取这些序列号,网络上搜寻的解决方案,全都是使用System.Management,然而,这个其实并不能直接在unity中使用。即便是将
Unity InstallPath/Editor/Data/MonoBleedingEdge/lib/mono/2.0-api/System.Management.dll文件引入Unity工程,也会报“未实现”的错。所以最终,我的解决方案是写了一个额外的控制台程序,在这个控制台程序中读取硬件信息,然后输出,在Unity中调用这个控制台程序,在后台去获取硬件信息。这就又引发了一系列其他的问题,比如:假设客户知道了原理,如果他伪造一个读取硬件信息的程序,当我unity去调用时,他就可以返回给我假的硬件特征码,比如无论什么机器都返回一个固定的值,这样他就可以只用一台机器的授权,好几台机器共用了。所以,我Unity在调用之前,必须判断好这个读取硬件特征码的软件,是我的软件,而不是他伪造的。还有一个问题,在unity中,调用的时候,必须是后台调用,不能出现任何界面。
那么两个问题的解决方法,就是:- 在调用外部进程之前,首先创建一个内存映射文件(
MemoryMappedFile,它虽然叫做File,但其实它只需要在内存中,实际并不需要磁盘IO),这个内存映射文件,是可以跨进程共享数据的。读硬件特征码的程序,读到硬件信息后,加密后写到这个内容映射文件中,而不是在控制台输出,这样,这个读取硬件特征的软件,客户就很难替换,因为:①客户不知道内存映射文件对象的名称;②客户不知道加密方式,或者说即便知道加密方式,不知道加密的key,无法向内存映射文件中写如正确数值。这样就避免了客户自行替换读硬件信息的程序。
提取特征码的具体算法,网络上很多,这里不再赘述。 - 在unity中静静的运行一个后台进程,其实很容易,这里以获取控制台输出为例:
- 在调用外部进程之前,首先创建一个内存映射文件(
// 读取机器码的协程
private static IEnumerator ReadMachineCode()
{// 启动读取硬件特征码的进程string exePath = Path.Combine(Application.streamingAssetsPath, "MachineCode/GetMachineCode.exe");if (!File.Exists(exePath)){OnMachineCodeReaded?.Invoke(null);yield break;}Process process = new Process();process.StartInfo.FileName = exePath;process.StartInfo.Arguments = "Your Some Arguments"; // 据实际情况写process.StartInfo.UseShellExecute = false;process.StartInfo.CreateNoWindow = true;process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;process.StartInfo.WorkingDirectory = Path.Combine(Application.streamingAssetsPath, "MachineCode");process.StartInfo.RedirectStandardOutput = true;process.Start();// 等待进程结束while (!process.HasExited)yield return null;// 获取进程运行结果,以控制台为例,而不是内存文件映射,生产环境应读取内存文件映射中的结果string result = process.StandardOutput.ReadToEnd();OnMachineCodeReaded?.Invoke(result);
}
- 构建授权数据,本质上是构架一个数据体,用于描述授权文件的信息。然后转换为Json字符串,方便加密和存储。
[Serializable]
internal class MachineLicense
{public string code; // 目标机器码public string desc; // 说明public int year; // 授权到期年public int month; // 授权到期月public int day; // 授权到期日
}
[Serializable]
internal class LicenseData
{public string projectName; // 项目名称public string description; // 说明public string check; // 校验字符串public List<MachineLicense> data; // 授权数据
}
- AES加密原理,采用AES加密。这也是很成熟的加密算法,下面提供一些与加解密相关的封装好的方法:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;public static class AESCryptography
{// AES加密public static byte[] Encrypt(byte[] rgbKey, byte[] rgbIV, string sourceText){using MemoryStream memoryStream = new MemoryStream();using (Aes aes = Aes.Create())using (ICryptoTransform transform = aes.CreateEncryptor(rgbKey, rgbIV))using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))using (StreamWriter streamWriter = new StreamWriter(cryptoStream)){streamWriter.Write(sourceText);streamWriter.Flush();}return memoryStream.ToArray();}// AES解密public static string Decrypt(byte[] rgbKey, byte[] rgbIV, byte[] cipherBuffer){using MemoryStream stream = new MemoryStream(cipherBuffer);using Aes aes = Aes.Create();using ICryptoTransform transform = aes.CreateDecryptor(rgbKey, rgbIV);using CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Read);using StreamReader streamReader = new StreamReader(cryptoStream);return streamReader.ReadToEnd();}// 字符串MD5加密public static byte[] Md5Encrypt(string tex){var md5 = MD5.Create();return md5.ComputeHash(Encoding.UTF8.GetBytes(tex));}// 将byte[]数据输出为HEX字符串public static string ByteArrayToString(byte [] data){StringBuilder sb = new StringBuilder();int index = 0;foreach( var d in data ){if (index > 0 && index % 4 == 0)sb.Append('-');sb.Append(d.ToString("X2"));++index;}return sb.ToString();}// 将HEX字符串转换为byte[16]public static bool HexStringToByte16(string hex, out byte[] data){data = new byte[16];int index = 0;bool half = true;foreach (var ch in hex){byte d;switch (ch){case >= '0' and <= '9':d = (byte)(ch - '0');break;case >= 'A' and <= 'F':d = (byte)(10 + (ch - 'A'));break;case >= 'a' and <= 'f':d = (byte)(10 + (ch - 'a'));break;case ' ':case '-':case ':':continue;default:return false;}if (half)data[index] = (byte) (d << 4);else{data[index] = (byte) ( data[index] | d );++index;if (index > 15)break;}half = !half;}return true;}
}
- 制作授权文件,Unity端判定授权无效,或授权到期后,系统会停止工作,并将机器码显示到屏幕上,提示授权到期信息等。我方技术人员获取到机器码后,按照上述算法制作授权文件,经过AES加密,生成Base64字符串,保存到授权文件中,交付客户。
- 授权验证,客户将授权文件存放到指定路径后,Unity端验证过程:首先读取授权文件,并进行解密,转换为解密后的json字符串,再转换为
LicneseData数据,如果校验码等信息全部通过后,再判定当前设备机器码是否在授权文件中,并且授权日期是否到期。完成授权验证。
foreach (var date in from target in licenseData.data where string.CompareOrdinal(target.code, MachineCode) == 0 select new DateTime(target.year, target.month, target.day).AddDays(1))
{OnLicenseChecked?.Invoke(DateTime.Now < date);yield break;
}
OnLicenseChecked?.Invoke(false);
相关文章:
Unity开发授权系统
Unity开发授权系统 引子 因为有些客户尾款到账不及时,因此研究了一套授权系统,当授权到期后,系统就提示软件授权已到期,不能继续使用云云,这样方便尾款的收回。 大体需求就是 时间相关性,可以自由设置授…...
一周时间,开发了一款封面图生成工具
介绍 这是一款封面图的制作工具,根据简单的配置即可生成一张好看的封面图,目前已有七款主题可以选择。做这个工具的初衷来自平时写文章,都为封面图发愁,去图片 网站上搜索很难找到满意的,而且当你要的图如果要搭配上文…...
【.NET Core】深入理解异步编程模型(APM)
【.NET Core】深入理解异步编程模型(APM) 文章目录 【.NET Core】深入理解异步编程模型(APM)一、APM概述二、IAsyncResult接口2.1 BeginInvoke2.2 EndInvoke2.3 IAsyncResult属性2.4 IAsyncResult异步演示 三、通过结束异步操作来…...
pyqtgraph绘图类
pyqtgraph绘图类 pyqtgraph绘图有四种方法: 方法描述pyqtgraph.plot()创建一个新的QWindow用来绘制数据PlotWidget.plot()在已存在的QWidget上绘制数据PlotItem.plot()在已存在的QWidget上绘制数据GraphicsLayout.addPlot()在网格布局中添加一个绘图 上面四个方法都接收同样…...
C#6-10新增的内容
目录 异常筛选器 属性语法 表达式主体定义 Null 条件运算符 ?. 和 ?[] 使用 $ 的字符串内插 nameof 表达式 元组类型 模糊匹配 本地函数 Expression-bodied 成员 Reference 变量 ?、??和??= .. 模式匹配功能(C# 9) Record init c#8.NET Framework 4.8…...
【立创EDA-PCB设计基础】3.网络表概念解读+板框绘制
前言:本文对网络表概念解读板框绘制(确定PCB板子轮廓) 网络表概念解读 在本专栏的上一篇文章【嘉立创EDA-PCB设计指南】2,将设计的原理图转为了PCB,在PCB界面下出现了所有的封装,以及所有的飞线属性&…...
在Python环境中运行R语言的配环境实用教程
前情提要 在做一些生物信息与医学统计的工作,本来偷懒希望只靠python完成的,结果还是需要用R语言,倒腾了一会儿,调成功了,就记录一下这个过程。 我的环境: win10, pycharm, R-4.3.2 首先,我们…...
2023年总结我所经历的技术大变革
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅…...
基于YOLOv7算法的高精度实时车载摄像头下车辆检测系统(PyTorch+Pyside6+YOLOv7)
摘要:基于YOLOv7算法的高精度实时车载摄像头下车辆检测系统可用于日常生活中检测与定位车辆,此系统可完成对输入图片、视频、文件夹以及摄像头方式的目标检测与识别,同时本系统还支持检测结果可视化与导出。本系统采用YOLOv7目标检测算法来训…...
深度学习(3)--递归神经网络(RNN)和词向量模型Word2Vec
目录 一.递归神经网络基础概念 二.自然语言处理-词向量模型Word2Vec 2.1.词向量模型 2.2.常用模型对比 2.3.负采样方案 2.4.词向量训练过程 一.递归神经网络基础概念 递归神经网络(Recursive Neural Network, RNN)可以解决有时间序列的问题,处理诸如树、图这样…...
【江科大】STM32:中断系统(理论)
文章目录 中断系统为什么要使用中断中断优先级中断嵌套STM32的中断系统如何管理这些中断NVIC的结构 优先级窗口看门狗(WWDG):外部中断模块的特性&#…...
JAVA 学习 面试(六)数据类型与方法
数据类型 基本数据类型 为什么float3.4报错 3.4 默认是浮点double类型的,如果赋值给float是向下转型,会出现精度缺失,,需要强制转换 Switch支持的数据类型? byte、short、int、char 、 enum 、 String 基本类型与包…...
Java 一个数组集合List<People> 赋值给另一个数组集合List<NewPeople> ,两个数组集合属性部分一致。
Java 一个数组集合List 赋值给另一个数组集合List ,两个数组集合属性部分一致。 下面是一个Demo, 具体要根据自己的业务调整。 import java.util.ArrayList; import java.util.List;class People {private String name;private int age;private String address;publ…...
基于神经网络的电力系统的负荷预测
一、背景介绍: 电力系统负荷预测是生产部门的重要工作之一,通过准确的负荷预测,可以经济合理地安排机组的启停、减少旋转备用容量、合理安排检修计划、降低发电成本和提高经济效益。负荷预测按预测的时间可以分为长期、中期和短期负荷预测。…...
OpenCV第 1 课 计算机视觉和 OpenCV 介绍
文章目录 第 1 课 计算机视觉和 OpenCV 介绍1.机器是如何“看”的2.机器视觉技术的常见应用3.图像识别介绍4. 图像识别技术的常见应用5.OpenCV 介绍6.图像在计算机中的存储形式 第 1 课 计算机视觉和 OpenCV 介绍 1.机器是如何“看”的 我们人类可以通过眼睛看到五颜六色的世界…...
C++面试:stl的栈和队列介绍
目录 栈 栈(stack)的声明: push(): 将元素推入栈顶 pop(): 弹出栈顶元素 top(): 访问栈顶元素,但不弹出 empty(): 检查栈是否为空 size(): 返回栈中元素的数量 …...
从0开始学习C++ 第十二课:指针强化
第十二课:指针强化 学习目标: 理解常量指针与指针常量的区别。学习如何使用函数指针。掌握指针与数组的高级使用技巧。 学习内容: 常量指针与指针常量 概念: 常量指针是一个指向常量的指针,这意味着不能通过这个指针…...
mongodb和python交互
1. mongdb和python交互的模块 pymongo 提供了mongdb和python交互的所有方法 安装方式: pip install pymongo 2. 使用pymongo 2.1 导入pymongo并选择要操作的集合 数据库和集合能够自动创建 2.1.1 无需权限认证的方式创建连接对象以及集合操作对象 from pymongo import Mong…...
力扣279. 完全平方数
动态规划 思路: 假设 dp[i] 为最少组成数 i 的平方数个数;则其上一个状态为 dp[i - j^2] 1,1 为 j^2: 即 i 的最少完全平方数 i - j^2 的最少完全平方数 1,其中 j^2 < i 为最接近 i 的平方数;初始值…...
【C++】list容器功能模拟实现
介绍 上一次介绍了list队容器的迭代器模拟,这次模拟实现list的简单功能,尤其要注意构造函数、析构函数、以及赋值运算符重载的实现。 list容器需要接纳所有类型的数据,因此,结构设置与迭代器设置同理,需要引入结点&…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
