在Linux可加载内核模块中探秘(3)
第一部分:基础知识
第三章:系统调用和内核符号表
作者:CoolBoy
上节我们讲到了insmod hello.o命令,在执行这个命令的时候,会呼叫系统调用create_module为LKM分配内存,并且会去读取内核符号表为LKM分配内核符号。那么,系统调用和内核符号表到底是什么呢?今天我们就要讨论这两样东西。
我想大家都知道,任何一种操作系统都会有一些内建于核心中的函数,这些函数能接触到计算机最底层的部分,实际上也就是操作系统的核心功能,它们虽然为数不多,并且相对来说比较简陋,但却可以完成对整个系统的任意操作。
——Linux中的这种函数就被称作“系统调用”。
系统调用是用户和核心空间之间传递信息的桥梁。例如,在用户空间中打开一个文件(比如c语言中很常见的fopen)在核心空间就由sys_open系统调用来解释。那么,我们的系统究竟提供了哪些系统调用呢?在linux系统中有这样一个文件/usr/include/sys/syscall.h,里面列出了所有的系统调用。比如我的syscall.h文件就是这个样子的:
#ifndef _SYS_SYSCALL_H
#define _SYS_SYSCALL_H
#define SYS_setup 0 /* Used only by init, to get system going. */
#define SYS_exit 1
#define SYS_fork 2
#define SYS_read 3
....
#define SYS_setresuid 164
#define SYS_getresuid 165
#define SYS_vm86 166
#define SYS_query_module 167
#define SYS_poll 168
#define SYS_syscall_poll SYS_poll
#endif /* */
从上面这个文件中,我们看到每个系统调用都对应一个预定义的数字代码(见上表),这个代码是用来代表每个系统调用的。
在Linux的核心中有一个特殊的结构,sys_call_table[]数组,数字代码和系统调用的映射关系就保存在这个数组里,一当系统要去调用某个系统函数时,就会根据这个数组查出相应的系统调用代码,然后根据这个代码通过0x80中断来执行真正的系统功能。
OK,掌握了以上知识之后,现在我们可以继续往下学习了。下面的表格中列出了一些最有趣最常用的系统调用,并给出了简短的功能描述。不过,如果你真想写一些派得上用场的LKM的话,可得仔细了解这些调用的工作过程哦。
系统调用:int sys_brk(unsigned long new_brk);
功能:改变DS(数据段)的大小(后面我们还将详细介绍它)。
系统调用:int sys_fork(struct pt_regs regs);
功能:和用户空间的fork()函数有相同功能。
系统调用:int sys_getuid () int sys_setuid (uid_t uid) ...
功能:管理用户ID(UID)的系统调用。
系统调用:int sys_get_kernel_sysms(struct kernel_sym *table)
功能:访问核心的系统表。见I.3
系统调用: int sys_sethostname (char *name, int len);
int sys_gethostname (char *name, int len);
功能:设置主机名字和查询主机名字。
系统调用: int sys_chdir (const char *path);
int sys_fchdir (unsigned int fd);
功能:二者均用于设置当前的目录。(即cd...命令)
系统调用: int sys_chmod (const char *filename, mode_t mode);
int sys_chown (const char *filename, mode_t mode);
int sys_fchmod (unsigned int fildes, mode_t mode);
int sys_fchown (unsigned int fildes, mode_t mode);
功能:管理用户权限。
系统调用:int sys_chroot (const char *filename);
功能:设置调用过程的根目录。
系统调用:int sys_execve (struct pt_regs regs);
功能:重要!这是执行文件的系统调用,pt_regs是寄存器栈。
系统调用:long sys_fcntl (unsigned int fd, unsigned int cmd, unsigned long arg);
功能:改变文件描述子的属性。
系统调用: int sys_link (const char *oldname, const char *newname);
int sym_link (const char *oldname, const char *newname);
int sys_unlink (const char *name);
功能:硬/软 链接的管理。
系统调用:int sys_rename (const char *oldname, const char *newname);
功能:文件重命名。
系统调用: int sys_rmdir (const char* name);
int sys_mkdir (const *char filename, int mode);
系统调用: 功能:创建和删除目录。
系统调用: int sys_open (const char *filename, int mode);
int sys_close (unsigned int fd);
功能:打开文件和关闭文件。
系统调用: int sys_read (unsigned int fd, char *buf, unsigned int count);
int sys_write (unsigned int fd, char *buf, unsigned int count);
功能:读文件和写文件。
系统调用:int sys_getdents (unsigned int fd, struct dirent *dirent, unsigned int count);
功能:查询文件列表(即ls...命令)。
系统调用:int sys_readlink (const char *path, char *buf, int bufsize);
功能:读取符号链接。
系统调用:int sys_selectt (int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp);
功能:IO复合操作。
系统调用:sys_socketcall (int call, unsigned long args);
功能:socket函数 。
系统调用: unsigned long sys_create_module (char *name, unsigned long size);
int sys_delete_module (char *name);
int sys_query_module (const char *name, int which, void *buf, size_t bufsize, size_t *ret);
功能:加载/卸载和查询LKM程序。
从黑客的角度来看,上述系统调用是最有趣的一部分,当然,也许在你的cool系统上还需要用到某些特殊的调用,但是,对于普通的黑客来说,使用上述列举的已经足够了。在后面几节,我们将学习怎么使用这些调用。
那么,什么又是内核符号表呢?
在/proc/ksyms文件中,就清清楚楚地把各种内核符号展示出来了,这个文件中的每一行就表示一个分配好了的(并且是没有保密的)内核符号,所以我们的LKM可以轻易读取这些核心符号。为什么我们的LKM需要访问这里?各位同学可以仔细看看这个文件,呵呵,我敢打包票你越看就会觉得越有趣,这里面不仅能看到一些很敏感的函数,甚至还能发现我们自己的LKM函数,因为这块公用田对什么都不保密,全都公开了。
不过,有一点要注意的是,既然我们能从这里看到LKM,那么系统管理员也一定能看到,说不定管理员看到后只一瞬间就把我们的LKM给灭了,那样就不大有趣了。那么,要把LKM隐藏起来不让人知道,有什么好办法呢?在后面有部分篇幅我们会讲述多种隐藏之道,不过这里我们要先讲最简单、最有效、最高明的一种办法。:)
其实,我们根本就不需要用到任何非法的手段,或是利用系统缺陷来防止我们的LKM输出到这个公开的内核符号表中,我们唯一需要做的就是另外建立一个我们自己的内核符号表,让LKM输出的内核符号到我们自己的内核表中去!怎么做呢?很简单:
static struct symbol_table module_syms= { /*定义我们自己的符号表!*/
#include /*这就是我们要输出的符号表*/
...
};
register_symtab(&module_syms); /*注册我们的符号表*/
因为我们不要输出任何符号到公共地段,所以我们使用下面的代码来初始化我们的LKM:
register_symtab(NULL);
注意:这行代码必须写在init_module()函数中,它表示我们不要使用缺省的符号表。
好啦,系统调用和内核符号就解释到这里,今天主要是给大家为下一节的课程做一个准备,了解这些知识之后,我们就好在下一节的“切入核心”课程中游刃有余啦。
发布人:Crystal 来自:Linux专区