Windows驱动开发入门-磁盘过滤驱动

碎碎念

这个驱动属于boot类型,这类驱动程序是启动最早的驱动程序,系统引导时必须加载完毕。需要在注册表HKEY_LOCAL_MACHINES\SYSTEM\CurrentControlSet\Services下驱动服务的start值需要指定为0。

驱动分析

驱动入口DriverEntry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NTSTATUS DriverEntry(IN	PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
int i;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
//初始化这个驱动所有的分发函数,默认值是初始化为DPDispatchAny
DriverObject->MajorFunction[i] = DPDispatchAny;
//下面将我们特殊关注的分发函数重新赋值为我们自己的处理函数
DriverObject->MajorFunction[IRP_MJ_POWER] = DPDispatchPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = DPDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DPDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_READ] = DPDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DPDispatchReadWrite;
//将这个驱动的AddDevice函数初始化为DpAddDevice函数
DriverObject->DriverExtension->AddDevice = DPAddDevice;
//将这个驱动的unload函数初始化为DpUnload函数
DriverObject->DriverUnload = DPUnload;
//注册一个boot驱动结束回调,这个回调函数会在所有的boot型驱动都运行完毕之后再去执行
IoRegisterBootDriverReinitialization(DriverObject, DPReinitializationRoutine, NULL);
//作为一个过滤驱动,无论如何都要返回成功
return STATUS_SUCCESS;
};

过滤设备基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//用来存储一个卷所有的相关信息的数据结构,放在过滤设备的设备扩展中
typedef struct _DP_FILTER_DEV_EXTENSION_{
//卷的名字,例如"C:,D:"等中的字母部分
WCHAR VolumeLetter;
//这个卷是否在保护状态
BOOL Protect;
//这个卷的总大小,以byte为单位
LARGE_INTEGER TotalSizeInByte;
//这个卷上文件系统的每簇大小,以byte为单位
DWORD ClusterSizeInByte;
//这个卷的每个扇区大小,以byte为单位
DWORD SectorSizeInByte;
//这个卷设备对应的过滤设备的设备对象
PDEVICE_OBJECT FltDevObj;
//这个卷设备对应的过滤设备的下层设备对象
PDEVICE_OBJECT LowerDevObj;
//这个卷设备对应的物理设备的设备对象
PDEVICE_OBJECT PhyDevObj;
//这个数据结构是否已经被初始化完毕了
BOOL InitializeCompleted;
//这个卷上的保护系统使用的位图的句柄
PDP_BITMAP Bitmap;
//用来转储的文件句柄
HANDLE TempFile;
//这个卷上的保护系统使用的请求队列
LIST_ENTRY ReqList;
//这个卷上的保护系统使用的请求队列的锁
KSPIN_LOCK ReqLock;
//这个卷上的保护系统使用的请求队列的同步事件
KEVENT ReqEvent;
//这个卷上的保护系统使用的请求队列的处理线程之线程句柄
PVOID ThreadHandle;
//这个卷上的保护系统使用的请求队列的处理线程之结束标志
BOOLEAN ThreadTermFlag;
//这个卷上的保护系统的关机分页电源请求的计数事件
KEVENT PagingPathCountEvent;
//这个卷上的保护系统的关机分页电源请求的计数
LONG PagingPathCount;
} DP_FILTER_DEV_EXTENSION, * PDP_FILTER_DEV_EXTENSION;

AddDevice

建立一个过滤设备,并绑定在真正的磁盘卷设备上。当任何磁盘卷设备建立时调用AddDevice函数,这时磁盘卷已经建立但还不能相应大部分IRP请求。绑定后先于磁盘卷设备接收到IRP请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
NTSTATUS DPAddDevice(IN	PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT	PhysicalDeviceObject) {
//NTSTATUS类型的函数返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = NULL;
//过滤设备的下层设备的指针对象
PDEVICE_OBJECT LowerDevObj = NULL;
//过滤设备的设备指针的指针对象
PDEVICE_OBJECT FltDevObj = NULL;
//过滤设备的处理线程的线程句柄
HANDLE ThreadHandle = NULL;
//建立一个过滤设备,这个设备是FILE_DEVICE_DISK类型的设备并且具有DP_FILTER_DEV_EXTENSION类型的设备扩展
ntStatus = IoCreateDevice(DriverObject, sizeof(DP_FILTER_DEV_EXTENSION), NULL, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &FltDevObj);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//将DevExt指向过滤设备的设备扩展指针
DevExt = FltDevObj->DeviceExtension;
//清空过滤设备的设备扩展
RtlZeroMemory(DevExt, sizeof(DP_FILTER_DEV_EXTENSION));
//将刚刚建立的过滤设备附加到这个卷设备的物理设备上
LowerDevObj = IoAttachDeviceToDeviceStack(FltDevObj, PhysicalDeviceObject);
if (NULL == LowerDevObj) {
ntStatus = STATUS_NO_SUCH_DEVICE;
goto ERROUT;
};
//初始化这个卷设备的分页路径计数的计数事件
KeInitializeEvent(&DevExt->PagingPathCountEvent, NotificationEvent, TRUE);
//对过滤设备的设备属性进行初始化,过滤设备的设备属性应该和它的下层设备相同
FltDevObj->Flags = LowerDevObj->Flags;
//给过滤设备的设备属性加上电源可分页的属性
FltDevObj->Flags |= DO_POWER_PAGABLE;
//对过滤设备进行设备初始化
FltDevObj->Flags &= ~DO_DEVICE_INITIALIZING;
//将过滤设备对应的设备扩展中的相应变量进行初始化
//卷设备的过滤设备对象
DevExt->FltDevObj = FltDevObj;
//卷设备的物理设备对象
DevExt->PhyDevObj = PhysicalDeviceObject;
//卷设备的下层设备对象
DevExt->LowerDevObj = LowerDevObj;
//初始化这个卷的请求处理队列
InitializeListHead(&DevExt->ReqList);
//初始化请求处理队列的锁
KeInitializeSpinLock(&DevExt->ReqLock);
//初始化请求处理队列的同步事件
KeInitializeEvent(&DevExt->ReqEvent, SynchronizationEvent, FALSE);
//初始化终止处理线程标志
DevExt->ThreadTermFlag = FALSE;
//建立用来处理这个卷的请求的处理线程,线程函数的参数则是设备扩展
ntStatus = PsCreateSystemThread(&ThreadHandle, (ACCESS_MASK)0L, NULL, NULL, NULL, DPReadWriteThread, DevExt);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//获取处理线程的对象
ntStatus = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, &DevExt->ThreadHandle, NULL);
if (!NT_SUCCESS(ntStatus)) {
DevExt->ThreadTermFlag = TRUE;
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
goto ERROUT;
};
ERROUT:
if (!NT_SUCCESS(ntStatus)) {
//如果上面有不成功的地方,首先需要解除可能存在的附加
if (NULL != LowerDevObj) {
IoDetachDevice(LowerDevObj);
DevExt->LowerDevObj = NULL;
};
//然后删除可能建立的过滤设备
if (NULL != FltDevObj) {
IoDeleteDevice(FltDevObj);
DevExt->FltDevObj = NULL;
};
};
//关闭线程句柄,我们今后不会用到它,所有对线程的引用都通过线程对象来进行了
if (NULL != ThreadHandle)
ZwClose(ThreadHandle);
//返回状态值
return ntStatus;
};

PnP请求响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
NTSTATUS DPDispatchPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向irp stack的指针
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
switch (irpsp->MinorFunction) {
case IRP_MN_REMOVE_DEVICE: {
//如果是PnP manager发过来的移除设备的irp,将进入这里 这里主要做一些清理工作
if (DevExt->ThreadTermFlag != TRUE && NULL != DevExt->ThreadHandle) {
//如果线程还在运行的话需要停止它,这里通过设置线程停止运行的标志并且发送事件信息,让线程自己终止运行
DevExt->ThreadTermFlag = TRUE;
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
//等待线程结束
KeWaitForSingleObject(DevExt->ThreadHandle, Executive, KernelMode, FALSE, NULL);
//解除引用线程对象
ObDereferenceObject(DevExt->ThreadHandle);
};
if (NULL != DevExt->Bitmap)
//如果还有位图,就释放
DPBitmapFree(DevExt->Bitmap);
if (NULL != DevExt->LowerDevObj)
//如果存在着下层设备,就先去掉挂接
IoDetachDevice(DevExt->LowerDevObj);
if (NULL != DevExt->FltDevObj)
//如果存在过滤设备,就要删除它
IoDeleteDevice(DevExt->FltDevObj);
break;
};
//这个是PnP 管理器用来询问设备能否支持特殊文件的irp,作为卷的过滤驱动,我们必须处理
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
BOOLEAN setPagable;
//如果是询问是否支持休眠文件和dump文件,则直接下发给下层设备去处理
if (irpsp->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
ntStatus = DPSendToNextDriver(DevExt->LowerDevObj, Irp);
return ntStatus;
};
//这里等一下分页计数事件
ntStatus = KeWaitForSingleObject(&DevExt->PagingPathCountEvent, Executive, KernelMode, FALSE, NULL);
//setPagable初始化为假,是没有设置过DO_POWER_PAGABLE的意思
setPagable = FALSE;
if (!irpsp->Parameters.UsageNotification.InPath && DevExt->PagingPathCount == 1) {
//如果是PnP manager通知我们将要删去分页文件,且我们目前只剩下最后一个分页文件的时候会进入这里
if (DeviceObject->Flags & DO_POWER_INRUSH) {}
else {
//到这里说明没有分页文件在这个设备上了,需要设置DO_POWER_PAGABLE这一位了
DeviceObject->Flags |= DO_POWER_PAGABLE;
setPagable = TRUE;
};
};
//到这里肯定是关于分页文件的是否可建立查询,或者是删除的通知,我们交给下层设备去做。这里需要用同步的方式给下层设备,也就是说要等待下层设备的返回
ntStatus = DPForwardIrpSync(DevExt->LowerDevObj, Irp);
if (NT_SUCCESS(ntStatus)) {
//如果发给下层设备的请求成功了,说明下层设备支持这个操作,会执行到这里 在成功的条件下我们来改变我们自己的计数值,这样就能记录我们现在这个设备上到底有多少个分页文件
IoAdjustPagingPathCount(&DevExt->PagingPathCount, irpsp->Parameters.UsageNotification.InPath);
if (irpsp->Parameters.UsageNotification.InPath)
if (DevExt->PagingPathCount == 1)
//如果这个请求是一个建立分页文件的查询请求,并且下层设备支持这个请求,而且这是第一个在这个设备上的分页文件,那么我们需要清除DO_POWER_PAGABLE位
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
}
else
//到这里说明给下层设备发请求失败了,下层设备不支持这个请求,这时候我们需要把之前做过的操作还原
if (setPagable == TRUE) {
//根据setPagable变量的值来判断我们之前是否做过对DO_POWER_PAGABLE的设置,如果有的话就清楚这个设置
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
setPagable = FALSE;
};
//设置分页计数事件
KeSetEvent(&DevExt->PagingPathCountEvent, IO_NO_INCREMENT, FALSE);
//到这里我们就可以完成这个irp请求了
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
};
default:
break;
};
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

Power请求响应

Windows Vista前后处理方式不一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
NTSTATUS DPDispatchPower(IN	PDEVICE_OBJECT DeviceObject, IN	PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
#if (NTDDI_VERSION < NTDDI_VISTA)
//如果是vista以前的版本的windows,需要使用特殊的向下层设备转发的函数
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(DevExt->LowerDevObj, Irp);
#else
//如果是vista系统,可以使用和一般下发irp一样的方法来下发
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
#endif
};

DeviceIoControl请求响应

磁盘卷设备过滤驱动是不需要响应的,转发给下层设备处理就行。当截获到IOCTL_VOLUME_ONLINE请求时开始初始化,这个请求作用时把目标卷设备设置为在线状态,之后才会有读写请求。先下方这个请求,这时卷设备开始运行,过滤驱动再开始初始化,这里使用同步的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
NTSTATUS DPDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向irp stack的指针
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
//用来同步IOCTL_VOLUME_ONLINE处理的事件
KEVENT Event;
//用来传给IOCTL_VOLUME_ONLINE的完成函数的上下文
VOLUME_ONLINE_CONTEXT context;
switch (irpsp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_VOLUME_ONLINE: {
//如果是卷设备的IOCTL_VOLUME_ONLINE,会进入到这里 我们打算自己处理这个irp请求,这里先初始化一个事件用来在这个请求的完成函数里面做同步信号
KeInitializeEvent(&Event, NotificationEvent, FALSE);
//给这个请求的完成函数初始化参数
context.DevExt = DevExt;
context.Event = &Event;
//这里copy一份irp stack
IoCopyCurrentIrpStackLocationToNext(Irp);
//设置完成函数
IoSetCompletionRoutine(Irp, DPVolumeOnLineCompleteRoutine, &context, TRUE, TRUE, TRUE);
//调用下层设备来处理这个irp
ntStatus = IoCallDriver(DevExt->LowerDevObj, Irp);
//等待下层设备处理结束这个irp
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
//返回
return ntStatus;
};
default:
//对于其它DeviceIoControl,我们一律调用下层设备去处理
break;
};
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

DPVolumeOnLineCompleteRoutine

上面那个请求的完成函数,调用这个函数时对应的磁盘卷设备已经可以工作了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
NTSTATUS DPVolumeOnLineCompleteRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOLUME_ONLINE_CONTEXT Context) {
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//这个卷设备的dos名字,也就是C,D等
UNICODE_STRING DosName = { 0 };
//在这里Context是不可能为空的,为空就是出错了
ASSERT(Context != NULL);
//下面调用我们自己的VolumeOnline处理
//获取这个卷的dos名字
ntStatus = IoVolumeDeviceToDosName(Context->DevExt->PhyDevObj, &DosName);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//将dos名字变成大写形式
Context->DevExt->VolumeLetter = DosName.Buffer[0];
if (Context->DevExt->VolumeLetter > L'Z')
Context->DevExt->VolumeLetter -= (L'a' - L'A');
//我们只保护“D”盘
if (Context->DevExt->VolumeLetter == L'D') {
//获取这个卷的基本信息
ntStatus = DPQueryVolumeInformation(Context->DevExt->PhyDevObj, &(Context->DevExt->TotalSizeInByte), &(Context->DevExt->ClusterSizeInByte), &(Context->DevExt->SectorSizeInByte));
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//建立这个卷对应的位图
ntStatus = DPBitmapInit(&Context->DevExt->Bitmap, Context->DevExt->SectorSizeInByte, 8, 25600, (DWORD)(Context->DevExt->TotalSizeInByte.QuadPart / (LONGLONG)(25600 * 8 * Context->DevExt->SectorSizeInByte)) + 1);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//对全局量赋值,说明我们找到需要保护的那个设备了
gProtectDevExt = Context->DevExt;
};
ERROUT:
if (!NT_SUCCESS(ntStatus)) {
if (NULL != Context->DevExt->Bitmap)
DPBitmapFree(Context->DevExt->Bitmap);
if (NULL != Context->DevExt->TempFile)
ZwClose(Context->DevExt->TempFile);
};
if (NULL != DosName.Buffer)
ExFreePool(DosName.Buffer);
//设置等待同步事件,这样可以让我们等待的DeviceIoControl处理过程继续运行
KeSetEvent(Context->Event, 0, FALSE);
return STATUS_SUCCESS;
};

bitmap结构

bitmap为一些内存块,每一个bit对应的扇区是否修改。当为0时表示对应扇区未修改,读取时读取原位置即可。当为1时为已修改,需要到转存的地方读。重启为全部都0,相当于恢复到之前状态。

这部分不想学就别学了,会用接口就行。

内部数据结构组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef unsigned char tBitmap;
typedef struct _DP_BITMAP_{
//这个卷中的每个扇区有多少字节,这同样也说明了bitmap中一个位所对应的字节数
unsigned long sectorSize;
//每个byte里面有几个bit,一般情况下是8
unsigned long byteSize;
//每个块是多大byte,
unsigned long regionSize;
//这个bitmap总共有多少个块
unsigned long regionNumber;
//这个块对应了多少个实际的byte,这个数字应该是sectorSize*byteSize*regionSize
unsigned long regionReferSize;
//这个bitmap对应了多少个实际的byte,这个数字应该是sectorSize*byteSize*regionSize*regionNumber
__int64 bitmapReferSize;
//指向bitmap存储空间的指针
tBitmap** Bitmap;
//用于存取bitmap的锁
void* lockBitmap;
} DP_BITMAP, * PDP_BITMAP;

初始化一个bitmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
NTSTATUS DPBitmapInit(DP_BITMAP** bitmap, unsigned long sectorSize, unsigned long byteSize, unsigned long regionSize, unsigned long regionNumber) {
int i = 0;
DP_BITMAP* myBitmap = NULL;
NTSTATUS status = STATUS_SUCCESS;
//检查参数,以免使用了错误的参数导致发生处零错等错误
if (NULL == bitmap || 0 == sectorSize || 0 == byteSize || 0 == regionSize || 0 == regionNumber)
return STATUS_UNSUCCESSFUL;
__try {
//分配一个bitmap结构,这是无论如何都要分配的,这个结构相当于一个bitmap的handle
if (NULL == (myBitmap = (DP_BITMAP*)DPBitmapAlloc(0, sizeof(DP_BITMAP)))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
};
//清空结构
memset(myBitmap, 0, sizeof(DP_BITMAP));
//根据参数对结构中的成员进行赋值
myBitmap->sectorSize = sectorSize;
myBitmap->byteSize = byteSize;
myBitmap->regionSize = regionSize;
myBitmap->regionNumber = regionNumber;
myBitmap->regionReferSize = sectorSize * byteSize * regionSize;
myBitmap->bitmapReferSize = (__int64)sectorSize * (__int64)byteSize * (__int64)regionSize * (__int64)regionNumber;
//分配出regionNumber那么多个指向region的指针,这是一个指针数组
if (NULL == (myBitmap->Bitmap = (tBitmap**)DPBitmapAlloc(0, sizeof(tBitmap*) * regionNumber))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
};
//清空指针数组
memset(myBitmap->Bitmap, 0, sizeof(tBitmap*) * regionNumber);
*bitmap = myBitmap;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
if (!NT_SUCCESS(status)) {
if (NULL != myBitmap)
DPBitmapFree(myBitmap);
*bitmap = NULL;
};
return status;
};

bitmap的置位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
NTSTATUS DPBitmapSet(DP_BITMAP* bitmap, LARGE_INTEGER offset, unsigned long length) {
__int64 i = 0;
unsigned long myRegion = 0, myRegionEnd = 0;
unsigned long myRegionOffset = 0, myRegionOffsetEnd = 0;
unsigned long myByteOffset = 0, myByteOffsetEnd = 0;
unsigned long myBitPos = 0;
NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER setBegin = { 0 }, setEnd = { 0 };
__try {
//检查变量
if (NULL == bitmap || offset.QuadPart < 0) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
if (0 != offset.QuadPart % bitmap->sectorSize || 0 != length % bitmap->sectorSize) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
//根据要设置的偏移量和长度来计算需要使用到哪些region,如果需要的话,就分配他们指向的内存空间
myRegion = (unsigned long)(offset.QuadPart / (__int64)bitmap->regionReferSize);
myRegionEnd = (unsigned long)((offset.QuadPart + (__int64)length) / (__int64)bitmap->regionReferSize);
for (i = myRegion; i <= myRegionEnd; ++i)
if (NULL == *(bitmap->Bitmap + i))
{
if (NULL == (*(bitmap->Bitmap + i) = (tBitmap*)DPBitmapAlloc(0, sizeof(tBitmap) * bitmap->regionSize))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
else
memset(*(bitmap->Bitmap + i), 0, sizeof(tBitmap) * bitmap->regionSize);
};
//开始设置bitmap,首先我们需要将要设置的区域按照byte对齐,这样可以按byte设置而不需要按bit设置,加快设置速度 对于没有byte对齐的区域先手工设置掉他们
for (i = offset.QuadPart; i < offset.QuadPart + (__int64)length; i += bitmap->sectorSize) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (0 == myBitPos) {
setBegin.QuadPart = i;
break;
};
*(*(bitmap->Bitmap + myRegion) + myByteOffset) |= bitmapMask[myBitPos];
};
if (i >= offset.QuadPart + (__int64)length) {
status = STATUS_SUCCESS;
__leave;
};
for (i = offset.QuadPart + (__int64)length - bitmap->sectorSize; i >= offset.QuadPart; i -= bitmap->sectorSize) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (7 == myBitPos) {
setEnd.QuadPart = i;
break;
};
*(*(bitmap->Bitmap + myRegion) + myByteOffset) |= bitmapMask[myBitPos];
};
if (i < offset.QuadPart || setEnd.QuadPart == setBegin.QuadPart) {
status = STATUS_SUCCESS;
__leave;
};
myRegionEnd = (unsigned long)(setEnd.QuadPart / (__int64)bitmap->regionReferSize);
for (i = setBegin.QuadPart; i <= setEnd.QuadPart;) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
//如果我们设置的区域没有跨两个region,只需要使用memset去做按byte的设置然后跳出即可
if (myRegion == myRegionEnd) {
myRegionOffsetEnd = (unsigned long)(setEnd.QuadPart % (__int64)bitmap->regionReferSize);
myByteOffsetEnd = myRegionOffsetEnd / bitmap->byteSize / bitmap->sectorSize;
memset(*(bitmap->Bitmap + myRegion) + myByteOffset, 0xff, myByteOffsetEnd - myByteOffset + 1);
break;
}
//如果我们设置的区域跨了两个region,需要设置完后递增
else {
myRegionOffsetEnd = bitmap->regionReferSize;
myByteOffsetEnd = myRegionOffsetEnd / bitmap->byteSize / bitmap->sectorSize;
memset(*(bitmap->Bitmap + myRegion) + myByteOffset, 0xff, myByteOffsetEnd - myByteOffset);
i += (myByteOffsetEnd - myByteOffset) * bitmap->byteSize * bitmap->sectorSize;
};
};
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
if (!NT_SUCCESS(status)) {};
return status;
};

获取指定区域bitmap的方法:一部分从原始数据来,另一部分从转存数据来,这两个内存缓冲区内容根据指定bitmap进行合并。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
NTSTATUS DPBitmapGet(DP_BITMAP* bitmap, LARGE_INTEGER offset, unsigned long length, void* bufInOut, void* bufIn) {
unsigned long i = 0;
unsigned long myRegion = 0;
unsigned long myRegionOffset = 0;
unsigned long myByteOffset = 0;
unsigned long myBitPos = 0;
NTSTATUS status = STATUS_SUCCESS;
__try {
//检查参数
if (NULL == bitmap || offset.QuadPart < 0 || NULL == bufInOut || NULL == bufIn) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
if (0 != offset.QuadPart % bitmap->sectorSize || 0 != length % bitmap->sectorSize) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
//遍历需要获取的位图范围,如果出现了位被设置为1,就需要用bufIn参数中指向的相应位置的数据拷贝到bufInOut中
for (i = 0; i < length; i += bitmap->sectorSize) {
myRegion = (unsigned long)((offset.QuadPart + (__int64)i) / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)((offset.QuadPart + (__int64)i) % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (NULL != *(bitmap->Bitmap + myRegion) && (*(*(bitmap->Bitmap + myRegion) + myByteOffset) & bitmapMask[myBitPos]))
memcpy((tBitmap*)bufInOut + i, (tBitmap*)bufIn + i, bitmap->sectorSize);
};
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
return status;
};

最后的准备工作

最后这一部分准备工作准备放到boot驱动完成回调函数中。

在这个示例中,我们把数据转存到另一个卷的稀疏文件中。建立稀疏文件时不占实际存储空间,写入多少申请多少,当然实际总大小不得超过该磁盘卷大小。也就是说例如1GB的磁盘可以申请10GB的稀疏文件,只要数据实际大小比1GB要小就行。这里选择放到另一个卷上,要不写这个文件时又要被该驱动捕获,造成重入。

申请稀疏文件需要在NTFS文件系统建立后进行,所以选择在boot驱动完成回调函数中进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
VOID DPReinitializationRoutine(IN PDRIVER_OBJECT DriverObject, IN PVOID Context, IN ULONG Count) {
//返回值
NTSTATUS ntStatus;
//D盘的缓冲文件名
WCHAR SparseFilename[] = L"\\??\\E:\\temp.dat";
UNICODE_STRING SparseFilenameUni;
//建立文件时的io操作状态值
IO_STATUS_BLOCK ios = { 0 };
//建立文件时的对象属性变量
OBJECT_ATTRIBUTES ObjAttr = { 0 };
//设置文件大小的时候使用的文件结尾描述符
FILE_END_OF_FILE_INFORMATION FileEndInfo = { 0 };
//打开我们将要用来做转储的文件 初始化要打开的文件名
RtlInitUnicodeString(&SparseFilenameUni, SparseFilename);
//初始化文件名对应的对象名,这里需要将其初始化为内核对象,并且大小写不敏感
InitializeObjectAttributes(&ObjAttr, &SparseFilenameUni, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
//建立文件,这里需要注意的是,要加入FILE_NO_INTERMEDIATE_BUFFERING选项,避免文件系统再缓存这个文件
ntStatus = ZwCreateFile(&gProtectDevExt->TempFile, GENERIC_READ | GENERIC_WRITE, &ObjAttr, &ios, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//设置这个文件为稀疏文件
ntStatus = ZwFsControlFile(gProtectDevExt->TempFile, NULL, NULL, NULL, &ios, FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//设置这个文件的大小为"D"盘的大小并且留出10m的保护空间
FileEndInfo.EndOfFile.QuadPart = gProtectDevExt->TotalSizeInByte.QuadPart + 10 * 1024 * 1024;
ntStatus = ZwSetInformationFile(gProtectDevExt->TempFile, &ios, &FileEndInfo, sizeof(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//如果成功初始化就将这个卷的保护标志设置为在保护状态
gProtectDevExt->Protect = TRUE;
return;
ERROUT:
KdPrint(("error create temp file!\n"));
return;
};

读写请求排队

bitmap操作没法并行处理,除不需要保护的卷外,其他必须顺序放入处理队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NTSTATUS DPDispatchReadWrite(IN	PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
if (DevExt->Protect) {
//这个卷在保护状态,我们首先把这个irp设为pending状态
IoMarkIrpPending(Irp);
//然后将这个irp放进相应的请求队列里
ExInterlockedInsertTailList(&DevExt->ReqList, &Irp->Tail.Overlay.ListEntry, &DevExt->ReqLock);
//设置队列的等待事件,通知队列对这个irp进行处理
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
//返回pending状态,这个irp就算处理完了
return STATUS_PENDING;
}
else
//这个卷不在保护状态,直接交给下层设备进行处理
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

读写请求处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
VOID DPReadWriteThread(IN PVOID Context){
//NTSTATUS类型的函数返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = (PDP_FILTER_DEV_EXTENSION)Context;
//请求队列的入口
PLIST_ENTRY ReqEntry = NULL;
//irp指针
PIRP Irp = NULL;
//irp stack指针
PIO_STACK_LOCATION Irpsp = NULL;
//irp中包括的数据地址
PBYTE sysBuf = NULL;
//irp中的数据长度
ULONG length = 0;
//irp要处理的偏移量
LARGE_INTEGER offset = { 0 };
//文件缓冲指针
PBYTE fileBuf = NULL;
//设备缓冲指针
PBYTE devBuf = NULL;
//io操作状态
IO_STATUS_BLOCK ios;
//设置这个线程的优先级
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
//下面是线程的实现部分,这个循环永不退出
for (;;){
//先等待请求队列同步事件,如果队列中没有irp需要处理,我们的线程就等待在这里,让出cpu时间给其它线程
KeWaitForSingleObject(&DevExt->ReqEvent,Executive,KernelMode,FALSE,NULL);
//如果有了线程结束标志,那么就在线程内部自己结束自己
if (DevExt->ThreadTermFlag) {
//这是线程的唯一退出地点
PsTerminateSystemThread(STATUS_SUCCESS);
return;
};
//从请求队列的首部拿出一个请求来准备处理,这里使用了自旋锁机制,所以不会有冲突
while (ReqEntry = ExInterlockedRemoveHeadList(&DevExt->ReqList,&DevExt->ReqLock)){
//从队列的入口里找到实际的irp的地址
Irp = CONTAINING_RECORD(ReqEntry, IRP, Tail.Overlay.ListEntry);
//取得irp stack
Irpsp = IoGetCurrentIrpStackLocation(Irp);
//获取这个irp其中包含的缓存地址,这个地址可能来自mdl,也可能就是直接的缓冲,这取决于我们当前设备的io方式是buffer还是direct方式
if (NULL == Irp->MdlAddress)
sysBuf = (PBYTE)Irp->UserBuffer;
else
sysBuf = (PBYTE)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
if (IRP_MJ_READ == Irpsp->MajorFunction) {
//如果是读的irp请求,我们在irp stack中取得相应的参数作为offset和length
offset = Irpsp->Parameters.Read.ByteOffset;
length = Irpsp->Parameters.Read.Length;
}
else if (IRP_MJ_WRITE == Irpsp->MajorFunction) {
//如果是写的irp请求,我们在irp stack中取得相应的参数作为offset和length
offset = Irpsp->Parameters.Write.ByteOffset;
length = Irpsp->Parameters.Write.Length;
}
else {
//除此之外,offset和length都是0
offset.QuadPart = 0;
length = 0;
};
if (NULL == sysBuf || 0 == length)
//如果传下来的irp没有系统缓冲或者缓冲的长度是0,那么我们就没有必要处理这个irp,直接下发给下层设备就行了
goto ERRNEXT;
//下面是转储的过程了
if (IRP_MJ_READ == Irpsp->MajorFunction) {
//这里是读的处理 首先根据bitmap来判断这次读操作读取的范围是全部为转储空间,还是全部为未转储空间,或者兼而有之
long tstResult = DPBitmapTest(DevExt->Bitmap, offset, length);
switch (tstResult) {
case BITMAP_RANGE_CLEAR:
//这说明这次读取的操作全部是读取未转储的空间,也就是真正的磁盘上的内容,我们直接发给下层设备去处理
goto ERRNEXT;
case BITMAP_RANGE_SET:
//这说明这次读取的操作全部是读取已经转储的空间,也就是缓冲文件上的内容,我们从文件中读取出来,然后直接完成这个irp 分配一个缓冲区用来从缓冲文件中读取
if (NULL == (fileBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(fileBuf, length);
ntStatus = ZwReadFile(DevExt->TempFile, NULL, NULL, NULL, &ios, fileBuf, length, &offset, NULL);
if (NT_SUCCESS(ntStatus)) {
Irp->IoStatus.Information = length;
RtlCopyMemory(sysBuf, fileBuf, Irp->IoStatus.Information);
goto ERRCMPLT;
}
else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
break;
case BITMAP_RANGE_BLEND:
//这说明这次读取的操作是混合的,我们也需要从下层设备中读出,同时从文件中读出,然后混合并返回 分配一个缓冲区用来从缓冲文件中读取
if (NULL == (fileBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(fileBuf, length);
//分配一个缓冲区用来从下层设备中读取
if (NULL == (devBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(devBuf, length);
ntStatus = ZwReadFile(DevExt->TempFile, NULL, NULL, NULL, &ios, fileBuf, length, &offset, NULL);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//把这个irp发给下层设备去获取需要从设备上读取的信息
ntStatus = DPForwardIrpSync(DevExt->LowerDevObj, Irp);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//将从下层设备获取到的数据存储到devBuf中
memcpy(devBuf, sysBuf, Irp->IoStatus.Information);
//把从文件获取到的数据和从设备获取到的数据根据相应的bitmap值来进行合并,合并的结果放在devBuf中
ntStatus = DPBitmapGet(DevExt->Bitmap, offset, length, devBuf, fileBuf);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//把合并完的数据存入系统缓冲并完成irp
memcpy(sysBuf, devBuf, Irp->IoStatus.Information);
goto ERRCMPLT;
default:
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
};
}
else {
//这里是写的过程 对于写,我们直接写缓冲文件,而不会写磁盘数据,这就是所谓的转储,但是转储之后需要在bitmap中做相应的标记
ntStatus = ZwWriteFile(DevExt->TempFile, NULL, NULL, NULL, &ios, sysBuf, length, &offset, NULL);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
}
else {
if (NT_SUCCESS(ntStatus = DPBitmapSet(DevExt->Bitmap, offset, length)))
goto ERRCMPLT;
else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
};
};
};
ERRERR:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPCompleteRequest(Irp,ntStatus,IO_NO_INCREMENT);
continue;
ERRNEXT:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPSendToNextDriver(DevExt->LowerDevObj,Irp);
continue;
ERRCMPLT:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPCompleteRequest(Irp,STATUS_SUCCESS,IO_DISK_INCREMENT);
continue;
};
};
return;
};

完整代码

Druver,c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
#include <ntifs.h>
#include <windef.h>
#include <mountmgr.h>
#include <mountdev.h>
#include <ntddvol.h>
#include <ntstrsafe.h>
#include "DPBitmap.h"
#include "Driver.h"

//当我们判断出那个设备是需要保护的时候,会将这个指针指向这个需要保护设备的DevExt
PDP_FILTER_DEV_EXTENSION gProtectDevExt = NULL;

VOID DPReinitializationRoutine(IN PDRIVER_OBJECT DriverObject, IN PVOID Context, IN ULONG Count) {
//返回值
NTSTATUS ntStatus;
//D盘的缓冲文件名
WCHAR SparseFilename[] = L"\\??\\E:\\temp.dat";
UNICODE_STRING SparseFilenameUni;
//建立文件时的io操作状态值
IO_STATUS_BLOCK ios = { 0 };
//建立文件时的对象属性变量
OBJECT_ATTRIBUTES ObjAttr = { 0 };
//设置文件大小的时候使用的文件结尾描述符
FILE_END_OF_FILE_INFORMATION FileEndInfo = { 0 };
//打开我们将要用来做转储的文件 初始化要打开的文件名
RtlInitUnicodeString(&SparseFilenameUni, SparseFilename);
//初始化文件名对应的对象名,这里需要将其初始化为内核对象,并且大小写不敏感
InitializeObjectAttributes(&ObjAttr, &SparseFilenameUni, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
//建立文件,这里需要注意的是,要加入FILE_NO_INTERMEDIATE_BUFFERING选项,避免文件系统再缓存这个文件
ntStatus = ZwCreateFile(&gProtectDevExt->TempFile, GENERIC_READ | GENERIC_WRITE, &ObjAttr, &ios, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//设置这个文件为稀疏文件
ntStatus = ZwFsControlFile(gProtectDevExt->TempFile, NULL, NULL, NULL, &ios, FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//设置这个文件的大小为"D"盘的大小并且留出10m的保护空间
FileEndInfo.EndOfFile.QuadPart = gProtectDevExt->TotalSizeInByte.QuadPart + 10 * 1024 * 1024;
ntStatus = ZwSetInformationFile(gProtectDevExt->TempFile, &ios, &FileEndInfo, sizeof(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//如果成功初始化就将这个卷的保护标志设置为在保护状态
gProtectDevExt->Protect = TRUE;
return;
ERROUT:
KdPrint(("error create temp file!\n"));
return;
};

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
int i;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
//初始化这个驱动所有的分发函数,默认值是初始化为DPDispatchAny
DriverObject->MajorFunction[i] = DPDispatchAny;
//下面将我们特殊关注的分发函数重新赋值为我们自己的处理函数
DriverObject->MajorFunction[IRP_MJ_POWER] = DPDispatchPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = DPDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DPDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_READ] = DPDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DPDispatchReadWrite;
//将这个驱动的AddDevice函数初始化为DpAddDevice函数
DriverObject->DriverExtension->AddDevice = DPAddDevice;
//将这个驱动的unload函数初始化为DpUnload函数
DriverObject->DriverUnload = DPUnload;
//注册一个boot驱动结束回调,这个回调函数会在所有的boot型驱动都运行完毕之后再去执行
IoRegisterBootDriverReinitialization(DriverObject, DPReinitializationRoutine, NULL);
//作为一个过滤驱动,无论如何都要返回成功
return STATUS_SUCCESS;
};

NTSTATUS DPCompleteRequest(IN PIRP Irp, IN NTSTATUS Status, IN CCHAR Priority) {
//将IRP的io状态赋值为传入的参数
Irp->IoStatus.Status = Status;
//调用IoCompleteRequest来完成这个Irp
IoCompleteRequest(Irp, Priority);
return STATUS_SUCCESS;
};

NTSTATUS DPSendToNextDriver(IN PDEVICE_OBJECT TgtDevObj, IN PIRP Irp) {
//跳过当前的irp stack
IoSkipCurrentIrpStackLocation(Irp);
//调用目标设备来处理这个irp
return IoCallDriver(TgtDevObj, Irp);
};

NTSTATUS DPIrpCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) {
//将传入的上下文参数转化为一个内核事件对象
PKEVENT Event = (PKEVENT)Context;
//忽略掉传入的不需要的参数
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
//设置这个对象,唤醒等待它的进程
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
//返回
return STATUS_MORE_PROCESSING_REQUIRED;
};

NTSTATUS DPForwardIrpSync(IN PDEVICE_OBJECT TgtDevObj, IN PIRP Irp) {
//用来等待的事件
KEVENT event;
//返回值
NTSTATUS status;
//初始化等待事件
KeInitializeEvent(&event, NotificationEvent, FALSE);
//拷贝一份irp stack
IoCopyCurrentIrpStackLocationToNext(Irp);
//设置完成函数,并且将等待事件设置为上面初始化的事件,如果完成函数被调用,这个事件将会被设置,同时也由此获知这个irp处理完成了
IoSetCompletionRoutine(Irp, DPIrpCompletionRoutine, &event, TRUE, TRUE, TRUE);
//调用目标设备去处理这个irp
status = IoCallDriver(TgtDevObj, Irp);
//如果调用返回的是STATUS_PENDING,说明目标设备在处理这个irp的时候需要更多的时间,我们就开始等待,直到它处理完毕为止
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
//等到了之后赋值状态变量
status = Irp->IoStatus.Status;
};
//返回状态变量
return status;
};

VOID DPReadWriteThread(IN PVOID Context){
//NTSTATUS类型的函数返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = (PDP_FILTER_DEV_EXTENSION)Context;
//请求队列的入口
PLIST_ENTRY ReqEntry = NULL;
//irp指针
PIRP Irp = NULL;
//irp stack指针
PIO_STACK_LOCATION Irpsp = NULL;
//irp中包括的数据地址
PBYTE sysBuf = NULL;
//irp中的数据长度
ULONG length = 0;
//irp要处理的偏移量
LARGE_INTEGER offset = { 0 };
//文件缓冲指针
PBYTE fileBuf = NULL;
//设备缓冲指针
PBYTE devBuf = NULL;
//io操作状态
IO_STATUS_BLOCK ios;
//设置这个线程的优先级
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
//下面是线程的实现部分,这个循环永不退出
for (;;){
//先等待请求队列同步事件,如果队列中没有irp需要处理,我们的线程就等待在这里,让出cpu时间给其它线程
KeWaitForSingleObject(&DevExt->ReqEvent,Executive,KernelMode,FALSE,NULL);
//如果有了线程结束标志,那么就在线程内部自己结束自己
if (DevExt->ThreadTermFlag) {
//这是线程的唯一退出地点
PsTerminateSystemThread(STATUS_SUCCESS);
return;
};
//从请求队列的首部拿出一个请求来准备处理,这里使用了自旋锁机制,所以不会有冲突
while (ReqEntry = ExInterlockedRemoveHeadList(&DevExt->ReqList,&DevExt->ReqLock)){
//从队列的入口里找到实际的irp的地址
Irp = CONTAINING_RECORD(ReqEntry, IRP, Tail.Overlay.ListEntry);
//取得irp stack
Irpsp = IoGetCurrentIrpStackLocation(Irp);
//获取这个irp其中包含的缓存地址,这个地址可能来自mdl,也可能就是直接的缓冲,这取决于我们当前设备的io方式是buffer还是direct方式
if (NULL == Irp->MdlAddress)
sysBuf = (PBYTE)Irp->UserBuffer;
else
sysBuf = (PBYTE)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
if (IRP_MJ_READ == Irpsp->MajorFunction) {
//如果是读的irp请求,我们在irp stack中取得相应的参数作为offset和length
offset = Irpsp->Parameters.Read.ByteOffset;
length = Irpsp->Parameters.Read.Length;
}
else if (IRP_MJ_WRITE == Irpsp->MajorFunction) {
//如果是写的irp请求,我们在irp stack中取得相应的参数作为offset和length
offset = Irpsp->Parameters.Write.ByteOffset;
length = Irpsp->Parameters.Write.Length;
}
else {
//除此之外,offset和length都是0
offset.QuadPart = 0;
length = 0;
};
if (NULL == sysBuf || 0 == length)
//如果传下来的irp没有系统缓冲或者缓冲的长度是0,那么我们就没有必要处理这个irp,直接下发给下层设备就行了
goto ERRNEXT;
//下面是转储的过程了
if (IRP_MJ_READ == Irpsp->MajorFunction) {
//这里是读的处理 首先根据bitmap来判断这次读操作读取的范围是全部为转储空间,还是全部为未转储空间,或者兼而有之
long tstResult = DPBitmapTest(DevExt->Bitmap, offset, length);
switch (tstResult) {
case BITMAP_RANGE_CLEAR:
//这说明这次读取的操作全部是读取未转储的空间,也就是真正的磁盘上的内容,我们直接发给下层设备去处理
goto ERRNEXT;
case BITMAP_RANGE_SET:
//这说明这次读取的操作全部是读取已经转储的空间,也就是缓冲文件上的内容,我们从文件中读取出来,然后直接完成这个irp 分配一个缓冲区用来从缓冲文件中读取
if (NULL == (fileBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(fileBuf, length);
ntStatus = ZwReadFile(DevExt->TempFile, NULL, NULL, NULL, &ios, fileBuf, length, &offset, NULL);
if (NT_SUCCESS(ntStatus)) {
Irp->IoStatus.Information = length;
RtlCopyMemory(sysBuf, fileBuf, Irp->IoStatus.Information);
goto ERRCMPLT;
}
else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
break;
case BITMAP_RANGE_BLEND:
//这说明这次读取的操作是混合的,我们也需要从下层设备中读出,同时从文件中读出,然后混合并返回 分配一个缓冲区用来从缓冲文件中读取
if (NULL == (fileBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(fileBuf, length);
//分配一个缓冲区用来从下层设备中读取
if (NULL == (devBuf = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, length, 'xypD'))) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
RtlZeroMemory(devBuf, length);
ntStatus = ZwReadFile(DevExt->TempFile, NULL, NULL, NULL, &ios, fileBuf, length, &offset, NULL);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//把这个irp发给下层设备去获取需要从设备上读取的信息
ntStatus = DPForwardIrpSync(DevExt->LowerDevObj, Irp);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//将从下层设备获取到的数据存储到devBuf中
memcpy(devBuf, sysBuf, Irp->IoStatus.Information);
//把从文件获取到的数据和从设备获取到的数据根据相应的bitmap值来进行合并,合并的结果放在devBuf中
ntStatus = DPBitmapGet(DevExt->Bitmap, offset, length, devBuf, fileBuf);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
goto ERRERR;
};
//把合并完的数据存入系统缓冲并完成irp
memcpy(sysBuf, devBuf, Irp->IoStatus.Information);
goto ERRCMPLT;
default:
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
};
}
else {
//这里是写的过程 对于写,我们直接写缓冲文件,而不会写磁盘数据,这就是所谓的转储,但是转储之后需要在bitmap中做相应的标记
ntStatus = ZwWriteFile(DevExt->TempFile, NULL, NULL, NULL, &ios, sysBuf, length, &offset, NULL);
if (!NT_SUCCESS(ntStatus)) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
}
else {
if (NT_SUCCESS(ntStatus = DPBitmapSet(DevExt->Bitmap, offset, length)))
goto ERRCMPLT;
else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto ERRERR;
};
};
};
ERRERR:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPCompleteRequest(Irp,ntStatus,IO_NO_INCREMENT);
continue;
ERRNEXT:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPSendToNextDriver(DevExt->LowerDevObj,Irp);
continue;
ERRCMPLT:
if (NULL != fileBuf) {
ExFreePool(fileBuf);
fileBuf = NULL;
};
if (NULL != devBuf) {
ExFreePool(devBuf);
devBuf = NULL;
};
DPCompleteRequest(Irp,STATUS_SUCCESS,IO_DISK_INCREMENT);
continue;
};
};
return;
};

NTSTATUS DPAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) {
//NTSTATUS类型的函数返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = NULL;
//过滤设备的下层设备的指针对象
PDEVICE_OBJECT LowerDevObj = NULL;
//过滤设备的设备指针的指针对象
PDEVICE_OBJECT FltDevObj = NULL;
//过滤设备的处理线程的线程句柄
HANDLE ThreadHandle = NULL;
//建立一个过滤设备,这个设备是FILE_DEVICE_DISK类型的设备并且具有DP_FILTER_DEV_EXTENSION类型的设备扩展
ntStatus = IoCreateDevice(DriverObject, sizeof(DP_FILTER_DEV_EXTENSION), NULL, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &FltDevObj);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//将DevExt指向过滤设备的设备扩展指针
DevExt = FltDevObj->DeviceExtension;
//清空过滤设备的设备扩展
RtlZeroMemory(DevExt, sizeof(DP_FILTER_DEV_EXTENSION));
//将刚刚建立的过滤设备附加到这个卷设备的物理设备上
LowerDevObj = IoAttachDeviceToDeviceStack(FltDevObj, PhysicalDeviceObject);
if (NULL == LowerDevObj) {
ntStatus = STATUS_NO_SUCH_DEVICE;
goto ERROUT;
};
//初始化这个卷设备的分页路径计数的计数事件
KeInitializeEvent(&DevExt->PagingPathCountEvent, NotificationEvent, TRUE);
//对过滤设备的设备属性进行初始化,过滤设备的设备属性应该和它的下层设备相同
FltDevObj->Flags = LowerDevObj->Flags;
//给过滤设备的设备属性加上电源可分页的属性
FltDevObj->Flags |= DO_POWER_PAGABLE;
//对过滤设备进行设备初始化
FltDevObj->Flags &= ~DO_DEVICE_INITIALIZING;
//将过滤设备对应的设备扩展中的相应变量进行初始化
//卷设备的过滤设备对象
DevExt->FltDevObj = FltDevObj;
//卷设备的物理设备对象
DevExt->PhyDevObj = PhysicalDeviceObject;
//卷设备的下层设备对象
DevExt->LowerDevObj = LowerDevObj;
//初始化这个卷的请求处理队列
InitializeListHead(&DevExt->ReqList);
//初始化请求处理队列的锁
KeInitializeSpinLock(&DevExt->ReqLock);
//初始化请求处理队列的同步事件
KeInitializeEvent(&DevExt->ReqEvent, SynchronizationEvent, FALSE);
//初始化终止处理线程标志
DevExt->ThreadTermFlag = FALSE;
//建立用来处理这个卷的请求的处理线程,线程函数的参数则是设备扩展
ntStatus = PsCreateSystemThread(&ThreadHandle, (ACCESS_MASK)0L, NULL, NULL, NULL, DPReadWriteThread, DevExt);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//获取处理线程的对象
ntStatus = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, &DevExt->ThreadHandle, NULL);
if (!NT_SUCCESS(ntStatus)) {
DevExt->ThreadTermFlag = TRUE;
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
goto ERROUT;
};
ERROUT:
if (!NT_SUCCESS(ntStatus)) {
//如果上面有不成功的地方,首先需要解除可能存在的附加
if (NULL != LowerDevObj) {
IoDetachDevice(LowerDevObj);
DevExt->LowerDevObj = NULL;
};
//然后删除可能建立的过滤设备
if (NULL != FltDevObj) {
IoDeleteDevice(FltDevObj);
DevExt->FltDevObj = NULL;
};
};
//关闭线程句柄,我们今后不会用到它,所有对线程的引用都通过线程对象来进行了
if (NULL != ThreadHandle)
ZwClose(ThreadHandle);
//返回状态值
return ntStatus;
};

VOID DPUnload(IN PDRIVER_OBJECT DriverObject) {
//这个驱动将会工作到系统关机,所以我们不会在驱动卸载的时候做任何清理动作,因为之后系统马上就关闭了
UNREFERENCED_PARAMETER(DriverObject);
return;
};

NTSTATUS DPDispatchAny(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//NTSTATUS类型的函数返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//对于我们不感兴趣的irp请求,标准的处理方式直接下发给下层设备去处理
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

NTSTATUS DPDispatchPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
#if (NTDDI_VERSION < NTDDI_VISTA)
//如果是vista以前的版本的windows,需要使用特殊的向下层设备转发的函数
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(DevExt->LowerDevObj, Irp);
#else
//如果是vista系统,可以使用和一般下发irp一样的方法来下发
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
#endif
};

NTSTATUS DPDispatchPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向irp stack的指针
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
switch (irpsp->MinorFunction) {
case IRP_MN_REMOVE_DEVICE: {
//如果是PnP manager发过来的移除设备的irp,将进入这里 这里主要做一些清理工作
if (DevExt->ThreadTermFlag != TRUE && NULL != DevExt->ThreadHandle) {
//如果线程还在运行的话需要停止它,这里通过设置线程停止运行的标志并且发送事件信息,让线程自己终止运行
DevExt->ThreadTermFlag = TRUE;
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
//等待线程结束
KeWaitForSingleObject(DevExt->ThreadHandle, Executive, KernelMode, FALSE, NULL);
//解除引用线程对象
ObDereferenceObject(DevExt->ThreadHandle);
};
if (NULL != DevExt->Bitmap)
//如果还有位图,就释放
DPBitmapFree(DevExt->Bitmap);
if (NULL != DevExt->LowerDevObj)
//如果存在着下层设备,就先去掉挂接
IoDetachDevice(DevExt->LowerDevObj);
if (NULL != DevExt->FltDevObj)
//如果存在过滤设备,就要删除它
IoDeleteDevice(DevExt->FltDevObj);
break;
};
//这个是PnP 管理器用来询问设备能否支持特殊文件的irp,作为卷的过滤驱动,我们必须处理
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
BOOLEAN setPagable;
//如果是询问是否支持休眠文件和dump文件,则直接下发给下层设备去处理
if (irpsp->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
ntStatus = DPSendToNextDriver(DevExt->LowerDevObj, Irp);
return ntStatus;
};
//这里等一下分页计数事件
ntStatus = KeWaitForSingleObject(&DevExt->PagingPathCountEvent, Executive, KernelMode, FALSE, NULL);
//setPagable初始化为假,是没有设置过DO_POWER_PAGABLE的意思
setPagable = FALSE;
if (!irpsp->Parameters.UsageNotification.InPath && DevExt->PagingPathCount == 1) {
//如果是PnP manager通知我们将要删去分页文件,且我们目前只剩下最后一个分页文件的时候会进入这里
if (DeviceObject->Flags & DO_POWER_INRUSH) {}
else {
//到这里说明没有分页文件在这个设备上了,需要设置DO_POWER_PAGABLE这一位了
DeviceObject->Flags |= DO_POWER_PAGABLE;
setPagable = TRUE;
};
};
//到这里肯定是关于分页文件的是否可建立查询,或者是删除的通知,我们交给下层设备去做。这里需要用同步的方式给下层设备,也就是说要等待下层设备的返回
ntStatus = DPForwardIrpSync(DevExt->LowerDevObj, Irp);
if (NT_SUCCESS(ntStatus)) {
//如果发给下层设备的请求成功了,说明下层设备支持这个操作,会执行到这里 在成功的条件下我们来改变我们自己的计数值,这样就能记录我们现在这个设备上到底有多少个分页文件
IoAdjustPagingPathCount(&DevExt->PagingPathCount, irpsp->Parameters.UsageNotification.InPath);
if (irpsp->Parameters.UsageNotification.InPath)
if (DevExt->PagingPathCount == 1)
//如果这个请求是一个建立分页文件的查询请求,并且下层设备支持这个请求,而且这是第一个在这个设备上的分页文件,那么我们需要清除DO_POWER_PAGABLE位
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
}
else
//到这里说明给下层设备发请求失败了,下层设备不支持这个请求,这时候我们需要把之前做过的操作还原
if (setPagable == TRUE) {
//根据setPagable变量的值来判断我们之前是否做过对DO_POWER_PAGABLE的设置,如果有的话就清楚这个设置
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
setPagable = FALSE;
};
//设置分页计数事件
KeSetEvent(&DevExt->PagingPathCountEvent, IO_NO_INCREMENT, FALSE);
//到这里我们就可以完成这个irp请求了
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
};
default:
break;
};
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

NTSTATUS DPQueryVolumeInformationCompletionRoutine(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context) {
KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
};

NTSTATUS DPQueryVolumeInformation(PDEVICE_OBJECT DevObj, PLARGE_INTEGER TotalSize, PDWORD ClusterSize, PDWORD SectorSize) {
#define _FileSystemNameLength 64
//定义FAT16文件系统签名的偏移量
#define FAT16_SIG_OFFSET 54
//定义FAT32文件系统签名的偏移量
#define FAT32_SIG_OFFSET 82
//定义NTFS文件系统签名的偏移量
#define NTFS_SIG_OFFSET 3
//这是FAT16文件系统的标志
const UCHAR FAT16FLG[4] = { 'F','A','T','1' };
//这是FAT32文件系统的标志
const UCHAR FAT32FLG[4] = { 'F','A','T','3' };
//这是NTFS文件系统的标志
const UCHAR NTFSFLG[4] = { 'N','T','F','S' };
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来读取卷DBR扇区的数据缓冲区
BYTE DBR[512] = { 0 };
//DBR扇区有512个bytes大小
ULONG DBRLength = 512;
//以下是三个指针,统一指向读取的DBR数据,但是这三个指针的类型分别代表FAT16,FAT32和NTFS类型文件系统的DBR数据结构
PDP_NTFS_BOOT_SECTOR pNtfsBootSector = (PDP_NTFS_BOOT_SECTOR)DBR;
PDP_FAT32_BOOT_SECTOR pFat32BootSector = (PDP_FAT32_BOOT_SECTOR)DBR;
PDP_FAT16_BOOT_SECTOR pFat16BootSector = (PDP_FAT16_BOOT_SECTOR)DBR;
//读取的偏移量,对于DBR来说是卷的起始位置,所以偏移量为0
LARGE_INTEGER readOffset = { 0 };
//读取时的io操作状态
IO_STATUS_BLOCK ios;
//为了同步读取所设置的同步事件
KEVENT Event;
//为了同步读取所需要构建的irp指针
PIRP pIrp = NULL;

//下面我们首先从指定的卷设备上读取偏移量为0的一个扇区,也就是这个卷的DBR扇区,准备加以分析 因为我们要同步读取,所以先初始化一个为了同步读取设置的事件
KeInitializeEvent(&Event, NotificationEvent, FALSE);
//构造一个irp用来发给卷设备来读取信息
pIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, DevObj, DBR, DBRLength, &readOffset, &ios);
if (NULL == pIrp)
goto ERROUT;
//设置完成函数,并且将同步事件作为完成函数的参数传入
IoSetCompletionRoutine(pIrp, DPQueryVolumeInformationCompletionRoutine, &Event, TRUE, TRUE, TRUE);
//调用目标设备去处理这个irp
ntStatus = IoCallDriver(DevObj, pIrp);
if (ntStatus = STATUS_PENDING) {
//如果下层设备一时不能完成这个irp请求,我们就等
ntStatus = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
//将返回值设置为这个io操作的状态
ntStatus = pIrp->IoStatus.Status;
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
};
if (*(DWORD*)NTFSFLG == *(DWORD*)&DBR[NTFS_SIG_OFFSET]) {
//通过比较标志发现这个卷是一个ntfs文件系统的卷,下面根据ntfs卷的DBR定义来对各种需要获取的值进行赋值操作
*SectorSize = (DWORD)(pNtfsBootSector->BytesPerSector);
*ClusterSize = (*SectorSize) * (DWORD)(pNtfsBootSector->SectorsPerCluster);
TotalSize->QuadPart = (LONGLONG)(*SectorSize) * (LONGLONG)pNtfsBootSector->TotalSectors;
}
else if (*(DWORD*)FAT32FLG == *(DWORD*)&DBR[FAT32_SIG_OFFSET]) {
//通过比较标志发现这个卷是一个ntfs文件系统的卷,下面根据ntfs卷的DBR定义来对各种需要获取的值进行赋值操作
*SectorSize = (DWORD)(pFat32BootSector->BytesPerSector);
*ClusterSize = (*SectorSize) * (DWORD)(pFat32BootSector->SectorsPerCluster);
TotalSize->QuadPart = (LONGLONG)(*SectorSize) *
(LONGLONG)(pFat32BootSector->LargeSectors + pFat32BootSector->Sectors);
}
else if (*(DWORD*)FAT16FLG == *(DWORD*)&DBR[FAT16_SIG_OFFSET]) {
//通过比较标志发现这个卷是一个ntfs文件系统的卷,下面根据ntfs卷的DBR定义来对各种需要获取的值进行赋值操作
*SectorSize = (DWORD)(pFat16BootSector->BytesPerSector);
*ClusterSize = (*SectorSize) * (DWORD)(pFat16BootSector->SectorsPerCluster);
TotalSize->QuadPart = (LONGLONG)(*SectorSize) *
(LONGLONG)(pFat16BootSector->LargeSectors + pFat16BootSector->Sectors);
}
else
//走到这里,可能是其它任何文件系统,但是不是windows认识的文件系统,我们统一返回错
ntStatus = STATUS_UNSUCCESSFUL;
ERROUT:
if (NULL != pIrp)
IoFreeIrp(pIrp);
return ntStatus;
};

NTSTATUS DPVolumeOnLineCompleteRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOLUME_ONLINE_CONTEXT Context) {
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//这个卷设备的dos名字,也就是C,D等
UNICODE_STRING DosName = { 0 };
//在这里Context是不可能为空的,为空就是出错了
ASSERT(Context != NULL);
//下面调用我们自己的VolumeOnline处理
//获取这个卷的dos名字
ntStatus = IoVolumeDeviceToDosName(Context->DevExt->PhyDevObj, &DosName);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//将dos名字变成大写形式
Context->DevExt->VolumeLetter = DosName.Buffer[0];
if (Context->DevExt->VolumeLetter > L'Z')
Context->DevExt->VolumeLetter -= (L'a' - L'A');
//我们只保护“D”盘
if (Context->DevExt->VolumeLetter == L'D') {
//获取这个卷的基本信息
ntStatus = DPQueryVolumeInformation(Context->DevExt->PhyDevObj, &(Context->DevExt->TotalSizeInByte), &(Context->DevExt->ClusterSizeInByte), &(Context->DevExt->SectorSizeInByte));
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//建立这个卷对应的位图
ntStatus = DPBitmapInit(&Context->DevExt->Bitmap, Context->DevExt->SectorSizeInByte, 8, 25600, (DWORD)(Context->DevExt->TotalSizeInByte.QuadPart / (LONGLONG)(25600 * 8 * Context->DevExt->SectorSizeInByte)) + 1);
if (!NT_SUCCESS(ntStatus))
goto ERROUT;
//对全局量赋值,说明我们找到需要保护的那个设备了
gProtectDevExt = Context->DevExt;
};
ERROUT:
if (!NT_SUCCESS(ntStatus)) {
if (NULL != Context->DevExt->Bitmap)
DPBitmapFree(Context->DevExt->Bitmap);
if (NULL != Context->DevExt->TempFile)
ZwClose(Context->DevExt->TempFile);
};
if (NULL != DosName.Buffer)
ExFreePool(DosName.Buffer);
//设置等待同步事件,这样可以让我们等待的DeviceIoControl处理过程继续运行
KeSetEvent(Context->Event, 0, FALSE);
return STATUS_SUCCESS;
};

NTSTATUS DPDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
//用来指向irp stack的指针
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
//用来同步IOCTL_VOLUME_ONLINE处理的事件
KEVENT Event;
//用来传给IOCTL_VOLUME_ONLINE的完成函数的上下文
VOLUME_ONLINE_CONTEXT context;
switch (irpsp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_VOLUME_ONLINE: {
//如果是卷设备的IOCTL_VOLUME_ONLINE,会进入到这里 我们打算自己处理这个irp请求,这里先初始化一个事件用来在这个请求的完成函数里面做同步信号
KeInitializeEvent(&Event, NotificationEvent, FALSE);
//给这个请求的完成函数初始化参数
context.DevExt = DevExt;
context.Event = &Event;
//这里copy一份irp stack
IoCopyCurrentIrpStackLocationToNext(Irp);
//设置完成函数
IoSetCompletionRoutine(Irp, DPVolumeOnLineCompleteRoutine, &context, TRUE, TRUE, TRUE);
//调用下层设备来处理这个irp
ntStatus = IoCallDriver(DevExt->LowerDevObj, Irp);
//等待下层设备处理结束这个irp
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
//返回
return ntStatus;
};
default:
//对于其它DeviceIoControl,我们一律调用下层设备去处理
break;
};
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

NTSTATUS DPDispatchReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
//用来指向过滤设备的设备扩展的指针
PDP_FILTER_DEV_EXTENSION DevExt = DeviceObject->DeviceExtension;
//返回值
NTSTATUS ntStatus = STATUS_SUCCESS;
if (DevExt->Protect) {
//这个卷在保护状态,我们首先把这个irp设为pending状态
IoMarkIrpPending(Irp);
//然后将这个irp放进相应的请求队列里
ExInterlockedInsertTailList(&DevExt->ReqList, &Irp->Tail.Overlay.ListEntry, &DevExt->ReqLock);
//设置队列的等待事件,通知队列对这个irp进行处理
KeSetEvent(&DevExt->ReqEvent, (KPRIORITY)0, FALSE);
//返回pending状态,这个irp就算处理完了
return STATUS_PENDING;
}
else
//这个卷不在保护状态,直接交给下层设备进行处理
return DPSendToNextDriver(DevExt->LowerDevObj, Irp);
};

Driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#ifndef _DPMAIN_H_
#define _DPMAIN_H_

#define FS_OTHER 0
#define FS_FAT16 1
#define FS_FAT32 2
#define FS_NTFS 3

//用来存储一个卷所有的相关信息的数据结构,放在过滤设备的设备扩展中
typedef struct _DP_FILTER_DEV_EXTENSION_{
//卷的名字,例如"C:,D:"等中的字母部分
WCHAR VolumeLetter;
//这个卷是否在保护状态
BOOL Protect;
//这个卷的总大小,以byte为单位
LARGE_INTEGER TotalSizeInByte;
//这个卷上文件系统的每簇大小,以byte为单位
DWORD ClusterSizeInByte;
//这个卷的每个扇区大小,以byte为单位
DWORD SectorSizeInByte;
//这个卷设备对应的过滤设备的设备对象
PDEVICE_OBJECT FltDevObj;
//这个卷设备对应的过滤设备的下层设备对象
PDEVICE_OBJECT LowerDevObj;
//这个卷设备对应的物理设备的设备对象
PDEVICE_OBJECT PhyDevObj;
//这个数据结构是否已经被初始化完毕了
BOOL InitializeCompleted;
//这个卷上的保护系统使用的位图的句柄
PDP_BITMAP Bitmap;
//用来转储的文件句柄
HANDLE TempFile;
//这个卷上的保护系统使用的请求队列
LIST_ENTRY ReqList;
//这个卷上的保护系统使用的请求队列的锁
KSPIN_LOCK ReqLock;
//这个卷上的保护系统使用的请求队列的同步事件
KEVENT ReqEvent;
//这个卷上的保护系统使用的请求队列的处理线程之线程句柄
PVOID ThreadHandle;
//这个卷上的保护系统使用的请求队列的处理线程之结束标志
BOOLEAN ThreadTermFlag;
//这个卷上的保护系统的关机分页电源请求的计数事件
KEVENT PagingPathCountEvent;
//这个卷上的保护系统的关机分页电源请求的计数
LONG PagingPathCount;
} DP_FILTER_DEV_EXTENSION, * PDP_FILTER_DEV_EXTENSION;

typedef struct _VOLUME_ONLINE_CONTEXT_{
//在volume_online的DeviceIoControl中传给完成函数的设备扩展
PDP_FILTER_DEV_EXTENSION DevExt;
//在volume_online的DeviceIoControl中传给完成函数的同步事件
PKEVENT Event;
}VOLUME_ONLINE_CONTEXT, * PVOLUME_ONLINE_CONTEXT;

#pragma pack(1)
typedef struct _DP_FAT16_BOOT_SECTOR{
UCHAR JMPInstruction[3];
UCHAR OEM[8];
USHORT BytesPerSector;
UCHAR SectorsPerCluster;
USHORT ReservedSectors;
UCHAR NumberOfFATs;
USHORT RootEntries;
USHORT Sectors;
UCHAR MediaDescriptor;
USHORT SectorsPerFAT;
USHORT SectorsPerTrack;
USHORT Heads;
DWORD HiddenSectors;
DWORD LargeSectors;
UCHAR PhysicalDriveNumber;
UCHAR CurrentHead;
} DP_FAT16_BOOT_SECTOR, * PDP_FAT16_BOOT_SECTOR;

typedef struct _DP_FAT32_BOOT_SECTOR{
UCHAR JMPInstruction[3];
UCHAR OEM[8];
USHORT BytesPerSector;
UCHAR SectorsPerCluster;
USHORT ReservedSectors;
UCHAR NumberOfFATs;
USHORT RootEntries;
USHORT Sectors;
UCHAR MediaDescriptor;
USHORT SectorsPerFAT;
USHORT SectorsPerTrack;
USHORT Heads;
DWORD HiddenSectors;
DWORD LargeSectors;
DWORD LargeSectorsPerFAT;
UCHAR Data[24];
UCHAR PhysicalDriveNumber;
UCHAR CurrentHead;
} DP_FAT32_BOOT_SECTOR, * PDP_FAT32_BOOT_SECTOR;

typedef struct _DP_NTFS_BOOT_SECTOR{
UCHAR Jump[3]; //0
UCHAR FSID[8]; //3
USHORT BytesPerSector; //11
UCHAR SectorsPerCluster; //13
USHORT ReservedSectors; //14
UCHAR Mbz1; //16
USHORT Mbz2; //17
USHORT Reserved1; //19
UCHAR MediaDesc; //21
USHORT Mbz3; //22
USHORT SectorsPerTrack; //24
USHORT Heads; //26
ULONG HiddenSectors; //28
ULONG Reserved2[2]; //32
ULONGLONG TotalSectors; //40
ULONGLONG MftStartLcn; //48
ULONGLONG Mft2StartLcn; //56
}DP_NTFS_BOOT_SECTOR, * PDP_NTFS_BOOT_SECTOR;
#pragma pack()

NTSTATUS DPCompleteRequest(IN PIRP Irp,IN NTSTATUS Status,IN CCHAR Priority);
NTSTATUS DPSendToNextDriver(IN PDEVICE_OBJECT TgtDevObj,IN PIRP Irp);
NTSTATUS DPSendToNextDriverSynchronous(IN PDEVICE_OBJECT DeviceObject,IN PDEVICE_OBJECT TargetDeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchShutdown(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPAddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject);
VOID DPUnload(IN PDRIVER_OBJECT DriverObject);
NTSTATUS DPDispatchAny(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchPower(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchPnp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DPDispatchReadWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
NTSTATUS DiskShldForwardIrpSynchronous(IN PDEVICE_OBJECT DeviceObject,IN PDEVICE_OBJECT TargetDeviceObject,IN PIRP Irp);
#endif

DPBitmap.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include <ntifs.h>
#include <windef.h>
#include "DPBitmap.h"

static tBitmap bitmapMask[8] ={
//需要用到的bitmap的位掩码
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};

void* DPBitmapAlloc(int poolType, unsigned long length) {
//根据参数来分配不同类型的内存,这里全部分配的是非分页类型内存
if (0 == poolType)
return ExAllocatePoolWithTag(NonPagedPool, length, 'mbpD');
else if (1 == poolType)
return ExAllocatePoolWithTag(PagedPool, length, 'mbpD');
else
return NULL;
};

void DPBitmapFree(DP_BITMAP* bitmap) {
//释放bitmap
DWORD i = 0;
if (NULL != bitmap) {
if (NULL != bitmap->Bitmap) {
for (i = 0; i < bitmap->regionNumber; i++)
if (NULL != *(bitmap->Bitmap + i))
//从最底层的块开始释放,所有块都轮询一次
ExFreePool(*(bitmap->Bitmap + i));
//释放块的指针
ExFreePool(bitmap->Bitmap);
};
//释放bitmap本身
ExFreePool(bitmap);
};
return;
};

void DPBitmapInitLock(void* lock) {}
void DPBitmapLock(void* lock) {}
void DPBitmapUnlock(void* lock) {}

NTSTATUS DPBitmapInit(DP_BITMAP** bitmap, unsigned long sectorSize, unsigned long byteSize, unsigned long regionSize, unsigned long regionNumber) {
int i = 0;
DP_BITMAP* myBitmap = NULL;
NTSTATUS status = STATUS_SUCCESS;
//检查参数,以免使用了错误的参数导致发生处零错等错误
if (NULL == bitmap || 0 == sectorSize || 0 == byteSize || 0 == regionSize || 0 == regionNumber)
return STATUS_UNSUCCESSFUL;
__try {
//分配一个bitmap结构,这是无论如何都要分配的,这个结构相当于一个bitmap的handle
if (NULL == (myBitmap = (DP_BITMAP*)DPBitmapAlloc(0, sizeof(DP_BITMAP)))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
};
//清空结构
memset(myBitmap, 0, sizeof(DP_BITMAP));
//根据参数对结构中的成员进行赋值
myBitmap->sectorSize = sectorSize;
myBitmap->byteSize = byteSize;
myBitmap->regionSize = regionSize;
myBitmap->regionNumber = regionNumber;
myBitmap->regionReferSize = sectorSize * byteSize * regionSize;
myBitmap->bitmapReferSize = (__int64)sectorSize * (__int64)byteSize * (__int64)regionSize * (__int64)regionNumber;
//分配出regionNumber那么多个指向region的指针,这是一个指针数组
if (NULL == (myBitmap->Bitmap = (tBitmap**)DPBitmapAlloc(0, sizeof(tBitmap*) * regionNumber))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
};
//清空指针数组
memset(myBitmap->Bitmap, 0, sizeof(tBitmap*) * regionNumber);
*bitmap = myBitmap;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
if (!NT_SUCCESS(status)) {
if (NULL != myBitmap)
DPBitmapFree(myBitmap);
*bitmap = NULL;
};
return status;
};

NTSTATUS DPBitmapSet(DP_BITMAP* bitmap, LARGE_INTEGER offset, unsigned long length) {
__int64 i = 0;
unsigned long myRegion = 0, myRegionEnd = 0;
unsigned long myRegionOffset = 0, myRegionOffsetEnd = 0;
unsigned long myByteOffset = 0, myByteOffsetEnd = 0;
unsigned long myBitPos = 0;
NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER setBegin = { 0 }, setEnd = { 0 };
__try {
//检查变量
if (NULL == bitmap || offset.QuadPart < 0) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
if (0 != offset.QuadPart % bitmap->sectorSize || 0 != length % bitmap->sectorSize) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
//根据要设置的偏移量和长度来计算需要使用到哪些region,如果需要的话,就分配他们指向的内存空间
myRegion = (unsigned long)(offset.QuadPart / (__int64)bitmap->regionReferSize);
myRegionEnd = (unsigned long)((offset.QuadPart + (__int64)length) / (__int64)bitmap->regionReferSize);
for (i = myRegion; i <= myRegionEnd; ++i)
if (NULL == *(bitmap->Bitmap + i))
{
if (NULL == (*(bitmap->Bitmap + i) = (tBitmap*)DPBitmapAlloc(0, sizeof(tBitmap) * bitmap->regionSize))) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
else
memset(*(bitmap->Bitmap + i), 0, sizeof(tBitmap) * bitmap->regionSize);
};
//开始设置bitmap,首先我们需要将要设置的区域按照byte对齐,这样可以按byte设置而不需要按bit设置,加快设置速度 对于没有byte对齐的区域先手工设置掉他们
for (i = offset.QuadPart; i < offset.QuadPart + (__int64)length; i += bitmap->sectorSize) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (0 == myBitPos) {
setBegin.QuadPart = i;
break;
};
*(*(bitmap->Bitmap + myRegion) + myByteOffset) |= bitmapMask[myBitPos];
};
if (i >= offset.QuadPart + (__int64)length) {
status = STATUS_SUCCESS;
__leave;
};
for (i = offset.QuadPart + (__int64)length - bitmap->sectorSize; i >= offset.QuadPart; i -= bitmap->sectorSize) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (7 == myBitPos) {
setEnd.QuadPart = i;
break;
};
*(*(bitmap->Bitmap + myRegion) + myByteOffset) |= bitmapMask[myBitPos];
};
if (i < offset.QuadPart || setEnd.QuadPart == setBegin.QuadPart) {
status = STATUS_SUCCESS;
__leave;
};
myRegionEnd = (unsigned long)(setEnd.QuadPart / (__int64)bitmap->regionReferSize);
for (i = setBegin.QuadPart; i <= setEnd.QuadPart;) {
myRegion = (unsigned long)(i / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)(i % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
//如果我们设置的区域没有跨两个region,只需要使用memset去做按byte的设置然后跳出即可
if (myRegion == myRegionEnd) {
myRegionOffsetEnd = (unsigned long)(setEnd.QuadPart % (__int64)bitmap->regionReferSize);
myByteOffsetEnd = myRegionOffsetEnd / bitmap->byteSize / bitmap->sectorSize;
memset(*(bitmap->Bitmap + myRegion) + myByteOffset, 0xff, myByteOffsetEnd - myByteOffset + 1);
break;
}
//如果我们设置的区域跨了两个region,需要设置完后递增
else {
myRegionOffsetEnd = bitmap->regionReferSize;
myByteOffsetEnd = myRegionOffsetEnd / bitmap->byteSize / bitmap->sectorSize;
memset(*(bitmap->Bitmap + myRegion) + myByteOffset, 0xff, myByteOffsetEnd - myByteOffset);
i += (myByteOffsetEnd - myByteOffset) * bitmap->byteSize * bitmap->sectorSize;
};
};
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
if (!NT_SUCCESS(status)) {};
return status;
};

NTSTATUS DPBitmapGet(DP_BITMAP* bitmap, LARGE_INTEGER offset, unsigned long length, void* bufInOut, void* bufIn) {
unsigned long i = 0;
unsigned long myRegion = 0;
unsigned long myRegionOffset = 0;
unsigned long myByteOffset = 0;
unsigned long myBitPos = 0;
NTSTATUS status = STATUS_SUCCESS;
__try {
//检查参数
if (NULL == bitmap || offset.QuadPart < 0 || NULL == bufInOut || NULL == bufIn) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
if (0 != offset.QuadPart % bitmap->sectorSize || 0 != length % bitmap->sectorSize) {
status = STATUS_INVALID_PARAMETER;
__leave;
};
//遍历需要获取的位图范围,如果出现了位被设置为1,就需要用bufIn参数中指向的相应位置的数据拷贝到bufInOut中
for (i = 0; i < length; i += bitmap->sectorSize) {
myRegion = (unsigned long)((offset.QuadPart + (__int64)i) / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)((offset.QuadPart + (__int64)i) % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (NULL != *(bitmap->Bitmap + myRegion) && (*(*(bitmap->Bitmap + myRegion) + myByteOffset) & bitmapMask[myBitPos]))
memcpy((tBitmap*)bufInOut + i, (tBitmap*)bufIn + i, bitmap->sectorSize);
};
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
};
return status;
};

long DPBitmapTest(DP_BITMAP* bitmap, LARGE_INTEGER offset, unsigned long length) {
char flag = 0;
unsigned long i = 0;
unsigned long myRegion = 0;
unsigned long myRegionOffset = 0;
unsigned long myByteOffset = 0;
unsigned long myBitPos = 0;
long ret = BITMAP_BIT_UNKNOW;
__try {
//检查参数
if (NULL == bitmap || offset.QuadPart < 0 || offset.QuadPart + length > bitmap->bitmapReferSize) {
ret = BITMAP_BIT_UNKNOW;
__leave;
};
for (i = 0; i < length; i += bitmap->sectorSize) {
//针对需要测试的bitmap范围进行测试,如果全部为0则返回BITMAP_RANGE_CLEAR,如果全部为1,则返回BITMAP_RANGE_SET,如果为0,1混合则返回BITMAP_RANGE_BLEND
myRegion = (unsigned long)((offset.QuadPart + (__int64)i) / (__int64)bitmap->regionReferSize);
myRegionOffset = (unsigned long)((offset.QuadPart + (__int64)i) % (__int64)bitmap->regionReferSize);
myByteOffset = myRegionOffset / bitmap->byteSize / bitmap->sectorSize;
myBitPos = (myRegionOffset / bitmap->sectorSize) % bitmap->byteSize;
if (NULL != *(bitmap->Bitmap + myRegion) && (*(*(bitmap->Bitmap + myRegion) + myByteOffset) & bitmapMask[myBitPos]))
flag |= 0x2;
else
flag |= 0x1;
if (flag == 0x3)
break;
};
if (0x2 == flag)
ret = BITMAP_RANGE_SET;
else if (0x01 == flag)
ret = BITMAP_RANGE_CLEAR;
else if (0x03 == flag)
ret = BITMAP_RANGE_BLEND;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ret = BITMAP_BIT_UNKNOW;
};
return ret;
};

DPBitmap.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#ifndef _DP_BITMAP_H_
#define _DP_BITMAP_H_

#define BITMAP_ERR_OUTOFRANGE -1
#define BITMAP_ERR_ALLOCMEMORY -2
#define BITMAP_SUCCESS 0
#define BITMAP_BIT_SET 1
#define BITMAP_BIT_CLEAR 2
#define BITMAP_BIT_UNKNOW 3
#define BITMAP_RANGE_SET 4
#define BITMAP_RANGE_CLEAR 5
#define BITMAP_RANGE_BLEND 6
#define BITMAP_RANGE_SIZE 25600
#define BITMAP_RANGE_SIZE_SMALL 256
#define BITMAP_RANGE_SIZE_MAX 51684
#define BITMAP_RANGE_AMOUNT 16*1024

typedef unsigned char tBitmap;

#pragma pack(1)

typedef struct _DP_BITMAP_{
//这个卷中的每个扇区有多少字节,这同样也说明了bitmap中一个位所对应的字节数
unsigned long sectorSize;
//每个byte里面有几个bit,一般情况下是8
unsigned long byteSize;
//每个块是多大byte,
unsigned long regionSize;
//这个bitmap总共有多少个块
unsigned long regionNumber;
//这个块对应了多少个实际的byte,这个数字应该是sectorSize*byteSize*regionSize
unsigned long regionReferSize;
//这个bitmap对应了多少个实际的byte,这个数字应该是sectorSize*byteSize*regionSize*regionNumber
__int64 bitmapReferSize;
//指向bitmap存储空间的指针
tBitmap** Bitmap;
//用于存取bitmap的锁
void* lockBitmap;
} DP_BITMAP, * PDP_BITMAP;

#pragma pack()
NTSTATUS DPBitmapInit(DP_BITMAP** bitmap,unsigned long sectorSize,unsigned long byteSize,unsigned long regionSize,unsigned long regionNumber);
void DPBitmapFree(DP_BITMAP* bitmap);
NTSTATUS DPBitmapSet(DP_BITMAP* bitmap,LARGE_INTEGER offset,unsigned long length);
NTSTATUS DPBitmapGet(DP_BITMAP* bitmap,LARGE_INTEGER offset,unsigned long length,void* bufInOut,void* bufIn);
long DPBitmapTest(DP_BITMAP* bitmap,LARGE_INTEGER offset,unsigned long length);
#endif