Under the microscope: Skeleton Warriors (Saturn, PlayStation)
Featuring Galois fields and commentary from Mick West
After looking at Tony Hawk’s Pro Skater 2 in an previous edition, I decided to check out Neversoft’s other titles. I chose Skeleton Warriors, the skeleton-themed beat-em-up for Saturn and PlayStation, since that was the studio’s first game.
I found that both the Saturn and PlayStation versions have some unreported cheat codes. I was able to determine one of them myself, but had to get a cryptography researcher to help with the other.
I shared my findings with Neversoft cofounder (and famous skeptic) Mick West, and he replied back with an excerpt of the the Skeleton Wars source code that confirms my findings.
Read on for the cheat codes, reverse engineering details, some intimidating math, and commentary from Mick!
The hashing code
I started where I normally do when looking for unknown cheat codes: examining how the already-known ones work. The cheat sites already list some for both versions Skeleton Warriors, and they’re entered the pause screen.
On Saturn, the function at 06011c8c
listens for input when the game is paused1. Each button press updates three values in memory: the first at 06070904
, the second at 0606ccd8
, and a third at 0606aaa8
. Here is some Python code (adapted from Ghidra’s decompilation of the game’s SH-2 assembly) that simulates the updates:
letter_map = {
"L": 1, "R": 2, "U": 3, "D": 4,
"A": 5, "B": 6, "C": 7,
"X": 8, "Y": 9, "Z": 10,
}
def cheat_code_helper(x, buffer_03):
ret = (buffer_03 & 0xFFFF) * (x & 0xFFFF)
if (((x >> 0x10) << 0x10) | (buffer_03 >> 0x10)) == 0:
return ret
y = (x & 0xFFFF) * (buffer_03 >> 0x10)
z = (buffer_03 & 0xFFFF) * (x >> 0x10)
return (ret + 0x10000 * (y * z)) & 0xFFFFFFFF
def process_sequence(code_buttons):
buffer_01, buffer_02, buffer_03 = 0, 0, 1
for button in code_buttons:
index = letter_map[button]
buffer_03 = (cheat_code_helper(index + 1, buffer_03) + buffer_02)
for __ in range(4):
if (index & 1) != 0:
buffer_02 = buffer_02 ^ 0xA001
a = ((buffer_01 & 0x80000000) == 0) ^ 1
b = ((buffer_02 & 0x80000000) == 0) ^ 1
buffer_01 = (buffer_01 * 2 + b) & 0xFFFFFFFF
buffer_02 = (buffer_02 * 2 + a) & 0xFFFFFFFF
index *= 2
return (buffer_01, buffer_02, buffer_03)
That is, it’s computing some sort of hash, the result of which is stored in three 32-bit values. After you leave the pause screen, the hash values are compared to nine known ones:
On PlayStation it’s the same story, but the button mappings are different (Square is S, Triangle is T, Circle is O, X is X, and L1 is E). And there are ten target values:
One unknown cheat for each version! But what are the buttons?
A brute force approach
I first tried brute force enumeration — generating every possible button sequence, computing the hash values, and checking the results2. Here’s my Python code:
from itertools import count, product
# Generate an infinite stream of button presses
def get_button_stream():
for r in count(1):
yield from product(letter_map, repeat=r)
# Check their hashes and print out any hits
def main():
for code_buttons in get_button_stream():
result = process_sequence(code_buttons)
if result in targets:
print(
format(result[0], "08x"),
format(result[1], "08x"),
format(result[2], "08x"),
"".join(code_buttons),
)
This got me the missing PlayStation code! OUTDOORS, i.e. Circle, Up, Triangle, Down, Circle, Circle, Right, Square. What’s it do?
Answer: debug mode! Press all four face buttons to activate it. You can reposition your character with the D-pad, clip through walls and floors, and change items with R1:
The mathematical approach
Brute force didn’t get me anywhere with the missing Saturn code — I was able to check all of the character sequences up to length 14, but didn’t come up with the missing one.
Since the leading letters for the sequences spell things out (e.g. CRAZY BALL
), I switched to something resembling dictionary attack. That is, I got a list of words; filtered it for the ones that use only U, D, L, R, A, B, C, X, Y, and Z; and then generated all possible permutations of them:
from itertools import count, permutations
def get_button_stream():
for r in count(1):
yield from permutations(letter_map, r)
This worked for Tony Hawk’s Pro Skater 2, but it got nowhere here. I checked the permutations up to length four (i.e. every possible ordering of four words), and had to stop the process — five would have taken forever complete.
I worked in information security long enough to know that you should never design your own hash algorithm — it will be breakable by a good cryptographer. I’m not a good cryptographer myself, so I asked for some help.
Mehdi Tibouchi is a good cryptographer. He looked at a version of the hash algorithm shown above and quickly answered:
Funny riddle. The answer is
ybulldazzlzduuru
, and can be obtained by a simple division in the ringF_2[x]/(x^64+1)
. I'll give a more detailed answer later if needed.
Ha! I took a lot of math in school, but I didn’t get to Galois fields. Research shows that F_2[x]
refers to, more or less, polynomials with binary coefficients. The division by the polynomial x^64+1
comes from the concatenation of the two 32-bit buffer_01
and buffer_02
values.
The specific answer wasn’t quite right. That was my fault, not Mehdi’s—I didn’t transcribe the algorithm correctly. I mistakenly left out the buffer_03
computation3. Whoops! But his answer provided the hint I needed: dazzlz
wasn’t in my word list for the dictionary attack.
I added it in, and… bingo. With that as a mandatory element in every permutation, the code popped out: RUBY BULL DAZZLZ DULL A, i.e. Right, Up, B, Y, B, Up, Left, Left, Down, A, Z, Z, L, Z, Down, Left, Left, A.
The effect is a very similar debug mode. This one lets you turn on hit boxes with the X button:
I actually knew about the effect before — here’s a video of it that I posted a while ago.
The real source
Mick West, co-founder of Neversoft, wrote a blog about his game development career. Its last post was about making Skeleton Warriors for the Saturn, and it includes some source code fragments.
I reached out to Mick about these new cheats, and he responded by sharing some of the more source code for the game! Here is the actual cheat check:
Amusingly, the code keeps the Saturn debug mode buttons a secret also! There’s a comment above that says:
The cheat codes are stored as 64bit CRC checksums plus a 32 bit combination product.
I've not worked it out, but I think the CRC of each code is practically unique and the only way to get a code from the CRC is to try all codes.
So, for most secure codes, do not used English words like DULLARD, use a random word like RLDBXYZ. For real security use a 16 letter code.
So that explains DAZZLZ
.
The source code also reveals some aspects of the debug controls that I missed above:
X+Start skips to the next stage
X+Z shows the coordinates
Holding L enables slow motion
Outro
Many thanks to Mehdi Tibouchi for the assistance. I wouldn’t have come up with the answer without his help! And many thanks also to Mick West for sharing a bit of video game history with us.
For previous coverage of long-undiscovered cheat codes, see my archive. What other games should I be looking at? I’ll have articles on some other Neversoft games in the future.
How do you find the functions that listen for input? First compare emulator memory snapshots taken while holding and not holding a button. Then set read breakpoints for the addresses that change when you press buttons.
The brute force enumeration approach worked well in Ecco the Dolphin: Defender of the Future.
The hash computation outputs three 32-bit values, but only the first two are needed to identify every cheat except for the missing one on Saturn. That one requires the third one!
Hello, I've been a fan of your fantastic work for some time now, but I'm only writing here for the first time. While you no doubt already have much to do and games on the horizon to look into, I leave some suggestions of things that have been hidden that I would love to see if they can result in anything if you dig them out.
Virtua Fighter 2: Using a hex editor, it is possible to find special messages wishing happy birthday to each specific fighter in the game; however, this is not visible in the game in any part.
Athlete Kings/Decathlete: There are various character names/flags from other countries present in the game files that are not available to play. These are most likely remnants from the arcade version, where the name and flag of the available characters would change based on the country of the arcade cabinet. It is possible to display some of those flags and names by editing some files, but others appear to be corrupt.
Nights into Dreams: This is the most known of them all and probably the one with nothing more to discover since it's been searched for many years by a lot of people. There is a cut special boss fight; all we know is that the boss's name is Selph, and there is a music track for the boss fight in the game. Nothing else is known. There is also an image in the game files of Selph's portrait that is not present in the game by normal means.
Thank you very much for your amazing work and for reading. Looking forward to all your future posts!