自从我上一篇文章以来,令我感到非常惊讶的是,您对此感兴趣。 我决定对其结果进行补充,该游戏是“ Contra(J)[T + Rus_Chronix]”游戏的黑手版本,具有一些功能,同时在NES上显示“代码注入”。 这次,我将让玩家使用抽水的Spreadgun开始游戏,要使其进入游戏,您需要选择图标“ S”,然后选择“ R”。

所有感兴趣的猫都欢迎。
传统上:
而且我们传统上计划采取行动的顺序。
- 查找地址
- 找出抽扩散枪的价值
- 在游戏开始时找出他在这些地址上写的内容
- 改写ROM
- Easyway-将基本武器的价值更改为抽水式
- 困难-如果简单方法失败,请使用完整代码注入
- 将结果保存到新文件
为了搜索地址,我们使用前面描述的方法,但是请记住,生活地点在相邻地址中,我们只会从第一位玩家那里寻找武器,希望第二位玩家会在附近。 在第一个级别开始后,打开“ Ram watch”窗口,我们在寻找未知值。 我相信基本武器设置为0,但我不确定。
我们沿着关卡奔跑而没有前进,向各个方向射击并清除变化的值。 武器尚未改变。
让我们使用另一个窗口选项,或者在字段“ Compare To / By”中突出显示射频“ Number of Changes”,将其放在字段0中。比较的类型当然是“等于”。 武器仍然没有改变。
因此,从选项“等于上一个值”跳转到选项“更改数量为0”,您可以访问大约10,000个地址。 当进一步的筛选会略微减少或根本不减少地址列表时,人们可以前进得足够远,以击倒第一个武器。
拾取后,我们立即使用搜索方法“不等于先前的值”,并通过搜索“更改次数为1”来进一步减少列表,武器仅更改了一次。
在我们拿起第一把武器的地方,还出现了一个武器放大器。 选择并拾取它后,您可以将地址数量减少到1,但是如果不能解决,只需杀死角色并再次更改武器即可。 (每次更改后都不要忘记搜索)。
使用武器放大器时,存在一些风险,即它不会更改武器本身的值,而是会更改内存中其他位置的标志,但我们希望游戏的作者能够保存内存和说明。 而且,我很幸运,奖金“ R”改变了武器的价值,地址在AA 16坐标处。 (我可能是错的,但是我在不同版本的《魂斗罗》游戏中反复监视了英雄的武器,就像这个地址到处都是AA 16一样 )。
我还注意到奖金“ R”将地址中的值增加了16 10或10 16 ,即,将十六进制数字的第一位增加了1。
在“ Ram watch”窗口中重新启动之后,可以看到基本值为00 00,奖金“ M”将第二个数字增加了1,奖金“ R”则将第一个数字增加了。
您可以购买一个Spreadgan,它肯定会在此级别满足要求,或者您可以更改地址的值,以查看它们生成的数字,武器。 根据经验,我发现01 16是“机枪”,02 16是“火”,03 16是“散弹枪”,04 16是“激光”。 当您在第二个数字中输入其他值时,会出现各种故障。
在重置游戏并输入值1x 16之后(其中“ x”是任何可接受的选项),然后再选择“快速”奖励,您会发现重新选择奖励不会改变任何内容。
现在您可以重新启动游戏,启动游戏两次,然后尝试更改与AA 16相邻的地址。 (其中有两个,搜索时间不会很长。)在射击了第二名玩家之后,我很快发现第二名玩家的武器确实存放在AB 16号附近。 现在我们知道了我们感兴趣的地址以及应该在其中添加的值,是时候找出他在这些地址中写的内容了。
在此地址的记录上抛出一个断点,我发现记录在那里发生了几次,其中一个在启动屏幕之后。 以下代码进行输入:
住址 | 操作码 | 助记符 | 争论 | 一 | X |
---|
C307 | A2 28 | LDX | #$ 28 | ?? | ?? |
C309 | A9 00 | Lda | #$ 00 | ?? | 28或29或...或F0 |
C30B | 95 00 | STA | $ 00,X | 00 | 28或29或...或F0 |
C30d | E8 | x | | 00 | 28或29或...或F0 |
C30e | E0 F0 | CPX | #$ F0 | 00 | 29或30或...或F0 |
C310 | D0 F9 | 贝内 | $ C30B | 00 | 29或30或...或F0 |
如果您仔细阅读游戏,此处的地址范围将从0028 16到00F0 16 ,这显然是我们感兴趣的两个地址范围。 因此,没有简单的方法。 我将不得不使用“代码注入”和我所看到的最简单的解决方案来找出从这里得到的地址,将执行重定向到内存中没有代码和数据的某个位置,在此处写入我的循环版本,该地址占据整个范围,除了地址00AA 16和00AB 16并返回回车执行回来。 顺便说一句,这是注射的最经典版本。 您还可以假设我们是从JSR(跳转到SubRoutine)指令获得的,这很容易通过堆栈进行检查。
堆栈如何工作?在6502处理器中,对于基于该处理器的所有计算机,堆栈始终位于地址范围0100 16-01FF 16中,并且从较大的地址增长到较小的地址。 有一个指向栈顶的单独寄存器,最初它等于FF 16,因为更高有效字节永不改变。 寄存器本身始终指示未占用有用数据的最高字节。
仿真器调试器不显示“堆栈指针”寄存器的值,而是显示该寄存器所指向的地址,现在是01F2 16 ,简单的计算表明堆栈上的最后一个数据是C3 16和C2 16 ,这可能导致我考虑的是地址C3C2 16,但是6502是“ Little Endian”类型的处理器,因此,当执行指令并将地址存储在内存或堆栈中时,低位有效字节将首先写入。 如果该地址确实位于堆栈的顶部,则为地址C2C3 16 。 而且,这是JSR指令的最后一个参数的地址,如果它还是一个地址的话。 检查非常容易,只需查看在地址C2C3 16上方两个字节写入的内容。
住址 | 操作码 | 助记符 | 争论 |
---|
C2C1 | 20 07 C3 | s | $ C307 |
如您所见,这是C307 16的JSR指令,这意味着子例程的假设是正确的。
现在,您需要正确编写注入代码,为其找到合适的位置,将其写入该位置,并将JSR指令重定向到该注入。
为此,使用记事本非常方便,我拥有Visual Studio Code 。 每个人都有自己的写作风格,我个人是第一个编写带有地址的JSR指令的人,以便知道在哪里更改以及完整的操作码,以便知道要更改的内容。
经过几次缩进后,我复制了循环代码,它可以在没有地址的情况下使用,但是查看带有操作码以外参数的助记符非常有用,并且抓住该循环之后的指令以及地址以了解从注入返回的位置非常有用。
C2C1:20 07 C3 JSR $C307 A2 28 LDX #$28 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 F0 CPX #$F0 D0 F9 BNE $C30B C312:A2 07 LDX #$07
您可以在下面的几个缩进中编写注入本身的代码,实际上,它是循环本身的重复,并带有一些附加内容。
C312:A2 07 LDX #$07 A2 28 LDX #$28 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 AA CPX #$AA D0 F9 BNE -7 A9 13 LDA #$13 95 00 STA $00,X E8 INX E0 AC CPX #$AC D0 F9 BNE -7 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 F0 CPX #$F0 D0 F9 BNE -7
为了清楚起见,我指出了缩进而不是跳转位置地址。
如果您无法阅读此代码这是相同的周期,但分为三个部分,在第一个周期中,我们将寄存器X与值A 16进行比较,因为我们需要将所有地址清零到地址00AA 16 ,然后将13 16放入寄存器A中,然后将抽运的电子枪的值写入第二个周期它将其发送到地址00AC 16,从该地址起,应再次将范围的其余部分清零。 我们返回到寄存器A零,并将范围的其余部分归零。
必须在最后完成注射返回指令。
E0 F0 CPX #$F0 D0 F9 BNE -7 4C 12 C3 JMP $C312
现在,为了方便起见,我更喜欢在文件中编写以下操作码。
4C 12 C3 JMP $C312 A2 28 A9 00 95 00 E8 E0 AA D0 F9 A9 13 95 00 E8 E0 AC D0 F9 A9 00 95 00 E8 E0 F0 D0 F9 4C 12 C3
计算完操作码后,很容易发现其中有32个。 因此,您需要在ROM上找到20 16个未占用的地址。 通常,未使用的地址是具有相同值的大空间,通常是零或FF16 。这是一个很大的块,需要找到,它必须至少有35 个10个地址,以便有一定的余量。
在“十六进制编辑器”中,我在B29E 16 -BFFF 16中找到了这样一个范围。 使用此类免费站点的开头进行注入可能很危险,因此建议您在末尾编写注入代码。 开始注入最方便的地址是BFE0 16 ,但这是控制台内存中的地址,以找出它在ROM文件中的位置,右键单击它,然后选择“在ROM文件中转到此处”。
现在,您可以在此处复制粘贴整个操作码(32个值)。 最终触摸更改说明
C2C1:20 07 C3 JSR $C307
跳转到我拥有的注入地址是BFE0 16 。
C2C1:20 E0 BF JSR $BFE0
当然,在ROM上找到真正的指令位置。
不专心的专业人士的答案。JSR指令的地址到达堆栈,因此,无论跳转的位置如何,都将存在相同的返回地址,并且从注入开始,我们将使用不影响堆栈的JMP指令返回代码。 因此,RTS指令在没有注入的地方工作,并返回到没有注入的地方。 结果,该注入不会破坏堆栈。
PS永远来说,您仍然需要确保角色死后能够以大量传播的方式重生,但是我敢肯定,有了所掌握的知识,您就可以自己解决这个问题。 我将目光转向其他事物。 以Unity引擎为例。