This commit is contained in:
Lenni Hein 2025-02-20 18:23:14 +01:00
commit 5b881c5cde
14 changed files with 17006 additions and 0 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
.obsidian
.direnv
\.idea/
cmake-build-debug/
cmake-build-release/
flush_flush/histogram/ff/calibration
flush_flush/histogram/ff/file

89
CacheAttacks Bootcamp.md Normal file
View File

@ -0,0 +1,89 @@
## Flush+Reload Covert Channel
### 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
```
##### [Muster](./src/cache_test.c)
### Block 2: Signale über den Cache
`sharedlib.c` sieht so aus:
```C
...
```
Das kann man so kompilieren:
```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
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 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

27
flake.lock generated Normal file
View File

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1739580444,
"narHash": "sha256-+/bSz4EAVbqz8/HsIGLroF8aNaO8bLRL7WfACN+24g4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8bb37161a0488b89830168b81c48aed11569cb93",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

25
flake.nix Normal file
View File

@ -0,0 +1,25 @@
{
description = "Development environment with CMake, GCC, Clang, Python, Matplotlib, and Scikit-learn";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
devShells.${system}.default = pkgs.mkShell {
buildInputs = [
pkgs.cmake
pkgs.gcc
pkgs.clang
pkgs.python3
pkgs.python3Packages.matplotlib
pkgs.python3Packages.scikit-learn
pkgs.python3Packages.numpy
];
};
};
}

21
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.10)
project(cache_attacks C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "-O0" CACHE STRING "C Compiler flags" FORCE)
set(CMAKE_CXX_FLAGS "-O0" CACHE STRING "C++ Compiler flags" FORCE)
# Add shared library
add_library(sharedlib SHARED sharedlib.c sharedlib.h)
# Add the sender executable
add_executable(send send.c lib.c lib.h)
target_link_libraries(send sharedlib)
# Add the receiver executable
add_executable(recv recv.c lib.c lib.h)
target_link_libraries(recv sharedlib)
# add the cache-test executable
add_executable(cache_test cache_test.c lib.c lib.h)

53
src/cache_test.c Normal file
View File

@ -0,0 +1,53 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define NUM_ACCESSES 1000000
extern void flush(void* addr);
extern void maccess(void* addr);
extern uint64_t time_maccess(void* addr);
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses) {
for (size_t i = 0; i < num_accesses; i++) {
flush((void*) addr);
misses[i] = time_maccess(addr);
flush((void*) addr);
maccess((void*) addr);
hits[i] = time_maccess(addr);
}
}
size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses) {
size_t hit_sum = 0, miss_sum = 0;
for (size_t i = 0; i < num_accesses; i++) {
hit_sum += hits[i];
miss_sum += misses[i];
}
size_t avg_hit = hit_sum / num_accesses;
size_t avg_miss = miss_sum / num_accesses;
return (avg_hit + avg_miss) / 2; // Midpoint as threshold
}
int main() {
size_t* hits = malloc(NUM_ACCESSES * sizeof(size_t));
size_t* misses = malloc(NUM_ACCESSES * sizeof(size_t));
if (!hits || !misses) {
perror("Memory allocation failed");
return 1;
}
measure_multiple_accesses((void*)maccess, NUM_ACCESSES, hits, misses);
size_t threshold = find_crossover_point(hits, misses, NUM_ACCESSES);
printf("Suggested threshold: %zu\n", threshold);
free(hits);
free(misses);
return 0;
}

59
src/lib.c Normal file
View File

@ -0,0 +1,59 @@
#include "lib.h"
size_t time_flush(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 ("CLFLUSH 0(%0)\n":: "c" (addr): "rax");
asm volatile ("MFENCE");
asm volatile ("RDTSC": "=a" (lo), "=d" (hi));
end = (hi<<32) | lo;
asm volatile ("LFENCE");
delta = end - start;
return delta;
}
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;
}
void maccess(void* p)
{
asm volatile ("movq (%0), %%rax\n"
:
: "c" (p)
: "rax");
}
void flush(void* p)
{
asm volatile ("clflush 0(%0)\n"
:
: "c" (p)
: "rax");
}

9
src/lib.h Normal file
View File

@ -0,0 +1,9 @@
# pragma once
#include <stddef.h> // For size_t
#include <stdint.h> // For uint64_t
void maccess(void* p);
void flush(void* p);
size_t time_maccess(void (*addr)(void));
size_t time_flush(void (*addr)(void));

27
src/mksharedlib.py Normal file
View File

@ -0,0 +1,27 @@
import os
def generate_nop_function(filename="sharedlib.c"):
with open(filename, "w") as f:
f.write("#include \"sharedlib.h\"\n\n")
f.write("void padding_before(void) { asm volatile (\n")
for _ in range(64):
f.write(" \"nop\\n\\t\"\n")
f.write(" :::\n); }\n\n")
f.write("void function(void)\n{")
f.write(" asm volatile (\n")
size = 64 * 256
for _ in range(size):
f.write(" \"nop\\n\\t\"\n")
f.write(" :::\n );\n}")
f.write("\n\nvoid padding_after(void) { asm volatile (\n")
for _ in range(64):
f.write(" \"nop\\n\\t\"\n")
f.write(" :::\n); }\n")
if __name__ == "__main__":
generate_nop_function()

87
src/recv.c Normal file
View File

@ -0,0 +1,87 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include "lib.h"
#include "sharedlib.h"
#define BITS 8
#define BUFFER_SIZE 256
volatile uint8_t buffer[BUFFER_SIZE];
volatile size_t byte_index = 0;
volatile int sample_interval = 100; // Default: 100 µs
volatile int threshold = 245; // Default: 245 cycles
void signal_handler(int signo) {
if (signo == SIGALRM) {
if (byte_index >= BUFFER_SIZE) {
ualarm(0, 0); // Stop sampling
printf("Buffer full. Stopping capture.\n");
return;
}
uint8_t byte = 0;
for (int i = 0; i < BITS; i++) {
size_t time = time_maccess((void *) function + (i << 9));
if (time < threshold) {
byte |= (1 << i);
}
}
// flush the cache
for (int i = 0; i < BITS; i++) {
flush((void *) function + (i << 9));
}
buffer[byte_index++] = byte; // Store received byte
}
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <sample_interval> <threshold>\n", argv[0]);
return 1;
}
sample_interval = atoi(argv[1]);
threshold = atoi(argv[2]);
printf("Receiver started with sample interval: %d µs, threshold: %d cycles\n", sample_interval, threshold);
for (int i = 0; i < BITS; i++) {
flush((void *) function + (i << 9));
}
signal(SIGALRM, signal_handler);
ualarm(sample_interval, sample_interval);
while (byte_index < BUFFER_SIZE) {
pause();
}
// Print received bytes in hex
printf("Received %zu bytes:\n", byte_index);
for (size_t i = 0; i < byte_index; i++) {
printf("%02X ", buffer[i]);
}
printf("\n");
// Print as ASCII characters
printf("ASCII Output:\n");
for (size_t i = 0; i < byte_index; i++) {
//if (buffer[i] >= 32 && buffer[i] <= 126) // Printable ASCII
if (isalnum(buffer[i]))
{
printf("%c", buffer[i]);
} else
{
printf("."); // Placeholder for non-printables
}
}
printf("\n");
return 0;
}

59
src/send.c Normal file
View File

@ -0,0 +1,59 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include "lib.h"
#include "sharedlib.h"
#define BITS 8
volatile uint8_t *buffer = NULL;
volatile size_t buffer_len = 0;
volatile size_t current_index = 0;
volatile uint8_t byte_to_send = 0;
void signal_handler(int signo) {
if (signo == SIGALRM) {
if (current_index < buffer_len)
{
byte_to_send = buffer[current_index++];
} else
{
current_index = 0;
}
}
}
int main(int argc, char *argv[]) {
if (argc < 3)
{
fprintf(stderr, "Usage: %s <string> <sample_interval>\n", argv[0]);
return 1;
}
buffer = (uint8_t *) argv[1];
buffer_len = strlen(argv[1]);
int sample_interval = atoi(argv[2]);
signal(SIGALRM, signal_handler);
ualarm(sample_interval, sample_interval);
while (1)
{
for (int i = 0; i < BITS; i++)
{
if (byte_to_send & (1 << i))
{
maccess((void *) function + (i<<9));
}
else
{
flush((void *) function + (i<<9));
}
}
}
return 0;
}

16527
src/sharedlib.c Normal file

File diff suppressed because it is too large Load Diff

9
src/sharedlib.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef SHAREDLIB_H
#define SHAREDLIB_H
// Function declarations
void padding_before(void);
void function(void);
void padding_after(void);
#endif // SHAREDLIB_H