使用自修改代码观察在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 回答
- 3 回答
- 0 关注
- 572 浏览
添加回答
举报
0/150
提交
取消