CS270 Recitation 9.2
LC-3 Hacking

Goals

  1. To extend your knowledge of LC-3 by extending its operating system.
  2. To show the basics of a stack based buffer overflow attack.

Setup

Make a subdirectory called R9.2 for the recitation, all files should reside in this subdirectory. Copy geth.asm and overflow.asm to the R9.2 directory. Now follow the directions here to save, extract, and build the linux tar file that contains a LC3 simulator and assembler. The tar file should be saved to your R9.2 directory.

Extending the LC3 OS

Once the install is completed you can now open the lc3os.asm file and see the LC3s operating system assembly code. Take some time to look through the code and get a feel for how the TRAP instructions work. You will notice that the addresses near x25 contain the address of the code that executes the corresponding TRAP subroutine. The file lc3os.asm is set to read only so before you can modify it you will need to change it to allow writing. This can be done with the command
 chmod 755 lc3os.asm
Now it is time to take the code in geth.asm and splice it into the lc3os.asm file (copy and paste with some slight modifications to the existing lc3os.asm code). Make sure you add a link to the newly added GETH code in the TRAP table (addresses x0000- x00FF). The link in the trap table can be put at any address but overflow.asm expects it to be at location x28, so I would suggest using location x28.

Once this is complete use the assembler found in the same lc3CSU folder as lc3os.asm to assemble the modified operating system. The purpose of the new GETH TRAP subroutine is to read hexadecimal input from the user and pack the corresponding bits into 16 bit LC3 words. This is similar to GETC, except the TRAP subroutine expects R0 to be the address of a buffer that can contain more that one word. The TRAP subroutine will keep processing hex characters in multiples of four characters until the enter key is pressed and signals the end of input in the buffer by appending a NULL word. Currently only 0-9 and uppercase A-F are accepted.

Executing the buffer overflow

Now its time to take a look at overflow.asm currently there are a few lines that can be changed but you should leave them unchanged until you are familiar with how the code works. By convention we have been using x3000 for a starting address for our programs and x4000 as the starting address of the stack but you may want change these to make keeping a eye on the OS, user program, and stack possible with minimal scrolling. For this recitation the default starting location of the stack is x3037.

The basic idea of overflow.asm is the Main function calls ProcessHexData which allocates a buffer on the stack, then calls the GETH TRAP to fill the buffer. If the user enters and amount of data equal to or less than the buffer size everything works normally and the function PrivateFunction will never be called. However there are no size checks associated with the buffer that GETH receives and so a clever user can exploit this to access PrivateFunction.

Try a variation

Once you have figured out how to call PrivateFunction using the buffer overflow technique, you can experiment by calling other OS functions/subroutines, and modifying the code to cause overflows of buffers in other locations besides the stack.

Bonus Challenge

This one is a lot of work, but results in a lot of flexibility in terms of what you can do. By modifying the current function's return address on the stack, you can get the PC to go anywhere you want it to. If you set the return address to a function that you write using the buffer overflow, then you can execute any code you want. However, you would need to convert each instruction to it's equivalent hex form, and then input that into the TRAP. It would be best to prepare a file with your input and use redirection to pass this into the simulator so you can repeat/modify it as needed.