USB & CD/DVD Blocking: One Way to Keep Data Free of Theft
Introduction
Have you ever thought of blocking access to USB Memory and CD? I will introduce an example of this. Perhaps some of you will not be interested in this, but I think this technique will be useful for more large-scale projects. I referenced a sample in Microsoft Windows DDK. This sample is implemented by file system filter driver. As you know, File System Filter Driver is commonly used in Anti-Virus and it can be used for some other purposes. In this sample, we can not only block access, but log the file path written to USB.
How to Use
This sample consists of 2 sysfiles and a DLL file. In order to test this sample, first execute install.exe in 1_install folder. You can uninstall this by executing uninstall.exe in 3_uninstall folder.
As seen above, click OK button first and then test the functions.
Using the Code
Here I would explain the file system filter driver. There are two ways of developing file system filter driver. One is to use filter function supported by FLTLIB.DLL in system32 directory. In this case, we can communicate with driver by using FilterConnectCommunicationPort()
function and FilterSendMessage()
function. Another one is to get file system driver's pointer and attach our driver to it by using IoAttachDeviceToDeviceStack()
function.
DriverEntry
DriverEntry()
function should be written like below:
NTSTATUS
DriverEntry (
__in PDRIVER_OBJECT DriverObject,
__in PUNICODE_STRING RegistryPath
)
{
PSECURITY_DESCRIPTOR sd;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING uniString;
NTSTATUS status;
PFLT_VOLUME fltvolume;
HANDLE handle = (PVOID)-1;
PROCESS_DEVICEMAP_INFORMATION ldrives;
ULONG drive, bit;
STRING ansiString, ansiVolString;
UNICODE_STRING unString, unVolString;
CHAR szDrv[20];
ULONG sizeneeded;
HANDLE hThread;
OBJECT_ATTRIBUTES oaThread;
KIRQL irql;
ULONG i;
try {
ACDrvData.LogSequenceNumber = 0;
ACDrvData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;
ACDrvData.RecordsAllocated = 0;
ACDrvData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD;
ACDrvData.DriverObject = DriverObject;
InitializeListHead( &ACDrvData.OutputBufferList );
KeInitializeSpinLock( &ACDrvData.OutputBufferLock );
#if ACDRV_LONGHORN
//
// Dynamically import FilterMgr APIs for transaction support
//
ACDrvData.PFltSetTransactionContext =
FltGetRoutineAddress( "FltSetTransactionContext" );
ACDrvData.PFltGetTransactionContext =
FltGetRoutineAddress( "FltGetTransactionContext" );
ACDrvData.PFltEnlistInTransaction =
FltGetRoutineAddress( "FltEnlistInTransaction" );
#endif
SpyReadDriverParameters(RegistryPath);
status = FltRegisterFilter( DriverObject,
&FilterRegistration,
&ACDrvData.Filter );
if (!NT_SUCCESS( status )) {
leave;
}
status = FltBuildDefaultSecurityDescriptor( &sd,
FLT_PORT_ALL_ACCESS );
if (!NT_SUCCESS( status )) {
leave;
}
RtlInitUnicodeString( &uniString, ACDRV_PORT_NAME );
InitializeObjectAttributes( &oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd );
status = FltCreateCommunicationPort( ACDrvData.Filter,
&ACDrvData.ServerPort,
&oa,
NULL,
SpyConnect,
SpyDisconnect,
SpyMessage,
1 );
FltFreeSecurityDescriptor( sd );
if (!NT_SUCCESS( status )) {
leave;
}
...
status = FltStartFiltering( ACDrvData.Filter );
...
} finally {
if (!NT_SUCCESS( status ) ) {
if (NULL != ACDrvData.ServerPort) {
FltCloseCommunicationPort( ACDrvData.ServerPort );
}
if (NULL != ACDrvData.Filter) {
FltUnregisterFilter( ACDrvData.Filter );
}
ExDeleteNPagedLookasideList( &ACDrvData.FreeBufferList );
}
}
return status;
}
After FltStartFiltering()
function is called, call FltAttachVolume()
function to monitor the specific Disk drive or CD drive.
Unload Routine
Unload Routine should be written as below:
NTSTATUS
SpyFilterUnload (
__in FLT_FILTER_UNLOAD_FLAGS Flags
)
{
PFLT_VOLUME fltvolume;
UNICODE_STRING uniString;
STRING AnsiString;
ULONG i;
KIRQL irql;
UNREFERENCED_PARAMETER( Flags );
PAGED_CODE();
...
for(i = 0; i < 26; i++)
{
fltvolume = NULL;
RtlInitAnsiString(&AnsiString, DrvInfo[i].szDrvName);
RtlAnsiStringToUnicodeString(&uniString, &AnsiString, TRUE);
FltGetVolumeFromName( ACDrvData.Filter, &uniString, &fltvolume);
FltDetachVolume( ACDrvData.Filter, fltvolume, NULL);
RtlFreeUnicodeString(&uniString);
}
FltCloseCommunicationPort( ACDrvData.ServerPort );
FltUnregisterFilter( ACDrvData.Filter );
ExDeleteNPagedLookasideList( &ACDrvData.FreeBufferList );
return STATUS_SUCCESS;
}
FilterRegistration Variable Definition
FilterRegistration
variable should be formatted like this:
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_CREATE_NAMED_PIPE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_CLOSE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_READ,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_WRITE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_QUERY_INFORMATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SET_INFORMATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_QUERY_EA,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SET_EA,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_FLUSH_BUFFERS,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_QUERY_VOLUME_INFORMATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SET_VOLUME_INFORMATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_DIRECTORY_CONTROL,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_FILE_SYSTEM_CONTROL,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_DEVICE_CONTROL,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_INTERNAL_DEVICE_CONTROL,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SHUTDOWN,
0,
SpyPreOperationCallback,
NULL }, //post operation callback not supported
{ IRP_MJ_LOCK_CONTROL,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_CLEANUP,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_CREATE_MAILSLOT,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_QUERY_SECURITY,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SET_SECURITY,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_QUERY_QUOTA,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_SET_QUOTA,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_PNP,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_ACQUIRE_FOR_MOD_WRITE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_RELEASE_FOR_MOD_WRITE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_ACQUIRE_FOR_CC_FLUSH,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_RELEASE_FOR_CC_FLUSH,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
/* { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },*/
{ IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_NETWORK_QUERY_OPEN,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_MDL_READ,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_MDL_READ_COMPLETE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_PREPARE_MDL_WRITE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_MDL_WRITE_COMPLETE,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_VOLUME_MOUNT,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_VOLUME_DISMOUNT,
0,
SpyPreOperationCallback,
SpyPostOperationCallback },
{ IRP_MJ_OPERATION_END }
};
const FLT_CONTEXT_REGISTRATION Contexts[] = {
#if ACDRV_LONGHORN
{ FLT_TRANSACTION_CONTEXT,
0,
SpyDeleteTxfContext,
sizeof(ACDRV_TRANSACTION_CONTEXT),
'ypsM' },
#endif // ACDRV_LONGHORN
{ FLT_CONTEXT_END }
};
//
// This defines what we want to filter with FltMgr
//
CONST FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
Contexts, // Context
Callbacks, // Operation callbacks
SpyFilterUnload, // FilterUnload
NULL, // InstanceSetup
SpyQueryTeardown, // InstanceQueryTeardown
NULL, // InstanceTeardownStart
NULL, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
#if ACDRV_LONGHORN
,
SpyKtmNotificationCallback // KTM notification callback
#endif // ACDRV_LONGHORN
};
SpyPreOperationCallback() Function
SpyPreOperationCallback()
function should be written like this:
FLT_PREOP_CALLBACK_STATUS
SpyPreOperationCallback (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext
)
{
FLT_PREOP_CALLBACK_STATUS returnStatus =
FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call
//our completion routine
...
if (Data->Iopb->MajorFunction == IRP_MJ_SYSTEM_CONTROL)
DbgPrint("System Control Called\n");
//------------------------------------insert my process
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
...
//If drive(path's first letter) is USB Memory Drive or CD Drive,
//Block!!!
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
returnStatus = FLT_PREOP_COMPLETE;
return returnStatus;
...
}
returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
...
return returnStatus;
}
Conclusion
Ok, it's all over. I know this is not the best method, but I think this technique can be used anytime. I hope this will be a good help for you to develop a similar program. If you have any questions, please contact me. I hope you are satisfied with my article. Please rate it for me. Thank you.
History
- 27th April, 2011: Initial version
发表评论
ioNCGP I truly appreciate this blog.Really thank you! Fantastic.
G1DgVJ This is very interesting, You are a very skilled blogger. I ave joined your rss feed and look forward to seeking more of your great post. Also, I ave shared your web site in my social networks!
k3tyD9 You made some decent points there. I did a search on the topic and found most persons will agree with your website.
l1QHs7 Woh I enjoy your content , saved to bookmarks!
9xWtHq up to other users that they will help, so here it occurs.
XKbbHq Maybe you can write subsequent articles relating to this
tyxuZ6
t1Zc0O It's actually a great and helpful piece of info. I am satisfied that you shared this useful information with us. Please stay us informed like this. Thank you for sharing.
n2GDME I think this is a real great blog.Thanks Again. Awesome.
kQFS8L Thanks-a-mundo for the post.Really looking forward to read more. Fantastic.
5EGhDx I value the blog.Much thanks again. Really Great.
WHKZru I really like and appreciate your blog post.Really looking forward to read more.