Overview

In this lab we will:

Before starting

To make things easy on ourselves getting started we will disable address space randomization. We will talk more about this later in the course but for now it is enough to know that ASR is a kernel feature designed to confuse attacks. On the lab machines we can disable and enable it as follows:

$ disable_asr
$ enable_asr

Disable it now. Note these commands I have added to /usr/local/bin so if you are using your laptop then you would enter:

$ sudo /sbin/sysctl kernel.randomize_va_space=0
$ sudo /sbin/sysctl kernel.randomize_va_space=1

A. Process layout in memory

Take a copy of the C source code in program.c. Build an executable as follows:

$ gcc -g -static -mpreferred-stack-boundary=2 -o program program.c

Sketch an outline of the virtual address space occupied by the process image, it should stretch from 0xFFFFFFFF at the top to 0x00000000 at the bottom. Using gdb, ldd and readelf to help you, locate (as best you can since in some cases it may not be possible to be precise) and mark the following on your diagram:

Here is an example gdb session which you might find useful in getting started. For extra help, you can also reference the gdb manual from here, download a gdb quick reference sheet or use the online gdb help. When you have finished the above, recompile the program to build a dynamically linked executable (just omit the -static flag). Where are the shared libraries mapped to? How are calls to the functions in libc (e.g. strcpy, malloc etc.) implemented?

B. Stack layout in memory

Set a breakpoint on the swap function in your program. Draw a diagram of the stack at the time the breakpoint is encountered. Your diagram should include arguments passed by the caller, the return address, the caller's frame pointer, the current frame pointer, local variables and any saved registers.

C. Disassembling code

Disassemble the sumup function and annotate each line with a description of what it does.

D. Rewriting a return address

Rather than having control return to the main function, add C code to the swap function such that the foo function is executed when swap returns. Hint: Declare a new pointer variable in swap and set it pointing to some other local variable; add an offset to it in order to have it point at the return address on the stack. Overwrite the return address with the address of the foo function. Buffer overflow attacks work in a similar way.

E. Dynamic linking

Take a copy of the C source code in linking.c. Build a dynamically linked executable as follows:

$ gcc -g -o linking linking.c

Disassemble main and look at where puts is called. The address jumped to is not in the C shared library - where is it? You should find it is in a section called the PLT - the procedure linkage table. The PLT plays a crucial role in supporting dynamic linking. We can use objdump to take a closer look at its contents. When we do we see that for each external function called there is a corresponding entry in the PLT. The PLT code does an indirect jump through an entry in the GOT - the global offset table. The GOT also plays a crucial role in supporting dynamic linking. If we look inside the GOT entry we see that initially the address jumped to is back in the PLT. However, this code invokes the dynamic linker which locates puts in the C shared library and updates the GOT entry to point directly to it. From then on every call to puts goes through the PLT and GOT to the code in the shared library. Get it? Here is an example. As we'll see in later labs, function pointers in the GOT are often targetted by exploits.