118 lines
3.2 KiB
Markdown
118 lines
3.2 KiB
Markdown
## 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` |