Professional Documents
Culture Documents
- Phần này chúng ta sẽ kiểm tra kết quả đọc dữ liệu từ Cache memory và main memory, so sánh
chúng có nhanh hay chậm hơn.
Figure 1:CacheTime.c
Ta tạo ra một mảng có kích thước 10*4096 vì kích thước của cache là 64bit nên ta sử dụng mảng
array[k*4096], đầu tiên ta gán các giá trị của array=1 tiếp theo là xóa các bộ nhớ trong cache để đảm
bảo dung lượng cho cuộc thử nghiện tiến ra dễ hàng. Đầu tiên ta truy cập và bộ nhớ của array[3*4096]
và array[7*4096] và dĩ nhiên giá trị này sẽ được lưu trong cache, sau đó ta thực hiện đo kết quả đọc từ
việc truy cập dữ liệu.
Như hình ở trên ta thấy mảng thứ 3 và 7 là nhanh nhất, cho thấy dữ liệu từ cache đọc lên sẽ nhanh hơn
từ main memory.
- Ở đoạn code thứ 2 ta đã lưu lại giá trị của địa chỉ vì thế ta dùng nó để in ra địa chỉ cần tìm.
Thực hiện các bước như sau:
Figure 5: Địa chỉ của Secret
Figure 6:Task4.c
- Ta thực hiện đoạn code trên để thực hiện đọc giá trị từ địa chỉ đã có được
- Dĩ nhiên là không được vì ta không truy cập được vì cơ chế nói ở trên
- Ở task trước ta đã bị gặp sự cố khi thực hiện truy cập, vậy ta có thể là gì để khác phục nó? Khi ta
truy cập vào vùng nhớ cấm thì SIGSEGV signal sẽ được tăng lên, nếu chương trình của chúng ta
không giải quyết cái này thì hệ điều hành sẽ làm và chương trình của chúng ta cũng chấm dứt.
- Vậy ra sẽ giải quyết việc này như thế nào? Để giải quyết chúng ta xử lý tín hiệu trong chương
trình và nắm bắt các ngoại lệ đó bởi catastrophic events.
Figure 8:ExceptionHandling.c
Đầu tiên ta thiết lập sử lí tín hiệu: ta đăng ký một trương trình sử lý ở line 2 và khi SIGSEGV tăng lên, thì
hàm catch_segv() được gọi lên
Thiết lập trạm kiểm xoát: sau khi sử lý ngoại lệ xong, thì nó phải cho chương trình chạy tiếp. vì vậy ta nên
thiết lập trạm kiểm soát trước. Để làm được điều này ta dùng sigsetjmp(jbuf,1) ở dòng tiếp theo. Để lưu
vào trong ngăn xếp in jbuf.
Quay chở lại trạm kiểm soát: khi sigsejmp(jbuf,1) được gọi, jbuf sẽ lưu lại chương trình và chả lại cho
sigsetjmp(), như chương trình chả lại này là chương trình thứ hai (1 là của chúng ta đang thực hiện). Vì
vậy sau khi giải quyết được ta sẽ tiếp tục được thực hiện từ việc rẽ nhánh
Kích hoạt ngoại lệ: ở line 4 ta kích hoạt ngoại lệ do vi phạm quyền nên chương trình không đúng sẽ bị
hủy
Đúng như kết quả mong đợi ta đã xử lý được ngoại lệ.
o Đoạn code thứ ba đã truy xuất vào bộ nhớ của kernel và điều này làm cho ngoại lệ được
bật lên đoạn code sẽ không thực thi, và giá trị của number lúc này vẫn bằng 0
- Thông thường thi các đoan code chạy theo xu hướng trên là từng dòng một. Tuy nhiên kiến trúc
của CPU không như vậy. CPU là một bộ máy rất tham lam và mục đích của mọi người tạo ra nó là
“ thực thi nhiều nhất có thể và không được nghỉ”. Ta quay về với đoạn code trên, ở dòng thứ 3,
kernel_data có truy xuất tới địa chỉ cấm (kernel_address), ví dụ như bộ nhớ này đang ở cache thì
nó load lên rất dễ và kiểm tra cũng rất dễ chỉ mất rất ít thời gian. Nhưng trong trường hợp nó
nằm ngoài cache, thì CPU phải đi đợi một khoảng thời gian truy xuất và xem xét nó có bị vi phạm
hay không thì nó khá mất thời gian, nên cách nhà sản xuất đã nghĩ ra một cách là thực thi một
lúc “một khối lệnh” (out-of-order execution) cho dù đúng hay sai thì vẫn thực thi. Như vậy nếu
theo cách này thì dòng số 4 của ta vẫn được thực hiện nhưng chỉ không hiện nên màn hình thôi.
- Các hãng làm CPU đã phạm phải sai lầm khi thiết kế ra out-of-order execution. Để tiết kiệm thời
gian và tốc độ của CPU họ đã quên đi việc xóa bộ nhớ từ cache. Các phép tính toán ở trên cho
dù đúng hay sai cũng đều lưu vào cache. Điều này là một sai sót rất lớn, vì dữ liệu ở cache ta có
thể sử dụng kĩ thuật side-channel ở task1 và task2 để đọc dữ liệu.
- Vì ta đã biết được hoạt động của out-of-order, ở đoạn code trên Line 1 sẽ dẫn tới exception,
nhưng vì hoạt động của out-of-order thì line 2 vẫn được thực thi nhưng kết quả sẽ bị loại bỏ, vì
nó đã được thực thi nên array[7*4096+DELTA] sẽ được lưu vào cache. Ta sử dụng side-channel
để đọc dữ liệu.
- Figure 9:MeltdownExperiment.c
- Khi out-of-order execution tải dữ liệu từ kernel vào thanh ghi thì đồng thời lúc đó việc đảm bảo
an toàn cũng được thực hiện. Nếu dữ liệu tải chậm hơn việc kiểm tra thì việc tải dữ liệu này sẽ bị
ngắt ngay lập tức bời vì kiểm tra điều kiện bị sai. Nên rất có có thể việc tấn công của chúng ta
dẫn đến sai. Nhưng nếu dữ liệu của kernel được lưu ở cache thì việc tải dữ liệu sẽ nhanh hơn
nhiều, điều này giúp chúng ta lấy được thứ cần tìm vì việc tải dữ liệu của ta đã hoàn thành trước
khi kiểm tra.
- Vậy trong bài lab này ta sẽ lưu kernel sercet data vào cache trước khi thực hiện cuộc tấn công.
- Thêm đoạn code sau vào đoạn code của task7.1 và chạy.
Như vậy việc này đã giúp cho cuộc tấn công xảy ra tốt đẹp hơn và tỉ lệnh thành công cao hơn
task 7.1
- Đoạn code trên ở line một ta cho một vòng lặp 400 lần, trong đó tao thêm 0x141 vào thanh ghi
%eax, nhìn có vẻ đoạn code này không có nghĩa lý gì, theo như tôi tìm hiểu thì CPU nó có tính tự
học. Bình thường thì CPU có xu hướng dự đoán đúng hơn là sai. Đoạn code này sẽ lặp lại 400 lần
sai, để dạy cho CPU sẽ dự đoán về phía sai, và điều đó giúp ta thành công trong việc tấn công
hơn.
Ta có thể thấy điều này diễn ra dễ dàng hơn khi gọi meltdown_asm() thay vì meltdown():
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <fcntl.h>
#include <emmintrin.h>
#include <x86intrin.h>
// signal handler
static sigjmp_buf jbuf;
static void catch_segv()
{
siglongjmp(jbuf, 1);
}
int main()
{
int i, j, ret = 0;
// Register signal handler
signal(SIGSEGV, catch_segv);
int fd = open("/proc/secret_data", O_RDONLY);
if (fd < 0) {
perror("open");
return -1;
}
memset(scores, 0, sizeof(scores));
flushSideChannel();
// Retry 1000 times on the same address.