执行软件序列号

时间:2011-04-06 06:23:45

标签: password-protection copy-protection

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

至少有两种概念方法可以实现这一点:

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

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

  3. 我最感兴趣的是第二种方法。在序列号中使用哪些技术来编码此信息?如果你能给出一个很好的简要概述。否则,你能推荐一些讨论这个的好书或网站吗?

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

2 个答案:

答案 0 :(得分:3)

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

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

如果您这样做是为了防止非法复制或使用,那么您提出的架构通常很容易破解 - 一个破解者会查找您检查正确许可的位置,并将asm代码修补为分支围绕它或总是返回积极的。如果您的应用程序是.NET,他们可以反转源代码并删除许可证检查部分。

免责声明:我为许可证管理/版权保护工具公司([Wibu-Systems])1工作,这就是我们所做的一切,因此我们知道打败大多数系统是多么容易。

我建议使用以下三种方法之一:

  1. 保持您的应用不受保护,并希望您的销售盗版副本比例合理。
  2. 滚动一个简单的系统,但要认识到如果s / w有价值,有人会快速轻松地破解它。为一些复杂的编码和维护做好准备,甚至可以做到这一点。
  3. 购买商业品质的保护系统,例如CodeMeterHASPKeyLok。有些人比其他人好(我们认为我们的是最好的,自然而然),但没有一个是免费的。

答案 1 :(得分:1)

好的,两个星期没有答案。 我将用一种非常简单的方法回答我用基本方法生成相对安全且高度可扩展的序列号。

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

这是一个需要考虑的天真,非数学,蛮力技术:

创建一个包含您要使用的字符的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'/'我'那么你有(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用于给出到期日。

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