系统:32位x86系统BBdLinux联盟 OS:FC6BBdLinux联盟 内核版本:2.6.18BBdLinux联盟 目的:新增一个系统调用,将当前进程的UID和EUID都设置成0(超级用户)BBdLinux联盟 步骤:BBdLinux联盟 1 内核源码树位置:SRC=/usr/src/redhat/BUILD/kernel2.6.18/linux-2.6.18.i386BBdLinux联盟 BBdLinux联盟 2 增加新系统调用号,修改头文件BBdLinux联盟 $SRC/include/arm-i386/unistd.hBBdLinux联盟 。BBdLinux联盟 。BBdLinux联盟 。BBdLinux联盟 #define __NR_splice 313BBdLinux联盟 #define __NR_sync_file_range 314BBdLinux联盟 #define __NR_tee 315BBdLinux联盟 #define __NR_vmsplice 316BBdLinux联盟 #define __NR_move_pages 317BBdLinux联盟 #define __NR_mysyscall 318 /*新增加的一行,其中318是系统调用号,BBdLinux联盟 根据系统不同自己定义与已经有的系统BBdLinux联盟 调用号不相同即可*/BBdLinux联盟 3 修改系统调用表。修改文件BBdLinux联盟 $SRC/arch/i386/kernel/syscall-table.s(注意,以前版本的内核应该修改和前面文件同BBdLinux联盟 BBdLinux联盟 级目录下的entry.s)BBdLinux联盟 ENTRY(sys_call_table)BBdLinux联盟 .long sys_restart_syscall /* 0 - old "setup()" system call, usedBBdLinux联盟 BBdLinux联盟 for restarting */BBdLinux联盟 .long sys_exitBBdLinux联盟 .long sys_forkBBdLinux联盟 .long sys_readBBdLinux联盟 .long sys_writeBBdLinux联盟 .long sys_open /* 5 */BBdLinux联盟 .long sys_closeBBdLinux联盟 .long sys_waitpidBBdLinux联盟 .long sys_creatBBdLinux联盟 .long sys_linkBBdLinux联盟 .long sys_unlink /* 10 */BBdLinux联盟 .BBdLinux联盟 .BBdLinux联盟 .BBdLinux联盟 .long sys_mysyscall /*新增加一行*/BBdLinux联盟 BBdLinux联盟 BBdLinux联盟 4 定义新系统调用的具体内容,一般可以直接在$SRC/kernel/sys.c中添加,BBdLinux联盟 当然也可以加入与其功能紧密联系的代码中去。这里我加在$SRC/kernel/sys.c中,BBdLinux联盟 在该文件末尾加入:BBdLinux联盟 asmlinkage int sys_mysyscall(void)BBdLinux联盟 {BBdLinux联盟 current->uid = current->euid = 0;BBdLinux联盟 return 0;BBdLinux联盟 }BBdLinux联盟 BBdLinux联盟 5 通常我们在用户层使用系统调用都是通过C库支持来使用的,也就是说C库中对系统调用BBdLinux联盟 BBdLinux联盟 进行了一次封装BBdLinux联盟 ,可以通过查看glibc的源码可以看到具体封装过程。由于glibc十分庞大,如果我们自己BBdLinux联盟 BBdLinux联盟 新定义对上面新增BBdLinux联盟 系统调用的C库函数的话,编译时间太长,所以我想直接利用LINUX提供的宏_syscallN来完BBdLinux联盟 BBdLinux联盟 成。BBdLinux联盟 其中N是系统调用的参数个数。举例说明该宏的用法:BBdLinux联盟 对于系统调用open()的定义本来是:BBdLinux联盟 long open(const char* filename,int flags,int mode)BBdLinux联盟 如果不靠c库支持使用该系统调用方法如下:BBdLinux联盟 #define __NR_open 5 /*5是open的系统调用号*/BBdLinux联盟 _syscall3(long,open,const char* filename,flags,mode)BBdLinux联盟 有了这些定义后下面对open可以直接调用了BBdLinux联盟 (一般书上都会讲到这种方法),但是在实际操作过程中,会发现在2.6.X中,已经取消了BBdLinux联盟 BBdLinux联盟 宏_syscallN的定义,所以BBdLinux联盟 你在编译的时候是通不过的。当然2.6取消了这些宏定义,我们可以自己在版本的代码中找BBdLinux联盟 BBdLinux联盟 出这些宏定义加上即BBdLinux联盟 在2.4.18内核的源代码中,可以在其内核源码树下include/asm-i386/unistd.h中找到这些BBdLinux联盟 BBdLinux联盟 宏定义:BBdLinux联盟 #define __syscall_return(type, res) \BBdLinux联盟 do { \BBdLinux联盟 if ((unsigned long)(res) >= (unsigned long)(-125)) { \BBdLinux联盟 errno = -(res); \BBdLinux联盟 res = -1; \BBdLinux联盟 } \BBdLinux联盟 return (type) (res); \BBdLinux联盟 } while (0)BBdLinux联盟 BBdLinux联盟 #define _syscall0(type,name) \BBdLinux联盟 type name(void) \BBdLinux联盟 { \BBdLinux联盟 long __res; \BBdLinux联盟 __asm__ volatile ("int $0x80" \BBdLinux联盟 : "=a" (__res) \BBdLinux联盟 : "0" (__NR_##name)); \BBdLinux联盟 __syscall_return(type,__res); \BBdLinux联盟 }BBdLinux联盟 BBdLinux联盟 #define _syscall1(type,name,type1,arg1) \BBdLinux联盟 type name(type1 arg1) \BBdLinux联盟 { \BBdLinux联盟 long __res; \BBdLinux联盟 __asm__ volatile ("int $0x80" \BBdLinux联盟 : "=a" (__res) \BBdLinux联盟 : "0" (__NR_##name),"b" ((long)(arg1))); \BBdLinux联盟 __syscall_return(type,__res); \BBdLinux联盟 }BBdLinux联盟 BBdLinux联盟 #define _syscall2(type,name,type1,arg1,type2,arg2) \BBdLinux联盟 type name(type1 arg1,type2 arg2) \BBdLinux联盟 { \BBdLinux联盟 long __res; \BBdLinux联盟 __asm__ volatile ("int $0x80" \BBdLinux联盟 : "=a" (__res) \BBdLinux联盟 : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \BBdLinux联盟 __syscall_return(type,__res); \BBdLinux联盟 }BBdLinux联盟 。BBdLinux联盟 。BBdLinux联盟 。BBdLinux联盟 将这些代码copy到你自己的$SRC/include/asm-i386/unistd.h(我在实际中去掉了所有宏BBdLinux联盟 BBdLinux联盟 中BBdLinux联盟 包含变量errno的一行,因为在我们的代码中没有这个变量的定义,即使你在其中加入BBdLinux联盟 extern int errno;BBdLinux联盟 在编译没问题,链接也会有问题的,试试就知道了,如果非要使用errno,那就自己专研吧BBdLinux联盟 BBdLinux联盟 ,呵呵!)BBdLinux联盟 BBdLinux联盟 6 所有准备工作完成,现在就是编译新内核了(可能有人会提到将这个系统调用以模块方BBdLinux联盟 BBdLinux联盟 式加载就不用重新BBdLinux联盟 编译内核了,想法是好的,但是考虑到安全问题,不知道是从什么版本开始,已经取消了BBdLinux联盟 BBdLinux联盟 对系统调用表BBdLinux联盟 sys_call_table的导出,所以你在模块中是没法访问系统调用表的,当然也可以通过其他BBdLinux联盟 BBdLinux联盟 途径来完成这个任务BBdLinux联盟 ,不过那是hacker们的办法了,这里不加讨论)BBdLinux联盟 在$SRC中BBdLinux联盟 make menuconfigBBdLinux联盟 make BBdLinux联盟 make installBBdLinux联盟 即可,在2.6中已经对编译内核的方法简化了许多,不需要以前对依赖关系的检查了,新的BBdLinux联盟 BBdLinux联盟 Makefile帮助完成这些工作了。BBdLinux联盟 在make install中已经帮你完成了BBdLinux联盟 (1)将新内核镜像bzImage拷贝到/boot/vmlinuz-verisonBBdLinux联盟 (2)构建新的initrd-version.img文件BBdLinux联盟 (3)创建新的内核符号表System.map(这个文件不是内核启动必须的,一般调试用)BBdLinux联盟 (4)修改你的grub或者lilo启动文件BBdLinux联盟 所以你只需要reboot就可以看到你新编译的内核了。至于内核的配置和vmlinuz和initrd2BBdLinux联盟 BBdLinux联盟 个文件各自的用处,大家可以上网查询,这里BBdLinux联盟 不详细介绍了。如果你的新内核无法启动,多半是initrd出了问题(简单的说下initrd文BBdLinux联盟 BBdLinux联盟 件包含了你的内核在加载文件系统前的一些驱动程序BBdLinux联盟 比如硬盘驱动,ext3文件系统的驱动等等,如果这些有问题,内核是无法启动的,网上说BBdLinux联盟 BBdLinux联盟 可以把这些一起编译到内核vmlinuz中而不需要initrd,BBdLinux联盟 大家可以去试试)BBdLinux联盟 7 现在新内核启动起来了,我们写一个用户测试程序mysyscall.c使用我们新的系统调用吧BBdLinux联盟 BBdLinux联盟 。BBdLinux联盟 #include <linux/unistd.h>BBdLinux联盟 #include <stdio.h>BBdLinux联盟 #include <sys/types.h>BBdLinux联盟 #include <fcntl.h>BBdLinux联盟 #include <sys/stat.h>BBdLinux联盟 #define __NR_mysyscall 318BBdLinux联盟 _syscall0(int,mysyscall)BBdLinux联盟 int main()BBdLinux联盟 {BBdLinux联盟 printf("before, my euid is: %d!\n",geteuid());BBdLinux联盟 if(open("/etc/shadow",O_RDONLY)<0)BBdLinux联盟 printf("open failed!\n");BBdLinux联盟 BBdLinux联盟 mysyscall();BBdLinux联盟 printf("after, my euid is %d!\n",geteuid());BBdLinux联盟 if(open("/etc/shadow",O_RDONLY)>0)BBdLinux联盟 printf("open succeed!\n");BBdLinux联盟 BBdLinux联盟 return 0;BBdLinux联盟 }BBdLinux联盟 在普通用户下运行该程序BBdLinux联盟 before, my euid is: 500!BBdLinux联盟 open failed!BBdLinux联盟 after, my euid is 0!BBdLinux联盟 open succeed!BBdLinux联盟 BBdLinux联盟 发现居然可以访问本来只有root能访问的shadown文件了 o(∩_∩)o!BBdLinux联盟 欢迎大家讨论!BBdLinux联盟
|
|
|
|
|
|