Complex Lab Systems Programming
Lecturer | Prof. Dr.-Ing. Horst Schirmeier, Dr. Michael Roitzsch |
Module | INF-04-KP, INF-DSE-20-E-ADSE, INF-DSE-20-M-INT, INF-E-4, INF-MA-PR, INF-VERT4, IST-05-FG-AVS, MINF-04-KP-FG2 (INF-BAS4 possible, too) |
Scope and Type |
4 SWS Lab (English) |
Cycle | Block event in winter semester (2022-09-22 to 30, 09:30-16:00) |
Enrollment | via jExam (limited number of participants), the lab is listed under summer semester 2022 |
Mailing List | |
Feedback | via email or our anonymous mailbox |
The course consists of two parts: a workshop taking place before the beginning of the winter term (2022-09-22 to 30) and a practical part where you work individually on four assignments.
Systems Programming Workshop
This workshop teaches advanced skills in programming on the system level. Attendance is especially advised for students wanting to brush up their programming skills before taking one of our hands-on courses like Operating-System Construction, Microkernel-Based Operating Systems, Microkernel Construction, or the Complex Lab Microkernel-Based Operating Systems.
This is not a programming course for beginners. We want to teach you advanced skills and thus will expect the following:
- You have programming experience, for example in Java or Python.
- You know how to work on a Linux or Unix command line.
- You have seen a manpage.
- You can work with a text editor.
- You can use Google.
If you say yes to all of these points, this course is for you. The training enhances your C++ programming skills in areas such as compiler and tool chain, building a library, debugging, assembler, multi-threading, POSIX, and system calls.
Slides
Will be published during the workshop.
- Day 1 – C++ (source files)
- Day 2 – Assembler
- Day 3 – Tools
- Day 4 – Rust
- Day 5 – Threads
- Day 6 - Debugging
- Day 7 – No LibC
Practical Part
The following four assignments can be solved individually at home. Each assignment has an associated tutor, whom you can ask questions and send your solution by mail. Please only send relevant (text-) files, or a tar-archive of multiple of these.
Please follow the scheme <s-number>_task<x>.tar.bz2 for naming the file. This makes life easier for the tutor.
Tutor: Martin Küttler
This exercise is about using GNU/Make to automate compiling and linking C files into an application.
-
Write a C application sincos that prints a table of sin/cos values for angles between 0 and 360 degrees. The application should receive exactly one argument which is the number of steps to use for printing.
A sample session with your program might look like this:
$> ./sincos 10
0.000 0.000 1.000
36.000 0.588 0.809
72.000 0.951 0.309
108.000 0.951 -0.309
144.000 0.588 -0.809
180.000 -0.000 -1.000
216.000 -0.588 -0.809
252.000 -0.951 -0.309
288.000 -0.951 0.309
324.000 -0.588 0.809
360.000 0.000 1.000
Your application shall consist of two C files:-
main.c: This file contains the main method of your application, checks the correctness of the command line parameter and then calls a function provided by the second file in order to perform real actions.
-
sincos.c: This file provides a function that iterates over the range between 0 and 360 degrees using the steps provided by the user. For each iteration it prints the current degree value as well as its sine and cosine.
-
The interface provided by sincos.c shall be defined in a header file that is included by the main application.
-
Write a Makefile to automate your application's build. The Makefile shall consist of two rules:
-
A rule to create the program from your two object files and
-
A clean rule to remove all generated files.
-
Dependency files should be created and included automatically by the Makefile.
Furthermore, the program shall be compiled with the compiler flags -Wall and -Wextra and compilation should not emit any warnings.
- Compile and link the application using GNU/Make.
- Test the correctness of your dependencies and file generation. The reference computer for this purpose is the FRZ's Ganymed.
Tutor: Carsten Weinhold
The goal of this exercise is to familiarize yourself with the basic operations and data structures of the UNIX file system.
The UNIX utility find searches the file system for files that meet certain requirements. Your task is to implement a small find utility that accepts the following command-line syntax:
find <directory name> [-name <pattern>] [-type <f | d>] [-follow] [-xdev]
Your program should print a filename if the corresponding file or directory matches all the constraints specified on the command line (if any). Using the -type switch, the user can specify that either regular files or directories will match, but not both. The option -follow shall tell your program to follow symbolic links. The option -xdev specifies that 'find' shall not search directories whose contents are located in another file system (e.g., another disk partition). The option -name shall accept wildcards as explained in the following manpage excerpt:
A string is a wildcard pattern if it contains one of the characters `?', `*' or
`['. Globbing is the operation that expands a wildcard pattern into the list of
pathnames matching the pattern. Matching is defined by:
A `?' (not between brackets) matches any single character.
A `*' (not between brackets) matches any string, including the empty string.
...
(see also glob(7))
If you are in doubt about what the exact behavior of your tool shall be for the various options, see what GNU find outputs in your test environment (comes with your Linux distribution). Reading the GNU find manpage is also a good idea. You might find the functions readdir(), stat(), and fnmatch() or glob() to be useful when you develop the functionality to traverse directory trees. Note that it is not allowed use existing directory tree walkers such nftw() or ftw(), instead you are expected to build this functionality yourself.
A complete solution of this exercise consists of a tarball containing C or C++ source code and a Makefile that builds your program from these sources. Please make sure that you test your program thoroughly and any debugging output is disabled before you submit it. We look at the source code, but we primarily use automated tests to verify the correctness of your find tool. Therefore, your program should print all matching pathnames like, GNU find does. Don't use output formats such as "found file: /path/to/file" or "found match: type=d, /path/to/dir"!
Important: To get good test coverage, you should download this tarball, which contains a shell script for creating a directory hierarchy for testing your find program. It is particularly useful for making sure that your implementation of the '-follow' option behaves in sensible way, like for example GNU find does! Also, see the included README for details on how to test the '-xdev' option.
Resources
Tutor: Martin Küttler
In this exercise you are going to write a simple shell that is able to execute command lines, interpret pipes and use input/and output redirection. You may use Flex and Bison to specify a parser for your command lines.
First, implement a shell with the following required features:
- Execute command lines with the following syntax:
command <args> [< input_redirect] (| command <args>)* [> output_redirect] [&]
As a starting point you may consider the files provided in the resources section below. - Support an arbitrary amount of arguments to programs as well as an arbitrary length for the pipe chain.
- Detect erroneous input and give useful feedback to the user.
Second, add convenience features to your shell:
- The readline library provides line editing features, such as deleting a word. libreadline comes along with libhistory which provides means to store strings in a list and navigate through this list. Use both libraries in order to implement easy-to-use line editing as well as access to previously entered commands using the arrow keys.
- Add support for the following shell builtins to your shell
- cd to change to another working directory
- pwd to print the current working directory
- kill <signo> <pid> to send a signal to a process
- alias and unalias to manage aliases (which are replaced by by their meaning during execution of commands).
Resources
- Flex and Bison are successors to Lex and Yacc, so all relevant information for Lex and Yacc is also useful for these tools.
- Manual for the Flex scanner generator
- Manual for the Bison parser generator
- Template Flex input file
- Template Bison input file
- The GNU readline library
- When implementing builtins you might have a look at the BASH reference manual.
- For executing processes, have a look at the man pages for fork(), execve(), and wait().
- For input and output redirection, take a look at open(), close(), and dup2().
- Pipes are created by pipe(), signal handling can be set up using sigaction().
Tutor: Jan Bierbaum
In this exercise you will learn to apply widely used debugging tools, namely, gdb and objdump while reverse engineering and exploiting a very simple and vulnerable login program.
In Unix systems, a login program is normally invoked by some login shell (like getty) together with a user-name. The login executable is often owned by the superuser and its SUID bit is set. Therefore, from an attacker's point of view, a vulnerable login program means potential root access to the system.
You have to exploit a simplified version of a login program, which waits for a password and, depending on the given string, will return a success or failure message. Note, that the given executable is an x86 ELF binary. You may need to set up support for running 32-bit binaries in your system. In case your machine cannot natively run x86 code, you can work remotely on ganymed.inf.tu-dresden.de – that machine is not reachable from the Internet, so you have to tunnel through the university's login server. If you need help with the SSH setup, ask the tutor.
Your first objective is to simply extract the password by analysing the binary with objdump or gdb. Describe how to extract the password from the binary by sending the output of a terminal session, where you use objdump and/or gdb to extract it. In addition to that, please send the plain password.
Next, exploit the buffer overflow vulnerability of this binary. By choosing an appropriated input you are able to skip the password check and force the program to jump directly to printing the success message. To do so, answer the following questions:
- How does gcc translate function calls to assembly code?
- Which Standard C-Library functions are vulnerable to buffer overflows?
- How to use this knowledge to overwrite the return address of a function?
Write a short program in C, C++, or Rust that takes an address of 8 hex-digits as input and produces a string as output. When using that string as input for the login program, it should directly jump to the given hex address. Additionally, send the hex-address you would use to circumvent the authentication check.
An example session using your resulting program should look similar to this one (replacing 00000000 with the correct value of course):
$> ./cracker 00000000 | ./simple_login
Enter the correct password:
Successful login! Now, we would execute a shell ...
When simply overwriting the return address of the password authentication function, you will experience problems (segmentation fault). Explain why this happens. How could you avoid this in theory? (Optional: Extend your implementation so that the generated string does not result in a segmentation fault when fed into simple_login.) What practical countermeasures of today's UNIX systems complicate such buffer overflow attacks?
Resources
Recommended Links
- Reference for C++ and the Standard Template Library
- Tutorial on using pointers in C
- PThreads Tutorial
- Coding Styles
- C++ standard
Organization
- The workshop takes place before the beginning of the winter term.
- There will be a one hour lunch break and additional breaks according to demand and time.
- Registration for the course works by enrolling via jExam.
- If fewer than 10 students enroll, the course will unfortunately be canceled.
- The practical assignments are meant to be solved individually after the workshop concludes. Solutions are handed in with the respective tutor.