搜索文章:

首页  |  Java技术  |  Asp.net  |  Asp编程  |  VC/C++  |  Delphi  |  VB编程

Windows NT下的内核后门

1 - 序言

2 - 概述当前windows nt下的内核后门
2.1 - ntrootkit
2.2 - he4hook
2.3 - slanret (ierk, backdoor-ali)

3 - obscurity 在磁盘,注册表和内存

4 - 我的不同: 痛苦之路
4.1 - shell
4.2 - 激活和与远端client通信
4.3 - obscurity 在磁盘上

5 - 总结
6 - 尾声
7 - 资源
8 - 文件

--[ 1 - 序言

本文面向的读者是了解windows nt内核结构,nt驱动的运作原理。本文测试在发展内核后门时遇到的问题.

最近,有一种趋势在流行,那就是把windows nt从家用或者办公用扩展到作为服务器来使用.同时,已经过时的windows 9x 系列
被windows nt系列代替,这样,远程管理工具(backdoors)和隐藏权限工具(rootkits)变得很有价值。大多数的已经公布的,用户态的工具
可以被杀毒软件或手工检查发现.

内核后门有很多好处:可以对用户态工具来说完全隐藏,杀毒软件不得不提供一些内核态组件来检测内核后门.防护内核后门的软件已经有了
(比如ipd,"完整性检测驱动"),但是它的用处不是很广泛.内核后门使用的不是非常广泛,应为它们和用户态后门相比,相对而言要复杂得多

--[2 - 概述当前windows nt下的内核后门

本节简要的回顾一下当前已经存在的windows nt下的内核后门.

----[ 2.1 - ntrootkit

ntrootkit © 是由 greg hoglund 和 一群自愿者一起创建并维护的设备驱动程序,工作在windows nt 4.0和windows 2000
下。它有可能(已经实现和准备实现):

- 从远程客户端接受命令.rk_packet模块含有一个简单的ip栈,使用被装有rootkit的那台主机所位于的子网上的一个未被使用的ip地址

它的mac 和 ip地址被硬编码在源代码中,用这个ip连接rootkit通过一个tcp连接,可以是到任何端口

ps - 列出进程
help - 帮助
buffertest,echo and debugint - 调试用
hidedir - 隐藏目录,文件
hideproc - 隐藏进程
sniffkeys - 键盘监视器

也有很多未完成的代码段:通过一个隐蔽的通道接受命令并且执行,从驱动中产生一个win32进程(非常难的,复杂的工作)

- 使用schneier的blowfish算法加密所有通信:rk_blowfish.c有这个文件,但是还没有被使用

- 自我保护 (rk_defence.c) - 隐藏受保护的对象(例如:注册表),以_root_作为标记;重定向已运行的进程

实现进程,目录和文件的隐藏的代码位于rk_ioman.c中,通过hook下列api来实现:

ntcreatefile
zwopenfile
zwquerydirectoryfile
zwopenkey
zwquerykey
zwqueryvaluekey
zwenumeratevaluekey
zwenumeratekey
zwsetvaluekey
zwcreatekey

检测这个rootkit的办法:

直接调用文件驱动,发送irp请求给它.还有另外一个模块hook了文件句柄:rk_files.c,这个文件参考了fileman的代码,不过没有使用

- 创建进程:一个未完成的执行组件可以在rk_command.c里面找到,另外一个(完成的并且是很好用的)是rk_exec.c

执行遇到的问题是zw*函数通常不能直接在驱动中获得,而是要调用system call接口(int 0x2e),nt 家族的不同版本由于system call num
可能改变,导致出现问题.

看起来对ntrootkit的工作是十分宽松的:每个开发者做他们认为有必要或者说急迫的事情.ntrootkit没有达到完全隐藏.它创建了一个
叫"ntroot"的设备,可以在用户态下看到

当实际使用ntrootkit时,人们需要一些和rootkit通信的办法,简而言之:需要一些shell.ntrootkit本身不能直接给出一个shell
尽管他可以创建一个进程---但是进程的i/o不能重定向.因此不得不启动nc这样的程序.ntrootkit可以隐藏进程,但是进程的tcp/ip
连接可以被看到.无法重定向是一个重大的缺陷.

然而,ntrootkit仍然在继续发展,将来很可能就是一个非常完善的rootkit.

----[ 2.2 - he4hook

这节的讨论基于[2].对于目前的2.15b6版来说,文件系统的访问通过2中不同的办法进行hook.它一次只能运用一种办法,而在2.15b6以后的版本,第一种办法将不再使用

方法a:hook kernel syscalls
===============================

zwcreatefile, zwopenfile - driver version 1.12 and from 1.17 to
2.15beta6
iocreatefile - from 1.13 to 2.15beta6
zwquerydirectoryfile, zwclose - before 2.15beta6

几乎所有的导出函数(zw***)有如下的函数体
mov eax, numberfunction
lea edx, [esp+04h]
int 2eh ; syscall interface

"numberfunction"是函数在syscall table中的编号(syscall table可以通过一个全局变量keservicedescriptortable来访问
这个变量指向下述结构

typedef struct systemservicedescriptortable
{
ssd systemservicedescriptors[4];
} ssdt, *lpssdt;

其它的几个结构
typedef void *sstat[];
typedef unsigned char sstpt[];
typedef sstat *lpsstat;
typedef sstpt *lpsstpt;

typedef struct systemservicedescriptor
{
lpsstat lpsystemservicetableaddresstable;
ulong dwfirstserviceindex;
ulong dwsystemservicetablenumentries;
lpsstpt lpsystemservicetableparametertable;
} ssd, *lpssd;

keservicedescriptortable指向的描述符表只能在内核态下才能访问,在用户态下,有一个keservicedescriptortableshadow,可惜没有被导出

基本系统服务在
keservicedescriptortable->systemservicedescriptors[0]
keservicedescriptortableshadow->systemservicedescriptors[0]

内核的gui系统服务在
keservicedescriptortableshadow->systemservicedescriptors[1]

这些表的其他元素已被发现,表的每个元素都是一个ssid结构,包含了下列数据

lpsystemservicetableaddresstable - a pointer to an array of addresses
of functions that will be called if
a matching syscall is called

dwfirstserviceindex - start index for the first function

dwsystemservicetablenumentries - number of services in table

lpsystemservicetableparametertable - an array of bytes specifying the
number of bytes from the stack that
will be passed through

为了hook一个系统调用,he4hookinv 替换了 keservicedescriptortable->systemservicedescriptos[0] 中存储的地址,使其指向自己构造的表

人们可以添加自己定义的系统服务到system call tables.he4hookinv更新2个表

- keservicedescriptortable
- keservicedescriptortableshadow.

否则,如果它只更新keservicedescriptortable这一个表,新的系统服务在用户态下不可用,为了定位keservicedescriptortableshadow,he4hookinv使用如下技术

keaddsystemservicetable函数可以用来向内核添加新的系统服务,它可以把系统服务添加到2个表中,考虑到它的0-th描述符是一致的,很可能,它扫描keaddsystemservicetable函数的代码,
来找出shadowtable的地址,你可以看看he4hook是怎么做的,在he4hookinv.c中的findshadowtable(void)函数

如果这个办法失败,he4hook使用一个硬编码的地址((keservicedescriptortable-0x230)来定位shadowtable的位置.这个地址从winnt-sp3来就没有变过.
另外一个问题是如何找到系统服务的编号,这个其实很简单,由于系统服务的函数体都具有以下形式(mov eax, numberfunction),所以我们只要把系统服务的函数地址加上
1bytes,就可以得到系统服务对应的编号。

method b: (for driver versions 2.11 and higher)
===============================================

文件系统驱动中的driver_object注册的回调函数表被修改了:irp句柄被替换,包括替换指向基本功能的指针(driver_object->majorfunction)和驱动的卸载例程

下面的例程被挂接:
irp_mj_create
irp_mj_create_named_pipe
irp_mj_create_mailslot
irp_mj_directory_control -> irp_mn_query_directory

详细的文件操作重定向请见source[2]

----[ 2.3 - slanret (ierk, backdoor-ali)

这个东东的源代码没有,它最初是一些管理员在网络上发现的,它看起来是一个正常的驱动("ierk8243.sys"),但是会周期性地导致蓝屏死机
在"服务"中察看它,它的名字是"virtual memory manager".

"slanret是一个root的一个组件。。。。。。。。"


----[ 3. stealth on disk, in registry and in memory

rootkit可以截获较低层的 i/o,因此要检测rootkit的存在是非常难的。可靠的截取是基于低级的磁盘操作(读/写扇区),这就要求
处理所有的文件系统:fat16, fat32, ntfs。

虽然fat相对容易处理(一些老的dos病毒使用相似的技术),
while fat was relatively easy to deal with (and some old dos stealth
viruses used similar techniques) an implementation of something similar
on winnt is a task for maniacs.

第2个hook的地方是hook 文件系统驱动的dispatch 的例程,这种方法相对比较通用,也正是he4hookinv使用的方法

第3个可能的办法是在文件系统驱动(fsd)上设置一个过滤器(filter),与之前2个办法相比,这个没有任何优势,而且更容易被发现(filemon使用这个办法)

zw**和io**等函数可以通过修改keservicedescriptortable或者直接修改函数体等办法来被hook,但是通过检查keservicedescriptortable
中的指针是否指向一个陌生的地址,或者检查函数体是否正常,可以很容易检测到,文件系统过滤驱动也很容易检测,通过调用iogetdeviceobjectpointer然后检查device_object->stacksize.

所有正常的驱动在注册表中都有自己的键,也就是hkey_local_machine\system\currentcontrolset\services


以上提到的rootkit都可以隐藏注册表键,但是显而易见的是,如果系统"干净"地启动,
管理员可以看见任何隐藏的东西.通常也可以
通过zwsetsysteminformation(systemloadandcallimage)来加载rootkit,而不需要创建任何注册表值,这种技术的一个例子在[6]中给出.

从一个单独的文件中装载rootkiy是非常不隐蔽的,我们可以通过修改一些系统启动时必须读装载的文件,选择一些内核或者用户态的文件,只要它有足够的权限。

()

相关文章:
© 2006   www.java-asp.net