I left you last time with a decoded file protection module, now let’s dive in and find out what this insidious module actually does.
Note when I say word here I mean 32-bit words.
Module Header
The module starts off with a module header – up to 13 words which each point to either code or a specific data structure to show what facilities the module provides. Of these only the first 7 are required, the rest are optional. This module only uses the first 11.
Code offsets
Start offset is a pointer to code if the module is Run (e.g. if it provides a language interpreter). As this offset is set to 0 then the module cannot be run.
Intialisation offset is a pointer to code the is executed when the module is loaded or reloaded. The module has a pointer to the code at address &70. We will look at this later.
Finalisation offset is a point to code that is executed when the module is killed, before it is removed from memory. The module has a point to address &3E4 and we will look at this later.
The Service call handler is to manage operating system calls, this is set to 0, so is not used.
Strings and command tables
The Title and Help string offsets are points to word aligned NUL terminated strings that define title and help strings. In this case, the title, i.e. the name of the module, is:
Sol
And the help string (i.e. what is shown with a *help sol
command) is:
Please go away!
The help and command table defines any commands that can be issued on the operating system command line (known as CLI). These have a defined structure which I have expanded below:
Command | Dtya |
Code offset | &1974 |
Minimum parameters | &00 |
Parameters which can be translated strings | &06 (i.e. 0110) |
Maximum number of parameters | &FF |
Flags | &00 (normal command) |
Syntax string offset | &4B0 "Caution!! DO NOT USE THIS COMMAND" |
Help string offset | &4B0 "Caution!! DO NOT USE THIS COMMAND" |
Command | " " (a hard space – &A0 in the Risc OS character set) |
Code offset | &570 |
Minimum parameters | &00 |
Parameters which can be translated strings | &06 (i.e. 0110) |
Maximum number of parameters | &01 |
Flags | &00 (normal command) |
Syntax string offset | &4D4 "Go Away you 'Orrible little worm" |
Help string offset | &4D4 "Go Away you 'Orrible little worm" |
Command | " " (two hard spaces – &A0 in the Risc OS character set) |
Code offset | &CFC |
Minimum parameters | &00 |
Parameters which can be translated strings | &06 (i.e. 0110) |
Maximum number of parameters | &02 |
Flags | &00 (normal command) |
Syntax string offset | &4D4 "Go Away you 'Orrible little worm" |
Help string offset | &4D4 "Go Away you 'Orrible little worm" |
SWI tables
Risc OS used the ARM facility to add routines that could be called through a SoftWare Interrupt to allow calls that could allow the modules to pretty much expand the operating system.
This module provides a SWI facility with the parameters:
SWI Base (32-bit) | &B8500 |
SWI Handler code | &2D8 |
SWI decoding table | &40 |
SWI decoding code | &00 |
The module has a predefined table of the following SWI calls. The SWI decoding table will convert a list of zero terminated names to a SWI number, starting from the SWI base, e.g. the first entry in the table is &B8500, the second is &B8501 etc.
The first entry in the table is the SWI prefix; so the module provides the following SWI call:
Reed_Disk &B8500
This can be called as Reed_Disk
, XReed_Disk
or &B8500
. The XReed_Disk
is a special variant which can return errors.
This is the all the defined header, so let’s look a bit closer at the code.
Intialisation Code
Rather than bore you trying to explain the ARM code, I’ll split it into sections.
The first part it attempts to work out whether the application whether the game is being run from a floppy disk and configures Disc1$Dir, Disc2$Dir and Disc3$Dir to point to the right place.
It will then perform a raw read of 1024 bytes from the floppy disk in drive zero at address &BCD00.
Finally it will attach itself to a number operating system vectors – these are a chain of addresses that are call when the operating system performs certain actions. Most of these are attached simply to prevent easy manipulation of the module or running files out of band whilst it is running.
It does make a mistake here, instead of attaching itself to ByteV (the OS_Byte vector) it sets the wrong register and overwrites the claim for ArgsV. (It should be MOV R0, #6 below.)
It also claims CLIV, the command line vector where it will perform the reset of doom if commands that match certain patterns are issues:
- M (presumably short of modules)
- A (don’t know)
- LOAD (load memory from a file)
- SAVE (save memory to a file)
- CAT (list a directory)
- . (alias for cat)
- DRIVE (change to another disk drive)
Finally it sets up a delayed call to the routine at &1974 to reset the computer after 1 second and make it harder to deactivate the module.
Almost every execution branch of the module has multiple errors and status checks, if these fail they go to a routine at &1974. This routine basically blanks out a load of memory, forces a full memory wipe on hard reset and then crashes the computer by forcing the program counter to an address of &00. Whilst this is annoying on an emulator it was really, really aggravating on a real computer and one of the most hated aspects, as it didn’t safely suspend the hard disk or anything. Imagine your anti virus software doing a hardware reset if it found something it didn’t like!
Where it does all its work is attaching itself to FileV which is call with the system call OS_File, which is used to perform an action on a complete file. Any action that causes a write does a system reset, but anything that loads a whole file goes into a special routine which performs a byte by byte exclusive or with the disc sectors that had been read during intialisation.
Software interrupts
The software interrupt code is really simple – it doesn’t check which interrupt is call – it just sets R0 (the return value) to &2BC and returns.
I suspect that different versions of the module have different facilities and the SWI isn’t used.
Commands
As we identified earlier, there are three defined commands.
“Dtya” has a code offset of &1974; above we mention that this is the manky reset code – and is obviously a trap for someone being too curious.
” ” (&A0 – a hard space) the first this does is disable the delayed call defined in the initialisation code, so it’s another trap to prevent attempts to run the game out of sequence. It will then load the files Code.CMP_Handle, Code.SFX_Handle and Code.Map1 so that they can be decoded by the claimed OS_File vector
Calling
But where are these commands called? How about we take another look at the !Run file, but this time look at a hexdump. And here we are, two instances of the ” ” command.
More next time when hopefully we shall totally remove the copy protection.
The final part is here.