Fun with RISC OS

FSLock

FSLock was a module designed to lock down computers in a shared environment so that they had a hope of working when somebody actually wanted to use one. It prohibited writing to the CMOS RAM and to all of the hard disc with the exception of a "Public" directory in the root directory, unless you unlocked it with a password. The existence of the Public directory was well-known, and anything stored there was liable to be read and/or deleted by any random luser who came along. Naturally, this didn't appeal to me at all...

The fundamental problem with FSLock was that RISC OS wasn't designed with security in mind. There was pretty much no memory protection, and access to supervisor mode (where what protections did exist were overridden) was just a system call away.

That said, the authors of FSLock had clearly put some effort into making it hard to bypass: the module itself was in ROM (hence unpatchable), and unkillable. I wasn't deterred, though.

<Wimp$ScrapDir>

First of all, it turned out that the Public directory, contrary to popular belief, wasn't the only writable location on the hard disc. The Scrap directory, found in the obscure location "$.!Boot.Resources.!Scrap.ScrapDir", was also writable, for use as temporary storage by apps. It also wasn't cleaned out automatically, as apps were trusted to clean up after themselves, so it made a perfect location to store stuff out of public scrutiny.

Messages

Both the strings "$.Public" and "$.!Boot.Resources.!Scrap.ScrapDir" weren't actually stored within FSLock itself, but in a separate Messages file, provided by a module of the same name, and also in ROM. Presumably this was to allow easy translation of the pathnames should machines ever end up in foreign climes where people don't know what "Public" means. But it also provided a handy loophole: Messages wasn't protected, so it could be copied to RAM, the copy patched to read "$" instead, and voilà: full write access. But the Messages module was over 200K. Ouch. (Also, this didn't grant access to the CMOS RAM.)

ROMPatch

Digging further into RISC OS's boot sequence uncovered a little utility called ROMPatch, apparently written to fix up all those pesky bugs that surfaced between the ROM chips going into production and the actual shipping date. There were several dozen words in ROM that were patched; I never figured out what they did, but not to worry: I could just add my patch along with the rest. (I never figured out how ROMPatch itself worked, either. Unfortunately, I had very little in the way of documentation, and there was definitely some deep magic going on here.) This worked great, except that I didn't necessarily want to have FSLock patched at boot time, and the ROM couldn't be patched twice. The ugly solution I found that allowed the creation of a standalone utility was to remove the original ROM patches just before installing the new ones, and assume that they matched. Messy.

RMReInit

I was perplexed by the fact that, despite being unkillable, disassembly of FSLock revealed that it nonetheless contained code to clean up after itself and remove the hooks it had inserted in the OS. (Despite not understanding its existence, I'd used this code for the previous ROMPatch exploit, so knew it worked.)

Further investigation revealed that there was more than one type of killing. The conventional, permanent way to kill a module (RMKill) just returned an error with FSLock. But there were other ways to kill a module temporarily. It could be reinitialized with RMReInit, which involved a complete shutdown and restart; surprisingly, FSLock allowed itself to be reinitialized without protest. This didn't help with disabling it of course, but was definitely food for thought.

Eventually I discovered that, when calling a module's shutdown code, the OS provides a flag to indicate whether the shutdown is permanent or temporary. A temporary shutdown indicates a promise on the part of the OS that the module will be restarted "soon". RMTidy, the command to reinitialize all the modules, moved modules about before restarting them. And, it finally dawned on me that the reason that FSLock allowed itself to be reinitialized (despite being in ROM and thus not going anywhere) was that RMTidy required the cooperation of every single module in order to be useful.

Once I understood what was going on, it was easy to pretend to be the OS and issue a "temporary" shutdown that was more permanent than FSLock was expecting. Having detached itself, FSLock was never called again and was thus unaware of the subterfuge. This was the ultimate solution I came up with to disabling FSLock: non-invasive (only standard OS interfaces are used, no patching required, thus version-independent), not easily fixable (the temporary shutdown mechanism combined with the unrestricted access to supervisor mode that allowed spoofing were part of the design of the OS and unlikely to be changed), and small (I'm pretty sure I had a version under 100 bytes at one point, although it hasn't survived).

Other approaches

Although I solved the immediate problem to my satisfaction, there were a number of other possible approaches that occurred to me later.

It would be possible to walk the vector chains (they were stored as singly-linked lists of {code, workspace} pairs starting from some location I've forgotten near the beginning of RAM) looking for pointers to locations within FSLock and excising them from the list. This would work even if FSLock's finalization code were removed.

FSLock didn't intercept low-level disc operations such as sector writes. This makes some sense, as there really was no way for it to know what locations on disc would be safe to write to. It didn't even try to verify that the caller was some presumably-trusted routine in ROM, however: thus, it was possible to perform arbitrary hard disc writes (including formatting) providing they didn't involve the filing system.

I didn't even look at how passwords were stored and verified. There were easier ways.

Speculations

It seems Acorn had some degree of confidence in the protections that FSLock provided, as apparently they thoughtfully provided a jumper on the motherboard for disabling it in the event that the password was lost. (And, for completeness, the ability to padlock the case to restrict access to the motherboard.)

Why did they bother? Presumably they knew about the weaknesses. Or would providing software to bypass it have been a tacit admission that it wasn't very secure after all?

Code

FSUnlock version 1 [1995-05-23], for RISC OS 3.5 only
SYS"XOS_CLI","RMFaster Messages"
SYS&1E,&12,"Messages"TO,,,pos%
pos%?&1D76B=10
*RMReInit Messages
FSUnlock version 2 (based on ROMPatch) [1995-06-29], for RISC OS 3.5 only
[reams of incredibly evil, obscure, probably copyrighted code elided]
FSUnlock version 3 [circa 1997]
DIM code%256
SYS"OS_Module",18,"FSLock"TO,,,ptr%,work%
FOR I%=0 TO2 STEP 2
P%=code%
[OPT I%
stmfd r13!,{r14}
swi"OS_EnterOS"
mov r10,#0
adr r12,bucket%
bl (ptr%+ptr%!8)
teqp pc,#0
mov r0,r0
ldmfd r13!,{pc}
.bucket%
equd work%
]NEXT
CALL code%