freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

关于《火焰纹章:晓之女神》的乱数生成规律的初步研究
2019-08-08 10:40:21

0x00缘由

本人是火焰纹章、英雄无敌等战棋类游戏的业余玩家,虽然技术一般,但是乐在其中,玩过GBA三作,但是后来由于工作繁忙,一直没有时间体验最新作品,闲暇之余准备把一些经典拿出来体验一下,于是就开始了苍炎和晓女之行(当然是模拟器玩家),玩火纹这种战棋类游戏免不了使用S/L大法来避免全军覆没或者练出个奇葩,但是运气差的时候升级有可能一个点都没有,运气好的时候点数又会全满,不断读档凸点随机性太大而且很耗费时间,强迫症犯了就想如何能不用修改器让升级点数自然最大化(奇怪的症结)。当我体验了苍炎之后,发现同一个即时存档升级的时候点数总是一定的,因此也萌生了找到苍/晓的升级算法,并写一款可以预测升级点数工具的想法。

PS:本文仅用于技术讨论与思路拓展,我们尊重原作者的知识产权,如果您喜欢《火焰纹章》系列,请购买正版。

经过网上一番折腾,发现目前有这么些资源/提示:1.属性修改器很多,苍炎本身也自带金手指作弊码,但晓女没有。2.大神指出火焰纹章系列游戏的如必杀、升级、双击等随机事件主要由其中的乱数机制控制,GBA的乱数也有大神已经研究出是通过乱数表查询来获取。改变乱数的方法在各作都不一样,从论坛上以往公布的方法有的是通过查看属性页,有的是通过看路径,有的是通过斜向瞄准等等。

为达到目的我们主要解决三个问题:1.逆向乱数生成算法。2.升级点数计算方法。3.编写自动化预测升级点数。

主要工具:

1.Dolphin5.0-6304(主要是自己电脑上就是这个版本,新的应该也可以)

2.IDA/GHIDRA(静态反汇编工具,推荐GHIDRA,因为可以反编译POWERPC指令,有IDA的POWERPC插件的当我没说)

3.cheat engine 6.7 (最新的我也没用过,应该没啥区别)

4.带HEX功能的文本编辑器(UE、010都可以)

0x01前置知识

WII是任天堂2006年推出的家用游戏机,其CPU是POWERPC,因此其游戏的系统指令集也是基于POWERPC的。晓女是2007年任天堂在WII上开发的一款游戏,是火焰纹章的系列作,可以构卖WII主机和游戏进行体验,也可以在网上下载晓女的镜像,通过跨平台模拟器dolphin完美模拟,PC版模拟器可以对游戏进行实时调试,这就给我们分析这款游戏带来了方便。

0x02 内存定位

DOLPHIN在载入游戏镜像后,会申请一块内存用于模拟游戏内存,此内存有游戏的主要代码,其地址在每次游戏启动的时候随机申请,然后通过模拟机制模拟WII的系统运行,游戏内存运行的地址通过dolphin调试模式可以看到其模拟的地址始终从0x80000000开始。可以通过模拟器调试模式的内存->转储主内存把当前的主要内存保存为文件,位置在:C:\Users\你的账户名\Documents\Dolphin Emulator\Dump\ram.raw

图片1.png旧版的32位dolphin申请的模拟器地址基本是在7FFFFFFF-80000000区间,而新版取消了以往的直接申请模式,地址随机变化,因此我们首先第一步就是要把游戏的模拟内存找到,这样便于动静态分析。目前网上没有一个统一的办法可以直接找到,这里我通过多次尝试找到一个比较稳定的查找映射内存地址的办法,就是用CE的内存区域查看,寻找dolphin.exe的内存结束地址,往下的第一个大小为0x2000000的MAPEPED内存地址就是目标内存地址。

图片2.png从截图可以看到目标内存地址起始为RFEJ01正是模拟器的内存起始内容。这样我们就找到了模拟器ROM在内存中的地址。

图片3.png第二步是找到合适的调试和监控工具,dolphin用于动态跟踪代码很有用,但是有个缺点就是功能单一,断点只有位置和内存断点,可以查看内存,但是dolphin的调试界面很不友好,在游戏的时候就没法监视,我们这里使用CE来定位和监控内存变化。但是CE中的内存地址空间和dolphin不同,在查找代码的时候很不方便,这里我找了一个网上大神的插件,可以修改CE在内存中部分内存的映射地址,也就是可以把CE监控的目标程序的内存地址空间映射到指定起始内存地址。 图片4.png我们使用这个工具将前面找到的ROM内存地址重新定位为0x80000000,这样CE中的内存地址就和实际地址一致了。

图片5.png

0x03 寻找乱数地址

火焰纹章游戏里的各类操作如攻击、升级、必杀、双倍攻击等特效的触发都是由一个随机数来判定的,但是这个随机不是真随机,而是自己定义的一套随机算法,以往火纹的随机数是通过查询一张或几张乱数表,通过消耗乱数的方式来达成随机效果,网上也大概查了一下,苍炎可以通过查看人物属性改变乱数,晓女通过斜向瞄准修改。为了验证这个乱数的随机性,我通过使用Dolphin的即时存档来测试:先保存一个即时存档通过前面的乱数变化的方式,如查看几次属性页,斜向瞄准几次,通过一次攻击击杀目标获得经验值升级,检测升级加的属性点数和类型来判断这个随机数的随机性。通过多次试验,我发现同一个即时存档在执行相同操作的时候,如查看几次属性页,瞄准几次之后,升级点数和类型都是一定的,甚至必杀和双倍攻击等随机事件都是完全按照顺序发生,因此我猜测火纹的随机与时间无关,只与指定操作有关,这也为我们可以预测升级点数提供了可能。

下面就开始查找这个随机数的地址。这里我们利用CE的变化值和非变化值的监控来达到目的,首先使用CE监控dolphin进程,并按照上述方法将ROM内存找到并重定位,利用未知初始值->变化的值->不变的值->变化的值…反复查找,最后找到有如下地址是可疑的

图片6.png我们把内存定位到804A7F4C,然后切换到游戏中,用远程攻击斜向瞄准目标,可以看到从804a7f50开始的6个连续字节是固定变化的。

图片7.png

通过多次测试(瞄准、升级、攻击等可以改变乱数的操作)查看字节变化,可以确定这六个字节就是关键乱数字节。

0x04 随机字节规律分析

现在已经找到乱数字节位置,接下来我们分析下这6个字节的变化规律,通过多次瞄准打乱随机值,可以看到其变化是有规律的,有2个字节总是会在一次打乱随机值后从6个字节序列末转移到序列头,因此,可以假设这六个字节是通过计算得出。

图片8.png为验证这个想法,我把6个字节手动修改为0,再通过游戏中的操作来改变,可以看到在本关卡的同一地图中,无论之后进行任何操作,六个字节始终为0。在游戏中体现为:1.升级点数全满,2.部分人物必杀率大大提高。我修改了之后随便测了两个人物升级的情况。

图片9.png图片10.png

因此,可以判断这六个字节就是关键乱数字节,其计算方式不是通过查表,而是通过相互计算得出的!这就给我们预测乱数地址提供了可能,只要把其计算方法分析出,就可以计算出N次循环后的乱数值。由于这六个字节影响游戏中的一切随机行为(升级、攻击必杀等)。因此,苍炎/晓女只要知道了某个时刻的乱数值,可以通过算法将后面行动中所有的出现情况预测出来。当然,如果只想每个人物每次升级都全满,研究到这里就可以结束了,但我们的目标是研究正常情况下如何预测其升级点数,所以需要进一步挖掘这六个字节的生成规律。

0x05 算法分析

为找到这六个字节的算法,我们就要找到这六个字节是如何生成的,还好Dolphin提供了内存断点功能,我们把这六个字节下内存写入断点,然后根据堆栈查看调用,就很容易找到调用函数。

图片11.png

这里可以看到内存窗口中地址和CE的地址内容是一致的,但是不能在CE的内存中直接下断点,因为CE里的代码都是基于INTELCPU的汇编,而模拟器里面可以直接显示POWERPC汇编,在模拟器的内存中下断点可以直接定位到powerpc指令。

通过跟踪,可以发现乱数是以2个字节为单位,6个字节是3个单位,先通过当前乱数计算新的1单位乱数,然后将当前乱数右移1个单位,用新的乱数单位替换空缺,完成1次循环。乱数循环均与参数无关,只与当前3个单位乱数值有关。

下面就是我找到的一些功能函数,当然,我只想预测升级时候的点数,因此其他很多计算必杀相关、双击等随机事件的函数我都没去找,有兴趣的可以进一步分析。因为升级的时候大部分是在战场上,最多的时候就是击杀敌人获得经验升级。我主要找的就是这方面功能的函数,可以先下内存断点,再在游戏中执行操作,会自动定位到功能函数。

进攻的时候会出现乱数计算,有必要将进攻类型全面分析。

80084e68  攻击一次乱数增加计算函数,乱数前进2次

80084de8 平时的计算函数,乱数前进1次

80084d08 用斜线瞄准计算乱数的函数

其中为实现自动化分析,需要将乱数计算的算法逆向,可以使用GHIDRA辅助理解。这是80084de8的POWERPC代码以及伪代码。

图片12.png

根据你想用的编程语言来确定代码,为便于在下步使用CE生成辅助工具,我这里是用LUA来复现这个函数,核心代码如下:

r5 = bShr(orgr0 , 5)

r4 = bAnd(bShl(orgr4 , 1) , 0xffff)

r0 = bAnd(orgr6, 0x8000)

r6 = bShl(bAnd(orgr6,0x1ffff),0xb)

r5 = r6 + r5

r6 = bAnd(r5 , 0x0000ffff)

r5 = orgr6

if bAnd(r5 , 0x8000) == 0x8000 then

 r0 = r4 +1

 else

 r0 = r4

end

r6 = bXor(r6,r0)

NextNum = bAnd(bShr(CalcNumV,16) ,0xffffffff) + bAnd(bShl(ReverseNum(r6),32),0xffff00000000)

r0 = bAnd(r6,0xffff) * 0x64

r0 = bShr(r0 , 0x10)

r0 = bXor(r0 , PersonData[index])

--print (string.format("r0=0x%x,r6=0x%x",r0,r6))

r4 = bShr(r0 , 1)

r0 = bAnd(r0 , PersonData[index])

CalcNumV为执行函数当前的乱数值,index为当前人物升级属性索引,8个属性,升级第几个index就是几。NextNum就是经过此次循环之后的3个单位乱数值。这样就可以随时计算经过多少次循环后乱数值是多少。可以看到,苍晓的乱数的生成算法还是比较简单的,主要是通过位运算来计算的。

0x06 点数计算

解决了乱数生成的问题,最后需要解决的就是在乱数升级函数执行前,攻击行为对乱数的影响。晓女的进攻时乱数的计算与进攻对象种类、攻击方式、能否反击等因素有关,经过多次测试,攻击对象主要分为:近战、法师、弓箭手、医疗类;攻击模式主要分为以下几种类型:直线和斜线,加上对双击的判定,将这些因素进行组合,就可以覆盖火纹中大部分的攻击模式。

如果此次进攻后经验满足升级条件,就会进入升级点数计算函数,此函数调用8次80084DE8,每次调用的时候,该人物的属性成长值就作为参数进行计算,根据返回值判断是否升级此属性。

0x07 自动化计算辅助工具

理想中的完全自动化是直接HOOK Dolphin的OPENGL显示,动态显示当前的乱数值,以及根据当前选择的人物瞄准攻击的目标,然后自动计算出改变乱数循环多少次升级属性的点数,由于对Dolphin代码研究不多加上自己懒,就只通过CE的Trainer实现了一个简化版的,通过确定此次攻击之后某个人物要升级,工具预测乱数值循环次数对升级属性的影响。

其主要流程为:获取当前乱数值->选择当前攻击模式(近战1X1,还是法师远程,还是弓箭手,以及是否反击和双击)->计算N次乱数循环后的升级值。

为便于使用,我采用斜向瞄准/取消瞄准乱数循环的方法,也就是通过远程攻击斜向瞄准的方式来改变乱数,一次斜向瞄准为1次计算,也就是在辅助工具中表现为一条计算结果。有以下几点注意的地方:1.标准情况的1次计算是射程为2的兵种(法师,弓箭手、装备攻击距离为2的近战兵种都可以)以斜向的方式瞄准一次敌方不能造成反击(射程小于等于1)的目标。如下左图这种情况。如果是能够反击(射程大于等于2)的敌方目标,计算次数翻倍。如法师瞄准手斧(下右图)。X2是双倍攻击的意思,不影响斜向瞄准乱数次数。注意:如果斜向瞄准的攻击路径上有障碍物,那此斜向瞄准不会修改乱数。

图片13.png图片14.png

使用方法:先启动Dolphin正常开始模拟游戏,然后打开辅助工具,等“初始化”按钮可以点击后,点击初始化,状态变为绿色,人物和计算按钮也都可以按了,这样就可以开始使用了。

如果某个人物即将在下次攻击之后升级,先保存下状态,之后使用这个人物去攻击,记住攻击者和目标反击的状态,比如我这个例子萨扎是两次攻击,目标重甲士兵一次反击但是MISS了,就可以在辅助工具中先选择人物,再依次选择近战、能反击、直线、双击、miss5个选项,下面自动计算出需要8次循环,点击“计算”,左边的文本框就会出现循环次数与升级点数的关联,为方便使用,我以远程一次斜向瞄准为1次计算,列出了60次瞄准每次的升级情况,每个属性能升级为1,反之为0。

图片15.png可以看到如果不瞄准,应该是第4第5也就是技、速升级。斜向瞄准10次之后可以达到较大的升级点数6个,与实际符合,因此,只需要选择你想要升级的属性点情况对应的瞄准次数,用一个远程兵去瞄准/取消瞄准一个不能反击的单位多少次就可以了。我内置计算60次,如果次数较大,可以瞄准远程单位来双倍计算次数。(瞄准一次远程单位循环次数为2)。如果远程兵都行动完了无法远程瞄准,正好有个要升级怎么办?我在网上找到有人发现通过切换“指南”,观看教程的方法可以改变乱数,也可以使用这个办法,但是这种方法有个缺点就是乱数一直在变,很难精确控制,只能看一下,暂停,用工具计算当前乱数对升级的影响也就是看瞄准为0的升级点,找到一个你满意的就退出,开始攻击升级,否则继续观看。

图片16.png图片17.png再举个远程攻击的例子,吉尔用手斧远程攻击一个目标是1次攻击,因此选择近战、不能反击、斜线、不双击、不miss,单次攻击循环次数就是8,计算情况如下:

图片18.png直接攻击升级就是5个点,如果瞄准6次就可以升级7个点!

图片19.png图片20.png

升级点数以及属性与计算的一致,这样就验证了我们的算法是没有问题的。这个工具只是一个简单的辅助,对很多情况(如,攻击时是否miss的判定函数、超远程距离攻击我都没有去跟踪)并没有深入研究,有兴趣的可以继续完善。

一点小问题:用Dolphin调试的时候有个问题一直没有解决,就是没有找到WII/NGC匹配的符号,即使是简单的内存函数的符号也不知道在哪,很多基本的功能函数无法解析,这就给我们看汇编代码带来很大麻烦,希望知道的大神可以介绍一下。当然,即使这样,也可以完成算法的分析,只是要稍微耗时间一点。

结语:这只是我对火焰纹章的初步研究,其中也有对Dolphin模拟器在模拟时候的一些使用心得,有兴趣的可以相互交流。

*本文作者:minjiang2011,本文属 FreeBuf 原创奖励计划,未经许可禁止转载

# 工具 # 火焰纹章 # 战棋游戏 # 升级点数
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录