如何在Unity中完全隐藏和保护播放器的琴弦?


47

我一直在使用Unity创建一个完全脱机的2D游戏(这是问题所在),游戏过程需要您在特定级别输入某些字符串,并且Unity编译为DLL,可以很容易地对其进行逆向工程,因此有没有一种方法可以保护这些字符串(游戏处于离线状态,所以我无法从其他来源获取)?

游戏非常依赖这些字符串,是的,我知道混淆,但是我想要更强大的东西。而且我知道,简单的方法就是从数据源在线进行所有操作,但是我想知道是否有可能。

可以这样反编译: 在此处输入图片说明


10
虽然我同意这是一场您永远无法真正获胜的战斗,但您可以毫不费力地使它变得更加困难。obfuscar.codeplex.com
Jacob Persi

46
@Gabriele Huh?反编译未混淆的C#代码几乎很容易。这样,您将获得几乎完全可读的代码,并将其与IDA为优化的C或C ++代码生成的代码进行比较。话虽这么说,本机代码也可以被理解,但是难度要大几个数量级。不知道混淆的效果如何-如果它使IL代码中的常规反编译器(DotPeek,ILSpy等)bar倒,应该引入障碍以使随便的人远离它。
Voo

15
@GabrieleVierti,将文本从DLL中拉出是微不足道的:在Linux或Cygwin下,只需将strings程序指向它即可。
标记

31
您到底想在这里做什么?这听起来像是XY问题的一个非常严重的案例。 meta.stackexchange.com/questions/66377/what-is-the-xy-problem 无论您试图实现什么(从游戏角度,而不是技术角度),几乎肯定都不是尝试隐藏和加密这些字符串的最佳选择。
GrandOpener

36
我想知道隐藏字符串是否真的有目的:一旦一些玩家解决了它们,它们很可能会在Wiki或类似物中传播,因此任何想知道它们的人都可以轻松地进行搜索。是的,通过混淆它们,播放器不能只在文本编辑器中打开dll并查找字符串,而是大多数将首先咨询google(或他们选择的搜索引擎)...
hoffmale

Answers:


159

不要存储这些字符串,请存储它们的(加密)哈希。

(加密的)哈希函数(例如加密)是一种将字符串转换为“乱码”(称为哈希)的方法,但是与加密不同,您无法从此哈希中获取原始字符串(除非您可以对其进行暴力破解或哈希功能已损坏)。大多数(如果不是全部)哈希函数采用任意长度的字符串,并返回恒定长度的字符串(取决于函数)。

您如何检查用户输入的字符串正确?由于您无法从哈希中获取有效的字符串,因此您唯一可以做的就是对用户的猜测进行哈希处理并将其与正确的哈希进行比较。

警告(由Eric Lippert撰写):请勿将内置函数GetHashCode用作此类函数-不同的.NET版本和平台之间其结果可能会有所不同,从而使您的代码只能在特定的.NET框架版本和平台上工作。


81
这确实是最好的答案。但是请记住,绝对不能在字符串上使用内置的哈希算法。它被设计为只做一件事,而是做一件事,那就是平衡哈希表。您不能存储字符串的哈希并将其用作共享机密的验证者,因为.NET运行时作者保留基于任何原因随时更改字符串哈希算法的权利,事实上,它们过去已经这样做。使用加密强度标准哈希,或实现自己的简单哈希。
埃里克·利珀特

4
这个。究竟。如果您使用诸如sha256之类的算法(有许多实现此功能的库,那么您不必编写自己的库),那么您将拥有不易破解且非常可靠的功能。
米歇尔·约翰逊

12
@Michael Johnson使用盐会使使用彩虹表变得更加困难,而使用像bcrypt这样的密码哈希将使其变慢得多,因此很难破解。但是最终归根结底,这似乎需要付出太多努力。用户购买了自己想作弊的游戏
梅尔科

2
@MichealJohnson:密钥拉伸可能会使这种暴力攻击更加困难(例如,大约十亿分之一)。但是这个答案的真正问题在于,大概在某个时候,游戏需要能够告诉玩家他们需要输入哪个字符串。我猜除非这些字符串实际上是玩家需要解决的某种难题的解决方案。或者,除非游戏是脱机的,否则除非在线提供字符串,就像老式的许可证密钥一样。
Ilmari Karonen

5
@Sentinel这意味着不同玩家的游戏玩法可能会有所不同。我们真的需要知道我们在谈论什么类型的字符串,如果它们包含在游戏的书本/符号/对话框中,或者它们是解决难题的方法,而这些难题的答案并没有直接提供给玩家,或者是随机生成的。以及它们是单词,句子还是随机字符。
米歇尔·约翰逊

106

您试图做的事既徒劳又毫无意义。

这是徒劳的,因为无法正确隐藏用户计算机上的信息。任何专心致志的人都会找到它。您可以加大难度,但永远无法避免。如果对它进行加密,则需要将加密密钥和算法存储在某个位置。无论您添加了多少个加密层,为了运行游戏,始终都需要对最外层进行解密。

这也毫无意义,因为我们生活在互联网时代。当您的游戏在远程流行时,这些密码将被发布到整个网络上。

您所能做的就是相信玩家不要通过查找游戏本来不会告诉他们的信息来破坏他们自己的游戏体验。无论如何,绝大多数玩家都不会开始对游戏进行逆向工程。如果少数具备必要技能的人这样做,那是他们自己的错。


38
逆向工程本身就是一个游戏!这是唯一正确的答案-如今,不应在单人游戏中隐藏信息,玩家可以根据需要访问信息。
Mephy

27
@TheBinaryGuy安全来自何处?您的用户正在玩离线游戏。暴露代码会带来什么威胁?无论如何,最终应该让玩家发现它,然后才能玩游戏!一个重要的安全规则是,您必须确定要保护的待遇;这被称为“威胁模型”。
jpmc26

7
@TheBinaryGuy除了其他要点外,我还要质疑使用“固定”密码的敏感性-即使不在线/通过反向工程查找密码,仅第二次玩游戏就已经允许玩家直接跳过游戏的各个部分(因为他们已经知道代码)。如果您真的想“强迫”玩家获得这些代码,则需要在每次游戏过程中对其进行更改(例如:进行某种形式的随机化)
UnholySheep

6
这个答案有一些优点,但是第二段是不正确的。您不必加密字符串。您可以对其进行哈希处理。如果玩家可以打破哈希表,那么他将抢劫银行账户,被FBI雇用或类似的东西。其他要点虽然有效。
Pedro A

5
@Hamsterrific我猜答案是假设您需要存储实际的字符串,例如在某个时候向播放器显示它们,这意味着不会进行哈希处理。
Frank Hopkins

4

如果确实需要这样的东西,则可以考虑在运行时从数字输入值构建字符串,而不是进行哈希处理。

优点是,如@Philipp所指出的那样,如果可以期望将代码隐藏在Internet上,则尝试将代码隐藏在可执行文件中是毫无意义的。不管是否经过哈希处理,在互联网上找到并输入到游戏中的相同单词都将给出相同的哈希值,并且将以任何一种方式起作用。

除了...,除非别人的代码对您不起作用。您可以轻松地做到这一点-不是100%防篡改,但对于普通用户而言相当难以解决。像“在线精灵名称生成器”一样简单的事情就可以做到(可以任意简单,实际上不需要太多markov文本生成引擎,从随机列表中提取4-5个音节就足够了)。

只需生成一个特定于用户或计算机的编号,它甚至不必是完全唯一的或非常防篡改的。大多数人可能会有所不同,并且不太可能定期更改的内容,例如计算机的网络名称,MAC地址或系统磁盘驱动器的GUID,无论如何(GPU序列号可能非常差因为用户可能会升级GPU)。加上解锁代码所指的数字代码,并将其输入到单词生成器中。但是准备好在玩家使用两台计算机或更换其网卡时回答支持问题(这很常见,但并非不可能)。最好只生成一次随机ID,然后将其与游戏设置一起存储,这是一个不错的计划。这样,如果发生任何更改,至少它不会破坏同一台计算机上的现有安装。

或者,您可能只使用游戏的序列号,该序列号是唯一的,并且在用户更换硬件时可以使用(具有讽刺意味的是,但是,这可能会促进盗版,因为共享的解锁代码适用于盗版序列而不适用于合法客户!)。

注意,防止用户作弊不一定是一件好事。在离线(即非竞争性游戏)中,如果用户从某个地方而不是从游戏中作弊并获取代码,通常是没有问题的。他只是在欺骗自己。谁在乎。
另一方面,如果他们真的想作弊,那就别忙了,这是彻底烦死付费客户的绝好机会。

所以...在采取这种方式之前,请先仔细考虑一下您是否真的想要那个,以及想要什么。非常有可能,拥有人类可读的字符串(或用xor使其琐碎地变成“不可读的”)就足够好了,实际上是可取的。


您设想什么过程?如果程序在下载后生成哈希值,则在下载时必须具有未哈希的字符串。那么服务器会查询客户端以获取标识信息,然后在服务器端生成哈希吗?
累计

@累积:什么服务器?Q说“完全脱机”。这可能意味着DVD上的程序(或者可能是下载文件)加上序列号。因此,您有事件1,2,3,4,必须存在密码。例如hash(serial + 1),您计算得出与第一个代码相对应的数字。然后将其输入到单词生成器中,该单词生成器从输入的每4位16个列表中提取一个音节。随您去,为每个用户提供单独的“单词”。
达蒙

如果是下载,则表示正在从服务器下载。如果程序hash(serial+1) 下载后进行计算,那么阻止用户进行什么计算hash(serial+1)呢?一旦下载了程序,用户就可以访问程序执行的所有操作。
累计

@累积:没有什么可以阻止用户首先对程序进行反编译然后进行计算hash(serial + 1)。所以呢?这不是问题。看,如果有人花一到两个小时(对于没有软件开发人员背景的典型“用户”来说可能花费6到8个小时)只是为了欺骗自己,那...让他吧。事实是,这只适用于一个人,但不适用于每个人,而且它没有竞争力……所以,没问题。
达蒙

3

如果您不需要显示字符串,那么散列方法可能是解决之道。另一方面,如果确实需要将它们显示给用户,则可以通过其他方法避免它们直接显示在DLL中。

除了加密或混淆字符串以外,解决此问题的一种方法是将它们分解。也许只有一个按字母顺序排序的字典,其中包含游戏中所有字符串中的所有可能单词。然后在某个地方放置一个数组,使您可以通过索引单词数组将单词拼凑成所需的字符串。这样,您就不会在游戏中的任何地方拥有完整的字符串。不需要主密钥来解密字符串。举例来说,如果每个使用字符串的函数只有一个局部于该函数的索引数组,那么表明其顺序的数据就可以遍及整个源。我不确定这在您的特定情况下有多实用,但这是做到这一点的一种方法。

您甚至可能会有由几个单词组成的字符串,但是它们最终以不同的顺序排列。例如,您可能需要以下两个字符串:

  1. 一只熊走过我的房子
  2. 我的房子保护我免受熊

您的词组列表将同时包含“一只熊”和“我的房子”,但是您可能会在它们之间插入大量其他词组,因此弄清楚哪一个与实际弄清楚一样困难游戏中的难题(或其他)。例如,动作短语可以是“经过”,“被烧毁”,“被推到”,“保护我免受”,“与我隔离”,“神奇地产生”等。

您可以通过使索引基于玩家已收集或完成的操作来将其纳入您的游戏。因此,游戏内任何地方都没有索引的主列表。它们将由玩游戏的玩家生成。


4
因此,您无需查找引用特定字符串的函数,而是查找引用带有索引的数组的函数,然后重新创建字符串。似乎没有超过30秒的额外保护。它所保护的是有人只是在使用strings可执行文件。
Voo

如我所说,如果将引用字符串的数组分发给需要它们的所有函数,那么这比仅在单个函数中找到单个数组要难一些。并非不可能,但是无需进行大量额外工作即可覆盖更大的区域。
user1118321

这不只是一种较小的加密形式吗?
阿图罗·托雷斯·桑切斯

2
等待,甚至没有加密。只是编码。
阿图罗·托雷斯·桑切斯

2
我已将答案更新为更清晰。基本上,如果索引是基于玩家所做的事情,那么它们实际上并没有存储在游戏中。同样,它可能不是万无一失,也不是完美的,但我将其作为人们可能喜欢探索的一种选择。
user1118321
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.