我正在尝试在模块中添加系统调用。我的理由是:
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
有人猜到如何禁用它吗?
内核具有防止这种行为的代码。
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
应该在只读存储器中,所以此功能将还原新的页面保护位的更改,从而导致在尝试写入页面时崩溃。