init
This commit is contained in:
commit
5b881c5cde
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal 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
89
CacheAttacks Bootcamp.md
Normal 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
27
flake.lock
generated
Normal 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
25
flake.nix
Normal 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
21
src/CMakeLists.txt
Normal 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
53
src/cache_test.c
Normal 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
59
src/lib.c
Normal 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
9
src/lib.h
Normal 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
27
src/mksharedlib.py
Normal 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
87
src/recv.c
Normal 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
59
src/send.c
Normal 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
16527
src/sharedlib.c
Normal file
File diff suppressed because it is too large
Load Diff
9
src/sharedlib.h
Normal file
9
src/sharedlib.h
Normal 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
|
||||||
Loading…
Reference in New Issue
Block a user