拦截系统调用(参数传递到哪里)

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

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

(假设我在 x86 上)

首先,我只是在做一个基本测试:我正在

kalloc
ating一小块可执行内存并用这个操作码填充它:

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
ing到的是真正的系统调用函数的地址。

Edit 2:我认为这很明显,但无论如何我都会解释它以防万一有人不理解我在做什么。我正在分配一个小的可执行内存块,用我正在显示的操作码填充它,然后使给定的系统调用(比方说

__NR_read
)指向该可执行内存块的地址。


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

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

linux assembly linux-kernel kernel-module system-calls
1个回答
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-在系统调用的定义中#step=6(需要注册)。

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