Chinaunix首页 | 论坛 | 博客
  • 博客访问: 762122
  • 博文数量: 790
  • 博客积分: 40560
  • 博客等级: 大将
  • 技术积分: 5065
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-28 16:29
文章分类

全部博文(790)

文章存档

2011年(1)

2008年(789)

我的朋友

分类: LINUX

2008-08-28 17:12:43

 
系统:32位x86系统
OS:FC6
内核版本:2.6.18
目的:新增一个系统调用,将当前进程的UID和EUID都设置成0(超级用户)
步骤:
1内核源码树位置:SRC=/usr/src/redhat/BUILD/kernel2.6.18/linux-2.6.18.i386

2增加新系统调用号,修改头文件
$SRC/include/arm-i386/unistd.h



#define__NR_splice313
#define__NR_sync_file_range314
#define__NR_tee315
#define__NR_vmsplice316
#define__NR_move_pages317
#define__NR_mysyscall318/*新增加的一行,其中318是系统调用号,
根据系统不同自己定义与已经有的系统
调用号不相同即可*/
3修改系统调用表。修改文件
$SRC/arch/i386/kernel/syscall-table.s(注意,以前版本的内核应该修改和前面文件同

级目录下的entry.s)
ENTRY(sys_call_table)
.longsys_restart_syscall/*0-old"setup()"systemcall,used

forrestarting*/
.longsys_exit
.longsys_fork
.longsys_read
.longsys_write
.longsys_open/*5*/
.longsys_close
.longsys_waitpid
.longsys_creat
.longsys_link
.longsys_unlink/*10*/
.
.
.
.longsys_mysyscall/*新增加一行*/


4定义新系统调用的具体内容,一般可以直接在$SRC/kernel/sys.c中添加,
当然也可以加入与其功能紧密联系的代码中去。这里我加在$SRC/kernel/sys.c中,
在该文件末尾加入:
asmlinkageintsys_mysyscall(void)
{
current->uid=current->euid=0;
return0;
}

5通常我们在用户层使用系统调用都是通过C库支持来使用的,也就是说C库中对系统调用

进行了一次封装
,可以通过查看glibc的源码可以看到具体封装过程。由于glibc十分庞大,如果我们自己

新定义对上面新增
系统调用的C库函数的话,编译时间太长,所以我想直接利用LINUX提供的宏_syscallN来完

成。
其中N是系统调用的参数个数。举例说明该宏的用法:
对于系统调用open()的定义本来是:
longopen(constchar*filename,intflags,intmode)
如果不靠c库支持使用该系统调用方法如下:
#define__NR_open5/*5是open的系统调用号*/
_syscall3(long,open,constchar*filename,flags,mode)
有了这些定义后下面对open可以直接调用了
(一般书上都会讲到这种方法),但是在实际操作过程中,会发现在2.6.X中,已经取消了

宏_syscallN的定义,所以
你在编译的时候是通不过的。当然2.6取消了这些宏定义,我们可以自己在版本的代码中找

出这些宏定义加上即
在2.4.18内核的源代码中,可以在其内核源码树下include/asm-i386/unistd.h中找到这些

宏定义:
#define__syscall_return(type,res)\
do{\
if((unsignedlong)(res)>=(unsignedlong)(-125)){\
errno=-(res);\
res=-1;\
}\
return(type)(res);\
}while(0)

#define_syscall0(type,name)\
typename(void)\
{\
long__res;\
__asm__volatile("int$0x80"\
:"=a"(__res)\
:"0"(__NR_##name));\
__syscall_return(type,__res);\
}

#define_syscall1(type,name,type1,arg1)\
typename(type1arg1)\
{\
long__res;\
__asm__volatile("int$0x80"\
:"=a"(__res)\
:"0"(__NR_##name),"b"((long)(arg1)));\
__syscall_return(type,__res);\
}

#define_syscall2(type,name,type1,arg1,type2,arg2)\
typename(type1arg1,type2arg2)\
{\
long__res;\
__asm__volatile("int$0x80"\
:"=a"(__res)\
:"0"(__NR_##name),"b"((long)(arg1)),"c"((long)(arg2)));\
__syscall_return(type,__res);\
}



将这些代码copy到你自己的$SRC/include/asm-i386/unistd.h(我在实际中去掉了所有宏


包含变量errno的一行,因为在我们的代码中没有这个变量的定义,即使你在其中加入
externinterrno;
在编译没问题,链接也会有问题的,试试就知道了,如果非要使用errno,那就自己专研吧

,呵呵!)

6所有准备工作完成,现在就是编译新内核了(可能有人会提到将这个系统调用以模块方

式加载就不用重新
编译内核了,想法是好的,但是考虑到安全问题,不知道是从什么版本开始,已经取消了

对系统调用表
sys_call_table的导出,所以你在模块中是没法访问系统调用表的,当然也可以通过其他

途径来完成这个任务
,不过那是hacker们的办法了,这里不加讨论)
在$SRC中
makemenuconfig
make
makeinstall
即可,在2.6中已经对编译内核的方法简化了许多,不需要以前对依赖关系的检查了,新的

Makefile帮助完成这些工作了。
在makeinstall中已经帮你完成了
(1)将新内核镜像bzImage拷贝到/boot/vmlinuz-verison
(2)构建新的initrd-version.img文件
(3)创建新的内核符号表System.map(这个文件不是内核启动必须的,一般调试用)
(4)修改你的grub或者lilo启动文件
所以你只需要reboot就可以看到你新编译的内核了。至于内核的配置和vmlinuz和initrd2

个文件各自的用处,大家可以上网查询,这里
不详细介绍了。如果你的新内核无法启动,多半是initrd出了问题(简单的说下initrd文

件包含了你的内核在加载文件系统前的一些驱动程序
比如硬盘驱动,ext3文件系统的驱动等等,如果这些有问题,内核是无法启动的,网上说

可以把这些一起编译到内核vmlinuz中而不需要initrd,
大家可以去试试)
7现在新内核启动起来了,我们写一个用户测试程序mysyscall.c使用我们新的系统调用吧


#include
#include
#include
#include
#include
#define__NR_mysyscall318
_syscall0(int,mysyscall)
intmain()
{
printf("before,myeuidis:%d!\n",geteuid());
if(open("/etc/shadow",O_RDONLY)<0)
printf("openfailed!\n");

mysyscall();
printf("after,myeuidis%d!\n",geteuid());
if(open("/etc/shadow",O_RDONLY)>0)
printf("opensucceed!\n");

return0;
}
在普通用户下运行该程序
before,myeuidis:500!
openfailed!
after,myeuidis0!
opensucceed!

发现居然可以访问本来只有root能访问的shadown文件了o(∩_∩)o!

阅读(941) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~