Skip to content

Study on a bug that happens in Crash Bash and allows a character to get hit two consecutive times even if it should be invulnerable.

License

Notifications You must be signed in to change notification settings

retrohacking/Crash-Bash-Invulnerability-Bug---Reverse-Engineering

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Crash Bash Invulnerability Bug Fix

Retro


Context

This project is born to fix a bug in Crash Bash that caused a character to receive two consecutive hits when hit by a physical attack while holding a box. In particular the character would first get hit by the physical attack and successively by the box it was holding, when it crashes to the ground. An example of the bug is shown at reference [4].

Process and commands

Understanding and exploring the .bin file

When I download or dump the ROM, I have the .bin and .cue files. By using binwalk on the .bin file I can see that it contains MIPS executable code, so I understand PS1 games use MIPS instructions set.

binwalk -Y   crashbash.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2317          0x90D           MIPS executable code, 32/64-bit, little endian, at least 527 valid instruct'

I try to extract the content of the bin file and I obtain a .iso file:

binwalk  -e crashbash.bin
file _crashbash.bin.extracted/1318.iso
_crashbash.bin.extracted/1318.iso: ISO 9660 CD-ROM filesystem data 'SCES_02834'

and by executing the file command on it i see that it contains an ISO 9660 CD-ROM filesystem data related to 'SCES_02834' that is, indeed, Crash Bash (PAL version) serial number.

ISO 9660 CD-ROM filesystem data

The .iso file (and the .bin too) results to be corrupted and cannot be opened with the standard Windows explorer. By using IsoBuster I've been able to see the structure of the disk and extract many files. Some sections result to be corrupted though, and I have ignored them,and these corrupted section seem to be related to Spyro3 Demo files and in particular to SPEECH.STR file.

So on IsoBuster documentation[1] I look at the different extraction modes and the correct one seems to be Extract RAW, that luckily seem to only work on CDs, and is recommended for engineering puposes.

So I extract 2 copies: one for RAW and the other for User understandable data (it is important to notice though, that all the work and modifications have been done on the user understandable copy); the structure of the extracted folders is the same for both, but the actual content will be different due to the uninteligible data not being discarded in RAW extraction:

The folder CRASHBSH contains:

  • CRASHBSH.DAT

And the folder SPYRO3:

  • SPEECH.STR

  • SPYRO3.EXE

  • WAD.WAD

retro@retro:/mnt/c/Users/lmong/Desktop/Progetti/Ricerca/PS1_games_reverse_engineering/crash_bash/SCES_02834$ file * */*
BASHY:                 data
SCES_028.34:           Sony Playstation executable PC=0x8002e4bc, .text=[0x80010000,0x6a800], Stack=0x801ffff0, (North America area)
SYSTEM.CNF:            ASCII text, with CRLF line terminators
CRASHBSH/CRASHBSH.DAT: data
SPYRO3/SPEECH.STR:     empty
SPYRO3/SPYRO3.EXE:     Sony Playstation executable PC=0x8005af6c, .text=[0x80010000,0x5c000], Stack=0x801ffff0, (North America area)
SPYRO3/WAD.WAD:        xBase index, root pointer 0x800, free node pointer 0x20000, reserved counter 0x20800, key length 0, index options (0), at 16 reserved 0x20800, at 492 reserved 0xf6d80000000000## REFERENCES

Executing the file command on the files in the RAW folders will only return data as result. On the contrary, as said before, in the human readable extracted folder, the file

SPEECH.STR is shown as empty.

I can suppose that CRASHBSH/CRASHBSH.DAT may contain textures and audio track, but these may be compressed. BASHY instead is totally unclear to me: May it contain functions or properties of the objects? or else vice versa?

I can say that the first option is the best because by running strings on the two files I get many known in game strings on CRASHBSH/CRASHBSH.DAT while I don't get anything more than "HpSt." on BASHY. Moreover running binwalk on the first file returns the following:

 retro@retro:/mnt/c/Users/lmong/Desktop/Progetti/Ricerca/PS1_games_reverse_engineering/crash_bash$ binwalk -B SCES_02834_RAW/CRASHBSH/CRASHBSH.DAT

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------

221196        0x3600C         mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
4738508       0x484DCC        LZMA compressed data, properties: 0xBE, dictionary size: 16777216 bytes, uncompressed size: 32 bytes
4738704       0x484E90        LZMA compressed data, properties: 0xBE, dictionary size: 50331648 bytes, uncompressed size: 32 bytes
8126692       0x7C00E4        LZMA compressed data, properties: 0x5A, dictionary size: 16777216 bytes, uncompressed size: 32 bytes
10055787      0x99706B        StuffIt Deluxe Segment (data): ffffffffffffffV5""""""RV#""""""2effffffffffffV#""""""2ef6#""""""2effffffffffV#""""""2cfffV#"""""2Sffffffffff5#"""""2efffff:"""""
10643616      0xA268A0        LZMA compressed data, properties: 0x65, dictionary size: 134217728 bytes, uncompressed size: 32 bytes
10837000      0xA55C08        MySQL ISAM index file Version 1
11901944      0xB59BF8        LZMA compressed data, properties: 0x66, dictionary size: 201326592 bytes, uncompressed size: 32 bytes
13074392      0xC77FD8        LZMA compressed data, properties: 0x66, dictionary size: 67108864 bytes, uncompressed size: 32 bytes
14047230      0xD657FE        PGP RSA encrypted session key - keyid: 79E6 4E010C03 RSA (Encrypt or Sign) 8129b
14047278      0xD6582E        PGP RSA encrypted session key - keyid: 70ED 5050C03 RSA (Encrypt or Sign) 8129b
14049246      0xD65FDE        PGP RSA encrypted session key - keyid: 79E6 4E010C03 RSA (Encrypt or Sign) 8129b
14049294      0xD6600E        PGP RSA encrypted session key - keyid: CFE8 47060C03 RSA (Encrypt or Sign) 8129b
14476699      0xDCE59B        MySQL MISAM index file Version 1
14837502      0xE266FE        MySQL MISAM compressed data file Version 2
14947794      0xE415D2        MySQL ISAM compressed data file Version 9
14948719      0xE4196F        MySQL MISAM index file Version 7
14956740      0xE438C4        MySQL ISAM index file Version 9
14975644      0xE4829C        MySQL MISAM index file Version 3
14975824      0xE48350        MySQL MISAM index file Version 3
14985090      0xE4A782        MySQL MISAM compressed data file Version 5
15872295      0xF23127        LZMA compressed data, properties: 0x5C, dictionary size: 0 bytes, uncompressed size: 1024 bytes
17150848      0x105B380       LANCOM WWAN firmware
21993837      0x14F996D       MySQL MISAM index file Version 3
30073769      0x1CAE3A9       MySQL MISAM index file Version 1
34641353      0x21095C9       MySQL MISAM index file Version 1
38952086      0x2525C96       MySQL MISAM index file Version 3
44649290      0x2A94B4A       MySQL MISAM index file Version 1
70901968      0x439E0D0       MySQL ISAM index file Version 3
80492161      0x4CC3681       StuffIt Deluxe Segment (data): fG5
81490733      0x4DB732D       Intel x86 or x64 microcode, pf_mask 0x3311fe04, 1FE2-10-10, rev 0x419c6210, size 32510705
82209841      0x4E66C31       StuffIt Deluxe Segment (data): fG5
83289580      0x4F6E5EC       StuffIt Deluxe Segment (data): ff#
83722348      0x4FD806C       StuffIt Deluxe Segment (data): ff#
84025756      0x502219C       StuffIt Deluxe Segment (data): ff#
84639628      0x50B7F8C       StuffIt Deluxe Segment (data): ff#
84898348      0x50F722C       StuffIt Deluxe Segment (data): ff#
85748659      0x51C6BB3       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
86021239      0x5209477       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
86586035      0x52932B3       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
87331455      0x534927F       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
87579913      0x5385D09       Toshiba SSD Firmware Update
88012407      0x53EF677       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
88298116      0x5435284       StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
88658199      0x548D117       mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
89835196      0x55AC6BC       StuffIt Deluxe Segment (data): ff#
90093916      0x55EB95C       StuffIt Deluxe Segment (data): ff#
91157345      0x56EF361       MySQL MISAM index file Version 1
91435297      0x5733121       MySQL MISAM index file Version 1
91626097      0x5761A71       MySQL MISAM index file Version 1
91815985      0x5790031       MySQL MISAM index file Version 1
92015905      0x57C0D21       MySQL MISAM index file Version 1
92221153      0x57F2EE1       MySQL MISAM index file Version 1
92992200      0x58AF2C8       MySQL MISAM index file Version 1
92992280      0x58AF318       MySQL MISAM index file Version 1
93969647      0x599DCEF       MySQL MISAM index file Version 1
94264628      0x59E5D34       MySQL MISAM index file Version 1
94264708      0x59E5D84       MySQL MISAM index file Version 1
95305948      0x5AE40DC       StuffIt Deluxe Segment (data): ff#
95658748      0x5B3A2FC       StuffIt Deluxe Segment (data): ff#
96804172      0x5C51D4C       StuffIt Deluxe Segment (data): ff#
97307500      0x5CCCB6C       StuffIt Deluxe Segment (data): ff#
97994284      0x5D7462C       StuffIt Deluxe Segment (data): ff#
98370604      0x5DD042C       StuffIt Deluxe Segment (data): ff#
98699884      0x5E20A6C       StuffIt Deluxe Segment (data): ff#
100527123     0x5FDEC13       Intel x86 or x64 microcode, sig 0x2cfc8e00, pf_mask 0x1022100, 1E00-01-01, rev 0x4fc8e00, size 139521
105852888     0x64F2FD8       StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
106334713     0x65689F9       Toshiba SSD Firmware Update
106544304     0x659BCB0       StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
108309798     0x674AD26       Sega MegaDrive/Genesis raw ROM dump, Name: "NY COMPUTER ENTE", "I MATSUSHITA",
108999292     0x67F327C       StuffIt Deluxe Segment (data): ff#
110514356     0x69650B4       Intel x86 or x64 microcode, pf_mask 0x229f2220, 2000-02-22, size 2048
114900714     0x6D93EEA       MySQL MISAM compressed data file Version 1
115590806     0x6E3C696       MySQL ISAM compressed data file Version 3
117642960     0x70316D0       MySQL MISAM index file Version 1
117642966     0x70316D6       MySQL MISAM index file Version 1
118481406     0x70FE1FE       MySQL ISAM index file Version 1
121258348     0x73A416C       MySQL ISAM index file Version 1

It may not seem a realistic result but something gave me an important detail is the line:

108309798 0x674AD26 Sega MegaDrive/Genesis raw ROM dump, Name: "NY COMPUTER ENTE", "I MATSUSHITA"

since for Crash Bash and Crash Team Racing, Sony Computer Entertainment made a deal with Universal Interactive Studios that at that time was an asset of a japanese company, MATSUSHITA (Today known as Panasonic). All the other files seem to be compressed though, and it is not granted that all the results are affordable.

This does not seem to be the right way to follow since the file are corrupted or may be compressed.

Definitely:

SYSTEM.CNF contains game boot information

SCES_028.34 is the main executable of the game with Program Counter=0x8002e4bc (address of the start function), text section .text=[0x80010000,0x6a800] and Stack Pointer at Stack=0x801ffff0

CRASHBSH/CRASHBSH.DAT may be a file containing assets that may be compressed, and in that case it is necessary to find the compression algorithm in the main executable

BASHY still unknown file

Static & Dynamic Analysis

Ghidra Setup

To first load, disassemble and try to decompile the game I've used Ghidra by installing the extension psx-ldr[2]. In particulare to grant the compatibility with the extension I've had to download the precise version PUBLIC 10.4 of both Ghidra and the extension. I just had to install the extension by importing the zip and not its content.

After first making a mistake by loading the entire .bin file, that was just a little distraction, I've first tried to load the file SCES_028.34 contained in the folder extracted in a RAW form. I've noticed that it returned many errors, so there probably were many bytes out of place.

Finally I've tried to open the same file but taken from the folder which contained the files converted to "user understandable" files and obtained "understandable" disassembled MIPS code and a way more clear decompiled code and had a confirm that the PC=8002e4bc is the address of the first function, start.

Finding the addresses with DuckStation

Then it was time for the experiments. I wanted an emulator to execute the game and analyze the memory to search for some values. The addresses that I was looking for are player 1's life bar and the invincibility. For this purpose I've decided to use DuckStation: epsxe didn't work at all at the last version, while the previous ones didn't load the BIOS as requested.

DuckStation provides a memory analyser with which I've been able to understant that the life bar ranges between 0 (minimum) and 20 (maximum), while the invulnerability has two addresses, one is the invulnerability state flag and the other is the timer.

The timer ranges between 0x28-0 (ms?) and the flags are:

  • 0: normal state

  • 2: blinking character and invincible

Following, the addresses I've found:

The first is the life bar, the second the number of won matches, the third the invincibility timer, and finally the invincibility flag.

The addresses don't seem to be RANDOM

Another set of addressess is the following, defining if the character is carrying a box or not (all values will be 0)

The only fixed addresses are 9ba8e and 9ba8f

Finding the function that sets the invulnerability

At this point it may seem trivial to find the function or the instruction that sets the value 2 at the address 0x0009baaf. That's wrong.

At first I've tried with No$psx: I've set the breakpoint as follows [0009baaf]! so that the execution could stop the execution in the moment in which the value stored at that specific byte would have changed. But it didn't work.

So I've changed the emulator to pcsx-redux and did the same, by setting the breakpoint for that address for any write. But once the match was loaded I've noticed that the breakpoint was hit continuously. So I could suppose that that address is part of a larger section of memory that is continuously read and written and traces the values of the battle. BUT this implies that the value 0x2 is being saved somewhere else before being written in this specific address (stack or registry?)

At this point it seems that I must proceed statically, but analysing all the code is undoable. So I will search instructions that move or add the value 2 to a register or a memory address. I need to filter out as many instructions as possible. I can do it with a regexp so that I can filter out all the operations that work on the stack pointer:

/(mov[a-z]*|add[a-z]*)\s(((?!sp).)*),(((?!sp).)*),0x2$/gm

and if necessary I can only work in the range of addresses of instruction executed by debugger during a single cycle.

Plot Twist: Using the timer address as breakpoint

Plot twist: I forgot about THE SECOND ADDRESS.

  • 0x0009baaf: can be set on 0 or 2, if you're invincible or not

  • 0x0009baac: this sets a timer

But while the first one seems to be continuously written even if the state of the character does not change, the second one is only reached and written when the character is hit and when the timer decreases.

These are the points in which the breakpoint is hit:

  1. 800854d0: the caracter is being hit:

  2. 8008558c: The character has already been hit, and is on the ground and the timer seems to be set to 0x28=40 (it is correct indeed, it is set to 0x28 at 80085554)

  3. 800856f4: the character is still on the ground and the timer is started and cyclically (notice the addiu $v0, -0x0001)

IN ORDER TO GET A CONFIRMATION: I've tried both the NTSC-U and NTSC-J versions to exclude that the reason for the "bug" could be the FPS difference from the PAL version and the bug happens on all the versions. The doubt rose in this moment because it could have been possible that the counter had different TIME durations based on the execution speed on the different versions, so the 0x28 didn't represent the exact time duration in the PAL and NTSCs. But at this moment I can completely exclude that the bug depends from this. I can confirm this thanks to the emulators that actually execute the PAL game at 50 FPS and the NTSC at 60 FPS.

Now, if I try to search those addresses in Ghidra I get a disappointing result:

In fact, we are in the RAM section and it is not possible to statically know what the content of these section will be.

Two alternatives

  1. PCSX-REDUX should give the possibility to export the memory at runtime, so I can try to do that and load it on ghidra again?

  2. Search for any function that loads something in one of the RAM address that I've found before.

The first option allows to find the function faster, just by loading the game, dumping the memory and finally studying the three functions.

The second one requires some extra steps:

  1. using the address of one of the three commands and write breakpoint to check when the istruction is written to a specific address.

  2. When the addess is written check for the command that has been executed and find where the function is actually stored.

The pro of the second metodology is that it is possible to understand and modify the game definitively while the first only allows to understand the function, avoiding the extra steps to find the storage of the function.

For completeness I'll proceed using the longer method (the second).

OR (many years have passed...) I can just search the particular instruction I've found at those addresses on ghidra.

The possible methods to search the instruction is by writing them literally or by searching their opcodes. Remember that it is necessary to search the opcodes in little endian on GHIDRA, so using the least relevant byte first (search the opcode found on pcsx-redux debugger, but reversed)

I need to load the CRASHBSH.DAT file because it could be there. -> There is nothing that is immediately recognizable.

Using an Hex Editor

I've finally opened the file in Hexedit[3] and I've searched for the opcode of the operation that sets the invulnerability timer to 0x28 (24020028) but in little endian so 28000224 and it hasn't found anything. So I've opened the file CRASHBSH/CRASHBSH.DAT and searched it there. The next step has been adding the last byte of the next opcode, to find two consequent operations, so 2800022404 (from the successive opcode 8e820004). And finally the magic has happened:

Have you noticed anything?

Right, the opcodes belong to the function related to the setting of the invulnerability timer.

Now we have:

  • The RAM address of the interesting functions: 0x80085554 (we take as example li $v0, 0x0028$)

  • The address in CRASHBSH.DAT of the same instruction: 0x068e58bc

  • The starting address of the commands loaded in RAM on the emulator PCSX-Redux: 0x80010000

What should this numbers be useful for? What we need now is the address of the start of the code in the file CRASHBSH.DAT file since it also contains other assets than code. This address will allow to convert the bytes to actual MIPS code on the Disassembler.

The calculation I've done is:

80085554 - 80010000 = 75554 #This is the offset of the instruction

068E58bc - 75554 = 6870368 #This should be the actual address on the file.

So the code will probably start at 0x6870368. Let's check:

It doesn't seem to be correct.

But this could have been correct only supposing that the code is all loaded like one single block in RAM from CRASHBSH.DAT.

By scrolling backwards manually and doing some intermediate checks, the last (first) point in which there is a match between the emulator and the file to disassemble is at the addresses 0x8007a498 (emulator) and 0x068da800 (disassembler).

Similarities, on the other hand, end at the addresses 0x80097acc (emulator) and 0x68f7e34 (disassembler).

and by using ghidra to convert this section to code, the disassembled instruction remain the same until the addresses 0x80093e08 (emulator) and 0x68f4170 (disassembler):

Well, now we have all the useful code to permanently modify the game's values.

Note: the offset between the emulator address and the disassembly address is 0x7979fc98.

Understanding the damage and hit process

Now is time to work dynamically in order to understand how the process of damage, hit and invincibility work. Once I understand it, I can modify it perpetually.

Let's take as example the function at address 0x800856f0 (timer decrease by one) and in particular the moment in which it is executed when writing on 0x0009baac (Player one's damage timer):

addiu $v0, -0x1

Notice that if we put the Exec breakpoint on the function address stated above, it would be hit for every char. So i can suppose that it is called using as argument the address of the timer of the character that is being hit. Considering this, we should see somewhere the point in which the particular address of the timer for the player one is passed as argument.

I can notice that the value of the timer is first calculated and then stored at a referenced address+0x44. This means that there are chunks of bytes that keep all the information about each character. In particular these are the beginning of the chunks for each player:

  • Player one: 0009ba68

  • Player two: 0009bad4

  • Player three: 0009bb40

  • Player four: 0009bbac

The difference between the starting point of one chunk and the other is 0x6c.

When I put a READ breakpoint at 0x0009baae (address of the value related to the invincibility) I notice that at 0x0009bab0 and 0x0009bab2 there is a timer of 0x19 related to the animation in which the character falls and gets up, at the end of which the actual timer begins... SO?

WHEN THIS SECOND TIMER IS GREATER THAN 0 THE TIMER OF THE INVINCIBILITY IS STILL 0. I CAN SUPPOSE THAT WHEN YOU GET HIT A SECOND TIME WHEN THE INVINCIBILITY TIMER IS STILL 0 THERE IS A CHANCE THAT THE CHARACTER CAN GET TWO CONSECUTIVE DAMAGES.

Now just a little graphic explanation, and the interesting numbers are highlighted. CONTEXT: I'm about to get hit:

  • 0x0009baac: Invincibility timer is still 0

  • 0x0009baae: Invincibility value, that I've later discovered it takes two bytes so it's not just 2, but 0x200, being it little endian.

  • 0x0009bab0: A timer for the falling animation

  • 0x0009bab2: as above.

Notice that the memory analyzer in the picture uses addresses starting with 0x800, even if they're effectively referenced with addresses starting with 0x000. I'll use this notation to distinguish addresses of the code from the content of the memory.

Well. I guess it's time to check who writes on those addresses, and above all to check if there is a frame in which both these timers, and the invincibility timer are equal to 0, while switching from one to another, because that may be the moment in which the studied bug happens. And effectively this happens:

  1. The portion of memory when the animation of the fall is about to happen:

  2. The animation is finished; one timer is equal to zero, while the second is equal to one (one is faster than the other?)

  3. The invincibility timer is about to start:

Does the bug happen again if we change the animation timer to a slightly longer time?

To know this, we have to check the functions that write to those addresses.

With breakpoint at 0009bab0:

With breakpoint at 0009bab2:

First thing to be noticed: The timer at 0x0009bab2 is initialized before the one at 0009bab0, but it is updated by subtracting 1 AFTER the other one.

Second thing to be noticed, as seen before: the invincibility timer (0x0009baac) is initialized to 0x28 when the timer at 0x0009bab0 is equal to 0, while 0x0009bab2 is equal to 1.

But by playing the game a little more there is also a THIRD FUNDAMENTAL THING to notice. The timer at 0x0009bab2 is not set, nor it works when i get a physical attack.

Indeed... No timer at 0x0009bab2, while it should be the first to be set.

My idea now is to edit the offset of the functions at 0x800bf0c4 and 0x80085538 to write at offset 0x4a (two bytes forward) instead of the other one.

For some reason the function at 0x800bf0c4 is not located where i expected in the file CRASHBSH.DAT (address-0x7979fc98) but at address 0x05A77708. I'll modify the instruction at this address in the file together with instructions at addresses 0x068E5890 and 0x068E58a0

Clearly the problem is not solved, but on the other hand we obtained some changes in game: now the bug is overly exaggerated, switching from a little chance to happen to a thing that always happens (all the characters can get hit by two boxes consecutively).

I should find a way to only add the command to write on the address 0x0009bab2 without touching the other timer. It seems that when a physical attack arrives, the function to set the second timer at 0x0009bab2 is not even called.

I think about a thing then:

This is a picture from before: I can see that the function to set the invisibility timer (0x80085554) is called after the animation timer reaches 0. May I stop the timer when it reaches another immediate small value near to zero (e.g. 1,2,3...)

I take the clean CRASHBSH.DAT file and try to do this change at the address 0x068E58A8:

  • 0x34010001li $at, 1

  • 0x54210012bne $v0, $at, 12th successive instruction

But the game got stuck whenever a character got it.

Now I'll just edit a little the values at 0x68ef284 and 0x5a77708.

An higher value than 0x19 makes it happen more often because the animation timer also sets a period of time in which the character cannot move and this happens for both the timers. A lower value instead makes the bug happen "before than normally".

So i will now try to fine tune it decreasing the value by one, starting from 0x18 until i see the bug doesn't happen anymore.

Didn't work. I must find another way.

Solution

Set the invincibility timer (offset 0x44) as additional timer together with the animation timer. I have to be careful about the instruction at 800bf0bc; I edit it too so that it jumps 3 instructions instead of two, so that I can steal the NOP to replace it with a more useful instruction:

Then i notice that that address of the invincibility time is set to 0 during the animation at 0x800854d0 (0x068E5838). I'll switch it to a NOP:

And finally make the timers decrease together (0x068E58A4):

It does not work because when a physical attack occurs, 0x0009baaf remains 0.

So It is not necessary to decrease the timer but i must put the 2 in memory.

To do so I need to load a value in a register and then store it to the desired address.

addiu $at, $zero, 2 24010002
sh $at, 0x47($s0) A4410047

By playing a little more, I notice that it is not necessary to have 02 written on 0x0009baaf, but the invincibility timer and the invincibility value must be different from 0. I'll just replace 0x8008553C to store on s0 at offset 0x47 (0x0009baaf).

Now the invincibility Timer only lasts 0x19 cycles, being it contemporary to the animation timer. But what if I sum 0x19+0x28 (animation + invincibility duration) to have invincibility during all the process?

I replaced 0x41 to 0x19 at 0x800bf0c0, I tried it, and it finally worked!

REFERENCES

[1] Extract Options - ISOBuster documentation

[2] ghidra_psx_ldr: Sony Playstation PSX executables loader for GHIDRA

[3] https://hexed.it/

[4] Crash Bash: Space Bash gameplay #1 - YouTube (2:24)

SOURCES

https://psx-spx.consoledev.net/

CDROM Drive - PlayStation Specifications - psx-spx (license_unlock)

Exploring Tokimeki Memorial: Tools and resources for PSX reverse-engineering

How to Reverse-Engineer a PS1 Game – X-MAS CTF 2020 Writeup · GitHub

MIPS Reference Sheet : David Broman

https://student.cs.uwaterloo.ca/~cs350/common/r3000-manual.pdf

Games - ps1-links -> Contains something about CTR that may have the same structure as Crash Bash

How to Mod PS1 Games - YouTube

About

Study on a bug that happens in Crash Bash and allows a character to get hit two consecutive times even if it should be invulnerable.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published