1. 程式人生 > >MiniFilter檔案系統學習

MiniFilter檔案系統學習

Minfilter與legacy filter區別


比sfilter載入順序更易控制. altitude被繫結到合適的位置。 
Minfilter在登錄檔服務項中有一項Altitude值
此值越高位置越靠前 (待考證
每一個minifilter驅動必須有一個叫做altitude的唯一識別符號.一個minifilter驅動的altitude定義了它載入時在I/O棧中相對其他minifilter驅動的位置。值越小,棧中位置就越低
FSFilter Anti-Virus 320000-329999 此組包括在檔案I/O期間探測並防毒的過濾驅動. 
FSFilter Encryption 140000-149999此組包括在檔案I/O期間加密和解密資料的過濾驅動. 
圖片2


可解除安裝能力. 


Callback模型僅需處理必要操作的能力. 
不再需要給一個IRP配置一個完成例程,Minfilter每個過濾功能有2個回撥函式,一個是“事前”回撥(PreCallBack),一個是“事後”回撥(PosCallBack)
相當於PosCallBack就是sfilter中的IRP完成例程
要呼叫PosCallBack只需要PreCallBack 返回 FLT_PREOP_SUCCESS_WITH_CALLBACK
而返回FLT_PREOP_SUCCESS_NO_CALLBACK則告訴系統,處理好這件事後不用呼叫PosCallBack了
一個相當於sfilter中的Cpy,一個是skip
阻止下發返回FLT_PREOP_COMPLETE


相容性更好


名字處理更容易
FltGetFileNameInformation
只需要傳入回撥函式CALL_DATA data 和一個PFLT_FILE_NAME_INFORMATION指標就可以獲得相關檔案的資訊 然後再呼叫
FltParseFileNameInformation就可以獲得路徑了
例子:(注意 獲取路徑需要在Pos中獲取(即建立成功後才能獲取到真實的資料))
//在postcreate裡獲得


PFLT_FILE_NAME_INFORMATION	pNameInfo = NULL;


ntStatus = FltGetFileNameInformation(Data,
		FLT_FILE_NAME_NORMALIZED| 
		FLT_FILE_NAME_QUERY_DEFAULT,
		&pNameInfo);
FltParseFileNameInformation(pNameInfo);


pNameInfo->Name.Buffer
pNameInfo->Volume


FltReleaseFileNameInformation(pNameInfo);




//重新命名的獲得:
PFILE_RENAME_INFORMATION 
    pFileRenameInfomation = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;


FltGetDestinationFileNameInformation //重新命名獲得

安裝方式(.inf/動態載入)


通訊方式(port)
同樣遵循IRQL,鎖等核心開發通用機制
FltCreateFile


Minfilter架構

結構

在DriverEntry中只需要註冊Fliter和Start
FltRegisterFilter( DriverObject,
		&fileMonitorRegistration,
		&g_pFilter );
FltStartFiltering( g_pFilter );

fileMonitorRegistration是唯一我們需要做的

這是一個FLT_REGISTRATION 結構

const FLT_REGISTRATION fileMonitorRegistration = 
{
sizeof( FLT_REGISTRATION ),                 	//  Size
FLT_REGISTRATION_VERSION,              	//  Version
0,                                         		//  Flags
ContextRegistration,                          	//  ContextRegistration //上下文陣列
fileMonitorCallbacks,                         		//  Operation callbacks//最重要的
fileMonUnload,                              		//  FilterUnload
fileMonInstanceSetup,                         	//  InstanceSetup
NULL,                               	   		//  InstanceQueryTeardown
fileMonInstanceTeardownStart,                  	//  InstanceTeardownStart
NULL,                              			//  InstanceTeardownComplete
NULL,                               			//  GenerateFileName
NULL,                               			//  GenerateDestinationFileName
NULL                                			//  NormalizeNameComponent
};



fileMonitorCallbacks 例子:
可以只需要一個回撥如IRP_MJ_CLEANUP
const FLT_OPERATION_REGISTRATION 
fileMonitorCallbacks[] =
{
	{ 
		IRP_MJ_CREATE,
		FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,//這個是可以忽略的IRP
		HOOK_PreNtCreateFile,
		HOOK_PostNtCreateFile
	},
	{ 
		IRP_MJ_CLEANUP,
		0,
		HOOK_PreNtCleanup,
		NULL
	},
	{
		IRP_MJ_WRITE,
		0,
		HOOK_PreNtWriteFile,
		HOOK_PostNtWriteFile
	},
	{  
		IRP_MJ_SET_INFORMATION,
		0,
		HOOK_PreNtSetInformationFile,
		HOOK_PostNtSetInformationFile
	},
	{ 
		IRP_MJ_OPERATION_END//這個是必須要加的
	}
};

//一個回撥的例子:
FLT_PREOP_CALLBACK_STATUS
HOOK_PreNtCreateFile (
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID *CompletionContext 
//分配的一個context資源
)
{
	//sandbox?
	//主防??
	//防毒引擎??
	//加解密??
	return XXX;
}
FLT_POSTOP_CALLBACK_STATUS
HOOK_PostNtCreateFile (
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID CompletionContext, 
	//在PRE-OP裡返回	      //FLT_PREOP_SUCCESS_WITH_CALLBACK
	//時獲取裡面的上下文,並最後釋放
FLT_POST_OPERATION_FLAGS Flags
)
{
	return XXX;
}

上下位陣列 例子:
PFLT_FILTER g_pFilter = NULL;
const FLT_CONTEXT_REGISTRATION 
ContextRegistration[] = 
{//在釋放context之前呼叫,可以在此釋放context裡的記憶體等
	{ 	
		FLT_INSTANCE_CONTEXT,
		0,
		CtxContextCleanup,
		CTX_INSTANCE_CONTEXT_SIZE,
		CTX_INSTANCE_CONTEXT_TAG 
	},
	{	 
		FLT_FILE_CONTEXT,
		0,
		CtxContextCleanup,
		CTX_FILE_CONTEXT_SIZE,
		CTX_FILE_CONTEXT_TAG 
	},
	{ 	
		FLT_STREAM_CONTEXT,
		0,
		CtxContextCleanup,
		CTX_STREAM_CONTEXT_SIZE,
		CTX_STREAM_CONTEXT_TAG
	 },
	{	
		FLT_STREAMHANDLE_CONTEXT,
		0,
		CtxContextCleanup,
		CTX_STREAMHANDLE_CONTEXT_SIZE,
		CTX_STREAMHANDLE_CONTEXT_TAG
	 },
	{ FLT_CONTEXT_END }
};

Minifilter的啟動
NTSTATUS initFileMonitor (PDRIVER_OBJECT DriverObject )
{
return FltRegisterFilter( DriverObject,
		&fileMonitorRegistration,
		&g_pFilter );
}




NTSTATUS startFileMonitor( )
{
	if(g_pFilter)
		return FltStartFiltering( g_pFilter );
	return STATUS_INSUFFICIENT_RESOURCES;
}


VOID stopFileMonitor( )
{
	if(g_pFilter)
	{
		FltUnregisterFilter( g_pFilter );
		g_pFilter = NULL;
	}
}

.inf檔案安裝minifilter
這個就是抄啊改的 沒什麼介紹的,只需要注意裡面的ClassGUID和Class必須對上
這是查詢網址
http://msdn.microsoft.com/en-us/library/windows/hardware/ff540394(v=vs.85).aspx


如果需要用自己的載入器載入Minfilter 只需要在登錄檔服務對應的RegPath下建立REG_SZ型別的Instances子健
在這裡鍵下建立鍵值項,項值為Altitude
 SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances子健下的鍵值項 
 例子:
   //-------------------------------------------------------------------------------------------------------
    // SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances子健下的鍵值項 
    //-------------------------------------------------------------------------------------------------------
    strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
    strcat(szTempStr,lpszDriverName);
    strcat(szTempStr,"\\Instances");
    if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
    {
        return FALSE;
    }
    // 登錄檔驅動程式的DefaultInstance 值 
    strcpy(szTempStr,lpszDriverName);
    strcat(szTempStr," Instance");
    if(RegSetValueEx(hKey,"DefaultInstance",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
    {
        return FALSE;
    }
    RegFlushKey(hKey);//重新整理登錄檔
    RegCloseKey(hKey);
 


    //-------------------------------------------------------------------------------------------------------
    // SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances\\DriverName Instance子健下的鍵值項 
    //-------------------------------------------------------------------------------------------------------
    strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
    strcat(szTempStr,lpszDriverName);
    strcat(szTempStr,"\\Instances\\");
    strcat(szTempStr,lpszDriverName);
    strcat(szTempStr," Instance");
    if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
    {
        return FALSE;
    }
    // 登錄檔驅動程式的Altitude 值
    strcpy(szTempStr,lpszAltitude);
    if(RegSetValueEx(hKey,"Altitude",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
    {
        return FALSE;
    }
    // 登錄檔驅動程式的Flags 值
    dwData=0x0;
    if(RegSetValueEx(hKey,"Flags",0,REG_DWORD,(CONST BYTE*)&dwData,sizeof(DWORD))!=ERROR_SUCCESS)
    {
        return FALSE;
    }
    RegFlushKey(hKey);//重新整理登錄檔
    RegCloseKey(hKey);


    return TRUE;

下面說一說回撥
FLT_PREOP_CALLBACK_STATUS
HOOK_PreNtCreateFile (
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID *CompletionContext 
//分配的一個context資源
)
{
	//sandbox?
	//主防??
	//防毒引擎??
	//加解密??
	return XXX;
}
FLT_POSTOP_CALLBACK_STATUS
HOOK_PostNtCreateFile (
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID CompletionContext, 
	//在PRE-OP裡返回	      //FLT_PREOP_SUCCESS_WITH_CALLBACK
	//時獲取裡面的上下文,並最後釋放
FLT_POST_OPERATION_FLAGS Flags
)
{
	return XXX;
}


PRE-OP的返回值:
FLT_PREOP_SUCCESS_WITH_CALLBACK,//常用
FLT_PREOP_SUCCESS_NO_CALLBACK,//常用

FLT_PREOP_PENDING,//掛起IRP 不常用
FLT_PREOP_DISALLOW_FASTIO,//關閉FASTIO
FLT_PREOP_COMPLETE,//阻止
FLT_PREOP_SYNCHRONIZE//不常用


POST-OP的返回值:
FLT_POSTOP_FINISHED_PROCESSING,//常用
FLT_POSTOP_MORE_PROCESSING_REQUIRED


我們可以判斷這個Data是什麼請求
判斷Data是什麼操作的巨集
FLT_IS_IRP_OPERATION
FLT_IS_FASTIO_OPERATION
FLT_IS_FS_FILTER_OPERATION
		if(FLT_IS_FASTIO_OPERATION(Data))
		{
			ntStatus = STATUS_FLT_DISALLOW_FAST_IO;
			Data->IoStatus.Status = ntStatus;
			Data->IoStatus.Information = 0;
			return FLT_PREOP_DISALLOW_FASTIO;


		}

引數資料的獲取:
PFLT_CALLBACK_DATA Data;
PEPROCESS processObject = 
		Data->Thread ? IoThreadToProcess(Data->Thread) : PsGetCurrentProcess();//獲取EPROCESS
HandleToUlong(PsGetProcessId(processObject));//獲取PID


Data->IoStatus.Status = ntStatus;//返回給R3的
Data->IoStatus.Information = 0;//同上


FltObjects->Volume,//卷
FltObjects->Instance,//例項
FltObjects->FileObject,//檔案物件
FltObjects->FileObject->DeviceObject//裝置物件


Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess //建立的許可權 


比如這次是查詢目錄 (怎麼判斷是什麼操作?每個對應的回撥就告訴你了這是什麼操作,不可以在Create的回撥中收到寫操作把)
PVOID	pQueryBuffer 	= 
		Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
ULONG	uQueryBufferSize 	=  
		Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length

//讀, 讀有可能是使用MDL可能使用其它buff 判斷的方法是看這個有沒有值,沒有值則是另一種
PMDL pReadMdl 		= Data->Iopb->Parameters.Read. MdlAddress;
PVOID pReadBuffer 		= Data->Iopb->Parameters.Read. ReadBuffer;
ULONG uReadLength 		= Data->Iopb->Parameters.Read.Length;

寫同上面

路徑的獲取:
//在postcreate裡獲得


PFLT_FILE_NAME_INFORMATION	pNameInfo = NULL;


ntStatus = FltGetFileNameInformation(Data,
		FLT_FILE_NAME_NORMALIZED| 
		FLT_FILE_NAME_QUERY_DEFAULT,
		&pNameInfo);
FltParseFileNameInformation(pNameInfo);


pNameInfo->Name.Buffer
pNameInfo->Volume


FltReleaseFileNameInformation(pNameInfo);


pNameInfo裡面還有很多東西
WDK裡面的例子:
 //  look again at the first example string from above:
    //
    //    \Device\HarddiskVolume1\Documents and Settings\MyUser\My Documents\Test Results.txt:stream1
    //
    //  Extension = "txt"
    //  Stream = ":stream1"
    //  FinalComponent = "Test Results.txt:stream1"
    //  ParentDir = "\Documents and Settings\MyUser\My Documents\"


//重新命名的獲得:

PFILE_RENAME_INFORMATION 
    pFileRenameInfomation = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;


FltGetDestinationFileNameInformation //重新命名獲得


其實NtCreateSection對應著一個IRP
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
Data->Iopb->Parameters.AcquireForSectionSynchronization.PageProtection == PAGE_EXECUTE
(等於這個就是建立程序)
在x64可以用這個監控程序 不過不需要


檔案操作
在過濾驅動中,我們不能使用預設的檔案操作,這會引起重入
Minfilter給我提供了專門的函式
FltCreateFile
FltReadFile
FltWriteFile
FltClose
FltQueryXxx
FltSetXxx
FltGetXxx
FltPerformXxx


其中的一個例子:
ntStatus = FltCreateFile(pFilter,
	pDstInstance,
	&hDstFile,
	GENERIC_WRITE | SYNCHRONIZE,
	&objDstAttrib,
	&ioStatus,
	0,
	FILE_ATTRIBUTE_NORMAL,
	FILE_SHARE_READ | 
	FILE_SHARE_WRITE | 
	FILE_SHARE_DELETE,
	FILE_CREATE,
	CreateOptions,
	NULL,0,0);

Minfilter上下文:
Context上下文:其實就是附著在某個物件上的一段資料,這段資料由自己定義;
FltAllocateContext
FltReleaseContext


Stream Context - 流上下文,也就是大家常用的FCB(File Control Block)的上下文,檔案和FCB是一對一的關係;
FltGetStreamContext
FltSetStreamContext
Stream Handle Context - 流控制代碼上下文,也就是大家常見的FO(File Object)的上下文,一個檔案可以對應多個FO,屬一對多關係;
FltGetStreamHandleContext
FltSetStreamHandleContext
Instance Context - 例項上下文,也就是過濾驅動在檔案系統的裝置堆疊上建立的一個過濾器例項;
FltGetInstanceContext
FltSetInstanceContext
Volume Context - 捲上下文,卷就是大家通常看到的C,D,E盤以及網路重定向器,一般情況下一個卷對應一個過濾器例項物件,在實際應用上經常用Instance Context來代替Volume Context。
FltGetVolumeContext 
FltSetVolumeContext
檔案上下文(vista之後)
FltGetFileContext
FltSetFileContext


PFLT_FILTER g_pFilter = NULL;
const FLT_CONTEXT_REGISTRATION 
ContextRegistration[] = 
{//在釋放context之前呼叫,可以在此釋放context裡的記憶體等
{ 	
FLT_INSTANCE_CONTEXT,
0,
CtxContextCleanup,
CTX_INSTANCE_CONTEXT_SIZE,
CTX_INSTANCE_CONTEXT_TAG 
},
{	 
	FLT_FILE_CONTEXT,
0,
CtxContextCleanup,
CTX_FILE_CONTEXT_SIZE,
CTX_FILE_CONTEXT_TAG 
},
{ 	
	FLT_STREAM_CONTEXT,
0,
CtxContextCleanup,
CTX_STREAM_CONTEXT_SIZE,
CTX_STREAM_CONTEXT_TAG
 },
{	
	FLT_STREAMHANDLE_CONTEXT,
0,
CtxContextCleanup,
CTX_STREAMHANDLE_CONTEXT_SIZE,
CTX_STREAMHANDLE_CONTEXT_TAG
 },
{ FLT_CONTEXT_END }
};


Context使用例子

typedef struct _INSTANCE_CONTEXT {
…
} INSTANCE_CONTEXT, *PINSTANCE_CONTEXT;
PINSTANCE_CONTEXT pContext = NULL;
//分配與設定
ntStatus = FltGetInstanceContext(FltObjects->Instance, & pContext);//嘗試獲取
if(NT_SUCCESS(Status) == FALSE)
{
	ntStatus = FltAllocateContext(g_pFilter,FLT_INSTANCE_CONTEXT,
		sizeof(INSTANCE_CONTEXT),
		PagedPool,& pContext);
	if(NT_SUCCESS(Status) == FALSE)
	{
		return STATUS_SUCCESS;
	}
	RtlZeroMemory(pContext, sizeof(INSTANCE_CONTEXT));
}
pContext ->m_DeviceType = VolumeDeviceType;
pContext->m_FSType = VolumeFilesystemType;
FltSetInstanceContext(FltObjects->Instance, FLT_SET_CONTEXT_REPLACE_IF_EXISTS,pContext,NULL);
if (pContext)
{
	FltReleaseContext(pContext);
}

//獲取訪問
PINSTANCE_CONTEXT pContext = NULL;
Status = FltGetInstanceContext(FltObjects->Instance,&pContext);
pContext->xxx = xxx;



Minifilter R3與R0通訊
不用像NT框架裡面的通訊方式了,Minifilter為我們提供了專門的函式進行通訊
在通訊時,我們使用Port進行通訊
在R0,我們建立一個Port,R3在通訊前會得到Port的控制代碼,我們就可以通過這個port進行通訊了
R0建立埠程式碼:

RtlInitUnicodeString( &uniString, ScannerPortName );


    //
    //  We secure the port so only ADMINs & SYSTEM can acecss it.
    //
	//設定通訊埠許可權 ,只有管理員和系統程序才能操作
    status = FltBuildDefaultSecurityDescriptor( &sd, FLT_PORT_ALL_ACCESS );


    if (NT_SUCCESS( status )) {


        InitializeObjectAttributes( &oa,
                                    &uniString,
                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                    NULL,
                                    sd );


		//建立通訊埠,並設定對應的回撥函式
        status = FltCreateCommunicationPort( ScannerData.Filter,
                                             &ScannerData.ServerPort,
                                             &oa,//設定的名字
                                             NULL,
                                             ScannerPortConnect,//當R3連線時回撥 主要是記錄R3的程序ID或EPROCESS以便放過本程序 還有記錄R3的通訊埠,給後面主動通訊的時候用
                                             ScannerPortDisconnect,//當R3離線時回撥 主要是關閉R3埠和設定R3的程序資訊為NULL
                                             NULL,//處理R3主動函式 比如R3下新的規則,
                                             1 );//最後一個常為1
        //
        //  Free the security descriptor in all cases. It is not needed once
        //  the call to FltCreateCommunicationPort() is made.
        //
		//設定好後需要釋放許可權的設定
        FltFreeSecurityDescriptor( sd );
        //下面就是判斷是否建立成功,成功後就開始開啟過濾

先說說HIPS的實現,
r3,首先得到R0通訊埠的控制代碼
使用FilterConnectCommunicationPort 只需要注意第一個引數和最後一個引數即可 其它都為0或NULL
然後繫結這個埠(我理解為繫結)
使用CreateIoCompletionPort 只需要注意第一個引數最後一個引數即可,最後一個引數:為這個工作執行緒設定幾個執行緒
這樣有助於高效率 一般小於64 ,也可以設定請求次數 這樣回覆也會高效率一些(具體看後面的程式碼)
我們首先使用FiltetGetMessage非同步獲取一下訊息,如果有資訊,則下面的GetQueuedCompletionStatus就會恢復(如果沒有訊息,GetQueuedCompletionStatus就會暫停下來,並讓出CPU,等待有資訊)


有了資訊後我們就可以進行掃描 過濾,搞定之後就呼叫FilterReplyMessage返回給核心
然後繼續呼叫FilterGetMessage-等待--處理--返回給核心
程式碼:
   
   FilterConnectCommunicationPort( ScannerPortName,//與R0的名字一致
                                         	       0,
		                          	       NULL,
                                         	       0,
                                         	       NULL,
                                         	       &port );//R0埠


//處理從R0來的請求,即R0呼叫FltSendMessage的請求
completion = CreateIoCompletionPort( port,NULL,0,1);
FilterGetMessage( Port,
			&message->MessageHeader,
			FIELD_OFFSET( SANDBOX_MESSAGE, Ovlp ),
			&message->Ovlp );
while(1)
{
GetQueuedCompletionStatus( lpContext->Completion, &outSize, &key, &pOvlp, INFINITE );
//過濾,掃描
FilterReplyMessage(Port,
			(PFILTER_REPLY_HEADER) &replyMessage,
			sizeof( replyMessage ) );
FilterGetMessage( Port,
			&message->MessageHeader,
			FIELD_OFFSET( SANDBOX_MESSAGE, Ovlp ),
			&message->Ovlp );
}


注意:這裡的FILTER_REPLY_HEADER結構,裡面有一項是固定的,有一項是可以自定義的,包括增加自己的結構
同樣的Message對應的結構裡面有一項也是可以自定義的 ,這要跟核心對應起來就可以了,核心裡面只有自定義的那一項

比如:R0資料結構 這個就是自定義的
typedef struct _SCANNER_NOTIFICATION 
{
    ULONG BytesToScan;
    ULONG Reserved; 
    UCHAR Contents[SCANNER_READ_BUFFER_SIZE];
    
} SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;


typedef struct _SCANNER_REPLY 
{
    BOOLEAN SafeToOpen;


} SCANNER_REPLY, *PSCANNER_REPLY;

R3的結構
typedef struct _SCANNER_MESSAGE 
{
	FILTER_MESSAGE_HEADER MessageHeader;
	SCANNER_NOTIFICATION Notification;//可以自定義的 ,跟核心結構對應起來
	OVERLAPPED Ovlp;
} SCANNER_MESSAGE, *PSCANNER_MESSAGE;


typedef struct _SCANNER_REPLY_MESSAGE 
{
	FILTER_REPLY_HEADER ReplyHeader;
	SCANNER_REPLY Reply;//可以自定義的,跟核心結構對應起來
} SCANNER_REPLY_MESSAGE,
  *PSCANNER_REPLY_MESSAGE;

那R0怎麼傳送訊息給R3呢
使用FltSendMessage
例項程式碼:
//傳送訊息給R3
timeout.QuadPart = (LONGLONG)40 * -10000000i64; // 核心等待 40 seconds
Status = FltSendMessage( g_pFilter,
			&g_pClientPort,//給R3發訊息
			&request,
			sizeof(SCANNER_NOTIFICATION),
			&reply,
			&replySize,
			&timeout );

主動通訊就講完了,不過需要注意的時,應用程式可能已經退出了,帶sys還在,那要怎麼辦呢,
R3的程式退出時,R0中那個斷開連線的回撥就會觸發,我們需要在那個回撥中設定使用者通訊埠為NULL

其它過濾函式中需要判斷這個是不是NULL 不是NULL就通訊,是NULL就放行


什麼是R3通訊埠?

其實這個是一個R3埠的控制代碼,當用戶使用FilterConnectCommunicationPort建立連線時,
核心中那個連線函式就會被呼叫,在那裡面就有使用者的通訊埠(控制代碼)(不記得了?往上拉看看吧,我們還在那裡面設定本程序的ID呢)


說完了主動通訊,我們來說說緩衝區的使用
比如寫操作,寫的資料可能在MDLbuff中,也可能在USERbuff中,那要怎麼操作呢,我記得上面提到過
判斷MDLbuff是不是為空,不為空則資料就在這個裡面,否則就是userbuff中


注意這裡需要使用異常處理結構塊,主流程使用tyr and finally 記憶體操作使用try and except( EXCEPTION_EXECUTE_HANDLER )


這裡還介紹了上下文的使用 只不過這裡是很簡單的處理了下
首先在CreatePre事前回調中,我們設定上下文,然後再後面操作中,我們獲取這個上下文,如果獲取不到就不是我們要操作的檔案
這裡其實沒啥用,只是演示如果使用上下文,當然這裡的上下文結構裡面可以自定義,我這裡是設定了需要不需要重新掃描

typedef struct _SCANNER_STREAM_HANDLE_CONTEXT {


    BOOLEAN RescanRequired;
    
} SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT;


程式碼例子:核心處理寫操作 需要注意的事,有一個IRP是FLT管理器發的,有點特殊,需要放過,見程式碼尾巴
//處理寫關閉
FLT_PREOP_CALLBACK_STATUS
ScannerPreWrite (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
/*++


Routine Description:


    Pre write callback.  We want to scan what's being written now.


Arguments:


    Data - The structure which describes the operation parameters.


    FltObject - The structure which describes the objects affected by this
        operation.


    CompletionContext - Output parameter which can be used to pass a context
        from this pre-write callback to the post-write callback.


Return Value:


    Always FLT_PREOP_SUCCESS_NO_CALLBACK.


--*/
{
    FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
    NTSTATUS status;
    PSCANNER_NOTIFICATION notification = NULL;
    PSCANNER_STREAM_HANDLE_CONTEXT context = NULL;
    ULONG replyLength;
    BOOLEAN safe = TRUE;
    PUCHAR buffer;


    UNREFERENCED_PARAMETER( CompletionContext );


    //
    //  If not client port just ignore this write.
    //
	//如果R3程序退出了
    if (ScannerData.ClientPort == NULL) {


        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }
	//獲取上下文
    status = FltGetStreamHandleContext( FltObjects->Instance,
                                        FltObjects->FileObject,
                                        &context );
	//不是我們要處理的檔案,(可以在建立事前回調中設定上下文,比如判斷這個檔案是不是記事本,如果是,我們就給它設定一個上下文,然後到後我們就可以知道這個檔案是不是我們設定過的記事本)
	//這也可以判斷,不過一般不這麼使用,一般是在上下文中插入自己想要資訊,然後到這裡我們到這個上下文中去取
    if (!NT_SUCCESS( status )) {


        //
        //  We are not interested in this file
        //


        return FLT_PREOP_SUCCESS_NO_CALLBACK;


    }


    //
    //  Use try-finally to cleanup
    //


	//必須使用異常處理結構
    try {


        //
        //  Pass the contents of the buffer to user mode.
        //
		//如果寫的長度為0 就放行
        if (Data->Iopb->Parameters.Write.Length != 0) {


            //
            //  Get the users buffer address.  If there is a MDL defined, use
            //  it.  If not use the given buffer address.
            //
			//開始獲取資料快取區 有2個快取區,需要判斷在資料在哪個buff中 判斷的方法前面說過了
            if (Data->Iopb->Parameters.Write.MdlAddress != NULL) {


                buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress,
                                                       NormalPagePriority );


                //
                //  If we have a MDL but could not get and address, we ran out
                //  of memory, report the correct error
                //
				//如果獲取失敗了 就返回資源不足 並不下發了
                if (buffer == NULL) {


                    Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
                    Data->IoStatus.Information = 0;
                    returnStatus = FLT_PREOP_COMPLETE;
                    leave;
                }


            } else {


                //
                //  Use the users buffer
                //
				//不是MDL就是USERbuff
                buffer  = Data->Iopb->Parameters.Write.WriteBuffer;
            }


            //
            //  In a production-level filter, we would actually let user mode scan the file directly.
            //  Allocating & freeing huge amounts of non-paged pool like this is not very good for system perf.
            //  This is just a sample!
            //
			//為傳送給R3資料申請記憶體
            notification = ExAllocatePoolWithTag( NonPagedPool,
                                                  sizeof( SCANNER_NOTIFICATION ),
                                                  'nacS' );
            if (notification == NULL) {


                Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
                Data->IoStatus.Information = 0;
                returnStatus = FLT_PREOP_COMPLETE;
                leave;
            }
			//取最小啦 這裡設定SCANNER_READ_BUFFER_SIZE為1024
            notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE );


            //
            //  The buffer can be a raw user buffer. Protect access to it
            //
			//記憶體操作 必須使用結構化異常處理
            try  {


                RtlCopyMemory( ¬ification->Contents,
                               buffer,
                               notification->BytesToScan );


            } except( EXCEPTION_EXECUTE_HANDLER ) {


                //
                //  Error accessing buffer. Complete i/o with failure
                //
				//出錯就返回錯誤程式碼並阻止下發啦
                Data->IoStatus.Status = GetExceptionCode() ;
                Data->IoStatus.Information = 0;
                returnStatus = FLT_PREOP_COMPLETE;
                leave;
            }


            //
            //  Send message to user mode to indicate it should scan the buffer.
            //  We don't have to synchronize between the send and close of the handle
            //  as FltSendMessage takes care of that.
            //
			//傳送資料給R3 處理
            replyLength = sizeof( SCANNER_REPLY );


            status = FltSendMessage( ScannerData.Filter,
                                     &ScannerData.ClientPort,
                                     notification,
                                     sizeof( SCANNER_NOTIFICATION ),
                                     notification,
                                     &replyLength,
                                     NULL );//永遠等待,一般40秒吧
			//為什麼共用一塊記憶體呢 你猜
            if (STATUS_SUCCESS == status) {
				//使用者返回的處理結果
               safe = ((PSCANNER_REPLY) notification)->SafeToOpen;


           } else {


               //
               //  Couldn't send message. This sample will let the i/o through.
               //


               DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
           }
        }
		//這個是不安全的 你猜怎麼辦?
        if (!safe) {


            //
            //  Block this write if not paging i/o (as a result of course, this scanner will not prevent memory mapped writes of contaminated
            //  strings to the file, but only regular writes). The effect of getting ERROR_ACCESS_DENIED for many apps to delete the file they
            //  are trying to write usually.
            //  To handle memory mapped writes - we should be scanning at close time (which is when we can really establish that the file object
            //  is not going to be used for any more writes)
            //


            DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" );


			//如果不是Flt管理髮送的,這個IRP很特殊 ,必須放行,如果不是這個IRP就阻止了,因為它是不安全的
            if (!FlagOn( Data->Iopb->IrpFlags, IRP_PAGING_IO )) {


                DbgPrint( "!!! scanner.sys -- blocking the write !!!\n" );


                Data->IoStatus.Status = STATUS_ACCESS_DENIED;
                Data->IoStatus.Information = 0;
                returnStatus = FLT_PREOP_COMPLETE;
            }
        }


    } finally {
		//該釋放的釋放
        if (notification != NULL) {


            ExFreePoolWithTag( notification, 'nacS' );
        }


        if (context) {


            FltReleaseContext( context );
        }
    }


    return returnStatus;
}


MF實現的一個封轉的處理函式,這個函式可以將檔案的內容發給R3,讓R3處理並返回一個結果

為什麼需要這個函式呢?

如果不是寫,我們不能直接快取區資料,那麼我們需要讀到這個檔案的內容發給R3,這個函式就是這個功能


程式碼:其中包括了Minifilter讀檔案操作
其實注意的是申請的大小,我們是不知道這個檔案到底有多大的,但我們確定的是這個檔案一般比這個卷的大小小,所以我們暫時先申請卷的大小
然後下面讀的時候會返回檔案的大小,到時候就可以知道有多大了


NTSTATUS
ScannerpScanFileInUserMode (
    __in PFLT_INSTANCE Instance,
    __in PFILE_OBJECT FileObject,
    __out PBOOLEAN SafeToOpen
    )
/*++


Routine Description:


    This routine is called to send a request up to user mode to scan a given
    file and tell our caller whether it's safe to open this file.


    Note that if the scan fails, we set SafeToOpen to TRUE.  The scan may fail
    because the service hasn't started, or perhaps because this create/cleanup
    is for a directory, and there's no data to read & scan.


    If we failed creates when the service isn't running, there'd be a
    bootstrapping problem -- how would we ever load the .exe for the service?


Arguments:


    Instance - Handle to the filter instance for the scanner on this volume.


    FileObject - File to be scanned.


    SafeToOpen - Set to FALSE if the file is scanned successfully and it contains
                 foul language.


Return Value:


    The status of the operation, hopefully STATUS_SUCCESS.  The common failure
    status will probably be STATUS_INSUFFICIENT_RESOURCES.


--*/


{
    NTSTATUS status = STATUS_SUCCESS;
    PVOID buffer = NULL;
    ULONG bytesRead;
    PSCANNER_NOTIFICATION notification = NULL;
    FLT_VOLUME_PROPERTIES volumeProps;
    LARGE_INTEGER offset;
    ULONG replyLength, length;
    PFLT_VOLUME volume = NULL;


    *SafeToOpen = TRUE;


    //
    //  If not client port just return.
    //


    if (ScannerData.ClientPort == NULL) {


        return STATUS_SUCCESS;
    }


    try {


        //
        //  Obtain the volume object .
        //


        status = FltGetVolumeFromInstance( Instance, &volume );


        if (!NT_SUCCESS( status )) {


            leave;
        }


        //
        //  Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are
        //  multiples of sector size. A more efficient way is to make this call once and remember the sector size in the
        //  instance setup routine and setup an instance context where we can cache it.
        //


        status = FltGetVolumeProperties( volume,
                                         &volumeProps,
                                         sizeof( volumeProps ),
                                         &length );
        //
        //  STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names
        //  hence we only check for error status.
        //


        if (NT_ERROR( status )) {


            leave;
        }


        length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize );


        //
        //  Use non-buffered i/o, so allocate aligned pool
        //


        buffer = FltAllocatePoolAlignedWithTag( Instance,
                                                NonPagedPool,
                                                length,
                                                'nacS' );


        if (NULL == buffer) {


            status = STATUS_INSUFFICIENT_RESOURCES;
            leave;
        }


        notification = ExAllocatePoolWithTag( NonPagedPool,
                                              sizeof( SCANNER_NOTIFICATION ),
                                              'nacS' );


        if(NULL == notification) {


            status = STATUS_INSUFFICIENT_RESOURCES;
            leave;
        }


        //
        //  Read the beginning of the file and pass the contents to user mode.
        //


        offset.QuadPart = bytesRead = 0;
        status = FltReadFile( Instance,
                              FileObject,
                              &offset,
                              length,
                              buffer,
                              FLTFL_IO_OPERATION_NON_CACHED |
                               FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
                              &bytesRead,
                              NULL,
                              NULL );


        if (NT_SUCCESS( status ) && (0 != bytesRead)) {


            notification->BytesToScan = (ULONG) bytesRead;


            //
            //  Copy only as much as the buffer can hold
            //


            RtlCopyMemory( ¬ification->Contents,
                           buffer,
                           min( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) );


            replyLength = sizeof( SCANNER_REPLY );


            status = FltSendMessage( ScannerData.Filter,
                                     &ScannerData.ClientPort,
                                     notification,
                                     sizeof(SCANNER_NOTIFICATION),
                                     notification,
                                     &replyLength,
                                     NULL );


            if (STATUS_SUCCESS == status) {


                *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen;


            } else {


                //
                //  Couldn't send message
                //


                DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
            }
        }


    } finally {


        if (NULL != buffer) {


            FltFreePoolAlignedWithTag( Instance, buffer, 'nacS' );
        }


        if (NULL != notification) {


            ExFreePoolWithTag( notification, 'nacS' );
        }


        if (NULL != volume) {


            FltObjectDereference( volume );
        }
    }


    return status;
}



思考:傳BUFF給R3幹什麼?
解答:你猜


說一說一種比較特殊的情況
一個檔案已寫許可權開啟(建立)了,剛開始我們掃描過它沒有問題
問:一個人現在是好人,那麼他一輩子都是好人嗎?


所以,我們需要會他設定一個上下文,上下文中有一個標誌,改標誌的作用是告訴Close時在掃描一次
其實這就是介紹上下文使用啦
核心程式碼:在建立事後回撥中(別以為事後回撥沒有控制權了,它還是可以返回取消前面的操作哦 見最後面的程式碼)

//前面是掃描過這個檔案了而且這個檔案是安全的,但它有寫許可權,那麼就要注意了
if (FltObjects->FileObject->WriteAccess) {


        //
        //
        //  The create has requested write access, mark to rescan the file.
        //  Allocate the context.
        //


        status = FltAllocateContext( ScannerData.Filter,
                                     FLT_STREAMHANDLE_CONTEXT,
                                     sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
                                     PagedPool,
                                     &scannerContext );


        if (NT_SUCCESS(status)) {


            //
            //  Set the handle context.
            //


            scannerContext->RescanRequired = TRUE;


            (VOID) FltSetStreamHandleContext( FltObjects->Instance,
                                              FltObjects->FileObject,
                                              FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
                                              scannerContext,
                                              NULL );


            //
            //  Normally we would check the results of FltSetStreamHandleContext
            //  for a variety of error cases. However, The only error status 
            //  that could be returned, in this case, would tell us that
            //  contexts are not supported.  Even if we got this error,
            //  we just want to release the context now and that will free
            //  this memory if it was not successfully set.
            //


            //
            //  Release our reference on the context (the set adds a reference)
            //


            FltReleaseContext( scannerContext );
        }


然後再close的時候我們處理
這裡說一說cleanup和closeup有什麼區別,前者是使用zwclose或者closehandle 後者使用Obdef (忘記了物件計數減1的那個)
//處理開啟時 有寫許可權但 開啟成功時是安全的,等它關閉的時候的我們來掃描它
//觸發這個回撥的條件是檔案引用技術為0,這個包括核心+R3的計數,一般是在上層使用了ZwClose或者CloseHandle時呼叫
FLT_PREOP_CALLBACK_STATUS
ScannerPreCleanup (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
/*++


Routine Description:


    Pre cleanup callback.  If this file was opened for write access, we want
    to rescan it now.


Arguments:


    Data - The structure which describes the operation parameters.


    FltObject - The structure which describes the objects affected by this
        operation.


    CompletionContext - Output parameter which can be used to pass a context
        from this pre-cleanup callback to the post-cleanup callback.


Return Value:


    Always FLT_PREOP_SUCCESS_NO_CALLBACK.


--*/
{
    NTSTATUS status;
    PSCANNER_STREAM_HANDLE_CONTEXT context;
    BOOLEAN safe;


    UNREFERENCED_PARAMETER( Data );
    UNREFERENCED_PARAMETER( CompletionContext );


    status = FltGetStreamHandleContext( FltObjects->Instance,
                                        FltObjects->FileObject,
                                        &context );


    if (NT_SUCCESS( status )) {


        if (context->RescanRequired) {


            (VOID) ScannerpScanFileInUserMode( FltObjects->Instance,
                                               FltObjects->FileObject,
                                               &safe );


            if (!safe) {


                DbgPrint( "!!! scanner.sys -- foul language detected in precleanup !!!\n" );
            }
        }


        FltReleaseContext( context );
    }




    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}


例子:這個例子攔截字尾為txt的檔案,如果txt中的內容有foul就被認定為病毒
這個例子演示了


通訊方式(HIPS)
上下文的使用
檔名的獲得
緩衝的使用


共有標頭檔案
scannuk.h

/*++


Copyright (c) 1999-2002  Microsoft Corporation


Module Name:


    scanuk.h


Abstract:


    Header file which contains the structures, type definitions,
    constants, global variables and function prototypes that are
    shared between kernel and user mode.


Environment:


    Kernel & user mode


--*/


#ifndef __SCANUK_H__
#define __SCANUK_H__


//
//  Name of port used to communicate
//


const PWSTR ScannerPortName = L"\\ScannerPort";




#define SCANNER_READ_BUFFER_SIZE   1024


typedef struct _SCANNER_NOTIFICATION {


    ULONG BytesToScan;
    ULONG Reserved;             // for quad-word alignement of the Contents structure
    UCHAR Contents[SCANNER_READ_BUFFER_SIZE];
    
} SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;


typedef struct _SCANNER_REPLY {


    BOOLEAN SafeToOpen;
    
} SCANNER_REPLY, *PSCANNER_REPLY;


#endif //  __SCANUK_H__


核心.h檔案

/*++


Copyright (c) 1999-2002  Microsoft Corporation


Module Name:


    scrubber.h


Abstract:
    Header file which contains the structures, type definitions,
    constants, global variables and function prototypes that are
    only visible within the kernel.


Environment:


    Kernel mode


--*/
#ifndef __SCANNER_H__
#define __SCANNER_H__




///////////////////////////////////////////////////////////////////////////
//
//  Global variables
//
///////////////////////////////////////////////////////////////////////////




typedef struct _SCANNER_DATA {


    //
    //  The object that identifies this driver.
    //


    PDRIVER_OBJECT DriverObject;


    //
    //  The filter handle that results from a call to
    //  FltRegisterFilter.
    //


    PFLT_FILTER Filter;


    //
    //  Listens for incoming connections
    //


    PFLT_PORT ServerPort;


    //
    //  User process that connected to the port
    //


    PEPROCESS UserProcess;


    //
    //  Client port for a connection to user-mode
    //


    PFLT_PORT ClientPort;


} SCANNER_DATA, *PSCANNER_DATA;


extern SCANNER_DATA ScannerData;


typedef struct _SCANNER_STREAM_HANDLE_CONTEXT {


    BOOLEAN RescanRequired;
    
} SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT;


#pragma warning(push)
#pragma warning(disable:4200) // disable warnings for structures with zero length arrays.


typedef struct _SCANNER_CREATE_PARAMS {


    WCHAR String[0];


} SCANNER_CREATE_PARAMS, *PSCANNER_CREATE_PARAMS;


#pragma warning(pop)




///////////////////////////////////////////////////////////////////////////
//
//  Prototypes for the startup and unload routines used for 
//  this Filter.
//
//  Implementation in scanner.c
//
///////////////////////////////////////////////////////////////////////////
DRIVER_INITIALIZE DriverEntry;
NTSTATUS
DriverEntry (
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    );


NTSTATUS
ScannerUnload (
    __in FLT_FILTER_UNLOAD_FLAGS Flags
    );


NTSTATUS
ScannerQueryTeardown (
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
    );


FLT_PREOP_CALLBACK_STATUS
ScannerPreCreate (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );


FLT_POSTOP_CALLBACK_STATUS
ScannerPostCreate (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in_opt PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );


FLT_PREOP_CALLBACK_STATUS
ScannerPreCleanup (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );


FLT_PREOP_CALLBACK_STATUS
ScannerPreWrite (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );


NTSTATUS
ScannerInstanceSetup (
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in FLT_INSTANCE_SETUP_FLAGS Flags,
    __in DEVICE_TYPE VolumeDeviceType,
    __in FLT_FILESYSTEM_TYPE VolumeFilesystemType
    );




#endif /* __SCANNER_H__ */



核心.c檔案
/*++


Copyright (c) 1999-2002  Microsoft Corporation


Module Name:


    scanner.c


Abstract:


    This is the main module of the scanner filter.


    This filter scans the data in a file before allowing an open to proceed.  This is similar
    to what virus checkers do.


Environment:


    Kernel mode


--*/


#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>
#include "scanuk.h"
#include "scanner.h"


#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")


//
//  Structure that contains all the global data structures
//  used throughout the scanner.
//


SCANNER_DATA ScannerData;


//
//  This is a static list of file name extensions files we are interested in scanning
//


const UNICODE_STRING ScannerExtensionsToScan[] =
    { RTL_CONSTANT_STRING( L"doc"),
      RTL_CONSTANT_STRING( L"txt"),
      RTL_CONSTANT_STRING( L"bat"),
      RTL_CONSTANT_STRING( L"cmd"),
      RTL_CONSTANT_STRING( L"inf"),
      /*RTL_CONSTANT_STRING( L"ini"),   Removed, to much usage*/
      {0, 0, NULL}
    };




//
//  Function prototypes
//


NTSTATUS
ScannerPortConnect (
    __in PFLT_PORT ClientPort,
    __in_opt PVOID ServerPortCookie,
    __in_bcount_opt(SizeOfContext) PVOID ConnectionContext,
    __in ULONG SizeOfContext,
    __deref_out_opt PVOID *ConnectionCookie
    );


VOID
ScannerPortDisconnect (
    __in_opt PVOID ConnectionCookie
    );


NTSTATUS
ScannerpScanFileInUserMode (
    __in PFLT_INSTANCE Instance,
    __in PFILE_OBJECT FileObject,
    __out PBOOLEAN SafeToOpen
    );


BOOLEAN
ScannerpCheckExtension (
    __in PUNICODE_STRING Extension
    );


//
//  Assign text sections for each routine.
//


#ifdef ALLOC_PRAGMA
    #pragma alloc_text(INIT, DriverEntry)
    #pragma alloc_text(PAGE, ScannerInstanceSetup)
    #pragma alloc_text(PAGE, ScannerPreCreate)
    #pragma alloc_text(PAGE, ScannerPortConnect)
    #pragma alloc_text(PAGE, ScannerPortDisconnect)
#endif




//
//  Constant FLT_REGISTRATION structure for our filter.  This
//  initializes the callback routines our filter wants to register
//  for.  This is only used to register with the filter manager
//


const FLT_OPERATION_REGISTRATION Callbacks[] = {


    { IRP_MJ_CREATE,
      0,
      ScannerPreCreate,
      ScannerPostCreate},


    { IRP_MJ_CLEANUP,
      0,
      ScannerPreCleanup,
      NULL},


    { IRP_MJ_WRITE,
      0,
      ScannerPreWrite,
      NULL},


    { IRP_MJ_OPERATION_END}
};




const FLT_CONTEXT_REGISTRATION ContextRegistration[] = {


    { FLT_STREAMHANDLE_CONTEXT,
      0,
      NULL,
      sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
      'chBS' },


    { FLT_CONTEXT_END }
};


const FLT_REGISTRATION FilterRegistration = {


    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    ContextRegistration,                //  Context Registration.
    Callbacks,                          //  Operation callbacks
    ScannerUnload,                      //  FilterUnload
    ScannerInstanceSetup,               //  InstanceSetup
    ScannerQueryTeardown,               //  InstanceQueryTeardown
    NULL,                               //  InstanceTeardownStart
    NULL,                               //  InstanceTeardownComplete
    NULL,                               //  GenerateFileName
    NULL,                               //  GenerateDestinationFileName
    NULL                                //  NormalizeNameComponent
};


////////////////////////////////////////////////////////////////////////////
//
//    Filter initialization and unload routines.
//
////////////////////////////////////////////////////////////////////////////


NTSTATUS
DriverEntry (
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
/*++


Routine Description:


    This is the initialization routine for the Filter driver.  This
    registers the Filter with the filter manager and initializes all
    its global data structures.


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:


    Returns STATUS_SUCCESS.
--*/
{
    OBJECT_ATTRIBUTES oa;
    UNICODE_STRING uniString;
    PSECURITY_DESCRIPTOR sd;
    NTSTATUS status;


    UNREFERENCED_PARAMETER( RegistryPath );


    //
    //  Register with filter manager.
    //


    status = FltRegisterFilter( DriverObject,
                                &FilterRegistration,
                                &ScannerData.Filter );




    if (!NT_SUCCESS( status )) {


        return status;
    }


    //
    //  Create a communication port.
    //


    RtlInitUnicodeString( &uniString, ScannerPortName );


    //
    //  We secure the port so only ADMINs & SYSTEM can acecss it.
    //
	//設定通訊埠許可權 ,只有管理員和系統程序才能操作
    status = FltBuildDefaultSecurityDescriptor( &sd, FLT_PORT_ALL_ACCESS );


    if (NT_SUCCESS( status )) {


        InitializeObjectAttributes( &oa,
                                    &uniString,
                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                    NULL,
                                    sd );


		//建立通訊埠,並設定對應的回撥函式
        status = FltCreateCommunicationPort( ScannerData.Filter,
                                             &ScannerData.ServerPort,
                                             &oa,//設定的名字
                                             NULL,
                                             ScannerPortConnect,//當R3連線時回撥 主要是記錄R3的程序ID或EPROCESS以便放過本程序 還有記錄R3的通訊埠,給後面主動通訊的時候用
                                             ScannerPortDisconnect,//當R3離線時回撥 主要是關閉R3埠和設定R3的程序資訊為NULL
                                             NULL,//處理R3主動函式 比如R3下新的規則,
                                             1 );//最後一個常為1
        //
        //  Free the security descriptor in all cases. It is not needed once
        //  the call to FltCreateCommunicationPort() is made.
        //
		//設定好後需要釋放許可權的設定
        FltFreeSecurityDescriptor( sd );


        if (NT_SUCCESS( status )) {


            //
            //  Start filtering I/O.
            //
			//開始過濾
            status = FltStartFiltering( ScannerData.Filter );


            if (NT_SUCCESS( status )) {


                return STATUS_SUCCESS;
            }
			//失敗就滾吧
            FltCloseCommunicationPort( ScannerData.ServerPort );
        }
    }
	//失敗就滾吧
    FltUnregisterFilter( ScannerData.Filter );


    return status;
}




NTSTATUS
ScannerPortConnect (
    __in PFLT_PORT ClientPort,
    __in_opt PVOID ServerPortCookie,
    __in_bcount_opt(SizeOfContext) PVOID ConnectionContext,
    __in ULONG SizeOfContext,
    __deref_out_opt PVOID *ConnectionCookie
    )
/*++


Routine Description


    This is called when user-mode connects to the server port - to establish a
    connection


Arguments


    ClientPort - This is the client connection port that will be used to
        send messages from the filter


    ServerPortCookie - The context associated with this port when the
        minifilter created this port.


    ConnectionContext - Context from entity connecting to this port (most likely
        your user mode service)


    SizeofContext - Size of ConnectionContext in bytes


    ConnectionCookie - Context to be passed to the port disconnect routine.


Return Value


    STATUS_SUCCESS - to accept the connection


--*/
{
    PAGED_CODE();


    UNREFERENCED_PARAMETER( ServerPortCookie );
    UNREFERENCED_PARAMETER( ConnectionContext );
    UNREFERENCED_PARAMETER( SizeOfContext);
    UNREFERENCED_PARAMETER( ConnectionCookie );


    ASSERT( ScannerData.ClientPort == NULL );
    ASSERT( ScannerData.UserProcess == NULL );


    //
    //  Set the user process and port.
    //
	//設定本身程序 和 R3的的通訊埠 給後面判斷和通訊時使用
    ScannerData.UserProcess = PsGetCurrentProcess();
    ScannerData.ClientPort = ClientPort;


    DbgPrint( "!!! scanner.sys --- connected, port=0x%p\n", ClientPort );


    return STATUS_SUCCESS;
}



VOID
ScannerPortDisconnect(
     __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 - Context from the port connect routine


Return value


    None


--*/
{
    UNREFERENCED_PARAMETER( ConnectionCookie );


    PAGED_CODE();


    DbgPrint( "!!! scanner.sys --- disconnected, port=0x%p\n", ScannerData.ClientPort );


    //
    //  Close our handle to the connection: note, since we limited max connections to 1,
    //  another connect will not be allowed until we return from the disconnect routine.
    //


	//關閉R3通訊埠
    FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort );


    //
    //  Reset the user-process field.
    //


	//設定R3程序為0
    ScannerData.UserProcess = NULL;
}



NTSTATUS
ScannerUnload (
    __in FLT_FILTER_UNLOAD_FLAGS Flags
    )
/*++


Routine Description:


    This is the unload routine for the Filter driver.  This unregisters the
    Filter with the filter manager and frees any allocated global data
    structures.


Arguments:


    None.


Return Value:


    Returns the final status of the deallocation routines.


--*/
{
    UNREFERENCED_PARAMETER( Flags );


    //
    //  Close the server port.
    //


    FltCloseCommunicationPort( ScannerData.ServerPort );


    //
    //  Unregister the filter
    //


    FltUnregisterFilter( ScannerData.Filter );


    return STATUS_SUCCESS;
}


NTSTATUS
ScannerInstanceSetup (
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in FLT_INSTANCE_SETUP_FLAGS Flags,
    __in DEVICE_TYPE VolumeDeviceType,
    __in FLT_FILESYSTEM_TYPE VolumeFilesystemType
    )
/*++


Routine Description:


    This routine is called by the filter manager when a new instance is created.
    We specified in the registry that we only want for manual attachments,
    so that is all we should receive here.


Arguments:


    FltObjects - Describes the instance and volume which we are being asked to
        setup.


    Flags - Flags describing the type of attachment this is.


    VolumeDeviceType - The DEVICE_TYPE for the volume to which this instance
        will attach.


    VolumeFileSystemType - The file system formatted on this volume.


Return Value:


  FLT_NOTIFY_STATUS_ATTACH              - we wish to attach to the volume
  FLT_NOTIFY_STATUS_DO_NOT_ATTACH       - no, thank you


--*/
{
    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( Flags );
    UNREFERENCED_PARAMETER( VolumeFilesystemType );


    PAGED_CODE();


    ASSERT( FltObjects->Filter == ScannerData.Filter );


    //
    //  Don't attach to network volumes.
    //


    if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) {


       return STATUS_FLT_DO_NOT_ATTACH;
    }


    return STATUS_SUCCESS;
}


NTSTATUS
ScannerQueryTeardown (
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
    )
/*++


Routine Description:


    This is the instance detach routine for the filter. This
    routine is called by filter manager when a user initiates a manual instance
    detach. This is a 'query' routine: if the filter does not want to support
    manual detach, it can return a failure status


Arguments:


    FltObjects - Describes the instance and volume for which we are receiving
        this query teardown request.


    Flags - Unused


Return Value:


    STATUS_SUCCESS - we allow instance detach to happen


--*/
{
    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( Flags );


    return STATUS_SUCCESS;
}




FLT_PREOP_CALLBACK_STATUS
ScannerPreCreate (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
/*++


Routine Description:


    Pre create callback.  We need to remember whether this file has been
    opened for write access.  If it has, we'll want to rescan it in cleanup.
    This scheme results in extra scans in at least two cases:
    -- if the create fails (perhaps for access denied)
    -- the file is opened for write access but never actually written to
    The assumption is that writes are more common than creates, and checking
    or setting the context in the write path would be less efficient than
    taking a good guess before the create.


Arguments:


    Data - The structure which describes the operation parameters.


    FltObject - The structure which describes the objects affected by this
        operation.


    CompletionContext - Output parameter which can be used to pass a context
        from this pre-create callback to the post-create callback.


Return Value:


   FLT_PREOP_SUCCESS_WITH_CALLBACK - If this is not our user-mode process.
   FLT_PREOP_SUCCESS_NO_CALLBACK - All other threads.


--*/
{
    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( CompletionContext );


    PAGED_CODE();


    //
    //  See if this create is being done by our user process.
    //


    if (IoThreadToProcess( Data->Thread ) == ScannerData.UserProcess) {


        DbgPrint( "!!! scanner.sys -- allowing create for trusted process \n" );


        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }


    return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}


BOOLEAN
ScannerpCheckExtension (
    __in PUNICODE_STRING Extension
    )
/*++


Routine Description:


    Checks if this file name extension is something we are interested in


Arguments


    Extension - Pointer to the file name extension


Return Value


    TRUE - Yes we are interested
    FALSE - No
--*/
{
    const UNICODE_STRING *ext;


    if (Extension->Length == 0) {


        return FALSE;
    }


    //
    //  Check if it matches any one of our static extension list
    //


    ext = ScannerExtensionsToScan;


    while (ext->Buffer != NULL) {


        if (RtlCompareUnicodeString( Extension, ext, TRUE ) == 0) {


            //
            //  A match. We are interested in this file
            //


            return TRUE;
        }
        ext++;
    }


    return FALSE;
}


//處理開啟,建立時(一般這個時候提示的話就是已經被感染了)
FLT_POSTOP_CALLBACK_STATUS
ScannerPostCreate (
    __inout PFLT_CALLBACK_DATA Data,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in_opt PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
/*++


Routine Description:


    Post create callback.  We can't scan the file until after the create has
    gone to the filesystem, since otherwise the filesystem wouldn't be ready
    to read the file for us.


Arguments:


    Data - The structure which describes the operation parameters.


    FltObject - The structure which describes the objects affected by this
        operation.


    CompletionContext - The operation context passed fron the pre-create
        callback.


    Flags - Flags to say why we are getting this post-operation callback.


Return Value:


    FLT_POSTOP_FINISHED_PROCESSING - ok to open the file or we wish to deny
                                     access to this file, hence undo the open


--*/
{
    PSCANNER_STREAM_HANDLE_C