Unix世界中一切皆文件的思想将socket通信变的简单的多, 通常我们直接可以用read,write等api函数作为socket通信的方法,这些api函数最终都会调用kernel提供的sys_XXX系列函数。平时用到的read等函数早以在c库中封装好了。其实我们可以自己直接向系统发送软中断int 0x80来执行sys_read函数,如:
int my_read(int fd, char * buf, off_t count) { long __res; __asm__ volatile ("push %%ebx; int $0x80; pop %%ebx" : "=a" (__res) : "0" (__NR_read), "ri" ((long)(fd), "c"((long)(buf), "d" ((long)(count)) :"memory"); return (int)(__res); }
由于内核和用户空间的数据段使用不同的段选择子,上面的程序由于涉及到 在内核空间和用户空间变换(int 0x80),所以需要通过set_fs(KERNEL_DS); 来设置段寄存器。(这个很重要)
客户端程序:
syscalls.h:
/* macros de syscalls */ int errno; #define my__syscall_return(type, res) \ do { \ if ((unsigned long)(res) >= (unsigned long)(-(128 + 1))) { \ errno = -(res); \ res = -1; \ } \ return (type) (res); \ } while (0) /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define my_syscall0(type,name) \ type name(void) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name)); \ my__syscall_return(type,__res); \ } #define my_syscall1(type,name,type1,arg1) \ type name(type1 arg1) \ { \ long __res; \ __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)) : "memory"); \ my__syscall_return(type,__res); \ } #define my_syscall2(type,name,type1,arg1,type2,arg2) \ type name(type1 arg1,type2 arg2) \ { \ long __res; \ __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)) \ : "memory"); \ my__syscall_return(type,__res); \ } #define my_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ type name(type1 arg1,type2 arg2,type3 arg3) \ { \ long __res; \ __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)) : "memory"); \ my__syscall_return(type,__res); \ } #define my_syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ { \ long __res; \ __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)),"S" ((long)(arg4)) : "memory"); \ my__syscall_return(type,__res); \ } #define my_syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ type5,arg5) \ type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ { \ long __res; \ __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; movl %1,%%eax ; " \ "int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "i" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)) \ : "memory"); \ my__syscall_return(type,__res); \ }kshell.c:
/* * kenel mode socket door v0.1 * * by wzt http://www.xsec.org */ #include <linux/types.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/sched.h> #include <linux/in.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/netdevice.h> #include <linux/dirent.h> #include <linux/proc_fs.h> #include <linux/errno.h> #include <net/tcp.h> #include <asm/processor.h> #include <asm/uaccess.h> #include <asm/unistd.h> #include <asm/ioctls.h> #include <asm/termbits.h> #include "syscalls.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("wzt"); #define __NR_e_exit __NR_exit #define SGID 0x489196ab #define HOME "/" #define port 22 static char *earg[4] = { "/bin/bash", "--noprofile", "--norc", NULL }; char *env[]={ "TERM=linux", "HOME=" HOME, "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin" ":/usr/local/sbin", "HISTFILE=/dev/null", NULL }; static inline my_syscall0(pid_t, fork); static inline my_syscall0(long, pause); static inline my_syscall2(int, kill, pid_t, pid, int, sig); static inline my_syscall1(int, chdir, const char *, path); static inline my_syscall1(long, ssetmask, int, newmask); static inline my_syscall3(int, write, int, fd, const char *, buf, off_t, count); static inline my_syscall3(int, read, int, fd, char *, buf, off_t, count); static inline my_syscall1(int, e_exit, int, exitcode); static inline my_syscall3(int, open, const char *, file, int, flag, int, mode); static inline my_syscall1(int, close, int, fd); static inline my_syscall2(int, dup2, int, oldfd, int, newfd); static inline my_syscall2(int, socketcall, int, call, unsigned long *, args); static inline my_syscall3(pid_t, waitpid, pid_t, pid, int *, status, int, options); static inline my_syscall3(int, execve, const char *, filename, const char **, argv, const char **, envp); static inline my_syscall3(long, ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg); static inline my_syscall5(int, _newselect, int, n, fd_set *, readfds, fd_set *, writefds, fd_set *, exceptfds, struct timeval *, timeout); static inline my_syscall2(unsigned long, signal, int, sig, __sighandler_t, handler); int k_listen(void *nouse) { struct task_struct *tsk = current; struct sockaddr_in serv_addr; struct sockaddr_in cli_addr; mm_segment_t old_fs; char buff[100]; unsigned long arg[3]; int sock_fd, sock_id; int tmp_kid; int i, n, cli_len; /* create socket */ arg[0] = AF_INET; arg[1] = SOCK_STREAM; arg[2] = 0; old_fs = get_fs(); set_fs(KERNEL_DS); if ((sock_fd = socketcall(SYS_SOCKET, arg)) == -1) { set_fs(old_fs); return 0; } printk("create socket ok.\n"); /* bind address */ memset((void *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = 0; arg[0] = sock_fd; arg[1] = (unsigned long) &serv_addr; arg[2] = (unsigned long) sizeof(serv_addr); if ((socketcall(SYS_BIND, arg)) == -1) { close(sock_fd); set_fs(old_fs); return 0; } printk("bind address ok.\n"); /* begin listen */ arg[0] = sock_fd; arg[1] = (unsigned long) 255; if ((socketcall(SYS_LISTEN, arg)) == -1) { close(sock_fd); set_fs(old_fs); return 0; } printk("listen on port %d\n", port); while(1){ char ch; printk("server waiting\n"); cli_len = sizeof(cli_addr); arg[0] = sock_fd; arg[1] = (unsigned long) &cli_addr; arg[2] = (unsigned long) &cli_len; if ((sock_id = socketcall(SYS_ACCEPT, arg)) == -1) { printk("accept error.\n"); close(sock_fd); set_fs(old_fs); return 0; } printk("accept a client.\n"); read(sock_id,&ch,1); printk("char from server is:%c\n",ch); ch ++; if(ch == 'Z') { break; } write(sock_id,&ch,1); close(sock_id); } close(sock_id); close(sock_fd); set_fs(old_fs); return 1; } static int ksocket_init(void) { printk("ksocket start.\n"); kernel_thread(k_listen,NULL,0); } static void ksocket_exit(void) { printk("ksocket exit.\n"); } module_init(ksocket_init); module_exit(ksocket_exit);
make 文件:
EXTRA_CFLAGS:= -g -O2 ifneq ($(KERNELRELEASE),) obj-m= net.o net-objs := kshell.o else KDIR := /home/sina/Debug/linux-3.0.1 all: make -C $(KDIR) M=$(PWD) KBUILD_EXTRA_SYMBOLS=./Module.symvers modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.order *~ *.symvers endif
将文件放入 busybox 编译的文件系统中测试(insmod 之后):
在另外一端用一个客户程序测试:
/* Make the necessary includes and set up the variables. */ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <netinet/in.h> #define port 22 int main(int argc,char *argv[]) { int sockfd; int len; struct sockaddr_in address; int result; char ch = *argv[1]; /* Create a socket for the client. */ sockfd = socket(AF_INET, SOCK_STREAM, 0); /* Name the socket, as agreed with the server. */ address.sin_family = AF_INET; address.sin_port = htons(port); //address.sin_addr.s_addr = inet_addr("192.168.100.2"); address.sin_addr.s_addr = inet_addr("192.168.100.2"); len = sizeof(address); /* Now connect our socket to the server's socket. */ result = connect(sockfd, (struct sockaddr *)&address, len); if(result == -1) { perror("oops: client1"); exit(1); } /* We can now read/write via sockfd. */ write(sockfd, &ch, 1); read(sockfd, &ch, 1); printf("char from server = %c\n", ch); close(sockfd); exit(0); }
在客户端测试:
sina@ubuntu:~/Debug/socket/virtual$ ./client A
\]char from server = B
sina@ubuntu:~/Debug/socket/virtual$ ./client G
char from server = H
sina@ubuntu:~/Debug/socket/virtual$ ./client E
char from server = F
sina@ubuntu:~/Debug/socket/virtual$ ./client Y
char from server = Y
此时虚拟机中显示如下:
由于客户机使用了宏定义 my_syscall 简化了程序的编写,但因为通过 int 调用,所以效率比较低,不是最好的办法。
根据安焦文章:
http://www.xfocus.net/articles/200808/985.html
整理而来。