System calls replacement.

Submitted by ig0r
on October 13, 2005 - 1:38pm

The task is to replace some syscalls in linux 2.6.x kernel. In the 2.4.x kernel it's easy to
do, but in 2.6.x system call table is not in exported symbols. It may be done for x86 in the
hackish way via sys_call_table address calculation:

----------||----------
/* System call table address calculation.
*/
ptr=(unsigned long *)((init_mm.end_code + 4) & 0xfffffffc);
printk (KERN_INFO"Searching for sys_call_table address...\n");
/* Lookup for the table in the data section. */
while((unsigned long )ptr < (unsigned long)init_mm.end_data) {
if (*ptr == (unsigned long *)sys_open) { /* The hit has happend! */

printk (KERN_INFO" -> matching detected at %p\n", ptr);
/* The pointers mast point to kernel code section... */
for(i = 0; i < 4 ;i++)
{
arr[i]=*(ptr+i);

arr[i]=(arr[i] >> 16) & 0x0000ffff;
}
/* And they does not mast match... */
if(arr[0] != arr[2] || arr[1] != arr[3])
{
sys_call_table=(ptr-__NR_open);
break;
}

}
ptr++; /* The next one... */
}
printk (KERN_INFO"sys_call_table base found at: %p\n",sys_call_table);

/* Saving pointers to original
* system call handlers.
*/
original_sys_write = sys_call_table[__NR_write];
original_sys_open = sys_call_table[__NR_open];
original_sys_close = sys_call_table[__NR_close];

printk (KERN_INFO"Starting patching!\n");

/* Remapping write, open and close syscall
* entries syscall table with to our functions.
*/
sys_call_table[__NR_write] = fake_sys_write;
sys_call_table[__NR_open] = fake_sys_open;
sys_call_table[__NR_close] = fake_sys_close;
...
----------||----------

Is there a clear way to system calls replacement (for example via audit API)?

Why would you want to do that

on
October 15, 2005 - 7:49am

Why would you want to do that? Aren't you trying to solve a problem the wrong way? You can intercept systemcalls in userspace easily for all dynamically linked programs. In the kernel you can make a LSM module or something, or your own wrapper filesystem or whatever. But simply replacing existing systemcalls with your own at runtime isn't the way to go.

your view seems good could y

on
October 17, 2005 - 3:11am

your view seems good could you give me a brief idea over this, i am also trying to intercept system calls but i am getting error

unresolved symbol sys_call_table in linux 2.4.21

i found from others that redhat has removed the option of exporting sys_call_table from version 2.4.18

ok bye ..
rajkaman

The question is what you want

on
October 17, 2005 - 9:44am

The question is what you want to do, not how.

If it's just some stupid college assignment, then you don't have much choice I guess. But if you want to achieve something useful and more long term then think very carefully what the right way is to reach your goal.

So the question is, what do you want to do? And think about the big picture, not the implementation details.

Could you point me how to int

Vadim (not verified)
on
November 15, 2005 - 3:06am

Could you point me how to intercept all system calls in userspace ?

See http://kerneltrap.org/nod

on
November 15, 2005 - 5:46am

See http://kerneltrap.org/node/5802

As strcmp said, you can also use ptrace, depending on what exactly your goal is. The preload thing only works with programs dynamically linked to libc (in practice almost all), but isn't really suitable for security related things as programs can always call systemcalls directly.

Improper System call table address calculation

on
March 23, 2006 - 11:39pm

If you need to calculate the syscall table (sys_call_table), here is the function you can use within a lkm (kernel 2.6.x). You can verify the address is correct by comparing it with:
grep sys_call_table /boot/System.map*


/* Stolen from scprint.c
 * http://downloads.securityfocus.com/downloads/scprint.tar.gz
 */
unsigned long **find_sys_call_table(void) {
    unsigned long **sctable;
    unsigned long ptr;
    extern int loops_per_jiffy;

    sctable = NULL;
    for (ptr = (unsigned long)&loops_per_jiffy;
        ptr < (unsigned long)&boot_cpu_data; ptr += sizeof(void *)){
    
        unsigned long *p;
        p = (unsigned long *)ptr;
        if (p[__NR_close] == (unsigned long) sys_close){
            sctable = (unsigned long **)p;
            return &sctable[0];
        }
    }

    return NULL;
}


unsigned long **sys_call_table = find_sys_call_table();

The complete code is here:
http://www.gnome.org/~lcolitti/gnome-startup/linux-iolog/readlog.c

finding sys_call_table: alternate method

Herbert 'herp' Rosmanith (not verified)
on
April 6, 2006 - 10:29am

somewhere in the linux-kernel, sys_call_table has to be called. now where is this place? we find near the int 0x80 vector, which points to "system_call", which, in "syscall_call", does a "call *sys_call_table(,eax,4)". the file is named "entry.S" in linux/arch/i386/kernel/. so, tell the compiler to generate an assembly listing when compiling entry.S and lookup the machine code generated for the instruction. the adress of sys_call_table should be about 1 or 2 bytes away from the "syscall_call" label.

unfortunately, allthough both symbols are in kallsyms:
hr@clio$ grep system_call /proc/kallsyms
c0102300 T system_call
hr@clio$ grep syscall_call /proc/kallsyms
c0102332 t syscall_call

the compiler cannot find it at compile time. so either you have to search /proc/kallsyms yourself, or ask the CPU via the SIDT machine instruction to store a pointer to the descriptor table. In memory of M$-DOS, I've written (for demonstration purposes) an LKM which implements the old "get_vector" "set_vector" DOS-function calls - maybe some of you remember "mov ax,2509h" "int 21".

the implementation seems straight forward enough, but if you find any bug, please tell me. I hope it is bugfree :-}.

at module_init, SIDT is executed and the int 0x80 vector is modified.
unfortunately, this site shortens my article, so check the full source-code at the URL below.

static int __init sysmon_init(void) {
int n;
...
asm("sidt idt_descr");
n=SYSCALL_VECTOR;
saved_vec=get_vector(n);
set_vector(n,new_syscall);
return 0;
}

you can look at the complete code here: http://wildsau.enemy.org/SYSMON
once the module is loaded, you can view /proc/sysmon which accumulates how often systemcalls are executed. there's a also a userspace program to show a statistic (./sysmon-txt | sort -nr +1 | head gives the 10 most frequently executed syscalls)

I'm aware, that this does not solve your problem, but you can use "get_vector(0x80)" to start scanning the for the "call *sys_call_table(,eax,4)" opcode. it will probably look like this:

hr@clio$ cat foo.s
call *0xdeadbeef(,%eax,4)

hr@clio$ as -al foo.s
GAS LISTING foo.s page 1
1
2
3 0000 FF1485EF call *0xdeadbeef(,%eax,4)
3 BEADDE

kind regards,
h.rosmanith

PS: but please write a follow-up to this article if you know another alternate solution

Due to some error, the webpag

Herbert 'herp' Rosmanith (not verified)
on
April 20, 2006 - 9:31am

Due to some error, the webpage http://wildsau.enemy.org/SYSMON was not available. This is fixed now - it's possible to access it now.

sorry for the inconvenience.

'grep sys_call_table

4r00n (not verified)
on
September 11, 2007 - 1:59pm

'grep sys_call_table /boot/System.map*' & address returned by find_sys_call_table(), did not match. When I used the address from the grep command, I am able to interrupt a system call. Can I use the value returned from grep? What is the disadvantage?

Thank you

Comment viewing options

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