我发现可能是通过SGDT汇编命令读取GDTR。在我的 C 代码中插入这段程序集,我得到Error: operand type mismatch for 'sgdt'
unsigned long j;
asm("sgdt %0" : "=r"(j));
sgdt
只能采用内存操作数,而不能采用寄存器,因此必须"=m"
。 操作数大小为 2+8 字节(对于 x86-64;限制然后按该顺序地址),因此您需要一个结构;使用long
将导致存储在对象外部。
阅读手册! https://www.felixcloutier.com/x86/sgdt
<小时 />其他注意事项:
UMIP(用户模式指令防护)允许内核阻止用户空间(权限级别 3)运行此指令,因为它只能帮助用户空间击败内核 ASLR 或其他漏洞;用户空间对此地址没有合法用途。 在像 Linux 这样的普通内核下,用户空间不能取消引用它从中获得的虚拟地址。 因此,如果硬件(Zen 2,Cannon Lake,Goldmont Plus)支持Linux,则可以启用UMIP。
Linux 内核有一个宏:
store_gdt(dtr)
,它使用asm volatile("sgdt %0":"=m" (*dtr));
和struct desc_ptr *dtr
。
在带有Skylake CPU(不支持UMIP)的Linux 5.18系统上,我将sgdt [rsp]
(NASM语法)放入静态可执行文件中,以便我可以使用GDB(starti
/stepi
)单步执行。 在该指令之后:
x /1hx $rsp
显示限制是0x007f
(以 2 个字节存储,GDB 称之为半字,英特尔称之为字)。x /1gx $rsp+2
显示 qword 地址恰好是0xfffffe00000ed000
,这是一个有效的内核地址(48 位符号扩展,但离地址空间规范范围上半部分的最顶端相当远)。 根据docs/x86/x86-64/mm.txt
,从fffffe0000000000
开始的0.5TB包含cpu_entry_area
映射,因此找到GDT以及其他内核内容(其地址暴露在用户空间中(在没有UMIP的CPU上)并且必须始终映射,即使在具有Meltdown的CPU上也是如此。
顺便说一下,在 Linux 内核中,也可以通过已经定义的宏执行相同的操作store_gdt(dtr)
.它包含相同的内联汇编代码。宏的标头asm/desc.h