需要对游戏进行逆向分析,我们首先需要了解游戏的内存系统。在一个游戏的运行过程中,游戏中所有的变动,比如玩家的血量,敌人的血量,玩家所在位置,场景的位置,剩余时间,等等一切,都在内存中有所体现。所有的数据,都保存在内存的某个地址。
比如一个玩家血量减少的大概逻辑为:
1、当被敌人攻击,程序分析敌人的攻击力数据。
2、从玩家血量的内存地址读取玩家的当前血量,把当前血量以敌人的攻击力大小减少相对应的值,再把已减少的值写入玩家血量的内存地址。
3、玩家的血量是否为空,如果为空,进入死亡程序分支。如果不为空,进入非死亡程序分支。
在这个逻辑下,我们首先需要知道玩家的血量的内存地址。我们如何找到玩家血量的内存地址呢,MAME内自带内存搜索指令,可以方便地搜索内存数据,下面,我们尝试用MAME自带的指令来搜索到"恐龙新世纪"游戏中1号机位对应玩家的血量。
我们搜索一个内存数据的逻辑为,我们首先在一定的情况下,把内存的所有数据保存下来,比如我们当前需要搜索玩家血量:
1、我们在玩家血量为满的情况下,把所有内存的数据保存下来。
2、我们再次进入游戏,利用敌人或其它方式让玩家的血量发生变化,再次搜索之前保存下来的数据,因为血量已经发生了变化,相对于之前的数据,血量的内存数据也必定发生了变化,我们把之前的数据与已变化的数据进行对比,筛选出已经发生变化的数据。
3、因为内存里的数据量通常比较大,虽然血量发生了变化,但可能有无数的数据也同样发生了变化,所以,我们通过不断地让血量发生变化,通过多次的筛选,来一步步地减少所筛选出来地址数量,直到找到对应玩家血量的地址。
我们介绍一下MAME内存搜索功能指令:
cheatinit(ci) -初始化搜索选定的内存区域
cheatinit [[<sign>[<width>[<swap>]]],[<address>,<length>[,<space>]]]
参数1
<sign> 可以是 u(表示无符号)或 s(表示有符号),
<width> 可以是 b(表示 8 位(字节))、w(表示 16 位(字))、d(表示 32 位(双字))或 q对于 64 位(四字);
<swap> 可以是 s 来表示相反的字节顺序。 如果第一个参数被省略或为空,则使用上一次作弊搜索的数据格式,如果这是第一次作弊搜索,则使用无符号 8 位格式。
参数2
<address> 指定开始搜索的地址,
<length> 指定要搜索的内存量。 如果指定,将搜索 <address> 到 <address>+<length>-1(含)范围内的可写 RAM; 否则,将搜索地址空间中所有可写的RAM。
cheatrange(cr) -添加选定的内存区域进行作弊搜索
cheatrange <address>,<length>
参数:
<address> 指定开始搜索的地址,
<length> 指定要搜索的内存量。 <地址> 到 <地址>+<长度>-1(含)范围内的可写 RAM 将添加到要搜索的区域。
cheatnext (cn) -通过与之前的值进行比较来筛选对应的地址
cheatnextf(cn) -通过与初始化的值进行比较来筛选对应的地址
cheatnext <condition>[,<comparisonvalue>]
cheatnextf <condition>[,<comparisonvalue>]
参数1:
all 更新最后的值
equal (eq) 如果没有 <comparisonvalue>,则搜索与之前搜索相同的值; 使用 <comparisonvalue>,搜索等于 <comparisonvalue> 的值。
notequal (ne) 如果没有<comparisonvalue>,则搜索不等于之前搜索的值; 使用 <comparisonvalue>,搜索不等于 <comparisonvalue> 的值。
decrease (de, -) 如果没有<comparisonvalue>,则搜索自上次搜索以来减少的值; 使用 <comparisonvalue>,搜索自上次搜索以来减少了 <comparisonvalue> 的值。
increase (in, +) 如果没有<comparisonvalue>,则搜索自上次搜索以来增加的值; 使用 <comparisonvalue>,搜索自上次搜索以来增加了 <comparisonvalue> 的值。
decreaseorequal (deeq) 搜索自上次搜索以来已减少或未更改的值(不使用 <comparisonvalue>)。
increaseorequal (ineq) 搜索自上次搜索后增加或不变的值(不使用 <comparisonvalue>)。
smallerof (lt, <) 搜索小于 <comparisonvalue> 的值(<comparisonvalue> 是必需的)。
greaterof (gt, >) 搜索大于 <comparisonvalue> 的值(<comparisonvalue> 是必需的)。
changedby (ch, ~) 搜索自上次搜索以来已按 <comparisonvalue> 更改的值(<comparisonvalue> 是必需的)。
cheatlist(cl) -显示已搜索相匹配的列表,或将它们保存到文件中
cheatlist [<filename>]
cheatundo(cu) -撤消最后一次作弊搜索(仅限状态)
cheatundo
我们进入游戏来实际测试一下:
首先,我们进入游戏,最好的方式是保持满血,场景内保留一个敌人,场景内保留一个回血的道具,在"恐龙新世纪"中,比较好的地方为第一个场景进入门后会有一个回血道具,我们清理完场景,保留一个敌人和一个回血道具,在此时,我们保留一个存档:
此时,我们按键盘的"~"键进入DEBUG调试器,在命令框中输入:
ci
按F12回到游戏,让玩家的血量发生变化(被敌人攻击,或使用扣血技能,或拾取回血道具),发生了变化后,按"~"再次进入DEBUG调试器,在命令框中输入:
cn -
//如果血量减少时使用减号"-"参数;
//如果血量增加使用加号"+"参数;
//如果血量不变使用"eq"参数;
这里提示,对比筛选出59个对应的地址。
再次按F12回到游戏,让玩家的血量发生变化(被敌人攻击,或使用扣血技能,或拾取回血道具),发生了变化后,按"~"再次进入DEBUG调试器,在命令框中输入:
cn -
//如果血量减少时使用减号"-"参数;
//如果血量增加使用加号"+"参数;
//如果血量不变使用"eq"参数;
尽量多次循环以上步骤,直到得到的地址足够少,最好可以得到唯一地址。
如果未能得到唯一地址,可以使用命令:
cl
列出已经得到的地址列表,尝试一个个测试得到的地址。
我们现在得到了唯一的地址:FFB2E1
我们在调试器中,新建一个内存窗口:
输入我们得到的地址:
在68000中,我们最好使用偶数地址,比如FFB2E1,我们选择偶数地址:FFB2E0
我们可以看到,当前FFB2E1地址的值为3D,这是一个16进制值。也就是说,当前玩家的血量对应的值为3D,我们可以尝试在内存窗口把该值进得更改,鼠标选中该值,在键盘输入你想需要的值,如50:
回到游戏,我们会发现,血条并未发生变化,因为血量发生变化后,游戏的显示并未发生变化,这其中的逻辑为,当血量发生变化后,会调用血条显示的程序。但这时通过其它方法去更改玩家的血量时,程序并不会进入血条显示的程序。所以,在显示上,血条并未发生变化。我们尝试再次在游戏中让血量发生变化(被敌人攻击,或使用扣血技能,或拾取回血道具),才可以测试出,我们对血量的改动是否成功。
以上就是对于MAME搜索内存的介绍,几乎所有逆向工程的开始都基于对于内存的搜索,所以,在之后对于游戏的所有逆向研究,几乎都会用到内存搜索功能。建议对该功能更加详尽地了解。