阅读目录
实战篇——第一道习题及答案
发布于:2016-7-12 09:49 | 123437次阅读 作者: 管理员 | 原作者: TP | 来自: 原创
题目: 逆向分析2048游戏生成随机数及通关过程,尝试构思2048游戏能否实现快速通关功能。 习题答案: 游戏功能分析实现外挂功能过程中,前期的游戏分析是必不可少的,只有掌握目标功能的游戏逻辑实现,才能快速做出相应需求的外挂。下面将按照常规的分析思路一步步找到目标功能实现点。 1.1功能可行性分析2048这款游戏玩法比较简单,具体的玩法读者可自行体验。目前很多平台上都有这款游戏的踪迹,本章重点关注的是android平台上面的这款游戏。首先需确认怎么做才能实现游戏的快速通关。下面列出一些可行的方案: 1. 影响生成牌的逻辑,让随机生成的牌均为同一种,如都是生成2。 2. 影响通关的判断逻辑,修改成就算不是达到2048也能通关。 3. 影响牌与牌之间的组合逻辑,比如2和4组合马上变成2048。 4. 影响生成牌的逻辑,直接生成一个2048。 带着这些想法,需要考虑另一个问题,就是这些逻辑会在客户端实现吗?这些生产牌的逻辑或者牌之间组成的逻辑等会在客户端吗? 虽然依据经验判断2048游戏不会在服务器中实现相关逻辑,但是这里还是详细给读者讲一下大概可以用什么方法初步的判断出来。先参考下之前的《游戏系统及开发相关概念介绍》里面的强联网和弱联网小节,这种游戏的经典模式在玩法上面与单机游戏类似,中间游戏过程就算断网也可以玩,初步可以认为游戏中间过程的游戏逻辑是在客户端处理的。那么就基本确定快速通关功能是可以做的。 既然功能存在可行性,那么利用上面提到的四种方案中选择一种,尝试去分析游戏客户端的实现。为了使功能实现得更加稳定,初定选择第一种方案。(第四种方案属于简单粗暴型的实现方式,由于功能过于高调,会存在被游戏的安全策略检测到的可能) 1.2 游戏引擎确认游戏的逻辑分析一定程度依赖于对引擎的了解程度,游戏引擎相关的概念和基础知识请参考《游戏引擎基本概念及常见引擎介绍》章节内容。 Android版2048手游安装包文件为2048_6.11_50.apk,接下来对2048_6.11_50.apk进行解压,使用Apktool或者直接解压均可以,解压之后的文件结构如下图1所示。 图1 2048手游解压之后文件结构 点击进入目录lib\armeabi\中,可观察到该目录中存在唯一一个so,名字为libcocos2dcpp.so,基本可确认游戏使用cocos2d-x引擎开发,并且以c++ 作为主逻辑编写的核心语言,主逻辑在so文件中。该类型游戏反编译得到的smali代码(java代码在android中的中间码)一般不存在游戏逻辑相关的代码,因为java代码只负责界面显示。(此处特别说明:游戏解压后如果没有lib目录的,那么游戏逻辑极有可能通过java代码实现,此类纯java的实现方式一般适用于玩法特别简单的游戏,如flappy bird) 1.3寻找关键逻辑通过上面的介绍,确定游戏逻辑通过C++语言实现,游戏的逻辑由客户端控制,接下来需要实际分析相关逻辑实现方式。读者可回顾《游戏逆向分析实战篇》中关于C++游戏分析的相关内容,下面将会在IDA中使用字符串定位法静态分析游戏逻辑实现方式。 将libcocos2dcpp.so拖入IDA中,暂且不关注游戏有没有符号信息,在IDA中按下“Shift + F12”快捷键便可获取游戏所有字符串信息,在IDA的字符串窗口可执行搜索相关的操作。实例中将会搜索“create”字符串,因为快速通关功能将修改创建牌的数字实现,可通过搜索“create”获取创建牌信息。IDA中“create”相关字符串显示如图2所示。 图2 IDA中搜索“create”的字符串 双击图2中“Create 2048 tile to win the game!”字符串之后跳转至字符串定义的位置,此时可获取到字符串的交叉引用,交叉引用的框中内容如图3所示,图3中存在PlaygroundController类函数信息,由此推断游戏没有抹除字符串(如函数名字符串)。 图3 aCreate2048Tile的交叉引用窗口 可猜测PlaygroundController类与游戏控制相关,在IDA的函数窗口搜索“PlaygroundController”字符串之后显示信息如图4所示: 图4 PlaygroundController类的成员函数
图4中存在分数相关的函数信息(对应函数名为:increaseScore),双击PlaygroundController类分数相关函数,可获取到该函数交叉引用如图5所示: 图5 PlaygroundController::increaseScore的交叉引用窗口 PlaygroundController::increaseScore函数负责增加主场景的分数,该函数上层调用函数为Playground::pushToIncreaseScore,Playground是PlaygroundController的基类。在IDA的函数窗口中搜索Playground类成员函数,此时会显示重要的逻辑处理函数信息。 通过IDA的Imports窗口搜索“rand”关键字,显示的信息如图6所示: 图6 Imports窗口“rand”关键串的结果 2048游戏在生成数字阶段会用随机函数,一般的编程思路为:通过调用API函数生成随机数,利用生成的随机数再结合一些算法去生成随机值,那么随机值处理方式可作为分析的线索。事实证明猜想是正确的,图6中arc4random函数的交叉引用信息如图7所示: 图7 导入函数arc4random的交叉引用 图7中存在Playground类,可推测通过Playground类定位到随机值生成的相关函数。Playground类中存在Playground::addBoxRandom 函数,通过函数名可推测与随机数的生成有关,函数实现如下图8所示:
图8 Playground::addBoxRandom的函数实现 游戏的玩法如图9所示: 图9 游戏过程截图 此时可通过动态调试验证之前静态分析的结论是否正确。动态调试具体的操作方法可参考《动态分析》章节中Android平台IDA动态调试相关内容,先通过“ aapt dump badging 2048_6.11_50.apk | grep package”获取到这个游戏的包名为“com.estoty.game2048”,IDA调试器附加游戏进程,之后便可调试程序。 每次滑动一个方向,Playground::addBoxRandom函数就会被调用一次,验证静态分析的结论完全正确。在BoxPointArray::count()的返回值处设置断点,如图10所示当断点触发时R0数值为0x10,十进制为16。意味着传入Playground::addBoxAtIndex的第二个参数区间为[0,15]。 图10 在BoxPointArray::count()的返回处断下的截图 因为游戏中有16个空格,Index的区间是[0,15],那么Box是什么?推测为每次滑动后生成的正方形,游戏定义为Box(也可理解为牌)。Playground:: addBoxAtIndex函数实现如下图11所示 图11 Playground::addBoxAtIndex的函数实现 图11中首先调用arc4random()函数,然后调用的Playground:: addBoxAtIndexWithLevel 函数中r2只有1和2两种可能,按概率计算1出现的可能性非常大。接下来通过动态调试分析参数的作用。首先滑动一次屏幕,然后让游戏停在断点位置,如图12所示修改R2寄存器为5。 图12 修改addBoxAtIndexWithLevel 函数R2参数 图12 在Playground::addBoxAtIndex 函数中将Playground::addBox AtIndexWithLevel函数R2寄存器修改为5,继续执行后,屏幕中反馈出一个有趣的信息,生成了一个值为32的Box,如图13所示: 图13 修改R2为5后继续运行游戏的截图 聪明的读者一定想到了2的5次方是32。那么2048是2的多少次方?是11次方,也就是十六进制的0xB。尝试修改R2为0x0B试试,修改后执行效果如图14所示: 图14 修改R2为值0x0B后继续运行游戏的截图 图14 提示的信息表明已顺利通关。下面会讲解功能实现的思路。 |

最新评论
发表评论