序列号格式
序列号结构
序列号由块组成。每个块都从一个标识符字节开始,该字节指示块的内容和可能的长度。最后一个块始终具有 255 标识符,其中包含除最后一个块之外的序列号的校验和。
根据块的类型,它可以具有恒定或可变长度。在后一种情况下,长度在块标识符后面的字节中指定。每个块的确切格式如下所述。
块格式
ID | 名称 | 大小(字节) | 描述 | 例子 |
---|---|---|---|---|
0x01 | 版本 | 1 | 该块包含一个版本的序列号,大小为 1 字节。版本必须为 1。 | 01 01 |
0x02 | 用户名 | 1 + N | 该块包含一个 UTF-8 编码的用户名。在用户名之前,有 1 个字节保存名称的长度。因此,用户名的总长度不得超过 255 个字节。不需要尾随 0。 | 02 04 4A 5F 48 4E |
0x03 | 电子邮件 | 1 + N | 该块包含用户的 UTF-8 编码电子邮件。在电子邮件之前有 1 个字节保存长度。因此,一封电子邮件的总长度不得超过 255 个字节。不需要尾随 0。 | 03 07 61 40 62 2E 63 6F 6D |
0x04 | 硬件标识符 | 1 + N | 该块包含由VMProtectGetCurrentHWID() 函数返回的硬件标识符。该函数返回一个 base-64 字符串。将字符串的解码版本放入序列号。数据块的长度必须是4的倍数。数据块前有一个长度字节。最大块长度为 32 字节。 | 04 08 E1 E2 E3 E4 A1 A2 A3 A4 |
0x05 | 授权到期日期 | 4 | 该块包含序列号到期日期日期格式如下所述。 | 05 01 0A 07 达 |
0x06 | 最长运行时间 | 1 | 该块包含 1 个字节,以分钟为单位保存程序可以运行的时间。因此,最长时间可以是 255 分钟。 | 06 05 |
0x07 | 产品代码 | 8 | 该块包含一个产品代码 – 由 VMProtect 创建并与产品参数一起导出的 8 个字节。这些数据以 base-64 编码导出,在放入序列号之前必须解码为字节数组。数组的大小必须正好是 8 个字节。 这个块是强制性的!没有它,受保护的程序将无法正常工作。 | 07 01 02 03 04 05 06 07 08 |
0x08 | 用户数据 | 1 + N | 该块包含最多 255 个字节的自定义用户数据。授权系统不会分析这些数据,并会在调用 VMProtectGetSerialNumberData() 函数时返回它们。在数据块之前,有一个字节保存用户数据的大小。 | 08 05 01 02 03 04 05 |
0x09 | 最大建造日期 | 4 | 该块包含应用程序的最大构建日期。格式如下所述。 | 09 01 0A 07 达 |
0xFF | 校验和 | 4 | 该块包含序列号校验和。该块位于最后一个块之前,并为所有先前的块计算校验和。校验和计算机制如下所述。 | FF 01 02 03 04 |
日期存储格式
日期以双字形式存储在序列号中 - 0xYYYYMMDD。高位词包含年,低位词包含日和月。字节遵循 Little Endian 表示 – 从低到高。如果有指向记录第一个字节的指针,则可以使用以下代码读取或写入日期:
byte *pDate = 0xNNNNNN; // 日期地址 *(DWORD *)pDate = (2010 << 16) | (10 << 8) | 1; // 2010 年 10 月 1 日 DWORD dwExp = *(DWORD *)pDate;
校验和计算
序列号的校验和是使用 SHA-1 散列算法计算的。结果是五个 32 位字。第一个字用作序列号的校验和。 请注意: 这个词是 Little Endian(从低到高)。使用 Big Endian 的 SHA-1 哈希的字符串表示 - 数字从高字节到低字节,因此如果 SHA-1 哈希是由字符串函数(如 PHP 中)生成的,则前四个字节哈希必须反转。
附加信息
授权系统将忽略具有上述以外数字的块。新版本可能会添加新块。 我们不建议使用非占用标识符创建您自己的块! 首先,这可能会使密钥在较新版本的授权系统中失效。其次,受保护的程序无论如何都无法读取这些块的值。要将附加信息存储在密钥中,请改用“ 用户数据 ”字段。正是为了这个目的。
序列号没有 SALT,这是一种随机信息,旨在根据相同的输入数据提供密钥的可变性。这个任务被强加给加密算法。如果您需要序列号级别的差异,例如,当向组织出售一系列密钥时,您可以将单个索引添加到用户名字段(“公司”有限责任公司,10 个中的第 1 个)或将此信息插入到任何适当格式的 用户数据 字段。