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
|
/*++
Copyright (c) 1989-2002 Microsoft Corporation
Module Name:
MiniSpy.c
Abstract:
This is the main module for the MiniSpy mini-filter.
Environment:
Kernel mode
--*/
#include "mspyKern.h"
#include <stdio.h>
//
// Global variables
//
MINISPY_DATA MiniSpyData;
NTSTATUS StatusToBreakOn = 0;
//---------------------------------------------------------------------------
// Function prototypes
//---------------------------------------------------------------------------
DRIVER_INITIALIZE DriverEntry;
NTSTATUS
DriverEntry (
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
);
NTSTATUS
SpyMessage (
_In_ PVOID ConnectionCookie,
_In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer,
_In_ ULONG InputBufferSize,
_Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer,
_In_ ULONG OutputBufferSize,
_Out_ PULONG ReturnOutputBufferLength
);
NTSTATUS
SpyConnect(
_In_ PFLT_PORT ClientPort,
_In_ PVOID ServerPortCookie,
_In_reads_bytes_(SizeOfContext) PVOID ConnectionContext,
_In_ ULONG SizeOfContext,
_Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie
);
VOID
SpyDisconnect(
_In_opt_ PVOID ConnectionCookie
);
NTSTATUS
SpyEnlistInTransaction (
_In_ PCFLT_RELATED_OBJECTS FltObjects
);
//---------------------------------------------------------------------------
// Assign text sections for each routine.
//---------------------------------------------------------------------------
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, SpyFilterUnload)
#pragma alloc_text(PAGE, SpyQueryTeardown)
#pragma alloc_text(PAGE, SpyConnect)
#pragma alloc_text(PAGE, SpyDisconnect)
#pragma alloc_text(PAGE, SpyMessage)
#endif
#define SetFlagInterlocked(_ptrFlags,_flagToSet) \
((VOID)InterlockedOr(((volatile LONG *)(_ptrFlags)),_flagToSet))
//---------------------------------------------------------------------------
// ROUTINES
//---------------------------------------------------------------------------
NTSTATUS
DriverEntry (
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called when a driver first loads. Its purpose is to
initialize global state and then register with FltMgr to start filtering.
Arguments:
DriverObject - Pointer to driver object created by the system to
represent this driver.
RegistryPath - Unicode string identifying where the parameters for this
driver are located in the registry.
Return Value:
Status of the operation.
--*/
{
PSECURITY_DESCRIPTOR sd;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING uniString;
NTSTATUS status = STATUS_SUCCESS;
try {
//
// Initialize global data structures.
//
MiniSpyData.LogSequenceNumber = 0;
MiniSpyData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;
MiniSpyData.RecordsAllocated = 0;
MiniSpyData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD;
MiniSpyData.DriverObject = DriverObject;
InitializeListHead( &MiniSpyData.OutputBufferList );
KeInitializeSpinLock( &MiniSpyData.OutputBufferLock );
ExInitializeNPagedLookasideList( &MiniSpyData.FreeBufferList,
NULL,
NULL,
POOL_NX_ALLOCATION,
RECORD_SIZE,
SPY_TAG,
0 );
#if MINISPY_VISTA
//
// Dynamically import FilterMgr APIs for transaction support
//
#pragma warning(push)
#pragma warning(disable:4055) // type cast from data pointer to function pointer
MiniSpyData.PFltSetTransactionContext = (PFLT_SET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltSetTransactionContext" );
MiniSpyData.PFltGetTransactionContext = (PFLT_GET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltGetTransactionContext" );
MiniSpyData.PFltEnlistInTransaction = (PFLT_ENLIST_IN_TRANSACTION) FltGetRoutineAddress( "FltEnlistInTransaction" );
#pragma warning(pop)
#endif
//
// Read the custom parameters for MiniSpy from the registry
//
SpyReadDriverParameters(RegistryPath);
//
// Now that our global configuration is complete, register with FltMgr.
//
status = FltRegisterFilter( DriverObject,
&FilterRegistration,
&MiniSpyData.Filter );
if (!NT_SUCCESS( status )) {
leave;
}
status = FltBuildDefaultSecurityDescriptor( &sd,
FLT_PORT_ALL_ACCESS );
if (!NT_SUCCESS( status )) {
leave;
}
RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME );
InitializeObjectAttributes( &oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd );
status = FltCreateCommunicationPort( MiniSpyData.Filter,
&MiniSpyData.ServerPort,
&oa,
NULL,
SpyConnect,
SpyDisconnect,
SpyMessage,
1 );
FltFreeSecurityDescriptor( sd );
if (!NT_SUCCESS( status )) {
leave;
}
//
// We are now ready to start filtering
//
status = FltStartFiltering( MiniSpyData.Filter );
} finally {
if (!NT_SUCCESS( status ) ) {
if (NULL != MiniSpyData.ServerPort) {
FltCloseCommunicationPort( MiniSpyData.ServerPort );
}
if (NULL != MiniSpyData.Filter) {
FltUnregisterFilter( MiniSpyData.Filter );
}
ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList );
}
}
return status;
}
NTSTATUS
SpyConnect(
_In_ PFLT_PORT ClientPort,
_In_ PVOID ServerPortCookie,
_In_reads_bytes_(SizeOfContext) PVOID ConnectionContext,
_In_ ULONG SizeOfContext,
_Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie
)
/*++
Routine Description
This is called when user-mode connects to the server
port - to establish a connection
Arguments
ClientPort - This is the pointer to the client port that
will be used to send messages from the filter.
ServerPortCookie - unused
ConnectionContext - unused
SizeofContext - unused
ConnectionCookie - unused
Return Value
STATUS_SUCCESS - to accept the connection
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( ServerPortCookie );
UNREFERENCED_PARAMETER( ConnectionContext );
UNREFERENCED_PARAMETER( SizeOfContext);
UNREFERENCED_PARAMETER( ConnectionCookie );
FLT_ASSERT( MiniSpyData.ClientPort == NULL );
MiniSpyData.ClientPort = ClientPort;
return STATUS_SUCCESS;
}
VOID
SpyDisconnect(
_In_opt_ PVOID ConnectionCookie
)
/*++
Routine Description
This is called when the connection is torn-down. We use it to close our handle to the connection
Arguments
ConnectionCookie - unused
Return value
None
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( ConnectionCookie );
//
// Close our handle
//
FltCloseClientPort( MiniSpyData.Filter, &MiniSpyData.ClientPort );
}
NTSTATUS
SpyFilterUnload (
_In_ FLT_FILTER_UNLOAD_FLAGS Flags
)
/*++
Routine Description:
This is called when a request has been made to unload the filter. Unload
requests from the Operation System (ex: "sc stop minispy" can not be
failed. Other unload requests may be failed.
You can disallow OS unload request by setting the
FLTREGFL_DO_NOT_SUPPORT_SERVICE_STOP flag in the FLT_REGISTARTION
structure.
Arguments:
Flags - Flags pertinent to this operation
Return Value:
Always success
--*/
{
UNREFERENCED_PARAMETER( Flags );
PAGED_CODE();
//
// Close the server port. This will stop new connections.
//
FltCloseCommunicationPort( MiniSpyData.ServerPort );
FltUnregisterFilter( MiniSpyData.Filter );
SpyEmptyOutputBufferList();
ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList );
return STATUS_SUCCESS;
}
NTSTATUS
SpyQueryTeardown (
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
)
/*++
Routine Description:
This allows our filter to be manually detached from a volume.
Arguments:
FltObjects - Contains pointer to relevant objects for this operation.
Note that the FileObject field will always be NULL.
Flags - Flags pertinent to this operation
Return Value:
--*/
{
UNREFERENCED_PARAMETER( FltObjects );
UNREFERENCED_PARAMETER( Flags );
PAGED_CODE();
return STATUS_SUCCESS;
}
NTSTATUS
SpyMessage (
_In_ PVOID ConnectionCookie,
_In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer,
_In_ ULONG InputBufferSize,
_Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer,
_In_ ULONG OutputBufferSize,
_Out_ PULONG ReturnOutputBufferLength
)
/*++
Routine Description:
This is called whenever a user mode application wishes to communicate
with this minifilter.
Arguments:
ConnectionCookie - unused
OperationCode - An identifier describing what type of message this
is. These codes are defined by the MiniFilter.
InputBuffer - A buffer containing input data, can be NULL if there
is no input data.
InputBufferSize - The size in bytes of the InputBuffer.
OutputBuffer - A buffer provided by the application that originated
the communication in which to store data to be returned to this
application.
OutputBufferSize - The size in bytes of the OutputBuffer.
ReturnOutputBufferSize - The size in bytes of meaningful data
returned in the OutputBuffer.
Return Value:
Returns the status of processing the message.
--*/
{
MINISPY_COMMAND command;
NTSTATUS status;
PAGED_CODE();
UNREFERENCED_PARAMETER( ConnectionCookie );
//
// **** PLEASE READ ****
//
// The INPUT and OUTPUT buffers are raw user mode addresses. The filter
// manager has already done a ProbedForRead (on InputBuffer) and
// ProbedForWrite (on OutputBuffer) which guarentees they are valid
// addresses based on the access (user mode vs. kernel mode). The
// minifilter does not need to do their own probe.
//
// The filter manager is NOT doing any alignment checking on the pointers.
// The minifilter must do this themselves if they care (see below).
//
// The minifilter MUST continue to use a try/except around any access to
// these buffers.
//
if ((InputBuffer != NULL) &&
(InputBufferSize >= (FIELD_OFFSET(COMMAND_MESSAGE,Command) +
sizeof(MINISPY_COMMAND)))) {
try {
//
// Probe and capture input message: the message is raw user mode
// buffer, so need to protect with exception handler
//
command = ((PCOMMAND_MESSAGE) InputBuffer)->Command;
} except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) {
return GetExceptionCode();
}
switch (command) {
case GetMiniSpyLog:
//
// Return as many log records as can fit into the OutputBuffer
//
if ((OutputBuffer == NULL) || (OutputBufferSize == 0)) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// We want to validate that the given buffer is POINTER
// aligned. But if this is a 64bit system and we want to
// support 32bit applications we need to be careful with how
// we do the check. Note that the way SpyGetLog is written
// it actually does not care about alignment but we are
// demonstrating how to do this type of check.
//
#if defined(_WIN64)
if (IoIs32bitProcess( NULL )) {
//
// Validate alignment for the 32bit process on a 64bit
// system
//
if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) {
status = STATUS_DATATYPE_MISALIGNMENT;
break;
}
} else {
#endif
if (!IS_ALIGNED(OutputBuffer,sizeof(PVOID))) {
status = STATUS_DATATYPE_MISALIGNMENT;
break;
}
#if defined(_WIN64)
}
#endif
//
// Get the log record.
//
status = SpyGetLog( OutputBuffer,
OutputBufferSize,
ReturnOutputBufferLength );
break;
case GetMiniSpyVersion:
//
// Return version of the MiniSpy filter driver. Verify
// we have a valid user buffer including valid
// alignment
//
if ((OutputBufferSize < sizeof( MINISPYVER )) ||
(OutputBuffer == NULL)) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Validate Buffer alignment. If a minifilter cares about
// the alignment value of the buffer pointer they must do
// this check themselves. Note that a try/except will not
// capture alignment faults.
//
if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) {
status = STATUS_DATATYPE_MISALIGNMENT;
break;
}
//
// Protect access to raw user-mode output buffer with an
// exception handler
//
try {
((PMINISPYVER)OutputBuffer)->Major = MINISPY_MAJ_VERSION;
((PMINISPYVER)OutputBuffer)->Minor = MINISPY_MIN_VERSION;
} except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) {
return GetExceptionCode();
}
*ReturnOutputBufferLength = sizeof( MINISPYVER );
status = STATUS_SUCCESS;
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
return status;
}
//---------------------------------------------------------------------------
// Operation filtering routines
//---------------------------------------------------------------------------
FLT_PREOP_CALLBACK_STATUS
#pragma warning(suppress: 6262) // higher than usual stack usage is considered safe in this case
SpyPreOperationCallback (
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID *CompletionContext
)
/*++
Routine Description:
This routine receives ALL pre-operation callbacks for this filter. It then
tries to log information about the given operation. If we are able
to log information then we will call our post-operation callback routine.
NOTE: This routine must be NON-PAGED because it can be called on the
paging path.
Arguments:
Data - Contains information about the given operation.
FltObjects - Contains pointers to the various objects that are pertinent
to this operation.
CompletionContext - This receives the address of our log buffer for this
operation. Our completion routine then receives this buffer address.
Return Value:
Identifies how processing should continue for this operation
--*/
{
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine
PRECORD_LIST recordList;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
UNICODE_STRING defaultName;
PUNICODE_STRING nameToUse;
NTSTATUS status;
#if MINISPY_VISTA
PUNICODE_STRING ecpDataToUse = NULL;
UNICODE_STRING ecpData;
WCHAR ecpDataBuffer[MAX_NAME_SPACE/sizeof(WCHAR)];
#endif
#if MINISPY_NOT_W2K
WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)];
#endif
//
// Try and get a log record
//
recordList = SpyNewRecord();
if (recordList) {
//
// We got a log record, if there is a file object, get its name.
//
// NOTE: By default, we use the query method
// FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP
// because MiniSpy would like to get the name as much as possible, but
// can cope if we can't retrieve a name. For a debugging type filter,
// like Minispy, this is reasonable, but for most production filters
// who need names reliably, they should query the name at times when it
// is known to be safe and use the query method
// FLT_FILE_NAME_QUERY_DEFAULT.
//
if (FltObjects->FileObject != NULL) {
status = FltGetFileNameInformation( Data,
FLT_FILE_NAME_NORMALIZED |
MiniSpyData.NameQueryMethod,
&nameInfo );
} else {
//
// Can't get a name when there's no file object
//
status = STATUS_UNSUCCESSFUL;
}
//
// Use the name if we got it else use a default name
//
if (NT_SUCCESS( status )) {
nameToUse = &nameInfo->Name;
//
// Parse the name if requested
//
if (FlagOn( MiniSpyData.DebugFlags, SPY_DEBUG_PARSE_NAMES )) {
#ifdef DBG
FLT_ASSERT( NT_SUCCESS( FltParseFileNameInformation( nameInfo ) ) );
#else
FltParseFileNameInformation( nameInfo );
#endif
}
} else {
#if MINISPY_NOT_W2K
NTSTATUS lstatus;
PFLT_FILE_NAME_INFORMATION lnameInfo;
//
// If we couldn't get the "normalized" name try and get the
// "opened" name
//
if (FltObjects->FileObject != NULL) {
//
// Get the opened name
//
lstatus = FltGetFileNameInformation( Data,
FLT_FILE_NAME_OPENED |
FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP,
&lnameInfo );
if (NT_SUCCESS(lstatus)) {
#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
(VOID)_snwprintf( name,
sizeof(name)/sizeof(WCHAR),
L"<%08x> %wZ",
status,
&lnameInfo->Name );
FltReleaseFileNameInformation( lnameInfo );
} else {
//
// If that failed report both NORMALIZED status and
// OPENED status
//
#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
(VOID)_snwprintf( name,
sizeof(name)/sizeof(WCHAR),
L"<NO NAME: NormalizeStatus=%08x OpenedStatus=%08x>",
status,
lstatus );
}
} else {
#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
(VOID)_snwprintf( name,
sizeof(name)/sizeof(WCHAR),
L"<NO NAME>" );
}
//
// Name was initialized by _snwprintf() so it may not be null terminated
// if the buffer is insufficient. We will ignore this error and truncate
// the file name.
//
name[(sizeof(name)/sizeof(WCHAR))-1] = L'\0';
RtlInitUnicodeString( &defaultName, name );
nameToUse = &defaultName;
#else
//
// We were unable to get the String safe routine to work on W2K
// Do it the old safe way
//
RtlInitUnicodeString( &defaultName, L"<NO NAME>" );
nameToUse = &defaultName;
#endif //MINISPY_NOT_W2K
#if DBG
//
// Debug support to break on certain errors.
//
if (FltObjects->FileObject != NULL) {
NTSTATUS retryStatus;
if ((StatusToBreakOn != 0) && (status == StatusToBreakOn)) {
DbgBreakPoint();
}
retryStatus = FltGetFileNameInformation( Data,
FLT_FILE_NAME_NORMALIZED |
MiniSpyData.NameQueryMethod,
&nameInfo );
if (!NT_SUCCESS( retryStatus )) {
//
// We always release nameInfo, so ignore return value.
//
NOTHING;
}
}
#endif
}
#if MINISPY_VISTA
//
// Look for ECPs, but only if it's a create operation
//
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
//
// Initialize an empty string to receive an ECP data dump
//
RtlInitEmptyUnicodeString( &ecpData,
ecpDataBuffer,
MAX_NAME_SPACE/sizeof(WCHAR) );
//
// Parse any extra create parameters
//
SpyParseEcps( Data, recordList, &ecpData );
ecpDataToUse = &ecpData;
}
//
// Store the name and ECP data (if any)
//
SpySetRecordNameAndEcpData( &(recordList->LogRecord), nameToUse, ecpDataToUse );
#else
//
// Store the name
//
SpySetRecordName( &(recordList->LogRecord), nameToUse );
#endif
//
// Release the name information structure (if defined)
//
if (NULL != nameInfo) {
FltReleaseFileNameInformation( nameInfo );
}
//
// Set all of the operation information into the record
//
SpyLogPreOperationData( Data, FltObjects, recordList );
//
// Pass the record to our completions routine and return that
// we want our completion routine called.
//
if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) {
//
// Since completion callbacks are not supported for
// this operation, do the completion processing now
//
SpyPostOperationCallback( Data,
FltObjects,
recordList,
0 );
returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
} else {
*CompletionContext = recordList;
returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
}
return returnStatus;
}
FLT_POSTOP_CALLBACK_STATUS
SpyPostOperationCallback (
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
/*++
Routine Description:
This routine receives ALL post-operation callbacks. This will take
the log record passed in the context parameter and update it with
the completion information. It will then insert it on a list to be
sent to the usermode component.
NOTE: This routine must be NON-PAGED because it can be called at DPC level
Arguments:
Data - Contains information about the given operation.
FltObjects - Contains pointers to the various objects that are pertinent
to this operation.
CompletionContext - Pointer to the RECORD_LIST structure in which we
store the information we are logging. This was passed from the
pre-operation callback
Flags - Contains information as to why this routine was called.
Return Value:
Identifies how processing should continue for this operation
--*/
{
PRECORD_LIST recordList;
PRECORD_LIST reparseRecordList = NULL;
PLOG_RECORD reparseLogRecord;
PFLT_TAG_DATA_BUFFER tagData;
ULONG copyLength;
UNREFERENCED_PARAMETER( FltObjects );
recordList = (PRECORD_LIST)CompletionContext;
//
// If our instance is in the process of being torn down don't bother to
// log this record, free it now.
//
if (FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING)) {
SpyFreeRecord( recordList );
return FLT_POSTOP_FINISHED_PROCESSING;
}
//
// Set completion information into the record
//
SpyLogPostOperationData( Data, recordList );
//
// Log reparse tag information if specified.
//
tagData = Data->TagData;
if (tagData) {
reparseRecordList = SpyNewRecord();
if (reparseRecordList) {
//
// only copy the DATA portion of the information
//
RtlCopyMemory( &reparseRecordList->LogRecord.Data,
&recordList->LogRecord.Data,
sizeof(RECORD_DATA) );
reparseLogRecord = &reparseRecordList->LogRecord;
copyLength = FLT_TAG_DATA_BUFFER_HEADER_SIZE + tagData->TagDataLength;
if(copyLength > MAX_NAME_SPACE) {
copyLength = MAX_NAME_SPACE;
}
//
// Copy reparse data
//
RtlCopyMemory(
&reparseRecordList->LogRecord.Name[0],
tagData,
copyLength
);
reparseLogRecord->RecordType |= RECORD_TYPE_FILETAG;
reparseLogRecord->Length += (ULONG) ROUND_TO_SIZE( copyLength, sizeof( PVOID ) );
}
}
//
// Send the logged information to the user service.
//
SpyLog( recordList );
if (reparseRecordList) {
SpyLog( reparseRecordList );
}
//
// For creates within a transaction enlist in the transaction
// if we haven't already done.
//
if ((FltObjects->Transaction != NULL) &&
(Data->Iopb->MajorFunction == IRP_MJ_CREATE) &&
(Data->IoStatus.Status == STATUS_SUCCESS)) {
//
// Enlist in the transaction.
//
SpyEnlistInTransaction( FltObjects );
}
return FLT_POSTOP_FINISHED_PROCESSING;
}
NTSTATUS
SpyEnlistInTransaction (
_In_ PCFLT_RELATED_OBJECTS FltObjects
)
/*++
Routine Description
Minispy calls this function to enlist in a transaction of interest.
Arguments
FltObjects - Contains parameters required to enlist in a transaction.
Return value
Returns STATUS_SUCCESS if we were able to successfully enlist in a new transcation or if we
were already enlisted in the transaction. Returns an appropriate error code on a failure.
--*/
{
#if MINISPY_VISTA
PMINISPY_TRANSACTION_CONTEXT transactionContext = NULL;
PMINISPY_TRANSACTION_CONTEXT oldTransactionContext = NULL;
PRECORD_LIST recordList;
NTSTATUS status;
static ULONG Sequence=1;
//
// This code is only built in the Vista environment, but
// we need to ensure this binary still runs down-level. Return
// at this point if the transaction dynamic imports were not found.
//
// If we find FltGetTransactionContext, we assume the other
// transaction APIs are also present.
//
if (NULL == MiniSpyData.PFltGetTransactionContext) {
return STATUS_SUCCESS;
}
//
// Try to get our context for this transaction. If we get
// one we have already enlisted in this transaction.
//
status = (*MiniSpyData.PFltGetTransactionContext)( FltObjects->Instance,
FltObjects->Transaction,
&transactionContext );
if (NT_SUCCESS( status )) {
//
// Check if we have already enlisted in the transaction.
//
if (FlagOn(transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) {
//
// FltGetTransactionContext puts a reference on the context. Release
// that now and return success.
//
FltReleaseContext( transactionContext );
return STATUS_SUCCESS;
}
//
// If we have not enlisted then we need to try and enlist in the transaction.
//
goto ENLIST_IN_TRANSACTION;
}
//
// If the context does not exist create a new one, else return the error
// status to the caller.
//
if (status != STATUS_NOT_FOUND) {
return status;
}
//
// Allocate a transaction context.
//
status = FltAllocateContext( FltObjects->Filter,
FLT_TRANSACTION_CONTEXT,
sizeof(MINISPY_TRANSACTION_CONTEXT),
PagedPool,
&transactionContext );
if (!NT_SUCCESS( status )) {
return status;
}
//
// Set the context into the transaction
//
RtlZeroMemory(transactionContext, sizeof(MINISPY_TRANSACTION_CONTEXT));
transactionContext->Count = Sequence++;
FLT_ASSERT( MiniSpyData.PFltSetTransactionContext );
status = (*MiniSpyData.PFltSetTransactionContext)( FltObjects->Instance,
FltObjects->Transaction,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
transactionContext,
&oldTransactionContext );
if (!NT_SUCCESS( status )) {
FltReleaseContext( transactionContext ); //this will free the context
if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) {
return status;
}
FLT_ASSERT(oldTransactionContext != NULL);
if (FlagOn(oldTransactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) {
//
// If this context is already enlisted then release the reference
// which FltSetTransactionContext put on it and return success.
//
FltReleaseContext( oldTransactionContext );
return STATUS_SUCCESS;
}
//
// If we found an existing transaction then we should try and
// enlist in it. There is a race here in which the thread
// which actually set the transaction context may fail to
// enlist in the transaction and delete it later. It might so
// happen that we picked up a reference to that context here
// and successfully enlisted in that transaction. For now
// we have chosen to ignore this scenario.
//
//
// If we are not enlisted then assign the right transactionContext
// and attempt enlistment.
//
transactionContext = oldTransactionContext;
}
ENLIST_IN_TRANSACTION:
//
// Enlist on this transaction for notifications.
//
FLT_ASSERT( MiniSpyData.PFltEnlistInTransaction );
status = (*MiniSpyData.PFltEnlistInTransaction)( FltObjects->Instance,
FltObjects->Transaction,
transactionContext,
FLT_MAX_TRANSACTION_NOTIFICATIONS );
//
// If the enlistment failed we might have to delete the context and remove
// our count.
//
if (!NT_SUCCESS( status )) {
//
// If the error is that we are already enlisted then we do not need
// to delete the context. Otherwise we have to delete the context
// before releasing our reference.
//
if (status == STATUS_FLT_ALREADY_ENLISTED) {
status = STATUS_SUCCESS;
} else {
//
// It is worth noting that only the first caller of
// FltDeleteContext will remove the reference added by
// filter manager when the context was set.
//
FltDeleteContext( transactionContext );
}
FltReleaseContext( transactionContext );
return status;
}
//
// Set the flag so that future enlistment efforts know that we
// successfully enlisted in the transaction.
//
SetFlagInterlocked( &transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION );
//
// The operation succeeded, remove our count
//
FltReleaseContext( transactionContext );
//
// Log a record that a new transaction has started.
//
recordList = SpyNewRecord();
if (recordList) {
SpyLogTransactionNotify( FltObjects, recordList, 0 );
//
// Send the logged information to the user service.
//
SpyLog( recordList );
}
#endif // MINISPY_VISTA
return STATUS_SUCCESS;
}
#if MINISPY_VISTA
NTSTATUS
SpyKtmNotificationCallback (
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PFLT_CONTEXT TransactionContext,
_In_ ULONG TransactionNotification
)
{
PRECORD_LIST recordList;
UNREFERENCED_PARAMETER( TransactionContext );
//
// Try and get a log record
//
recordList = SpyNewRecord();
if (recordList) {
SpyLogTransactionNotify( FltObjects, recordList, TransactionNotification );
//
// Send the logged information to the user service.
//
SpyLog( recordList );
}
return STATUS_SUCCESS;
}
#endif // MINISPY_VISTA
VOID
SpyDeleteTxfContext (
_Inout_ PMINISPY_TRANSACTION_CONTEXT Context,
_In_ FLT_CONTEXT_TYPE ContextType
)
{
UNREFERENCED_PARAMETER( Context );
UNREFERENCED_PARAMETER( ContextType );
FLT_ASSERT(FLT_TRANSACTION_CONTEXT == ContextType);
FLT_ASSERT(Context->Count != 0);
}
LONG
SpyExceptionFilter (
_In_ PEXCEPTION_POINTERS ExceptionPointer,
_In_ BOOLEAN AccessingUserBuffer
)
/*++
Routine Description:
Exception filter to catch errors touching user buffers.
Arguments:
ExceptionPointer - The exception record.
AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow
the caller to munge the error to a desired status.
Return Value:
EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run.
EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of
this exception.
--*/
{
NTSTATUS Status;
Status = ExceptionPointer->ExceptionRecord->ExceptionCode;
//
// Certain exceptions shouldn't be dismissed within the namechanger filter
// unless we're touching user memory.
//
if (!FsRtlIsNtstatusExpected( Status ) &&
!AccessingUserBuffer) {
return EXCEPTION_CONTINUE_SEARCH;
}
return EXCEPTION_EXECUTE_HANDLER;
}
|