使用自修改代碼觀察在x86上獲取過時的指令我被告知并且從英特爾的手冊中讀到可以將指令寫入內存,但是指令預取隊列已經獲取了陳舊的指令并將執行那些舊的指令。我沒有成功觀察到這種行為。我的方法如下。英特爾軟件開發手冊從第11.6節開始說明對當前在處理器中高速緩存的代碼段中的存儲器位置的寫入導致相關聯的高速緩存行(或多個行)無效。此檢查基于指令的物理地址。此外,P6系列和奔騰處理器檢查對代碼段的寫入是否可以修改已經預取執行的指令。如果寫入影響預取指令,則預取隊列無效。后一種檢查基于指令的線性地址。所以,看起來如果我希望執行陳舊的指令,我需要有兩個不同的線性地址指向同一個物理頁面。所以,我將內存映射到兩個不同的地址。int fd = open("code_area", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);assert(fd>=0);write(fd, zeros, 0x1000);uint8_t *a1 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FILE | MAP_SHARED, fd, 0);uint8_t *a2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FILE | MAP_SHARED, fd, 0);assert(a1 != a2);我有一個匯編函數,它接受一個參數,一個指向我想要更改的指令的指針。fun:
push %rbp
mov %rsp, %rbp
xorq %rax, %rax # Return value 0# A far jump simulated with a far return# Push the current code segment %cs, then the address we want to far jump to
xorq %rsi, %rsi
mov %cs, %rsi
pushq %rsi
leaq copy(%rip), %r15
pushq %r15
lretq
copy:# Overwrite the two nops below with `inc %eax'. We will notice the change if the# return value is 1, not zero. The passed in pointer at %rdi points to the same physical# memory location of fun_ins, but the linear addresses will be different.
movw $0xc0ff, (%rdi)fun_ins:
nop # Two NOPs gives enough space for the inc %eax (opcode FF C0)
nop
pop %rbp
ret
fun_end:
nop在C中,我將代碼復制到內存映射文件中。我從線性地址調用函數a1,但我將指針傳遞給a2代碼修改的目標。#define DIFF(a, b) ((long)(b) - (long)(a))long sz = DIFF(fun, fun_end);memcpy(a1, fun, sz);void *tochange = DIFF(fun, fun_ins);int val = ((int (*)(void*))a1)(tochange);如果CPU選擇了修改后的代碼,則val == 1。否則,如果執行過時指令(兩個nops),則val == 0。我在1.7GHz Intel Core i5(2011 macbook air)和Intel(R)Xeon(R)CPU X3460 @ 2.80GHz上運行。但是,每次都看到val == 1表示CPU始終注意到新指令。有沒有人經歷過我想觀察的行為?我的推理是否正確?我對提到P6和奔騰處理器的手冊有點困惑,以及缺乏提及我的Core i5處理器的原因。也許正在發生的其他事情導致CPU刷新其指令預取隊列?任何見解都會非常有幫助!
- 3 回答
- 0 關注
- 588 瀏覽
添加回答
舉報
0/150
提交
取消