Windows驱动开发入门-文件系统透明加密源码

上一节的源码。

源代码

cf_create.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
///
/// @file cf_create.c
/// @author crazy_chu
/// @date 2009-2-4
/// @brief 实现对create irp的处理。
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>
#include "..\inc\sfilter\sfilter.h"
#include "cf_list.h"
#include "cf_file_irp.h"

#define CF_FILE_HEADER_SIZE (1024*4)
#define CF_MEM_TAG 'cfct'

// 在create之前的时候,获得完整的路径。
ULONG
cfFileFullPathPreCreate(
PFILE_OBJECT file,
PUNICODE_STRING path
)
{
NTSTATUS status;
POBJECT_NAME_INFORMATION obj_name_info = NULL;
WCHAR buf[64] = { 0 };
void *obj_ptr;
ULONG length = 0;
BOOLEAN need_split = FALSE;

ASSERT( file != NULL );
if(file == NULL)
return 0;
if(file->FileName.Buffer == NULL)
return 0;

obj_name_info = (POBJECT_NAME_INFORMATION)buf;
do {

// 获取FileName前面的部分(设备路径或者根目录路径)
if(file->RelatedFileObject != NULL)
obj_ptr = (void *)file->RelatedFileObject;
else
obj_ptr= (void *)file->DeviceObject;
status = ObQueryNameString(obj_ptr,obj_name_info,64*sizeof(WCHAR),&length);
if(status == STATUS_INFO_LENGTH_MISMATCH)
{
obj_name_info = ExAllocatePoolWithTag(NonPagedPool,length,CF_MEM_TAG);
if(obj_name_info == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(obj_name_info,length);
status = ObQueryNameString(obj_ptr,obj_name_info,length,&length);
}
// 失败了就直接跳出即可
if(!NT_SUCCESS(status))
break;

// 判断二者之间是否需要多一个斜杠。这需要两个条件:
// FileName第一个字符不是斜杠。obj_name_info最后一个
// 字符不是斜杠。
if( file->FileName.Length > 2 &&
file->FileName.Buffer[ 0 ] != L'\\' &&
obj_name_info->Name.Buffer[ obj_name_info->Name.Length / sizeof(WCHAR) - 1 ] != L'\\' )
need_split = TRUE;

// 获总体名字的长度。如果长度不足,也直接返回。
length = obj_name_info->Name.Length + file->FileName.Length;
if(need_split)
length += sizeof(WCHAR);
if(path->MaximumLength < length)
break;

// 先把设备名拷贝进去。
RtlCopyUnicodeString(path,&obj_name_info->Name);
if(need_split)
// 追加一个斜杠
RtlAppendUnicodeToString(path,L"\\");

// 然后追加FileName
RtlAppendUnicodeStringToString(path,&file->FileName);
} while(0);

// 如果分配过空间就释放掉。
if((void *)obj_name_info != (void *)buf)
ExFreePool(obj_name_info);
return length;
}

// 用IoCreateFileSpecifyDeviceObjectHint来打开文件。
// 这个文件打开之后不进入加密链表,所以可以直接
// Read和Write,不会被加密。
HANDLE cfCreateFileAccordingIrp(
IN PDEVICE_OBJECT dev,
IN PUNICODE_STRING file_full_path,
IN PIO_STACK_LOCATION irpsp,
OUT NTSTATUS *status,
OUT PFILE_OBJECT *file,
OUT PULONG information)
{
HANDLE file_h = NULL;
IO_STATUS_BLOCK io_status;
ULONG desired_access;
ULONG disposition;
ULONG create_options;
ULONG share_access;
ULONG file_attri;
OBJECT_ATTRIBUTES obj_attri;

ASSERT(irpsp->MajorFunction == IRP_MJ_CREATE);

*information = 0;

// 填写object attribute
InitializeObjectAttributes(
&obj_attri,
file_full_path,
OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,
NULL,
NULL);

// 获得IRP中的参数。
desired_access = irpsp->Parameters.Create.SecurityContext->DesiredAccess;
disposition = (irpsp->Parameters.Create.Options>>24);
create_options = (irpsp->Parameters.Create.Options & 0x00ffffff);
share_access = irpsp->Parameters.Create.ShareAccess;
file_attri = irpsp->Parameters.Create.FileAttributes;

// 调用IoCreateFileSpecifyDeviceObjectHint打开文件。
*status = IoCreateFileSpecifyDeviceObjectHint(
&file_h,
desired_access,
&obj_attri,
&io_status,
NULL,
file_attri,
share_access,
disposition,
create_options,
NULL,
0,
CreateFileTypeNone,
NULL,
0,
dev);

if(!NT_SUCCESS(*status))
return file_h;

// 记住information,便于外面使用。
*information = io_status.Information;

// 从句柄得到一个fileobject便于后面的操作。记得一定要解除
// 引用。
*status = ObReferenceObjectByHandle(
file_h,
0,
*IoFileObjectType,
KernelMode,
file,
NULL);

// 如果失败了就关闭,假设没打开文件。但是这个实际上是不
// 应该出现的。
if(!NT_SUCCESS(*status))
{
ASSERT(FALSE);
ZwClose(file_h);
}
return file_h;
}

// 写入一个文件头。
NTSTATUS cfWriteAHeader(PFILE_OBJECT file,PDEVICE_OBJECT next_dev)
{
static WCHAR header_flags[CF_FILE_HEADER_SIZE/sizeof(WCHAR)] = {L'C',L'F',L'H',L'D'};
LARGE_INTEGER file_size,offset;
ULONG length = CF_FILE_HEADER_SIZE;
NTSTATUS status;

offset.QuadPart = 0;
file_size.QuadPart = CF_FILE_HEADER_SIZE;
// 首先设置文件的大小为4k。
status = cfFileSetFileSize(next_dev,file,&file_size);
if(status != STATUS_SUCCESS)
return status;

// 然后写入8个字节的头。
return cfFileReadWrite(next_dev,file,&offset,&length,header_flags,FALSE);
}


// 打开预处理。
ULONG cfIrpCreatePre(
PIRP irp,
PIO_STACK_LOCATION irpsp,
PFILE_OBJECT file,
PDEVICE_OBJECT next_dev)
{
UNICODE_STRING path = { 0 };
// 首先获得要打开文件的路径。
ULONG length = cfFileFullPathPreCreate(file,&path);
NTSTATUS status;
ULONG ret = SF_IRP_PASS;
PFILE_OBJECT my_file = NULL;
HANDLE file_h;
ULONG information = 0;
LARGE_INTEGER file_size,offset = { 0 };
BOOLEAN dir,sec_file;
// 获得打开访问期望。
ULONG desired_access = irpsp->Parameters.Create.SecurityContext->DesiredAccess;
WCHAR header_flags[4] = {L'C',L'F',L'H',L'D'};
WCHAR header_buf[4] = { 0 };
ULONG disp;

// 无法得到路径,直接放过即可。
if(length == 0)
return SF_IRP_PASS;

// 如果只是想打开目录的话,直接放过
if(irpsp->Parameters.Create.Options & FILE_DIRECTORY_FILE)
return SF_IRP_PASS;

do {

// 给path分配缓冲区
path.Buffer = ExAllocatePoolWithTag(NonPagedPool,length+4,CF_MEM_TAG);
path.Length = 0;
path.MaximumLength = (USHORT)length + 4;
if(path.Buffer == NULL)
{
// 内存不够,这个请求直接挂掉
status = STATUS_INSUFFICIENT_RESOURCES;
ret = SF_IRP_COMPLETED;
break;
}
length = cfFileFullPathPreCreate(file,&path);

// 得到了路径,打开这个文件。
file_h = cfCreateFileAccordingIrp(
next_dev,
&path,
irpsp,
&status,
&my_file,
&information);

// 如果没有成功的打开,那么说明这个请求可以结束了
if(!NT_SUCCESS(status))
{
ret = SF_IRP_COMPLETED;
break;
}

// 得到了my_file之后,首先判断这个文件是不是已经在
// 加密的文件之中。如果在,直接返回passthru即可
cfListLock();
sec_file = cfIsFileCrypting(my_file);
cfListUnlock();
if(sec_file)
{
ret = SF_IRP_PASS;
break;
}

// 现在虽然打开,但是这依然可能是一个目录。在这里
// 判断一下。同时也可以得到文件的大小。
status = cfFileGetStandInfo(
next_dev,
my_file,
NULL,
&file_size,
&dir);

// 查询失败。禁止打开。
if(!NT_SUCCESS(status))
{
ret = SF_IRP_COMPLETED;
break;
}

// 如果这是一个目录,那么不管它了。
if(dir)
{
ret = SF_IRP_PASS;
break;
}

// 如果文件大小为0,且有写入或者追加数据的意图,
// 就应该加密文件。应该在这里写入文件头。这也是唯
// 一需要写入文件头的地方。
if(file_size.QuadPart == 0 &&
(desired_access &
(FILE_WRITE_DATA|
FILE_APPEND_DATA)))
{
// 不管是否成功。一定要写入头。
cfWriteAHeader(my_file,next_dev);
// 写入头之后,这个文件属于必须加密的文件
ret = SF_IRP_GO_ON;
break;
}

// 这个文件有大小,而且大小小于头长度。不需要加密。
if(file_size.QuadPart < CF_FILE_HEADER_SIZE)
{
ret = SF_IRP_PASS;
break;
}

// 现在读取文件。比较来看是否需要加密,直接读个8字
// 节就足够了。这个文件有大小,而且比CF_FILE_HEADER_SIZE
// 长。此时读出前8个字节,判断是否要加密。
length = 8;
status = cfFileReadWrite(next_dev,my_file,&offset,&length,header_buf,TRUE);
if(status != STATUS_SUCCESS)
{
// 如果失败了就不加密了。
ASSERT(FALSE);
ret = SF_IRP_PASS;
break;
}
// 读取到内容,比较和加密标志是一致的,加密。
if(RtlCompareMemory(header_flags,header_buf,8) == 8)
{
// 到这里认为是必须加密的。这种情况下,必须返回GO_ON.
ret = SF_IRP_GO_ON;
break;
}

// 其他的情况都是不需要加密的。
ret = SF_IRP_PASS;
} while(0);

if(path.Buffer != NULL)
ExFreePool(path.Buffer);
if(file_h != NULL)
ZwClose(file_h);
if(ret == SF_IRP_GO_ON)
{
// 要加密的,这里清一下缓冲。避免文件头出现在缓冲里。
cfFileCacheClear(my_file);
}
if(my_file != NULL)
ObDereferenceObject(my_file);

// 如果要返回完成,则必须把这个请求完成。这一般都是
// 以错误作为结局的。
if(ret == SF_IRP_COMPLETED)
{
irp->IoStatus.Status = status;
irp->IoStatus.Information = information;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}

// 要注意:
// 1.文件的CREATE改为OPEN.
// 2.文件的OVERWRITE去掉。不管是不是要加密的文件,
// 都必须这样做。否则的话,本来是试图生成文件的,
// 结果发现文件已经存在了。本来试图覆盖文件的,再
// 覆盖一次会去掉加密头。
disp = FILE_OPEN;
irpsp->Parameters.Create.Options &= 0x00ffffff;
irpsp->Parameters.Create.Options |= (disp << 24);
return ret;
}

cf_file_irp.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
///
/// @file cf_file_irp.c
/// @author crazy_chu
/// @date 2009-1-29
/// @brief 对文件的操作,直接发送irp来避免重入
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>

#define CF_MEM_TAG 'cffi'

static NTSTATUS cfFileIrpComp(
PDEVICE_OBJECT dev,
PIRP irp,
PVOID context
)
{
*irp->UserIosb = irp->IoStatus;
KeSetEvent(irp->UserEvent, 0, FALSE);
IoFreeIrp(irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}

// 自发送SetInformation请求.
NTSTATUS
cfFileSetInformation(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
FILE_INFORMATION_CLASS infor_class,
FILE_OBJECT *set_file,
void* buf,
ULONG buf_len)
{
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION ioStackLocation;

KeInitializeEvent(&event, SynchronizationEvent, FALSE);

// 分配irp
irp = IoAllocateIrp(dev->StackSize, FALSE);
if(irp == NULL)
return STATUS_INSUFFICIENT_RESOURCES;

// 填写irp的主体
irp->AssociatedIrp.SystemBuffer = buf;
irp->UserEvent = &event;
irp->UserIosb = &IoStatusBlock;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = file;
irp->RequestorMode = KernelMode;
irp->Flags = 0;

// 设置irpsp
ioStackLocation = IoGetNextIrpStackLocation(irp);
ioStackLocation->MajorFunction = IRP_MJ_SET_INFORMATION;
ioStackLocation->DeviceObject = dev;
ioStackLocation->FileObject = file;
ioStackLocation->Parameters.SetFile.FileObject = set_file;
ioStackLocation->Parameters.SetFile.Length = buf_len;
ioStackLocation->Parameters.SetFile.FileInformationClass = infor_class;

// 设置结束例程
IoSetCompletionRoutine(irp, cfFileIrpComp, 0, TRUE, TRUE, TRUE);

// 发送请求并等待结束
(void) IoCallDriver(dev, irp);
KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
return IoStatusBlock.Status;
}

NTSTATUS
cfFileQueryInformation(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
FILE_INFORMATION_CLASS infor_class,
void* buf,
ULONG buf_len)
{
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION ioStackLocation;

// 因为我们打算让这个请求同步完成,所以初始化一个事件
// 用来等待请求完成。
KeInitializeEvent(&event, SynchronizationEvent, FALSE);

// 分配irp
irp = IoAllocateIrp(dev->StackSize, FALSE);
if(irp == NULL)
return STATUS_INSUFFICIENT_RESOURCES;

// 填写irp的主体
irp->AssociatedIrp.SystemBuffer = buf;
irp->UserEvent = &event;
irp->UserIosb = &IoStatusBlock;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = file;
irp->RequestorMode = KernelMode;
irp->Flags = 0;

// 设置irpsp
ioStackLocation = IoGetNextIrpStackLocation(irp);
ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
ioStackLocation->DeviceObject = dev;
ioStackLocation->FileObject = file;
ioStackLocation->Parameters.QueryFile.Length = buf_len;
ioStackLocation->Parameters.QueryFile.FileInformationClass = infor_class;

// 设置结束例程
IoSetCompletionRoutine(irp, cfFileIrpComp, 0, TRUE, TRUE, TRUE);

// 发送请求并等待结束
(void) IoCallDriver(dev, irp);
KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
return IoStatusBlock.Status;
}

NTSTATUS
cfFileSetFileSize(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
LARGE_INTEGER *file_size)
{
FILE_END_OF_FILE_INFORMATION end_of_file;
end_of_file.EndOfFile.QuadPart = file_size->QuadPart;
return cfFileSetInformation(
dev,file,FileEndOfFileInformation,
NULL,(void *)&end_of_file,
sizeof(FILE_END_OF_FILE_INFORMATION));
}

NTSTATUS
cfFileGetStandInfo(
PDEVICE_OBJECT dev,
PFILE_OBJECT file,
PLARGE_INTEGER allocate_size,
PLARGE_INTEGER file_size,
BOOLEAN *dir)
{
NTSTATUS status;
PFILE_STANDARD_INFORMATION infor = NULL;
infor = (PFILE_STANDARD_INFORMATION)
ExAllocatePoolWithTag(NonPagedPool,sizeof(FILE_STANDARD_INFORMATION),CF_MEM_TAG);
if(infor == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
status = cfFileQueryInformation(dev,file,
FileStandardInformation,(void *)infor,
sizeof(FILE_STANDARD_INFORMATION));
if(NT_SUCCESS(status))
{
if(allocate_size != NULL)
*allocate_size = infor->AllocationSize;
if(file_size != NULL)
*file_size = infor->EndOfFile;
if(dir != NULL)
*dir = infor->Directory;
}
ExFreePool(infor);
return status;
}


NTSTATUS
cfFileReadWrite(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
LARGE_INTEGER *offset,
ULONG *length,
void *buffer,
BOOLEAN read_write)
{
ULONG i;
PIRP irp;
KEVENT event;
PIO_STACK_LOCATION ioStackLocation;
IO_STATUS_BLOCK IoStatusBlock = { 0 };

KeInitializeEvent(&event, SynchronizationEvent, FALSE);

// 分配irp.
irp = IoAllocateIrp(dev->StackSize, FALSE);
if(irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}

// 填写主体。
irp->AssociatedIrp.SystemBuffer = NULL;
// 在paging io的情况下,似乎必须要使用MDL才能正常进行。不能使用UserBuffer.
// 但是我并不肯定这一点。所以这里加一个断言。以便我可以跟踪错误。
irp->MdlAddress = NULL;
irp->UserBuffer = buffer;
irp->UserEvent = &event;
irp->UserIosb = &IoStatusBlock;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = file;
irp->RequestorMode = KernelMode;
if(read_write)
irp->Flags = IRP_DEFER_IO_COMPLETION|IRP_READ_OPERATION|IRP_NOCACHE;
else
irp->Flags = IRP_DEFER_IO_COMPLETION|IRP_WRITE_OPERATION|IRP_NOCACHE;

// 填写irpsp
ioStackLocation = IoGetNextIrpStackLocation(irp);
if(read_write)
ioStackLocation->MajorFunction = IRP_MJ_READ;
else
ioStackLocation->MajorFunction = IRP_MJ_WRITE;
ioStackLocation->MinorFunction = IRP_MN_NORMAL;
ioStackLocation->DeviceObject = dev;
ioStackLocation->FileObject = file;
if(read_write)
{
ioStackLocation->Parameters.Read.ByteOffset = *offset;
ioStackLocation->Parameters.Read.Length = *length;
}
else
{
ioStackLocation->Parameters.Write.ByteOffset = *offset;
ioStackLocation->Parameters.Write.Length = *length;
}

// 设置完成
IoSetCompletionRoutine(irp, cfFileIrpComp, 0, TRUE, TRUE, TRUE);
(void) IoCallDriver(dev, irp);
KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
*length = IoStatusBlock.Information;
return IoStatusBlock.Status;
}

// 清理缓冲
void cfFileCacheClear(PFILE_OBJECT pFileObject)
{
PFSRTL_COMMON_FCB_HEADER pFcb;
LARGE_INTEGER liInterval;
BOOLEAN bNeedReleaseResource = FALSE;
BOOLEAN bNeedReleasePagingIoResource = FALSE;
KIRQL irql;

pFcb = (PFSRTL_COMMON_FCB_HEADER)pFileObject->FsContext;
if(pFcb == NULL)
return;

irql = KeGetCurrentIrql();
if (irql >= DISPATCH_LEVEL)
{
return;
}

liInterval.QuadPart = -1 * (LONGLONG)50;

while (TRUE)
{
BOOLEAN bBreak = TRUE;
BOOLEAN bLockedResource = FALSE;
BOOLEAN bLockedPagingIoResource = FALSE;
bNeedReleaseResource = FALSE;
bNeedReleasePagingIoResource = FALSE;

// 到fcb中去拿锁。
if (pFcb->PagingIoResource)
bLockedPagingIoResource = ExIsResourceAcquiredExclusiveLite(pFcb->PagingIoResource);

// 总之一定要拿到这个锁。
if (pFcb->Resource)
{
bLockedResource = TRUE;
if (ExIsResourceAcquiredExclusiveLite(pFcb->Resource) == FALSE)
{
bNeedReleaseResource = TRUE;
if (bLockedPagingIoResource)
{
if (ExAcquireResourceExclusiveLite(pFcb->Resource, FALSE) == FALSE)
{
bBreak = FALSE;
bNeedReleaseResource = FALSE;
bLockedResource = FALSE;
}
}
else
ExAcquireResourceExclusiveLite(pFcb->Resource, TRUE);
}
}

if (bLockedPagingIoResource == FALSE)
{
if (pFcb->PagingIoResource)
{
bLockedPagingIoResource = TRUE;
bNeedReleasePagingIoResource = TRUE;
if (bLockedResource)
{
if (ExAcquireResourceExclusiveLite(pFcb->PagingIoResource, FALSE) == FALSE)
{
bBreak = FALSE;
bLockedPagingIoResource = FALSE;
bNeedReleasePagingIoResource = FALSE;
}
}
else
{
ExAcquireResourceExclusiveLite(pFcb->PagingIoResource, TRUE);
}
}
}

if (bBreak)
{
break;
}

if (bNeedReleasePagingIoResource)
{
ExReleaseResourceLite(pFcb->PagingIoResource);
}
if (bNeedReleaseResource)
{
ExReleaseResourceLite(pFcb->Resource);
}

if (irql == PASSIVE_LEVEL)
{
KeDelayExecutionThread(KernelMode, FALSE, &liInterval);
}
else
{
KEVENT waitEvent;
KeInitializeEvent(&waitEvent, NotificationEvent, FALSE);
KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, &liInterval);
}
}

if (pFileObject->SectionObjectPointer)
{
IO_STATUS_BLOCK ioStatus;
CcFlushCache(pFileObject->SectionObjectPointer, NULL, 0, &ioStatus);
if (pFileObject->SectionObjectPointer->ImageSectionObject)
{
MmFlushImageSection(pFileObject->SectionObjectPointer,MmFlushForWrite); // MmFlushForDelete
}
CcPurgeCacheSection(pFileObject->SectionObjectPointer, NULL, 0, FALSE);
}

if (bNeedReleasePagingIoResource)
{
ExReleaseResourceLite(pFcb->PagingIoResource);
}
if (bNeedReleaseResource)
{
ExReleaseResourceLite(pFcb->Resource);
}
}


cf_list.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
///
/// @file cf_list.c
/// @author crazy_chu
/// @date 2009-1-29
/// @brief 实现一个链表,保存所有正在加密打开着的文件。
/// 请注意只支持WindowsXP下的FastFat文件系统。
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>
#include "cf_file_irp.h"
#include "fat_headers/fat.h"
#include "fat_headers/nodetype.h"
#include "fat_headers/fatstruc.h"

#define CF_MEM_TAG 'cfls'
#define CF_FILE_HEADER_SIZE (1024*4)

typedef struct {
LIST_ENTRY list_entry;
FCB *fcb;
} CF_NODE,*PCF_NODE;

static LIST_ENTRY s_cf_list;
static KSPIN_LOCK s_cf_list_lock;
static KIRQL s_cf_list_lock_irql;
static BOOLEAN s_cf_list_inited = FALSE;
BOOLEAN cfListInited()
{
return s_cf_list_inited;
}

void cfListLock()
{
ASSERT(s_cf_list_inited);
KeAcquireSpinLock(&s_cf_list_lock,&s_cf_list_lock_irql);
}

void cfListUnlock()
{
ASSERT(s_cf_list_inited);
KeReleaseSpinLock(&s_cf_list_lock,s_cf_list_lock_irql);
}

void cfListInit()
{
InitializeListHead(&s_cf_list);
KeInitializeSpinLock(&s_cf_list_lock);
s_cf_list_inited = TRUE;
}

// 任意给定一个文件,判断是否在加密链表中。这个函数没加锁。
BOOLEAN cfIsFileCrypting(PFILE_OBJECT file)
{
PLIST_ENTRY p;
PCF_NODE node;
for(p = s_cf_list.Flink; p != &s_cf_list; p = p->Flink)
{
node = (PCF_NODE)p;
if(node->fcb == file->FsContext)
{
//KdPrint(("cfIsFileCrypting: file %wZ is crypting. fcb = %x \r\n",&file->FileName,file->FsContext));
return TRUE;
}
}
return FALSE;
}

// 追加一个正在使用的机密文件。这个函数有加锁来保证只插入一
// 个,不会重复插入。
BOOLEAN cfFileCryptAppendLk(PFILE_OBJECT file)
{
// 先分配空间
PCF_NODE node = (PCF_NODE)
ExAllocatePoolWithTag(NonPagedPool,sizeof(CF_NODE),CF_MEM_TAG);
node->fcb = (PFCB)file->FsContext;

cfFileCacheClear(file);

// 加锁并查找,如果已经有了,这是一个致命的错误。直接报错即可。
cfListLock();
if(cfIsFileCrypting(file))
{
ASSERT(FALSE);
return TRUE;
}
else if(node->fcb->UncleanCount > 1)
{
// 要成功的加入,必须要符合一个条件。就是FCB->UncleanCount <= 1.
// 这样的话说明没有其他程序打开着这个文件。否则的话可能是一个普
// 通进程打开着它。此时不能加密。返回拒绝打开。
cfListUnlock();
// 释放掉。
ExFreePool(node);
return FALSE;
}

// 否则的话,在这里插入到链表里。
InsertHeadList(&s_cf_list, (PLIST_ENTRY)node);
cfListUnlock();

//cfFileCacheClear(file);
return TRUE;
}


// 当有文件被clean up的时候调用此函数。如果检查发现
// FileObject->FsContext在列表中
BOOLEAN cfCryptFileCleanupComplete(PFILE_OBJECT file)
{
PLIST_ENTRY p;
PCF_NODE node;
FCB *fcb = (FCB *)file->FsContext;

KdPrint(("cfCryptFileCleanupComplete: file name = %wZ, fcb->UncleanCount = %d\r\n",
&file->FileName,fcb->UncleanCount));

// 必须首先清文件缓冲。然后再从链表中移除。否则的话,清缓
// 冲时的写操作就不会加密了。
if(fcb->UncleanCount <= 1 || (fcb->FcbState & FCB_STATE_DELETE_ON_CLOSE) )
cfFileCacheClear(file);
else
return FALSE;

cfListLock();
for(p = s_cf_list.Flink; p != &s_cf_list; p = p->Flink)
{
node = (PCF_NODE)p;
if(node->fcb == file->FsContext &&
(node->fcb->UncleanCount == 0 ||
(fcb->FcbState & FCB_STATE_DELETE_ON_CLOSE)))
{
// 从链表中移除。
RemoveEntryList((PLIST_ENTRY)node);
cfListUnlock();
// 释放内存。
ExFreePool(node);
return TRUE;
}
}
cfListUnlock();
return FALSE;
}

cf_modify_irp.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
///
/// @file cf_modify_irp.c
/// @author crazy_chu wowocock
/// @date 2009-2-1
/// @brief 实现对irp请求和结果的修改
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>
#include "cf_file_irp.h"
#include "cf_list.h"
#include "fat_headers/fat.h"
#include "fat_headers/nodetype.h"
#include "fat_headers/fatstruc.h"

#define CF_FILE_HEADER_SIZE (1024*4)
#define CF_MEM_TAG 'cfmi'

// 对这些set information给予修改,使之隐去前面的4k文件头。
void cfIrpSetInforPre(
PIRP irp,
PIO_STACK_LOCATION irpsp)
{
PUCHAR buffer = irp->AssociatedIrp.SystemBuffer;
NTSTATUS status;

ASSERT(irpsp->MajorFunction == IRP_MJ_SET_INFORMATION);
switch(irpsp->Parameters.SetFile.FileInformationClass)
{
case FileAllocationInformation:
{
PFILE_ALLOCATION_INFORMATION alloc_infor =
(PFILE_ALLOCATION_INFORMATION)buffer;
alloc_infor->AllocationSize.QuadPart += CF_FILE_HEADER_SIZE;
break;
}
case FileEndOfFileInformation:
{
PFILE_END_OF_FILE_INFORMATION end_infor =
(PFILE_END_OF_FILE_INFORMATION)buffer;
end_infor->EndOfFile.QuadPart += CF_FILE_HEADER_SIZE;
break;
}
case FileValidDataLengthInformation:
{
PFILE_VALID_DATA_LENGTH_INFORMATION valid_length =
(PFILE_VALID_DATA_LENGTH_INFORMATION)buffer;
valid_length->ValidDataLength.QuadPart += CF_FILE_HEADER_SIZE;
break;
}
case FilePositionInformation:
{
PFILE_POSITION_INFORMATION position_infor =
(PFILE_POSITION_INFORMATION)buffer;
position_infor->CurrentByteOffset.QuadPart += CF_FILE_HEADER_SIZE;
break;
}
case FileStandardInformation:
((PFILE_STANDARD_INFORMATION)buffer)->EndOfFile.QuadPart += CF_FILE_HEADER_SIZE;
break;
case FileAllInformation:
((PFILE_ALL_INFORMATION)buffer)->PositionInformation.CurrentByteOffset.QuadPart += CF_FILE_HEADER_SIZE;
((PFILE_ALL_INFORMATION)buffer)->StandardInformation.EndOfFile.QuadPart += CF_FILE_HEADER_SIZE;
break;

default:
ASSERT(FALSE);
};
}

void cfIrpQueryInforPost(PIRP irp,PIO_STACK_LOCATION irpsp)
{
PUCHAR buffer = irp->AssociatedIrp.SystemBuffer;
ASSERT(irpsp->MajorFunction == IRP_MJ_QUERY_INFORMATION);
switch(irpsp->Parameters.QueryFile.FileInformationClass)
{
case FileAllInformation:
{
// 注意FileAllInformation,是由以下结构组成。即使长度不够,
// 依然可以返回前面的字节。
//typedef struct _FILE_ALL_INFORMATION {
// FILE_BASIC_INFORMATION BasicInformation;
// FILE_STANDARD_INFORMATION StandardInformation;
// FILE_INTERNAL_INFORMATION InternalInformation;
// FILE_EA_INFORMATION EaInformation;
// FILE_ACCESS_INFORMATION AccessInformation;
// FILE_POSITION_INFORMATION PositionInformation;
// FILE_MODE_INFORMATION ModeInformation;
// FILE_ALIGNMENT_INFORMATION AlignmentInformation;
// FILE_NAME_INFORMATION NameInformation;
//} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
// 我们需要注意的是返回的字节里是否包含了StandardInformation
// 这个可能影响文件的大小的信息。
PFILE_ALL_INFORMATION all_infor = (PFILE_ALL_INFORMATION)buffer;
if(irp->IoStatus.Information >=
sizeof(FILE_BASIC_INFORMATION) +
sizeof(FILE_STANDARD_INFORMATION))
{
ASSERT(all_infor->StandardInformation.EndOfFile.QuadPart >= CF_FILE_HEADER_SIZE);
all_infor->StandardInformation.EndOfFile.QuadPart -= CF_FILE_HEADER_SIZE;
all_infor->StandardInformation.AllocationSize.QuadPart -= CF_FILE_HEADER_SIZE;
if(irp->IoStatus.Information >=
sizeof(FILE_BASIC_INFORMATION) +
sizeof(FILE_STANDARD_INFORMATION) +
sizeof(FILE_INTERNAL_INFORMATION) +
sizeof(FILE_EA_INFORMATION) +
sizeof(FILE_ACCESS_INFORMATION) +
sizeof(FILE_POSITION_INFORMATION))
{
if(all_infor->PositionInformation.CurrentByteOffset.QuadPart >= CF_FILE_HEADER_SIZE)
all_infor->PositionInformation.CurrentByteOffset.QuadPart -= CF_FILE_HEADER_SIZE;
}
}
break;
}
case FileAllocationInformation:
{
PFILE_ALLOCATION_INFORMATION alloc_infor =
(PFILE_ALLOCATION_INFORMATION)buffer;
ASSERT(alloc_infor->AllocationSize.QuadPart >= CF_FILE_HEADER_SIZE);
alloc_infor->AllocationSize.QuadPart -= CF_FILE_HEADER_SIZE;
break;
}
case FileValidDataLengthInformation:
{
PFILE_VALID_DATA_LENGTH_INFORMATION valid_length =
(PFILE_VALID_DATA_LENGTH_INFORMATION)buffer;
ASSERT(valid_length->ValidDataLength.QuadPart >= CF_FILE_HEADER_SIZE);
valid_length->ValidDataLength.QuadPart -= CF_FILE_HEADER_SIZE;
break;
}
case FileStandardInformation:
{
PFILE_STANDARD_INFORMATION stand_infor = (PFILE_STANDARD_INFORMATION)buffer;
ASSERT(stand_infor->AllocationSize.QuadPart >= CF_FILE_HEADER_SIZE);
stand_infor->AllocationSize.QuadPart -= CF_FILE_HEADER_SIZE;
stand_infor->EndOfFile.QuadPart -= CF_FILE_HEADER_SIZE;
break;
}
case FileEndOfFileInformation:
{
PFILE_END_OF_FILE_INFORMATION end_infor =
(PFILE_END_OF_FILE_INFORMATION)buffer;
ASSERT(end_infor->EndOfFile.QuadPart >= CF_FILE_HEADER_SIZE);
end_infor->EndOfFile.QuadPart -= CF_FILE_HEADER_SIZE;
break;
}
case FilePositionInformation:
{
PFILE_POSITION_INFORMATION PositionInformation =
(PFILE_POSITION_INFORMATION)buffer;
if(PositionInformation->CurrentByteOffset.QuadPart > CF_FILE_HEADER_SIZE)
PositionInformation->CurrentByteOffset.QuadPart -= CF_FILE_HEADER_SIZE;
break;
}
default:
ASSERT(FALSE);
};
}

// 读请求。将偏移量前移。
void cfIrpReadPre(PIRP irp,PIO_STACK_LOCATION irpsp)
{
PLARGE_INTEGER offset;
PFCB fcb = (PFCB)irpsp->FileObject->FsContext;
offset = &irpsp->Parameters.Read.ByteOffset;
if(offset->LowPart == FILE_USE_FILE_POINTER_POSITION && offset->HighPart == -1)
{
// 记事本不会出现这样的情况。
ASSERT(FALSE);
}
// 偏移必须修改为增加4k。
offset->QuadPart += CF_FILE_HEADER_SIZE;
KdPrint(("cfIrpReadPre: offset = %8x\r\n",
offset->LowPart));
}

// 读请求结束,需要解密。
void cfIrpReadPost(PIRP irp,PIO_STACK_LOCATION irpsp)
{
// 得到缓冲区,然后解密之。解密很简单,就是xor 0x77.
PUCHAR buffer;
ULONG i,length = irp->IoStatus.Information;
ASSERT(irp->MdlAddress != NULL || irp->UserBuffer != NULL);
if(irp->MdlAddress != NULL)
buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);
else
buffer = irp->UserBuffer;

// 解密也很简单,xor 0x77
for(i=0;i<length;++i)
buffer[i] ^= 0X77;
// 打印解密之后的内容
KdPrint(("cfIrpReadPost: flags = %x length = %x content = %c%c%c%c%c\r\n",
irp->Flags,length,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4]));
}

// 分配一个MDL,带有一个长度为length的缓冲区。
PMDL cfMdlMemoryAlloc(ULONG length)
{
void *buf = ExAllocatePoolWithTag(NonPagedPool,length,CF_MEM_TAG);
PMDL mdl;
if(buf == NULL)
return NULL;
mdl = IoAllocateMdl(buf,length,FALSE,FALSE,NULL);
if(mdl == NULL)
{
ExFreePool(buf);
return NULL;
}
MmBuildMdlForNonPagedPool(mdl);
mdl->Next = NULL;
return mdl;
}

// 释放掉带有MDL的缓冲区。
void cfMdlMemoryFree(PMDL mdl)
{
void *buffer = MmGetSystemAddressForMdlSafe(mdl,NormalPagePriority);
IoFreeMdl(mdl);
ExFreePool(buffer);
}

// 写请求上下文。因为写请求必须恢复原来的irp->MdlAddress
// 或者irp->UserBuffer,所以才需要记录上下文。
typedef struct CF_WRITE_CONTEXT_{
PMDL mdl_address;
PVOID user_buffer;
} CF_WRITE_CONTEXT,*PCF_WRITE_CONTEXT;

// 写请求需要重新分配缓冲区,而且有可能失败。如果失败
// 了就直接报错了。所以要有一个返回。TRUE表示成功,可
// 以继续GO_ON。FALSE表示失败了,错误已经填好,直接
// 完成即可
BOOLEAN cfIrpWritePre(PIRP irp,PIO_STACK_LOCATION irpsp, PVOID *context)
{
PLARGE_INTEGER offset;
ULONG i,length = irpsp->Parameters.Write.Length;
PUCHAR buffer,new_buffer;
PMDL new_mdl = NULL;

// 先准备一个上下文
PCF_WRITE_CONTEXT my_context = (PCF_WRITE_CONTEXT)
ExAllocatePoolWithTag(NonPagedPool,sizeof(CF_WRITE_CONTEXT),CF_MEM_TAG);
if(my_context == NULL)
{
irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
irp->IoStatus.Information = 0;
return FALSE;
}

// 在这里得到缓冲进行加密。要注意的是写请求的缓冲区
// 是不可以直接改写的。必须重新分配。
ASSERT(irp->MdlAddress != NULL || irp->UserBuffer != NULL);
if(irp->MdlAddress != NULL)
{
buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);
new_mdl = cfMdlMemoryAlloc(length);
if(new_mdl == NULL)
new_buffer = NULL;
else
new_buffer = MmGetSystemAddressForMdlSafe(new_mdl,NormalPagePriority);
}
else
{
buffer = irp->UserBuffer;
new_buffer = ExAllocatePoolWithTag(NonPagedPool,length,CF_MEM_TAG);
}
// 如果缓冲区分配失败了,直接退出即可。
if(new_buffer == NULL)
{
irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
irp->IoStatus.Information = 0;
ExFreePool(my_context);
return FALSE;
}
RtlCopyMemory(new_buffer,buffer,length);

// 到了这里一定成功,可以设置上下文了。
my_context->mdl_address = irp->MdlAddress;
my_context->user_buffer = irp->UserBuffer;
*context = (void *)my_context;

// 给irp指定行的mdl,到完成之后再恢复回来。
if(new_mdl == NULL)
irp->UserBuffer = new_buffer;
else
irp->MdlAddress = new_mdl;

offset = &irpsp->Parameters.Write.ByteOffset;
KdPrint(("cfIrpWritePre: fileobj = %x flags = %x offset = %8x length = %x content = %c%c%c%c%c\r\n",
irpsp->FileObject,irp->Flags,offset->LowPart,length,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4]));

// 加密也很简单,xor 0x77
for(i=0;i<length;++i)
new_buffer[i] ^= 0x77;

if(offset->LowPart == FILE_USE_FILE_POINTER_POSITION && offset->HighPart == -1)
{
// 记事本不会出现这样的情况。
ASSERT(FALSE);
}
// 偏移必须修改为增加4KB。
offset->QuadPart += CF_FILE_HEADER_SIZE;
return TRUE;
}

// 请注意无论结果如何,都必须进入WritePost.否则会出现无法恢复
// Write的内容,释放已分配的空间的情况。
void cfIrpWritePost(PIRP irp,PIO_STACK_LOCATION irpsp,void *context)
{
PCF_WRITE_CONTEXT my_context = (PCF_WRITE_CONTEXT) context;
// 到了这里,可以恢复irp的内容了。
if(irp->MdlAddress != NULL)
cfMdlMemoryFree(irp->MdlAddress);
if(irp->UserBuffer != NULL)
ExFreePool(irp->UserBuffer);
irp->MdlAddress = my_context->mdl_address;
irp->UserBuffer = my_context->user_buffer;
ExFreePool(my_context);
}

cf_proc.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
///
/// @file cf_proc.c
/// @author crazy_chu
/// @date 2009-1-30
/// @brief 获得当前进程的名字。
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>

// 这个函数必须在DriverEntry中调用,否则cfCurProcName将不起作用。
static size_t s_cf_proc_name_offset = 0;
void cfCurProcNameInit()
{
ULONG i;
PEPROCESS curproc;
curproc = PsGetCurrentProcess();
// 搜索EPROCESS结构,在其中找到字符串
for(i=0;i<3*4*1024;i++)
{
if(!strncmp("System",(PCHAR)curproc+i,strlen("System")))
{
s_cf_proc_name_offset = i;
break;
}
}
}

// 以下函数可以获得进程名。返回获得的长度。
ULONG cfCurProcName(PUNICODE_STRING name)
{
PEPROCESS curproc;
ULONG i,need_len;
ANSI_STRING ansi_name;
if(s_cf_proc_name_offset == 0)
return 0;

// 获得当前进程PEB,然后移动一个偏移得到进程名所在位置。
curproc = PsGetCurrentProcess();

// 这个名字是ansi字符串,现在转化为unicode字符串。
RtlInitAnsiString(&ansi_name,((PCHAR)curproc + s_cf_proc_name_offset));
need_len = RtlAnsiStringToUnicodeSize(&ansi_name);
if(need_len > name->MaximumLength)
{
return RtlAnsiStringToUnicodeSize(&ansi_name);
}
RtlAnsiStringToUnicodeString(name,&ansi_name,FALSE);
return need_len;
}

// 判断当前进程是不是notepad.exe
BOOLEAN cfIsCurProcSec(void)
{
WCHAR name_buf[32] = { 0 };
UNICODE_STRING proc_name = { 0 };
UNICODE_STRING note_pad = { 0 };
ULONG length;
RtlInitEmptyUnicodeString(&proc_name,name_buf,32*sizeof(WCHAR));
length = cfCurProcName(&proc_name);
RtlInitUnicodeString(&note_pad,L"notepad.exe");
if(RtlCompareUnicodeString(&note_pad,&proc_name,TRUE) == 0)
return TRUE;
return FALSE;
}


cf_sfilter.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
///
/// @file cf_sfilter.c
/// @author crazy_chu
/// @date 2009-1-29
/// @brief 实现sfilter的回调函数。
///
/// 免责声明
/// 本代码为示例代码。未经详尽测试,不保证可靠性。作者对
/// 任何人使用此代码导致的直接和间接损失不负责任。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是楚狂人与wowocock为《寒江独
/// 钓——Windows内核编程与信息安全》所编写的文件透明加密
/// 示例。本工程仅仅支持WindowsXP下,FastFat文件系统下记事
/// 本的加密。未测试与杀毒软件或者其他文件过滤驱动并存的
/// 情况。本代码全部权利为作者保留,仅供读者学习和阅读使
/// 用。未经两位作者书面授权,不得直接复制、或者基于此代
/// 码进行修改、利用此代码提供的全部或者部分技术用于商业
/// 的软件开发、或者其他的获利行为。如有违反,作者保留起
/// 诉和获取赔偿之权力。阅读此代码,则自动视为接受以上授
/// 权协议。如不接受此协议者,请不要阅读此代码。
///

#include <ntifs.h>
#include "..\inc\sfilter\sfilter.h"
#include "cf_proc.h"
#include "cf_list.h"
#include "cf_modify_irp.h"
#include "cf_create.h"
#include "fat_headers/fat.h"
#include "fat_headers/nodetype.h"
#include "fat_headers/fatstruc.h"

#define CF_FILE_HEADER_SIZE (1024*4)

SF_RET OnSfilterIrpPre(
IN PDEVICE_OBJECT dev,
IN PDEVICE_OBJECT next_dev,
IN PVOID extension,
IN PIRP irp,
OUT NTSTATUS *status,
PVOID *context)
{
// 获得当前调用栈
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
PFILE_OBJECT file = irpsp->FileObject;
// 看当前进程是否是加密进程
BOOLEAN proc_sec = cfIsCurProcSec();
BOOLEAN file_sec;

// 我仅仅过滤文件请求。 FileObject不存在的情况一律passthru.
if(file == NULL)
return SF_IRP_PASS;


// 首要决定哪些请求是我们必须过滤的。多余的提前passthru掉。
if( irpsp->MajorFunction != IRP_MJ_CREATE &&
irpsp->MajorFunction != IRP_MJ_CLOSE &&
irpsp->MajorFunction != IRP_MJ_READ &&
irpsp->MajorFunction != IRP_MJ_WRITE &&
irpsp->MajorFunction != IRP_MJ_CLOSE &&
irpsp->MajorFunction != IRP_MJ_CLEANUP &&
irpsp->MajorFunction != IRP_MJ_SET_INFORMATION &&
irpsp->MajorFunction != IRP_MJ_DIRECTORY_CONTROL &&
irpsp->MajorFunction != IRP_MJ_QUERY_INFORMATION)
return SF_IRP_PASS;

if(!cfListInited())
return SF_IRP_PASS;


// 对于文件打开,用cfIrpCreatePre统一处理。
if(irpsp->MajorFunction == IRP_MJ_CREATE)
{
if(proc_sec)
return cfIrpCreatePre(irp,irpsp,file,next_dev);
else
{
// 其他的情况,作为普通进程,不允许打开一个正在加
// 密的文件。但是在这里无法判断这个文件是否正在加
// 密,所以返回GO_ON来判断。
return SF_IRP_GO_ON;
}
}

cfListLock();
file_sec = cfIsFileCrypting(file);
cfListUnlock();

// 如果不是加密的文件的话,就可以直接passthru了,没有别
// 的事情了。
if(!file_sec)
return SF_IRP_PASS;

// 如果是close就可以删除节点了
if(irpsp->MajorFunction == IRP_MJ_CLOSE)
return SF_IRP_GO_ON;

// 操作上有偏移。以下三种请求必须特殊处理。进行GO_ON
// 处理。其他的set information操作不需要处理。
// 1.SET FILE_ALLOCATION_INFORMATION
// 2.SET FILE_END_OF_FILE_INFORMATION
// 3.SET FILE_VALID_DATA_LENGTH_INFORMATION
if(irpsp->MajorFunction == IRP_MJ_SET_INFORMATION &&
(irpsp->Parameters.SetFile.FileInformationClass == FileAllocationInformation ||
irpsp->Parameters.SetFile.FileInformationClass == FileEndOfFileInformation ||
irpsp->Parameters.SetFile.FileInformationClass == FileValidDataLengthInformation ||
irpsp->Parameters.SetFile.FileInformationClass == FileStandardInformation ||
irpsp->Parameters.SetFile.FileInformationClass == FileAllInformation ||
irpsp->Parameters.SetFile.FileInformationClass == FilePositionInformation))
{
// 对这些set information给予修改,使之隐去前面的4k文件头。
cfIrpSetInforPre(irp,irpsp/*,next_dev,file*/);
return SF_IRP_PASS;
}

if(irpsp->MajorFunction == IRP_MJ_QUERY_INFORMATION)
{
// 要对这些read information的结果给予修改。所以返回go on.
// 结束后会调用cfIrpQueryInforPost(irp,irpsp);
if(irpsp->Parameters.QueryFile.FileInformationClass == FileAllInformation ||
irpsp->Parameters.QueryFile.FileInformationClass == FileAllocationInformation ||
irpsp->Parameters.QueryFile.FileInformationClass == FileEndOfFileInformation ||
irpsp->Parameters.QueryFile.FileInformationClass == FileStandardInformation ||
irpsp->Parameters.QueryFile.FileInformationClass == FilePositionInformation ||
irpsp->Parameters.QueryFile.FileInformationClass == FileValidDataLengthInformation)
return SF_IRP_GO_ON;
else
{
// KdPrint(("OnSfilterIrpPre: %x\r\n",irpsp->Parameters.QueryFile.FileInformationClass));
return SF_IRP_PASS;
}
}

// 暂时不处理。
//if(irpsp->MajorFunction == IRP_MJ_DIRECTORY_CONTROL)
//{
// // 要对这些read information的结果给予修改。所以返回go on.
// // 结束后会调用cfIrpQueryInforPost(irp,irpsp);
// if(irpsp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
// irpsp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
// irpsp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation)
// return SF_IRP_GO_ON;
// else
// {
// KdPrint(("OnSfilterIrpPre: Query information: %x passthru.\r\n",
// irpsp->Parameters.QueryDirectory.FileInformationClass));
// return SF_IRP_PASS;
// }
//}

// 最后两种是read和write,这两种都要修改请求后再下传。同时,read要有完成
// 处理。请注意:只处理直接读硬盘的请求。对缓冲文件请求不处理。
if(irpsp->MajorFunction == IRP_MJ_READ &&
(irp->Flags & (IRP_PAGING_IO|IRP_SYNCHRONOUS_PAGING_IO|IRP_NOCACHE)))
{
cfIrpReadPre(irp,irpsp);
return SF_IRP_GO_ON;
}
if(irpsp->MajorFunction == IRP_MJ_WRITE &&
(irp->Flags & (IRP_PAGING_IO|IRP_SYNCHRONOUS_PAGING_IO|IRP_NOCACHE)))
{
if(cfIrpWritePre(irp,irpsp,context))
return SF_IRP_GO_ON;
else
{
IoCompleteRequest(irp, IO_NO_INCREMENT);
return SF_IRP_COMPLETED;
}
}

// 不加任何处理,直接返回。
return SF_IRP_PASS;
}

VOID OnSfilterIrpPost(
IN PDEVICE_OBJECT dev,
IN PDEVICE_OBJECT next_dev,
IN PVOID extension,
IN PIRP irp,
IN NTSTATUS status,
PVOID context)
{
// 获得当前调用栈
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
BOOLEAN crypting,sec_proc,need_crypt,need_write_header;
PFILE_OBJECT file = irpsp->FileObject;
ULONG desired_access;
BOOLEAN proc_sec = cfIsCurProcSec();

// 当前进程是否是加密进程
sec_proc = cfIsCurProcSec();

// 如果操作不成功,就没有必要处理。
if( !NT_SUCCESS(status) &&
!(irpsp->MajorFunction == IRP_MJ_QUERY_INFORMATION &&
irpsp->Parameters.QueryFile.FileInformationClass == FileAllInformation &&
irp->IoStatus.Information > 0) &&
irpsp->MajorFunction != IRP_MJ_WRITE)
{
if(irpsp->MajorFunction == IRP_MJ_READ)
{
KdPrint(("OnSfilterIrpPost: IRP_MJ_READ failed. status = %x information = %x\r\n",
status,irp->IoStatus.Information));
}
else if(irpsp->MajorFunction == IRP_MJ_WRITE)
{
KdPrint(("OnSfilterIrpPost: IRP_MJ_WRITE failed. status = %x information = %x\r\n",
status,irp->IoStatus.Information));
}
return;
}

// 是否是一个已经被加密进程打开的文件
cfListLock();
// 如果是create,不需要恢复文件长度。如果是其他请求,在pre的
// 时候就应该已经恢复了。
crypting = cfIsFileCrypting(file);
cfListUnlock();

// 对所有的文件打开,都用如下的过程操作:
if(irpsp->MajorFunction == IRP_MJ_CREATE)
{
if(proc_sec)
{
ASSERT(crypting == FALSE);
// 如果是加密进程,则追加进去即可。
if(!cfFileCryptAppendLk(file))
{
IoCancelFileOpen(next_dev,file);
irp->IoStatus.Status = STATUS_ACCESS_DENIED;
irp->IoStatus.Information = 0;
KdPrint(("OnSfilterIrpPost: file %wZ failed to call cfFileCryptAppendLk!!!\r\n",&file->FileName));
}
else
{
KdPrint(("OnSfilterIrpPost: file %wZ begin to crypting.\r\n",&file->FileName));
}
}
else
{
// 是普通进程。根据是否是加密文件。如果是加密文件,
// 否决这个操作。
if(crypting)
{
IoCancelFileOpen(next_dev,file);
irp->IoStatus.Status = STATUS_ACCESS_DENIED;
irp->IoStatus.Information = 0;
}
}
}
else if(irpsp->MajorFunction == IRP_MJ_CLOSE)
{
// clean up结束了。这里删除加密节点,删除缓冲。
ASSERT(crypting);
cfCryptFileCleanupComplete(file);
}
else if(irpsp->MajorFunction == IRP_MJ_QUERY_INFORMATION)
{
ASSERT(crypting);
cfIrpQueryInforPost(irp,irpsp);
}
else if(irpsp->MajorFunction == IRP_MJ_READ)
{
ASSERT(crypting);
cfIrpReadPost(irp,irpsp);
}
else if(irpsp->MajorFunction == IRP_MJ_WRITE)
{
ASSERT(crypting);
cfIrpWritePost(irp,irpsp,context);
}
else
{
ASSERT(FALSE);
}
}

NTSTATUS OnSfilterDriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
OUT PUNICODE_STRING userNameString,
OUT PUNICODE_STRING syblnkString,
OUT PULONG extensionSize)
{
UNICODE_STRING user_name,syb_name;
NTSTATUS status = STATUS_SUCCESS;

#if DBG
// _asm int 3
#endif

// 初始化加密链表
cfListInit();

// 确定控制设备的名字和符号链接。
RtlInitUnicodeString(&user_name,L"crypt_file_cdo");
RtlInitUnicodeString(&syb_name,L"crypt_file_cdo_syb");
RtlCopyUnicodeString(userNameString,&user_name);
RtlCopyUnicodeString(syblnkString,&syb_name);

// 设置控制设备为所有用户可用
sfilterSetCdoAccessForAll();

// 初始化进程名字查找
cfCurProcNameInit();


return STATUS_SUCCESS;
}

VOID OnSfilterDriverUnload()
{
// 没什么要做的...;
}

NTSTATUS OnSfilterCDODispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
return STATUS_UNSUCCESSFUL;
}

BOOLEAN OnSfilterAttachPre(
IN PDEVICE_OBJECT ourDevice,
IN PDEVICE_OBJECT theDeviceToAttach,
IN PUNICODE_STRING DeviceName,
IN PVOID extension)
{
// 直接返回TRUE,所有设备都绑定
return TRUE;
}

VOID OnSfilterAttachPost(
IN PDEVICE_OBJECT ourDevice,
IN PDEVICE_OBJECT theDeviceToAttach,
IN PDEVICE_OBJECT theDeviceToAttached,
IN PVOID extension,
IN NTSTATUS status)
{
// 不需要做什么。
}

头文件

cf_create.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
///
/// @file cf_create.c
/// @author crazy_chu
/// @date 2009-2-4
/// @brief 实现对create irp的处理。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是crazy_chu为《寒江独钓——
/// Windows内核编程与信息安全》所编写的文件透明加密示例。
/// 本工程仅仅支持WindowsXP下,FastFat文件系统下记事本的
/// 加密。未测试与杀毒软件或者其他文件过滤驱动并存的情况。
/// 本代码全部权利为作者保留,仅供读者学习和阅读使用。阅
/// 读者须承诺:不直接复制、或者基于此代码进行修改、利用
/// 此代码提供的全部或者部分技术用于商业的软件开发、或者
/// 其他的获利行为。如有违反,同意付出获利十倍之赔偿。
/// 阅读此代码,则自动视为接受以上授权协议。如不接受此协
/// 议者,请不要阅读此代码。

#ifndef _CF_CREATE_HEADER_
#define _CF_CREATE_HEADER_

// 打开预处理。请注意,只有当前进程为加密进程,才需要调
// 用这个预处理来处理。
ULONG cfIrpCreatePre(
PIRP irp,
PIO_STACK_LOCATION irpsp,
PFILE_OBJECT file,
PDEVICE_OBJECT next_dev);

#endif // _CF_CREATE_HEADER_

cf_file_irp.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
///
/// @file cf_file_irp.h
/// @author crazy_chu
/// @date 2009-1-29
/// @brief 对文件的操作,直接发送irp来避免重入
///

#ifndef _CF_FILE_IRP_HEADER_
#define _CF_FILE_IRP_HEADER_

// 自发送SetInformation请求.
NTSTATUS
cfFileSetInformation(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
FILE_INFORMATION_CLASS infor_class,
FILE_OBJECT *set_file,
void* buf,
ULONG buf_len);

NTSTATUS
cfFileQueryInformation(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
FILE_INFORMATION_CLASS infor_class,
void* buf,
ULONG buf_len);

NTSTATUS
cfFileReadWrite(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
LARGE_INTEGER *offset,
ULONG *length,
void *buffer,
BOOLEAN read_write);

NTSTATUS
cfFileGetStandInfo(
PDEVICE_OBJECT dev,
PFILE_OBJECT file,
PLARGE_INTEGER allocate_size,
PLARGE_INTEGER file_size,
BOOLEAN *dir);

NTSTATUS
cfFileSetFileSize(
DEVICE_OBJECT *dev,
FILE_OBJECT *file,
LARGE_INTEGER *file_size);

// 清理缓冲
void cfFileCacheClear(PFILE_OBJECT pFileObject);

#endif // _CF_FILE_IRP_HEADER_

cf_list.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
///
/// @file cf_list.h
/// @author crazy_chu
/// @date 2009-1-29
///

#ifndef _CF_LIST_HEADER_
#define _CF_LIST_HEADER_

void cfListInit();
BOOLEAN cfListInited();
void cfListLock();
void cfListUnlock();
// 任意给定一个文件,判断是否在加密链表中。
BOOLEAN cfIsFileCrypting(PFILE_OBJECT file);
BOOLEAN cfFileCryptAppendLk(PFILE_OBJECT file);
BOOLEAN cfIsFileNeedCrypt(
PFILE_OBJECT file,
PDEVICE_OBJECT next_dev,
ULONG desired_access,
BOOLEAN *need_write_header);
// 当有文件被clean up的时候调用此函数。如果检查发现
// FileObject->FsContext在列表中
BOOLEAN cfCryptFileCleanupComplete(PFILE_OBJECT file);
NTSTATUS cfWriteAHeader(PFILE_OBJECT file,PDEVICE_OBJECT next_dev);

#endif // _CF_LIST_HEADER_

cf_modify_irp.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
///
/// @file cf_modify_irp.c
/// @author crazy_chu
/// @date 2009-2-1
/// @brief 实现对irp请求和结果的修改
///
/// 授权协议
/// 本代码从属于工程crypt_file.是crazy_chu为《寒江独钓——
/// Windows内核编程与信息安全》所编写的文件透明加密示例。
/// 本工程仅仅支持WindowsXP下,FastFat文件系统下记事本的
/// 加密。未测试与杀毒软件或者其他文件过滤驱动并存的情况。
/// 本代码全部权利为作者保留,仅供读者学习和阅读使用。阅
/// 读者须承诺:不直接复制、或者基于此代码进行修改、利用
/// 此代码提供的全部或者部分技术用于商业的软件开发、或者
/// 其他的获利行为。如有违反,同意付出获利十倍之赔偿。
/// 阅读此代码,则自动视为接受以上授权协议。如不接受此协
/// 议者,请不要阅读此代码。

#ifndef _CF_MODIFY_IRP_HEADER_
#define _CF_MODIFY_IRP_HEADER_

void cfIrpSetInforPre(
PIRP irp,
PIO_STACK_LOCATION irpsp);

void cfIrpQueryInforPost(PIRP irp,PIO_STACK_LOCATION irpsp);

void cfIrpDirectoryControlPost(PIRP irp,PIO_STACK_LOCATION irpsp);

void cfIrpReadPre(PIRP irp,PIO_STACK_LOCATION irpsp);

void cfIrpReadPost(PIRP irp,PIO_STACK_LOCATION irpsp);

BOOLEAN cfIrpWritePre(PIRP irp,PIO_STACK_LOCATION irpsp,void **context);

void cfIrpWritePost(PIRP irp,PIO_STACK_LOCATION irpsp,void *context);

#endif // _CF_MODIFY_IRP_HEADER_

cf_proc.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
///
/// @file cf_proc.c
/// @author crazy_chu
/// @date 2009-1-30
/// @brief 获得当前进程的名字。
///
/// 授权协议
/// 本代码从属于工程crypt_file.是crazy_chu为《寒江独钓——
/// Windows内核编程与信息安全》所编写的文件透明加密示例。
/// 本工程仅仅支持WindowsXP下,FastFat文件系统下记事本的
/// 加密。未测试与杀毒软件或者其他文件过滤驱动并存的情况。
/// 本代码全部权利为作者保留,仅供读者学习和阅读使用。阅
/// 读者须承诺:不直接复制、或者基于此代码进行修改、利用
/// 此代码提供的全部或者部分技术用于商业的软件开发、或者
/// 其他的获利行为。如有违反,同意付出获利十倍之赔偿。
/// 阅读此代码,则自动视为接受以上授权协议。如不接受此协
/// 议者,请不要阅读此代码。

#ifndef _CF_PROC_HEADER_
#define _CF_PROC_HEADER_

void cfCurProcNameInit();

// 以下函数可以获得进程名。返回获得的长度。
ULONG cfCurProcName(PUNICODE_STRING name);

// 判断当前进程是不是notepad.exe
BOOLEAN cfIsCurProcSec(void);

#endif // _CF_PROC_HEADER_

fat_headers\\fat.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
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
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

Fat.h

Abstract:

This module defines the on-disk structure of the Fat file system.


--*/

#ifndef _FAT_
#define _FAT_

//
// The following nomenclature is used to describe the Fat on-disk
// structure:
//
// LBN - is the number of a sector relative to the start of the disk.
//
// VBN - is the number of a sector relative to the start of a file,
// directory, or allocation.
//
// LBO - is a byte offset relative to the start of the disk.
//
// VBO - is a byte offset relative to the start of a file, directory
// or allocation.
//

typedef LONGLONG LBO; /* for Fat32, LBO is >32 bits */

typedef LBO *PLBO;

typedef ULONG32 VBO;
typedef VBO *PVBO;


//
// The boot sector is the first physical sector (LBN == 0) on the volume.
// Part of the sector contains a BIOS Parameter Block. The BIOS in the
// sector is packed (i.e., unaligned) so we'll supply a unpacking macro
// to translate a packed BIOS into its unpacked equivalent. The unpacked
// BIOS structure is already defined in ntioapi.h so we only need to define
// the packed BIOS.
//

//
// Define the Packed and Unpacked BIOS Parameter Block
//

typedef struct _PACKED_BIOS_PARAMETER_BLOCK {
UCHAR BytesPerSector[2]; // offset = 0x000 0
UCHAR SectorsPerCluster[1]; // offset = 0x002 2
UCHAR ReservedSectors[2]; // offset = 0x003 3
UCHAR Fats[1]; // offset = 0x005 5
UCHAR RootEntries[2]; // offset = 0x006 6
UCHAR Sectors[2]; // offset = 0x008 8
UCHAR Media[1]; // offset = 0x00A 10
UCHAR SectorsPerFat[2]; // offset = 0x00B 11
UCHAR SectorsPerTrack[2]; // offset = 0x00D 13
UCHAR Heads[2]; // offset = 0x00F 15
UCHAR HiddenSectors[4]; // offset = 0x011 17
UCHAR LargeSectors[4]; // offset = 0x015 21
} PACKED_BIOS_PARAMETER_BLOCK; // sizeof = 0x019 25
typedef PACKED_BIOS_PARAMETER_BLOCK *PPACKED_BIOS_PARAMETER_BLOCK;

typedef struct _PACKED_BIOS_PARAMETER_BLOCK_EX {
UCHAR BytesPerSector[2]; // offset = 0x000 0
UCHAR SectorsPerCluster[1]; // offset = 0x002 2
UCHAR ReservedSectors[2]; // offset = 0x003 3
UCHAR Fats[1]; // offset = 0x005 5
UCHAR RootEntries[2]; // offset = 0x006 6
UCHAR Sectors[2]; // offset = 0x008 8
UCHAR Media[1]; // offset = 0x00A 10
UCHAR SectorsPerFat[2]; // offset = 0x00B 11
UCHAR SectorsPerTrack[2]; // offset = 0x00D 13
UCHAR Heads[2]; // offset = 0x00F 15
UCHAR HiddenSectors[4]; // offset = 0x011 17
UCHAR LargeSectors[4]; // offset = 0x015 21
UCHAR LargeSectorsPerFat[4]; // offset = 0x019 25
UCHAR ExtendedFlags[2]; // offset = 0x01D 29
UCHAR FsVersion[2]; // offset = 0x01F 31
UCHAR RootDirFirstCluster[4]; // offset = 0x021 33
UCHAR FsInfoSector[2]; // offset = 0x025 37
UCHAR BackupBootSector[2]; // offset = 0x027 39
UCHAR Reserved[12]; // offset = 0x029 41
} PACKED_BIOS_PARAMETER_BLOCK_EX; // sizeof = 0x035 53

typedef PACKED_BIOS_PARAMETER_BLOCK_EX *PPACKED_BIOS_PARAMETER_BLOCK_EX;

//
// The IsBpbFat32 macro is defined to work with both packed and unpacked
// BPB structures. Since we are only checking for zero, the byte order
// does not matter.
//

#define IsBpbFat32(bpb) (*(USHORT *)(&(bpb)->SectorsPerFat) == 0)

typedef struct BIOS_PARAMETER_BLOCK {
USHORT BytesPerSector;
UCHAR SectorsPerCluster;
USHORT ReservedSectors;
UCHAR Fats;
USHORT RootEntries;
USHORT Sectors;
UCHAR Media;
USHORT SectorsPerFat;
USHORT SectorsPerTrack;
USHORT Heads;
ULONG32 HiddenSectors;
ULONG32 LargeSectors;
ULONG32 LargeSectorsPerFat;
union {
USHORT ExtendedFlags;
struct {
ULONG ActiveFat:4;
ULONG Reserved0:3;
ULONG MirrorDisabled:1;
ULONG Reserved1:8;
};
};
USHORT FsVersion;
ULONG32 RootDirFirstCluster;
USHORT FsInfoSector;
USHORT BackupBootSector;
} BIOS_PARAMETER_BLOCK, *PBIOS_PARAMETER_BLOCK;

//
// This macro takes a Packed BIOS and fills in its Unpacked equivalent
//

#define FatUnpackBios(Bios,Pbios) { \
CopyUchar2(&(Bios)->BytesPerSector, &(Pbios)->BytesPerSector[0] ); \
CopyUchar1(&(Bios)->SectorsPerCluster, &(Pbios)->SectorsPerCluster[0]); \
CopyUchar2(&(Bios)->ReservedSectors, &(Pbios)->ReservedSectors[0] ); \
CopyUchar1(&(Bios)->Fats, &(Pbios)->Fats[0] ); \
CopyUchar2(&(Bios)->RootEntries, &(Pbios)->RootEntries[0] ); \
CopyUchar2(&(Bios)->Sectors, &(Pbios)->Sectors[0] ); \
CopyUchar1(&(Bios)->Media, &(Pbios)->Media[0] ); \
CopyUchar2(&(Bios)->SectorsPerFat, &(Pbios)->SectorsPerFat[0] ); \
CopyUchar2(&(Bios)->SectorsPerTrack, &(Pbios)->SectorsPerTrack[0] ); \
CopyUchar2(&(Bios)->Heads, &(Pbios)->Heads[0] ); \
CopyUchar4(&(Bios)->HiddenSectors, &(Pbios)->HiddenSectors[0] ); \
CopyUchar4(&(Bios)->LargeSectors, &(Pbios)->LargeSectors[0] ); \
CopyUchar4(&(Bios)->LargeSectorsPerFat,&((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->LargeSectorsPerFat[0] ); \
CopyUchar2(&(Bios)->ExtendedFlags, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->ExtendedFlags[0] ); \
CopyUchar2(&(Bios)->FsVersion, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsVersion[0] ); \
CopyUchar4(&(Bios)->RootDirFirstCluster, \
&((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->RootDirFirstCluster[0] ); \
CopyUchar2(&(Bios)->FsInfoSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsInfoSector[0] ); \
CopyUchar2(&(Bios)->BackupBootSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->BackupBootSector[0] ); \
}

//
// Define the boot sector
//

typedef struct _PACKED_BOOT_SECTOR {
UCHAR Jump[3]; // offset = 0x000 0
UCHAR Oem[8]; // offset = 0x003 3
PACKED_BIOS_PARAMETER_BLOCK PackedBpb; // offset = 0x00B 11
UCHAR PhysicalDriveNumber; // offset = 0x024 36
UCHAR CurrentHead; // offset = 0x025 37
UCHAR Signature; // offset = 0x026 38
UCHAR Id[4]; // offset = 0x027 39
UCHAR VolumeLabel[11]; // offset = 0x02B 43
UCHAR SystemId[8]; // offset = 0x036 54
} PACKED_BOOT_SECTOR; // sizeof = 0x03E 62

typedef PACKED_BOOT_SECTOR *PPACKED_BOOT_SECTOR;

typedef struct _PACKED_BOOT_SECTOR_EX {
UCHAR Jump[3]; // offset = 0x000 0
UCHAR Oem[8]; // offset = 0x003 3
PACKED_BIOS_PARAMETER_BLOCK_EX PackedBpb; // offset = 0x00B 11
UCHAR PhysicalDriveNumber; // offset = 0x040 64
UCHAR CurrentHead; // offset = 0x041 65
UCHAR Signature; // offset = 0x042 66
UCHAR Id[4]; // offset = 0x043 67
UCHAR VolumeLabel[11]; // offset = 0x047 71
UCHAR SystemId[8]; // offset = 0x058 88
} PACKED_BOOT_SECTOR_EX; // sizeof = 0x060 96

typedef PACKED_BOOT_SECTOR_EX *PPACKED_BOOT_SECTOR_EX;

//
// Define the FAT32 FsInfo sector.
//

typedef struct _FSINFO_SECTOR {
ULONG SectorBeginSignature; // offset = 0x000 0
UCHAR ExtraBootCode[480]; // offset = 0x004 4
ULONG FsInfoSignature; // offset = 0x1e4 484
ULONG FreeClusterCount; // offset = 0x1e8 488
ULONG NextFreeCluster; // offset = 0x1ec 492
UCHAR Reserved[12]; // offset = 0x1f0 496
ULONG SectorEndSignature; // offset = 0x1fc 508
} FSINFO_SECTOR, *PFSINFO_SECTOR;

#define FSINFO_SECTOR_BEGIN_SIGNATURE 0x41615252
#define FSINFO_SECTOR_END_SIGNATURE 0xAA550000

#define FSINFO_SIGNATURE 0x61417272

//
// We use the CurrentHead field for our dirty partition info.
//

#define FAT_BOOT_SECTOR_DIRTY 0x01
#define FAT_BOOT_SECTOR_TEST_SURFACE 0x02

//
// Define a Fat Entry type.
//
// This type is used when representing a fat table entry. It also used
// to be used when dealing with a fat table index and a count of entries,
// but the ensuing type casting nightmare sealed this fate. These other
// two types are represented as ULONGs.
//

typedef ULONG32 FAT_ENTRY;

#define FAT32_ENTRY_MASK 0x0FFFFFFFUL

//
// We use these special index values to set the dirty info for
// DOS/Win9x compatibility.
//

#define FAT_CLEAN_VOLUME (~FAT32_ENTRY_MASK | 0)
#define FAT_DIRTY_VOLUME (~FAT32_ENTRY_MASK | 1)

#define FAT_DIRTY_BIT_INDEX 1

//
// Physically, the entry is fully set if clean, and the high
// bit knocked out if it is dirty (i.e., it is really a clean
// bit). This means it is different per-FAT size.
//

#define FAT_CLEAN_ENTRY (~0)

#define FAT12_DIRTY_ENTRY 0x7ff
#define FAT16_DIRTY_ENTRY 0x7fff
#define FAT32_DIRTY_ENTRY 0x7fffffff

//
// The following constants the are the valid Fat index values.
//

#define FAT_CLUSTER_AVAILABLE (FAT_ENTRY)0x00000000
#define FAT_CLUSTER_RESERVED (FAT_ENTRY)0x0ffffff0
#define FAT_CLUSTER_BAD (FAT_ENTRY)0x0ffffff7
#define FAT_CLUSTER_LAST (FAT_ENTRY)0x0fffffff

//
// Fat files have the following time/date structures. Note that the
// following structure is a 32 bits long but USHORT aligned.
//

typedef struct _FAT_TIME {

USHORT DoubleSeconds : 5;
USHORT Minute : 6;
USHORT Hour : 5;

} FAT_TIME;
typedef FAT_TIME *PFAT_TIME;

typedef struct _FAT_DATE {

USHORT Day : 5;
USHORT Month : 4;
USHORT Year : 7; // Relative to 1980

} FAT_DATE;
typedef FAT_DATE *PFAT_DATE;

typedef struct _FAT_TIME_STAMP {

FAT_TIME Time;
FAT_DATE Date;

} FAT_TIME_STAMP;
typedef FAT_TIME_STAMP *PFAT_TIME_STAMP;

//
// Fat files have 8 character file names and 3 character extensions
//

typedef UCHAR FAT8DOT3[11];
typedef FAT8DOT3 *PFAT8DOT3;


//
// The directory entry record exists for every file/directory on the
// disk except for the root directory.
//

typedef struct _PACKED_DIRENT {
FAT8DOT3 FileName; // offset = 0
UCHAR Attributes; // offset = 11
UCHAR NtByte; // offset = 12
UCHAR CreationMSec; // offset = 13
FAT_TIME_STAMP CreationTime; // offset = 14
FAT_DATE LastAccessDate; // offset = 18
union {
USHORT ExtendedAttributes; // offset = 20
USHORT FirstClusterOfFileHi; // offset = 20
};
FAT_TIME_STAMP LastWriteTime; // offset = 22
USHORT FirstClusterOfFile; // offset = 26
ULONG32 FileSize; // offset = 28
} PACKED_DIRENT; // sizeof = 32
typedef PACKED_DIRENT *PPACKED_DIRENT;

//
// A packed dirent is already quadword aligned so simply declare a dirent as a
// packed dirent
//

typedef PACKED_DIRENT DIRENT;
typedef DIRENT *PDIRENT;

//
// The first byte of a dirent describes the dirent. There is also a routine
// to help in deciding how to interpret the dirent.
//

#define FAT_DIRENT_NEVER_USED 0x00
#define FAT_DIRENT_REALLY_0E5 0x05
#define FAT_DIRENT_DIRECTORY_ALIAS 0x2e
#define FAT_DIRENT_DELETED 0xe5

//
// Define the NtByte bits.
//

#define FAT_DIRENT_NT_BYTE_8_LOWER_CASE 0x08
#define FAT_DIRENT_NT_BYTE_3_LOWER_CASE 0x10

//
// Define the various dirent attributes
//

#define FAT_DIRENT_ATTR_READ_ONLY 0x01
#define FAT_DIRENT_ATTR_HIDDEN 0x02
#define FAT_DIRENT_ATTR_SYSTEM 0x04
#define FAT_DIRENT_ATTR_VOLUME_ID 0x08
#define FAT_DIRENT_ATTR_DIRECTORY 0x10
#define FAT_DIRENT_ATTR_ARCHIVE 0x20
#define FAT_DIRENT_ATTR_DEVICE 0x40
#define FAT_DIRENT_ATTR_LFN (FAT_DIRENT_ATTR_READ_ONLY | \
FAT_DIRENT_ATTR_HIDDEN | \
FAT_DIRENT_ATTR_SYSTEM | \
FAT_DIRENT_ATTR_VOLUME_ID)


//
// These macros convert a number of fields in the Bpb to bytes from sectors
//
// ULONG
// FatBytesPerCluster (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//
// ULONG
// FatBytesPerFat (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//
// ULONG
// FatReservedBytes (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//

#define FatBytesPerCluster(B) ((ULONG)((B)->BytesPerSector * (B)->SectorsPerCluster))

#define FatBytesPerFat(B) (IsBpbFat32(B)? \
((ULONG)((B)->BytesPerSector * (B)->LargeSectorsPerFat)) : \
((ULONG)((B)->BytesPerSector * (B)->SectorsPerFat)))

#define FatReservedBytes(B) ((ULONG)((B)->BytesPerSector * (B)->ReservedSectors))

//
// This macro returns the size of the root directory dirent area in bytes
// For Fat32, the root directory is variable in length. This macro returns
// 0 because it is also used to determine the location of cluster 2.
//
// ULONG
// FatRootDirectorySize (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//

#define FatRootDirectorySize(B) ((ULONG)((B)->RootEntries * sizeof(DIRENT)))


//
// This macro returns the first Lbo (zero based) of the root directory on
// the device. This area is after the reserved and fats.
//
// For Fat32, the root directory is moveable. This macro returns the LBO
// for cluster 2 because it is used to determine the location of cluster 2.
// FatRootDirectoryLbo32() returns the actual LBO of the beginning of the
// actual root directory.
//
// LBO
// FatRootDirectoryLbo (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//

#define FatRootDirectoryLbo(B) (FatReservedBytes(B) + ((B)->Fats * FatBytesPerFat(B)))
#define FatRootDirectoryLbo32(B) (FatFileAreaLbo(B)+((B)->RootDirFirstCluster-2)*FatBytesPerCluster(B))

//
// This macro returns the first Lbo (zero based) of the file area on the
// the device. This area is after the reserved, fats, and root directory.
//
// LBO
// FatFirstFileAreaLbo (
// IN PBIOS_PARAMTER_BLOCK Bios
// );
//

#define FatFileAreaLbo(B) (FatRootDirectoryLbo(B) + FatRootDirectorySize(B))

//
// This macro returns the number of clusters on the disk. This value is
// computed by taking the total sectors on the disk subtracting up to the
// first file area sector and then dividing by the sectors per cluster count.
// Note that I don't use any of the above macros since far too much
// superfluous sector/byte conversion would take place.
//
// ULONG
// FatNumberOfClusters (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//

//
// for prior to MS-DOS Version 3.2
//
// After DOS 4.0, at least one of these, Sectors or LargeSectors, will be zero.
// but DOS version 3.2 case, both of these value might contains some value,
// because, before 3.2, we don't have Large Sector entry, some disk might have
// unexpected value in the field, we will use LargeSectors if Sectors eqaul to zero.
//

#define FatNumberOfClusters(B) ( \
\
IsBpbFat32(B) ? \
\
((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \
\
- ((B)->ReservedSectors + \
(B)->Fats * (B)->LargeSectorsPerFat )) \
\
/ \
\
(B)->SectorsPerCluster) \
: \
((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \
\
- ((B)->ReservedSectors + \
(B)->Fats * (B)->SectorsPerFat + \
(B)->RootEntries * sizeof(DIRENT) / (B)->BytesPerSector ) ) \
\
/ \
\
(B)->SectorsPerCluster) \
)

//
// This macro returns the fat table bit size (i.e., 12 or 16 bits)
//
// ULONG
// FatIndexBitSize (
// IN PBIOS_PARAMETER_BLOCK Bios
// );
//

#define FatIndexBitSize(B) \
((UCHAR)(IsBpbFat32(B) ? 32 : (FatNumberOfClusters(B) < 4087 ? 12 : 16)))

//
// This macro raises STATUS_FILE_CORRUPT and marks the Fcb bad if an
// index value is not within the proper range.
// Note that the first two index values are invalid (0, 1), so we must
// add two from the top end to make sure the everything is within range
//
// VOID
// FatVerifyIndexIsValid (
// IN PIRP_CONTEXT IrpContext,
// IN PVCB Vcb,
// IN ULONG Index
// );
//

#define FatVerifyIndexIsValid(IC,V,I) { \
if (((I) < 2) || ((I) > ((V)->AllocationSupport.NumberOfClusters + 1))) { \
FatRaiseStatus(IC,STATUS_FILE_CORRUPT_ERROR); \
} \
}

//
// These two macros are used to translate between Logical Byte Offsets,
// and fat entry indexes. Note the use of variables stored in the Vcb.
// These two macros are used at a higher level than the other macros
// above.
//
// Note, these indexes are true cluster numbers.
//
// LBO
// GetLboFromFatIndex (
// IN FAT_ENTRY Fat_Index,
// IN PVCB Vcb
// );
//
// FAT_ENTRY
// GetFatIndexFromLbo (
// IN LBO Lbo,
// IN PVCB Vcb
// );
//

#define FatGetLboFromIndex(VCB,FAT_INDEX) ( \
( (LBO) \
(VCB)->AllocationSupport.FileAreaLbo + \
(((LBO)((FAT_INDEX) - 2)) << (VCB)->AllocationSupport.LogOfBytesPerCluster) \
) \
)

#define FatGetIndexFromLbo(VCB,LBO) ( \
(ULONG) ( \
(((LBO) - (VCB)->AllocationSupport.FileAreaLbo) >> \
(VCB)->AllocationSupport.LogOfBytesPerCluster) + 2 \
) \
)

//
// The following macro does the shifting and such to lookup an entry
//
// VOID
// FatLookup12BitEntry(
// IN PVOID Fat,
// IN FAT_ENTRY Index,
// OUT PFAT_ENTRY Entry
// );
//

#define FatLookup12BitEntry(FAT,INDEX,ENTRY) { \
\
CopyUchar2((PUCHAR)(ENTRY), (PUCHAR)(FAT) + (INDEX) * 3 / 2); \
\
*ENTRY = (FAT_ENTRY)(0xfff & (((INDEX) & 1) ? (*(ENTRY) >> 4) : \
*(ENTRY))); \
}

//
// The following macro does the tmp shifting and such to store an entry
//
// VOID
// FatSet12BitEntry(
// IN PVOID Fat,
// IN FAT_ENTRY Index,
// IN FAT_ENTRY Entry
// );
//

#define FatSet12BitEntry(FAT,INDEX,ENTRY) { \
\
FAT_ENTRY TmpFatEntry; \
\
CopyUchar2((PUCHAR)&TmpFatEntry, (PUCHAR)(FAT) + (INDEX) * 3 / 2); \
\
TmpFatEntry = (FAT_ENTRY) \
(((INDEX) & 1) ? ((ENTRY) << 4) | (TmpFatEntry & 0xf) \
: (ENTRY) | (TmpFatEntry & 0xf000)); \
\
*((UNALIGNED UCHAR2 *)((PUCHAR)(FAT) + (INDEX) * 3 / 2)) = *((UNALIGNED UCHAR2 *)(&TmpFatEntry)); \
}

//
// The following macro compares two FAT_TIME_STAMPs
//

#define FatAreTimesEqual(TIME1,TIME2) ( \
RtlEqualMemory((TIME1),(TIME2), sizeof(FAT_TIME_STAMP)) \
)


#define EA_FILE_SIGNATURE (0x4445) // "ED"
#define EA_SET_SIGNATURE (0x4145) // "EA"

//
// If the volume contains any ea data then there is one EA file called
// "EA DATA. SF" located in the root directory as Hidden, System and
// ReadOnly.
//

typedef struct _EA_FILE_HEADER {
USHORT Signature; // offset = 0
USHORT FormatType; // offset = 2
USHORT LogType; // offset = 4
USHORT Cluster1; // offset = 6
USHORT NewCValue1; // offset = 8
USHORT Cluster2; // offset = 10
USHORT NewCValue2; // offset = 12
USHORT Cluster3; // offset = 14
USHORT NewCValue3; // offset = 16
USHORT Handle; // offset = 18
USHORT NewHOffset; // offset = 20
UCHAR Reserved[10]; // offset = 22
USHORT EaBaseTable[240]; // offset = 32
} EA_FILE_HEADER; // sizeof = 512

typedef EA_FILE_HEADER *PEA_FILE_HEADER;

typedef USHORT EA_OFF_TABLE[128];

typedef EA_OFF_TABLE *PEA_OFF_TABLE;

//
// Every file with an extended attribute contains in its dirent an index
// into the EaMapTable. The map table contains an offset within the ea
// file (cluster aligned) of the ea data for the file. The individual
// ea data for each file is prefaced with an Ea Data Header.
//

typedef struct _EA_SET_HEADER {
USHORT Signature; // offset = 0
USHORT OwnEaHandle; // offset = 2
ULONG32 NeedEaCount; // offset = 4
UCHAR OwnerFileName[14]; // offset = 8
UCHAR Reserved[4]; // offset = 22
UCHAR cbList[4]; // offset = 26
UCHAR PackedEas[1]; // offset = 30
} EA_SET_HEADER; // sizeof = 30
typedef EA_SET_HEADER *PEA_SET_HEADER;

#define SIZE_OF_EA_SET_HEADER 30

#define MAXIMUM_EA_SIZE 0x0000ffff

#define GetcbList(EASET) (((EASET)->cbList[0] << 0) + \
((EASET)->cbList[1] << 8) + \
((EASET)->cbList[2] << 16) + \
((EASET)->cbList[3] << 24))

#define SetcbList(EASET,CB) { \
(EASET)->cbList[0] = (CB >> 0) & 0x0ff; \
(EASET)->cbList[1] = (CB >> 8) & 0x0ff; \
(EASET)->cbList[2] = (CB >> 16) & 0x0ff; \
(EASET)->cbList[3] = (CB >> 24) & 0x0ff; \
}

//
// Every individual ea in an ea set is declared the following packed ea
//

typedef struct _PACKED_EA {
UCHAR Flags;
UCHAR EaNameLength;
UCHAR EaValueLength[2];
CHAR EaName[1];
} PACKED_EA;
typedef PACKED_EA *PPACKED_EA;

//
// The following two macros are used to get and set the ea value length
// field of a packed ea
//
// VOID
// GetEaValueLength (
// IN PPACKED_EA Ea,
// OUT PUSHORT ValueLength
// );
//
// VOID
// SetEaValueLength (
// IN PPACKED_EA Ea,
// IN USHORT ValueLength
// );
//

#define GetEaValueLength(EA,LEN) { \
*(LEN) = 0; \
CopyUchar2( (LEN), (EA)->EaValueLength ); \
}

#define SetEaValueLength(EA,LEN) { \
CopyUchar2( &((EA)->EaValueLength), (LEN) ); \
}

//
// The following macro is used to get the size of a packed ea
//
// VOID
// SizeOfPackedEa (
// IN PPACKED_EA Ea,
// OUT PUSHORT EaSize
// );
//

#define SizeOfPackedEa(EA,SIZE) { \
ULONG _NL,_DL; _NL = 0; _DL = 0; \
CopyUchar1(&_NL, &(EA)->EaNameLength); \
GetEaValueLength(EA, &_DL); \
*(SIZE) = 1 + 1 + 2 + _NL + 1 + _DL; \
}

#define EA_NEED_EA_FLAG 0x80
#define MIN_EA_HANDLE 1
#define MAX_EA_HANDLE 30719
#define UNUSED_EA_HANDLE 0xffff
#define EA_CBLIST_OFFSET 0x1a
#define MAX_EA_BASE_INDEX 240
#define MAX_EA_OFFSET_INDEX 128


#endif // _FAT_


fat_headers\\fatstruc.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
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
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

FatStruc.h

Abstract:

This module defines the data structures that make up the major internal
part of the Fat file system.


--*/

#ifndef _FATSTRUC_
#define _FATSTRUC_

typedef PVOID PBCB; //**** Bcb's are now part of the cache module


//
// The FAT_DATA record is the top record in the Fat file system in-memory
// data structure. This structure must be allocated from non-paged pool.
//

typedef struct _FAT_DATA {

//
// The type and size of this record (must be FAT_NTC_DATA_HEADER)
//

NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;


PVOID LazyWriteThread;


//
// A queue of all the devices that are mounted by the file system.
//

LIST_ENTRY VcbQueue;

//
// A pointer to the Driver object we were initialized with
//

PDRIVER_OBJECT DriverObject;

//
// A pointer to the filesystem device objects we created.
//

PVOID DiskFileSystemDeviceObject;
PVOID CdromFileSystemDeviceObject;

//
// A resource variable to control access to the global Fat data record
//

ERESOURCE Resource;

//
// A pointer to our EPROCESS struct, which is a required input to the
// Cache Management subsystem.
//

PEPROCESS OurProcess;

//
// The following tells us if we should use Chicago extensions.
//

BOOLEAN ChicagoMode:1;

//
// The following field tells us if we are running on a Fujitsu
// FMR Series. These machines supports extra formats on the
// FAT file system.
//

BOOLEAN FujitsuFMR:1;

//
// Inidicates that FspClose is currently processing closes.
//

BOOLEAN AsyncCloseActive:1;

//
// The following BOOLEAN says shutdown has started on FAT. It
// instructs FspClose to not keep the Vcb resources anymore.
//

BOOLEAN ShutdownStarted:1;

//
// The following flag tells us if we are going to generate LFNs
// for valid 8.3 names with extended characters.
//

BOOLEAN CodePageInvariant:1;

//
// The following flags tell us if we are in an aggresive push to lower
// the size of the deferred close queues.
//

BOOLEAN HighAsync:1;
BOOLEAN HighDelayed:1;

//
// The following list entry is used for performing closes that can't
// be done in the context of the original caller.
//

ULONG AsyncCloseCount;
LIST_ENTRY AsyncCloseList;

//
// The following two fields record if we are delaying a close.
//

ULONG DelayedCloseCount;
LIST_ENTRY DelayedCloseList;

//
// This is the ExWorkerItem that does both kinds of deferred closes.
//

PIO_WORKITEM FatCloseItem;

//
// This spinlock protects several rapid-fire operations. NOTE: this is
// pretty horrible style.
//

KSPIN_LOCK GeneralSpinLock;

//
// Cache manager call back structures, which must be passed on each call
// to CcInitializeCacheMap.
//

CACHE_MANAGER_CALLBACKS CacheManagerCallbacks;
CACHE_MANAGER_CALLBACKS CacheManagerNoOpCallbacks;

} FAT_DATA;
typedef FAT_DATA *PFAT_DATA;

//
// An array of these structures will keep

typedef struct _FAT_WINDOW {

ULONG FirstCluster; // The first cluster in this window.
ULONG LastCluster; // The last cluster in this window.
ULONG ClustersFree; // The number of clusters free in this window.

} FAT_WINDOW;
typedef FAT_WINDOW *PFAT_WINDOW;

//
// Forward reference some circular referenced structures.
//

typedef struct _VCB VCB;
typedef VCB *PVCB;

typedef struct _FCB FCB;
typedef FCB *PFCB;

//
// This structure is used to keep track of information needed to do a
// deferred close. It is now embedded in a CCB so we don't have to
// allocate one in the close path (with mustsucceed).
//

typedef struct {

//
// Two sets of links, one for the global list and one for closes
// on a particular volume.
//

LIST_ENTRY GlobalLinks;
LIST_ENTRY VcbLinks;

PVCB Vcb;
PFCB Fcb;
enum _TYPE_OF_OPEN TypeOfOpen;
BOOLEAN Free;

} CLOSE_CONTEXT;

typedef CLOSE_CONTEXT *PCLOSE_CONTEXT;

//
// The Vcb (Volume control Block) record corresponds to every volume mounted
// by the file system. They are ordered in a queue off of FatData.VcbQueue.
// This structure must be allocated from non-paged pool
//

typedef enum _VCB_CONDITION {
VcbGood = 1,
VcbNotMounted,
VcbBad
} VCB_CONDITION;

typedef struct _VCB {

//
// This is a common head for the FAT volume file
//

FSRTL_ADVANCED_FCB_HEADER VolumeFileHeader;

//
// The links for the device queue off of FatData.VcbQueue
//

LIST_ENTRY VcbLinks;

//
// A pointer the device object passed in by the I/O system on a mount
// This is the target device object that the file system talks to when it
// needs to do any I/O (e.g., the disk stripper device object).
//
//

PDEVICE_OBJECT TargetDeviceObject;

//
// A pointer to the VPB for the volume passed in by the I/O system on
// a mount.
//

PVPB Vpb;

//
// The internal state of the device. This is a collection of fsd device
// state flags.
//

ULONG VcbState;
VCB_CONDITION VcbCondition;

//
// A pointer to the root DCB for this volume
//

struct _FCB *RootDcb;

//
// If the FAT has so many entries that the free cluster bitmap would
// be too large, we split the FAT into buckets, and only one bucket's
// worth of bits are kept in the bitmap.
//

ULONG NumberOfWindows;
PFAT_WINDOW Windows;
PFAT_WINDOW CurrentWindow;

//
// A count of the number of file objects that have opened the volume
// for direct access, and their share access state.
//

CLONG DirectAccessOpenCount;
SHARE_ACCESS ShareAccess;

//
// A count of the number of file objects that have any file/directory
// opened on this volume, not including direct access. And also the
// count of the number of file objects that have a file opened for
// only read access (i.e., they cannot be modifying the disk).
//

CLONG OpenFileCount;
CLONG ReadOnlyCount;

//
// The bios parameter block field contains
// an unpacked copy of the bpb for the volume, it is initialized
// during mount time and can be read by everyone else after that.
//

BIOS_PARAMETER_BLOCK Bpb;

PUCHAR First0x24BytesOfBootSector;

//
// The following structure contains information useful to the
// allocation support routines. Many of them are computed from
// elements of the Bpb, but are too involved to recompute every time
// they are needed.
//

struct {

LBO RootDirectoryLbo; // Lbo of beginning of root directory
LBO FileAreaLbo; // Lbo of beginning of file area
ULONG RootDirectorySize; // size of root directory in bytes

ULONG NumberOfClusters; // total number of clusters on the volume
ULONG NumberOfFreeClusters; // number of free clusters on the volume

UCHAR FatIndexBitSize; // indicates if 12, 16, or 32 bit fat table

UCHAR LogOfBytesPerSector; // Log(Bios->BytesPerSector)
UCHAR LogOfBytesPerCluster; // Log(Bios->SectorsPerCluster)

} AllocationSupport;

//
// The following Mcb is used to keep track of dirty sectors in the Fat.
// Runs of holes denote clean sectors while runs of LBO == VBO denote
// dirty sectors. The VBOs are that of the volume file, starting at
// 0. The granuality of dirt is one sectors, and additions are only
// made in sector chunks to prevent problems with several simultaneous
// updaters.
//

LARGE_MCB DirtyFatMcb;

//
// The FreeClusterBitMap keeps track of all the clusters in the fat.
// A 1 means occupied while a 0 means free. It allows quick location
// of contiguous runs of free clusters. It is initialized on mount
// or verify.
//

RTL_BITMAP FreeClusterBitMap;

//
// The following fast mutex controls access to the free cluster bit map
// and the buckets.
//

FAST_MUTEX FreeClusterBitMapMutex;

//
// A resource variable to control access to the volume specific data
// structures
//

ERESOURCE Resource;

//
// A resource to make sure no one changes the volume bitmap while
// you're using it. Only for volumes with NumberOfWindows > 1.
//

ERESOURCE ChangeBitMapResource;


//
// The following field points to the file object used to do I/O to
// the virtual volume file. The virtual volume file maps sectors
// 0 through the end of fat and is of a fixed size (determined during
// mount)
//

PFILE_OBJECT VirtualVolumeFile;

//
// The following field contains a record of special pointers used by
// MM and Cache to manipluate section objects. Note that the values
// are set outside of the file system. However the file system on an
// open/create will set the file object's SectionObject field to point
// to this field
//

SECTION_OBJECT_POINTERS SectionObjectPointers;

//
// The following fields is a hint cluster index used by the file system
// when allocating a new cluster.
//

ULONG ClusterHint;

//
// This field contains the "DeviceObject" that this volume is
// currently mounted on. Note Vcb->Vpb->RealDevice is constant.
//

PDEVICE_OBJECT CurrentDevice;

//
// This is a pointer to the file object and the Fcb which represent the ea data.
//

PFILE_OBJECT VirtualEaFile;
struct _FCB *EaFcb;

//
// The following field is a pointer to the file object that has the
// volume locked. if the VcbState has the locked flag set.
//

PFILE_OBJECT FileObjectWithVcbLocked;

//
// The following is the head of a list of notify Irps.
//

LIST_ENTRY DirNotifyList;

//
// The following is used to synchronize the dir notify list.
//

PNOTIFY_SYNC NotifySync;

//
// The following fast mutex is used to synchronize directory stream
// file object creation.
//

FAST_MUTEX DirectoryFileCreationMutex;

//
// This field holds the thread address of the current (or most recent
// depending on VcbState) thread doing a verify operation on this volume.
//

PKTHREAD VerifyThread;

//
// The following two structures are used for CleanVolume callbacks.
//

KDPC CleanVolumeDpc;
KTIMER CleanVolumeTimer;

//
// This field records the last time FatMarkVolumeDirty was called, and
// avoids excessive calls to push the CleanVolume forward in time.
//

LARGE_INTEGER LastFatMarkVolumeDirtyCall;

//
// The following fields holds a pointer to a struct which is used to
// hold performance counters.
//

struct _FILE_SYSTEM_STATISTICS *Statistics;

//
// The property tunneling cache for this volume
//

TUNNEL Tunnel;

//
// The media change count is returned by IOCTL_CHECK_VERIFY and
// is used to verify that no user-mode app has swallowed a media change
// notification. This is only meaningful for removable media.
//

ULONG ChangeCount;

//
// Preallocated VPB for swapout, so we are not forced to consider
// must succeed pool.
//

PVPB SwapVpb;

//
// Per volume threading of the close queues.
//

LIST_ENTRY AsyncCloseList;
LIST_ENTRY DelayedCloseList;

//
// Fast mutex used by the ADVANCED FCB HEADER in this structure
//

FAST_MUTEX AdvancedFcbHeaderMutex;

//
// This is the close context associated with the Virtual Volume File.
//

PCLOSE_CONTEXT CloseContext;

//
// How many close contexts were preallocated on this Vcb
//
#if DBG
ULONG CloseContextCount;
#endif
} VCB;
typedef VCB *PVCB;

#define VCB_STATE_FLAG_LOCKED (0x00000001)
#define VCB_STATE_FLAG_REMOVABLE_MEDIA (0x00000002)
#define VCB_STATE_FLAG_VOLUME_DIRTY (0x00000004)
#define VCB_STATE_FLAG_MOUNTED_DIRTY (0x00000010)
#define VCB_STATE_FLAG_SHUTDOWN (0x00000040)
#define VCB_STATE_FLAG_CLOSE_IN_PROGRESS (0x00000080)
#define VCB_STATE_FLAG_DELETED_FCB (0x00000100)
#define VCB_STATE_FLAG_CREATE_IN_PROGRESS (0x00000200)
#define VCB_STATE_FLAG_BOOT_OR_PAGING_FILE (0x00000800)
#define VCB_STATE_FLAG_DEFERRED_FLUSH (0x00001000)
#define VCB_STATE_FLAG_ASYNC_CLOSE_ACTIVE (0x00002000)
#define VCB_STATE_FLAG_WRITE_PROTECTED (0x00004000)
#define VCB_STATE_FLAG_REMOVAL_PREVENTED (0x00008000)
#define VCB_STATE_FLAG_VOLUME_DISMOUNTED (0x00010000)
#define VCB_STATE_VPB_NOT_ON_DEVICE (0x00020000)

//
// N.B - VOLUME_DISMOUNTED is an indication that FSCTL_DISMOUNT volume was
// executed on a volume. It does not replace VcbCondition as an indication
// that the volume is invalid/unrecoverable.
//

//
// Define the file system statistics struct. Vcb->Statistics points to an
// array of these (one per processor) and they must be 64 byte aligned to
// prevent cache line tearing.
//

typedef struct _FILE_SYSTEM_STATISTICS {

//
// This contains the actual data.
//

FILESYSTEM_STATISTICS Common;
FAT_STATISTICS Fat;

//
// Pad this structure to a multiple of 64 bytes.
//

UCHAR Pad[64-(sizeof(FILESYSTEM_STATISTICS)+sizeof(FAT_STATISTICS))%64];

} FILE_SYSTEM_STATISTICS;

typedef FILE_SYSTEM_STATISTICS *PFILE_SYSTEM_STATISTICS;


//
// The Volume Device Object is an I/O system device object with a workqueue
// and an VCB record appended to the end. There are multiple of these
// records, one for every mounted volume, and are created during
// a volume mount operation. The work queue is for handling an overload of
// work requests to the volume.
//

typedef struct _VOLUME_DEVICE_OBJECT {

DEVICE_OBJECT DeviceObject;

//
// The following field tells how many requests for this volume have
// either been enqueued to ExWorker threads or are currently being
// serviced by ExWorker threads. If the number goes above
// a certain threshold, put the request on the overflow queue to be
// executed later.
//

ULONG PostedRequestCount;

//
// The following field indicates the number of IRP's waiting
// to be serviced in the overflow queue.
//

ULONG OverflowQueueCount;

//
// The following field contains the queue header of the overflow queue.
// The Overflow queue is a list of IRP's linked via the IRP's ListEntry
// field.
//

LIST_ENTRY OverflowQueue;

//
// The following spinlock protects access to all the above fields.
//

KSPIN_LOCK OverflowQueueSpinLock;

//
// This is a common head for the FAT volume file
//

FSRTL_COMMON_FCB_HEADER VolumeFileHeader;

//
// This is the file system specific volume control block.
//

VCB Vcb;

} VOLUME_DEVICE_OBJECT;

typedef VOLUME_DEVICE_OBJECT *PVOLUME_DEVICE_OBJECT;


//
// This is the structure used to contains the short name for a file
//

typedef struct _FILE_NAME_NODE {

//
// This points back to the Fcb for this file.
//

struct _FCB *Fcb;

//
// This is the name of this node.
//

union {

OEM_STRING Oem;

UNICODE_STRING Unicode;

} Name;

//
// Marker so we can figure out what kind of name we opened up in
// Fcb searches
//

BOOLEAN FileNameDos;

//
// And the links. Our parent Dcb has a pointer to the root entry.
//

RTL_SPLAY_LINKS Links;

} FILE_NAME_NODE;
typedef FILE_NAME_NODE *PFILE_NAME_NODE;

//
// This structure contains fields which must be in non-paged pool.
//

typedef struct _NON_PAGED_FCB {

//
// The following field contains a record of special pointers used by
// MM and Cache to manipluate section objects. Note that the values
// are set outside of the file system. However the file system on an
// open/create will set the file object's SectionObject field to point
// to this field
//

SECTION_OBJECT_POINTERS SectionObjectPointers;

//
// This context is non-zero only if the file currently has asynchronous
// non-cached valid data length extending writes. It allows
// synchronization between pending writes and other operations.
//

ULONG OutstandingAsyncWrites;

//
// This event is set when OutstandingAsyncWrites transitions to zero.
//

PKEVENT OutstandingAsyncEvent;

//
// This is the mutex that is inserted into the FCB_ADVANCED_HEADER
// FastMutex field
//

FAST_MUTEX AdvancedFcbHeaderMutex;

} NON_PAGED_FCB;

typedef NON_PAGED_FCB *PNON_PAGED_FCB;

//
// The Fcb/Dcb record corresponds to every open file and directory, and to
// every directory on an opened path. They are ordered in two queues, one
// queue contains every Fcb/Dcb record off of FatData.FcbQueue, the other
// queue contains only device specific records off of Vcb.VcbSpecificFcbQueue
//

typedef enum _FCB_CONDITION {
FcbGood = 1,
FcbBad,
FcbNeedsToBeVerified
} FCB_CONDITION;

typedef struct _FCB {

//
// The following field is used for fast I/O
//
// The following comments refer to the use of the AllocationSize field
// of the FsRtl-defined header to the nonpaged Fcb.
//
// For a directory when we create a Dcb we will not immediately
// initialize the cache map, instead we will postpone it until our first
// call to FatReadDirectoryFile or FatPrepareWriteDirectoryFile.
// At that time we will search the Fat to find out the current allocation
// size (by calling FatLookupFileAllocationSize) and then initialize the
// cache map to this allocation size.
//
// For a file when we create an Fcb we will not immediately initialize
// the cache map, instead we will postpone it until we need it and
// then we determine the allocation size from either searching the
// fat to determine the real file allocation, or from the allocation
// that we've just allocated if we're creating a file.
//
// A value of -1 indicates that we do not know what the current allocation
// size really is, and need to examine the fat to find it. A value
// of than -1 is the real file/directory allocation size.
//
// Whenever we need to extend the allocation size we call
// FatAddFileAllocation which (if we're really extending the allocation)
// will modify the Fat, Mcb, and update this field. The caller
// of FatAddFileAllocation is then responsible for altering the Cache
// map size.
//
// We are now using the ADVANCED fcb header to support filter contexts
// at the stream level
//

FSRTL_ADVANCED_FCB_HEADER Header;

//
// This structure contains fields which must be in non-paged pool.
//

PNON_PAGED_FCB NonPaged;

//
// The head of the fat alloaction chain. FirstClusterOfFile == 0
// means that the file has no current allocation.
//

ULONG FirstClusterOfFile;

//
// The links for the queue of all fcbs for a specific dcb off of
// Dcb.ParentDcbQueue. For the root directory this queue is empty
// For a non-existent fcb this queue is off of the non existent
// fcb queue entry in the vcb.
//

LIST_ENTRY ParentDcbLinks;

//
// A pointer to the Dcb that is the parent directory containing
// this fcb. If this record itself is the root dcb then this field
// is null.
//

struct _FCB *ParentDcb;

//
// A pointer to the Vcb containing this Fcb
//

PVCB Vcb;

//
// The internal state of the Fcb. This is a collection Fcb state flags.
// Also the shared access for each time this file/directory is opened.
//

ULONG FcbState;
FCB_CONDITION FcbCondition;
SHARE_ACCESS ShareAccess;

#ifdef SYSCACHE_COMPILE

//
// For syscache we keep a bitmask that tells us if we have dispatched IO for
// the page aligned chunks of the stream.
//

PULONG WriteMask;
ULONG WriteMaskData;

#endif

//
// A count of the number of file objects that have been opened for
// this file/directory, but not yet been cleaned up yet. This count
// is only used for data file objects, not for the Acl or Ea stream
// file objects. This count gets decremented in FatCommonCleanup,
// while the OpenCount below gets decremented in FatCommonClose.
//

CLONG UncleanCount;

//
// A count of the number of file objects that have opened
// this file/directory. For files & directories the FsContext of the
// file object points to this record.
//

CLONG OpenCount;

//
// A count of how many of "UncleanCount" handles were opened for
// non-cached I/O.
//

CLONG NonCachedUncleanCount;

//
// The following field is used to locate the dirent for this fcb/dcb.
// All directory are opened as mapped files so the only additional
// information we need to locate this dirent (beside its parent directory)
// is the byte offset for the dirent. Note that for the root dcb
// this field is not used.
//

VBO DirentOffsetWithinDirectory;

//
// The following field is filled in when there is an Lfn associated
// with this file. It is the STARTING offset of the Lfn.
//

VBO LfnOffsetWithinDirectory;

//
// Thess entries is kept in ssync with the dirent. It allows a more
// accurate verify capability and speeds up FatFastQueryBasicInfo().
//

LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;

//
// Valid data to disk
//

ULONG ValidDataToDisk;

//
// The following field contains the retrieval mapping structure
// for the file/directory. Note that for the Root Dcb this
// structure is set at mount time. Also note that in this
// implementation of Fat the Mcb really maps VBOs to LBOs and not
// VBNs to LBNs.
//

LARGE_MCB Mcb;

//
// The following union is cased off of the node type code for the fcb.
// There is a seperate case for the directory versus file fcbs.
//

union {

//
// A Directory Control Block (Dcb)
//

struct {

//
// A queue of all the fcbs/dcbs that are opened under this
// Dcb.
//

LIST_ENTRY ParentDcbQueue;

//
// The following field points to the file object used to do I/O to
// the directory file for this dcb. The directory file maps the
// sectors for the directory. This field is initialized by
// CreateRootDcb but is left null by CreateDcb. It isn't
// until we try to read/write the directory file that we
// create the stream file object for non root dcbs.
//

ULONG DirectoryFileOpenCount;
PFILE_OBJECT DirectoryFile;

//
// If the UnusedDirentVbo is != 0xffffffff, then the dirent at this
// offset is guarenteed to unused. A value of 0xffffffff means
// it has yet to be initialized. Note that a value beyond the
// end of allocation means that there an unused dirent, but we
// will have to allocate another cluster to use it.
//
// DeletedDirentHint contains lowest possible VBO of a deleted
// dirent (assuming as above that it is not 0xffffffff).
//

VBO UnusedDirentVbo;
VBO DeletedDirentHint;

//
// The following two entries links together all the Fcbs
// opened under this Dcb sorted in a splay tree by name.
//
// I'd like to go into why we have (and must have) two separate
// splay trees within the current fastfat architecture. I will
// provide some insight into what would have to change if we
// wanted to have a single UNICODE tree.
//
// What makes FAT unique is that both Oem and Unicode names sit
// side by side on disk. Several unique UNICODE names coming
// into fastfat can match a single OEM on-disk name, and there
// is really no way to enumerate all the possible UNICODE
// source strings that can map to a given OEM name. This argues
// for converting the incomming UNICODE name into OEM, and then
// running through an OEM splay tree of the open files. This
// works well when there are only OEM names on disk.
//
// The UNICODE name on disk can be VERY different from the short
// name in the DIRENT and not even representable in the OEM code
// page. Even if it were representable in OEM, it is possible
// that a case varient of the original UNICODE name would match
// a different OEM name, causing us to miss the Fcb in the
// prefix lookup phase. In these cases, we must put UNICODE
// name in the splay to guarentee that we find any case varient
// of the input UNICODE name. See the routine description of
// FatConstructNamesInFcb() for a detailed analysis of how we
// detect this case.
//
// The fundamental limitation we are imposing here is that if
// an Fcb exists for an open file, we MUST find it during the
// prefix stage. This is a basic premise of the create path
// in fastfat. In fact if we later find it gravelling through
// the disk (but not the splay tree), we will bug check if we
// try to add a duplicate entry to the splay tree (not to
// mention having two Fcbs). If we had some mechanism to deal
// with cases (and they would be rare) that we don't find the
// entry in the splay tree, but the Fcb is actually in there,
// then we could go to a single UNICODE splay tree. While
// this uses more pool for the splay tree, and makes string
// compares maybe take a bit as longer, it would eliminate the
// need for any NLS conversion during the prefix phase, so it
// might really be a net win.
//
// The current scheme was optimized for non-extended names
// (i.e. US names). As soon as you start using extended
// characters, then it is clearly a win as many code paths
// become active that would otherwise not be needed if we
// only had a single UNICODE splay tree.
//
// We may think about changing this someday.
//

PRTL_SPLAY_LINKS RootOemNode;
PRTL_SPLAY_LINKS RootUnicodeNode;

//
// The following field keeps track of free dirents, i.e.,
// dirents that are either unallocated for deleted.
//

RTL_BITMAP FreeDirentBitmap;

//
// Since the FCB specific part of this union is larger, use
// the slack here for an initial bitmap buffer. Currently
// there is enough space here for an 8K cluster.
//

ULONG FreeDirentBitmapBuffer[1];

} Dcb;

//
// A File Control Block (Fcb)
//

struct {

//
// The following field is used by the filelock module
// to maintain current byte range locking information.
//

FILE_LOCK FileLock;

//
// The following field is used by the oplock module
// to maintain current oplock information.
//

OPLOCK Oplock;

//
// This pointer is used to detect writes that eminated in the
// cache manager's lazywriter. It prevents lazy writer threads,
// who already have the Fcb shared, from trying to acquire it
// exclusive, and thus causing a deadlock.
//

PVOID LazyWriteThread;

} Fcb;

} Specific;

//
// The following field is used to verify that the Ea's for a file
// have not changed between calls to query for Ea's. It is compared
// with a similar field in a Ccb.
//
// IMPORTANT!! **** DO NOT MOVE THIS FIELD ****
//
// The slack space in the union above is computed from
// the field offset of the EaModificationCount.
//

ULONG EaModificationCount;

//
// The following field is the fully qualified file name for this FCB/DCB
// starting from the root of the volume, and last file name in the
// fully qualified name.
//

FILE_NAME_NODE ShortName;

//
// The following field is only filled in if it is needed with the user's
// opened path
//

UNICODE_STRING FullFileName;

USHORT FinalNameLength;

//
// To make life simpler we also keep in the Fcb/Dcb a current copy of
// the fat attribute byte for the file/directory. This field must
// also be updated when we create the Fcb, modify the File, or verify
// the Fcb
//

UCHAR DirentFatFlags;

//
// The case preserved long filename
//

UNICODE_STRING ExactCaseLongName;

//
// If the UNICODE Lfn is fully expressible in the system Oem code
// page, then we will store it in a prefix table, otherwise we will
// store the last UNICODE name in the Fcb. In both cases the name
// has been upcased.
//
// Note that we may need neither of these fields if an LFN was strict
// 8.3 or differed only in case. Indeed if there wasn't an LFN, we
// don't need them at all.
//

union {

//
// This first field is present if FCB_STATE_HAS_OEM_LONG_NAME
// is set in the FcbState.
//

FILE_NAME_NODE Oem;

//
// This first field is present if FCB_STATE_HAS_UNICODE_LONG_NAME
// is set in the FcbState.
//

FILE_NAME_NODE Unicode;

} LongName;

//
// Defragmentation / ReallocateOnWrite synchronization object. This
// is filled in by FatMoveFile() and affects the read and write paths.
//

PKEVENT MoveFileEvent;

} FCB, *PFCB;

#ifndef BUILDING_FSKDEXT
//
// DCB clashes with a type defined outside the filesystems, in headers
// pulled in by FSKD. We don't need this typedef for fskd anyway....
//
typedef FCB DCB;
typedef DCB *PDCB;
#endif


//
// Here are the Fcb state fields.
//

#define FCB_STATE_DELETE_ON_CLOSE (0x00000001)
#define FCB_STATE_TRUNCATE_ON_CLOSE (0x00000002)
#define FCB_STATE_PAGING_FILE (0x00000004)
#define FCB_STATE_FORCE_MISS_IN_PROGRESS (0x00000008)
#define FCB_STATE_FLUSH_FAT (0x00000010)
#define FCB_STATE_TEMPORARY (0x00000020)
#define FCB_STATE_SYSTEM_FILE (0x00000080)
#define FCB_STATE_NAMES_IN_SPLAY_TREE (0x00000100)
#define FCB_STATE_HAS_OEM_LONG_NAME (0x00000200)
#define FCB_STATE_HAS_UNICODE_LONG_NAME (0x00000400)
#define FCB_STATE_DELAY_CLOSE (0x00000800)

//
// Copies of the dirent's FAT_DIRENT_NT_BYTE_* flags for
// preserving case of the short name of a file
//

#define FCB_STATE_8_LOWER_CASE (0x00001000)
#define FCB_STATE_3_LOWER_CASE (0x00002000)

//
// This is the slack allocation in the Dcb part of the UNION above
//

#define DCB_UNION_SLACK_SPACE ((ULONG) \
(FIELD_OFFSET(DCB, EaModificationCount) - \
FIELD_OFFSET(DCB, Specific.Dcb.FreeDirentBitmapBuffer)) \
)

//
// This is the special (64bit) allocation size that indicates the
// real size must be retrieved from disk. Define it here so we
// avoid excessive magic numbering around the driver.
//

#define FCB_LOOKUP_ALLOCATIONSIZE_HINT ((LONGLONG) -1)


//
// The Ccb record is allocated for every file object. Note that this
// record is exactly 0x34 long on x86 so that it will fit into a 0x40
// piece of pool. Please carefully consider modifications.
//
// Define the Flags field.
//

#define CCB_FLAG_MATCH_ALL (0x0001)
#define CCB_FLAG_SKIP_SHORT_NAME_COMPARE (0x0002)

//
// This tells us whether we allocated buffers to hold search templates.
//

#define CCB_FLAG_FREE_OEM_BEST_FIT (0x0004)
#define CCB_FLAG_FREE_UNICODE (0x0008)

//
// These flags prevents cleanup from updating the modify time, etc.
//

#define CCB_FLAG_USER_SET_LAST_WRITE (0x0010)
#define CCB_FLAG_USER_SET_LAST_ACCESS (0x0020)
#define CCB_FLAG_USER_SET_CREATION (0x0040)

//
// This bit says the file object associated with this Ccb was opened for
// read only access.
//

#define CCB_FLAG_READ_ONLY (0x0080)

//
// These flags, are used is DASD handles in read and write.
//

#define CCB_FLAG_DASD_FLUSH_DONE (0x0100)
#define CCB_FLAG_DASD_PURGE_DONE (0x0200)

//
// This flag keeps track of a handle that was opened for
// DELETE_ON_CLOSE.
//

#define CCB_FLAG_DELETE_ON_CLOSE (0x0400)

//
// This flag keeps track of which side of the name pair on the file
// associated with the handle was opened
//

#define CCB_FLAG_OPENED_BY_SHORTNAME (0x0800)

//
// This flag indicates that the query template has not been upcased
// (i.e., query should be case-insensitive)
//

#define CCB_FLAG_QUERY_TEMPLATE_MIXED (0x1000)

//
// This flag indicates that reads and writes via this DASD handle
// are allowed to start or extend past the end of file.
//

#define CCB_FLAG_ALLOW_EXTENDED_DASD_IO (0x2000)

//
// This flag indicates we want to match volume labels in directory
// searches (important for the root dir defrag).
//

#define CCB_FLAG_MATCH_VOLUME_ID (0x4000)

//
// This flag indicates the ccb has been converted over into a
// close context for asynchronous/delayed closing of the handle.
//

#define CCB_FLAG_CLOSE_CONTEXT (0x8000)

//
// This flag indicates that when the handle is closed, we want
// a physical dismount to occur.
//

#define CCB_FLAG_COMPLETE_DISMOUNT (0x10000)

//
// This flag indicates the handle may not call priveleged
// FSCTL which modify the volume.
//

#define CCB_FLAG_MANAGE_VOLUME_ACCESS (0x20000)

typedef struct _CCB {

//
// Type and size of this record (must be FAT_NTC_CCB)
//

NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;

//
// Define a 24bit wide field for Flags, but a UCHAR for Wild Cards Present
// since it is used so often. Line these up on byte boundaries for grins.
//

ULONG Flags:24;
BOOLEAN ContainsWildCards;

//
// Overlay a close context on the data of the CCB. The remaining
// fields are not useful during close, and we would like to avoid
// paying extra pool for it.
//

union {

struct {

//
// Save the offset to start search from.
//

VBO OffsetToStartSearchFrom;

//
// The query template is used to filter directory query requests.
// It originally is set to null and on the first call the NtQueryDirectory
// it is set to the input filename or "*" if the name is not supplied.
// All subsquent queries then use this template.
//
// The Oem structure are unions because if the name is wild we store
// the arbitrary length string, while if the name is constant we store
// 8.3 representation for fast comparison.
//

union {

//
// If the template contains a wild card use this.
//

OEM_STRING Wild;

//
// If the name is constant, use this part.
//

FAT8DOT3 Constant;

} OemQueryTemplate;

UNICODE_STRING UnicodeQueryTemplate;

//
// The field is compared with the similar field in the Fcb to determine
// if the Ea's for a file have been modified.
//

ULONG EaModificationCount;

//
// The following field is used as an offset into the Eas for a
// particular file. This will be the offset for the next
// Ea to return. A value of 0xffffffff indicates that the
// Ea's are exhausted.
//

ULONG OffsetOfNextEaToReturn;

};

CLOSE_CONTEXT CloseContext;
};

} CCB;
typedef CCB *PCCB;

//
// The Irp Context record is allocated for every orginating Irp. It is
// created by the Fsd dispatch routines, and deallocated by the FatComplete
// request routine. It contains a structure called of type REPINNED_BCBS
// which is used to retain pinned bcbs needed to handle abnormal termination
// unwinding.
//

#define REPINNED_BCBS_ARRAY_SIZE (4)

typedef struct _REPINNED_BCBS {

//
// A pointer to the next structure contains additional repinned bcbs
//

struct _REPINNED_BCBS *Next;

//
// A fixed size array of pinned bcbs. Whenever a new bcb is added to
// the repinned bcb structure it is added to this array. If the
// array is already full then another repinned bcb structure is allocated
// and pointed to with Next.
//

PBCB Bcb[ REPINNED_BCBS_ARRAY_SIZE ];

} REPINNED_BCBS;
typedef REPINNED_BCBS *PREPINNED_BCBS;

typedef struct _IRP_CONTEXT {

//
// Type and size of this record (must be FAT_NTC_IRP_CONTEXT)
//

NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;

//
// This structure is used for posting to the Ex worker threads.
//

WORK_QUEUE_ITEM WorkQueueItem;

//
// A pointer to the originating Irp.
//

PIRP OriginatingIrp;

//
// Originating Device (required for workque algorithms)
//

PDEVICE_OBJECT RealDevice;

//
// Originating Vcb (required for exception handling)
// On mounts, this will be set before any exceptions
// indicating corruption can be thrown.
//

PVCB Vcb;

//
// Major and minor function codes copied from the Irp
//

UCHAR MajorFunction;
UCHAR MinorFunction;

//
// The following fields indicate if we can wait/block for a resource
// or I/O, if we are to do everything write through, and if this
// entry into the Fsd is a recursive call.
//

UCHAR PinCount;

ULONG Flags;

//
// The following field contains the NTSTATUS value used when we are
// unwinding due to an exception
//

NTSTATUS ExceptionStatus;

//
// The following context block is used for non-cached Io
//

struct _FAT_IO_CONTEXT *FatIoContext;

//
// For a abnormal termination unwinding this field contains the Bcbs
// that are kept pinned until the Irp is completed.
//

REPINNED_BCBS Repinned;

} IRP_CONTEXT;
typedef IRP_CONTEXT *PIRP_CONTEXT;

#define IRP_CONTEXT_FLAG_DISABLE_DIRTY (0x00000001)
#define IRP_CONTEXT_FLAG_WAIT (0x00000002)
#define IRP_CONTEXT_FLAG_WRITE_THROUGH (0x00000004)
#define IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH (0x00000008)
#define IRP_CONTEXT_FLAG_RECURSIVE_CALL (0x00000010)
#define IRP_CONTEXT_FLAG_DISABLE_POPUPS (0x00000020)
#define IRP_CONTEXT_FLAG_DEFERRED_WRITE (0x00000040)
#define IRP_CONTEXT_FLAG_VERIFY_READ (0x00000080)
#define IRP_CONTEXT_STACK_IO_CONTEXT (0x00000100)
#define IRP_CONTEXT_FLAG_IN_FSP (0x00000200)
#define IRP_CONTEXT_FLAG_USER_IO (0x00000400) // for performance counters
#define IRP_CONTEXT_FLAG_DISABLE_RAISE (0x00000800)
#define IRP_CONTEXT_FLAG_PARENT_BY_CHILD (0x80000000)


//
// Context structure for non-cached I/O calls. Most of these fields
// are actually only required for the Read/Write Multiple routines, but
// the caller must allocate one as a local variable anyway before knowing
// whether there are multiple requests are not. Therefore, a single
// structure is used for simplicity.
//

typedef struct _FAT_IO_CONTEXT {

//
// These two field are used for multiple run Io
//

LONG IrpCount;
PIRP MasterIrp;

//
// MDL to describe partial sector zeroing
//

PMDL ZeroMdl;

union {

//
// This element handles the asychronous non-cached Io
//

struct {
PERESOURCE Resource;
PERESOURCE Resource2;
ERESOURCE_THREAD ResourceThreadId;
ULONG RequestedByteCount;
PFILE_OBJECT FileObject;
PNON_PAGED_FCB NonPagedFcb;
} Async;

//
// and this element the sycnrhonous non-cached Io
//

KEVENT SyncEvent;

} Wait;

} FAT_IO_CONTEXT;

typedef FAT_IO_CONTEXT *PFAT_IO_CONTEXT;

//
// An array of these structures is passed to FatMultipleAsync describing
// a set of runs to execute in parallel.
//

typedef struct _IO_RUNS {

LBO Lbo;
VBO Vbo;
ULONG Offset;
ULONG ByteCount;
PIRP SavedIrp;

} IO_RUN;

typedef IO_RUN *PIO_RUN;

//
// This structure is used by FatDeleteDirent to preserve the first cluster
// and file size info for undelete utilities.
//

typedef struct _DELETE_CONTEXT {

ULONG FileSize;
ULONG FirstClusterOfFile;

} DELETE_CONTEXT;

typedef DELETE_CONTEXT *PDELETE_CONTEXT;

//
// This record is used with to set a flush to go off one second after the
// first write on slow devices with a physical indication of activity, like
// a floppy. This is an attempt to keep the red light on.
//

typedef struct _DEFERRED_FLUSH_CONTEXT {

KDPC Dpc;
KTIMER Timer;
WORK_QUEUE_ITEM Item;

PFILE_OBJECT File;

} DEFERRED_FLUSH_CONTEXT;

typedef DEFERRED_FLUSH_CONTEXT *PDEFERRED_FLUSH_CONTEXT;

//
// This structure is used for the FatMarkVolumeClean callbacks.
//

typedef struct _CLEAN_AND_DIRTY_VOLUME_PACKET {

WORK_QUEUE_ITEM Item;
PIRP Irp;
PVCB Vcb;
PKEVENT Event;
} CLEAN_AND_DIRTY_VOLUME_PACKET, *PCLEAN_AND_DIRTY_VOLUME_PACKET;

//
// This structure is used when a page fault is running out of stack.
//

typedef struct _PAGING_FILE_OVERFLOW_PACKET {
PIRP Irp;
PFCB Fcb;
} PAGING_FILE_OVERFLOW_PACKET, *PPAGING_FILE_OVERFLOW_PACKET;

//
// This structure is used to access the EaFile.
//

#define EA_BCB_ARRAY_SIZE 8

typedef struct _EA_RANGE {

PCHAR Data;
ULONG StartingVbo;
ULONG Length;
USHORT BcbChainLength;
BOOLEAN AuxilaryBuffer;
PBCB *BcbChain;
PBCB BcbArray[EA_BCB_ARRAY_SIZE];

} EA_RANGE, *PEA_RANGE;

#define EA_RANGE_HEADER_SIZE (FIELD_OFFSET( EA_RANGE, BcbArray ))

//
// These symbols are used by the upcase/downcase routines.
//

#define WIDE_LATIN_CAPITAL_A (0xff21)
#define WIDE_LATIN_CAPITAL_Z (0xff3a)
#define WIDE_LATIN_SMALL_A (0xff41)
#define WIDE_LATIN_SMALL_Z (0xff5a)

//
// These values are returned by FatInterpretClusterType.
//

typedef enum _CLUSTER_TYPE {
FatClusterAvailable,
FatClusterReserved,
FatClusterBad,
FatClusterLast,
FatClusterNext
} CLUSTER_TYPE;


#endif // _FATSTRUC_



fat_headers\\lfn.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
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

Lfn.h

Abstract:

This module defines the on-disk structure of long file names on FAT.


--*/

#ifndef _LFN_
#define _LFN_

//
// This strucure defines the on disk format on long file name dirents.
//

typedef struct _PACKED_LFN_DIRENT {
UCHAR Ordinal; // offset = 0
UCHAR Name1[10]; // offset = 1 (Really 5 chars, but not WCHAR aligned)
UCHAR Attributes; // offset = 11
UCHAR Type; // offset = 12
UCHAR Checksum; // offset = 13
WCHAR Name2[6]; // offset = 14
USHORT MustBeZero; // offset = 26
WCHAR Name3[2]; // offset = 28
} PACKED_LFN_DIRENT; // sizeof = 32
typedef PACKED_LFN_DIRENT *PPACKED_LFN_DIRENT;

#define FAT_LAST_LONG_ENTRY 0x40 // Ordinal field
#define FAT_LONG_NAME_COMP 0x0 // Type field

//
// A packed lfn dirent is already quadword aligned so simply declare a
// lfn dirent as a packed lfn dirent.
//

typedef PACKED_LFN_DIRENT LFN_DIRENT;
typedef LFN_DIRENT *PLFN_DIRENT;

//
// This is the largest size buffer we would ever need to read an Lfn
//

#define MAX_LFN_CHARACTERS 260
#define MAX_LFN_DIRENTS 20

#define FAT_LFN_DIRENTS_NEEDED(NAME) (((NAME)->Length/sizeof(WCHAR) + 12)/13)

#endif // _LFN_


fat_headers\\nodetype.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
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
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

NodeType.h

Abstract:

This module defines all of the node type codes used in this development
shell. Every major data structure in the file system is assigned a node
type code that is. This code is the first CSHORT in the structure and is
followed by a CSHORT containing the size, in bytes, of the structure.


--*/

#ifndef _FATNODETYPE_
#define _FATNODETYPE_

typedef CSHORT NODE_TYPE_CODE;
typedef NODE_TYPE_CODE *PNODE_TYPE_CODE;

#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000)

#define FAT_NTC_DATA_HEADER ((NODE_TYPE_CODE)0x0500)
#define FAT_NTC_VCB ((NODE_TYPE_CODE)0x0501)
#define FAT_NTC_FCB ((NODE_TYPE_CODE)0x0502)
#define FAT_NTC_DCB ((NODE_TYPE_CODE)0x0503)
#define FAT_NTC_ROOT_DCB ((NODE_TYPE_CODE)0x0504)
#define FAT_NTC_CCB ((NODE_TYPE_CODE)0x0507)
#define FAT_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0508)

typedef CSHORT NODE_BYTE_SIZE;

#ifndef NodeType

//
// So all records start with
//
// typedef struct _RECORD_NAME {
// NODE_TYPE_CODE NodeTypeCode;
// NODE_BYTE_SIZE NodeByteSize;
// :
// } RECORD_NAME;
// typedef RECORD_NAME *PRECORD_NAME;
//

#define NodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr)))
#endif


//
// The following definitions are used to generate meaningful blue bugcheck
// screens. On a bugcheck the file system can output 4 ulongs of useful
// information. The first ulong will have encoded in it a source file id
// (in the high word) and the line number of the bugcheck (in the low word).
// The other values can be whatever the caller of the bugcheck routine deems
// necessary.
//
// Each individual file that calls bugcheck needs to have defined at the
// start of the file a constant called BugCheckFileId with one of the
// FAT_BUG_CHECK_ values defined below and then use FatBugCheck to bugcheck
// the system.
//


#define FAT_BUG_CHECK_ACCHKSUP (0x00010000)
#define FAT_BUG_CHECK_ALLOCSUP (0x00020000)
#define FAT_BUG_CHECK_CACHESUP (0x00030000)
#define FAT_BUG_CHECK_CLEANUP (0x00040000)
#define FAT_BUG_CHECK_CLOSE (0x00050000)
#define FAT_BUG_CHECK_CREATE (0x00060000)
#define FAT_BUG_CHECK_DEVCTRL (0x00070000)
#define FAT_BUG_CHECK_DEVIOSUP (0x00080000)
#define FAT_BUG_CHECK_DIRCTRL (0x00090000)
#define FAT_BUG_CHECK_DIRSUP (0x000a0000)
#define FAT_BUG_CHECK_DUMPSUP (0x000b0000)
#define FAT_BUG_CHECK_EA (0x000c0000)
#define FAT_BUG_CHECK_EASUP (0x000d0000)
#define FAT_BUG_CHECK_FATDATA (0x000e0000)
#define FAT_BUG_CHECK_FATINIT (0x000f0000)
#define FAT_BUG_CHECK_FILEINFO (0x00100000)
#define FAT_BUG_CHECK_FILOBSUP (0x00110000)
#define FAT_BUG_CHECK_FLUSH (0x00120000)
#define FAT_BUG_CHECK_FSCTRL (0x00130000)
#define FAT_BUG_CHECK_FSPDISP (0x00140000)
#define FAT_BUG_CHECK_LOCKCTRL (0x00150000)
#define FAT_BUG_CHECK_NAMESUP (0x00160000)
#define FAT_BUG_CHECK_PNP (0x00170000)
#define FAT_BUG_CHECK_READ (0x00180000)
#define FAT_BUG_CHECK_RESRCSUP (0x00190000)
#define FAT_BUG_CHECK_SHUTDOWN (0x001a0000)
#define FAT_BUG_CHECK_SPLAYSUP (0x001b0000)
#define FAT_BUG_CHECK_STRUCSUP (0x001c0000)
#define FAT_BUG_CHECK_TIMESUP (0x001d0000)
#define FAT_BUG_CHECK_VERFYSUP (0x001e0000)
#define FAT_BUG_CHECK_VOLINFO (0x001f0000)
#define FAT_BUG_CHECK_WORKQUE (0x00200000)
#define FAT_BUG_CHECK_WRITE (0x00210000)

#define FatBugCheck(A,B,C) { KeBugCheckEx(FAT_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); }


//
// In this module we'll also define some globally known constants
//

#define UCHAR_NUL 0x00
#define UCHAR_SOH 0x01
#define UCHAR_STX 0x02
#define UCHAR_ETX 0x03
#define UCHAR_EOT 0x04
#define UCHAR_ENQ 0x05
#define UCHAR_ACK 0x06
#define UCHAR_BEL 0x07
#define UCHAR_BS 0x08
#define UCHAR_HT 0x09
#define UCHAR_LF 0x0a
#define UCHAR_VT 0x0b
#define UCHAR_FF 0x0c
#define UCHAR_CR 0x0d
#define UCHAR_SO 0x0e
#define UCHAR_SI 0x0f
#define UCHAR_DLE 0x10
#define UCHAR_DC1 0x11
#define UCHAR_DC2 0x12
#define UCHAR_DC3 0x13
#define UCHAR_DC4 0x14
#define UCHAR_NAK 0x15
#define UCHAR_SYN 0x16
#define UCHAR_ETB 0x17
#define UCHAR_CAN 0x18
#define UCHAR_EM 0x19
#define UCHAR_SUB 0x1a
#define UCHAR_ESC 0x1b
#define UCHAR_FS 0x1c
#define UCHAR_GS 0x1d
#define UCHAR_RS 0x1e
#define UCHAR_US 0x1f
#define UCHAR_SP 0x20

#ifndef BUILDING_FSKDEXT

//
// These are the tags we use to uniquely identify pool allocations.
//

#define TAG_CCB 'CtaF'
#define TAG_FCB 'FtaF'
#define TAG_FCB_NONPAGED 'NtaF'
#define TAG_ERESOURCE 'EtaF'
#define TAG_IRP_CONTEXT 'ItaF'

#define TAG_BCB 'btaF'
#define TAG_DIRENT 'DtaF'
#define TAG_DIRENT_BITMAP 'TtaF'
#define TAG_EA_DATA 'dtaF'
#define TAG_EA_SET_HEADER 'etaF'
#define TAG_EVENT 'vtaF'
#define TAG_FAT_BITMAP 'BtaF'
#define TAG_FAT_CLOSE_CONTEXT 'xtaF'
#define TAG_FAT_IO_CONTEXT 'XtaF'
#define TAG_FAT_WINDOW 'WtaF'
#define TAG_FILENAME_BUFFER 'ntaF'
#define TAG_IO_RUNS 'itaF'
#define TAG_REPINNED_BCB 'RtaF'
#define TAG_STASHED_BPB 'StaF'
#define TAG_VCB_STATS 'VtaF'
#define TAG_DEFERRED_FLUSH_CONTEXT 'ftaF'

#define TAG_VPB 'vtaF'

#define TAG_VERIFY_BOOTSECTOR 'staF'
#define TAG_VERIFY_ROOTDIR 'rtaF'

#define TAG_OUTPUT_MAPPINGPAIRS 'PtaF'

#define TAG_ENTRY_LOOKUP_BUFFER 'LtaF'

#define TAG_IO_BUFFER 'OtaF'
#define TAG_IO_USER_BUFFER 'QtaF'

#define TAG_DYNAMIC_NAME_BUFFER 'ctaF'

#endif

#endif // _NODETYPE_



sfilter.h

之前有了,这里就不放了。