今年3月初写过一篇《iO平台游戏安全小议》,到现今已有7个月了。在这段时间内,iOS平台上的安全问题也产生了不小的变化。从作弊方式来说,从以前稍有门槛的手工操作(命令行操作)发展成了傻瓜式的只需要点击按钮的
今年3月初写过一篇《iO平台游戏安全小议》,到现今已有7个月了。在这段时间内,iOS平台上的安全问题也产生了不小的变化。从作弊方式来说,从以前稍有门槛的手工操作(命令行操作)发展成了傻瓜式的只需要点击按钮的外挂工具的普及,使得作弊方式可以大量的泛滥。从防御方式来说,发展虽然缓慢,但也有了一定的有效对策。
游戏,无论是运行在哪个平台,PC机,掌机,街机,电视还是手机上?无论是单机,弱联网的,强联网的?无论是否具备客户端,网页游戏还是客户端游戏,本质上就是数据与指令。总的来说由三部分组成
1.负责逻辑处理即指令部分
在windows上它有exe,dll后缀,好识别(还有swf,air等后缀),基本是PE32 executable格式。
在linux/unix系统上为ELF executable,so格式
在mac/iOS上它为Mach-o executable,dylib格式
在Android上它为dalvik dex,so格式
无论是啥格式,都是可以依靠调试与逆向来查明或修改其处理逻辑,唯一的区别就是实现语言和运行平台的不同带来的使用工具和逆向难度有所不同。
实现语言不同,决定了得到的汇编代码还是源码,像java和as可以直接获得源码,而objective-c(c或类c)目前只能获得汇编代码。
运行平台不同,得到了汇编代码也会有所不同,是Intel的还是arm的,目前手机上都是arm的。
2.留在本地的数据(内存+硬盘)
雁过留痕,运行在本地的游戏,会将数据写入内存和硬盘。这也是游戏作弊的重点对象。
通过修改写入硬盘的数据作弊的外挂,往往会有xxx(游戏名)存档修改器,xxx(游戏)存档补丁/插件这种直白的名字。
(存档修改是个比较综合的半手工作弊方式,计划专门为其写一篇博客)
通过修改写入内存的数据作弊的外挂,名字则会有趣得多,windows有名的有cheat engine,手机上的有八门神器
很明显cheat engine要比八门神器功能要强大的多,它支持多种数值类型(包括自定义数据类型)和扫描方式,八门神器虽然支持的类型较少但也足以应付常用游戏数据类型的修改了,并且我相信只要手机游戏继续繁荣,在不久的将来其功能毕将更加完善。
内存修改器对游戏收费与游戏寿命的破坏非常大。对大多数玩家而言,游戏过程就是打怪升级的一个过程,给一个目标,然后达成它,无论目标是获得更多的积分还是争取更多的游戏币。如果这些数值不做加密处理,是非常容易使用工具定位和修改成指定的数值。
对玩家而言,如果目标容易达成,游戏的乐趣也随着减少了,可能会寻求另一款游戏。而且免费可以获得的东西大多数人是不会再花费获得了。即使玩家是真正喜好游戏的,作弊获得好处的人带来的不公平感也会迫使玩家离开游戏。
想想这个场景,如果1000个游戏币需要100块人民币,当你发现通过八门神器可以不花一毛钱将游戏币的数量改成999999的时候,你会再花费吗?
要保护游戏不被八门神器这种工具破坏,就要从工具的原理,修改对象及缺陷着手处理。
内存修改工具的原理都是通过不断改变待修改数据的大小来缩小地址范围来定位实际的数据存储地址并给以修改。
玩家的修改对象是在游戏运行过程中,存储在内存中的可以在游戏界面看到变化的游戏数值,比如说游戏币,道具数量,等级,HP,攻击力等等。
由此我们可以得知八门神器只能定位到特定数据类型(浮点型数据,自定义数据,和需要服务端做验证的数据都不能修改)的数值,并且只能进行精确数值的修改,而普通玩家也只会修改在游戏界面有回显的数值。
因此我们可以将存储在内存中的数据经过加密算法来进行转换(将游戏数值由常规类型变成自定义数据类型),使得内存修改器不能定位到搜索的数值,也就无法进行修改。
例如
int c = 30000 常规存储为 0000 0000 0000 0000 0111 0101 0011 0000
可以通过特定算法来进行位转换和位移动操做来变成自定义存储格式,例如存储为0000 0000 0000 0000 0001 1100 0000 0010
这种加密方式可以防御普通玩家使用工具进行修改,但抵挡不了逆向高手获知加密算法逻辑后定制的特定搜索修改器,但已经从很大程度的防御了普通玩家作弊。针对外挂专业工作室的防御,将是逆向与反逆向的领域了,相信只有火到一定程度的游戏才会引起这些人的专门破坏。
最后,为了减轻定位到修改数值后造成的破坏,最好在调用该数值的时候,检测一下数值的逻辑范围,将过于夸张的数值判定为非法。
3.传输在网络中的数据
游戏从app store中下载需要联网,游戏中有购买行为(例如购买道具,解锁关卡)也需要联网,如果游戏需要在开发商服务器上存储数据或进行逻辑处理(例如登录),也是需要联网的。
总的来说,联网过程涉及到三方,app store,开发商服务器(即游戏服务端),和玩家设备(即游戏客户端)。这三方间的数据传输,只要经过玩家设备的都可以通过代理工具捕获并进行修改。
对于需要在登录验证并在服务端存储数据的游戏,最常犯的错误,就是在玩家登录游戏后,将服务器拉取的游戏信息明文返回给客户端。
例如来自同一公司也具备同样错误的space city和contract killer游戏,就是登录的时候,从服务器返回当前的金币数TapPoints,我们可以通过抓取服务器返回给设备上应用的数据包,将金币数量修改为想要的数量就ok了。
这种在设备上安装代理工具,劫持并修改通信包的方式,是具备悠久历史的,也叫中间人攻击MITM(man-in-the-middle)。唯一的区别是不同的运行平台有不同的工具,越成熟的平台工具越容易操作越方便普及,而不成熟的平台则需要命令行操作和数据包分析等专业知识,操作门槛越高游戏相对越安全。
当然安全也取决于破坏对象带来的利益是否具备足够的吸引力。而IAP(in-apple-purchase)游戏内购(即通过IAP下载到设备内的额外的解锁内容,英文全称是DownLoadable Content,通常简称DLC)解锁就具备了相当大的吸引力,在这一功能上,破坏方法也是非常精彩的,从早期的iap cracker工具,到现在非常流行的iap free工具,还有非越狱机也可以使用的通过伪造IAP认证服务器的俄罗斯游戏内购方法,都非常好用。
(补充:游戏内购部分的本质就是需要付费才能下载到本地的游戏内容,而通过存档修改覆盖也可以达到解锁的目的,网上有名的游戏内购解锁网站也多是有偿提供这种存档来解锁盈利,计划在下篇存档修改博客中具体介绍这种方式)
从iap cracker发展为iap free,我们可以看到外挂功能升级的一个过程。
(1)iap cracker
我们从IAP的API上分析iap cracker到底做了什么,相信具备IAP功能的游戏中一定有这样一段类似的代码
开发就是根据transaction的类型来决定内购状态变化所要呈现的东西的, 购买状态transaction的值有四种
SKPaymentTransactionStatePurchasing
SKPaymentTransactionStatePurchased
SKPaymentTransactionStateFailed
SKPaymentTransactionStateRestored。
而所有交易成功的消息就会进入到SKPaymentTransactionStatePurchased这个case中,而iap cracker就是模拟了返回交易成功的消息而进入到了这个case中。如果不做下一步验证,当我们应用中收到这个“假的”交易成功的消息就会直接给用户加钱,加设备,加各种东西,iap cracker运行的整个过程甚至都无需联网验证。
有趣的是这款软件在使用说明上就标注是使用了什么样的漏洞,只要不需要游戏服务器再次验证的处理方式就会中招。
而针对这个问题,apple给开发者的建议就是验证付费收据,如果检验失败,则将该用户(设备udid)加入黑名单,进行封号处理,封号阶段,从该设备向服务器发送的所有请求都将被拒绝。(有趣的是这一处理方式滋生了另一个叫UdidFaker的辅助工具,可以随机伪造设备的udid。)
验证的重要步骤如下:
1.将提取的订单信息转换成如下json格式
{ |
"receipt-data" : "(receipt bytes here)" |
} |
2.将生成的json数据通过HTTP POST方法传入到https://buy.itunes.apple.com/verifyReceipt去验证
3.app store server验证完成后,会返回以下格式的json数据,根据status的值就可以判断订单是否合法,只有为0才是合法的,如果不为0,则是非法的。
{ |
"status" : 0, |
"receipt" : { (receipt here) } |
} |
以下是合法的订单情况
{“receipt”:{“original_purchase_date_pst”:”2012-07-12 05:54:35 America/Los_Angeles”, “purchase_date_ms”:”1342097675882″, “original_transaction_id”:”170000029449420″, “original_purchase_date_ms”:”1342097675882″, “app_item_id”:”450542233″, “transaction_id”:”170000029449420″, “quantity”:”1″, “bvrs”:”1.4″, “version_external_identifier”:”9051236″, “bid”:”com.zeptolab.ctrexperiments”, “product_id”:”com.zeptolab.ctrbonus.superpower1″, “purchase_date”:”2012-07-12 12:54:35 Etc/GMT”, “purchase_date_pst”:”2012-07-12 05:54:35 America/Los_Angeles”, “original_purchase_date”:”2012-07-12 12:54:35 Etc/GMT”, “item_id”:”534185042″}, “status”:0}
(2)iap free
iap free则在iap cracker的基础上(伪造SKPaymentTransactionStatePurchased 的transaction状态),在你与app store server进行收据验证的步骤中,增加了伪造假收据的功能
最新的iap free针对开发商会根据UDID或mac地址这些设备唯一标识来进行封号处理推出了UDID和mac地址伪造功能。
(3)俄罗斯游戏内购方法(伪造成app store server)
第一步:注销已登录的apple ID
第二步:在设备上安装证书文件
http://system.in-appstore.com/certs/cacert.pem
http://system.in-appstore.com/certs/itcert.pem
第三步:修改设备接入wifi的DHCP设置中DNS服务器栏,替换为黑客提供的地址
【二、三步就是通过在iOS设备上安装伪造的SSL证书,修改DNS表将正常请求重定向到攻击者搭建的服务器上,使其可以伪造成app store server,无论订单是否合法,都返回合法的状态来达到iap内购破解的目的】
第四步:进入游戏,打开购买链接,在如下弹出框中选择like,然后随便输入非真实的apple id,就可以购买成功了。
和iap free类似,最终目的都是返回购买成功的订单数据,只不过iap free是在游戏客户端将来自app store server的验证数据由失败状态改成成功状态。而俄罗斯游戏内购,则是伪造成app store server,让其直接返回购买成功的验证数据。
这种即使有了IAP收据验证,也能被iap free和伪造app store server进行游戏内购破解的关键原因是,收据验证过程是由iOS设备与app store server直接验证的。
再次强调,从iOS设备上游戏客户端返回的数据是不可信的,是可以篡改的。如果要保证验证过程的安全,也只有由游戏服务器与app store server进行直接验证。
苹果针对(2)(3)这种收据验证漏洞,也给出了相应的处理方法
第一种情况:收据验证,是由开发商自己的游戏服务器与app store进行验证(不经过iOS设备上的游戏客户端)
不会受iap free和俄罗斯内购破解的影响,是苹果推荐的最佳验证方式。安全的验证流程应该如下图所示
通过看这个流程图,我们可以得知之前iap实现所犯的错误,
iap cracker有效的原因是由第8步到第9步,iOS设备上的应用直接相信了伪造的SKPaymentTransactionStatePurchased状态。
iap free和俄罗斯内购破解有效的原因是收据信息由app store和客户端直接验证,客户的相信了伪造的购买成功的收据数据。
正确的过程应该是由第11步到12步,由游戏服务器与app store直接通信去验证收据数据。如果收据合法则下发购买部分内容给客户端应用(第12步到第14步)。
第二种情况:收据验证,是由游戏客户端直接与app store进行验证
虽然苹果建议开发商最好采用第一种情况的验证流程,但无疑这种流程增加了服务器与app store的通信验证等待时间,针对无线网络不稳定的情况下,可能导致支付过程中端,影响用户的付费率。
我观察到不少iOS应用,其中不少是国外的知名游戏开发公司出品的(例如日本的音乐类游戏乐动魔方就完全可以内购破解)仍然选择由客户的验证收据,或者连收据都不验证。不知道是否有这方面的原因。
对坚持由客户端直接与app store进行验证的,苹果也给出了以下对策:
1.检查用于连接app store server服务器的SSL证书,确认是否是EV 证书(Extended Validation SSL Certificate)
2.检查返回的验证信息是否与SKPayment对象中的信息一致
3.检查收据上的签名是否合法
4.检查transaction ID是否唯一
并提供了相应的验证实现代码https://developer.apple.com/library/ios/releasenotes/StoreKit/IAP_ReceiptValidation/VerificationController.zip
苹果提供的两种验证方式都属于事前验证,只有在通过验证的情况下才下发购买部分内容给客户端。
在早期的iap实现中,一般采取的是事后验证,即在交易完成后,将收据存储到服务器上,然后再次与app store server对账,非法交易的给予封号处理,为了怕影响玩家情绪,往往只对超大金额的玩家给予惩罚处理,惩罚其失去联网部分功能,但对于弱联网的游戏,这种惩罚并不能带来威慑。并且在游戏推广争取用户阶段,有封号,也就有相应的解封操作,解封过程一般采取客服人工解封,在iap free这种傻瓜式工具泛滥的情况下,大量的非法订单带来的封号解封处理必然增加了客服工作量。
总的来说,游戏安全的发展是与外挂博弈的过程,因为太多的原因,或是游戏性能,或是玩家体验,或是人力成本,有效防御总是滞后于攻击手段的。但正是这些有趣的外挂,让我们游戏实现更具挑战。