首页 > 代码库 > 如何防止游戏关键数据被修改器篡改
如何防止游戏关键数据被修改器篡改
题记:
以前玩过pc单机游戏的同学,相信对金山游侠这款作弊修改器一定不陌生,玩家通过修改器可以轻松地定位、修改游戏里的关键变量,如boss血量,金钱,主角战力等数据,轻松制霸游戏。作弊修改器的原理非常简单,就是通过数值搜索,在内存中查找到相同数值的变量,确定其是目标变量后,对其进行修改。相似的作弊器还有cheat engine和手机端的八门神器。这类作弊修改器除了对单机游戏有效,对于一些部分游戏逻辑放于客户端处理,没有经过服务器验证的网游,同样有效。我们都知道,外挂对于网游而言会极大破坏游戏公平性,不啻于毁灭性打击。
解决这类作弊的办法有许多种:
1、最彻底的方法当然将所有的游戏逻辑都放在服务端判断,或服务端进行强校验,确保客户端没有作弊。但这类方法的难点比较大:服务端需要与客户端保持一样的计算逻辑,会使服务端的复杂性增大;客户端上报的数据包不可避免会变大。
2、退而求其次的方法则是进行客户端防内存修改处理。对于关键内存数据进行加密存储,虽然安全性不如前者高,但可以有效地防范前面所提到的那一类作弊修改器,保证关键数据安全。
上周我们的页游项目上了一个新功能,是一个较独立的游戏系统,上了一个星期不到,有人就发现用ce修改器可以篡改关卡boss血量,从而达到快速通关。像这种关卡类的游戏,一般都是消除类游戏,游戏步骤多且细,其游戏逻辑一般是由客户端判断完成后,再将游戏结果上报给服务端的。而服务端验证方面只做了游戏开始,游戏结束两条协议的匹配验证、游戏时长的验证(游戏时间少于一定时长内判断游戏无效,防止使用外挂快速通关),关键数据未做内存加密,也没有进行服务端验证,以致让修改器有迹可循。讨论后,决定用第2种方法,对关键数据进行内存加密。
通过一个自定义的安全类型SDTInt来代替int类型进行数据存储,其原理非常简单。SDTInt通过set/get value方法来存放/读取关键数据,set/get方法里分别会进行加密/解密的操作,因此常驻内存中的关键数据是以密文的形式存在的,可以有效防范使用数值搜索修改的外挂。而其加密/解密原理也非常简单,set时随机产生一个8位的key数组,让明文数据与key数组进行位异或操作,得到密文,从而实现加密;get时让密文与key数组再次进行位异或操作,得到明文,从而实现解密。关键代码如下:
protected override function doDecrypt(originBytes:Array, keyBytes:Array):Array { return crypt(originBytes, keyBytes); } protected override function doEncrypt(originBytes:Array, keyBytes:Array):Array { return crypt(originBytes, keyBytes); }
private static function crypt(originBytes: Array, keyBytes: Array): Array { var bytes: Array = null; if (originBytes && keyBytes) { bytes = MemoryPool.getArray();//[]; for (var i: int = 0, n: int = originBytes.length, m: int = keyBytes.length; i < n; i++) { var byte: uint = originBytes[i]; var mask: uint = keyBytes[i % m]; byte ^= mask; bytes.push(byte); } } return bytes; }
经过实测,使用SDTInt代替原生的int类型存放数据,可以有效避免被这类外挂搜索到。我实现的版本是as3,理解原理后可以很容易地改写成其他语言。附上githut上的源码demo,供有需要的人学习。
如何防止游戏关键数据被修改器篡改