在之前我们已经测试过,在"恐龙新世纪"这个游戏中,我们在使用扣血技能时,之所以可以实现减少血量,是利用sub指令来减少血量所对应的内存的值。我们已经测试,当把sub指令修改为空指令nop后,因为被改变了sub指令就失去作用。我们可以猜测,当玩家受到攻击时的扣血,原理,应该与使用扣血技能的扣血相似,本章,我们来测试如何才能实现被敌人攻击后不扣除血量:
我们已经通过MAME的搜索功能找到了保存玩家血量的地址为:FFB2E1
我们和之前的测试一样,使用内存访问监视点指令wp对该地址进行监视:
wp ffb2e1,1,w
设置好中断后,如果还保留有之前的存档,我们读取这个存档:
当敌人对玩家进行攻击时,会中断程序运行,我们可以看到,和之前一样,同样是sub指令对血量的内存进行了操作:
我们可以看到,这句sub指令和之前的一样,占用了4个字节空间,所以,因为每个nop指令是占用2个字节空间,要让这句sub指令完全失去做用,我们需要用两个nop指令来替换这句指令。
我们看到这句指令所在的地址为:116D2,我们可以用指令改变他们的机器码:
rd@116d2 = 4e714e71
同时,我们把之前的中断删除
wpclear
这时,我们回到游戏,可以发现,我们再次被敌人攻击时,不会再扣除血量。
那么,经过这个测试,我们又可以猜测,当我们拾取回血道具时,用来增加血量的指令是否为和sub相反的add指令呢,我们同样可以测试这个情况,我们硬重启一下游戏,默认硬重启为shift+F3。我们需要在测试之前让自已的血量有一定的消耗。在拾取回血道具之前,我们输入中断指令:
wp ffb2e1,1,w
回到游戏,我们拾取回血道具后,程序出现中断:
我们发现,和我们猜测的不同,这里是用move指令来把d0的数据传递到血量的内存地址的。
那么,这个D0的数据是从何而来的呢,我们需要知道程序的运行历史,才能了解程序是如何给人物回复血量的,这里我们用一个指令来查看程序的运行历史:
hi
这时,我们可以看到程序的运行历史,我们给这段程序从下到上作一个注释,来猜测这段程序的运行逻辑:
028AA4: move.w ($26,A6), D0 //从($26,a6)读取数值传到D0
028AA8: subq.w #1, D0 //d0 = d0 - $1
028AAA: add.w D0, D0 //d0 = d0 + d0
028AAC: add.w D0, D0 //d0 = d0 + d0
028AAE: lea ($60,PC) ; ($28b10), A1 //把地址$28b10传到a1寄存器
028AB2: adda.w D0, A1 //把d0和a1寄存器的值相加
028AB4: movea.w ($70,A6), A0 //我们已经知道($6c,a0)指向血量内存,我们可以猜测这里是读取人物对像的基地址
028AB8: cmpi.w #$64, ($6c,A0) //应该是用来检测血量是否为满
028ABE: beq $28af2
028AC2: move.w (A1), D0 //从(A1)读取数值,传到D0,这里我们可以猜测(a1)保存的就是该道具的回血值
028AC4: add.w ($6c,A0), D0 //我们已经知道($6c,a0)指向血量内存,这里应该是把之前的血量与道具的回血值相加。
028AC8: cmpi.w #$64, D0 //把$64和d0进行对比,作用很可以是用来确定血量是否为满血
028ACC: bls $28ad4
028AD4: move.w D0, ($6c,A0) //把d0的数据传到血量的内存,这时我们可以确定($6c,a0)为指向血量的内存
028AD8: move.w A6, -(A7)
通过注释,我们可以猜测,在A1寄存器所指向的地址应该为该回血道具所能回复的血量值。我们查看一下A1寄存器的值。
我们新建一个内存窗口,查看一下这个地址:
我们可以改动这个数值,来测试这里存储的是否为该回血道具所能回复的血量值,我们猜测该回血道具的回复量为$20,那么,我们把该值改为区别较大的数值,比如,改为$01,如果我们的猜测正确,我们再次拾取时,会发现只能回复$01点血量:
rw@28b2c=01
我们再次读取存档,先查看当前的血量值,测试再次拾取道具:
当前血量值为1e,如果我们的猜测正确,我们再次拾取时,血量应该为$1e+$1 = $1f:
这时,我们会发现,拾取后,血量和我们所猜测的一样,只回复了$01点。
我们回到这段程序的运行历史:
028AA4: move.w ($26,A6), D0 //从($26,a6)读取数值传到D0
028AA8: subq.w #1, D0 //d0 = d0 - $1
028AAA: add.w D0, D0 //d0 = d0 + d0
028AAC: add.w D0, D0 //d0 = d0 + d0
028AAE: lea ($60,PC) ; ($28b10), A1 //把地址$28b10传到a1寄存器
028AB2: adda.w D0, A1 //把d0和a1寄存器的值相加
028AB4: movea.w ($70,A6), A0 //我们已经知道($6c,a0)指向血量内存,我们可以猜测这里是读取人物对像的基地址
028AB8: cmpi.w #$64, ($6c,A0) //应该是用来检测血量是否为满
028ABE: beq $28af2
028AC2: move.w (A1), D0 //从(A1)读取数值,传到D0,这里我们可以猜测(a1)保存的就是该道具的回血值
028AC4: add.w ($6c,A0), D0 //我们已经知道($6c,a0)指向血量内存,这里应该是把之前的血量与道具的回血值相加。
028AC8: cmpi.w #$64, D0 //把$64和d0进行对比,作用很可以是用来确定血量是否为满血
028ACC: bls $28ad4
028AD4: move.w D0, ($6c,A0) //把d0的数据传到血量的内存,这时我们可以确定($6c,a0)为指向血量的内存
028AD8: move.w A6, -(A7)
我们从最后一行向上去理解这段程序的运行逻辑,我们从最后一句开始:
028AD4: move.w D0, ($6c,A0)
我们知道move指令把d0的数据传到($6c,a0),那么,我们就可以确定,($6c,a0)是指向血量的,而d0的数据就是之前的血量值和道具回复的血量值相加得出的数值。
我们向上分析
028AC8: cmpi.w #$64, D0 //把$64和d0进行对比,作用很可以是用来确定血量是否为满血 028ACC: bls $28ad4
这两句因为并未出现数据的读写,可以暂时不进行分析
028AC4: add.w ($6c,A0), D0
经过我们之前的猜测,那么这句指令就是之前的血量值和道具回复的血量值相加得出的数值。
028AC2: move.w (A1), D0
因为后面的指令会把之前的血量值和道具回复的血量值相加,那么,我们可以猜测到这里读取到的D0就是该道具可以回复的血量值。
至此,我们通过一个血量值的地址,一步步就分析出了一个回血道具的血量回复值所在的地址。在之后的逆向研究中,基本都是通过这样的方法来查看程序的运行逻辑,一步步分析,得到更多的分析数据。