WDK Mini Filter Example
userscan.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 2011 Microsoft Corporation
4 
5 Module Name:
6 
7  userscan.c
8 
9 Abstract:
10 
11  The implementation of user space scanning module. You have to install filter driver first, and
12  have filter manager load the minifilter. When filter driver is in its position, after calling
13  UserScanInit(...) all the subsequent CreateFile or CloseHandle would trigger the data scan if
14  the file is dirty.
15 
16  Before the user space scanner exit, it must call UserScanFinalize(...) to cleanup the data structure
17  and close the listening threads.
18 
19 Environment:
20 
21  User mode
22 
23 --*/
24 
25 #include <stdio.h>
26 #include <assert.h>
27 #include "userscan.h"
28 #include "utility.h"
29 
30 #define USER_SCAN_THREAD_COUNT 6 // the number of scanning worker threads.
31 
32 typedef struct _SCANNER_MESSAGE {
33 
34  //
35  // Required structure header.
36  //
37 
38  FILTER_MESSAGE_HEADER MessageHeader;
39 
40  //
41  // Private scanner-specific fields begin here.
42  //
43 
45 
46  //
47  // Overlapped structure: this is not really part of the message
48  // However we embed it here so that when we get pOvlp in
49  // GetQueuedCompletionStatus(...), we can restore the message
50  // via CONTAINING_RECORD macro.
51  //
52 
53  OVERLAPPED Ovlp;
54 
56 
57 #define SCANNER_MESSAGE_SIZE (sizeof(FILTER_MESSAGE_HEADER) + sizeof(AV_SCANNER_NOTIFICATION))
58 
59 typedef struct _SCANNER_REPLY_MESSAGE {
60 
61  //
62  // Required structure header.
63  //
64 
65  FILTER_REPLY_HEADER ReplyHeader;
66 
67  //
68  // Private scanner-specific fields begin here.
69  //
70 
71  ULONG ThreadId;
72 
74 
75 #define SCANNER_REPLY_MESSAGE_SIZE (sizeof(FILTER_REPLY_HEADER) + sizeof(ULONG))
76 
77 //
78 // Local routines
79 //
80 
83  _In_reads_bytes_(Size) PUCHAR StartingAddress,
84  _In_ SIZE_T Size,
85  _Inout_ PBOOLEAN pAbort
86  );
87 
88 HRESULT
90  _In_ PUSER_SCAN_CONTEXT Context,
91  _In_ PSCANNER_MESSAGE Message,
92  _In_ PSCANNER_THREAD_CONTEXT ThreadCtx
93  );
94 
95 HRESULT
97  _Inout_ PUSER_SCAN_CONTEXT Context
98  );
99 
100 HRESULT
102  _Inout_ PUSER_SCAN_CONTEXT Context
103  );
104 
105 DWORD
106 WaitForAll (
107  _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes
108  );
109 
110 HRESULT
112  _In_ DWORD ThreadId,
113  _In_ PUSER_SCAN_CONTEXT Context,
114  _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx
115  );
116 
117 VOID
119  _In_ PUSER_SCAN_CONTEXT Context
120  );
121 
122 HRESULT
124  _In_ PUSER_SCAN_CONTEXT Context
125  );
126 
127 HRESULT
129  _In_ PUSER_SCAN_CONTEXT Context
130  );
131 
132 //
133 // Implementation of exported routines.
134 // Declared in userscan.h
135 //
136 
137 HRESULT
139  _Inout_ PUSER_SCAN_CONTEXT Context
140  )
141 /*++
142 
143 Routine Description:
144 
145  This routine initializes all the necessary data structures and forks listening threads.
146  The caller thread is responsible for calling UserScanFinalize(...) to cleanup the
147  data structures and close the listening threads.
148 
149 Arguments:
150 
151  Context - User scan context, please see userscan.h
152 
153 Return Value:
154 
155  S_OK if successful. Otherwise, it returns a HRESULT error value.
156 
157 --*/
158 {
159  HRESULT hr = S_OK;
160  ULONG i = 0;
161  HANDLE hEvent = NULL;
162  PSCANNER_THREAD_CONTEXT scanThreadCtxes = NULL;
163  HANDLE hListenAbort = NULL;
164  AV_CONNECTION_CONTEXT connectionCtx = {0};
165 
166  if (NULL == Context) {
167 
168  return MAKE_HRESULT(SEVERITY_ERROR, 0, E_POINTER);
169  }
170 
171  //
172  // Create the abort listening thead.
173  // This thread is particularly listening the abortion event.
174  //
175 
176  hListenAbort = CreateThread( NULL,
177  0,
178  (LPTHREAD_START_ROUTINE)UserScanListenAbortProc,
179  Context,
180  CREATE_SUSPENDED,
181  NULL );
182 
183  if (NULL == hListenAbort) {
184 
185  hr = HRESULT_FROM_WIN32(GetLastError());
186  goto Cleanup;
187  }
188 
189  //
190  // Initialize scan thread contexts.
191  //
192 
193  scanThreadCtxes = HeapAlloc(GetProcessHeap(), 0, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT);
194  if (NULL == scanThreadCtxes) {
195 
196  hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY);
197  goto Cleanup;
198  }
199 
200  ZeroMemory(scanThreadCtxes, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT);
201 
202  //
203  // Create scan listening threads.
204  //
205 
206  for (i = 0;
208  i ++ ) {
209 
210  scanThreadCtxes[i].Handle = CreateThread( NULL,
211  0,
212  (LPTHREAD_START_ROUTINE)UserScanWorker,
213  Context,
214  CREATE_SUSPENDED,
215  &scanThreadCtxes[i].ThreadId );
216 
217  if (NULL == scanThreadCtxes[i].Handle) {
218  hr = HRESULT_FROM_WIN32(GetLastError());
219  goto Cleanup;
220  }
221  InitializeCriticalSection(&(scanThreadCtxes[i].Lock));
222  }
223 
224  //
225  // Prepare the scan communication port.
226  //
227 
228  connectionCtx.Type = AvConnectForScan;
229  hr = FilterConnectCommunicationPort( AV_SCAN_PORT_NAME,
230  0,
231  &connectionCtx,
232  sizeof(AV_CONNECTION_CONTEXT),
233  NULL,
234  &Context->ConnectionPort );
235  if (FAILED(hr)) {
236 
237  Context->ConnectionPort = NULL;
238  goto Cleanup;
239  }
240 
241  //
242  // Create the IO completion port for asynchronous message passing.
243  //
244 
245  Context->Completion = CreateIoCompletionPort( Context->ConnectionPort,
246  NULL,
247  0,
248  USER_SCAN_THREAD_COUNT );
249 
250  if ( NULL == Context->Completion ) {
251  hr = HRESULT_FROM_WIN32(GetLastError());
252  goto Cleanup;
253  }
254 
255  Context->ScanThreadCtxes = scanThreadCtxes;
256  Context->AbortThreadHandle = hListenAbort;
257 
258  //
259  // Resume all the scanning threads.
260  //
261 
262  for (i = 0;
264  i ++ ) {
265  if ( ResumeThread( scanThreadCtxes[i].Handle ) == -1) {
266 
267  fprintf(stderr, "[UserScanInit]: ResumeThread scan listening thread failed.\n");
268  hr = HRESULT_FROM_WIN32(GetLastError());
269  goto Cleanup;
270  }
271  }
272 
273  //
274  // Resume abort listening thread.
275  //
276 
277  if ( ResumeThread( hListenAbort ) == -1 ) {
278  fprintf(stderr, "[UserScanInit]: ResumeThread abort listening thread failed.\n");
279  hr = HRESULT_FROM_WIN32(GetLastError());
280  goto Cleanup;
281  }
282 
283  //
284  // Pump messages into queue of completion port.
285  //
286 
287  for (i = 0;
289  i ++ ) {
290 
291  PSCANNER_MESSAGE msg = HeapAlloc( GetProcessHeap(), 0, sizeof( SCANNER_MESSAGE ) );
292 
293  if (NULL == msg) {
294 
295  hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY);
296  goto Cleanup;
297  }
298 
299  FillMemory( &msg->Ovlp, sizeof(OVERLAPPED), 0);
300  hr = FilterGetMessage( Context->ConnectionPort,
301  &msg->MessageHeader,
302  FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ),
303  &msg->Ovlp );
304 
305  if (hr == HRESULT_FROM_WIN32( ERROR_IO_PENDING )) {
306 
307  hr = S_OK;
308 
309  } else {
310 
311  fprintf(stderr, "[UserScanInit]: FilterGetMessage failed.\n");
312  DisplayError(hr);
313  HeapFree(GetProcessHeap(), 0, msg );
314  goto Cleanup;
315  }
316  }
317 
318  return hr;
319 
320 Cleanup:
321 
322  if (Context->Completion && !CloseHandle(Context->Completion)) {
323 
324  fprintf(stderr, "[UserScanInit] Error! Close completion port failed.\n");
325  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
326  }
327  if (Context->ConnectionPort && !CloseHandle(Context->ConnectionPort)) {
328 
329  fprintf(stderr, "[UserScanInit] Error! Close connection port failed.\n");
330  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
331  }
332  if (scanThreadCtxes) {
333 
334  for (i = 0;
336  i ++ ) {
337 
338  if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) {
339 
340  fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n");
341  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
342  }
343  DeleteCriticalSection(&(scanThreadCtxes[i].Lock));
344  }
345  HeapFree(GetProcessHeap(), 0, scanThreadCtxes);
346  }
347  if (hListenAbort && !CloseHandle(hListenAbort)) {
348 
349  fprintf(stderr, "[UserScanInit] Error! Close listen abort thread failed.\n");
350  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
351  }
352  if (hEvent && !CloseHandle(hEvent)) {
353 
354  fprintf(stderr, "[UserScanInit] Error! Close event handle failed.\n");
355  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
356  }
357 
358  return hr;
359 }
360 
361 HRESULT
363  _In_ PUSER_SCAN_CONTEXT Context
364  )
365 /*++
366 
367 Routine Description:
368 
369  This routine cleans up all the necessary data structures and closes listening threads.
370  It does the following things:
371  1) Cancel all the scanning threads and wait for them to terminate.
372  2) Close all the thread handles
373  3) Close all the port handles
374  4) Free memory of scan thread contexts.
375 
376 Arguments:
377 
378  Context - User scan context, please see userscan.h
379 
380 Return Value:
381 
382  S_OK if successful. Otherwise, it returns a HRESULT error value.
383 
384 --*/
385 {
386  HRESULT hr = S_OK;
387  printf("=================finalize\n");
388 
389  UserScanSynchronizedCancel( Context );
390 
391  printf("[UserScanFinalize]: Closing connection port\n");
392 
393  hr = UserScanCleanup( Context );
394 
395  return hr;
396 }
397 
398 
399 //
400 // Implementation of local routines
401 //
402 
403 DWORD
405  _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes
406  )
407 /*++
408 
409 Routine Description:
410 
411  A local helper function that enable the caller to wair for all the scan threads.
412 
413 Arguments:
414 
415  ScanThreadCtxes - Scan thread contextes.
416 
417 Return Value:
418 
419  Please consult WaitForMultipleObjects(...)
420 
421 --*/
422 {
423  ULONG i = 0;
424  HANDLE hScanThreads[USER_SCAN_THREAD_COUNT] = {0};
425  for (i = 0;
427  i ++ ) {
428  hScanThreads[i] = ScanThreadCtxes[i].Handle;
429  }
430  return WaitForMultipleObjects(USER_SCAN_THREAD_COUNT, hScanThreads, TRUE, INFINITE);
431 }
432 
433 HRESULT
435  _In_ DWORD ThreadId,
436  _In_ PUSER_SCAN_CONTEXT Context,
437  _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx
438  )
439 /*++
440 
441 Routine Description:
442 
443  This routine search for the scan thread context by its thread id.
444 
445 Arguments:
446 
447  ThreadId - The thread id to be searched.
448 
449  Context - The user scan context.
450 
451  ScanThreadCtx - Output scan thread context.
452 
453 Return Value:
454 
455  S_OK if found, otherwise not found.
456 
457 --*/
458 {
459  HRESULT hr = S_OK;
460  ULONG i;
461  PSCANNER_THREAD_CONTEXT scanThreadCtx = Context->ScanThreadCtxes;
462 
463  *ScanThreadCtx = NULL;
464 
465  for (i = 0;
467  i ++ ) {
468 
469  if ( ThreadId == scanThreadCtx[i].ThreadId ) {
470  *ScanThreadCtx = (scanThreadCtx + i);
471  return hr;
472  }
473  }
474  return MAKE_HRESULT(SEVERITY_ERROR,0,E_FAIL);
475 }
476 
477 VOID
479  _In_ PUSER_SCAN_CONTEXT Context
480  )
481 /*++
482 
483 Routine Description:
484 
485  This routine tries to abort all the scanning threads and wait for them to terminate.
486 
487 Arguments:
488 
489  Context - User scan context, please see userscan.h
490 
491 Return Value:
492 
493  Please consult WaitForMultipleObjects(...)
494 
495 --*/
496 {
497  ULONG i;
498  PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes;
499 
500  if (NULL == scanThreadCtxes) {
501  fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n");
502  return;
503  }
504 
505  //
506  // Tell all scanning threads that the program is going to exit.
507  //
508 
509  Context->Finalized = TRUE;
510 
511  //
512  // Signal cancellation events for all scanning threads.
513  //
514 
515  for (i = 0;
517  i ++ ) {
518 
519  scanThreadCtxes[i].Aborted = TRUE;
520  }
521 
522  //
523  // Wake up the listening thread if it is waiting for message
524  // via GetQueuedCompletionStatus()
525  //
526 
527  CancelIoEx(Context->ConnectionPort, NULL);
528 
529  //
530  // Wait for all scan threads to complete cancellation,
531  // so we will be able to close the connection port and etc.
532  //
533 
534  WaitForAll(scanThreadCtxes);
535 
536  return;
537 }
538 
539 HRESULT
541  _In_ PUSER_SCAN_CONTEXT Context
542  )
543 /*++
544 
545 Routine Description:
546 
547  This routine cleans up all the necessary data structures and closes listening threads.
548  It does closing the scanning communication port and completion port.
549 
550 Arguments:
551 
552  Context - User scan context, please see userscan.h
553 
554 Return Value:
555 
556  S_OK if successful. Otherwise, it returns a HRESULT error value.
557 
558 --*/
559 {
560  HRESULT hr = S_OK;
561  if (!CloseHandle(Context->ConnectionPort)) {
562  fprintf(stderr, "[UserScanFinalize]: Failed to close the connection port.\n");
563  hr = HRESULT_FROM_WIN32(GetLastError());
564  }
565 
566  Context->ConnectionPort = NULL;
567 
568  if (!CloseHandle(Context->Completion)) {
569  fprintf(stderr, "[UserScanFinalize]: Failed to close the completion port.\n");
570  hr = HRESULT_FROM_WIN32(GetLastError());
571  }
572 
573  Context->Completion = NULL;
574 
575  return hr;
576 }
577 
578 HRESULT
580  _In_ PUSER_SCAN_CONTEXT Context
581  )
582 /*++
583 
584 Routine Description:
585 
586  This routine cleans up all the necessary data structures and closes listening threads.
587  It does closing abort thread handle and all scanning threads. It also closes the ports
588  by calling UserScanClosePorts(...).
589 
590 Arguments:
591 
592  Context - User scan context, please see userscan.h
593 
594 Return Value:
595 
596  S_OK if successful. Otherwise, it returns a HRESULT error value.
597 
598 --*/
599 {
600  ULONG i = 0;
601  HRESULT hr = S_OK;
602  PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes;
603 
604  if (NULL == scanThreadCtxes) {
605 
606  fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n");
607  return E_POINTER;
608  }
609 
610  if (Context->AbortThreadHandle) {
611 
612  CloseHandle( Context->AbortThreadHandle );
613  }
614 
615  hr = UserScanClosePorts( Context );
616 
617  //
618  // Clean up scan thread contexts
619  //
620 
621  for (i = 0;
623  i ++ ) {
624 
625  if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) {
626  fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n");
627  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
628  }
629  DeleteCriticalSection(&(scanThreadCtxes[i].Lock));
630  }
631  HeapFree( GetProcessHeap(), 0, scanThreadCtxes );
632  Context->ScanThreadCtxes = NULL;
633  return hr;
634 }
635 
638  _In_reads_bytes_(Size) PUCHAR StartingAddress,
639  _In_ SIZE_T Size,
640  _Inout_ PBOOLEAN pAbort
641  )
642 /*++
643 
644 Routine Description:
645 
646  This routine is a naive search for virus signiture.
647  Note that this function is by no means efficient and scalable, but
648  this is not the focus of this example. Thus, an anti-virus
649  vendor may want to focus on and expand this function.
650 
651  It will reset the abort flag if it is aborted.
652 
653 Arguments:
654 
655  StartingAddress - The starting address of the memory to be searched.
656 
657  Size - The size of the memory.
658 
659  pAbort - A pointer to a boolean that notifies the scanning should be canceled..
660 
661  Infected - TRUE if this file is infected. FALSE, otherwise.
662 
663 Return Value:
664 
665  S_OK.
666 
667 --*/
668 {
669  ULONG i;
670  UCHAR targetString[AV_DEFAULT_SEARCH_PATTERN_SIZE] = {0};
671  SIZE_T searchStringLength = AV_DEFAULT_SEARCH_PATTERN_SIZE-1;
672  ULONG ind;
673  PUCHAR p;
674  PUCHAR start = StartingAddress;
675  PUCHAR end = start + Size - searchStringLength;
676 
677  //
678  // Decode the target pattern. We could decode only once and cache it.
679  //
680 
681  CopyMemory( (PVOID) targetString,
684 
685  for (ind = 0;
686  ind < searchStringLength;
687  ind++) {
688 
689  targetString[ind] = ((UCHAR)targetString[ind]) ^ AV_DEFAULT_PATTERN_XOR_KEY;
690  }
691  targetString[searchStringLength] = '\0';
692 
693  //
694  // Scan the memory stream for the target pattern.
695  // If not cancelled.
696  //
697 
698  for (p = start, i = 1;
699  p <= end ;
700  p++, i++) {
701 
702  //
703  // If (*pAbort == TRUE), then we abort the scanning in the loop.
704  //
705 
706  if ( *pAbort ) {
707 
708  *pAbort = FALSE;
710  }
711 
712  if ( !memcmp( p, targetString, searchStringLength )) {
713 
714  return AvScanResultInfected;
715  }
716  }
717 
718  return AvScanResultClean;
719 }
720 
721 HRESULT
723  _In_ PUSER_SCAN_CONTEXT Context,
724  _In_ PSCANNER_MESSAGE Message,
725  _In_ PSCANNER_THREAD_CONTEXT ThreadCtx
726  )
727 /*++
728 
729 Routine Description:
730 
731  After receiving the scan request from the kernel. This routine is
732  the main function that handle the scan request.
733 
734  This routine does not know which file it is scanning because it
735  does not need to know.
736 
737  Its main job includes:
738 
739  1) Send message to the filter to create a section object.
740  2) Map the view of the section.
741  3) Scan the memory
742  4) Send message to tell the filter the result of the scan
743  and close the section object.
744 
745 Arguments:
746 
747  Context - The user scan context.
748 
749  Message - The message recieved from the kernel.
750 
751  ThreadCtx - The scan thread context.
752 
753 Return Value:
754 
755  S_OK.
756 
757 --*/
758 {
759  HRESULT hr = S_OK;
760  ULONG bytesReturned = 0;
761  HANDLE sectionHandle = NULL;
762  DWORD dwErrCode = 0;
763  PVOID scanAddress = NULL;
764  MEMORY_BASIC_INFORMATION memoryInfo;
765  PAV_SCANNER_NOTIFICATION notification = &Message->Notification;
766  COMMAND_MESSAGE commandMessage = {0};
767  DWORD flags = 0;
768 
769  //
770  // Send the message to the filter to create a section object for data scan.
771  // If success, we would get section handle.
772  //
773  // We just have to transparently pass ScanContextId to filter, which we
774  // obtained from the filter previously.
775  //
776 
777  commandMessage.Command = AvCmdCreateSectionForDataScan;
778  commandMessage.ScanId = notification->ScanId;
779  commandMessage.ScanThreadId = ThreadCtx->ThreadId;
780 
781  hr = FilterSendMessage( Context->ConnectionPort,
782  &commandMessage,
783  sizeof( COMMAND_MESSAGE ),
784  &sectionHandle,
785  sizeof( HANDLE ),
786  &bytesReturned );
787 
788  if (FAILED(hr)) {
789 
790  fprintf(stderr,
791  "[UserScanHandleStartScanMsg]: Failed to send message SendMessageToCreateSection to the minifilter.\n");
792  DisplayError(hr);
793  return hr;
794  }
795 
796  scanAddress = MapViewOfFile( sectionHandle,
797  FILE_MAP_READ,
798  0L,
799  0L,
800  0 );
801  if (scanAddress == NULL) {
802  fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to map the view.\n");
803  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
804  goto Cleanup;
805  }
806 
807  if( !VirtualQuery( scanAddress, &memoryInfo, sizeof(memoryInfo) )) {
808  fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to query the view.\n");
809  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
810  goto Cleanup;
811  }
812 
813  //
814  // Data scan here.
815  //
816 
817  commandMessage.ScanResult = UserScanMemoryStream( (PUCHAR)scanAddress,
818  memoryInfo.RegionSize,
819  &ThreadCtx->Aborted );
820 
821  //
822  // If scanning on file open, give the pages a transient boost
823  // since they may soon be accessed in read operations on the
824  // file.
825  //
826 
827  if (notification->Reason == AvScanOnOpen) {
828  flags = MEM_UNMAP_WITH_TRANSIENT_BOOST;
829  }
830 
831 Cleanup:
832 
833  if (scanAddress != NULL) {
834 
835  if (!UnmapViewOfFileEx( scanAddress, flags )) {
836 
837  fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to unmap the view.\n");
838  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
839  }
840  }
841 
842  //
843  // We have to close the section handle after we finish using it.
844  // It is required to close the section handle here in user mode.
845  //
846 
847  if (!CloseHandle(sectionHandle)) {
848 
849  fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to close the section handle.\n");
850  DisplayError(HRESULT_FROM_WIN32(dwErrCode));
851  }
852 
853  //
854  // Send the message to tell filter to close the section object.
855  // This call will set the file clean or infected depending on the scan result, and
856  // also trigger events and release the waiting I/O request thread.
857  //
858 
859  commandMessage.Command = AvCmdCloseSectionForDataScan;
860  hr = FilterSendMessage( Context->ConnectionPort,
861  &commandMessage,
862  sizeof( COMMAND_MESSAGE ),
863  NULL,
864  0,
865  &bytesReturned );
866  if (FAILED(hr)) {
867 
868  fprintf(stderr,
869  "[UserScanHandleStartScanMsg]: Failed to close message SendMessageToCreateSection to the minifilter.\n");
870  DisplayError( hr );
871  return hr;
872  }
873 
874  return hr;
875 }
876 
877 HRESULT
879  _Inout_ PUSER_SCAN_CONTEXT Context
880  )
881 /*++
882 
883 Routine Description:
884 
885  This routine is the scanning worker thread procedure.
886  The pseudo-code of this function is as follows,
887 
888  while(TRUE) {
889  1) Get a overlap structure from the completion port.
890  2) Obtain message from overlap structure.
891  3) Process the message via calling UserScanHandleStartScanMsg(...)
892  4) Pump overlap structure into completion port using FilterGetMessage(...)
893  }
894 
895 Arguments:
896 
897  Context - The user scan context.
898 
899 Return Value:
900 
901  S_OK if no error occurs; Otherwise, it would return appropriate HRESULT.
902 
903 --*/
904 {
905  HRESULT hr = S_OK;
906 
907  PSCANNER_MESSAGE message = NULL;
908  SCANNER_REPLY_MESSAGE replyMsg;
909  LPOVERLAPPED pOvlp = NULL;
910 
911  DWORD outSize;
912  ULONG_PTR key;
913  BOOL success = FALSE;
914 
915  PSCANNER_THREAD_CONTEXT threadCtx = NULL;
916 
917  hr = UserScanGetThreadContextById( GetCurrentThreadId(), Context, &threadCtx );
918  if (FAILED(hr)) {
919  fprintf(stderr,
920  "[UserScanWorker]: Failed to get thread context.\n");
921  return hr;
922  }
923 
924  ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE );
925 
926  printf("Current thread handle %p, id:%u\n", threadCtx->Handle, threadCtx->ThreadId);
927 
928  //
929  // This thread is waiting for scan message from the driver
930  //
931 
932  for(;;) {
933 
934  message = NULL;
935 
936  //
937  // Get overlapped structure asynchronously, the overlapped structure
938  // was previously pumped by FilterGetMessage(...)
939  //
940 
941  success = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE );
942 
943  if (!success) {
944 
945  hr = HRESULT_FROM_WIN32(GetLastError());
946 
947  //
948  // The completion port handle associated with it is closed
949  // while the call is outstanding, the function returns FALSE,
950  // *lpOverlapped will be NULL, and GetLastError will return ERROR_ABANDONED_WAIT_0
951  //
952 
953  if (hr == E_HANDLE) {
954 
955  printf("Completion port becomes unavailable.\n");
956  hr = S_OK;
957 
958  } else if (hr == HRESULT_FROM_WIN32(ERROR_ABANDONED_WAIT_0)) {
959 
960  printf("Completion port was closed.\n");
961  hr = S_OK;
962  }
963 
964  break;
965  }
966 
967  //
968  // Recover message strcuture from overlapped structure.
969  // Remember we embedded overlapped structure inside SCANNER_MESSAGE.
970  // This is because the overlapped structure obtained from GetQueuedCompletionStatus(...)
971  // is asynchronously and not guranteed in order.
972  //
973 
974  message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp );
975 
976  if (AvMsgStartScanning == message->Notification.Message) {
977 
978  //
979  // Reset the abort flag since this is a new scan request and remember
980  // the scan context ID. This ID will allow us to match a cancel request
981  // with a given scan task.
982  //
983 
984  EnterCriticalSection(&(threadCtx->Lock));
985  threadCtx->Aborted = FALSE;
986  threadCtx->ScanId = message->Notification.ScanId;
987  LeaveCriticalSection(&(threadCtx->Lock));
988 
989  //
990  // Reply the scanning worker thread handle to the filter
991  // This is important because the filter will also wait for the scanning thread
992  // in case that the scanning thread is killed before telling filter
993  // the scan is done or aborted.
994  //
995 
996  ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE );
997  replyMsg.ReplyHeader.MessageId = message->MessageHeader.MessageId;
998  replyMsg.ThreadId = threadCtx->ThreadId;
999  hr = FilterReplyMessage( Context->ConnectionPort,
1000  &replyMsg.ReplyHeader,
1002 
1003  if (FAILED(hr)) {
1004 
1005  fprintf(stderr,
1006  "[UserScanWorker]: Failed to reply thread handle to the minifilter\n");
1007  DisplayError(hr);
1008  break;
1009  }
1010  hr = UserScanHandleStartScanMsg( Context, message, threadCtx );
1011 
1012  } else {
1013 
1014  assert( FALSE ); // This thread should not receive other kinds of message.
1015  }
1016 
1017 
1018  if (FAILED(hr)) {
1019 
1020  fprintf(stderr,
1021  "[UserScanWorker]: Failed to handle the message.\n");
1022  }
1023 
1024  //
1025  // If fianlized flag is set from main thread,
1026  // then it would break the while loop.
1027  //
1028 
1029  if (Context->Finalized) {
1030 
1031  break;
1032  }
1033 
1034  //
1035  // After we process the message, pump a overlapped structure into completion port again.
1036  //
1037 
1038  hr = FilterGetMessage( Context->ConnectionPort,
1039  &message->MessageHeader,
1040  FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ),
1041  &message->Ovlp );
1042 
1043  if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) {
1044 
1045  printf("FilterGetMessage aborted.\n");
1046  break;
1047 
1048  } else if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) {
1049 
1050  fprintf(stderr,
1051  "[UserScanWorker]: Failed to get message from the minifilter. \n0x%x, 0x%x\n",
1052  hr, HRESULT_FROM_WIN32(GetLastError()));
1053  DisplayError(hr);
1054  break;
1055  }
1056 
1057  } // end of while(TRUE)
1058 
1059  if (message) {
1060 
1061  //
1062  // Free the memory, which originally allocated at UserScanInit(...)
1063  //
1064 
1065  HeapFree(GetProcessHeap(), 0, message);
1066  }
1067 
1068  printf("***Thread id %u exiting\n", threadCtx->ThreadId);
1069 
1070  return hr;
1071 }
1072 
1073 HRESULT
1075  _Inout_ PUSER_SCAN_CONTEXT Context
1076  )
1077 /*++
1078 
1079 Routine Description:
1080 
1081  This routine is the abort listening thread procedure.
1082  This thread is particularly listening the abortion notifcation from the filter.
1083  The pseudo-code of this function is as follows,
1084 
1085  while(TRUE) {
1086  1) Wair for and get a message from the filter via FilterGetMessage(...)
1087  2) Find the scan thread context by its thread id.
1088  3) Set the cancel flag to be TRUE.
1089  }
1090 
1091 Arguments:
1092 
1093  Context - The user scan context.
1094 
1095 Return Value:
1096 
1097  S_OK if no error occurs; Otherwise, it would return appropriate HRESULT.
1098 
1099 --*/
1100 {
1101  HRESULT hr = S_OK;
1102  HANDLE abortPort = NULL; // A port for listening the abort notification from driver.
1103  SCANNER_MESSAGE message;
1104  DWORD dwThisThread = GetCurrentThreadId();
1105  SCANNER_REPLY_MESSAGE replyMsg;
1106  AV_CONNECTION_CONTEXT connectionCtx = {0};
1107  PSCANNER_THREAD_CONTEXT threadCtx = NULL;
1108 
1109  ZeroMemory( &message, SCANNER_MESSAGE_SIZE );
1110 
1111  //
1112  // Prepare the abort communication port.
1113  //
1114 
1115  connectionCtx.Type = AvConnectForAbort;
1116  hr = FilterConnectCommunicationPort( AV_ABORT_PORT_NAME,
1117  0,
1118  &connectionCtx,
1119  sizeof(AV_CONNECTION_CONTEXT),
1120  NULL,
1121  &abortPort );
1122  if (FAILED(hr)) {
1123 
1124  abortPort = NULL;
1125  return hr;
1126  }
1127 
1128  //
1129  // This thread is listening an scan abortion notifcation or filter unloading from the kernel
1130  // If it receives notification, its type must be AvMsgAbortScanning or AvMsgFilterUnloading
1131  //
1132 
1133  for(;;) {
1134 
1135  //
1136  // Wait until an abort command is sent from filter.
1137  //
1138 
1139  hr = FilterGetMessage( abortPort,
1140  &message.MessageHeader,
1142  NULL );
1143 
1144  if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) {
1145 
1146  printf("[UserScanListenAbortProc]: FilterGetMessage aborted.\n");
1147  hr = S_OK;
1148  break;
1149 
1150  } else if (FAILED(hr)) {
1151 
1152  fprintf(stderr,
1153  "[UserScanListenAbortProc]: Failed to get message from the minifilter.\n" );
1154  DisplayError(hr);
1155  continue;
1156  }
1157 
1158  printf("[UserScanListenAbortProc]: Got message %llu. \n", message.MessageHeader.MessageId);
1159 
1160  if (AvMsgAbortScanning == message.Notification.Message) {
1161 
1162  //
1163  // After this thread receives AvMsgAbortScanning
1164  // it does
1165  // 1) Find the user scan thread context
1166  // 2) Set Aborted flag to be TRUE
1167  //
1168 
1170  Context,
1171  &threadCtx);
1172  if (SUCCEEDED(hr)) {
1173 
1174  printf("[UserScanListenAbortProc]: User Set AvMsgAbortScanning\n");
1175 
1176  //
1177  // Without critical section here, we cannot prevent the scanner thread from
1178  // proceeding to the next task after we check the Id and before we set the abort flag.
1179  // In short, without critical section, it will be a TOCTTOU bug.
1180  //
1181  EnterCriticalSection(&(threadCtx->Lock));
1182  if (threadCtx->ScanId == message.Notification.ScanId) {
1183 
1184  threadCtx->Aborted = TRUE;
1185  printf("[UserScanListenAbortProc]: %lld aborted\n", message.Notification.ScanId);
1186  } else {
1187 
1188  printf("[UserScanListenAbortProc]: tried to abort %lld, but current scan in this thread is %lld\n",
1189  message.Notification.ScanId,
1190  threadCtx->ScanId);
1191  }
1192  LeaveCriticalSection(&(threadCtx->Lock));
1193 
1194  } else {
1195 
1196  fprintf(stderr, "[UserScanListenAbortProc]: Error! UserScanGetThreadContextById failed.\n");
1197  }
1198 
1199  } else if (AvMsgFilterUnloading == message.Notification.Message) {
1200 
1201  //
1202  // After this thread receives AvMsgFilterUnloading
1203  // it does
1204  // 1) Cancell all the scanning threads
1205  // 2) Wait for them to finish the cancel.
1206  // 3) Reply to filter so that the filter can know it can close the server ports.
1207  // 4) Close scan port, completion port, and abortion port.
1208  // 5) Exit the process
1209  //
1210 
1211  UserScanSynchronizedCancel( Context );
1212  printf("The filter is unloading, exit!\n");
1213  ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE );
1214  replyMsg.ReplyHeader.MessageId = message.MessageHeader.MessageId;
1215  replyMsg.ThreadId = dwThisThread;
1216  hr = FilterReplyMessage( abortPort,
1217  &replyMsg.ReplyHeader,
1219 
1220  if (FAILED(hr)) {
1221 
1222  fprintf(stderr, "[UserScanListenAbortProc]: Error! FilterReplyMessage failed.\n");
1223  }
1224 
1225  UserScanClosePorts( Context );
1226  CloseHandle( abortPort );
1227  ExitProcess( 0 );
1228  break;
1229 
1230  } else {
1231 
1232  assert( FALSE ); // This thread should not receive other kinds of message.
1233  }
1234 
1235  if (FAILED(hr)) {
1236 
1237  fprintf(stderr, "[UserScanListenAbortProc]: Failed to handle the message.\n");
1238  DisplayError(HRESULT_FROM_WIN32(GetLastError()));
1239  }
1240  } // end of while(TRUE)
1241 
1242  if (!CloseHandle(abortPort)) {
1243 
1244  fprintf(stderr, "[UserScanListenAbortProc]: Failed to close the connection port.\n");
1245  }
1246  abortPort = NULL;
1247 
1248  return hr;
1249 }
1250 
HRESULT UserScanHandleStartScanMsg(_In_ PUSER_SCAN_CONTEXT Context, _In_ PSCANNER_MESSAGE Message, _In_ PSCANNER_THREAD_CONTEXT ThreadCtx)
Definition: userscan.c:722
#define SCANNER_MESSAGE_SIZE
Definition: userscan.c:57
FILTER_REPLY_HEADER ReplyHeader
Definition: userscan.c:65
#define MAKE_HRESULT(sev, fac, code)
Definition: userscan.h:28
AVSCAN_COMMAND Command
Definition: avlib.h:95
#define AV_ABORT_PORT_NAME
Definition: avlib.h:35
HRESULT UserScanWorker(_Inout_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:878
#define AV_SCAN_PORT_NAME
Definition: avlib.h:34
HRESULT UserScanCleanup(_In_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:579
AV_SCANNER_NOTIFICATION Notification
Definition: userscan.c:44
HRESULT UserScanListenAbortProc(_Inout_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:1074
return TRUE
#define SCANNER_REPLY_MESSAGE_SIZE
Definition: userscan.c:75
#define USER_SCAN_THREAD_COUNT
Definition: userscan.c:30
ULONG ScanThreadId
Definition: avlib.h:109
AVSCAN_RESULT UserScanMemoryStream(_In_reads_bytes_(Size) PUCHAR StartingAddress, _In_ SIZE_T Size, _Inout_ PBOOLEAN pAbort)
Definition: userscan.c:637
struct _SCANNER_REPLY_MESSAGE * PSCANNER_REPLY_MESSAGE
LONGLONG ScanId
Definition: avlib.h:102
VOID UserScanSynchronizedCancel(_In_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:478
#define AV_DEFAULT_PATTERN_XOR_KEY
Definition: avlib.h:192
OVERLAPPED Ovlp
Definition: userscan.c:53
HRESULT UserScanFinalize(_In_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:362
_In_ PLARGE_INTEGER _In_ ULONG _In_ ULONG _In_reads_bytes_(Length)
AVSCAN_REASON Reason
Definition: avlib.h:146
struct _SCANNER_MESSAGE * PSCANNER_MESSAGE
#define AV_DEFAULT_SEARCH_PATTERN_SIZE
Definition: avlib.h:191
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
#define AV_DEFAULT_SEARCH_PATTERN
Definition: avlib.h:190
HRESULT UserScanInit(_Inout_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:138
VOID DisplayError(_In_ DWORD Code)
Definition: user/utility.c:26
AVSCAN_MESSAGE Message
Definition: avlib.h:140
enum _AVSCAN_RESULT AVSCAN_RESULT
CRITICAL_SECTION Lock
Definition: userscan.h:62
FILTER_MESSAGE_HEADER MessageHeader
Definition: userscan.c:38
DWORD WaitForAll(_In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes)
Definition: userscan.c:404
AVSCAN_RESULT ScanResult
Definition: avlib.h:125
HRESULT UserScanClosePorts(_In_ PUSER_SCAN_CONTEXT Context)
Definition: userscan.c:540
struct _SCANNER_REPLY_MESSAGE SCANNER_REPLY_MESSAGE
struct _SCANNER_MESSAGE SCANNER_MESSAGE
HRESULT UserScanGetThreadContextById(_In_ DWORD ThreadId, _In_ PUSER_SCAN_CONTEXT Context, _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx)
Definition: userscan.c:434
AVSCAN_CONNECTION_TYPE Type
Definition: avlib.h:182
LONGLONG ScanId
Definition: avlib.h:153

Social Network


Services Overview

Architect, implement and test file system filter drivers for a wide range of functionality. We can offer several levels of assistance to meet your specific.

Contact Us

You are welcome to contact us for salse or partnership.

Sales: sales@easefilter.com
Support: support@easefilter.com
Info: info@easefilter.com