Session 4: Microarchitecture security (Mengjia Yan, Vincent Ulitzsch)¶
Homework Introduction¶
This week’s assignment is bellow.
Measure how long it takes the processor to read data from different levels of the memory hierarchy (the cache vs. main memory / DRAM).
Solve a CTF (capture-the-flag) puzzle using a cache-based side channel attack called Flush+Reload.
The goal of this lab is to give you hands-on experience interacting with the cache on a real machine. In class we talked about the cache hierarchy: the processor keeps recently used data close by in small, fast caches (L1, L2, L3), and falls back to the much slower main memory (DRAM) when the data is not cached. That speed difference is normally invisible to a program — but if we measure carefully, we can see it. And once we can see whether a piece of data was in the cache or not, we can use that as a side channel to learn secrets.
By the end of this lab you will have measured the cache-vs-memory timing difference yourself, and then used it to steal a secret from another program.
Install tools¶
Following to the instruction, I installed make and gcc tool.
sudo apt install make gcc
Then I checked the version using following command
gcc –version make –version

Run the downloaded program¶
I downloaded the assignment program from this link, https://class.academany.org/futures/cybersecurity/2026/curriculum/homework/#class-4-microarchitecture-security
I excuted make command.
make make run
Part 1: Timing the Cache vs. Main Memory¶
I got the following result.

It is saying it takes 35-39 cycle time for reading data from cache.
Next, I will add program for measuring cycle time for reading out from DRAM flush memory.
I added 2 lines in the for loop.
// ======
// [TODO] DRAM latency
// ======
// Fill in this loop, following the L1 example above.
// The data must NOT be in the cache, so each timed read is SLOW.
// Hint: use clflush() to push the line out to DRAM *before* you time it.
for (int i = 0; i < SAMPLES; i++) {
// Step 1: flush the target line out of the cache.
clflush((uint64_t)target_buffer);
// Step 2: measure how long it takes to read it.
dram_latency[i] = measure_one_block_access_time((uint64_t)target_buffer);
}
I got result.

additional research¶
I tried to understand about this assignment.
I focus on a function “measure_one_block_access_time”

It is written in assembler language. So it is quite hard to understand. I asked Claude to explain about this code.
Line-by-line trace
mov %1, %%r8 ; copy the argument addr (the memory address we want to time) into register r8
lfence ; force all preceding instructions to finish before continuing (fix the ordering)
rdtsc ; read the current cycle counter → result goes into eax
mov %%eax, %%edi ; stash that value (the start time) into edi
mov (%%r8), %%r8 ; ★ the actual point of interest: read the data at the address r8 points to
lfence ; fix ordering again (wait for this memory read to fully complete)
rdtsc ; read the cycle counter again → eax now holds the end time
sub %%edi, %%eax ; eax (end time) − edi (start time) = elapsed cycle count
So what this is doing is:
t1 = record the current time
actually read the contents at addr (this is the thing we want to time — fast or slow)
t2 = record the current time
elapsed time = t2 − t1
The key point might be “lfence” command. If we don’t use this command, CPU will automatically optimize(change) the order.
Discussion Question 1-1¶
Run your program and report the typical cache (L1) latency and the typical DRAM latency that you measured. How big is the difference?
- From this result, DRAM takes 5-14 times cycle time than cashe(L1).
Discussion Question 1-2¶
Based on your numbers, pick a single threshold value (in cycles) that you could use to decide, given one timing measurement, whether the data was in the cache or in DRAM. You will use this threshold in Part 2.
- I set the threshold as 100 cycle time.
Part 2: Capture the Flag with Flush+Reload¶
Now we turn that timing difference into an attack. In this part you will extract a secret value from a victim program without the victim ever telling you what it is — just by watching which memory it touches.
Exercise 2-1¶
Complete the code in Part2-FlushReload/attacker.c so that it reliably extracts the secret flag from the victim.
In util.h, I could see any values of constant.

-
Compile with make.
-
I typed make command
make then I got following error message

I put prompt on Claude to generate the code of attacker.c
But I got some error message when I try to make.
I asked claude about this error code, then it replyed to put -no-pie option for 3 lines in Makefile.
Makefile
CC=gcc
CFLAGS=-O0 -no-pie
TARGETS=attacker
UTILS=util.o
all: $(TARGETS)
$(UTILS): %.o: %.c %.h
$(CC) $(CFLAGS) -c $
%.o: %.c util.h
$(CC) $(CFLAGS) -c $
$(TARGETS): %:%.o util.o
$(CC) $(CFLAGS) $^ -o $@
run_victim:
./victim
run_attacker: attacker
./attacker
.PHONY: clean run_victim run_attacker
clean:
$(RM) *.o $(HELPERS) $(TARGETS)
I succeeded to make.
- Generate the shared buffer file with python3 gen_file.py.
I typed
python3 gen_file.py
- Open two terminals (use two SSH sessions, or a tool like tmux or screen). In one, run make run_victim. In the other, run make run_attacker.
I ran 2 terminal in VM. Then excuted victim and attacker in each window.

Then I got result like bellow
candidate 599: hits=115 / 2000
candidate 600: hits=108 / 2000
candidate 601: hits=117 / 2000
candidate 602: hits=119 / 2000
candidate 603: hits=110 / 2000
candidate 604: hits=128 / 2000
candidate 605: hits=111 / 2000
candidate 606: hits=101 / 2000
candidate 607: hits=94 / 2000
candidate 608: hits=1980 / 2000
candidate 609: hits=1335 / 2000
candidate 610: hits=1355 / 2000
candidate 611: hits=1341 / 2000
candidate 612: hits=1368 / 2000
candidate 613: hits=1365 / 2000
candidate 614: hits=1384 / 2000
candidate 615: hits=1344 / 2000
candidate 616: hits=1349 / 2000
candidate 617: hits=1365 / 2000
candidate 618: hits=1368 / 2000
candidate 619: hits=1373 / 2000
candidate 620: hits=1366 / 2000
candidate 621: hits=1384 / 2000
candidate 622: hits=1340 / 2000
candidate 623: hits=1385 / 2000
candidate 624: hits=1370 / 2000
candidate 625: hits=1352 / 2000
No.608 got most high score. It means When attacker.c tried to read from 608th memory area, it took ober 100 threashold cycle time for 1980 times. So the program guess 608 will be the flag that victim.c choose.
- The victim prints the true flag, so you can check whether your attacker recovered it correctly.**
It is collect. Victim program showing 608 as a flag so.
