assembly - 试图通过 cr0 寄存器禁用分页
问题描述
我正在尝试使用 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");
}
那么预期的结果将是被翻转的位。实际结果是段错误。
解决方案
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。如果您尝试在 user-space 中运行此代码,这就是我所期望的,mov %cr0, %rax
因为它是 privileged 的错误,并且内核提供 SIGSEGV 以响应该用户空间#GP
异常。
如果您从 LKM 的 init 函数中运行此函数,就像 Brendan 所说的那样,预期的结果将是该内核上的内核崩溃。或者内核可能会捕获它并将 SIGSEGV 传递到modprobe(1)
.
此外,您使用的是 GNU C Basic asm(没有任何破坏),因此 GCC 的代码生成假定寄存器(包括 RAX 和 RBX)没有被修改。当然,当您的代码不在身份映射页面中时,禁用分页也是一种跳跃,因此是否向编译器制造其他小谎言并不重要。如果这个函数没有内联到任何东西,那么在实践中破坏 RAX 不会受到伤害。但是破坏RBX绝对可以;它在 x86-64 System V 调用约定中保留调用。
顺便说一句,CR0 只有 32 个有效位。你可以and $0x7fffffff, %eax
清除它。或者btr $31, %rax
,如果您想清除 64 位寄存器中的第 31 位。 https://wiki.osdev.org/CPU_Registers_x86
根据英特尔手册第 3 卷(2019 年 1 月)的第 2.5 节:
CR0 和 CR4 的位 63:32 保留,必须写入零。将非零值写入任何高 32 位都会导致一般保护异常 #GP(0)。
根据 AMD 手册第 2 卷(2017 年 12 月)的第 3.1.1 节:
在长模式下,位 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 掩码是0x7FFFFFFF
. 或btr $31, %eax
。将 RAX 截断为 EAX 很好。
这实际上会使您的内核在长模式下崩溃,就像您尝试的那样:
// 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"
);
推荐阅读
- scala - 如何实现 Akka 演员工厂?
- javascript - 如何遍历具有数组的对象并对值求和?
- javascript - 如何使用 laravel 内置的 Gmail 验证系统发送验证电子邮件
- regex - 构建一个字符串,排除包含两个特定单词的文本
- mysql - 有没有关于这个 PyMysql/Mysql 行为的文档?
- javascript - 带有 datepicker.js 的 ToDoList 保存本地存储
- arrays - 如何在 Swift 5 中使用 typeAlias 将双字典添加到数组中
- php - 如何正确重新排序来自 HTML 的 POST 数组?
- django - 如何将 models.IntegerField() 转换为整数?
- azure-devops - 用于测试/运行天蓝色 DevOps API 查询的 HTTP GET 中的日期格式