Linux Kernel sys_call_table、Kernel Symbols Export Table Generation Principle、Difference Between System Calls Entrance In 32bit、64bit Linux【转】

转自:http://www.cnblogs.com/LittleHann/p/4127096.html

目录

1. sys_call_table:系统调用表
2. 内核符号导出表:Kernel-Symbol-Table
3. Linux 32bit、64bit环境下系统调用入口的异同
4. Linux 32bit、64bit环境下sys_call_table replace hook

 

1. sys_call_table:系统调用表

0x1: sys_call_table简介

sys_call_table在Linux内核中是在Linux内核中的一段连续内存的数组,数组中的每个元素保存着对应的系统调用处理函数的内存地址

1. 32bit:
cat /boot/System.map-3.13.0-32-generic | grep sys_call_table
c1663140 R sys_call_table

2. 64bit
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_call_table
ffffffff81600460 R sys_call_table

3. 64bit 兼容 32bit
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep ia32sys_call_table
ffffffff8160a1f8 r ia32_sys_call_table

sys_call_table由Linux内核在初始化的时候填充,从内核源代码中可以得到它的声明和定义

1. 32bit:
/source/arch/x86/kernel/syscall_32.c
__visible const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] =
{
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
    [0 ... __NR_syscall_max] = &sys_ni_syscall,
    #include <asm/syscalls_32.h>
};

2. 64bit
/source/arch/x86/kernel/syscall_64.c
asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] =
{
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
    [0 ... __NR_syscall_max] = &sys_ni_syscall,
    #include <asm/syscalls_64.h>
};

3. 64bit 兼容 32bit
/source/arch/x86/ia32/syscall_ia32.c
const sys_call_ptr_t ia32_sys_call_table[__NR_ia32_syscall_max+1] =
{
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
    [0 ... __NR_ia32_syscall_max] = &compat_ni_syscall,
    #include <asm/syscalls_32.h>
};

Relevant Link:

深入linux内核架构(中文版).pdf
http://www.cnblogs.com/LittleHann/p/3850653.html
http://www.cnblogs.com/LittleHann/p/3854977.html

0x2: Linux下获取sys_call_table内核地址的方式

http://www.cnblogs.com/LittleHann/p/3854977.html
//搜索:0x3: 获取sys_call_table的常用方法

 

2. 内核符号导出表:Kernel-Symbol-Table
驱动LKM也是存在于内核空间的,函数中的函数、变量都会有对应的符号,这部分符号也可以称作内核符号,这些内核符号有两种状态

1. 导出(EXPORT_SYMBOL)
在内核代码中明确声明EXPORT_SYMBOL(xxxx_FUNCTION)之后,这个函数就可以作为内核对外的接口,供外部使用了。对于这部分导出的内核符号表我们称之为"内核导出符号表"

2. 不导出
对于不导出的内核函数,只能在内核中使用

insmod的时候并不是所有的函数都得到内核符号表去寻找对应的符号,每一个驱动在自已的分配的空间里也会存在一份符号表,里面有关于这个驱动里使用到的变量以及函数的一些符号,首先驱动会在这里面找,如果发现找不到就会去公共内核符号表中搜索,搜索到了则该模块加载成功,搜索不到则该模块加载失败

可以通过nm -l xx.ko来查看一个模块里的符号情况

nm -l find_sys_call_table.ko
/*
00000000 T cleanup_module
00000030 T find_sys_call_table
000000b6 T init_module
         U loops_per_jiffy
         U mcount
00000ef8 r __module_depends
         U printk    find_sys_call_table.c:0
000000b6 t syscall_init
000001c1 t syscall_release
00000000 B syscall_table
         U sys_close
00007980 D __this_module
00000ec9 r __UNIQUE_ID_license0
00000ed5 r __UNIQUE_ID_srcversion1
00000f01 r __UNIQUE_ID_vermagic0
00003c20 r ____versions
*/

在Linux内核中,大部分的函数、变量都是导出的,我们可以通过对内核符号表的遍历搜索得到我们想要获得的内核函数、内核变量的地址
 

3. Linux 32bit、64bit下系统调用入口的异同

以sys_execve、sys_socketcall、sys_init_module这三个系统调用作为研究对象,为了更好的说明问题,我们打印一下Linux 64bit的sys_call_table的函数指针地址

find_sys_call_table.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <linux/syscalls.h>
#include <linux/delay.h>    // loops_per_jiffy

/* Just so we do not taint the kernel */
MODULE_LICENSE("GPL");

void **syscall_table;
unsigned long **find_sys_call_table(void);

unsigned long **find_sys_call_table() {

    unsigned long ptr;
    unsigned long *p;

    for (ptr = (unsigned long)sys_close;
         ptr < (unsigned long)&loops_per_jiffy;
         ptr += sizeof(void *)) {

        p = (unsigned long *)ptr;

        if (p[__NR_close] == (unsigned long)sys_close) {
            printk(KERN_DEBUG "Found the sys_call_table!!!\n");
            return (unsigned long **)p;
        }
    }

    return NULL;
} 

static int __init syscall_init(void)
{
    int ret;
    unsigned long addr;
    unsigned long cr0;
    int num = 0;

    syscall_table = (void **)find_sys_call_table();

    if (!syscall_table)
    {
        printk(KERN_DEBUG "Cannot find the system call address\n");
        return -1;
    }

    do
        {
            printk("%d:  the address is: %16x\n", num, syscall_table[num]);
            num++;
        } while (num < 400);

    return 0;
}

static void __exit syscall_release(void)
{
}

module_init(syscall_init);
module_exit(syscall_release);

Makefile

obj-m := find_sys_call_table.o
PWD       := $(shell pwd)

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    rm -rf *.o *~ core .*.cmd *.mod.c ./tmp_version *.ko modules.order  Module.symvers

clean_omit:
    rm -rf *.o *~ core .*.cmd *.mod.c ./tmp_version modules.order  Module.symvers

0x1: Linux 32bit

1. sys_execve

对于Linux 32bit操作系统来说,sys_execve的系统调用号、以及在它在sys_call_table中的索引位置

\linux-3.15.5\arch\sh\include\uapi\asm\unistd_32.h
#define __NR_execve         11

//系统调用处理函数在内核内存中的地址可以通过以下方式得到
cat /boot/System.map-3.13.0-32-generic | grep sys_execve
c117fc00 T sys_execve

//和sys_call_table中的函数地址进行逐行对比
cat info | grep c117fc00
[16595.247404] 11:  the address is:         c117fc00

在正常情况下(当前linux没有被rootkit、sys_call_table没有被hooked),sys_call_table(系统调用表)中的函数地址和内核导出符号表中的函数地址应该是相同的,即

sys_call_table[__NR_sys_execve] = cat /boot/System.map-3.13.0-32-generic | grep sys_execve

系统调用函数的入口点跟踪如下

linux-3.15.5\fs\exec.c

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}

这是个宏定义,等价于对sys_execve的声明

int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    return do_execve_common(filename, argv, envp);
}

2. sys_socketcall

\linux-3.15.5\arch\sh\include\uapi\asm\unistd_32.h
#define __NR_socketcall        102

//系统调用处理函数在内核内存中的地址可以通过以下方式得到
cat /boot/System.map-3.13.0-32-generic | grep sys_socketcall
c1560100 T sys_socketcall

//和sys_call_table中的函数地址进行逐行对比
cat info | grep c1560100
[16595.247515] 102:  the address is:         c1560100

\linux-3.15.5\net\socket.c

/*
进行socket调用派发
*/
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
    unsigned long a[AUDITSC_ARGS];
    unsigned long a0, a1;
    int err;
    unsigned int len;

    if (call < 1 || call > SYS_SENDMMSG)
        return -EINVAL;

    len = nargs[call];
    if (len > sizeof(a))
        return -EINVAL;

    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, len))
        return -EFAULT;

    err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
    if (err)
        return err;

    a0 = a[0];
    a1 = a[1];

    switch (call) {
    case SYS_SOCKET:
        err = sys_socket(a0, a1, a[2]);
        break;
    case SYS_BIND:
        err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_CONNECT:
        err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_LISTEN:
        err = sys_listen(a0, a1);
        break;
    case SYS_ACCEPT:
        err = sys_accept4(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2], 0);
        break;
    case SYS_GETSOCKNAME:
        err =
            sys_getsockname(a0, (struct sockaddr __user *)a1,
                    (int __user *)a[2]);
        break;
    case SYS_GETPEERNAME:
        err =
            sys_getpeername(a0, (struct sockaddr __user *)a1,
                    (int __user *)a[2]);
        break;
    case SYS_SOCKETPAIR:
        err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
        break;
    case SYS_SEND:
        err = sys_send(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_SENDTO:
        err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
                 (struct sockaddr __user *)a[4], a[5]);
        break;
    case SYS_RECV:
        err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_RECVFROM:
        err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                   (struct sockaddr __user *)a[4],
                   (int __user *)a[5]);
        break;
    case SYS_SHUTDOWN:
        err = sys_shutdown(a0, a1);
        break;
    case SYS_SETSOCKOPT:
        err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
        break;
    case SYS_GETSOCKOPT:
        err =
            sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
                   (int __user *)a[4]);
        break;
    case SYS_SENDMSG:
        err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
        break;
    case SYS_SENDMMSG:
        err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
        break;
    case SYS_RECVMSG:
        err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
        break;
    case SYS_RECVMMSG:
        err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
                   (struct timespec __user *)a[4]);
        break;
    case SYS_ACCEPT4:
        err = sys_accept4(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2], a[3]);
        break;
    default:
        err = -EINVAL;
        break;
    }
    return err;
}

3. sys_init_module

\linux-3.15.5\arch\sh\include\uapi\asm\unistd_32.h
#define __NR_init_module    128

//系统调用处理函数在内核内存中的地址可以通过以下方式得到
cat /boot/System.map-3.13.0-32-generic | grep sys_init_module
c10c4820 T sys_init_module

//和sys_call_table中的函数地址进行逐行对比
cat info | grep c10c4820
[16595.247540] 128:  the address is:         c10c4820

\linux-3.15.5\kernel\module.c

SYSCALL_DEFINE3(init_module, void __user *, umod,
        unsigned long, len, const char __user *, uargs)
{
    int err;
    struct load_info info = { };

    err = may_init_module();
    if (err)
        return err;

    pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs);

    err = copy_module_from_user(umod, len, &info);
    if (err)
        return err;

    return load_module(&info, uargs, 0);
}

0x2: Linux 64bit

在Linux 64bit下,系统调用的入口点和32bit下有一点区别

1. sys_execve

/source/arch/x86/syscalls/syscall_64.tbl
#
# 64-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point>
#
# The abi is "common", "64" or "x32" for this file.
59      64      execve                  stub_execve

//在内核符号导出表中得到的内核内存地址
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep stub_execve
ffffffff8100b4e0 T stub_execve
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_execve
ffffffff810095b0 T sys_execve

//在sys_call_table中搜索系统调用函数地址
cat info | grep 8100b4e0: [10298.905575] 59:  the address is:         8100b4e0
cat info | grep 810095b0: no result

//对于64bit的Linux系统来说,在系统调用外层使用了stub(wrapper functions)
\linux-3.15.5\arch\x86\kernel\entry_64.S
ENTRY(stub_execve)
    CFI_STARTPROC
    addq $8, %rsp
    PARTIAL_FRAME 0
    SAVE_REST
    FIXUP_TOP_OF_STACK %r11
    call sys_execve
    movq %rax,RAX(%rsp)
    RESTORE_REST
    jmp int_ret_from_sys_call
    CFI_ENDPROC
END(stub_execve)

在Linux 64bit下,stub_execve就是sys_execve的wrapper函数

/source/arch/x86/um/sys_call_table_64.c

#define stub_execve sys_execve

这也意味着在Linux 64bit下,sys_execeve在sys_call_table里不存在了,而是用stub_execve取代了,我们的hook对象也就是stub_execve

2. sys_socketcall

sys_socketcall 只适用于x86-32平台下适用,在非x86-32平台下,sys_socketcall是不存在的,Linux 64bit将sys_socketcall的"系统调用派发机制"拆分成了分别独立的系统调用,例如sys_socket、sys_bind、 sys_connect

//找到Linux 64bit对应的unistd_64.h文件
find /usr/src/kernels/`uname -r` -name unistd_64.h
vim /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h

1. sys_socket
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_socket
ffffffff8140a210 T sys_socket
cat info | grep 8140a210
[924227.139549] 41:  the address is:         8140a210
cat /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h | grep __NR_socket
#define __NR_socket                             41
__SYSCALL(__NR_socket, sys_socket)

2. sys_connect
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_connect
ffffffff8140bfb0 T sys_connect
[924227.139550] 42:  the address is:         8140bfb0
cat /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h | grep __NR_connect
#define __NR_connect                42
__SYSCALL(__NR_connect, sys_connect)

3. sys_bind
cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_bind
ffffffff8140c0a0 T sys_bind
[924227.139558] 49:  the address is:         8140c0a0
cat /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h | grep __NR_bind
#define __NR_bind                49
__SYSCALL(__NR_bind, sys_bind)

在Linux 64bit环境下,可以直接针对sys_connect(即TCP_CONNECT动作进行监控)

3. sys_init_module

//找到Linux 64bit对应的unistd_64.h文件
find /usr/src/kernels/`uname -r` -name unistd_64.h
vim /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h

cat /boot/System.map-2.6.32-220.23.2.ali878.el6.x86_64 | grep sys_init_module
ffffffff810afe50 T sys_init_module
cat info | grep 810afe50
[924227.139712] 175:  the address is:         810afe50

cat /usr/src/kernels/2.6.32-220.23.2.ali878.el6.x86_64/arch/x86/include/asm/unistd_64.h | grep __NR_init_module
#define __NR_init_module                        175
__SYSCALL(__NR_init_module, sys_init_module)

Relevant Link:

http://stackoverflow.com/questions/9940391/looking-for-a-detailed-document-on-linux-system-calls

 

4. Linux 32bit、64bit环境下sys_call_table replace hook:建立通用兼容性的sys_call_table Replace Hook Engine

0x1: Linux 32/64bit Hook Point

1. 32bit
    1) sys_execve
    2) sys_socketcall
    3) sys_init_module
2. 64bit
    1) stub_execve
    2) sys_connect
    3) sys_init_module

0x2: Linux 32/64bit __NR_XX定义的异同

在进行sys_call_table replace hook的时候,我们往往需要借助系统头文件<asm/unistd.h>的__NR_XX宏定义来定位寻址到我们要hook的目标系统调用处理函数,需要注意的是,这个宏定义在32/64bit的Linux上存在着一些差异,我们在编写sys_call_table hook engine的时候需要特别注意这块的兼容性

print_NR.c

#include <asm/unistd.h>
#include <linux/module.h>    // included for all kernel modules
#include <linux/kernel.h>    // included for KERN_INFO
#include <linux/init.h>        // included for __init and __exit macros

static int __init hello_init(void)
{
    //在32bit Linux下编译(未做跨平台兼容)
    /*
    printk("__NR_execve: %d\n", __NR_execve);
    printk("__NR_socketcall: %d\n", __NR_socketcall);
    printk("__NR_init_module: %d\n", __NR_init_module);
    */

    //在64bit Linux下编译(未做跨平台兼容)
    printk("__NR_execve: %d\n", __NR_execve);
    printk("__NR_connect: %d\n", __NR_connect);
    printk("__NR_init_module: %d\n", __NR_init_module);

    printk(KERN_INFO "Hello world!\n");
    return 0;    // Non-zero return means that the module couldn't be loaded.
}

static void __exit hello_cleanup(void)
{
    printk(KERN_INFO "Cleaning up module.\n");
}

module_init(hello_init);
module_exit(hello_cleanup);

在Linux 32bit、Linux 64bit环境下分别作编译,结果如下

1. 32 bit
[22726.762562] __NR_execve: 11: sys_execve
[22726.762570] __NR_socketcall: 102: sys_socketcall
[22726.762573] __NR_init_module: 128: sys_init_module

2. 64 bit
[ 2295.036784] __NR_execve: 59: stub_execve
[ 2295.036785] __NR_connect: 42: sys_connect
[ 2295.036786] __NR_init_module: 175: sys_init_module

从结果上来看,_NR_XX宏定义的打印结果和我们的调研结果是一致的,所不同的是在32、64环境下系统调用对应的__NR_XX宏定义名字不同了

0x3: Linux 64bit 环境下 sys_execve hook特殊处理

为了充分利用Linux 64bit下的寄存器资源、以及提高Linux 64bit针对进程执行系统调用的执行效率,Linux 64bit内核针对sys_execve进行了特殊处理,使用了"wrapper function":stub_execve,对sys_execve进行了包装

\linux-3.15.5\arch\x86\kernel\entry_64.S
ENTRY(stub_execve)
    /*
    汇编代码对rsp,堆栈参数寻址寄存器进行了修正
    说明内核在进入stub_execve之前,rsp本身就是"不准确"的,需要进行修正,而rsp不准确也意味着栈上参数寻址是不准确的,这直接带来一个问题就是我们进行replace hook的fake_xxx_function不能简单的直接使用函数声明中传递进来的参数
    */
    CFI_STARTPROC
    addq $8, %rsp
    PARTIAL_FRAME 0
    SAVE_REST
    FIXUP_TOP_OF_STACK %r11

    //修复栈、寄存器状态之后,进入真正的sys_execve系统调用
    call sys_execve

    movq %rax,RAX(%rsp)
    RESTORE_REST
    jmp int_ret_from_sys_call
    CFI_ENDPROC
END(stub_execve)

在Linux 64bit环境下,我们对sys_call_table[__NR_execve]进行replace hook之后,我们在fake_sys_execve中得到的参数不是准确的,因为这个时候rsp是不准确的,解决这个问题的思路有两个

1. 在fake_sys_execve中使用"inline asm 内联汇编"的方式,模仿stub_execve在进入sys_execve之前所做的事情
/*
这种方式需要在内核代码中插入大量的汇编
并且承当更大的兼容性风险
*/

2. 直接对sys_execve进行"inline hook"
    1) 通过kprobe监控sys_execve的系统调用,使用争夺自旋锁的方式强制当前所有CPU等待"inline hook"的地址替换动作完成
    2) 通过kprobe获取到sys_execve在内核中的函数地址
    3) 直接拷贝sys_execve入口点开始的9字节的字节码,将这9字节字节码替换为:jmp fake_sys_execve(总共9字节)
    4) 在fake_sys_execve中,将窃取的原始的sys_execve入口点的汇编字节码重新执行一次
    5) 由于fake_sys_execve是inline hook到sys_execve中的,所以这个时候fake_sys_execve可以直接使用sys_execve的栈上的参数,这个时候,我们就可以正常执行fake_sys_execve的主体代码
    5) 当fake_sys_execve执行完毕之后,直接跳回到之前sys_execve开头那段被替换的地址之后的第一条指令,继续执行即可
/*
使用inline hook需要重点关注的问题就是:
    1) "inline hook"的关键动作是一段内核内存地址的替换过程
    2) 大多数情况下这个地址替换过程需要消耗超过1条的CPU指令去完成
    3) 在多线程/多CPU情况下,CPU在执行多条指令的期间可能会被打断,从而破坏"inline hook"这个过程的一致性,可能导致地址替换失败
    4) 需要使用自旋锁的机制强制保证这个地址替换(hook)的过程的"原子性"
*/

0x4: 模块卸载时旧的函数调用的返回导致的"bad memory address access"问题

模块在卸载的时候有可能有之前旧的用户态习系统调用请求hung在当前我们的hook模块中,不能直接粗暴的rmmod,否则会引起原始函数返回后ret到一块"not valid memory"中,解决这个问题的方案是采用内联汇编,构造直接返回的栈状态,下图解释

因为Linux GCC不支持nick裸函数,所以我们不能直接push origin_func、ret的方式构造特殊的栈状态,跳转到原始系统调用函数中

if(sizeof(void*)==4)
{
    asm volatile ("movl %1,%%eax \n movl %%eax,%0":"=r"(old_func):"r"(old_func));
    asm volatile (".intel_syntax");//这里换用intel语法,gas语法简直不是给人用的
    asm volatile ("mov %esp,%ebp");
    asm volatile ("pop %ebp");//现在ebp的原始值已经被恢复了
    asm volatile (".att_syntax");//换回gas的att语法
    asm volatile ("pushl %eax");
    asm volatile ("ret");
}else
{
    asm volatile ("movq %1,%%rax \n movq %%rax,%0":"=r"(old_func):"r"(old_func));
    asm volatile (".intel_syntax");//这里换用intel语法,gas语法简直不是给人用的
    asm volatile ("mov %rsp,%rbp");
    asm volatile ("pop %rbp");
    asm volatile (".att_syntax");//换回gas的att语法
    asm volatile ("pushq %rax");
    asm volatile ("ret");
}

0x5:  动态获取系统调用函数在sys_call_table中的索引号

1. 获取"kallsyms_lookup_name"的函数地址
    1) 如果linux内核直接导出了这个函数,则可以直接使用
    2) 或者使用kprobe机制去获取这个函数的地址
        2.1) kprobe注册"kallsyms_lookup_name"
        2.2) 获取kprobe注册后的函数地址
        2.3) kprobe解除注册
2. 调用kallsyms_lookup_name()获取我们要HOOK的函数在内核符号导出表的地址: hook_func_address
3. 使用kallsyms_lookup_name()获取sys_call_table的内核地址
3. 将hook_func_address在sys_call_table中逐行遍历,得到对应的偏移索引号
4. 使用动态获取的索引号进行sys_call_table replace hook

0x6: 总结

/*
sys_call_table replace hook: 要解决的问题是让原始系统调用直接返回用户态系统调用的入口点之后
inline hook: 要解决的问题是保证地址替换的过程的原子性
*/
1. 32 bit
sys_execve: sys_call_table replace hook
sys_socketcall: sys_call_table replace hook
sys_init_module: sys_call_table replace hook

2. 64 bit
stub_execve: inline hook
sys_connect: sys_call_table replace hook
sys_init_module: sys_call_table replace hook

 

Copyright (c) 2014 LittleHann All rights reserved

 

 

分类: Linux内核

 

时间: 2024-10-26 02:55:52

Linux Kernel sys_call_table、Kernel Symbols Export Table Generation Principle、Difference Between System Calls Entrance In 32bit、64bit Linux【转】的相关文章

了解最新一代的Linux虚拟化技术Kernel Virtual Machine

尽管这种变化的动机主要与构建和支持相关,而不是技术,但事实是许多对虚拟化感兴趣的企业 IT 小组需要学习 KVM 所使用的管理和控制工具.类似地,已投资 Xen 虚拟化且正在转向使用 KVM 的 IT 小组,可能希望尽可能将现有的虚拟机转换为 KVM 支持的格式,而不是重新创建它们. 能够在单个服务器硬件平台上运行多个虚拟机 (VM) 的能力在如今的 IT 基础架构中实现了了成本.系统管理和灵活性等方面的优势.在单个硬件平台上托管多个虚拟机,可减少硬件开支并帮助最大限度降低基础架构成本,比如能耗

《鸟哥的Linux 私房菜 基础学习篇(第三版)》——1.2 Torvalds的Linux开发

1.2 Torvalds的Linux开发 鸟哥的Linux 私房菜 基础学习篇(第三版) 我们前面一节当中,提到了UNIX的历史,也提到了Linux是由芬兰人Torvalds所开发的.那么为何托瓦兹可以开发Linux呢?凭空想象而来的,还是有什么渊源?这里我们就来谈一谈! 1.2.1 Minix Linus Torvalds(托瓦兹, 1969年出生)的外祖父是赫尔辛基大学的统计学家,他的外祖父为了让自己的小孙子能够学点东西,所以从小就将托瓦兹带到身边来管理一些微计算机.在这个时期,托瓦兹接触了

《Linux嵌入式实时应用开发实战(原书第3版)》——3.5 Linux文件系统

3.5 Linux文件系统 Linux文件系统在很多方面与Windows PC或Macintosh(苹果公司生产的)上的文件系统类似.它是一个等级系统,允许你在"/"标识的根目录下创建任何数量的子目录.和Windows中一样,文件名可以很长.尽管如此,Linux和多数类似UNIX的系统一样,文件的扩展名--文件名中"."后面的那部分--不像它们在Windows中那样有意义.例如,Windows中的可执行文件的扩展名为".exe",但是Linux中

linux中禁止Kernel自动升级的方法

不过在更新其他软件包时,如果依赖最新的内核,那么该软件包是没法更新成功的. 方法如下: 方法1: # vim /etc/yum.conf exclude=kernel* 在 [main]配置段下,追加或修改以上内容. 可通过下面的命令查看是否生效: # yum update | grep -i kernel 方法2: 在yum命令行中加上-x参数,来跳过指定的更新.如: # yum -x 'kernel*' update

linux下uboot kernel操作cpu寄存器

大多数的内核里面都有会对GPIO的操作,而且内核里面对GPIO进行配置也很方便,要什么功能就配置成什么就可以了. 还有一些寄存器是内核没有配置到的,但是我们要操作怎么办,内核里面也定义了相关的接口函数. 在u-boot中操作某个寄存器:   [cpp] view plain copy    print? reg = readl(IOMUXC_BASE_ADDR + IOMUXC_REG_GPR1);   reg &= ~IOMUXC_REG_GPR1_ACTCS0_MASK;   writel(

嵌入式 uboot引导kernel,kernel引导fs

1.uboot引导kernel: u-boot中有个bootm命令,它可以引导内存中的应用程序映像(Kernel),bootm命令对应 common/cmd_bootm.c中的do_bootm()函数,此函数实现下面几个功能: 1)读flash中的内核映像文件 2)解压内核 3)校验内核 4)跳到内核执行(调用do_bootm_linux()函数) { 1.Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下  

嵌入式 uboot引导kernel,kernel引导fs【转】

转自:http://www.cnblogs.com/lidabo/p/5383934.html#3639633 1.uboot引导kernel: u-boot中有个bootm命令,它可以引导内存中的应用程序映像(Kernel),bootm命令对应 common/cmd_bootm.c中的do_bootm()函数,此函数实现下面几个功能: 1)读flash中的内核映像文件 2)解压内核 3)校验内核 4)跳到内核执行(调用do_bootm_linux()函数) { 1.Stage1 start.S

linux上mysql报错:Table “xxx” doesn’t exist如何解决

问题:linux上的mysql出Table 'xxx' doesn't exist错误 原因:linux 上的mysql 默认是区分大小写导致的. 解决:改动mysql的配置文件,在my.cnf中的[mysqld]下面(位置不能错)加上lower_case_table_name=1这句(1表示不区分大小写,0区分大小写),保存重新启动mysql. 没有my.cnf文件,如果是默认安装执行以下列操作 [root@hqw mysql]# cp /usr/share/mysql/my-huge.cnf

Linux下MongoDB的安装,通过配置文件启动Mongodb的方式研究,mongodb自启动脚本(Linux),Windows下安装MongoDB服务

关于MongoDB的windows的 安装,可以参考: http://www.runoob.com/mongodb/mongodb-window-install.html 关于Linux的安装可以参考: http://www.runoob.com/mongodb/mongodb-linux-install.html 使用MongoDB的场景: A:需要写入大量的数据,但是这些数据的价值并不是太高,比如:日志 B:数据增长量较大,而且数据结构有时候还不一致 C:未来数据会很大. 注意:Mongod