Cracking RISC OS Elite

A while back I wrote a blog post about breaking the Key module on RISC OS games from the 4th Dimension and how I went back to reminding myself how I did it many years ago.

I thought I’d do another one using different tactics this time. For this one I chose the Archimedes port of Elite, the best version of Elite on any platform. When this originally came out it didn’t have any form of disc protection, but required that a word was entered from the manual.

This was an effective method, but didn’t protect it from piracy when somebody just photocopied the manual and passed that around. The game was later published for free on the cover of Acorn User, with the copy protection removed.

Identifying the code

A quick search through the code shows no obvious words that could indicate that it has a copy of the manual local, so it’s like there’s a set of hashes for the word in a look up table. I could reverse engineer this, but couldn’t frankly be bothered.

It’s time to dig out a tool, in this case !Hacker by Andrew Clover of Doggysoft. This was an excellent tool that would allow you to press both alt keys and go into a GUI that could allow you to search through memory and see a disassembly of the program currently running. It was so useful that I actually paid the whole £5 to get a legal version!

So we run !élite and !Hacker, start up the game, enter a known string, in this case “tautology” and press both alt keys and return to enter !Hacker.

The main Hacker screen

The first step is to search through memory for the canary, using the Search pane. It has a think and returns a list with the address &0000A6BC in it, so we now know that whatever does the comparison uses this address as a buffer. If we browse to this in the disassemble pane we can see the string in context.

Location of input bugger
Input buffer in the Disassembler pane

To use this buffer, it must be assigned to a register at some point, so we can use the search facility to look for the address as a mnemonic. Which returns two addresses in the same area of RAM (&A668 and &A690) and one much higher.

Searching for the buffer in the disassembled text

Looking in the Disassembler pane for &A668 we can see the whole copy protection routine in its simplicity:

Code which uses the input buffer

The routine started at &A684 looks to be the routine that does the comparison for the word – it’s looping through the buffer and eoring the characters together to make a simple hash, which it then compares with the value in R5 and, if successful jumps to &A8C8. So, what if we change this single mnemonic to always branch, i.e. alter it from BEQ to B?

ARM uses the top nybble of the machine code to contain the condition for execution – in this case it’s set to &0 for EQ; setting to &F would be NV (never); but setting to &E would be AL (always, implied). So if we change the word at &A6A8 to &EA000086 then it should be come B &A8C8 and always branch. Let’s try!

Changing the branch

And then clicking the Back button to enter Elite and it successfully loads Elite!

Access to the game!

Writing a Crack

So how can we use this – we could go through the same route as we did with the Key module and “decrypt” the elite executable to alter this, or we could do it the lazy way and cheat.

If we write a module that we load before loading Elite and set it up with a routine to check and alter this memory location, then we could enter anything to make the check successful.

There’s multiple ways of doing this, but the easiest is to hook the event vector to look for a key press, such as F1, then check the memory location, alter it and remove the hook. This is some simple ARM code.

I have a few similar bits of code I’ve written in the past, so I stole one and amended it. It looks a tad weird as it’s written for the Assembler that a mate and I wrote back in the day, it could easily be ported to Acorn Assembler or the Assembler in BBC BASIC, but I didn’t need to.

The code is relatively self explanatory, the table at the start defines the module calls. init is called at the module start and calls XOS_Claim to claim the event vector (event &10). term is called when the module is killed and calls XOS_Release to release any claim on the vector.

The magic happens in code. Where it checks that it is being called because of a keypress and that the key being pressed it F1; then it checks that the address to be patched is valid, checks if it has the right value, patches the address and then calls XOS_Release to release its claim. At this point I should probably have called XOS_Module to kill the module, but I’m lazy.

And that’s it – copy protection on Elite defeated.