许可证生成方法并不那么重要,只要它不是微不足道的。诀窍是您的客户如何验证许可证是否正确。
假设您执行以下操作:
BOOL verifyLicense (char* licenseKey)
{
BOOL result = false;
if(strlen(licenseKey) > 128)
return false;
char* url = (char*)malloc(1024);
sprintf(url, "%s%s", LicenseServerBaseURL, licenseKey);
char* response = http_get(url);
if (strcmp(response, "OK") == 0)
result = true;
free(result);
free(url);
}
这非常简单——我们对许可证密钥进行一些基本检查,创建一个包含许可证密钥的 URL,然后对服务器执行 HTTP GET 以检查许可证。如果它返回“OK”,我们接受它。
这样做的问题是任何人都可以反汇编客户端:
...
push 06f2011c ; address of url string
call http_get ; http_get(url)
mov ebx, eax ; store a copy of the result address
push 04830040 ; ASCII "OK"
push eax ; address of response string
call msvcrt.strcmp ; strcmp(response, "OK")
test eax, eax ; if( ^ == 0 )
jnz exit ; skip if branch if non-zero
mov eax, 1 ; result = true
exit:
push ebx
call msvcrt.free ; free(result)
push 06f2011c
call msvcrt.free ; free(url)
ret
我所要做的就是将 更改jnz exit为一系列nop指令,因此永远不会发生跳转并且结果始终为真。这样,任何许可证密钥都被接受。更好的是,我可以简单地将方法的开头修改为立即设置eax为 1 并返回,因此它甚至不必费心要求您的服务器进行许可证验证。
那么,我们如何解决这个问题呢?不幸的是,我们不能。您遇到了 DRM 问题,该问题实质上表明,如果您将一些数据交给某人,他们总是可以更改它。无论您对代码进行了多少混淆,用户最终都有可能对其进行逆向工程并弄清楚如何进行更改或提取数据。如果您加密内容,您最终必须在系统上拥有密钥才能对其进行解密,因此用户总有办法提取该密钥并永久解密内容。
你能做的最好的就是让它变得困难,这需要大量的时间和精力。最终,您的软件将被破解并最终出现在某处的 torrent 站点上。对于使用这种商业模式的开发人员来说,这很糟糕,但这是我们生活的世界。我的建议是你走以下两条路线之一:
创建一个有效的基本许可系统,并接受某些人会盗版它的事实。通过花费您的开发时间实际改进软件,而不是实施和维护严苛的 DRM 系统,为您的真正客户服务。
改变您的整个开发和业务范式,使您的产品成为服务而不是软件应用程序。SaaS非常成功,因为盗版服务(实际上)是不可能的,并且该模型允许您对代码库进行实时更改,并对使用情况进行分析。
如果您选择选项 1,请不要使用服务器进行验证。它增加了复杂性,没有提供额外的安全性,带来一些隐私问题,并且如果您的许可服务器关闭,您的用户将无法使用您的软件。只需坚持一些简单的东西,例如用户名的哈希值和客户端的秘密值。打破它是微不足道的,但它为盗版提供了最低限度的障碍。