0

我正在尝试扩展在线游戏的遗留代码,以合理保证与游戏相关的资源文件是最新版本,并且没有被篡改。我想在没有 DRM、不进入内核模式和没有硬件帮助的情况下执行此操作。理想情况下,我最终决定的应该是与用户空间一样安全。如果他们能绕过这些机制,那么我会说他们已经获得了调皮的权利——至少在一段时间内,然后我们禁止他们。:)

显而易见的做法是获取每个文件的加密哈希,并将哈希输出与预先计算的哈希进行比较。但必须注意确保用户不能轻易篡改用于与当前计算的哈希进行比较的预先计算的哈希。以下是我在架构层面考虑的措施:

  1. 使用操作系统工具在进程启动时锁定所有资源文件以进行写入,因此没有其他进程可以在事后覆盖这些文件。
  2. 计算每个资源文件的 MD5 和。
  3. 连接到 https 服务器,需要针对存储在客户端可执行文件中的特定签名证书进行验证。
  4. 通过 https 下载包含“正确”哈希的文件,将其存储在 RAM 中(而不是磁盘)。
  5. 将计算的哈希值与通过 https 接收的哈希值进行比较。
  6. 如果哈希不匹配则退出。

这比在客户端存储哈希值更安全,因为只要它们存储在客户端,通常有人可以修改它们。使用此方案,攻击者必须弄清楚如何以更改嵌入式公共证书的方式修改可执行文件,以便它实际上包含攻击者的证书,该证书会针对攻击者的 https Web 服务器进行验证,该服务器包含与攻击者匹配的中毒散列篡改的资源文件。这当然是可行的,但比程序使用存储在客户端上的预计算密钥要困难得多,对吧?

可执行文件是本机代码,因此可以将其包装在打包程序中,这使得编辑二进制文件和替换公钥变得更加困难。

现在,尽管我刚刚在两段前描述了如何潜在地攻击我设计的方案:除了我所描述的之外,还有其他方法可以攻击这个方案吗?

我的第二个问题是,什么是可以从 Win32 C++(Visual Studio 2010 编译器)调用以完成这些任务的免费(如免费软件或自由)库?它们可以通过内置的 Windows API 完成还是需要第三方解决方案?

总而言之,问题是: 1. 我建议的方案是否足够强大,足以让大多数脚本小子感到困惑?和 2. 我将使用哪些依赖项或库来实现它?

由于这只是一个游戏,而不是国家安全甚至金钱风险的问题(游戏是 100% 免费软件,没有游戏内经济),我实施的解决方案应该是相当不错的,但它不一定是工业的-力量。

如果您认为合适,请随意回答我的问题的一个或两个部分,如果您认为我一开始就问错了问题,请纠正我。

谢谢!

4

1 回答 1

2

我喜欢你的解决方案。这是经过深思熟虑的。你基本上回答了你自己的问题。我有几点建议要指出。

最适合您的任务的 Win32 库是用于散列的Windows Crypto API 。它还支持我推荐的 SHA-1(因为 MD5 现在被认为是易受攻击和可破解的)。您可能还想查看将 HMAC 与 SHA1 结合使用,以便计算出的哈希包含密钥(嵌入在代码中)。

Wininet是一个适合下载的库。我相信它支持带有证书验证的 HTTPS。

用于打开文件的 Windows API CreateFile基本上可以锁定内容文件。只需打开所有文件而不指定任何 FILE_SHARE_* 标志。

让我抛出一些我在游戏行业的短暂时间中学到的其他一些想法。

大多数游戏在作弊者进入下一个有趣的游戏之前只有很短的生命周期(几个月到一年)。因此,鉴于确实没有一个完美的解决方案可以阻止一个无所事事的坚定黑客,您实际上是在寻找可以赢得时间的解决方案。也就是说,您在游戏中添加的保护黑客越多,一个坚定的黑客破解您的解决方案所需的时间就越长。即使您的所有保护措施都不是加密安全的,它也会为您赢得更多时间。

如果您不担心黑客会尝试拆卸和修改您游戏的 EXE,那么您可能已经完成了您提出的解决方案。但请考虑以下内容。

1)这是一个更简单的攻击。在您的代码中的某处,您可能会有一个函数根据您描述的哈希验证解决方案来决定是否可以继续进行游戏。黑客所要做的就是找到最终验证结束的位置,并强制您的验证代码始终成功。也就是说,如果您的代码中有这样的函数:

bool IsContentValid()
{
   // compare the local hash with the hash returned from the https response
   return (0 == memcmp(local_hash, network_hash, sizeof(HASH)));
}

然后,对于一个坚定的黑客来说,修改他自己的 EXE 以使该代码路径始终成功变得非常容易。黑客只是等待代码到达它进行最终检查并替换程序集以将错误位翻转为真的地步。

一个明显的解决方法是将此函数的不同变体链接到游戏代码的不同区域,并在游戏过程中定期调用。使用#define C 宏而不是函数来实现此检查。将 10 种不同的代码变体实现为 10 个不同的宏。在代码的 50 个不同位置调用这些宏 - 这样黑客就必须找到它们(并修补它们)才能成功。如果内容被认为是无效的,那么游戏就会出现错误。例如,如果他的内容文件无效,则玩家的生命值和弹药值会随机归零,因此他很容易被杀死!

2) 更频繁地修补游戏(每隔几个月),即使除了生成内容的新哈希之外没有任何理由,并且 EXE 的新布局是另一种可以争取时间的保护。想象一下,你每个月都在给游戏打补丁,然后简单地更新加密哈希算法并对内容进行一些细微的调整。发布原始破解的黑客不得不重新开始。如果他再次成功,这将花费他更多时间来分发他更新的破解。

3) 我会同时对内容进行本地验证和网络验证。他们应该使用不同的散列算法。

4) 另一个可以争取时间的小技巧:验证 EXE 本身的 HASH 以及内容(本地和网络上的)。

5) 使用内容和 exe 的另一个哈希值作为简单的加密密钥,用于将所有网络消息数字签名到服务器。您可以简单地附加到网络消息:your_simple_fash_hash(content_hash, exe_hash, network_message)。服务器对所有收到的网络消息做同样的事情。为不同的网络消息类型实现此哈希/签名代码的不同版本。将Murmur 哈希视为一个快速哈希函数示例。现在黑客也必须破解网络代码!

6) 让黑客难以使用 Softice 或任何其他类型的内核调试器来反汇编您的游戏代码。任何用于存储内容验证哈希(或任何加密密钥)的变量也应该被加密。一种俗气的保护 - 在将所有重要变量存储在 RAM 中时,将它们与 0xAAAAAAAAA(或其他一些噪声)进行异或。当您需要使用这些值时,将它们异或回临时变量,适当地使用它们,然后在完成计算后立即将结果归零。除了内容文件的哈希值之外,玩家健康、燃料和弹药的变量也应该以这种方式处理。

7) 如果可能,找到一种巧妙的方法在 RAM 中移动重要变量,这样它们就不会在整个游戏中持续存在于同一内存位置。

好吧,这是一个大脑转储。比我更喜欢为在 stackoverflow 上只有 1 个声誉点的人写的东西。我希望它有所帮助。

于 2010-08-15T06:33:26.207 回答