软件序列号的实现

发布于 2024-10-30 08:12:24 字数 470 浏览 2 评论 0原文

假设我编写了一个程序并想将其分发给人们。我希望能够在安装过程中向用户请求序列号,以验证他们是否拥有该软件的许可副本。此外,我希望序列号能够存储他们可以访问的软件版本以及许可证何时到期。

我至少可以想到两种概念性方法来实现此目的:

  1. 在用户购买软件的服务器上生成序列号,并将其通过电子邮件发送给他们。该软件在安装过程中连接到服务器并激活产品。服务器返回许可证权限和到期时间。序列号没有特定的格式/数学规则,只是根据服务器上的数据库进行检查。

  2. 为客户生成序列号并将其通过电子邮件发送给他们。该序列具有一些特殊的数学属性,允许程序检查它是否有效、对应的许可证以及何时过期。

我最感兴趣的是第二种方法。使用什么技术将此信息编码为序列号?如果您能提供一个简短的概述,那就太好了。或者,您能推荐一些讨论这个问题的好书或网站吗?

我觉得奇怪的是,在我编码的这些年里,我从未真正见过这些技术的实现或描述。

Let's say I have written a program and want to distribute it to people. I want to be able to request a serial number from the user during installation which will validate that they have a licensed copy of the software. Additionally, I want the serial number to store which edition of the software they have access to, and when the license expires.

There are at least two conceptual ways I can think of to accomplish this:

  1. Generate a serial on the server where user buys software, and email it to them. The software connects to server during installation and activates the product. The server returns the license privileges and expiration. The serial has no specific format/mathematical rules, it is simply checked against the database on the server.

  2. Generate a serial for the customer and email it to them. The serial has some special mathematical properties which allow the program to check if it is valid, what license it corresponds to, and when it expires.

I'm mostly interested in the 2nd method. What techniques are used in encoding this information in a serial number? If you could give a brief overview that would be great. Otherwise, can you recommend some good books or websites which discuss this?

I find it strange that in all the years I've been coding, I've never actually seen an implementation or description of these techniques.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

最偏执的依靠 2024-11-06 08:12:24

您正在考虑做的基本上是创建您自己的许可模块。这没有什么问题,如果你自己开发,它将是免费的(只是你编码和调试的时间)。然而,破解起来非常简单,所以如果您这样做是为了获得某种许可证合规性(即复制保护),我不会打扰。

您打算以某种方式将其绑定到物理机吗?一种方法是获取 CPU 序列号、HD 序列号、MAC 地址等或上述的某种组合,对它们进行哈希处理,并在启动时使用哈希值进行检查,以确保许可证有效。当然,如果用户更改任何内容,检查将失败,并且他们可能会因为他们的软件将不再运行而感到恼火,即使他们拥有有效的许可证。

如果您这样做是为了防止非法复制或使用,那么您提出的模式通常很容易破解——破解者会寻找您检查正确许可证的位置,然后修补 asm 代码以在其周围进行分支,或者始终返回正值。如果您的应用程序是 .NET,他们只需反转到源代码并删除许可证检查部分。

免责声明:我在一家许可证管理/复制保护工具公司 ([Wibu-Systems])1 工作,并且此这就是我们所做的一切,所以我们知道击败大多数系统是多么容易。

我推荐以下三种方法之一:

  1. 让您的应用程序不受保护,并希望您的盗版销量比例合理。
  2. 推出一个简单的系统,但要认识到,如果软件有价值,那么有人会快速轻松地破解它。为一些复杂的编码和维护做好准备,以便更好地完成这项工作。
  3. 购买商业质量的保护系统,例如 CodeMeterHASP,或 KeyLok。有些比其他更好(我们自然认为我们的是最好的),但没有一个是免费的。

What you're thinking about doing is basically creating your own licensing module. Nothing wrong with that, and if you roll your own it's going to be free (just your time to code and debug it). However, it will be enormously simple to crack so if you're doing this to obtain some kind of license compliance (ie copy protection) I wouldn't bother.

Are you going to bind it to the physical machine in some way? One way is to get the CPU serial number, HD serial number, MAC address, etc, or some combination of the above, hash them and use the hash to check at startup to make sure the license is valid. Of course, if the user changes anything the check will fail and they might be irritated that their software will no longer run, even though they have a valid license.

If you're doing this for protection against illegal copying or usage, the schema you're proposing is usually pretty simple to crack--a cracker looks for where you check for the right license and just patches the asm code to branch around it or always return a positive. If your app is .NET they can just reverse to source code and remove the license checking part.

Disclaimer: I work for a license management/copy protection tools company ([Wibu-Systems])1 and this is all we do, so we know how easy it is to defeat most systems.

I'd recommend one of three methods:

  1. Leave your app unprotected and hope your sales to pirated copies ratio is reasonable.
  2. Roll a simple system but recognize that if the s/w has value someone will crack it quickly and easily. Be prepared for some complex coding and maintenance to do this even half decently.
  3. Buy a commercial-quality protection system like CodeMeter, HASP, or KeyLok. Some are better than others (we think ours is the best, naturally), but none is free.
触ぅ动初心 2024-11-06 08:12:24

好吧,两周了还没有答案。
我将用我提出的一个非常简单的方法来回答,该方法使用基本方法生成相对安全且高度可扩展的序列号。

作为一名数学家,我非常肯定有一些先进的技术可以将各种信息存储在序列号中 - 但我主要对快速和肮脏的方法感兴趣。

这里有一个需要考虑的天真的、非数学的暴力技术:

创建一个包含您要使用的字符的 byte[] 数组。您只能使用十六进制,但没有理由限制自己。为什么不使用整个字母数字范围减去“0”/“O”和“1”/“I”(出于显而易见的原因)。

接下来,编写一个函数,如下所示(示例为 C#):

byte[] genRandomSerial(int length, byte[] characters, Random r)
{
  var sn = new byte[length];

  for (int i = 0; i < length; i++)
    sn[i] = characters[r.Next(0, characters.Length)];

  return sn;
}

这将为您提供一个随机序列号,我们不知道该序列号是否有效。

接下来:

int sum(byte[] sn, MD5 md5)
{
  val = 0;

  foreach (byte b in md5.ComputeHash(sn))
    val += (int)b;

  return val;
}

然后

bool validate(byte[] sn, uint radix, uint expected, MD5 md5)
{
  return (sum(sn, md5) % radix == expected);
}

我们现在拥有的是一种将 MD5 哈希函数的 16 字节输出相加的方法,并评估模 n 的总和是否等于某个 x

现在,决定您想要存在多少个序列号。序列号越多,就越容易有人随机猜测出有效的组合。

将您的随机序列分成块。假设 5 个 4 块,给出 20 个字符,格式为:ABCD-EFGH-IJKL-MNOP-QRST 根据

序列号创建 5 个数组:

{A、B、C、D}、{E、F、G、H }、{I、J、K、L}、{M、N、O、P} 和 {Q、R、S、T}。

测试一下您的 5 个数组是否有效,如下所示:

if (validate(block1, radix, expected, md5))
  // This block is valid.

如果您将基数设置为 2,则该块有 1/2 的概率有效。如果将基数设置为 10,则该块有效的概率为 1/10。如果您有 5 个区块,并且将每个区块的基数设置为 10,则整个序列号有效的概率为 0.1^5 = 0.00001。 (换句话说,每 100000 个随机序列中有 1 个是有效的。这意味着如果您使用完整的字母数字范围减去“0”/“O”、“1”/“I”,那么您将得到 (8 + 24)^< em>n * 0.00001 = ~1.2 * 10^19 个有效密钥,序列长度为 20。这很多 - 但请记住,无论如何,您都不会找到所有密钥。基数越高,就越安全。序列号会,但生成时间会更长)。

注意,“预期”应该介于 0 和 radix-1 之间

现在我们有一种方法来验证特定序列号是否有效,但是我们如何存储它是什么类型的序列号呢?事实上,我们已经有了方法。采取整个随机(但经过验证)序列“sn”:

int licenseType = sum(sn, md5) % 4; // Where 4 is the number of licenses you want to have

if (licenseType == 0)
{
  // Evaluation
}
else if (licenseType == 1)
{
  // Standard
}
else if (licenseType == 2)
{
  // Full
}
else // licenseType == 3
{
  // Unrestricted
}

随着您生成越来越多的密钥,每种类型的许可证的数量将逐渐趋于平稳。

如果您想在密钥中存储其他信息,例如到期日期,可以使用类似的方法。例如,您可以将奇数字符之和模 12 得到到期月份,将偶数字符之和模 31 得到到期日期。

您应用的这些限制和细分越多,生成每种类型的密钥所需的时间就越长。

Okay, so two weeks without an answer.
I'm going to answer with a very simple method I've come up with for generating relatively secure, and highly scalable serial numbers using elementary methods.

As a mathematician, I'm quite positive that there are some advanced techniques for storing all sorts of information in a serial number - but I'm mainly interested in quick-and-dirties.

Here's a naive, non-mathematical, brute-force technique to consider:

Create a byte[] array containing the characters you want to use. You can use hex only, but there's no reason to limit yourself. Why not use the whole alphanumeric range minus '0'/'O', and '1'/'I' (for obvious reasons).

Next, write a function as follows (example is C#):

byte[] genRandomSerial(int length, byte[] characters, Random r)
{
  var sn = new byte[length];

  for (int i = 0; i < length; i++)
    sn[i] = characters[r.Next(0, characters.Length)];

  return sn;
}

This will give you a random serial number, which we don't know is valid or not.

Next:

int sum(byte[] sn, MD5 md5)
{
  val = 0;

  foreach (byte b in md5.ComputeHash(sn))
    val += (int)b;

  return val;
}

and then

bool validate(byte[] sn, uint radix, uint expected, MD5 md5)
{
  return (sum(sn, md5) % radix == expected);
}

What we have now is a way of summing together the 16-byte output of the MD5 hashing function, and evaluating whether the sum modulo n is equal to some x.

Now, decide on how many serial numbers you want to exist. The more serial numbers exist, the easier it is that someone will randomly guess a valid combination.

Split your random serial into blocks. Let's say 5 blocks of 4, giving 20 characters in the form: ABCD-EFGH-IJKL-MNOP-QRST

Create 5 arrays out of your serial number:

{A, B, C, D}, {E, F, G, H}, {I, J, K, L}, {M, N, O, P}, and {Q, R, S, T}.

test to see if your 5 arrays validate as follows:

if (validate(block1, radix, expected, md5))
  // This block is valid.

If you set radix to 2, then there is a 1/2 probability that the block will be valid. If you set the radix to 10, then there is a 1/10 probability that the block will be valid. If you have 5 blocks, and you set the radices each to 10, then the probability that the whole serial number will be valid is 0.1^5 = 0.00001. (In other words, 1 in every 100000 random serials will be valid. That means if you use the full alphanumeric range minus '0'/'O', '1'/'I' then you have (8 + 24)^n * 0.00001 = ~1.2 * 10^19 valid keys for a serial length of 20. That's a lot - but remember you're not going to find them all anyway. The higher your radix, the more secure the serial will be, but the longer it will take to generate).

Note, 'expected' should be somewhere between 0 and radix-1.

So now we have a way of validating a particular serial number as valid, but how do we store what type of serial it is? As a matter of fact, we already have the method to do it. Taking the entire random (but validated) serial 'sn':

int licenseType = sum(sn, md5) % 4; // Where 4 is the number of licenses you want to have

if (licenseType == 0)
{
  // Evaluation
}
else if (licenseType == 1)
{
  // Standard
}
else if (licenseType == 2)
{
  // Full
}
else // licenseType == 3
{
  // Unrestricted
}

The number of each type of license will gradually level out as you generate more and more keys.

If you want to store additional information in the key, for example expiry dates, you can use similar methods. You could, for example take the sum of the odd characters modulo 12 to get an expiring month, and the modulo 31 of the sum of the even characters to give the expiring day.

The more of these restrictions and sub-divisions you apply, the longer it will take to generate each type of key.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文