拦截系统调用(args通过的地方)

时间:2014-01-04 15:26:02

标签: linux assembly linux-kernel kernel-module system-calls

我正在做一个拦截内核系统调用的内核模块。拦截,或者只是用普通C中的伪系统调用地址替换真实的系统调用地址就像1-2-3一样简单。但是我想知道它在低级别上是如何工作的。

(让我假装我在x86上)

首先,我只做了一个基本的测试:我kalloc使用一小块可执行内存并用这个操作码填充它:

0xB8, 0x00, 0x00, 0x00, 0x00,          //mov eax, &real_syscall_function;
0xFF, 0xE0,                            //jmp eax;

插入模块并更换系统调用非常完美

现在,根据this SO answer,参数在寄存器中传递。 我想检查一下,所以我创建了一个可执行的内存块并用这段代码填充它:

0x55,                                  //push ebp;
0x89, 0xE5,                            //mov ebp, esp;
0x83, 0xEC, 0x20,                      //sub esp, 32; 

0xB8, 0x00, 0x00, 0x00, 0x00,          //mov eax, &real_syscall_function;
0xFF, 0xE0,                            //jmp eax;

0x89, 0xEC,                            //mov esp, ebp;
0x5D,                                  //pop ebp;
0xC3                                   //ret;

这也应该有用,因为我没有触及任何寄存器,我只是在玩堆栈,但它不起作用。这让我觉得参数实际上是在堆栈上传递的。但为什么?我理解与错误相关的SO答案吗?调用系统调用时,是否应该将args放在寄存器中?

额外问题:为什么使用jmp eax 有效,但call eax 不起作用? (这适用于第一个和第二个示例代码)。

编辑:对不起,我错过了一些ASM代码中的评论。我jmp的内容是真实系统调用函数的地址。

编辑2 :我认为这很明显,但无论如何我会解释它,以防万一有人不理解我在做什么。我正在分配一小块可执行内存,用我正在显示的操作码填充它,然后使给定的系统调用(假设__NR_read)指向该可执行内存块的地址。


工作得很完美 ==系统保持运行没有问题。这意味着真正的系统调用是从伪系统调用

调用的

它不起作用 ==系统崩溃,因为假的系统调用没有调用真正的系统调用

1 个答案:

答案 0 :(得分:1)

Syscall参数首先从用户空间通过寄存器传递给system_call()函数,该函数本质上是一个常见的系统调度程序。但是,system_call()然后以通常的方式调用实际的系统调用函数,例如sys_read(),通过堆栈传递参数。因此,搞乱堆栈会导致崩溃。 另外,请参阅此SO答案:https://stackoverflow.com/a/10459713以及关于quora的非常详细的解释:http://www.quora.com/Linux-Kernel/What-does-asmlinkage-mean-in-the-definition-of-system-calls#step=6(需要注册)。