Windows2000 内核级进程隐藏、侦测技术
指导老师:龙老师
学生:LionDB 学号:0137506
摘要
信息对抗是目前计算机发展的一个重要的方向,为了更好的防御,必须去深入的了解敌人进攻的招式。信息对抗促使信息技术飞速的发展。下面我选取了信息对抗技术的中一个很小一角关于windows内核级病毒隐藏技术和反病毒侦测技术作为议题详细讨论。
关键字:
内核, 拦截, 活动进程链表, 系统服务派遣表, 线程调度链
Abstract
Nowadays, information opposability is a very important development aspect in computer technique。In order to defense better, we must to deeply know army intrusion system by various methods。Information opposability technology cause Information technology development at very fast speed。Then I choose process hiding and detection in windows kernel model as my topic for particular discussion。It is a very small part of information opposability technology only。
KeyWord:
Kernel, Hook, Active Process Link, System Service Dispath Table, Dispatcher Thread Link
目录
1. 驱动程序简介
1.1 为什么选驱动程序
1.2 入口例程DriverEntry
1.3 Unload例程
1.4派遣例程
1.5驱动程序的安装
2. 通过Hook SSDT (System Service Dispath Table) 隐藏进程
驱动程序简介
1.为什么选驱动程序
驱动程序是运行在系统信任的Ring0环境下在代码,她拥有对系统任何软件和硬件的访问权限。这意味着内核驱动可以访问所有的系统资源,可以读取所有的内存空间,而且也被允许执行CPU的特权指令,如,读取CPU控制寄存器的当前值等。而处于用户模式下的程序如果试图从内核空间中读取一个字节或者试图执行像MOV EAX,CR3这样的汇编指令都会被立即终止掉。不过,这种强大的底线是驱动程序的一个很小的错误就会让整个系统崩溃。所以对隐藏和反隐藏技术来说都提供了一个极好的环境。但是又对攻击者和反查杀者提出了更高的技术要求。
2.入口例程DriverEntry
DriverEntry是内核模式驱动程序主入口点常用的名字,她的作用和main,WinMain,是一样的。
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
...
}
DriverEntry的第一个参数是一个指针,指向一个刚被初始化的驱动程序对象,该对象就代表你的驱动程序,DriverEntry的第二个参数是设备服务键的键名。DriverEntry函数返回一个NTSTATUS值。NTSTATUS实际就是一个长整型,但你应该使用NTSTATUS定义该函数的返回值而不是LONG,这样代码的可读性会更好。大部分内核模式支持例程都返回NTSTATUS状态代码,你可以在DDK头文件NTSTATUS.H中找到NTSTATUS的代码列表。
DriverEntry的作用主要就是创建设备对象,建立设备对象的符号链接,设置好各个类型的回调函数等。
例如:
extern "C"
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload; <--1
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->DriverStartIo = StartIo;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; <--2
DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchWmi;
...
}
在WDM中通过设置AddDevice回调函数来创建设备对象。在NT驱动中在DriverEntry例程中创建设备对象和符号链接。
例如:
RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer); //初始化设备名字
//创建设备
ntStatus = IoCreateDevice (DriverObject,
0,
&deviceNameUnicodeString,
##DeviceId,
0,
FALSE,
&deviceObject
);
if ( NT_SUCCESS ( ntStatus ) ) {
RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer); //初始化符号链接名字
//创建符号链接
ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString, &deviceNameUnicodeString);
if ( !NT_SUCCESS ( ntStatus ) ) {
IoDeleteDevice (deviceObject); //如果创建符号链接失败,删除设备
return ntStatus;
}
}
建立符号链接的作用就是暴露一个给应用程序的接口,应用程序可以通过CreateFile API打开链接符号,得到一个语柄,和我们的驱动程序进行交互操作。
3.Unload例程
虽然各个驱动程序的Unload例程不尽相同,但是它大致执行下列工作:
释放属于驱动程序的任何硬件。
从Win32的名字空间移除符号连接名。
这个动作可以调用IoDeleteSymbolicLink来实现。
使用IoDeleteDevice移除设备对象。
释放驱动程序持有的任何缓冲池等。
VOID DriverUnload ( IN PDRIVER_OBJECT pDriverObject )
{
PDEVICE_OBJECT pNextObj;
// 循环每一个驱动过程控制的设备
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
//从设备对象中取出设备Extension
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)extObj->DeviceExtension;
// 取出符号连接名
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName); //删除符号连接名
IoDeleteDevice(pNextObj); // 删除设备
pNextObj = pNextObj->NextDevice;
}
}
4. 派遣例程
Win2000的I/O请求是包驱动的,当一个I/O请求开始,I/O管理器先创建一个IRP去跟踪这个请求,另外,它存储一个功能代码在IRP的I/O堆栈区的MajorField域中来唯一的标识请求的类型。MajorField域是被I/O管理器用来索引驱动程序对象的MajorFunction表,这个表包含一个指向一个特殊I/O请求的派遣例程的功能指针,如果驱动程序不支持这个请求,MajorFunction表就会指向I/O管理器函数_IopInvalidDeviceRequest,该函数返回一个错误给原始的调用者。驱动程序的作者有责任提供所有的驱动程序支持的派遣例程。所有的驱动程序必须支持IRP_MJ_CREATE功能代码,因为这个功能代码是用来响应Win32用户模式的CreateFile调用,如果不支持这功能代码,Win32程序就没有办法获得设备的句柄,类似的,驱动程序必须支持IRP_MJ_CLOSE功能代码,因为它用来响应Win32用户模式的CloseHandle调用。顺便提一下,系统自动调用CloseHandle函数,因为在程序退出的时候,所有的句柄都没有被关闭。
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
//得到当前IRP (I/O请求包)
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch (irpSp->MajorFunction)
{
case IRP_MJ_CREATE:
DbgPrint("IRP_MJ_CREATE/n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
break;
case IRP_MJ_CLOSE:
DbgPrint("IRP_MJ_CLOSE/n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;