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
此时虚拟机中显示如下:
根据安焦文章:
http://www.xfocus.net/articles/200808/985.html
整理而来。