## Flush+Reload Covert Channel ### Primitive in `./src/lib.c` sind folgende Primitive: `time_maccess` misst wie lange der Zugriff auf die Speicheradresse `addr` dauert. ```C size_t time_maccess(void (* addr)(void)) { uint64_t start, end, delta; uint64_t lo, hi; asm volatile ("LFENCE"); asm volatile ("RDTSC": "=a" (lo), "=d" (hi)); start = (hi<<32) | lo; asm volatile ("LFENCE"); asm volatile ("movq (%0), %%rax\n" : : "c" (addr) : "rax"); asm volatile ("LFENCE"); asm volatile ("RDTSC": "=a" (lo), "=d" (hi)); end = (hi<<32) | lo; asm volatile ("LFENCE"); delta = end - start; return delta; } ``` `maccess` greift auf diese Speicheradresse `addr` zu. ```C void maccess(void* addr) { asm volatile ("movq (%0), %%rax\n" : : "c" (addr) : "rax"); } ``` `flush` verdrängt die Speicheradresse `addr` aus dem Cache. ```C void flush(void* addr) { asm volatile ("clflush 0(%0)\n" : : "c" (addr) : "rax"); } ``` ### Block 1: Messen der Timing-Differenzen + Threshold bestimmen ##### Tips - am besten kompiliert man ohne Compileroptimierungen (`-O0`): ```bash gcc -O0 cache_test.c primitive.c -o cache_test ``` - CMake ist praktisch! siehe `./src/CMakeList.txt`. Verwenden durch `cmake .` und dann `make`. Muster: `./src/cache_test.c` ### Block 2: Signale über den Cache `sharedlib.c` sieht so aus: ```C void function(void) { asm volatile ( "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ... ``` Die Adresse von `function` kann also zum Flushen und Reloaded verwendet werden. Da immer eine gesamte Cacheline geladen wird, sollte sich nichts anderes in dem selben 64Byte Block befinden. Die Funktionen `padding_before` und `padding_after` sind beide 64 Byte groß, also eine Cacheline. Damit kann dann auch gar nichts mehr schief gehen :) `sharedlib.c` kann man so kompilieren und linken: ```bash # sharedlib kompilieren gcc -fPIC -shared -O0 -o libsharedlib.so sharedlib.c # sharedlib bei Sender und Empfänger linken gcc -O0 sender.c primitive.c -L. -lsharedlib -o sender gcc -O0 empfaenger.c primitive.c -L. -lsharedlib -o empfaenger # ggfs. muss man noch den PATH exportieren export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ``` dann kann man `function` verwenden: ```C # in e.g. sender.c maccess((void *) function) ... ``` ##### Tips - `taskset` zum pinnen der Programme auf einen bestimmten CPU-Kern. - Verwendet man blockierenden Sleep um zu Takten, z.B. `clock_nanosleep`, dann sollte man unbedingt darauf achten nicht eine Periodendauer zu schlafen, sondern nur bis zum nächsten Zeitabschnitt. Einfacher geht es mit nicht-blockierendem Sleep, z.B. `ualarm`. - Der Zeitabstand von Flush zu Reload sollte MAXIMIERT werden! Dies ist das Zeitfenster, in welchem der Sender die Adresse(n) zurück in den Cache laden kann. - (Der Sender kann das Flushen übernehmen) - (Der Sender kann im gesamten Zeitabschnitt Flushen oder Laden, nicht nur einmal) ### Block 3: Pakete über den Cache als Medium - Kann man irgendwie mehr als ein Bit gleichzeitig senden? ;) Muster: `./src/sender.c` und `./src/receiver.c`