Dragon Warrior III Dream Ruby glitch (aka numbness glitch, item glitch) by vaxherd
Gidsen
/
Dragon Warrior III Dream Ruby glitch (aka numbness glitch, item glitch) by vaxherd
Geüpdatet 7 years ago door pjplustwo

By forming a party consisting of only dead characters, you can confuse the game into modifying your party's status in useful ways, such as changing weak armor into strong weapons and getting access to endgame towns through the Return spell.

Cause of the glitch

Each time you take a step, the game checks the square each character just stepped onto and applies damage if it's a swamp or barrier square. The game is smart enough not to try and damage characters who are dead, but it assumes there's always at least one living character in the party -- which the game normally ensures by preventing you from removing your last living character at Luisa's Place, but if that character is numb, the game lets you leave that character behind anyway, giving you a party of only dead characters. You can also end up with a party of 1 dead character (the hero) by going to the fortuneteller in Assaram with the hero dead and all other living characters numb, typically accomplished by using the Dream Ruby on the other characters (hence the name of the glitch).

Once you do get an all-dead party, the damage check gets confused and tries to check a whopping 256 characters for damage. Depending on what data happens to be in memory, some of those "characters" will take "damage" by subtracting 2 from the memory location that would have the current HP of that character. If the right "character" triggers damage, the memory location will be where the party's items are stored, causing an item to change to the item 2 steps before it in the item list, or where the Return destination list is stored, giving access to towns you haven't yet visited.

Technical details

When moving to a new tile, $D389 does a far call to $B16A in bank 14. This routine has two purposes: auto-cure any numb characters (1/16 chance per character per step) and check each non-dead character for field damage. The routine first initializes variables for the damage check by calling $B1BD, then performs the check itself at $B1EE.

The main purpose of $B1BD is to count the number of living characters in the party. This count is stored to $66, and a list of party indices of the living characters is stored starting at $68. Zero is stored in $67 to initialize the counter for $B1EE.

$B1EE performs the field damage check as follows:

  1. Load the current counter value ($67) into register Y.
  2. Load the index of the tile under party member Y from $93+Y.
  3. If the map type ($9A) is zero (the overworld) and the tile index is $07, apply swamp damage and go to step 6. (This is effectively dead code since the tile type for overworld tile index $07 is $05, so swamp damage would be applied anyway.)
  4. Load the tile type for the tile index from $6DE0 (indexed by the low 5 bits of the tile byte; the high 3 bits are the layer number in towns and dungeons).
  5. If the tile type is $05, apply swamp damage; if the tile type is $06, apply barrier damage.
  6. Increment the counter.
  7. If the counter is not equal to the number of live party members ($66), repeat from step 1. Note that the counter is not checked against the limit until the end of the loop, and it is tested for equality, so if there are no living party members, the loop will iterate 256 times before the counter overflows from 255 to 0 and the stop condition becomes true.

Damage is applied by loading a byte from the array at $68 (indexed by the current counter value) and calling the damage routine, which subtracts the damage from the character's current HP (HP is stored as 4 16-bit values at $071C).

In order for the bug to occur, at least one tile type on the map must be $05 (swamp) or $06 (barrier). This is true, for example, in the second floor of Luisa's Place (map $44, tile type index $01). Using that map as an example, any zero-page byte whose low 5 bits are 00001 will cause 2 to be subtracted from an even address in the range $071C...$081A (due to NES memory mapping, $0800...$081A are mapped to zero-page addresses $00...$1A). For a byte at zero-page address A which triggers damage, the party index will be loaded from zero-page address A minus $2B (the difference between $93 and $68); if the value of the byte at that address is B, 2 (swamp) or 15 (barrier) will be subtracted from the byte at address $071C + ((B & 127) ¤ 2).

Potentially useful addresses to modify are:

  • $0746, $074C: High byte of experience for characters 1 and 3, which can be underflowed to level the character up to 99 in the next battle. Requires a party index byte of $15 or $18, respectively, and requires the next character's experience to not be a multiple of 256.
  • $0752, $0758: 3rd byte of Return list for characters 1 and 3 (if zero, swamp damage will change it to $FE, allowing access to endgame towns -- note that the borrow clears the Aliahan bit for the next character). Requires a party index byte of $1B or $1E, respectively.
  • $077C...$079A: Party inventory. The 1st, 3rd, 5th, and 7th items in each character's inventory can be changed; underflowing will also change the following item (as long as that item is not ID 0, an unequipped Cypress Stick). Requires a party index byte of $30...$3F.
  • $079C...$07BA: Party spells. The 3rd page of battle spells can be underflowed if the field spell byte is nonzero (as is the case for a level-1 Pilgrim, for example). Requires a party index byte of $41...$4F (odd values).
  • $07BE: High byte of party gold. Gold will not be capped at 99999 when buying items (though the number displayed in the gold window is capped at 99999). On underflow, this marks the first slot in the stored character list (normally occupied by the hero) as empty; creating a new character at Luisa's Place 2F would then overwrite the hero's data in the save file. Requires a party index byte of $51.
  • $07C2: Stored-character index (in the adventurer list) for the 2nd party member. This could be underflowed to a large value, though I haven't investigated whether this would produce any useful effects. Underflowing would also decrement the index of the third party slot, which could potentially allow having two copies of the same character in the party. Requires a party index byte of $53, and requires the hero to not be in the third party slot. In all cases, the party index byte is shifted left to address the HP value, so the high bit of the byte can be either 0 or 1.

Note that some bytes which seem like they might be useful to underflow (maximum HP, for example) can't be underflowed because the next byte is zero; the damage routine treats the two bytes as a 16-bit integer, and will not attempt to reduce it below zero.

Also note that if you underflow the third spell page for a Pilgrim, Heal will be removed from the field spell list. If you also subtract 2 from the first spell page, the character will (if at level 1 or 2) learn Heal at the next level-up.

The following addresses can trigger damage on Luisa 2F: - The random number seed ($1C and $1D) will trigger damage if it has the proper value. - $2F will be $01 in towns, which triggers damage if $01 is a swamp tile (as on Luisa's Place 2F) - The lead character position ($30 and $31) can trigger damage; for example, on Luisa 2F, tile index $01 is a swamp tile, so X or Y coordinate 1 (the top or left edge of the map) will trigger damage. - $83 appears to be used as a temporary in message processing, and it can take on values which trigger damage. - The map number ($8B) and map type ($9A) could potentially trigger damage. Maps with type $01 are: $00 (Aliahan), $06, $07, $0A (Portoga), $0D (undefined map), $12, $50 (Isis), $96 (Isis oasis). - $9D receives the low 2 bits of a random value every 64 frames on the overworld if no party members are standing on a town. There are no swamp or barrier overworld tiles in the $00-$03 range, but since the value does not change while in a town, it could be set on the overworld and then used in a town to trigger damage. - $A4 is a counter which increments with each random number returned; it could be manipulated to trigger damage. - The music data pointers at $D6...$DD could trigger damage. - The sound timers at $E2...$E9 and envelope offsets at $F7, $F9, $FB, and $FD could trigger damage, though timing would be tight.

Additional values which can trigger damage on Luisa 2F: - Consistent: $34, $45, $71, $73, $8D, $99, $B5, $C8, $CF, $DD. - Inconsistent (conditions not yet known): $0A, $0E, $12, $75, $77, $79, $7B.

Additional values which can trigger damage on the overworld: - The byte at $01 is consistently $07, which triggers damage. The associated character index byte is at $D6, the low byte of the first music channel data pointer. - $6C is consistently $E7 (low byte of the end of the overworld entrance list at 6/$B387) at entry to $B1EE, which would trigger damage, but it's cleared by other code if any preceding byte (such as $01) triggers damage.

The bug is also present in the initial release of the Japanese version of the game (Dragon Quest III), though it was reportedly fixed in a later rerelease. Since DQ3 limits names to four letters (kana) long, all data at $077C and later in DW3 is shifted 16 bytes toward $0700; for example, inventory data is located at $076C rather than $077C.

Stick Slime glitch

The Stick Slime is a dummy item which can be obtained via the Dream Ruby glitch by glitching an empty inventory slot. The game has 125 different items, but the format of the inventory data allows for 128 different item types, so setting an inventory slot to one of those 3 unused types causes the game to read parts of the program code as item data.

When you try to use an item, the game checks a table that tells it whether the item can actually be used and what to do when you use it. For the Stick Slime, the game reads part of the program code that makes it think the item can be used, but the data that says what to do with it points into the middle of nowhere, so the results are random: sometimes you can get several item messages at once, sometimes the game will crash or do other strange things, sometimes nothing will happen.

Technical details

$AE12 in bank 0 is called when using an item on the field. This routine first looks up the item's data in the table at $922B; bit 7 of the value indicates whether the item can be used at all. If so, or if (as applies here) the item number is at least $77, the routine then looks up a jump pointer for the item effect subroutine, which is stored in a 3-byte-per- entry table (the first byte appears to be a flag byte) at $B0C5, which only has entries for field-usable items. The Stick Slime is item $7D; as $7D is the first invalid item number, the effect routine pointer is read from the first 3 bytes after the end of the $B0C5 table, which gives the pointer $2948.

$2948 is within the PPU register region, pointing to (a mirror of) PPUCTRL. The CPU attempts to read an opcode from this address and gets open bus, which will be $29 (the high byte of the address read for the indirect jump). $29 is AND #imm, so the CPU reads an operand byte from $2949 (PPUMASK); this is also write-only, so the CPU gets $29 from open bus again.

The next instruction is found at $294A; this is PPUSTAT, which will return $00 (BRK) at the time this particular code is executed. Dragon Warrior III implements BRK as a sort of far call instruction, with two operand bytes which indirectly select the function to be called (via table lookup). The first byte will be at $294B (OAMADDR), which is write-only so the CPU gets $29 from open bus (the high byte of the indirect address used for the load). The second byte, however, will be at $294C, or OAMDATA; since this code will execute while the PPU is rendering the screen, the byte loaded will be depend on the precise timing of the read. The BRK handler uses table lookup rather than directly taking the address from the operand bytes, so this won't necessarily lead to a crash, but the particular behavior is effectively random.

Incidentally, the subroutine pointer for item $7E (Black Raven) is $D385 ($85 $D3 = sta $D3), which is also used for item $7F (Sword Horned) since that item's flag byte doesn't have bit 7 set and the table offset consequently isn't incremented for that item. This address happens to lie right before the normal call to the field damage subroutine, which is what got all this started in the first place. Coincidence or insanely clever inside joke? You decide.

The names of the invalid items come from overrunning the name tables; for example, "Stick" is the second line of the name of the first item (Cypress Stick), and "Slime" is the first line of the name of the first enemy (Slime). The garbage character at the end of "Slime" is a code used in enemy names to indicate how the name is pluralized.

Is arbitrary code execution possible?

I really, really want to say "yes", just because it would be so much fun. But unfortunately, it doesn't seem to be possible, at least not reliably.

The most likely exploitable path would be to read a value of $xF from OAMDATA with $4 <= x <= $E; this causes the BRK handler to select bank $1x at $8000, treat $8000 as a table of addresses, and jump to the address at the entry in that table selected by the first BRK operand byte -- here $29, so the jump target is read from $8052. Potentially exploitable values for the second operand byte include (note that on entry, A is the party index of the character who has the Stick Slime, X is $16, and Y is $29 from open bus):

  • $5F => $691D; this is the second byte of the 10th character's spell list in the third save file.
  • $6F => $68A8; this is the fifth inventory slot of the 7th character in the third save file.
  • $AF => $0F00; this mirrors to $0700 and is the beginning of the active party data.
  • $BF => $C915; this jumps into the middle of another subroutine with PLA instructions following, so stack manipulation might be possible. However, the possibilities for finding one of these bytes in OAM are limited. Sprites in DW3 are positioned with an offset (0,4) from multiple-of-8 coordinates, so byte 0 of a sprite entry will always be $x3 or $xB and byte 3 will always be $x0 or $x8. Bits $1C of byte 2 are always zero, so that leaves finding a sprite that uses one of the values above in its tile set and triggering the OAMDATA read at the exact instant (one of 2 cycles) the PPU is copying that byte to the secondary OAM during sprite evaluation.

If the Stick Slime could be used in battle, data past the end of the battle effect routine list ($BE3A, 53 entries) gives some interesting pointers, like $0558 (found at offset $72; this is the battle command data list, so you could write e.g. $20 $65 $65 by using Clothes, Medical Herb, Medical Herb with the first 3 characters, use the Stick Slime with the 4th character, and jump into the second save file's inventory data). Unfortunately, the item flags for battle are located right before the field flags, so the overflow items overlap with the first three items in the field list, which are weapons and thus not field-usable. Even if they were, the corresponding effect byte past the end of 4/$B270 is $00, which is handled properly by the code (it would be treated like the Blaze spell). This applies to the other invalid items as well.

On the Japanese version (Dragon Quest III), the Stick Slime routine pointer is $8100, in the middle of a separate routine, and the Black Raven pointer is $6600, in the second save file's spell list. The latter is promising, but unfortunately, when the game tries to write any of the glitched item names to the text window, it crashes due to overflowing a temporary buffer for copying the name. (DQ3 uses a different method to store names, with a length byte following the name of the item. The name copy loop has a similar bug to the Dream Ruby glitch in that a length of zero results in copying 256 bytes. The buffer is located at $0634, so copying 256 bytes will overwrite all the state information stored at the end of the $06xx page, such as the current $8000 bank index which is stored at $06D5.)