如何在Linux内核中写入受保护的页面?

问题描述 投票:0回答:1

我正在尝试在模块中添加系统调用。我的理由是:

  1. 这是针对一个研究项目,因此确切的实施没有关系。
  2. 在内核中添加系统调用需要花费很长时间才能重新编译。我可以使用扩展的syscall表吸收一次编译,但并非每次都可以。即使使用增量编译,最终二进制文件的链接和归档也需要很长时间。
  3. 由于项目对时间敏感,因此使用kprobes截取syscall处理程序会降低syscall处理程序的速度。

我仍然对添加系统调用的其他方式持开放态度,但是由于上述原因,我认为在模块中写入sys_call_table是执行我要执行的操作的最干净的方法。

我已经从System.map中获取了系统调用表的地址,禁用了kaslr,并且我试图清除页面保护,但是某些写保护仍使我退缩。

// following https://web.iiit.ac.in/~arjun.nath/random_notes/modifying_sys_call.html

// clear cr0 write protection
write_cr0 (read_cr0 () & (~ 0x10000));

// clear page write protection
sys_call_table_page = virt_to_page(&sys_call_table[__NR_execves]);
set_pages_rw(sys_call_table_page, 1);

// do write
sys_call_table[__NR_execves] = sys_execves;

但是,我仍然遇到权限错误,但是我不知道强制执行该错误的机制:

[   11.145647] ------------[ cut here ]------------
[   11.148893] CR0 WP bit went missing!?
[   11.151539] WARNING: CPU: 0 PID: 749 at arch/x86/kernel/cpu/common.c:386 native_write_cr0+0x3e/0x70
...
Here was a call trace pointing to the write of sys_call_table
...
[   11.332825] ---[ end trace c20c95651874c08b ]---
[   11.336056] CPA  protect  Rodata RO: 0xffff888002804000 - 0xffff888002804fff PFN 2804 req 8000000000000063 prevent 0000000000000002
[   11.343934] CPA  protect  Rodata RO: 0xffffffff82804000 - 0xffffffff82804fff PFN 2804 req 8000000000000163 prevent 0000000000000002
[   11.351720] BUG: unable to handle page fault for address: ffffffff828040e0
[   11.356418] #PF: supervisor write access in kernel mode
[   11.359908] #PF: error_code(0x0003) - permissions violation
[   11.363665] PGD 3010067 P4D 3010067 PUD 3011063 PMD 31e29063 PTE 8000000002804161
[   11.368701] Oops: 0003 [#1] SMP KASAN PTI

full dmesg

有人猜到如何禁用它吗?

linux-kernel system-calls paging
1个回答
0
投票

内核具有防止这种行为的代码。

https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/pageattr.c#L517

/*
* Certain areas of memory on x86 require very specific protection flags,
* for example the BIOS area or kernel text. Callers don't always get this
* right (again, ioremap() on BIOS memory is not uncommon) so this function
* checks and fixes these known static required protection bits.
*/

以及稍后在函数中:

/* Check the PFN directly */
res = protect_rodata(pfn, pfn + npg - 1);
check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO");
forbidden |= res;

return __pgprot(pgprot_val(prot) & ~forbidden);

由于sys_call_table应该在只读存储器中,所以此功能将还原新的页面保护位的更改,从而导致在尝试写入页面时崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.