哈工大操作系统试验2 系统调用

由于一时疏忽,导致who.c的代码不能通过部分测试数据,现已经改正。


今天整理了一下实验报告

在unistd.h中可以看到现在Linux 0.11 支持3个参数的传递。添加参数的方法大概有3条

               1.可以采用ESI,EDI,EBP,ESP这几个寄存器传递参数。

               2.可以采用《Linux 0.11注释》中提到的系统调用门的办法。

               3.可以开辟一块用户态的空间,允许内核态访问,传递参数时,只需传递此空间的首地址指针即可。

向linux 0.11添加一个系统调用foo()的步骤:

               首先。在内核中编写系统调用处理函数。
              其次。在include/unistd.h中添加系统调用的功能号(#define   __NR_foo   **)
                        并且相应的在include/linux/sys.h中声明新的系统调用处理函数以及添加系统
                        调用处理程序指针数组表中该项的索引值。在make file中添加新系统调用所在
                        文件的编译、链接规则(依赖关系)。修改system_call.s中系统调用总数。
              最后。在应用程序中提供接口,调用系统调用。




这次试验,需要用c来写,比上次要简单一些。

需要改写unistd.h       sys.h           system_call.s     makefile  4个文件

而且需要自己写出 who.c   iam.c    whoami.c   3个文件


sys.h在     linux-0.11/include/linux  之中,源文件关键处如下

extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid };

只需要把 iam与whoami两个函数加到全局变量,和中断函数表中就可以了,中断被调用的时候,先查找中断向量表,

找到相应的函数名,调用其函数。

extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
extern int sys_iam();
extern int sys_whoami();

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid,sys_iam,sys_whoami };
system_call.s 在    linux-0.11/kernel 中

# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12

nr_system_calls = 72
只需要把   nr_system_calls 改为 74 就好 ,其代表了中断函数的个数。修改后是这样。
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12

nr_system_calls = 74

Makefile  试验指导书上讲得很详细

修改Makefile

要想让我们添加的kernel/who.c可以和其它Linux代码编译链接到一起,必须要修改Makefile文件。Makefile里记录的是所有源程序文件的编译、链接规则,《注释》3.6节有简略介绍。我们之所以简单地运行make就可以编译整个代码树,是因为make完全按照Makefile里的指示工作。

Makefile在代码树中有很多,分别负责不同模块的编译工作。我们要修改的是kernel/Makefile。需要修改两处。一处是:

OBJS  = sched.o system_call.o traps.o asm.o fork.o \
        panic.o printk.o vsprintf.o sys.o exit.o \
        signal.o mktime.o

改为:

OBJS  = sched.o system_call.o traps.o asm.o fork.o \
        panic.o printk.o vsprintf.o sys.o exit.o \
        signal.o mktime.o who.o

另一处:

### Dependencies:
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
  ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
  ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
  ../include/asm/segment.h

改为:

### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
  ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
  ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
  ../include/asm/segment.h

Makefile修改后,和往常一样“make all”就能自动把who.c加入到内核中了。如果编译时提示who.c有错误,就说明修改生效了。所以,有意或无意地制造一两个错误也不完全是坏事,至少能证明Makefile是对的。


unistd.h 不能直接在oslab直接直接修改,而需要在虚拟机中修改,在oslab中有一个mount-hdc脚本

运行sudo ./mount-hdc 可以把虚拟机硬盘挂载在oslab/hdc 目录下。

在hdc/usr/include 目录下修改unistd.h 

关键原代码为

#define __NR_ssetmask	69
#define __NR_setreuid	70
#define __NR_setregid	71
只需要把两个自定义函数的宏定义在这里就好。

修改后为

#define __NR_ssetmask	69
#define __NR_setreuid	70
#define __NR_setregid	71
#define __NR_iam   72
#define __NR_whoami  73

这样修改工作就完成了成了

who.c 的代码如下

#define __LIBRARY__
#include 
#include 
#include 


char temp[64]={0};


int sys_iam(const char* name)
{
   int i=0;
   while(get_fs_byte(name+i)!='\0')
        i++;
   if(i>23){   
        return -EINVAL;
   } 
   printk("%d\n",i);
   i=0;
   while((temp[i]=get_fs_byte(name+i))!='\0'){
i++;
    }   
    return i;
}


int sys_whoami(char* name,unsigned int size)
{
    int i=0;
    while (temp[i]!='\0')
i++;
    if (size
写完后吧 who.c 放到linux-0.01/kernel   目录下   执行make all    who.c 就会被编译到内核里了

iam.c 与 whoami.c 两个文件是测试用的,可以在本地写好,通过挂载的hdc传到linux下,也可以通过vi在linux下写好(如果你蛋疼的话)

一定要在linux 执行编译 ,然后执行sync确保缓冲区数据写入磁盘就可以了。

代码如下 

iam.c

#define __LIBRARY__
#include 
#include 
#include 
#include 
_syscall1(int,iam,const char*,name)

int main(int argc,char* argv[])
{
	
    if(argc>1){
	if(iam(argv[1])<0){
		printf("SystemCall Exception!\n");
		return -1;
	}
    }
    else{
	printf("Input Exception!\n");
	return -1;
    }		
    return 0;
}

whoami.c

#define __LIBRARY__
#include 
#include 
#include 
#include 

_syscall2(int,whoami,char*,name,unsigned int,size)

int main()
{
    int counter;
	char buff[128]={0};
	
    counter=whoami(buff,128);
	if(counter < 0)
	{
	   printf("SystemCall Exception!");
	   return -1;
    	}
	else{
		printf("%s\n",buff);
    	}
    return 0;
}

最后需要用脚本测试一下,程序的正确性,把脚本放到iam.c   whoami.c同目录下 运行就好

脚本代码如下

经过分析它不会检查你的的  erron是不是正确,只会检查whoami返回的的字符串正不正确。

最后一个数据很阴险,注意字符串过长的时候,不要覆盖之前保存的字符串,就是说所保存的字符串是之前存的字符串就好。

#/bin/sh

string1="Sunner"
string2="Richard Stallman"
string3="This is a very very long string!"

score1=10
score2=10
score3=10

expected1="Sunner"
expected2="Richard Stallman"
expected3="Richard Stallman"

echo Testing string:$string1
./iam "$string1"
result=`./whoami`
if [ "$result" = "$expected1" ]; then
	echo PASS.
else
	score1=0
	echo FAILED.
fi
score=$score1

echo Testing string:$string2
./iam "$string2"
result=`./whoami`
if [ "$result" = "$expected2" ]; then
	echo PASS.
else
	score2=0
	echo FAILED.
fi
score=$score+$score2

echo Testing string:$string3
./iam "$string3"
result=`./whoami`
if [ "$result" = "$expected3" ]; then
	echo PASS.
else
	score3=0
	echo FAILED.
fi
score=$score+$score3

let "totalscore=$score"
echo Score: $score = $totalscore%


另外一个测试程序testlab2.c会检查你的返回值是否正确。



你可能感兴趣的:(os研究与HOOK)