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

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

本文介绍了尝试通过cr0寄存器禁用分页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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

解决方案

TL:DR:这行不通,但是您的尝试并未禁用分页,因为您清除了位32而不是位31. IDK为什么会为任何用户空间进程生成SIGSEGV.

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


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

根据osdev论坛主题 x86_64-禁用分页?

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

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

也相关:为什么长模式需要分页-可能是因为支持未分页的64位模式是不必要的硬件开销,永远不会有太多实际用途.


我不确定您为什么会收到 segfault .这就是我期望的,如果您尝试在用户空间中运行此代码,其中 mov%cr0,%rax 会出错,因为具有特权,并且内核会响应该用户空间 #GP 异常而提供SIGSEGV.

如果您是从LKM的init函数运行此函数的,例如Br​​endan说,预期结果将使该内核上的内核崩溃.或者内核可能会捕获到该错误并将SIGSEGV交付给 modprobe(1).


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

顺便说一句,CR0只有32个有效位.您可以和$ 0x7fffffff,%eax 清除它.或 btr $ 31,%rax (如果要清除64位寄存器中的第31位). https://wiki.osdev.org/CPU_Registers_x86

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

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

因此,至少在可预见的将来,将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遮罩为 0x7FFFFFFF .或 btr $ 31,%eax .将RAX截断为EAX很好.

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

 //禁用分页,应该崩溃asm volatile("mov %%cr0, %%rax \n\t"//不带 REX 前缀的汇编,与 mov %cr0,%eax 相同"btr $ 31,%% eax \ n \ t"//重置(清除)位31移动%% rax,%% cr0 \ n \ t":::"rax",内存"); 

I'm trying to disable paging completely with an LKM (don't ask me why I'm just experimenting).

I've tried just changing the value directly with the 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");
}

Well the expected result would be the bit being flipped. The actual result is a segfault.

解决方案

TL:DR: This can't work, but your attempt didn't disable paging because you cleared bit 32 instead of bit 31. IDK why that would result in a SIGSEGV for any user-space process, though.

Any badness you get from this is from clobbering RAX + RBX without telling the compiler.


You're obviously building a module for x86-64 Linux which runs in long mode. But long mode requires paging to be enabled.

According to an osdev forum thread x86_64 - disabling paging?

If that's actually true (rather than just trapping with a #GP exception or something), then obviously it's a complete disaster!!

Code fetch from EIP instead of RIP is extremely unlikely to fetch anything, and REX prefixes would decode as inc/dec if you do happen to end up with EIP pointing at some 64-bit code somewhere in the low 4GiB of physical address space. (Kernel addresses are in the upper canonical range, but it's remotely possible that the low 32 bits of RIP could be the physical address of some code.)

Also related: Why does long mode require paging - probably because supporting unpaged 64-bit mode is an unnecessary hardware expense that would never get much real use.


I'm not sure why you'd get a segfault. That's what I'd expect if you tried to run this code in user-space, where mov %cr0, %rax faults because it's privileged, and the kernel delivers SIGSEGV in response to that user-space #GP exception.

If you are running this function from an LKM's init function, like Brendan says the expected result would be crashing the kernel on that core. Or possibly the kernel would catch that and deliver SIGSEGV to modprobe(1).


Also, you're using GNU C Basic asm (without any clobbers), so GCC's code-gen assumes that registers (including RAX and RBX) aren't modified. Of course disabling paging is also a jump when your code isn't in an identity-mapped page, so it doesn't really matter whether make other small lies to the compiler or not. If this function doesn't inline into anything, then in practice clobbering RAX won't hurt. But clobbering RBX definitely can; it's call-preserved in the x86-64 System V calling convention.

And BTW, CR0 only has 32 significant bits. You could and $0x7fffffff, %eax to clear it. Or btr $31, %rax if you like to clear bit 31 in a 64-bit register. https://wiki.osdev.org/CPU_Registers_x86

According to Section 2.5 of the Intel manual Volume 3 (January 2019):

According to Section 3.1.1 of the AMD manual Volume 2 (December 2017):

So it would be fine to truncate RAX to EAX, at least for the foreseeable future. New stuff tends to get added to MSRs, not CR bits. Since there's no way to do this in Linux without crashing, you might as well just keep it simple for silly computer tricks.


0xFFFFFFFEFFFFFFFF clears bit 32, not bit 31

All of the above is predicated on the assumption that you were actually clearing the paging-enable bit. So maybe SIGSEGV is simply due to corrupting registers with GNU C basic asm without actually changing the control register at all.

https://wiki.osdev.org/CPU_Registers_x86 shows that Paging is bit 31 of CR0, and that there are no real bits in the high half. https://en.wikipedia.org/wiki/Control_register#CR0 says CR0 is a 64-bit register in long mode. (But there still aren't any bits that do anything in the high half.)

Your mask actually clears bit 32, the low bit of the high half. The right AND mask is 0x7FFFFFFF. Or btr $31, %eax. Truncating RAX to EAX is fine.

This will actually crash your kernel in long mode like you were trying to:

// disable paging, should crash
    asm volatile(
        "mov  %%cr0, %%rax        \n\t"   // assembles with no REX prefix, same as mov %cr0,%eax
        "btr  $31, %%eax          \n\t"   // reset (clear) bit 31
        "mov  %%rax, %%cr0        \n\t"
        ::
        : "rax", "memory"
     );

这篇关于尝试通过cr0寄存器禁用分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 07:05