手游防破解防外挂技术方案(二)服务端篇

由于客户端理论上总是能被人破解,所以所有客户端都是不可信的,只有一个服务端是可信的。客户端的逻辑不可信,客户端发给服务端的数据也不可信。服务端作为唯一可信的中心,需要去验证客户端端发来的数据是否真实可信,然后根据业务需求做一些处理。此外,客户端和服务端的通信信道可能被人截获,所以通信协议上要做加密处理。本文分以下几小节:
- 通信协议加密
- 账号伪装
- 重放攻击
- 异常数据判定
- 一个案例
 

通信协议加密

在网络上传输重要的数据都不应使用明文,有很多工具可以很容易地截取协议包,抓取和更改其中的数据,比如WireShark。对于长连接,使用TCP或UDP协议,数据会以明文方式存在于协议包中。对于短连接,http如今已很少使用,苹果明确规定开发者须使用https,而非http,对于使用http协议的app会拒审。即使使用https,数据被网络协议层做了加密,但仍不安全。最典型的一种攻击是中间人攻击,做法是攻击者位于客户端和服务端的中间,截获任何一方发送的数据,处理后发给另一方。对于客户端来说,中间人伪装成了服务端。对于服务端来说,中间人伪装成了客户端。中间人截获到任何一方的数据,都用公钥做一层解密,然后用自己的私钥加密数据发送给另一方。这种情况下,我们设计的所有通信数据会被明文暴露给攻击者。所以,不论使用什么形式的连接以及哪种网络协议,都需要自己做一层通信数据加密。
 
典型的方式是用对称加密,客户端和服务端协商好一个密钥,可以通过一个公用的逻辑生成,也可以由服务端生成后传输给客户端。根据共用的密钥,一方对数据做加密后发送,另一方获取到数据后做解密。经过这样处理后,攻击者抓包后无法简单地获取到解密数据。但是,由于客户端总是能被人破解,通过分析二进制代码或dll中间代码,动态调试等方法,就能逆向出加密解密的算法和密钥。所以,我们能做的就是增加破解的难度,通常有以下方法:
 
1 用加固加壳反调试等各种方式增加逆向客户端代码的难度。
2 密钥不存明文,而是通过一个函数动态计算返回,函数名和实现都写得尽量难懂一些。更进一步,可以一定程度牺牲代码的可维护性,不把通用的加密解密算法封装成函数,而是把逻辑分散到网络相关的逻辑中,加大逆向难度。
3 密钥经常更换,比如每一次连接用一个密钥,或者一次客户端启动用一个密钥,或者每一个客户端版本用一个密钥。根据业务需求设计不同强度的密钥逻辑。
4 原始数据最好不用文本格式,而用二进制,比如Protocol buffer。
 
通信协议加密是防破解中重要的一环,一旦协议被人破解,攻击者就能获取原始数据制造进一步的危害。脱机挂就是其中一种,完全舍弃了游戏逻辑,只要实现所有通信协议接口就能伪装成客户端和服务端通信。
 

账号伪装

客户端是完全不可信的,所以服务端应校验客户端的真实性。一般游戏,服务端会对每个客户端分配一个唯一标识符ID,以方便管理。攻击者可能会改变自己的ID,从而把自己伪装成其他玩家,然后修改或破坏其他玩家的数据。很多攻击者利用这种方式来帮助其他玩家修改数据,从而牟利。所以,我们得从机制上防止账号伪装。一个解决方案是用token(或cookie)机制,大致流程为:
 
1 客户端登录时,服务端回给客户端一个token。该token是服务端根据用户ID等重要信息加密后得到。注意加密算法和密钥仅保存于服务端,客户端不知道,所以破解者无法破解。
2 客户端保存该token,以后发送任何请求,都带上该token。
3 服务端响应客户端后续请求时,先根据token和密钥解密得到用户信息,与客户端请求中的其他信息比如用户ID做对比,若不匹配,则校验失败,判定客户端不可信。
 
Token机制实现上的考虑:
1 token算好后是存入缓存,后续收到请求时直接从缓存取,还是丢弃掉,每次请求时再算一遍。
2 token是否设置过期时间。
 

重放攻击

重放攻击是一种常见的攻击方式。攻击者截取网络包,简单地复制多份,重复发送给接收端,使得接收端误以为事件发生了多次。客户端和服务端都可能遭受这种攻击。攻击者可以用这种方式实现各种效果,比如有限时间内多次攻击怪物,多次下单购买商品,多次获取同一商品。解决方案一般有几种:
 
1 自增ID。协议包包含一个自增ID字段,每发送一次自增一次。接收端收到包后,判定ID是否和之前重复,或者增长量得太大,如果是则有重放攻击的风险。这个方案实现简单,但一旦网络协议被破解,很容易绕过去。
2 随机数。协议包包含一个随机数字段,发送端和接收端都用随机数生成器生成一连串随机数,用一个buffer保存之前一段时间接收到过的随机数。接收端收到包后,判定随机数是否在buffer内。更进一步,可以判定收到的随机数是否在未来N个随机数范围内,以判定网络协议是否可能遭到了破解。
3 时间戳。协议包包含一个时间戳字段,接收端判定收到的时间是否是过去时间且和现在时间有较大差值。一般会结合时间戳和随机数,在秒级别的时间戳上拼接一个随机数,如果有重复则有被重放攻击的风险。
 

异常数据判定

所谓道高一尺,魔高一丈。我们做的很多防破解工作都是在提高破解门槛,增加破解难度,根本上很难防住所有漏洞。但是,在服务端我们有一种从根本上解决问题的方式。任何一个被破解的数据,在数据上都会体现出一些规律。所以我们可以玩家的行为数据上寻找线索和模式,以判定该数据是否为作弊玩家。
 
比如,玩家的攻击力,攻击频率,游戏货币的生成和消耗,各种道具的生成和消耗,都会符合我们游戏设计的一些规律,比如事件的产生不会超过一定频率,某些数值的变化量每次不会超过一个阈值,某些数值的总量不会超过一个阈值,一些数据之间可能存在某种约束关系。我们可以记录下这些关键数据和事件的消耗,上传给服务端,由服务端的一个特定服务来批量处理这些数据,然后判定玩家是否存在作弊行为。
 
这些判定不一定非常准确。在判定的严格程度上,我们宁可漏掉一些作弊玩家,也不能把正常玩家误判为作弊玩家。可以根据数据处理结果,把玩家标记为几个档次:正常,嫌疑,作弊。确定为作弊的玩家可以自动做警告,封号或清空数据等处理。对于嫌疑玩家,我们不确定他是否一定作弊了,就用人工再筛查一遍,并采取相对温和的处理方式。为了满足这些需求,我们需要建立相对完善的后台工具。
 

一个案例

淘宝上有很多卖游戏破解的服务。比如有个比较流行的弱联网游戏《梦幻家园》的破解服务,可以得到上亿金币和奖杯。卖家提供的破解有两种方式:
 
1 伪造客户端
我们只需要给破解者提供我们设备的IDFA,破解者就可以伪造该玩家的客户端,上传一个无限金币的存档。若玩家在系统设置里设置了限制广告标识符,则IDFA获取为0,这个方式就行不通。
2 伪造服务端
按照如下步骤:
(1)从破解者指定网站下载证书,将其添加到系统信任。设置wifi代理,填写为破解者提供的网址。
(2)进入游戏,此时客户端的网络请求会发给破解者的后台程序。破解者解包得到玩家的存档信息,然后修改为无限金币,返回给客户端。
(3)客户端拿到了该存档后,就已经是无限金币。删除证书和代理,再进游戏,连到官方服务器,金币仍为无限。
 
这两种破解都需要完全破解通信协议,而这又是通过逆向分析完全能做到的。该游戏的开发商Playrix也做了一定防护,当金币改成无限后玩一段时间,就会弹出警告框,提示检测到您的数据出现异常,被限制参加各种联网活动。这正是我们前面提到的异常数据判定方案。
 
 

猜你喜欢

转载自www.cnblogs.com/nichos1983/p/13342155.html