intercept file open system call

Submitted by appas
on July 21, 2004 - 10:50pm

Hi,
I want to write a linux module which should intercept Open file system call but should not intervene the underlying file system driver ( for eg. ext3 ). ie My module as well as linux file system driver (ext3) both should receive the file open system call.
Can anyone let me know how to start the proceedings.

Generic syscall intercept example

brentonr
on
July 22, 2004 - 8:34am

See http://kernelnewbies.org/code/intercept/ for a generic example that intercepts the gettimeofday system call.
Also see http://www.kernelnewbies.org/faq/#intercept as a good general backgrounder on the pros (not so many) and cons (some pretty good ones) on intercepting system calls.

To intercept (wrap) the sys_open() syscall, create your own my_sys_open() function with the same parameters as the real sys_open() function using the example source above as a guide.

By intercepting the sys_open() syscall, you can analyze or modify the parameters passed to the real sys_open(), or the return value returned from the real sys_open() (just like is shown with gettimeofday() in the example source above).

Since the sys_open() syscall is at the kernel's VFS layer, it is called before any lower-lying filesystem operations. Thus you don't have to worry about any filesystem-specific issues.

Also keep in mind that parameters to syscalls are often pointers to memory allocated in userspace (like the pathname passed to sys_open()), which must be copied to kernelspace before you access them (see functions copy_from_user() and copy_to_user()).

Other gotchas include the fact that the 2.6 tree has stopped exposing the sys_call_table symbol (as has RedHat, I believe, in their more recent kernels based on 2.4), and that it's not pretty when intercepting some system calls like sys_read(), or sys_execve().

The above source should work on any 2.4 series kernel (although I haven't inspected it that closely - it's more of a starting point than anything).

Error in intercepting sys_creat system function

appas
on
July 23, 2004 - 8:37am

Thanks for the detailed explanation.Still i have a problem in the below code.sys_call_table cannot be used in my kernel version and therefore i am creating a function pointer to intercept system function sys_creat. Address to sys_creat is obtained from System.map.
When i install the below module it installs witout saying any error
but the file passed as argument is not created. Return value is found to be -14 in /var/log/messages.Could anyone point where i went wrong.

long (*acct_process) (const char * , int ) = ( long (*) (const char*,int )) 0xc0149640 ;

int init_module(void)
{
long ret =10 ;
printk("SYSCREATE : MODULE INIT \n");
*(long *)&acct_code[1] = (long)_acct_process;
ret =acct_process("/home/appadurai/TestWorld.txt",0777 );
printk("SYSCREATE : RETURN VALUE AFTER %ld \n" , ret);
return 0;
}

void cleanup_module(void)
{
printk(" SYSCREATE : CLEANING MODULE PARAMETERS\n");
}

Re: Error in intercepting sys_creat system function

brentonr
on
July 23, 2004 - 1:57pm

Sorry, I forgot to finish a thought related to the topic of userspace parameters passed to system calls.


The fixed string you are passing to acct_process is coming from an address inside kernel space (since the fixed string is part of the module you compile and link into the kernel with insmod). The error you get, -14, or EFAULT, means that you have a "bad address".

Or in other words, the real sys_creat function doesn't like the pointer to your fixed string because sys_creat expects a pointer in userspace, whereas you've given it one in kernelspace.


This can be fixed by one of two ways:


One, you find a way to allocate userspace memory, copy your string to that allocated userspace memory, and then pass acc_process() the pointer to that userspace memory. This is difficult since you can't readily allocate userspace memory from the kernel (at least it would be _very_ tricky).


The other option is to trick the kernel into thinking the pointer you're passing sys_creat is in fact in userspace and it's okay to operate on it. For this, I will explain a little bit of background:


The kernel uses register segments to define where userspace and kernelspace are split. Commonly, userspace is assigned to any address below 3GB, and kernelspace is assigned to any address above 3GB (up to 4GB, assuming a 32-bit architecture, and more specifically, x86 hardware). There are lots of caveats to what I just said, but you can probably assume what I said is okay in your situation.


The kernel uses the 3GB boundary to determine if an address comes from userspace (0-3GB) or kernelspace (3-4GB), and can generate an EFAULT error when it doesn't like where something is coming from.


If you change the boundary from 3GB to 4GB, so that the kernel's idea of "valid userspace addresses" is now from 0-4GB it won't have an error.


To change the boundary, you have to change the segment register in your processor (I'm assuming x86 architecture here). To do that, you have to temporarily store the original segment, replace it, call your system call (where it will think even kernelspace pointers are okay), and finally set the segment back to its original value.


That being said, and I may have screwed up there, but the following is taken from a reprint of a Linux Magazine article, which has a much more detailed (and probably more correct) explaination of this process:

      mm_segment_t fs;

      fs = get_fs();     /* save previous value */
      set_fs (get_ds()); /* use kernel limit */

      /* system calls can be invoked */

      set_fs(fs); /* restore before returning to user space */




So, using that idea in your code would yeild:

long (*acct_process) (const char * , int ) = ( long (*) (const char*,int )) 0xc0149640 ;

int init_module(void)
{
    long ret =10 ;
    mm_segment_t fs;
    printk("SYSCREATE : MODULE INIT \n");
    *(long *)&acct_code[1] = (long)_acct_process;

    /* Modify kernel's limit for valid addresses */
    fs = get_fs();
    set_fs (get_ds());

    /* Make system call */
    ret =acct_process("/home/appadurai/TestWorld.txt",0777 );

    /* Restore to previous limit */
    set_fs(fs);

    printk("SYSCREATE : RETURN VALUE AFTER %ld \n" , ret);
    return 0;
}

void cleanup_module(void)
{
    printk(" SYSCREATE : CLEANING MODULE PARAMETERS\n");
} 




In general, this will allow you to supply kernelspace pointers where other system calls expect userspace pointers.


And, assuming that I haven't spaced off another critical part of what I was trying to say, that should work. One final note, though - make use of the MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros. If you oops the kernel, you'll at least know about it when it shows a constant use count in lsmod output.

how to intercept syscalls using pointer to sys_call_table

appas
on
July 24, 2004 - 12:19am

Thank you, you are teaching this newbie lot of linux.
I am writing a proxy file system driver which should intercept many file system calls.Instead of getting of the address of each system call function and pointing them to my function, I would use the address of sys_call_table and assigning it to my hacked table. The code below does this for intercepting sys_exit.
I use the below statement to assign the address of sys_call_table to
my hack_sys_call_table. (0xc032f974 is obtained from System.map).
i am just started in c.
* (long *) &hack_sys_call_table = 0xc032f974;
Could you point me out the error , if any in the above statement and the way of using it.

void * hack_sys_call_table[] ;
asmlinkage int (*original_sys_exit)(int errorcode);
asmlinkage int our_fake_exit_function(int error_code)
{
}

/*this function is called when the module is
* *loaded (initialization)*/
int init_module()
{
* (long *) &hack_sys_call_table = () 0xc032f974;

/*store reference to the original sys_exit*/
original_sys_exit=hack_sys_call_table[__NR_exit];

/*manipulate sys_call_table to call our
* *fake exit function instead
* *of sys_exit*/
hack_sys_call_table[__NR_exit]=our_fake_exit_function;
return 0;
}

void cleanup_module()
{
}

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.