Carmageddon 2 Cheats RE - The Search for Steel Gonad O' Death

Discussing, modding and troubleshooting the greatest unfinished sequel ever!
User avatar
liamswift
pedestrian
Posts: 2
Joined: Wed Dec 15, 2021 12:04 am

Carmageddon 2 Cheats RE - The Search for Steel Gonad O' Death

Post by liamswift »

I recently joined the Carmageddon Discord and got into reverse engineering. I heard that some were curious to figure out how the cheats worked in C2, because there's no trace of them in the exe. Turns out the answer was right in front of us all along.

Anyways, I'll make it short and sweet. The TLDR is: it's the same as C1.

It's a true team effort where everyone contributed some useful piece, be it software, reverse engineering knowledge or anything else. It's heartwarming to see people come together for a game they're passionate about. Especially a shout out to Robin_Jadoul, who put a SMT solver program together in Python with z3 just to help crack the hashes in reasonable time.

Cheats are stored in the balls 0059098c
At this memory location is a table of sorts, presumably much like the table that can be found in the Dethrace project. https://github.com/dethrace-labs/dethra ... c#L79-L122
(Thanks to jeff-1amstudios for pointing me here!). There are two parts of a hash called code and code2, a function and an argument for said function.

Code and code2 and why they matter
The table contains code & code2, these can be seen as hashes that are calculated as the user presses keys on the keyboard. We all know if you wait 1 second, whatever cheat you were typing, you have to start over. C2 uses the same logic for calculating these hashes. This algorithm (which is batshit insane) can be found here:
https://github.com/dethrace-labs/dethra ... put.c#L237

So, the cheat table simply stores the expected hashes for every cheat. That's how you link "WETWET" or any other string to an actual cheat.

You press buttons on your keyboard. As you do so, code & code2, which are essentially hashes derived from your keyboard input, get calculated. These values for code & code2 get compared with code & code 2 in the cheats table. If there's a match, huzzah you're a cheating bastard.

What this means for Carmageddon 2
The same algorithm from C1/Dethrace can be used to translate keypresses into code & code2 hashes for C2. The resulting values can then be searched at 0059098c where the cheat table is located. It presumably uses the same system as C1/Dethrace, where the code & code2 values are mapped to cheats.

Cesm had actually found this cheat table in the same location in the past (if you want to find it easier, search for the 'Cheating off' string, then scroll up a bit). C2 Cheat Editor, which Cesm had made, basically moved blocks around in this cheat table, essentially swapping cheats around. But now we know what the actual data blocks contain; they contain the code and code2 hashes, plus a function and an argument for said function. Assuming it's just like C1's table in the first link of this post, at least, but it sure does look like it.

As a result, in theory it should be possible to press any number of keys in C1/Dethrace, grab the resulting code and code2 values, then apply them to the C2 binary (I think they're stored in reverse). That way you'd end up with custom cheat strings. This hasn't actually been tried yet, however. So far, we've just been able to succesfully find matches in the C2 binary for these hashes based off of C2 cheat strings using the C1 code/code2 hashing algorithm.

What's next?
Espyo is writing a brute forcer because he wants to find the Steel Gonad O' Death's keystrokes. There is the problem that the string could be any length and there could be hash collisions, making searching more difficult.

We could use means of elimination to exclude all known cheats from the 0059098c table, leaving just the Steel Gonad O' Death's hash. Then we could attempt brute forcing and maybe dictionary attacking the resulting string to find the possible original cheat string. That is, assuming the Steel Gonad O' Death is inside the cheat table to begin with.

This has been a fun first journey for me into reverse engineering. I want to thank Espyo, Toshiba-3, Cesm, jeff-1amstudios, Trent, maarten and anyone else who contributed.

I'll leave you with 2 attachments which are a hash calculator made by Espyo, as well as the hashes/functions/arguments for C2 that Espyo has uncovered thus far, so big props to him for making that!

Edit: maarten hinted that the cheat database may actually start at 00590970, meaning the CSV is missing 3 entries
Edit 2: Steel gonad's codes are 4B054B60 and 6B6736CB. If anyone wants to try their hand at cracking them using Espyo's/Dethrace's algorithm, feel free. The problem is that a brute-force approach scales exponentially.. This is to be researched ><
Edit 3: If you play with the hash calculation, I found that the first part of the hash goes up the longer the string. Because of this, I could determine the string is at least 18, probably 19+ characters in length. Smaller strings will never get you to '6B' at the beginning. The second part of the hash also moves in a linear fashion (higher letters = higher last hash). The problem is that the two are interdependent, but surely this property of the hashes can be exploited?
Edit 4: Robin_Jadoul from the DECIPHER Discord server managed to find some strings that map to the Gonad hashes:
(17) ezpzkballxxewazon (as noted on the wiki)
(18) vruftjjethasxmdlbg <- Verified Gonad cheat to work ingame! Thanks Espyo for checking!
(16) tenzxhlkyqtbrvyo <- Verified Gonad cheat to work ingame! Thanks Espyo for checking!
(15) uwsqyzhxtpduvyn <- Verified Gonad cheat to work ingame! Verified by myself/Espyo!
(16) dzzkcakyyuomxtyn
(16) tenzxhlkyqtbrvyo
(17) nmqyufpfxfrjcmgwu
(17) mqdsqhkyqmkemdyut
(17) kayymenmhpkpfmwsx
(17) imjmhtwsagxekmwvv
(17) usjlwcguwokdsrjxc
(17) cdtezggqqmlrfyyym
(17) lrqbkoojyykgkgwos
(17) vunkrsgzyyhdqchan
(17) lwxwisnccsulqckye
(18) oeyodtchssqcemniew
(18) uboyihsaaecyyieqqo
(18) bgvlxqoapesyggmbom
(18) jzyghdrckwokenkipf
(18) kfbjrbeaqmmmdryszs
(18) qpheeokaqkxskmxeik
(18) vvxykpcjmsiqkgahac
(18) brbqckwrwsaovgfdze
(18) yabaiiakzkyokmtyap
(18) nhdxpknewatdebtkzn
Edit 5: Here's the program that Robin_Jadoul had put together to crack the hashes. I rewarded him for donating his knowledge to put this nifty thing together!!!
Version w/ optional crib support (search constraint):

Code: Select all

# Python 3.10 with pip install z3-solver
from z3 import *
from math import floor, ceil

code2_target = 0xa11ee75d
code_target = 0xf805eddd

def length_bounds_estimates():
  sum = code_target >> 21
  return (floor(sum / (22 + 25)), ceil(sum / 22))

def char_to_val(x):
    return ord(x.lower()) - ord("a") + 22

def step(code, code2, sum, inp):
    sum += inp
    code += (inp << 11)
    code = LShR(code, 17) + (code << 4)
    code2 = LShR(code2, 29) + inp*inp + (code2 << 3)
    return (code, code2, sum)

def final(code, code2, sum):
    return (LShR(code, 11) + (sum << 21), code2)

def main(LEN, crib):
    for offset in range(LEN - len(crib) + 1):
        keys = [z3.BitVec(f"k_{i}", 32) for i in range(LEN)]
        s = Solver()
        for k in keys:
            s.add(k >= 22)
            s.add(k < 22 + 26)

        code = code2 = sum = BitVecVal(0, 32)
        for k in keys:
            code, code2, sum = step(code, code2, sum, k)
        for c, k in zip(crib, keys[offset:]):
            s.add(k == char_to_val(c))
        code, code2 = final(code, code2, sum)
        s.add(code == code_target)
        s.add(code2 == code2_target)
        found = False
        while (t := s.check()) == sat:
            print(f"Crib '{crib}' at offset {offset}, length {LEN}: ", end="")
            print("".join(chr(s.model()[k].as_long() + ord('a') - 22) for k in keys))
            s.add(Or(*[k != s.model()[k].as_long() for k in keys]))
            found = True
        print(f"Crib '{crib}' at offset {offset}, length {LEN}: {t}")
        if not crib:
            break # don't needlessly loop when no crib
        

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} length [crib]")
        exit(1)

    print(f"Length bound estimates min/max: {length_bounds_estimates()}")
    main(int(sys.argv[1]), (sys.argv + [""])[2])

Previous non-crib version:

Code: Select all

# Python 3.10 with pip install z3-solver
from z3 import *

code2_target = 0x6B6736CB
code_target = 0x4B054B60
for LEN in range(12, 29):
    keys = [z3.BitVec(f"k_{i}", 32) for i in range(LEN)]

    def step(code, code2, sum, inp):
        sum += inp
        code += (inp << 11)
        code = LShR(code, 17) + (code << 4)
        code2 = LShR(code2, 29) + inp*inp + (code2 << 3)
        return (code, code2, sum)

    def final(code, code2, sum):
        return (LShR(code, 11) + (sum << 21), code2)

    s = Solver()
    for k in keys:
        s.add(k >= 22)
        s.add(k < 22 + 26)

    code = code2 = sum = BitVecVal(0, 32)
    for k in keys:
        code, code2, sum = step(code, code2, sum, k)
    code, code2 = final(code, code2, sum)
    s.add(code == code_target)
    s.add(code2 == code2_target)
    found = False
    while (t := s.check()) == sat:
        print(f"Potential cheat found of length {LEN}: ", end="")
        print("".join(chr(s.model()[k].as_long() + ord('a') - 22) for k in keys))
        s.add(Or(*[k != s.model()[k].as_long()]))
        found = True
    else:
        if not found:
            print("length", LEN, t)
Edit 6: As per the program (the first one, which accepts cribs), we've got a lot of random strings but are now interested in English-looking ones. Basically, something that would be a sensible cheat code instead of random characters. To do this, we need to fire up an instance per length, per crib. So this could be 16 with "test" and 17 with "test" and 18 with "ball" etc.. The crib is some text that should appear in the output.
I'm putting a list together of cribs that might be useful:
test - from testes, testicles, etc.. many puns can be made from it - Thank you Trent!
ball - from balls, ballz ?
bollock - fitting UK word for balls
Edit 7: Everything with length 17, 18 (no crib) is exhausted, so no need to look for strings with length 17, 18 anymore. (actually this isn't certain if they were fully exhausted, as Robin didn't see the crib strings appear in the non-crib one)
You do not have the required permissions to view the files attached to this post.
Last edited by liamswift on Fri Dec 17, 2021 4:31 pm, edited 1 time in total.
User avatar
Espyo
road raged psycho
Posts: 1288
Joined: Sat May 28, 2011 10:43 am

Re: Carmageddon 2 Cheats RE - The Search for Steel Gonad O' Death

Post by Espyo »

Just wanna drop an update on the state of things.

First of all, Liam is constantly updating the OP, for anyone unaware. So I guess my reply also counts as a thread bump on behalf of Liam's updates :P

Right now, Liam, maarten, and Robin Jadoul are trying their hand at a bunch of different software that I've been too busy and too simple-minded to follow, and are hoping to combine efforts to find more codes. Although all of C2's cheats have now had codes found for them, some of them are just random nonsense that happens to work, and the hope is to find all official (or official-looking, I guess) codes for the cheats.

In parallel, I've updated the wiki with the new information.

To finish, here's like a summary of everything that's been going on, and what's to come:
  1. The following official cheat codes have been found:
    1. Random APO (RANDYPANDY)
    2. Random APO potential (RANDYPOT)
    3. Random good APO (RANDYQUAID)
  2. The following cheat codes have been found, but are probably not official:
    1. Edit mode that also works in multiplayer (XZSUYYUCWZZZZZWYVYOZVWVXPVQWJZ)
    2. Full car and race unlock (MWUCUZYSFUYHTQWXEPVU in main menu)
    3. Steel Gonad of Death mode (EZPZKBALLXXEWAZON)
  3. The following power ups have been found to not have any codes, period:
    1. Mine
    2. Random selection 28
    3. Random selection 29
    4. Random selection 30
    5. Random selection 31
    6. 8-bit mine
    7. Got 'im in the bollocks
    8. Time substitution powerup
    9. Sever tail
  4. Nothing else in Carma 2's executable makes use of the cheat code typing logic
  5. What's in store for the future:
    1. Find the official code for that multiplayer edit mode
    2. Find the official code for the full unlock
    3. Find the official code for gonad mode
    4. Analyse Carma 1 for unknown codes, and if other things exist that call the cheat code typing logic
User avatar
Razor
Stomping-on-Kittens
Posts: 3356
Joined: Sat Dec 01, 2007 6:38 pm
Location: Beaver City

Re: Carmageddon 2 Cheats RE - The Search for Steel Gonad O' Death

Post by Razor »

From a quick glance over, this is very useful information. I will give it a full read when I get some more time later on and I will add the information to my in-dev megathread section about The Steel Gonad O' Death
Thank you for all of your hard work and thanks for not keeping it on Discord where it will inevitably be lost within minutes.
[ Razor @ Carmageddon Webmasters Alliance Network ] [ My Downloads ] [ My Carma Items ] [ Conversion lists ]
To download the BETA Mod and more information on my other work, visit me @ https://razor.cwaboard.co.uk
User avatar
Toshiba-3
アルカリ (単3形)
Posts: 5289
Joined: Sun Jun 08, 2003 12:14 am
Location: Bleak City

Re: Carmageddon 2 Cheats RE - The Search for Steel Gonad O' Death

Post by Toshiba-3 »

It was amazing and impressive to watch you guys come together and crack this so fast. :twisted:

IMO if you can find similar cheats like EZPZKBALLXXEWAZON that might not be the original ones but easy to memorize then that'll already be fantastic. I've found myself using MWUCUZYSFUYHTQWXEPVU a few times now, it's handy but yeah... have to memorize it again every time :slol:
You do not have the required permissions to view the files attached to this post.
Image / carmageddon add-ons at road reaction