Linux内核:系统调用挂钩示例


72

我正在尝试编写一些简单的测试代码,以作为钩住系统调用表的演示。

“ sys_call_table”在2.6中不再导出,因此我只是从System.map文件中获取地址,我可以看到它是正确的(在内存中查找我找到的地址,我可以看到指向该地址的指针系统调用)。

但是,当我尝试修改该表时,内核会给“ Oops”加上“无法处理虚拟地址c061e4f4上的内核分页请求”,然后机器会重新启动。

这是运行2.6.18-164.10.1.el5的CentOS 5.4。有某种保护措施还是我只有一个bug?我知道SELinux附带了它,我已经尝试过将其设置为宽松模式,但这并没有什么不同

这是我的代码:


1
您是否尝试过LD_PRELOADptrace?他们不满足您的尝试吗?
ezpz 2010年

3
并非如此,该练习的目的是加载一个内核模块,该模块将挂钩整个系统的系统调用。那时它做什么并不重要。
斯蒂芬

5
请注意,出于教学目的,可以对此进行研究,但同时存在技术和许可问题。不要在现实世界中使用它!
robert.berger 2011年

1
该代码的用例是什么?我可以通过这种方式挂接任何linux系统吗?
アレックス

6
@ robert.berger,什么?愿意扩大一点吗?
泰勒

Answers:


62

我终于找到了答案。

http://www.linuxforums.org/forum/linux-kernel/133982-cannot-modify-sys_call_table.html

在某些时候更改了内核,因此系统调用表是只读的。

密码朋克:

即使已经很晚了,但解决方案也可能使其他人感兴趣:在entry.S文件中,您将找到:代码:

sys_call_table-> ReadOnly如果您想使用sys_call_table来“破解”,则必须编译新的内核。

该链接还有一个将存储器更改为可写的示例。

nasekomoe:

大家好。感谢您的答复。很久以前,我通过修改对内存页面的访问来解决了这个问题。我已经为我的上层代码实现了两个功能:

这是对我有用的原始代码的修改版本。


3
请注意,在提供的链接中,Linuxerlive声称change_page_attr对于大于2.6.24的内核不起作用,因为它已被折旧了。
斯蒂芬

10
+1,用于记录您寻求他人解决的解决方案。
jgottula

1
请注意,当您调用set_memory_rw()并且地址没有页面对齐时,会得到以下信息:警告:位于arch / x86 / mm / pageattr.c:877 change_page_attr_set_clr + 0x343 / 0x530()(未污染)。我正在使用2.6.32,仍在制定解决方案(因为在我对其进行调用之后,内存似乎仍然是只读的)。
科里·亨德森

真棒回答您自己的问题。非常详细。当然+1。干杯
史密斯(A.Smith)2012年

26

感谢Stephen,您在这里的研究对我有所帮助。但是,我遇到了一些问题,因为我正在2.6.32内核上进行尝试,并且WARNING: at arch/x86/mm/pageattr.c:877 change_page_attr_set_clr+0x343/0x530() (Not tainted)随后出现内核OOPS,因为它们无法写入内存地址。

上述行上方的注释指出:

以下修改的代码有效:

请注意,在某些情况下,这实际上仍未将页面设置为读/写。在static_protections()内部调用的函数会在以下情况下set_memory_rw()删除_PAGE_RW标志:

  • 在BIOS区域
  • 地址在.rodata内部
  • 设置CONFIG_DEBUG_RODATA并将内核设置为只读

我在调试后发现了这个问题,为什么在尝试修改内核函数的地址时仍然出现“无法处理内核分页请求”。最终,我可以自己找到地址的页表条目并将其手动设置为可写,从而解决了该问题。值得庆幸的是,该lookup_address()功能已在2.6.26+版本中导出。这是我为此编写的代码:

最后,尽管Mark的答案在技术上是正确的,但在Xen中运行时会遇到问题。如果要禁用写保护,请使用读/写cr0功能。我像这样宏化它们:

希望这对遇到这个问题的其他人有所帮助。


嗨,关于您对Mark的回答的评论,只是好奇:当在Xen中运行时,它会引起什么问题?
mrduclaw

3
在我尝试过的Xen内核上,它会导致“常规保护错误”。如果您会注意到,xen定义了自己的xen_write_cr0()函数,该函数不会禁用写保护,因为虚拟机管理程序会处理它,而来宾OS则无法访问CPU寄存器。
科里·亨德森

科里,非常感谢您分享您的发现...希望我能再投票100次!
loopforever 2011年

您可以在这里找到它:github.com/cormander/tpe-lkm/blob / ...请注意,对于不同的内核版本,我有两次这些功能。
Corey Henderson

感谢您的代码,解决了我的问题。为什么要使用unsigned long参数addr。这会引起很多警告。我用作void**addr参数的类型。有使用的特殊原因unsigned long吗?
BitSchupser 2011年

19

请注意,以下内容也可以代替使用change_page_attr起作用,并且不能折旧:


2
这里做什么样的沃杜?0x00010000咒语称为什么精神?
osgx 2011年

7
@osgxcr0是控制寄存器。第16位控制页面保护的执行-切换它并突然使页面变为“只读”不再重要。您可以在内核空间中执行此操作,因为代码被标记为特权级别(环)0。普通程序无法自行执行此操作。因此,基本上,关闭写保护,践踏“只读”内存,再次将其重新打开,瞧。您不能弃用它,因为它是内核设计的一部分,并且是整体的,所有模块都在环0中运行

2
如果要执行此操作,则一旦完成,应先禁用中断cli,然后再修改cr0和重新启用中断sti。有关详细信息,请参见vulnfactory.org/blog/2011/08/12/wp-safe-or-not
mttrb

cr0以这种方式进行修改是否意味着其作用于当前页面?
sherrellbc

如果您修改cr0,它将计入cpu,因此cpu禁用时,所有指令都将禁用这些保护,而与地址无关。(en.wikipedia.org/wiki/Control_register
user7296055

13

如果您使用的是3.4和更高版本的内核(它也可以与较早的内核一起使用,我没有对其进行测试),我建议您使用一种更聪明的方法来获取系统调用表位置。

例如

而已。没有地址,它可以与我测试过的每个内核正常工作。

您可以通过模块使用未导出的内核功能的相同方法:

请享用!


是kallsyms_lookup_name将在代码段和数据段中搜索吗?
ransh

哼,我以为只有KALLSYMS_ALL=yes.config编译内核时才能做到这一点。如果您没有中的符号,我不知道它是否有效/proc/kallsyms
perror

1
在互联网上的所有答案中,只有这个对我有用!从System.map复制sys_call_table的地址会在内核中生成页面错误。
skrtbhtngr
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.