Komplexpraktikum Systemnahe Programmierung
Dozent | Dr. Michael Roitzsch, Dipl.-Math. Martin Küttler |
Modul | 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 (Einbringen in INF-BAS4 u.U. möglich) |
Umfang und Art |
4 SWS Praktikum (Englisch) |
Turnus | Blockveranstaltung im Wintersemester (22.-30.09.2022, 09:30-16:00) |
Anmeldung | Einschreibung in jExam notwendig (begrenzte Teilnehmerzahl), die Veranstaltung ist dem Sommersemester 2022 zugeordnet |
Mailingliste | Bitte mit einer TU-Dresden-Adresse einschreiben |
Feedback | per E-Mail oder anonymen Briefkasten |
Das Praktikum besteht aus zwei Teilen: einem Workshop, welcher vor Beginn des Wintersemesters stattfindet (22.-30.09.2022), und einem praktischen Teil aus vier Aufgaben, welcher im Anschluss individuell bearbeitet wird.
Workshop systemnahe Programmierung
Der Workshop vermittelt fortgeschrittene Fertigkeiten in der systemnahen Programmierung. Er dient deshalb auch der Vorbereitung auf Programmier-Aufgaben in den praktischen Veranstaltungen der Professur für Betriebssysteme wie Betriebssystembau, Mikrokernbasierte Betriebssysteme, Mikrokernkonstruktion oder dem Komplexpraktikum mikrokernbasierte Betriebssysteme.
Es handelt sich hier nicht um einen Programmierkurs für Anfänger. Wir wollen euch fortgeschrittene Fertigkeiten vermitteln und setzen daher voraus:
- Ihr habt Programmier-Erfahrungen, zum Beispiel in Java oder Python.
- Ihr wisst, wie man in einer Linux- bzw. Unix-Kommandozeile arbeitet.
- Ihr habt schon mal eine Man-Page gesehen.
- Ihr könnt mit einem Texteditor arbeiten.
- Ihr könnt Google bedienen.
Wenn ihr alle diese Punkte bejahen könnt, ist dieser Kurs für euch. Der Workshop vertieft eure Fähigkeiten der C++ Programmierung Gebieten wie Compiler und Werkzeuge, Bauen von Bibliotheken, Debugging, Assembler, Multithreading, POSIX und Systemcalls.
Folien (nur auf Englisch)
Werden im Laufe des Workshops veröffentlicht.
- Day 1 – C++ (source files)
- Day 2 – Assembler
- Day 3 – Tools
- Day 4 – Rust
- Day 5 – Threads
- Day 6 - Debugging
- Day 7 – No LibC
Praktischer Teil
Die vier gestellten Aufgaben können individuell zu Hause bearbeitet werden. Für jede Aufgabe gibt es einen Ansprechpartner, dem Fragen gestellt werden können, und bei dem die Lösung per E-Mail einzureichen ist. Der E-Mail sollen bitte nur die nötigen (Text-) Dateien angehängt werden, ggf. in einem tar-Archiv.
Bitte benutzen Sie das Schema <s-Nummer>_task<x>.tar.bz2 für die Benennung der Datei. Das macht dem Tutor das Leben einfacher.
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
Weiterführende Links
- C++/Standard Template Libraray – Referenz
- Tutorial über Zeiger in C
- PThreads-Tutorial
- Coding Styles
- C++-Standard
Organisation
- Der Workshop findet vor Beginn des Wintersemesters statt.
- Es gibt eine einstündige Mittagspause und weitere Pausen nach Bedarf und Zeit.
- Die Anmeldung für das Praktikum läuft per Einschreibung in jExam.
- Bei weniger als 10 Einschreibungen wird der Kurs leider nicht stattfinden.
- Die praktischen Aufgaben werden im Anschluss individuell bearbeitet und die Lösungen per Mail beim jeweiligen Ansprechpartner eingereicht.