This repository contains the source code for RCECoaster, an exploit for Rollercoaster Tycoon 1999. See docs for more info about the exploit.
The exploit heavily relies on fixed offsets, both for the initial RIP control and further staging. The exploit will only work on the specific version of the game it was written for, namely https://archive.org/details/Roller_Coaster_Tycoon_Hasbro_Interactive_1999 .
The default demo grabs the relevant handles to the screen buffer and replaces the current thread with Doom.
For more details about the bug, refer to the writeup.
In order to build the exploit from source, you will need a Linux environment with a Rust compiler and i686-w64-mingw32-gcc to compile the Doom demo.
To build everything in one go, run the build.sh script:
./build.sh <output file>
In order to run the Doom demo, you will need to place a doom1.wad file in the same directory as the crafted roller coaster tycoon save file. You can obtain a copy of doom1.wad here: https://doomwiki.org/wiki/DOOM1.WAD
The source code for the initial stage can be found in exploit/implant/loader. The loader assumes that the next stage will be appended to the save file, after the "legit" save game data. Stage 2 (exploit/implant/stage2) reads all auxiliary data from the save file, maps it into memory, and jumps to it.
This removes any limitations on the size of the final-stage shellcode.
The Doom port is based on the Windows port provided by doomgeneric. The Windows port already uses the same rendering library as Rollercoaster Tycoon, so we can just pass handles to the window to doomgeneric's render handler.
All calls to the standard library have been replaced with shellcode_shim.c, which uses PEB walking to resolve functions at runtime. Doom is compiled with MinGW into a single shellcode blob.
The area of memory containing the initial shellcode is not fully "stable". In the sense that the game will start writing to the second DWORD of the shellcode after the initial frame is rendered. Therefore things like resizing the screen can cause the exploit to fail.
This can potentially be fixed by adding another layer of indirection, by jumping to a different part of the save file within the first 4 bytes of shellcode. But at this point I've already spent way too much time on this project, so I'll leave that as an exercise to the reader.
