Windows NT下的内核后门

Windows NT下的内核后门 - 网络安全 - 电脑教程网

Windows NT下的内核后门

日期:2006-05-24   荐:
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

[1] [2] [3]  

连接可以被看到.无法重定向是一个重大的缺陷.

然而,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)

 [1] [2] [3]  

===============================================

文件系统驱动中的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是非常不隐蔽的,我们可以通过修改一些系统启动时必须读装载的文件,选择一些内核或者用户态的文件,只要它有足够的权限。

(出处:http://www.sheup.com)


 [1] [2] [3] 


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

(出处:http://www.sheup.com)


 [1] [2] [3] [4] 

中的指针是否指向一个陌生的地址,或者检查函数体是否正常,可以很容易检测到,文件系统过滤驱动也很容易检测,通过调用IoGetDeviceObjectPointer然后检查DEVICE_OBJECT->StackSize.

所有正常的驱动在注册表中都有自己的键,也就是HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services


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

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

(出处:http://www.sheup.com)


 [1] [2] [3] [4] [5] 

标签: