3

I'm doing a kernel module that intercepts kernel syscalls. Intercepting, or rather just replacing the real syscall address with a fake syscall address in plain C is as easy as 1-2-3. But I'd like to know how that works on low level.

(let's pretend I'm on x86)

First of all, I'm doing just a basic test: I'm kallocating a small chunk of executable memory and filling it with this opcode:

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

Inserting the module and replacing the syscall works just perfect.

Now, according to this SO answer, arguments are passed in the registers. I want to check this, so I create an executable chunk of memory and fill it with this code:

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;

This should work too, as I'm not touching any of the registers, I'm just playing with the stack, but it doesn't work. That makes me think arguments are actually passed on the stack. But why? Am I understand the SO answer I linked to wrong? Aren't args supposed to be in the registers when a syscall is called?

Extra question: Why using jmp eax works, but call eax doesn't work? (This applies to both first and second example code).

Edit: I'm sorry, I missed a little bit the comments in the ASM code. What I'm jmping to is the address of the real syscall function.

Edit 2: I think it's obvious, but anyways I'll explain it just in case somebody is not understanding what I'm doing. I'm allocating a small executable chunk of memory, filling it with the opcode I'm showing and then making a given syscall (let's say __NR_read) point to the address of that executable chunk of memory.


works just perfect == system keeps running without problems. It means the real syscall is being called from the fake syscall

it doesn't work == system crashes because the fake syscall isn't calling the real syscall

Community
  • 1
  • 1
alexandernst
  • 13,242
  • 19
  • 83
  • 188
  • Define what exactly you call a syscall. In general, they go thru the VDSO (using e.g. `SYSENTER` there). but the C function (e.g. `mmap`) use the stack (and pass arguments to registers). Read the Linux Assembly HowTo and the ABI specification. – Basile Starynkevitch Jan 04 '14 at 15:39
  • @BasileStarynkevitch By ```syscall``` I literaly mean a ```syscall```, the funcitons that are pointed by the syscall table in the linux kernel. Btw, I'm currently trying the code with ```__NR_read```. – alexandernst Jan 04 '14 at 15:42

1 Answers1

1

Syscall params are first passed from userspace via registers to system_call() function which is in essence a common syscall dispatcher. However system_call() then calls real system call functions such as sys_read() in a usual manner, passing parameters via stack. Therefore, messing up with the stack leads to crash. Also, see this SO answer: https://stackoverflow.com/a/10459713 and very detailed explanation on quora: http://www.quora.com/Linux-Kernel/What-does-asmlinkage-mean-in-the-definition-of-system-calls#step=6 (registration required).

Community
  • 1
  • 1
  • I'm not really sure this is true for x64. I have another kernel module specifically written for x64 which hooks syscalls (sys_read) and the arguments are passed via registers (and I'm completely sure about that because the kernel module is completely working and has been tested several times). Anyways, going back to my question: Where should I look for the arguments if I'm hooking a syscall from a kernel module? In the stack? – alexandernst Jan 04 '14 at 18:20
  • So... Let's say I want to keep my code (the second one). How could I get the args from the caller and place them in my currect stack? (because I'm moving the stack). – alexandernst Jan 04 '14 at 18:41
  • Deleted my previous comment since the part about printk doesn't really make sense. Just to summarize: arguments are passed via registers to system_call() function but *not* to particular system calls like sys_read(). Therefore, second version of code doesn't make sense: it corrupts the stack and jumps to original system call which results in immediate crash. –  Jan 04 '14 at 19:03
  • Also, good read about hooking: http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/ (not only system calls but actually almost any kernel function). –  Jan 04 '14 at 19:09
  • This is not quite answering my question though... Look at https://github.com/alexandernst/procmon/blob/master/procmon_kmodule/sct_hook.c#L118 as you see I'm hooking both x86 and x64 syscalls. Not the system_call() but each syscall individually. That means that what I'm trying to do is possible. I'm just not sure *how* exactly is it done. If the args are on the stack, how can I allocate my own stack and then copy the args? If they are not on the stack, where are they? – alexandernst Jan 04 '14 at 19:29
  • I think I answered all the questions in the topic, namely: But why? You are corrupting the stack and then jumping to the function, it naturally crashes. Am I understand the SO answer I linked to wrong? It was about system_call() function but not about particular syscalls. Aren't args supposed to be in the registers when a syscall is called? At the moment it is called from userspace, args are in the registers, at the moment actual syscall function starts execution, they are on the stack. Extra question: Why using jmp eax works, but call eax doesn't work? It modifies the stack. –  Jan 04 '14 at 19:41
  • Not sure what do you mean by allocating another stack and copying args but it seems to be an offtopic to this question. –  Jan 04 '14 at 19:42
  • Ah, I see where are we misunderstanding each other. I was talking about hooking/hijacking a specific syscall, not the system_call() itself. Maybe you didn't see my edits? – alexandernst Jan 04 '14 at 20:00
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44517/discussion-between-ivan-and-alexandernst) –  Jan 04 '14 at 20:04
  • Please see reply in chat :) – alexandernst Jan 05 '14 at 00:41