尝试通过cr0寄存器禁用分页

时间:2019-07-20 08:58:43

标签: assembly linux-kernel x86-64 paging

我正在尝试完全禁用LKM分页(不要问我为什么要进行实验)。

我尝试过直接使用LKM更改值。

void disable_paging(void)
{
    asm("movq %cr0, %rax\n\t"
        "movq $0xFFFFFFFEFFFFFFFF, %rbx\n\t"
        "and  %rbx, %rax\n\t"
        "movq %rax, %cr0\n\t");
}

那么预期的结果就是该位被翻转了。实际结果是一个段错误。

1 个答案:

答案 0 :(得分:3)

TL:DR:这是行不通的,但是您的尝试并未禁用分页,因为您清除了位32而不是位31。IDK为什么会为任何用户导致SIGSEGV太空过程。

由此带来的任何弊端都是在不告知编译器的情况下破坏了RAX + RBX。


您显然正在为x86-64 Linux构建一个以长模式运行的模块。但是长模式需要启用分页。

根据osdev论坛主题x86_64 - disabling paging?

  

如果在长时间模式下禁用分页,则您将不再处于长时间模式。

如果这确实是对的(而不是仅仅诱捕#GP异常之类的东西),那显然是一场彻底的灾难!

从EIP而不是RIP提取代码极不可能提取任何东西,如果您碰巧以EIP指向物理地址低4GiB处的某些64位代码结尾,则REX前缀将解码为inc / dec空间。 (内核地址在规范的较高范围内,但是RIP的低32位很可能是某些代码的物理地址。)

也相关:Why does long mode require paging-可能是因为支持未分页的64位模式是不必要的硬件开销,永远不会有太多实际用途。


我不确定您为什么会收到 segfault 。这就是我期望的,如果您尝试在用户空间中运行此代码,其中mov %cr0, %raxit's privileged而失败,并且内核会响应该用户空间{{ 1}}例外。

如果您是从LKM的init函数运行此函数的,例如Br​​endan说,预期结果将导致该内核的内核崩溃。或者内核可能会捕获到该错误并将SIGSEGV传递给#GP


此外,您正在使用GNU C Basic asm(不包含任何内容),因此GCC的代码源假定未修改寄存器(包括RAX和RBX)。当然,当代码不在身份映射页面中时,禁用分页也是一个跳跃,因此,是否对编译器进行其他改动并不重要。如果此函数没有内联任何内容,那么实际上破坏RAX不会受到伤害。但是破坏RBX绝对可以; x86-64 System V调用约定中保留了该调用。

顺便说一句,CR0只有32个有效位。您可以modprobe(1)清除它。如果您想清除64位寄存器中的第31位,则使用and $0x7fffffff, %eaxhttps://wiki.osdev.org/CPU_Registers_x86

根据Intel手册第3卷(2019年1月)的2.5节:

  

CR0和CR4的位63:32被保留,并且必须用零写入。   将非零值写入任意高32位会导致   一般保护异常#GP(0)。

根据AMD手册第2卷3.1.1节(2017年12月):

  

在长模式下,保留位63:32,并且必须将其写入零,   否则会发生#GP。

因此,至少在可预见的将来,将RAX截断为EAX很好。新东西往往会添加到MSR中,而不是CR位中。由于在Linux中无法做到这一点而不会崩溃,因此您最好还是将其简化为愚蠢的计算机技巧。


0xFFFFFFFEFFFFFFFF将位32而不是位31清零

以上所有假设均基于您实际上正在清除分页启用位的假设。因此,SIGSEGV可能仅仅是由于GNU C基本asm破坏了寄存器而根本没有改变控制寄存器。

https://wiki.osdev.org/CPU_Registers_x86显示分页是CR0的第31位,并且高半部分没有实际位。 https://en.wikipedia.org/wiki/Control_register#CR0表示CR0是长模式下的64位寄存器。 (但是上半部分仍然没有任何东西可以做任何事情。)

您的掩码实际上清除了高位一半的低位32。正确的AND遮罩为btr $31, %rax。或0x7FFFFFFF。将RAX截断为EAX很好。

这实际上会 使您的内核在长时间运行时崩溃,就像您尝试执行的操作一样:

btr $31, %eax