文 / 幻想搭积木
这里主要总结一下单机手机游戏里一些保护措施。虽然看似简单,但效果也还可以,能让试图破解你游戏的人稍微费点功夫。当然完全防止破解是不太现实的,这本身就是个成本问题,破解者全力以赴于破解,开发者还是要全力以赴于游戏逻辑的。
内存加密
我记得我十几年前玩一些PC单机游戏的时候,使用过一个叫金山游侠的工具修改内存,把游戏里的钱或者其他属性改成我想要的。基本原理是,游戏运行的时候里面的一些数值比如金钱、生命值之类的都是存储在内存中的,修改器就通过搜索游戏进程中中所有特定数值的内存并返回,让你可以修改。比如你在一个游戏里有22222金币,然后你用修改器搜索内存,找到了一个地方存着这么个值,那你就基本可以肯定这个地址就是游戏程序用来存放金币的内存地址,然后只要修改这个地址上的值,你游戏里的钱就也被改了;如果第一次搜索搜到多个结果,可以玩一会儿游戏等数值变了再搜,多试几次,几次的数值变动情况都对应到同一个地址也就能确认位置了。
应对这种作弊方式很简单,一种是加密存储一些关键性数值,每次代码中要读取/赋值也都要走一遍加密/解密的步骤,内存中永远存放的是密文。当然这样别人也很容易发现,游戏中明明有的某个数值搜内存竟然搜不到,那他也许就会采取措施来定位你的密文然后试图解密了。所以最好还是在内存中放一份明文的数值,让玩家可以搜到,当然等他们改了你就能知道,因为和密文对比不一致,然后就能知道这个家伙是个作弊玩家了。
至于检测到作弊玩家后如何处理,这就取决于开发者的脑洞了。可以对设备封号,可以偷偷的把这个玩家游戏里需要花钱地方的数值调高,或者让他的游戏变成另一个完全不同的样子,甚至我们的策划表示可以直接告诉作弊玩家“我们发现你是个作弊玩家,现在给你一个机会,只要充值10块钱,立马转正成正版玩家”。
存档加密
由于是单机游戏,所以存档都存储在本地的文件系统上。在Android和越狱的iOS上,是可以很容易得到这个存档文件的。如果直接使用某种文本格式来存储游戏进度,比如把存档序列化成xml或者json之类,那简直就是不设防,随便用一个文本编辑器打开稍微看一下就能知道怎么改;稍微好一点的用二进制来存放,但这也是可以很容易找到规律,或者干脆用像修改内存那样的方式来修改存档。
基于以上的情况,游戏存档最好加密进行存储,而且最好使用密钥加密的算法来做而不要把加密解密算法写死。这样以后哪天发现被破解了可以再更换密钥。
存档绑定
虽然存档加密了,破解起来稍微费点劲,但是还是可以拷贝存档啊!谁谁谁玩出一个很不错的存档,分享出存档文件给大家。其实这是很破坏游戏乐趣的一件事,正常点的玩家都不太会去做。但如果你的单机游戏还是有点玩家间交互的,比如有排行榜,看着那些作弊玩家占据着高名次,肯定觉得挺没意思的,然后就删游戏了吧。
其实处理这种情况很容易,让存档跟设备绑定就好了,而且存档肯定要加密,不然什么都白搭。具体做法分两种,一种是在玩家第一次玩游戏存档被创建的时候把这台设备的device id也写到存档里,之后每次要读存档的时候都比较存档里的device id和本机的device id是否一致,不一致则不让进游戏或者开启新的存档;另一种方式是把本机的device id来作为加密存档的密钥来使用,这样存档被拷贝到别的机器上之后也是无法正常读取的。
内购验证
如果一个游戏里可以花钱购买金币道具之类的,作弊者就可以通过内购破解来不花钱就得到那些东西。内购破解是一件比较通用的事情,因为玩家不是通过直接破解你的游戏而是破解了AppStore或者GooglePlay的支付来达到的;一般是在越狱的iOS设备和root的Android设备上进行的。
由于这种事情是在游戏以外的部分做了手脚,从游戏的层面上无法判断,在你游戏里你会收到正确的购买成功回调。不过好在AppStore和GooglePlay都提供了额外的协议允许你去验证订单,所以你可以在客户端的支付完成后再去苹果或谷歌的服务器查证刚刚的那笔订单的真实性。这一步都是放在自己的服务器上来进行的(我不确定手机端是否也能验证订单,不过即便可以放在手机端做也是不安全的),也就是你在手机端支付完成后再和自己的服务器通信把订单的信息传给自己服务器,自己的服务器再去跟苹果或谷歌的服务器验证这笔订单然后再告诉手机端这笔交易是不是有效。
这样做的话首先自己需要架设服务器专门用于内购验证,这是额外的开销;另外玩家在游戏中购买一个东西的时间会变长,稍微影响一些体验。当然堵死了内购破解这条路并不意味着能赚到原本内购破解所损失的钱,很多作弊玩家大不了就当免费玩家好了,另外还有一些情商高不放弃的会继续尝试其他的方法。
强制更新
虽然是单机游戏,一般感觉是不该做强制更新这种事的,当然使用了热更新技术的人也可以忽略这条。
这一步需要一个服务器,当然一般的CDN服务器就可以了(这只需要钱,不需写服务器代码),把一些基本的配置信息放在上面,每次游戏开启的时候都下载一下并存到本地(当然不一定要每次都下载,可以先下载一个放版本号的小文本,只有配置文件的版本比本地新才下)。然后这种配置信息里面可以保存一个游戏最低版本号的字段,如果发现当前运行的游戏版本低于该版本就把游戏锁定不让玩并引导玩家去更新游戏版本就好了(这种配置文件最好好也加密)。
其实这么做的意义在安全方面就是可以把以前发布的有严重漏洞可以利用的那些版本给干掉。
改时间限制
这一条主要是针对那些对时间敏感的单机游戏,比如挂机游戏这种。如果只是取本地时间的话,至少可以做成每次写存档都把当前时间写进去,然后玩家改时间作弊通常是改到未来把未来该得到的东西都得到了然后再改回来,只要你发现时间倒退了就对玩家做出一定的惩罚或者怎么样就可以了。
还有一种是联机获取网络时间,不时的获取网络时间来跟本地时间进行对比以判断玩家是否在时间上作弊了,或者在某些比较重要的逻辑进行的时候只使用网络时间(比如每日登录奖励)。具体的做法可以是自己架设一个服务器来提供时间,当然也可以从网络上免费的授时服务器来直接获取时间。
不过使用授时服务器获取网络时间有一些问题,由于获取时间使用的是NTP协议(参看RFC 1305),而NTP是基于UDP报文进行传输的,所以其实不是那么稳定,实测下来有的时候失败率挺高的;还有一个就是发现国内部分地区的运营商的网络环境下一直无法获取到时间,由于是玩家报告的,所以无法得知究竟是那个地方连不到国家授时中心还是那个运营商封了NTP协议。
迁存档限制
之前我所做的单机游戏里有一个功能,迁存档。虽然做了防止存档拷贝,但是为了让玩家可以在换手机的时候继续在新手机上沿着她(这里用“她”因为那个游戏的大部分玩家是妹纸)的老存档进行我们的游戏,我们提供了官方的存档迁移功能。
具体的做法就是先把存档上传到我们服务器,并返回给玩家一个随机码,然后客户端锁定上传了存档的游戏,让玩家通过那个码再从新设备里把存档下载下来,只能下载一次。
这个功能给很多作弊者带来了新的机遇,他们纷纷在淘宝上开店,通过迁移存档的方式,远程收钱帮助其他玩家修改存档。当时我们还没有做内购验证,很多卖家先把其他玩家的存档迁移到自己手机上然后用内购破解购买了很多充值币再把存档返还给买家。当然后来做了内购验证后一些只会内购破解的淘宝卖家被淘汰了,但还是有人能继续做这个生意,这充分了说明道高一尺,魔高一丈。
所以我觉得这种功能的使用应该是要受到一定限制的,比如固定的次数限制或者玩满多少个小时之后才有一次迁移的机会之类。
代码混淆
这个大家基本都知道,其实就是在游戏的源代码层面,在保持代码功能不改变的情况下将代码变的面目全非。一般就是通过大量的替换原有标识符的名字来达到这样的目的,不过这肯定不是人工来做,都是用代码混淆器来自动完成的。
当然对于某些写代码自带混淆的人来说,这一步其实没什么意义。(对吧,天王~)
游戏加壳
这个其实大家也都知道的,在游戏的包编译出来之后进行的。iOS的ipa我不是很清楚,但是对Android的apk来说,这一步还是挺有必要的。不加壳的话,别人可以很轻易的反编译你的包,加一点他自己的广告(或替换掉原有的)后重新打包在别的应用市场上发布;加壳的话,别人要稍微费点事来破解你的包。。。
现在国内的有些渠道甚至会要求你把包提交给他们之前先加个壳,不过不知道为什么一般是指定了用360加固宝,其实也是有一些比较安全的加壳工具比如APK Protect这种。
相关阅读:
弱联网手游如何防作弊?
Android如何防止apk程序被反编译